Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Микроконтроллеры в системах управления. Лабораторный практикум
.pdf
Лабораторная работа № 4
УПРАВЛЕНИЕ МИКРОКОНТРОЛЛЕРОМ
НА ЯЗЫКЕ СИ
Цель работы – изучение функционирования системы управле-
ния исполнительного уровня на микроконтроллерах серии
ATmega, языка программирования С, составление программ и
их отладка в составе аппаратно-программного комплекса.
Теоретические сведения
Общая характеристика языка Си
Язык программирования Си относится к языкам высокого
уровня, не привязанным к конкретной элементной базе компьютера. Благодаря гибкости, выразительности и компактности
своих конструкций Си и его версии (С++, С#) завоевали максимальную популярность в среде профессиональных программистов и прикладных пользователей.
Язык Си представляет собой удачный компромисс между
стремлением воспользоваться теми возможностями, которые
обычно предоставляют программисту языки высокого уровня,
и необходимостью эффективно использовать особенности
компьютера. Кроме набора средств, присущих современным
языкам программирования высокого уровня (структурность,
модульность, определяемые типы данных), в него включены
средства для программирования, близкие к уровню ассемблера
– использование указателей, побитовые операции, операции
сдвига, – а также непосредственные ассемблерные вставки типа asm volatile("nop"). Обширный набор операторов позволяет
писать компактные и эффективные программы. Однако, такие
мощные средства требуют от программиста внимательности,
аккуратности и хорошего знания языка со всеми его преимуществами и недостатками, а также «поведения» транслятора
51

(см. ниже). При этом ответственность за корректность программ полностью ложится на программиста.
Язык Си относится к типу компилирующихся, т.е. текст
программы на Си, подготовленный с помощью текстового редактора, для получения объектного модуля обрабатывается
компилятором, причем компиляция выполняется в два прохода. При первом проходе (претрансляции) обрабатываются строки-директивы, начинающиеся со знака #, при втором –
транслируется текст программы и создается объектный (машинный) код. Для получения загрузочного (исполняемого) модуля компилятор реализует внешние связи объектного модуля
– подсоединяет к нему соответствующие библиотечные модули, заданные программистом. Следует учесть, что в целях получения максимально компактного исполняемого модуля программы и минимального времени ее выполнения компилятор
оптимизирует ее, перекомпоновывая исходный текст программы, и может даже исключить некоторые фрагменты (например,
пустые циклы в ожидании возникновения прерываний), обнаружив, что их результаты не влияют не последующий ход вычислений. Для сохранения в программе таких фрагментов
(например, заготовок для последующего расширения возможностей программы) можно использовать работу с незадействуемыми регистрами ввода-вывода, а также защиту таких фрагментов ключевым словом volatile, используемым при объявлении переменной. Оно сообщает компилятору, что значение переменной может изменяться в любой момент извне, без какоголибо действия со стороны кода, который компилятор обнаруживает поблизости, соответственно исключение связанного с
ней фрагмента недопустимо. Более подробно, с примерами,
вопросы оптимизации кода компилятором и возможные
неожиданности для программиста многократно рассмотрены и
интернете (например, в [19])
В настоящее время имеется большое количество систем
программирования на Си для разных типов компьютеров. Разработано множество библиотек модулей, инструментальных
52

Заголовок программы (наименование, авторство и т.п.)
Директивы компилятора – с префиксом «шарп», т.е. #
Тело программы:
Функции
{
}
Прерывания
{
}
main()
{
инициализация;
Главный БЕСКОНЕЧНЫЙ цикл
{
собственно программа
}
}
средств разработки и отладки, облегчающих создание новых
программ.
Базовые навыки программирования на Си студентами
должны быть получены на предшествующих данной дисциплине курсах, однако следует повторить основные введения о
следующих разделах: типы данных, константы, переменные,
указатели, выражения, операторы, функции, а также о структуре программ. Для этого можно использовать сведения из многочисленных интернет-источников.
Напомним, что рекомендуемая структура программы имеет вид:
Программирование микроконтроллеров на языке Си
в среде разработки «AVRStudio»
Язык Си – это язык относительно "низкого уровня" среди
высокоуровневых. Это означает, что Си оперирует с объекта-
53

ми того же вида, что и большинство команд ЭВМ, а именно, с
символами, числами и адресами. Они могут обрабатываться
посредством обычных арифметических и логических операций, осуществляемых реальными ЭВМ. Соответственно, Си
активно используется при программировании микроконтроллеров и к его использованию дается доступ в соответствующих
средах программирования, в частности в среде разработки
«AVR Studio» («Atmel Studio»), описанной выше.
Ниже рассмотрены процедуры управления МК AVR, свя-
занные с управлением отдельными битами регистров этих МК.
Как показано выше, периферийные устройства МК представляются процессору в виде совокупностей регистров, причём
регистры данных используются для приема и передачи единых
слов данных, а регистры состояния и управления – для восприятия отдельных битов и воздействия на биты, управляющие
режимами устройств. Простейшим случаем регистров состояния и управления являются порты. Дальнейшее описание битовых операций приводится на примере битов какого-либо
порта, однако наибольшее значение имеют операции с регистрами таймеров, АЦП и других сложных периферийных
устройств МК.
Работа с регистрами AVR микроконтроллера на Си,
битовые операции
Для записи и чтения отдельных битов в портах микроконтроллера необходимо научиться выполнять битовые операции, уметь использовать битовые маски и выполнять запись в
порт.
Операции битового сдвига
Существует несколько разновидностей операций битового
сдвига:
• логический (сдвинутые в направлении биты теряются, а
освободившиеся позиции заполняются нулями);
54

• арифметический (сдвиг влево аналогичен логическому,
а при сдвиге вправо свободные позиции заполняются значениями крайнего левого бита, который еще называют знаковым);
• циклический (потерянные с одной стороны биты пере-
мещаются на освободившиеся позиции с другой, как замкнутое
кольцо).
Операторы битового сдвига в языке программирования Си
обозначаются как ">>" и "<<" и выполняют логический сдвиг
битов в используемой переменной в указанном направлении и
на указанное число элементов. При этом существенно то, какой тип данных подвергается сдвигу, т.е. какова разрядность
переменной. Ниже рассмотрены сдвиги в пределах байта (биты, сдвинутые за пределы байта, теряются).
Для примера выполним сдвиги битов в разных числах,
предварительно представив их в двоичном виде.
Для десятичного числа 217 (в двоичной системе
0b11011001):
• 217 = 11011001;
• 217 << 3 = 200 (11011001000);
• 217 << 5 = 32 (1101100100000);
• 217 >> 5 = 6 (0000011011001).
Еще один пример. Для числа 1 (в двоичной системе
0b00000001):
• 1 << 0 = 1 (00000001);
• 1 << 1 = 2 (00000010);
• 1 << 2 = 4 (00000100)
• 1 << 5 = 32 (00100000);
• 1 >> 2 = 0 (0000000001).
55

1101 1001 (217)
&
0000 0111 (7)
---------------0000 0001 (1)
1101 1001 (217)
~
---------------0010 0110 (38)
1101 0001 (217)
|
0000 0111 (7)
---------------1101 1111 (223)
1101 1001 (217)
^
0000 0111 (7)
---------------1101 1110 (222)
Видно, что сдвиг влево на один разряд выполняет умножение числа на 2, а сдвиг вправо – деление числа на 2 нацело.
Далее будет использоваться сдвиг на задаваемое количество
разрядов именно числа 1.
Битовые операторы в языке Си
Рассмотрим операторы Си, действующие побитно:
• "&" (логическое И, AND) или умножение – бинарная
операция, результат которой равен 1 только в том случае, если
оба операнда равны 1, в противном случае будем иметь 0;
• "|" (логическое ИЛИ, OR) или сложение – бинарная опе-
рация, результат которой равен 1 в том случае, если хотя бы
один из операндов равен 1;
• "^" (исключающее ИЛИ, XOR) – бинарная операция, ре-
зультат которой равен 1 в том случае, если только один из двух
операндов равен 1;
• "~" (логическое НЕ) или инверсия – унарная операция,
результат которой равен 0, если операнд равен 1, и наоборот –
результат равен 1, если операнд равен 0.
Рассмотрим примеры битовых операций над двоичными
представлениями двух чисел 217 и 7:
Результаты можно интерпретировать так – битовые опера-
ции позволяют установить или сбросить отдельные биты байта.
56

Установка битов в регистре порта
Для примера сделаем установку (т.е. перевод в состояние
1) в регистре порта PORTB для разряда PB5. Допустим, что
сейчас в регистре PORTB содержится число 0b10001000 (высокий уровень на разрядах PB7 и PB3).
Чтобы установить PB5, будем использовать битовую опе-
рацию логического ИЛИ в комплексе с битовой маской. Для
получения битовой маски, при помощи которой позже будет
установлен один бит, мы выполним левосторонний сдвиг битов числа 1 (00000001) на 5 разрядов:
0000 0001
<< 5
---------------0010 0000
В результате битовой операции получим число
0b00100000.
Теперь сделаем битовую операцию ИЛИ (итак, для
УСТАНОВКИ – ИЛИ) над текущим числом в регистре и получившимся числом-маской:
1000 1000
|
0010 0000
-------------1010 1000
А теперь сравните состояние регистра перед операцией и
после – все состояния битов сохранены и дополнительно установлен 5-й разряд.
Для установки высокого уровня для разряда PB5 и после-
дующей записи числа в регистр порта PORTB в данном примере можно использовать любую из следующих конструкций
операторов, они все выполняют идентичную задачу:
• PORTB = PORTB | 0b00100000;
• PORTB = PORTB | (1 << 5);
• PORTB = PORTB | (1 << PB5);
• PORTB |= (1 << PB5);
57

Наиболее удобно использовать последнюю краткую за-
пись, где используется комбинирования операция логического
ИЛИ и присвоения, в данном случае PB5. К примеру, константа PB5 (разряд 5 порта B) определена в файле
/usr/lib/avr/include/avr/iom16.h для микроконтроллера
ATmega16 и она равна числу 5.
Как установить несколько бит в регистре? Можно вызвать
поочередно две конструкции с операторами, но можно выполнить всё одной командой. Допустим, нужно установить те биты в регистре порта PORTD, которые соответствуют разрядам
PD1 и PD5. Можно использовать любую из следующих конструкций операторов:
• PORTB |= ( 1 << 1 ) | ( 1 << 5 );
• PORTB |= ( 1 << PD1 ) | ( 1 << PD5 );
Если следует установить один или несколько разрядов ре-
гистра в 1, а остальные в 0, то достаточно только присвоения,
например,
• PORTD = 1<< PD2;
даст PORTD = 0b00000100 независимо от предшествующего
значения в этом порте.
Сброс битов в регистре порта
Для сброса разрядов в регистре порта будем использовать
битовую операцию "&" (логическое "И"), которая применяется
к двум битам (бинарная операция) и даёт единицу только в том
случае, если оба исходных бита имеют единичное значение,
кроме того, потребуется использовать битовую унарную операцию "~" (логическое "НЕ", инверсия).
Выполним для примера сброс разряда PD4 в регистре порта PORTD. Допустим, что сейчас в регистре PORTD содержится число 0b10011101.
Для того чтобы сбросить заданный разряд порта PORTD,
подготовим маску (как при установке битов), инвертируем ее
биты "~", а потом выполним битовую операцию "&" над теку-
58

щим значением регистра и полученной инвертированной маской.
Для подготовки маски выполним сдвиг битов на 4 разря-
дов в числе 1 (00000001).
0000 0001
<< 4
---------------0001 0000
Маска готова, получили число 16 (00010000), 2 в 4-й сте-
пени. Выполним инверсию битов:
0001 0000
~
------------1110 1111
Осталось применить маску к содержимому регистра порта
PORTB, используя битовую операцию "&":
1001 1101
&
1110 1111
------------1000 1101
Теперь в содержимом регистра PORTD значение PD4
установлено в 0 с сохранением состояний остальных бит. В
языке Си данные операции можно выполнить, используя любую из приведенных ниже, идентичных по результату команд:
• PORTD = PORTD & ~( 1 << 4 );
• PORTD = PORTD & ~( 1 << PD4 );
• PORTD &= ~( 1 << PD4 );
Итак, для сброса следует применять НЕ-И
В данном случае наиболее удобной и информативной
формой команды будет последний укороченный вариант. Для
одновременного сброса нескольких битов регистра можно использовать вот такие конструкции из операторов:
• PORTD = PORTD & ~( ( 1 << PD4 ) | ( 1 << PD6 ) );
59

• PORTD &= ~( ( 1 << PD4 ) | ( 1 << PD6 ) ); – следите за
скобками!
Проверка разрядов регистра
Теперь рассмотрим, каким образом можно проверить раз-
ряды регистра на наличие в них 1 или 0. Это может потребоваться, если нужно получить значение битов в регистрах специального назначения (флагов) микропроцессора, а также для
чтения состояния различных устройств и модулей, которые передают свое состояние, используя битовую структуру.
Как проверить значение определенного бита в регистре на
Си? Для этого нужно подобрать специальное выражение с использованием битовых операций, результатом работы которого
будет значение: правда (True) или ложь (False). Имея булево
(bool) значение выражения, мы можем использовать для работы условные операторы языка Си.
Например, нам нужно проверить есть ли единица в разряде
PD2 порта PORTD. Примем, что текущее значение регистра –
10010101.
Для проверки используем выражение, которое состоит из
битовой маски с установленным битом для проверки и проверяемого регистра, к которым применен битовый оператор "&"
(логическое И).
Готовим маску, в которой только разряд 2 установлен в 1.
Для этого выполним сдвиг битов числа 1 на 2 разряда влево:
0000 0001
<< 2
-------------
0000 0100
Теперь применим битовую операцию "&" (логическое И) к
содержимому регистра PORTD и получившейся маске:
1001 0101
&
0000 0100
-------------
0000 0100
60
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
