- •Программирование микроконтроллеров 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.9. Работа с оперативной памятью данных
Внутренняя оперативная память данных RAM доступна как для чтения, так и для записи. Существуют команды двух типов. Команды типа LD – загрузить регистр из оперативной памяти и команды типа ST – сохранить содержимое регистра в оперативной памяти. То есть доступ к оперативной памяти данных RAM осуществляется через регистры общего назначения.
Пример:
Загрузить константу 01Н в ячейку RAM с адресом 0060Н (младшая ячейка RAM). Будем использовать команду STS (Сохранить непосредственно). В указанной команде явно указан адрес регистра источника и ячейки памяти приемника. В качестве временного регистра используем регистр R16. В данном примере могут быть использованы регистры с номерами с R16 до R31 включительно и любая ячейка памяти, существующая для конкретной модели микроконтроллера.
ldi r16, $01 ; загрузка в регистр R16 константы 01Н
sts $0060, r16 ; сохранение содержимого регистра R16 в ячейке памяти
; RAM с адресом 0060Н
Пример:
Загрузить в регистр R0 содержимое ячейки RAM с адресом 0060Н (младшая ячейка RAM). Будем использовать команду LDS (загрузить непосредственно). В указанной команде явно указан адрес регистра источника и ячейки памяти приемника. В данном примере могут быть использованы регистры с номерами с R0 до R31 включительно и любая ячейка памяти, существующая для конкретной модели микроконтроллера.
lds r0, $0060 ; загрузка в регистр R0 содержимого ячейки памяти RAM
; с адресом 0060Н
Пример:
Скопировать содержимое ячейки RAM с адресом 0060Н в ячейку RAM с адресом 0061Н. Воспользуемся двумя приведенными выше примерами.
lds r0, $0060 ; загрузка в регистр R0 содержимого ячейки памяти RAM
; с адресом 0060Н
sts $0061, r0 ; сохранение содержимого регистра R0 в ячейке памяти
; RAM с адресом 0061Н
Организация массивов во внутренней оперативной памяти данных RAM:
При организации массивов используется косвенная адресация. Адрес ячейки памяти, с которой будет осуществляться обмен данными, находится в регистровой паре. Шестнадцатиразрядный адрес ячейки памяти разбивается на старшую и младшую части. Старшая часть адреса хранится в старшем регистре, а младшая часть – в младшем регистре. Для этого реализованы три регистровые пары: X, Y, Z. Регистровая пара образуется двумя специально выделенными регистрами:
X – R27: R26, (R27 – старший, R26 – младший);
Y – R29: R28, (R29 – старший, R28 – младший);
Z – R31: R30, (R31 – старший, R30 – младший).
Рассмотрим команды для операций чтения-записи оперативной памяти RAM с косвенной адресацией. Для указанных выше регистровых пар существует группа команд типа LD и ST. В качестве примера приведем команды для регистровой пары X – R27:R26, (R27 – старший, R26 – младший). Знак «+» обозначает операцию «пост инкремент» – увеличение адреса после операции, знак «–» обозначает операцию «пре декремент» – уменьшение адреса до операции.
ld r16, X ; загрузить в регистр R16 содержимое ячейки памяти
; с адресом, хранящимся в регистровой паре X (R27:R26)
ld r16, X+ ; загрузить в регистр R16 содержимое ячейки памяти
; с адресом, хранящимся в регистровой паре X (R27:R26)
; и увеличить содержимое регистровой пары на один
ld r16, -X ; уменьшить содержимое регистровой пары X (R27: R26)
; и загрузить в регистр R16 содержимое ячейки памяти
; с адресом, хранящимся в регистровой паре X (R27:R26)
st X, r16 ; сохранить в ячейке памяти с адресом, хранящимся в
; регистровой паре X (R27:R26), содержимое регистра R16
st X+, r16 ; сохранить в ячейке памяти с адресом, хранящимся в
; регистровой паре X (R27:R26), содержимое регистра R16
; и увеличить содержимое регистровой пары на один
st -X, r16 ; уменьшить содержимое регистровой пары X (R27:R26)
; сохранить в ячейке памяти с адресом, хранящимся в
; регистровой паре X (R27:R26), содержимое регистра R16
Пример: «Заполнение массива константой»:
Пояснение к приведенному ниже примеру: в оперативной памяти данных RAM создается массив из шестнадцати ячеек памяти; количество использованных ячеек памяти задается в регистре R20; начальный адрес массива (базовый адрес) определяется парой регистров R27:R26 (регистровая пара Х). В регистр R16 записывается начальная константа, которая затем на каждом цикле увеличивается на единицу. Таким образом, данный фрагмент программы заполняет оперативную память, начиная с ячейки 0100H константами 0, 1, 2….14, 15.
MASSIV: wdr ; начало программного модуля и сброс сторожевого таймера
ldi r16, $00 ; начальная константа
ldi r27, $01 ; старшая часть базового адреса RAM
ldi r26, $00 ; младшая часть базового адреса RAM
ldi r20, $10 ; счетчик циклов
MASSIV_0: wdr ; начало внутреннего цикла и сброс сторожевого таймера
st X+, r16 ; запомнить содержимое регистра R16 в ячейке RAM,
; адрес которой находится в регистровой паре X(R27: R26)
inc r16 ; увеличить на единицу константу
dec r20 ; уменьшить на единицу счетчик циклов
brne MASSIV_0 ; проверка на ноль (флаг Z) содержимого счетчика циклов
; и переход, если не ноль (флаг Z – сброшен)
nop ; выход из процедуры
Пример: «Копирование одного массива в другой»:
В данном примере производится копирование шестнадцати ячеек памяти RAM, начиная с адреса 0060Н в ячейки с адреса 0100H.
COPY_RAM: wdr ; начало программного модуля и сброс
; сторожевого таймера
ldi r27, $00 ; старшая часть адреса источника (пары Х)
ldi r26, $60 ; младшая часть адреса источника (пары Х)
ldi r29, $01 ; старшая часть адреса приемника (пары Y)
ldi r28, $00 ; младшая часть адреса приемника (пары Y)
ldi r20, $10 ; счетчик циклов
COPY_RAM_0: wdr ; начало внутреннего цикла и сброс
; сторожевого таймера
ld r16, Y+ ; загрузить в регистр R16 содержимое ячейки
; источника RAM, адрес которой находится
; в регистровой; паре Y (R29:R28)
st X+, r16 ; запомнить содержимое регистра R16 в ячейке RAM,
; адрес которой находится в регистровой
; паре X(R27:R26)
dec r20 ; уменьшить на единицу счетчик циклов
brne COPY_RAM_0 ; проверка на ноль (флаг Z) содержимого счетчика
; циклов и переход если не ноль (флаг Z – сброшен)
nop ; выход из процедуры
Использование памяти программ для хранения констант и текстовых сообщений:
Память программ может содержать константы, которые могут являться или числовыми данными, или текстовыми сообщениями. Загружаются эти константы при помощи директив «.db» и «.dw». Подробно они рассмотрены в разделе, посвященном директивам.
В приведенном ниже примере в памяти программ хранится текстовое сообщение “Hello World”. В следующей после текстового сообщения ячейке размещена константа «0», которая обозначает конец сообщения. Программа извлекает из памяти программ символы и выводит их в PORTB. После этого проверяет, есть ли конец сообщения. Если находит «0» (конец сообщения), происходит выход из программы, если не «0», то происходит переход на повтор указанных выше действий.
Пример:
; Приведенный ниже фрагмент программы загружает адрес «сообщения» в
; регистр Z. Умножает адрес слова на 2, чтобы получить адреса байта, и ; использует функции high() и low(), чтобы вычислить старший и младший ; байт адреса
ldi ZH, high(2*message) ; загрузка старшей части адреса байта в ZH
ldi ZL, low(2*message) ; загрузка старшей части адреса байта в ZH
LOAD_BYTE: wdr ; метка начала цикла и сброс сторожевого таймера
lpm ; загрузка байта из памяти программ в регистр R0
tst r0 ; проверка, достигли ли мы конца сообщения
breq quit ; если ноль, то выход из процедуры
out PORTB, r0 ; вывод в PORTB символа
adiw ZL,1 ; инкремент адреса ячеек памяти программ,
; где хранятся сообщения
rjmp LOAD_BYTE
quit: rjmp quit ; бесконечный цикл (конец процедуры)
message: ; метка начала сообщения
.db “Hello World” ; текст сообщения
.db 0 ; константа «0» (конец сообщения)