This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Switch branches/tags
Branches Tags
Could not load branches
Nothing to show
Could not load tags
Nothing to show
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Cancel Create
- Local
- Codespaces
HTTPS GitHub CLI
Use Git or checkout with SVN using the web URL.
Work fast with our official CLI. Learn more about the CLI.
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Лекция 4. Архитектура AVR. Ассемблер
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
Latest commit
Git stats
Files
Failed to load latest commit information.
Latest commit message
Commit time
November 10, 2020 12:02
April 13, 2021 19:11
December 6, 2020 18:20
March 11, 2021 02:05
November 6, 2020 19:13
README.md
Конспект по AVR ASM
Для удобного просмотра таблицы команд разверните файл
3 вида памяти в AVR:
1) Flash 16-bits — память программ
Не очень много памяти. 0x0000 — 0xFFFF
2) SRAM 8-bits — Static Random access memory
Лучше хранит данные, без необходимости их обновлять. 0x0000 — 0xFFFF
- 0x0000 — 0x001F — POH
- 0x0020 — 0x005F — I/O (все данные подключенные к процессору, настройки переферии)
- 0x0060 — RAMEND — Внутренняя SRAM (хранить что хочется)
- RAMEND+1 — 0xFFFF — Внешняя SRAM
Всегда 32 регистра. В каждом по байту.
Адресация:
2-х битные могут объединяться по парам (X, Y, Z):
- X — 26, 27
- Y — 28, 29
- Z — 30, 31
Данные между включениями и выключениями
SP (Stack Pointer) — указатель стека, указывает на начало стека. Стек растет в верх, указатель стека вниз. При добавлении элемента указатель стека уменьшается
SPH , SPL – Если у МК больше 256 байт памяти для адресации стека требуется 16 бит
.dseg — Данные после запуска МК
.cseg — Сегмент кода
RESET — Код если МК сломан
SREG — Регистр статуса
Список флагов:
C | Carry | Флаг переноса |
Z | Zero | Флаг нулевого значения |
N | Negative | Флаг отрицательного значения (Старший бит после операции) |
V | Two’s complement overflow indictor | Флаг указатель переполнения (Переполнение перешло в знак) |
S | Signed Test | Флаг для проверок со знаком (S = V xor N) |
H | Half Carry | Флаг полупереноса (Переполнение 4 бита) |
T | Transfer bit | Флаг переноски (Используется как временный флаг) |
I | Interrupt Enable Flag | Флаг разрешения глобального прерывания |
AVR Ассемблер. Урок 1. Вводный. AVR Assembler. Lesson 1. Promo.
Синтаксис команд:
Список команд:
NOP | NOP | 0000 0000 | 0000 0000 | 1 | Ничего не делать | ||
MOV | MOV Rd, Rr | 0010 11rd | dddd rrrr | 1 | Копировать регистр | ||
LDI | LDI Rd, K | 1110 KKKK | dddd KKKK | 16 | 1 | Загрузить значение в регистр | |
ADD | ADD Rd, Rr | 0000 11rd | dddd rrrr | 1 | H+S+V+N+Z+C+ | Сложить без переноса | |
INC | INC Rd | 1001 010d | dddd 0011 | 1 | S+V+N+Z+ | Инкрементировать | |
DEC | DEC Rd | 1001 010d | dddd 1010 | 1 | S+V+N+Z+ | Декрементировать | |
SUB | SUB Rd, Rr | 0001 10rd | dddd rrrr | 1 | H+S+V+N+Z+C+ | Вычесть без переноса | |
SUBI | SUBI Rd, K | 0101 KKKK | dddd KKKK | 16 | 1 | H+S+V+N+Z+C+ | Вычесть значение из регистра |
ADIW | ADIW Rdl, K | 1001 0110 | KKdd KKKK | dl (X, Y, Z), 0 | 2 | S+V+N+Z+C+ | Сложить значение с парой регистров (16 бит) |
SBIW | SBIW RDl, K | 1001 0111 | KKdd KKKK | dl (X, Y, Z), 0 | 2 | S+V+N+Z+C+ | Вычесть значение из пары регистров |
ADC | ADC Rd, Rr | 0001 11rd | dddd rrrr | 1 | H+S+V+N+Z+C+ | Сложение двух регистров и содержимого флага переноса | |
SBC | SBC Rd, Rr | 0000 10rd | dddd rrrr | 1 | H+S+V+N+Z+C+ | Вычитание содержимого регистра и содержимого флага переноса (С) | |
SBCI | SBCI Rd, K | 0100 KKKK | dddd KKKK | 1 | H+S+V+N+Z+C+ | Вычитание константы и содержимого флага переноса | |
СOM | COM Rd | 1001 010d | dddd 0000 | 1 | S+V0N+Z+C1 | Перевод в обратный код | |
NEG | NEG Rd | 1001 010d | dddd 0001 | 1 | H+S+V+N+Z+C+ | Перевод в дополнительный код(Значение 0x80 не изменно) | |
AND | AND Rd, Rr | 0010 00rd | dddd rrrr | 1 | S+V0N+Z+ | Логические И между двумя регистрами | |
ANDI | ANDI Rd, K | 0111 KKKK | dddd KKKK | 16 | 1 | S+V0N+Z+ | Логическое И между регистром и значением |
OR | OR Rd, Rr | 0010 10rd | dddd rrrr | 1 | S+V0N+Z+ | Логическое ИЛИ между двумя регистрами | |
ORI | ORI Rd, K | 0110 KKKK | dddd KKKK | 16 | 1 | S+V0N+Z+ | Логическое ИЛИ между ригистром и значением |
EOR | EOR Rd, Rr | 0010 01rd | dddd rrrr | 1 | S+V0N+Z+ | Исключающее ИЛИ между двумя регистрами | |
LSL | LSL Rd | 0000 11dd | dddd dddd | 1 | H+S+V+N+Z+C+ | Логический сдвинуть влево (7-й бит выгружается во флаг переноса (C)) | |
LSR | LSR Rd | 1001 010d | dddd 0110 | 1 | S+V+N0Z+C+ | Логически сдвинуть вправо | |
ROL | ROL Rd | 0001 11dd | dddd dddd | 1 | H+S+V+N+Z+C+ | Логически сдвинуть влево через перенос (Флаг переноса (С) смещается на место бита 0 регистра Rd. Бит 7 смещается во флаг переноса (С)) | |
ROR | ROR Rd | 1001 010d | dddd 0111 | 1 | S+V+N+Z+C+ | Логически сдвинуть вправо через перенос (Флаг переноса (С) на место 0-го бита, 7-й бит смещается во флаг переноса (С)) | |
ASR | ASR Rd | 1001 010d | dddd 0101 | 1 | S+V+N+Z+C+ | Арифметически сдвинуть вправо (Флаг переноса (С) смещается на место 7-го бита, 0-й бит выгружается во флаг переноса (С)) | |
SEP | SEP Rd | 1110 1111 | dddd 1111 | 16 | 1 | Включить все биты регистра | |
CLR | CLR Rd | 0010 01dd | dddd dddd | 1 | S0V0N0Z1 | Очистить регистр | |
RJMP | RJMP k | 1100 kkkk | kkkk kkkk | -2K≤k≤2K | 2 | Перейти относительно | |
JMP | JMP k | 1001 010k | kkkk 110k | kkkk kkkk | kkkk kkkk | 3 | Перейти абсолютно | ||
BRCC | BRCC k | 1111 01kk | kkkk k000 | -64 | 1 or 2 | Перейти если флаг С очищен | |
BRCS | BRCS k | 1111 00kk | kkkk k000 | -64 | 1 or 2 | Перейти если флаг С установлен | |
BRNE | BRNE k | 1111 01kk | kkkk k001 | -64 | 1 or 2 | Перейти если флаг Z очищен | |
BREQ | BREQ k | 1111 00kk | kkkk k001 | -64 | 1 or 2 | Перейти если флаг Z установлен | |
CP | CP Rd, Rr | 0001 01rd | dddd rrrr | 1 | H+S+V+N+Z+C+ | Сравнить без переноса | |
CPC | CPC Rd, Rr | 0000 01rd | dddd rrrr | 1 | H+S+V+N+Z+C+ | Сравнить с учетом переноса | |
CPI | CPI Rd, K | 0011 KKKK | dddd KKKK | 16 | 1 | H+S+V+N+Z+C+ | Сравнить с константой |
CPSE | CPSE Rd, Rr | 0001 00rd | dddd rrrr | 1or2or3 | H+S+V+N+Z+C+ | Сравнить и пропустить если равно | |
OUT | OUT P, Rr | 1011 1PPr | rrrr PPPP | 1 | Записать данные из регистра в порт I/O | ||
PUSH | PUSH Rd | 1001 001d | dddd 1111 | 2 | Загрузить регистр в стек | ||
POP | POP Rd | 1001 000d | dddd 1111 | 2 | Загрузить значение из стека в регистр | ||
IN | IN Rd, P | 1011 0PPd | dddd PPPP | 1 | Загрузить данные из порта I/O в регистр | ||
RCALL | RCALL k | 1101 kkkk | kkkk kkkk | 3 | Вызов процедуры находящейся по адресу удаленному от текущему PC на k. Адрес возврата в стеке | ||
CALL | CALL k | 1001 010k | kkkk 111k | kkkk kkkk kkkk kkkk | 4 | Вызов процедуры находящейся по адресу k. Адрес возврата в стеке | ||
RET | RET | 1001 0101 | 0XX0 1000 | 4 | Возвращает из подпрограммы. Адрес возврата берется из стека | ||
STS | STS k, Rr | 1001 001d | dddd 0000 | kkkk kkkk kkkk kkkk | 3 | Загрузить значение в SRAM | ||
LDS | LDS Rd, k | 1001 000d | dddd 0000 | kkkk kkkk kkkk kkkk | 3 | Выгрузить значение из SRAM | ||
ST | ST X(X+)(-X), Rr | 1001 001r | rrrr 1100(01)(10) | 2 | Записать из регистра в SRAM | ||
SEI | SEI | 1001 0100 | 0111 1000 | 1 | I1 | Установить флаг глобального прерывания | |
CLI | CLI | 1001 0100 | 1111 1000 | 1 | I0 | Очистить флаг глобального прерывания |
LDI R16, 5 LDI R18, 5 NEG R18 ADD R16, R18 ; 5 + (-5)
- Swap двух регистров без временного регистра:
A = A xor B B = A xor B A = A xor B
- Загрузить старшие биты числа 1234 в регистр R28, а в регистр R29 записать младшие биты числа 1234:
Источник: github.com
Теория
Память программ микроконтроллеров AVR, помимо своего прямого предназначения может быть использована для хранения константных данных. Для этого в AVR ассемблере есть специальные директивы .db и .dw. Первая определяет константный байт или несколько байтов, вторая константное слово или несколько слов. (слово — это 2 байта). Для того чтобы указать, что заданные константы расположены во flash памяти микроконтроллера, используется директива .cseg
Пример:
.cseg //эта директива определяет начало сегмента памяти программ
// данные следующие после нее будут размещены во flash
//массив байтов
key1: .db 12,66,7,19,26
//строка
text: .db “hi gringo”
//массив шестнадцатиразрядных слов
key2: .dw 0xff12, 0x0134, 0x3056, 0x01ff
//массив указателей на подпрограммы
func: .dw Led1On, Led1Off, Led2On, Led2Off
С помощью этих директив можно организовывать во flash памяти массивы данных, массивы указателей на подпрограммы, многомерные массивы, а также структуры и массивы структур. Массив — это набор данных одного типа последовательно размещенных в памяти, тогда как структура – это набор данных разного типа. Конечно, тип данных, массив, структура — термины языков высокого уровня, в частности Си. В ассемблере никаких типов данных нет. Просто эту терминологию удобно использовать, чтобы как-то разграничить обычные данные и указатели на подпрограммы.
Память программ микроконтроллеров AVR имеет 16-разрядную организацию. Доступ к данным в памяти программ осуществляется с помощью команды lpm и индексного регистра Z. При этом старшие 15 разрядов содержимого регистра определяют адрес 16-разрядного слова, а младший разряд определяет, какой из байтов слова будет прочитан: 0 – младший байт, 1 – старший байт.
Пример использования инструкции lpm:
.cseg
key1: .db 12,66,7,19,26
…
ldi ZH, High(key1<<1) //инициализируем индексный регистр
ldi ZL, Low(key1<<1) //адресом метки key1
lpm r16, Z+ //считываем в r16 первый байт массива – 12
//значение Z увеличивается на единицу
lpm r16, Z //считывем в r16 второй байт массива — 66
…
Итак, мы можем хранить во flash памяти данные и имеем к ним доступ. Важно четко понимать, как это происходит.
Практика
Я долго думал как изложить этот материал. Привести сначала абстрактный пример, а потом рабочий, как в статье про switch или сразу разбирать что-то конкретное. В конце концов остановился на втором варианте. Сам по себе знаю как трудно пробираться через дебри чужого ассемблерного кода, но что поделать. Напрягитесь и не будете разочарованы.
Вообщем рассматривать меню на таблицах будем, используя предыдущий проект. Структура меню, функции кнопок и вся аппаратная часть осталась без изменений. В самом проекте изменились только 2 файла:
Menu1.asm – главный файл проекта
Menu.asm – файл содержащий обработчик кнопок и таблицы.
Новый проект можно сказать здесь.
Программа, реализующая меню на switch`е, состояла из двух отдельных частей – диспетчера и четырех обработчиков. Диспетчер был построен с помощью инструкции косвенного перехода ijmp. А основу обработчиков составляли ветви switch`а построенные на макросе Case. Ему “передавались” три параметра – текущее состояние(где находимся сейчас), следующее состояние(куда перейти потом), имя подпрограммы. Ветви switch`а были зеркальным отражением таблицы, которую я приводил в предыдущей статье.
В новом проекте диспетчер и обработчик нажатия кнопки совмещены в одной подпрограмме под названием HandlerMenu, а логика переходов по меню расписана в таблицах во флэш памяти микроконтроллера.
Таблицы
У нас есть четыре кнопки – Up, Down, Enter, Cancel. Для каждой кнопки во flash памяти микроконтроллера существует своя таблица. Строки таблиц определены с помощью директивы .db и имеют следующую структуру.
(где находимся сейчас)
(куда перейти потом)
.equ End = 255
HUpButton:
.db 1,1, Low(Empty), High(Empty)
.db End
где HUpButton – метка, название таблицы,
Low(Empty) – младший байт указателя на подпрограмму Empty,
High(Empty) – старший байт указателя на подпрограмму Empty,
End – маркер конца таблицы.
Запись вида Low(Empty), High(Empty) довольно неудобна, поэтому я изменил ее с помощью директивы препроцессора define. Благо AVR ассемблер поддерживает такие директивы
#define func(x) Low(x),High(x)
Теперь таблицу для кнопки Up можно записать в таком виде
#define func(x) Low(x),High(x)
.equ End = 255
HUpButton:
.db 1,1, func(Empty)
.db 2,1, func(lcdSelectLed1)
.db 3,3, func(Empty)
.db 4,3, func(lcdSelectLed1)
.db 5,5, func(Empty)
.db 6,5, func(lcdSelectLed1)
.db End
Довольно наглядная запись. (Кстати, это чистой воды массив Си структур).
Еще во flash памяти микроконтроллера есть массив, в котором содержатся указатели на таблицы кнопок. Он определен с помощью директивы .dw
Handlers:
.dw HUpButton, HDownButton, HEnterButton, HCancelButton
Обработчик
Обработчик имеет следующий вид.
HandlerMenu:
//вычисляем указатель на таблицу
dec r16
clr zl
clr zh
mov zl,R16
lsl zl
subi zl, LOW(-(Handlers <<1))
sbci zh, HIGH(-(Handlers <<1))
lpm R16, Z+
lpm R17, Z
movw ZH:ZL, R17:R16
lsl zl
rol zh
//ищем нужную строку таблицы
lds r19, pCurrentState
CheckState:
lpm r16, z
cp r16, r19
breq ChangeState
cpi r16, End
breq ExitHM
adiw Z, 4
rjmp CheckState
//если нашли – то меняем текущее состояние
//и вызываем подпрограмму
ChangeState:
adiw Z, 1
lpm r16, Z+
sts pCurrentState, r16
lpm R16, Z+
lpm R17, Z
movw ZH:ZL, R17:R16
icall
ExitHM:
ret
Разберем алгоритм его работы
Регистр r16 содержит считанный из ОЗУ номер кнопки. Он помещается туда перед вызовом обработчика HandlerMenu. Номер кнопки используется как смещение для доступа к соответствующему элементу массива Handlers. Кнопки имеют номера от 1 до 4. А смещение имеет диапазон от 0 до 3. (потому что элементы массива нумеруются начиная с нулевого) Итак, мы декрементируем значение r16, очищаем регистровую пару ZH:ZL, загружаем в нее значение r16 и сдвигаем ZL.
dec r16
clr zl
clr zh
mov zl,R16
lsl zl
Теперь нам нужно сложить адрес метки Handlers и смещение. В системе команд микроконтроллера AVR нет команды сложения регистра с константой. Инструкцию adiw не в счет, потому что она не может прибавить к регистру больше 63. Выход из положения следующий. Вычитаем отрицательное значение адреса метки из регистров ZH:ZL.
Минус на минус дает плюс и в итоге получается, что мы на самом деле складываем адрес метки со смещением.
subi zl, LOW(-(Handlers <<1))
sbci zh, HIGH(-(Handlers<<1))
Считываем указатель на нужную таблицу в r16 и r17, копируем в регистровую пару ZH:ZL и сдвигаем влево.
lpm R16, Z+
lpm R17, Z
movw ZH:ZL, R17:R16
lsl zl
rol zh
Понятно? Мы взяли адрес адрес метки Handlers, добавили смещение и считали из памяти программ адрес метки таблицы.
Ок. Теперь переходим к поиску нужной строки таблицы.
Считываем из ОЗУ текущее состояние, а затем в цикле сравниваем его с первым байтом строки таблицы. Если они не равны – проверяем, не дошли ли мы до маркера конца таблицы. Если дошли — выходим из обработчика, если не дошли – прибавляем к регистровой паре ZH:ZL смещение. Здесь то нам и пригодилась команда adiw.
Каждая строка таблицы состоит из четырех байтов, и вся таблица расположена во flash памяти непрерывно. Собственно, поэтому мы и имеем доступ к ее строкам, зная только начальный адрес и значение смещения.
lds r19, pCurrentState
CheckState:
lpm r16, z
cp r16, r19
breq ChangeState
cpi r16, End
breq ExitHM
adiw Z, 4
rjmp CheckState
На метку ChangeState микроконтроллер попадает, если нашел нужную строчку в таблице. Здесь мы считываем второй байт строки, который определяет следующее состояние, и записываем его в ОЗУ по адресу pCurrentState. Далее считываем указатель на подпрограмму (это 3 и 4 байты) и вызываем ее.
ChangeState:
adiw Z, 1
lpm r16, Z+
sts pCurrentState, r16
lpm R16, Z+
lpm R17, Z
movw ZH:ZL, R17:R16
icall
Источник: chipenable.ru
Примеры программ avr ассемблер
Написание макросов в ассемблере
12.02.2011 3 Mins Read
При освоении языка программирования, Ассемблер, в описании инструментов этого языка, упоминается такой элемент, как — Макрос. Зачем он нужен и какая от него польза? Чтобы реально это понять, вероятно, лучше это показать на конкретном примере, конкретной программы.
Работа с энергонезависимой памятью EEPROM
22.03.2010 1 Min Read
Кроме FLASH-памяти программ и оперативной памяти RAM в микроконтроллере есть другой вид памяти — EEPROM. В отличии от RAM-памяти данные…
Регистры и порты микроконтроллера AVR
02.02.2009 1 Min Read
Одним из самых важных аспектов программирования микроконтроллеров является работа с регистрами и портами. У микроконтроллеров серии AVR несколько регистров ввода/вывода…
Основные ассемблерные команды микроконтроллеров AVR
02.02.2009 1 Min Read
Для изучения азов программирования микроконтроллеров AVR на ассемблере AVR Studio необходимо понимать значения ассемблерных мнемоник. В новейших микроконтроллерах AVR семейства…
Пример работы ШИМ и АЦП на ассемблере AVRStudio
02.02.2008 1 Min Read
Простенькая программка иллюстрирует работу ШИМ в режиме Fast PWM таймера Timer1 и АЦП. Написана на ассемблере, компилируется в AVR Studio.…
Моя первая программа на ассемблере
06.01.2008 1 Min Read
Напишем простенькую программку для микроконтроллера. Рассмотрим пример простой программы «мигалка», написанной на ассемблере для микроконтроллера ATtiny2313. Разобравшись с этой программой Вы сможете писать собственные!
Уроки ассемблера для микроконтроллеров AVR
28.12.2007 1 Min Read
Занятия с Радиодедом помогут Вам разобраться с программированием микроконтроллеров AVR на ассемблере. Самоучитель состоит из лекций, читать желательно попорядку. Изложены азы:…
Создание проекта в AVR Studio
28.12.2007 1 Min Read
Надеюсь, Вы уже установили AVR Studio. Приступим к созданию простенького проекта. Рассмотрим процесс создания, компиляции и симуляции программы для микроконтроллера.
Разделы
- Полезные советы
- Программирование микроконтроллеров
- Проекты на Arduino
- Схемы на микроконтроллерах
Источник: radioded.ru