Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Микроконтроллеры в системах управления. Лабораторный практикум

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
☆
Лабораторная работа № 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
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]