- •Программирование микроконтроллеров avr на языке ассемблера
- •Рецензенты:
- •Введение
- •1. Архитектура однокристальных микроконтроллеров семейства avr
- •2. Технические характеристики микроконтроллера aTmega32
- •3. Разработка программного обеспечения микроконтроллеров avr
- •3.1. Этапы разработки программного обеспечения однокристальных микроконтроллеров
- •3.2. Правила записи констант и выражений
- •3.3. Программная модель микроконтроллеров avr
- •3.4. Регистр статуса
- •3.5. Команды ассемблера
- •3.6. Директивы ассемблера
- •3.7. Настройка указателя стека
- •3.8. Работа с портами ввода-вывода
- •3.9. Работа с оперативной памятью данных
- •4. Интегрированная среда проектирования
- •4.1. Создание проекта в интегрированной среде проектирования avr studio 4.16
- •4.2. Отладка программы в avr studio 4.16
- •4.3. Загрузка программы в энергонезависимую память программ
- •Заключение
- •Библиографический список
- •Содержание
- •191028, Санкт-Петербург, ул. Моховая, 26
3.8. Работа с портами ввода-вывода
Порты ввода-вывода предназначены для обеспечения обмена данными между микроконтроллером и внешними устройствами. В микроконтроллерах AVR порты имеют буквенные имена, начиная с буквы «А», например «PORTА». Хотя для различных моделей микроконтроллеров AVR количество портов ввода-вывода будет отличаться, но процедура программирования будет одинаковой. Порты ввода-вывода могут иметь альтернативные функции (например, PORTA может использоваться как входы аналого-цифрового преобразователя).
Все порты ввода-вывода являются восьмиразрядными, но каждая линия порта может использоваться самостоятельно. Все линии ввода-вывода могут находиться в следующих состояниях:
– Z-состояние, линия имеет высокое входное сопротивление;
– режим ввода;
– режим вывода;
– альтернативная функция порта.
Управление портами ввода-вывода осуществляется через специальные регистры. Ниже представлено назначение этих регистров («x» – означает имя регистра):
PORTx (DATA REGISTER) – регистр данных;
DDRx (DATA DIRECTION REGISTER) – регистр управления;
PINx (INPUT PINS ADDRESS) – физические линии данных.
Например:
PORTA – регистр данных порта А;
DDRA – регистр управления порта А;
PINA – физические линии данных порта А.
По сигналу системного сброса все порты ввода-вывода устанавливаются в Z-состояние (состояние высокого импеданса). Перед выполнением операций порты должны быть сконфигурированы на ввод или вывод. Таким образом, работа с портами осуществляется следующим образом: настройка режима линий порта (ввод или вывод), вывод или ввод через линии порта. При использовании библиотеки описания имен можно вместо адресов использовать имена регистров порта.
Настройка портов ввода-вывода:
Настройка портов заключается в конфигурировании линии порта на ввод или вывод. При конфигурировании портов следует пользоваться табл. 2.
Таблица 2. Конфигурация портов ввода-вывода
DDxn |
PORTxn |
I/O |
Pull-up |
Комментарии |
0 |
0 |
Input |
Нет |
Z-состояние |
0 |
1 |
Input |
Да |
Ввод |
1 |
0 |
Output |
Нет |
Вывод лог. 0 |
1 |
1 |
Output |
Нет |
Вывод лог. 1 |
В табл. 2 буквой «x» обозначено имя порта, а буквой «n» – номер линии порта. Весь порт может быть сконфигурирован одинаково, или некоторые линии порта настроены на ввод, а некоторые на вывод.
Приведем примеры настройки портов ввода-вывода. Предположим, выполнено следующее распределение линий портов:
PORTA – весь порт конфигурируется на ввод (вход);
PORTB – весь порт конфигурируется на вывод (выход);
PORTC – четыре младшие линии (PC0 – PC3) конфигурируются на ввод (вход), четыре старшие (PC4 – PC7) конфигурируются на вывод (выход);
PORTD – каждая линия порта конфигурируется самостоятельно;
PD0 – ввод (вход);
PD1 – вывод (выход);
PD2 – ввод (вход);
PD3 – ввод (вход);
PD4 – вывод (выход);
PD5 – вывод (выход);
PD6 – ввод (вход);
PD7 – ввод (вход).
В соответствии с программной моделью микроконтроллеров семейства AVR загрузка данных в регистры портов ввода-вывода осуществляется из регистров общего назначения (РОН). Таким образом, при загрузке констант в регистры портов следует сначала загрузить их в регистры общего назначения. Константы будем формировать в соответствии с таблицей. В качестве регистра общего назначения во всех примерах будем использовать регистр R16.
Настройка PORTA на ввод (вход):
Сформируем константы для регистров DDRA (DATA DIRECTION REGISTER – регистр управления) и PORTA (DATA REGISTER – регистр данных).
DDRA = 0000 00002
PORTA = 1111 11112
Преобразуем в шестнадцатеричный код:
DDRA = 00H
PORTA = FFH
По сигналу системного сброса при включении питания во все разряды регистров управления портами DDRx уже записываются все нули и нам этого делать уже не нужно.
Пример программы: «Настройка PORTA на ввод (вход)»:
ldi r16, $FF ; загрузка константы FFH в регистр R16
out PORTA, r16 ; загрузка содержимого R16 в регистр данных порта А
Настройка PORTB на вывод (выход):
Сформируем константы для регистров DDRB (DATA DIRECTION REGISTER – регистр управления) и PORTB (DATA REGISTER – регистр данных).
DDRB = 1111 11112
Преобразуем в шестнадцатеричный код:
DDRB = FFH
Пример программы: «Настройка PORTB на вывод (выход)»:
ldi r16, $FF ; загрузка константы FFH в регистр R16
out DDRB, r16 ; загрузка содержимого R16 в регистр управления порта B
Настройка PORTC:
Четыре младшие линии (PC0 – PC3) конфигурируются на ввод, четыре старшие (PC4 – PC7) конфигурируются на вывод.
Сформируем константы для регистров DDRC (DATA DIRECTION REGISTER – регистр управления) и PORTC (DATA REGISTER – регистр данных).
DDRC = 0000 11112
PORTC = 1111 00002
Примечание. В нашем примере на линиях (PC4 – PC7) будут выводиться логические нули.
Преобразуем в шестнадцатеричный код:
DDRC = 0FH
PORTC = F0H
Пример программы: «Настройка PORTC»:
Четыре младшие линии (PC0 – PC3) конфигурируются на ввод (вход), четыре старшие (PC4 – PC7) конфигурируются на вывод (выход):
ldi r16, $0F ; загрузка константы 0FH в регистр R16
out DDRC, r16 ; загрузка содержимого R16 в регистр управления порта С
ldi R16, $F0 ; загрузка константы F0H в регистр R16
out PORTC, r16 ; загрузка содержимого R16 в регистр данных порта С
Настройка PORTD:
Каждая линия порта конфигурируется самостоятельно:
PD0 – ввод (вход);
PD1 – вывод (выход);
PD2 – ввод (вход);
PD3 – ввод (вход);
PD4 – вывод (выход);
PD5 – вывод (выход);
PD6 – ввод (вход);
PD7 – ввод (вход).
Сформируем константы для регистров DDRD (DATA DIRECTION REGISTER – регистр управления) и PORTD (DATA REGISTER – регистр данных).
DDRD = 0111 00102
PORTD = 1000 11012
Примечание. В нашем примере на линиях PD1, PD4, PD5 будут выводиться логические нули.
Преобразуем в шестнадцатеричный код:
DDRD = 72H
PORTD = 8DH
Пример программы: «Настройка PORTD»:
Каждая лини порта конфигурируется самостоятельно:
ldi r16, $72 ; загрузка константы 72H в регистр R16
out DDRD, r16 ; загрузка содержимого R16 в регистр управления порта D
ldi r16, $8D ; загрузка константы 8DH в регистр R16
out PORTD, r16 ; загрузка содержимого R16 в регистр данных порта D
Ввод информации в порт:
Если весь порт настроен на ввод (вход), то операция очень простая. В нашем примере на ввод (вход) настроен PORTA.
Пример ввода через PORTA. Весь порт настроен на ввод (вход):
in r16, PINA ; ввод информации из порта А и сохранение ее в R16
Вывод информации в порт:
В зависимости от того, настроен весь порт на вывод или нет, возможны разные варианты вывода информации через порт.
Самый простой вариант работы с портом, который полностью настроен на вывод. В нашем примере полностью настроен на вывод PORTB. В соответствии с программной моделью операция вывода в порт осуществляется через регистры.
Пример программы: «Вывод через PORTB»:
out PORTB, r16 ; загрузка содержимого R16 в регистр данных порта B
nop ; холостая команда для задержки времени
Примечание. При операциях вывода информация отправляется в регистр данных порта PORTx, но это внутренний регистр. Внешние выводы контроллера связаны с регистром PINx. Информация из регистра данных порта PORTx поступает в регистр PINx с отставанием на один машинный такт (т. е. на одну команду). Для удобства контроля программы при отладке в предыдущем примере введена холостая команда NOP.
Более сложный случай с PORTC. В нем четыре младшие линии (PC0 – PC3) конфигурируются на ввод, четыре старшие (PC4 – PC7) конфигурируются на вывод. В этом случае мы должны выводить информацию только через старшие четыре разряда PORTC и не мешать младшим.
Напомним, что раз линии (PC0 – PC3) конфигурируются на ввод, то содержимое разрядов PORTC0 – PORTC3 должно быть равно логическим единицам. Следовательно, необходимо применять операцию маскирования.
Сформулируем требования к программе. Пусть исходная информация находится в регистре R20. Требуется произвести вывод информации, не меняя содержимого разрядов PORTC0 – PORTC3. В разрядах PORTC0 – PORTC3 должны находиться логические единицы.
Пример программы: «Вывод информации через PORTС»:
Линии (PC0 – PC3) конфигурируются на ввод (вход), линии (PC4 – PC7) конфигурируются на вывод (вывод).
mov r21, r20 ; копируем исходную информацию
ori r21, $0F ; логическое сложение копии информации с маской 0000 11112
out PORTC, r21 ; вывод содержимого R21 в регистр данных PORTC
nop ; холостая команда для задержки времени