Технический Портал Левши Понедельник, 18.12.2017, 17:40
| RSS
Меню сайта

Категории раздела
ПИТАНИЕ [17]
МЕТАЛЛО ДЕТЕКТОРЫ [1]
СВЯЗЬ [5]
НАЧИНАЮЩИМ [6]
МикроКонтроллеры [15]
АВТОМАТИКА [4]
АУДИО. [2]
СИСТЕМЫ ОХРАНЫ И БЕЗОПАСНОСТИ [6]
СИСТЕМЫ ДЛЯ САМООБОРОНЫ [1]
ТЕЛЕФОНИЯ [7]

Мини-чат

Главная » Статьи » РАДИОЭЛЕКТРОНИКА » МикроКонтроллеры

Введение в ассемблер ATMEL-AVR микропроцессоров (часть 2)
Введение в ассемблер ATMEL-AVR микропроцессоров
Продолжение

Регистры
Что такое регистры?
Регистр - это участок памяти МК емкостью 8 бит, его структуру можно представить примерно так:

Младший бит имеет номер 0 (два в степени 0 будет 1).
Регистр может хранить числа от 0 до 255 (положительные числа, без отрицательных значений), или числа от -128 до +127 (число вместе со знаковым битом 7), или значение, представляющее ASCII-кодированный символ (например, 'A'). Также может использоваться, как восемь отдельных бит, которые не имеют ничего общего (флаги, используемые в командах выбора да/нет).
Специальные названия регистров, соответствующие отдельным участкам памяти, служат для:
- прямого использования в ассемблерных командах,
- действия с их содержимым требуют только одной команды,
- они напрямую соединяются с центральным процессорным устройством и называются аккумуляторы,
- они служат источником данных и приемником для результатов вычислений.
AVR МК имеет 32 регистра. Они именуются R0..R31, также им можно присвоить осмысленные имена, используя ассемблерную директиву. Например:

.DEF MyRegister = R16

Ассемблерные директивы всегда начинаются с точки в первом столбике текста. Они не генерируют никакого исполняемого кода. После определения имени регистра, можно использовать или его новое имя MyRegister ил же стандартное R16. Таким образом, используется несколько больше текста при использовании этого регистра, но мы получаем определенные ассоциации, связанные с его содержимым.
Использование команды

LDI MyRegister, 150

означает, загрузить число 150 непосредственно в регистр R16, LoaD Immediate. Исполнение команды приведет к загрузке фиксированного значения или константы в указанный регистр. В границах одной команды два регистры могут исполнять действие. Наипростейшей командой этого типа есть команда копирования MOV. Она копирует содержимое одного регистра в другой. Например:

.DEF MyRegister = R16
.DEF AnotherRegister = R15
LDI MyRegister, 150
MOV AnotherRegister, MyRegister

Первые строчки этой программы являются директивами, которые определяют новые имена регистров R16 и R15 для ассемблера. Эти строчки не продуцируют никакого кода для МК. Строчки с LDI и MOV генерируют исполняемый код.
ВАЖНОЕ замечание:
Первый регистр всегда является целевым для результатов вычислений - результат будет записан в него!

Группы регистров
Можно подумать так: а почему бы не дать команду например этой:

.DEF AnotherRegister = R15
LDI AnotherRegister, 150

Но она не сработает. Потому что только регистры с R16 по R31 могут быть загружены константой напрямую через команду LDI, а регистры с R0 по R15 нет. Это не очень хороший запрет, но его не возможно не учитывать при программировании AVR. Есть только одно исключение с этого правила - установка регистра в нулевое значение. Команда
CLR MyRegister

Работает для всех регистров.

Кроме команды LDI существуют и другие, на которые действует это запрещение относительно классов регистров:

-ANDI Rx, K; (Logical AND with Immediate) Побитовое логическое И регистра Rx и постоянного значения К,
-CBR Rx, M; (Clear Bits in Register) Стереть биты регистра Rx, определенные постоянной маской М,
-CPI Rx, K; (Compare with Immediate) Сравнить содержимое регистра Rx с постоянным значением K,
-SBCI Rx, K; (Subtract Immediate with Carry) Вычитать константу К и текущее значение флага переноса из содержимого регистра Rx и сохранить результат в регистр Rx,
-SBR Rx, M; (Set Bits in Register) Установить значения битов регистра Rx, указанных в маске М, равными 1,
-SER Rx; (Set all bits in Register) Установить все биты регистра Rx, равными 1 (соотв. LDI Rx, 255),
-SUBI Rx, K; (Subtract Immediate) Вычитать константу К из содержимого регистра Rx и сохранить результат в регистр Rx.

В этих командах регистры должны использоваться только с R16 по R31 (Верхняя группа). Если вы будете использовать эти команды в своих будущих проектах, используйте один из этих регистров. Это упростит программу. Также это является дополнительным основанием для использования директивы определения имени регистра, потому что впоследствии так проще изменять распределение регистров.

Регистры указатели

Для пар регистров R26:R27, R28:R29 и R30:R31 предопределена дополнительная специальная возможность. Эта возможность есть настолько важная, что эти пары регистров в AVR ассемблере имеют дополнительные имена - X, Y и Z. Пары являются 16-битными регистрами-указателями, и они позволяют указывать на адреса с 16-битным значением в SRAM (X, Y и Z) или в памяти программ (Z).
Младший байт 16-битного адреса помещается в нижнем регистре, старший в верхнем. Все пары имеют свои собственные имена, то есть верхний байт указателя Z называется ZH (=R31), нижний - ZL (=R30). Эти имена определены в стандартных файлах-заголовках для каждого МК. Деление имен шестнадцатибитных регистров-указателей на две части происходит следующим образом:
.EQU Adress = RAMEND; RAMEND - наибольший 16-битный адрес в SRAM

LDI YH, HIGH(Adress); Установка старшего байта
LDI YL, LOW(Adress); Установка младшего байта
Адресация через указатели программируется с помощью специально разработанных команд. Доступ для чтения именуется LD (LoaD), доступ для записи - ST (STore) через Х-указатель

Указатель Исполняемые действия Пример
X Чтение/запись по адресу Х без изменений указателя LD R1, X
ST X, R1
X+ Чтение/запись по адресу Х с последующим увеличением указателя на 1 LD R1, X+
ST X+, R1
-X Уменьшение указателя на 1 с последующим чтением/записью по новому адресу LD R1, -X
ST -X, R1

Таким же образом можно использовать указатели Y и Z.

Доступ к памяти программ может осуществляться только командой чтения (запись запрещена, она может быть только при самопрограммировании). Как указатель она использует пару Z и называется LPM (Load from Program Memory). Команда копирует байт из памяти программ по адресу, указанному в Z в регистр R0. Так как память программ организованна по словах (одна команда на один адрес содержит 16 бит или два байта или одно слово), младший бит указывает на старший или младший байт (0-младший, 1-старший). Поэтому, начальный адрес должен быть умножен на 2 и доступ ограничен до 15 битного адреса или 32кб памяти программ. Например:

LDI ZH, High(2*Adress)
LDI ZL, Low(2*Adress)
LPM

Следующей командой адрес должен быть увеличен, чтобы указывать на следующий байт памяти программ. Для этого есть специальная команда увеличения указателя:

ADIW ZL, 1
LPM

ADIW (Add Immediate to Word) значит - прибавить константу к слову. Таким образом, к регистру-указателю можно прибавить максимум число 63. Обратите внимание, что ассемблер требует нижний регистр ZL пары как первый параметр. Это слегка сбивает с толку при проведении 16-битной операции.
Соответствующей командой, вычитающей постоянное значение от 0 до 63 с 16-битного указателя, является SBIW, (Subtract Immediate from Word) - вычитать константу из слова. ADIW и SBIW в качестве параметров используют имена регистров-указателей X, Y и Z или пару R25:R24. Они не могут работать с другими именами регистров или с памятью данных (SRAM) или с памятью программ. Для 16-битных значений наилучше использовать пару R25:R24.
Запись таблицы значений в память программ осуществляется ассемблерной директивой .DB и .DW. Этими директивами можно поместить побайтную или пословную таблицу значений. Побайтная организация таблицы выглядит примерно так:

.DB 123, 45, 67, 89 ;Таблица из 4-х байт
.DB "Text message" ;Таблица символов

Всегда нужно чтобы число байт было парным, иначе ассемблер добавит нулевой байт в конце, что иногда может быть нежелательным.
Таблица из слов может выглядеть так:
.DW 12345, 6789 ;Таблица из двух слов
Вместо констант вы можете разместить метки в таблице, например

Label1:
[...]
Lavel2:
[...]
Table:
.DW Label1, Label2 ;побайтная таблица меток

Достаточно специализированным применением регистров-указателей есть доступ к самим регистрам. Регистры размещены в первых 32 байтах адресного пространства МК (адреса с 0х0000 к 0х001F). Этот доступ имеет смысл, если нужно копировать содержимое регистров в SRAM или EEPROM или чтение этих значений для загрузки обратно в регистры. Более общим применением указателей является доступ к таблицам с фиксированными значениями в памяти программ. Например, есть таблица с 10ю разными 16-битными значениями, 5-ый элемент должен быть загружен в R25:R24:

Table:
.DW 0x1234,0x2345,0x3456,0x4567,0x5678 ;Значения таблицы, записанные
.DW 0x6789,0x789A,0x89AB,0x9ABC,0xABCD ;побайтно
...
Read5: LDI ZH, High(Table*2) ;Адрес таблицы загружаем в указатель Z
LDI ZL, Low(Table*2) ;предварительно умноженный на 2 для побайтного
;доступа
ADIW ZL, 10 ;Указываем на 5-ый элемент таблицы
LPM ;Читаем младший байт памяти программ
MOV R24, R0 ;Копируем LSB в 16-битный регистр
ADIW ZL, 1 ;Указываем на старший байт памяти программ
LPM ;Читаем старший байт памяти программ
MOV R25, R0 ;Копируем MSB в 16-битный регистр
Это только пример. Можно вычислить значение указателя Z для любого другого элемента таблицы. Таблица может быть организована как побайтно, так и пословно.

Рекомендации для использования регистров
- Определяйте имена регистров директивой .DEF, никогда не используйте их прямое имя Rx;
- Если вам нужен указатель для доступа, резервируйте для этого регистры с R26 по R31;
- Если вам нужен доступ для чтения из памяти программ таблиц, резервируйте Z (R31:R30) и R0 для этого;
- Если вам нужен доступ к отдельным битам регистров (для флагов), используйте регистры с R16 по R23 для этого.
Пример. Создание нового проекта, чтение таблицы из памяти программ и запись в порт, с подключенными светодиодами.
При запуске VMLab появляется приглашение:

Следующим шагом будет создание проекта - меню Project ->new project
В появившимся окне первым шагом указываем каталог для нового проекта, далее - выбираем МК - Мега16, стандартный ассемблер/линкер (по умолчанию) и в конце жмем кнопку Add this.

После выбора этих опций и нажатия кнопки ОК появляются следующие окна - Окно сообщений, в нем отображается результат всех операций
Окно редактора кода, в нем редактируем код программы
Окно редактора проекта, в нем задаются опции для симулятора.

; ************************************************************
; PROJECT:
; AUTHOR:
; ************************************************************

; Micro + software running
; ------------------------------------------------------------
.MICRO "ATmega16"
.PROGRAM "first.asm"
.TARGET "first.hex"

.TRACE ; Activate micro trace

; Following lines are optional; if not included
; exactly these values are taken by default
; ------------------------------------------------------------
.POWER VDD=5 VSS=0 ; Power nodes
.CLOCK 1meg ; Micro clock
.STORE 250m ; Trace (micro+signals) storage time

; Micro nodes: RESET, AREF, PA0-PA7, PB0-PB7, PC0-PC7, PD0-PD7, ACO, TIM1OVF
; Define here the hardware around the micro
; ------------------------------------------------------------

В тексте проекта комментарии начинаются с символа «;».
Директива .MICRO определяет тип МК, .PROGRAM - имя файла программы, .TARGET – исполняемый файл.
Далее задаем частоту тактирования МК, - указав .CLOCK 4meg
Подключаем светодиоды для симуляции. Схема включения – Анод к VDD (+5V), катод через резистор примерно 300 Ом к выводу МК:
Ставим курсор в конце файла проекта, потом в меню Component выбираем Resistor, жмем клавишу Enter. Получается примерно такое:

; Define here the hardware around the micro
; ------------------------------------------------------------

R[inst_name] node1 node2 value

Вместо [inst_name] пишем номер компонента (например 1), вместо node2 – PB0, вместо value – 300.
Получается что-то вроде – R1 node1 PB0 300.
Далее аналогично ставим R2, R3 … R8. Только узлы node тоже нумеруем от 1 до 8, а выводы порта PORTB от 0 до 7 (к порту B на тестовой плате подключено 8 светодиодов по такой же схеме).
Потом Component -> LED diode, аналогично ставим 8 светодиодов.

Вот что должно выйти:

R1 node1 PB0 300
R2 node2 PB1 300
R3 node3 PB2 300
R4 node4 PB3 300
R5 node5 PB4 300
R6 node6 PB5 300
R7 node7 PB6 300
R8 node8 PB7 300

D1 VDD node1
D2 VDD node2
D3 VDD node3
D4 VDD node4
D5 VDD node5
D6 VDD node6
D7 VDD node7
D8 VDD node8

То есть светодиод Dx подключен между VDD и узлом nodex, а резистор Rx – между nodex и PBx-1.
На этом файл проекта может быть завершен.

Переходим к файлу программы.
Генератор начального кода вставил туда следующее:

; ******************************************************
; BASIC .ASM template file for AVR
; ******************************************************

.include "C:\VMLAB\include\m16def.inc"

; Define here the variables
;
.def temp =r16

; Define here Reset and interrupt vectors, if any
;
reset:
rjmp start
reti ; Addr $01
reti ; Addr $02
reti ; Addr $03
reti ; Addr $04
reti ; Addr $05
reti ; Addr $06 Use 'rjmp myVector'
reti ; Addr $07 to define a interrupt vector
reti ; Addr $08
reti ; Addr $09
reti ; Addr $0A
reti ; Addr $0B This is just an example
reti ; Addr $0C Not all MCUs have the same
reti ; Addr $0D number of interrupt vectors
reti ; Addr $0E
reti ; Addr $0F
reti ; Addr $10

; Program starts here after Reset
;
start:
nop ; Initialize here ports, stack pointer,
nop ; cleanup RAM, etc.
nop ;
nop ;

forever:
nop
nop ; Infinite loop.
nop ; Define your main system
nop ; behaviour here
rjmp forever

Комментарии тоже начинаются из символа «;».
Строчка .include "C:\VMLAB\include\m16def.inc" указывает компилятору путь к файлу с описанием регистров и портов МК. Директива .def temp =r16 определяет имя регистра R16 как temp (для использования как временного регистра).
С метки RESET: программа начинает свою работу. По нулевому адресу будет исполняться команда rjmp START – переход к метке START. (Команда NOP не делает ни чего).
Приступаем к написанию программы. После метки START нужно инициировать указатель стека.

LDI R16, HIGH(RAMEND)
LDI R17, LOW(RAMEND)
OUT SPH, R16
OUT SPL, R17

Эти команды загружают максимальный адрес памяти данных в регистр указатель стека. Так как этот регистр есть 16-битный, то для доступа к нему используются его 8-битные части – SPH и SPL.
Далее проводим инициализацию порта PORTB.

LDI temp, 0xFF ;загружаем в регистр temp число 0xFF
OUT PORTB, temp ;перекидаем его в PORTB
OUT DDRB, temp ;настраиваем PORTB на выход с внутренней подтяжкой

В конце файла помещаем таблицу

Message1: .DB "Hello, World!", 0x00

Нуль в конце необходим для того, чтобы число элементов таблицы было парным, также он может служить как указатель конца таблицы.

Содержимое таблицы будем циклично засвечивать на светодиодах.
Таким образом:
1. Загружаем в указатель Z адрес таблицы Message1,
2. Загружаем элемент таблицы командой LPM в регистр temp с последующим увеличением указателя,
3. Если значение равно 0, мы пришли к концу таблицы, переходим к пункту 1, иначе –
4. Командой COM инвертируем содержимое регистра temp (необходимо в силу того, что светодиоды загораются при подаче на соответствующую ножку низкого уровня),
5. Копируем в PORTB содержимое регистра temp,
6. Переходим к пункту 2.

forever:
LDI ZL, low(2*Message1) ;Загружаем регистр
LDI ZH, high(2*Message1) ;указатель
local_label:
LPM temp, Z+ ;Загружаем элемент таблицы
;в регистр temp с постинкрементом
;указателя
CPI temp, 0x00 ;проверяем равность регистра с нулем
BREQ forever ;если равно, переход к метке forever
MOV R20, temp ;Копируем регистр temp в R20 - на работу программы не влияет,
;нужно для того, чтобы показать, что следующая команда
;инвертирует регистр temp
COM temp ;инвертируем регистр temp
OUT PORTB, temp ;Пишем в порт
jmp local_label ;переход

После того, как программа написана, нажимаем кнопку Build, появляется уведомление о успешной компиляции, тогда можно проводить отладку в пошаговом режиме (кнопкой Step Into или F7).

Рисунок – отладочная сессия
На рисунке видно, что содержимое регистра R20 инверсно относительно регистра temp.

Vm lab проект в  прикрепленном архиве – http://elektron.ucoz.ru/statyi/first.rar

Вопросы, как обычно, на форум - http://elektron.ucoz.ru/forum/9
Продолжение следует

Категория: МикроКонтроллеры | Добавил: ЛЕВША (03.04.2008) | Автор: Vasyl Yefуmchuk
Просмотров: 10898 | Рейтинг: 2.0/1
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск

Статистика

Наш опрос
КАКАЯ ОБЛАСТЬ ЭЛЕКТРОНИКИ ВАС ИНТЕРЕСУЕТ?
Всего ответов: 2470

Друзья сайта
  • Форумы Игростроя
  • CЛОВЕНСК
  • RemEXpert
  • ПАЯЛЬНИК
  • МЕДТЕХНИКА

  • Copyright MyCorp © 2017