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

2341

.pdf
Скачиваний:
2
Добавлен:
15.11.2022
Размер:
1.43 Mб
Скачать

Лабораторная работа № 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

Видно, что сдвиг влево на один разряд выполняет умножение числа на 2, а сдвиг вправо – деление числа на 2 нацело. Далее будет использоваться сдвиг на задаваемое количество разрядов именно числа 1.

Битовые операторы в языке Си

Рассмотрим операторы Си, действующие побитно:

"&" (логическое И, AND) или умножение – бинарная операция, результат которой равен 1 только в том случае, если оба операнда равны 1, в противном случае будем иметь 0;

"|" (логическое ИЛИ, OR) или сложение – бинарная операция, результат которой равен 1 в том случае, если хотя бы один из операндов равен 1;

"^" (исключающее ИЛИ, XOR) – бинарная операция, результат которой равен 1 в том случае, если только один из двух операндов равен 1;

"~" (логическое НЕ) или инверсия – унарная операция, результат которой равен 0, если операнд равен 1, и наоборот – результат равен 1, если операнд равен 0.

Рассмотрим примеры битовых операций над двоичными представлениями двух чисел 217 и 7:

1101 1001

(217)

1101 1001

(217)

&

 

~

 

0000 0111

(7)

----------------

 

----------------

 

0010 0110

(38)

0000 0001

(1)

 

 

1101 0001

(217)

1101 1001 (217)

|

 

^

 

0000 0111

(7)

0000 0111

(7)

----------------

 

----------------

1101 1111

(223)

1101 1110

(222)

Результаты можно интерпретировать так – битовые операции позволяют установить или сбросить отдельные биты байта.

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]