Академический Документы
Профессиональный Документы
Культура Документы
1
Микроконтроллеры (далее МК) прочно вошли в нашу жизнь, на просторах интернета можно встретить очень
много интересных схем, которые исполнены на МК. Чего только нельзя собрать на МК: различные
индикаторы, вольтметры, приборы для дома (устройства защиты, коммутации, термометры…),
металлоискатели, разные игрушки, роботы и т.д. перечислять можно очень долго. Первую схему на
микроконтроллере я увидел лет 5-6 назад в журнале радио, и практически сразу же перелистнул страницу,
подумав про себя "все равно не смогу собрать". Действительно, в то время МК для меня были чем то очень
сложным и непонятым устройством, я не представлял как они работают, как их прошивать, и что делать с
ними в случае неправильной прошивки. Но около года назад, я впервые собрал свою первую схему на МК,
это была схема цифрового вольтметра на 7 сегментных индикаторах, и микроконтроллере ATmega8. Так
получилось, что микроконтроллер я купил случайно, когда стоял в отделе радиодеталей, парень передо мной
покупал МК, и я тоже решил купить, и попробовать собрать что-нибудь. В своих статьях я расскажу вам
про микроконтроллеры AVR фирмы ATMEL, научу вас работать с ними, рассмотрим программы для
прошивки, изготовим простой и надежный программатор, рассмотрим процесс прошивки и самое главное
проблемы, которые могут возникнуть и не только у новичков.
Рабочая температура: -55…+125*С
Температура хранения: -65…+150*С
Напряжение на выводе RESET относительно GND: max 13В
Максимальное напряжение питания: 6.0В
Максимальный ток линии ввода/вывода: 40мА
Максимальный ток по линии питания VCC и GND: 200мА
Расположение выводов моделей ATmega 8X
Почти все современные МК имеют возможность внутрисхемного программирования ISP, т.е. если ваш
микроконтроллер запаян на плату, то для того чтобы сменить прошивку нам не придется выпаивать его с
платы.
Иногда еще используют вывода XTAL 1 и XTAL2, на эти вывода цепляется кварц, если МК будет работать от
внешнего генератора, в ATmega 64 и 128 вывода MOSI и MISO не применяются для ISP программирования,
вместо них вывода MOSI подключают к ножке PE0, a MISO к PE1. При соединении микроконтроллера с
программатором, соединяющие провода должны быть как можно короче, а кабель идущий от программатора
на порт LPT так-же не должен быть слишком длинным.
Первые цифры в названии микроконтроллера обозначают объем FLASH ПЗУ в килобайтах, например
ATtiny15 – 1 Кб, ATtiny26 – 2 Кб, AT90S4414 – 4 Кб, Atmega8535 – 8 Кб, ATmega162 – 16Кб, ATmega32 – 32 Кб,
ATmega6450 – 64Кб, Atmega128 – 128Кб.
Иногда встречаются схемы, где применены микроконтроллеры с названиями типа AT90S… это старые
модели микроконтроллеров, некоторые из них можно заменить на современные, например:
AT90S4433 – ATmega8
AT90S8515 – ATmega8515
AT90S8535 – ATmega8535
AT90S2313 – ATtiny2313
ATmega163 – ATmega16
ATmega161 – ATmega162
ATmega323 – ATmega32
ATmega103 – ATmega64/128
ATmega 8 имеет несколько выводов питания, цифровое – VCC, GND и аналоговое – AVCC, GND. В
стандартном включении обе пары выводов соединяют параллельно, т.е. вместе. Микроконтроллеры AVR не
любят повышенного напряжения, если питание выше 6 вольт, то они могут выйти из строя. Я обычно
применяю маломощный стабилизатор напряжения на 5 вольт, КР142ЕН5 или 78L05. Если напряжение
питания слишком низкое, то МК не прошьется, программа будет ругаться и выдавать ошибки (к примеру -24 в
PonyProg).
На этом закончим, пока можете выбрать в интернете понравившуюся схему и изучить ее, можете заодно
сходить и купить нужный микроконтроллер. В следующих частях статьи мы будем собирать простой и
надежный программатор, познакомимся с программами для прошивания и попробуем прошить МК.
На фото, на плате программатора присутствует кварц и конденсаторы. Кварц нужно ставить на 4 мГц,
конденсаторы на 5-15 пФ, два штырька рядом со светодиодами на плате, используются для подачи питания,
5 вольт. Если вы будете собирать переходник, то питание подается с платы переходника, т.к. там стоит
"кренка" на 5 вольт, она защитит микроконтроллер и схему от переполюсовки или превышения напряжения.
К программатору дополнительно нужно будет изготовить плату, на которой будут размещаться панельки под
разные МК, в принципе, можно просто запаивать проводки к нужными выводам МК, как вам удобнее.
Для того, чтобы проверить работает ли наш программатор, подключаем его в порт LPT, кликаем правой
кнопкой мыши на значке "Мой компьютер", в появившемся списке выбираем "диспетчере устройств",
откроется окошко, кликаем обновить конфигурацию оборудования и на программаторе моргнут светодиоды
чтения/записи. Кстати, питание берется от внешнего источника, а не с LPT порта, будьте внимательны.
В первую очередь подключаем программатор в нужный нам порт, только сначала ставим микроконтроллер в
панельку. Питание подавайте после того, как программатор будет подключен к порту. Для того, чтобы
запрограммировать микроконтроллер нам нужны файлы прошивки, файл с расширением .hex, в некоторых
проектах используется еще файл EEPROM .eep. (электрически изменяемая память данных, в нем хранятся
различного рода константы).
И не забываем про фьюзы, если они выставляются, обычно в статьях всегда прикрепляют картинку или
пишут какие фьюзы выставлять.
1) Скачиваем и запускаем программу PonyProg, появится окошко (возможно проиграется звук), нажимаем ОК.
2) Далее в списке микроконтроллеров нужно выбрать наш МК, кликаем меню "Устройство", затем выбираем
AVRmicro -> ATmega8.
3) Далее, нам нужно выбрать файлы прошивки, для этого кликаем Файл -> Открыть файл программы (FLASH)
… Откроется окошко, где нам нужно выбрать наш файл прошивки с расширением .hex.
4) В окошке не забываем в списке выбрать нужное нам расширение файла, иначе файл прошивки не
обнаружится.
После того как откроем файл, окошко будет заполнено непонятными цифрами и буквами. Это
шестнадцатеричное представление прошивки.
5) Теперь точно также нужно выбрать файл прошивки EEPROM с расширением .eep, если конечно он нужен и
прилагается.
Если вы еще не подключили программатор и не подали на него питание, сделайте это. Не забудьте вставить
МК в панельку, не перепутайте положение в панельке, обратите внимание на насечку или кружок на корпусе
МК, оттуда и начинается отсчет ножек.
6) Кликаем Установки -> Настройка оборудования. Все настройки выставляем как на рисунке ниже, затем
нажимаем кнопку "Проверка" – должно выйти окошко "Тест ОК"
Если этого не произошло и вышла окошко с надписью "Тест ОШИБКА"
Кликаем правой кнопкой мыши на значке "Мой компьютер" и выбираем "Диспетчер устройств" или можно
зайти в Панель управления -> Система -> Оборудование -> Диспетчер устройств.
Появится такое окошко, в списке находим Порты (COM и LPT ), кликаем правой кнопкой мыши на нем и
нажимаем "Обновить конфигурацию оборудования"
Затем закрываем окно, и возвращаемся к PonyProg, повторяем пункт 6, нажимаем "Проверка", должно выйти
окошко с надписью "Тест ОК".
7) Нажимаем "ОК", далее переходим в меню Установки -> Калибровка, нажимаем Yes.
Ставим строго те галочки, которые нам нужно, для моего проекта нужно выставлять следующие фьюзы:
BOOTSZ1, BOOTSZ0, BODEN, CKSEL3, CKSEL2. Затем нажимаем ОК. Фьюзы при программировании нужно
выставлять только те, что указано, если запрограммируете ненужные вам фьюзы, МК может залочиться,
разблокировать его потом будет очень сложно, нужен будет более сложный программатор чем наш, будьте
внимательны! Записывать программу в МК можно без выставления фьюзов – заработает затем устройство
или нет, это другой вопрос, если даже и заработает, может очень сильно тормозить, или будет работать
частично, это ничем не грозит, МК лочится только после неправильно выставленных фьюзов. В некоторых
проектах могут использоваться фьюзы, которых может не оказаться в PonyProg, например SPIEN, этот фьюз
отключает возможность внутрисхемного программирования, поэтому во многих программах не показывается
специально, чтобы вы случайно его не установили.
9) Теперь все готово, и можно прошить микроконтроллер, нажимаем Команды -> Записать все. Если вы еще
не передумали, то в окошке которое вышло, нажимаем "Yes", и начнется процесс записи и проверки.
После чего выйдет сообщение об успешном завершении операции
Все, теперь можно вынимать МК из программатора и проверить наше устройство, если вы все сделали все
как я расписал, все будет работать, Если по какой то причине устройство не заработало, возможно допущены
ошибки при сборке программатора, перепроверьте все и попробуйте еще раз перепрошить МК.
10) Если спустя какое то время вам захочется сменить прошивку, или использовать МК для другого проекта,
то снова вставляем его в программатор, подключаем тот к ПК, открываем программу, кликаем Команды ->
Стереть, программа сотрет все данные с МК.
Стирать данные совсем не обязательно, при записи новой программы, старая программа сотрется
автоматически, и только потом запишется новая.
Что касается программы PonyProg, в разных версиях свой список устройств, если в вашей версии программы
вы не можете найти в списке МК скажем ATmega32, скачайте другую версию.
Список радиоэлементов
Программатор STK200
Конденсато Поиск в
C1 0.1 мкФ 1
р Utsource
Поиск в
R1, R5-R9 Резистор 33 Ом 6
Utsource
Поиск в
R2-R4 Резистор 510 Ом 3
Utsource
Поиск в
VD1 Светодиод красный 1
Utsource
Поиск в
VD2 Светодиод желтый 1
Utsource
Поиск в
VD3 Светодиод зеленый 1
Utsource
Utsource
Поиск в
XS2 Разъем IDC-10 1
Utsource
Для начала можете микроконтроллер поставить в панельку программатора, и подключить его к LPT порту,
затем подавайте питание.
3) Выбираем нужный нам микроконтроллер из выпадающего меню, я выбрал ATmega8. Если у вас в названии
микроконтроллера после ATmegaX стоит буква, к примеру V или L, то в списке выбирайте такой же МК, с
такой же буквой.
4) Теперь нам нужно открыть файл прошивки, в этом окошке нажимаем File -> Load FLASH
5) Откроется окно, где нужно будет выбрать файл прошивки с расширением .hex, кстати, не забудьте внизу из
выпадающего меню "Тип файлов" выбрать этот тип файла.
6) Файл EEPROM выбираем точно так-же, для этого нажимаем меню File -> Load EEPROM, расширение этого
файла .eep, если к вашему проекту такой файл не прилагается, значит нужно прошивать только FLASH т.е.
.hex.
7) Итак, файлы прошивки мы загрузили, теперь нам нужно выставить фьюзы, для моего проекта фьюзы
следующие: BOOTSZ1, BOOTSZ0, SUT1, CKSEL3, CKSEL2, выставляем их.
8) Затем ставим галочку Program Fuse Bit(s), если вы не поставите галочку – фьюзы не будут записываться.
Чтобы проверить, видит ли наша программа программатор, подключенный к LPT порту, нажимаем кнопку
Reset Chip, на программаторе должны мигнуть светодиоды чтения/записи. Если светодиоды не мигают,
значит нам нужно проделать операции после пункта 6, описанные в предыдущей части статьи.
9) Теперь можно прошить МК, нажимаем кнопку Program All, и начнется процесс прошивки.
Если вы загружали только файл прошивки FLASH, .hex, то по ходу прошивки программа предложит загрузить
файл EEPROM, жмем NO, т.е. НЕТ.
После чего пробегут еще 2 полоски и процесс прошивки завершится
Теперь можете проверить МК, поставив его в панельку вашего устройства. Если вы хотите записать другую
прошивку на этот же МК, новую прошивку можно записать поверх старой, или же стереть сначала старую,
потом записать новую, как вам удобнее, разницы особой нету.
В данной программе можно считать данные FLASH или EEPROM с МК, или просмотреть Lock биты,
установленные Fuse биты. Все это в меню Read (считать).
Если вы случайно запрограммировали какой то фьюз, после чего МК залочился, вспомните, что это был за
фьюз, некоторые МК с неправильно зашитыми фьюзами удается восстановить, есть несколько способов как
это сделать. На К155ЛА3 можно собрать генератор чтобы восстановить МК с запрограммированным
RSTDSBL, если вы выставили фьюзы на работу от внешнего генератора, подавая сигнал на вывод XTAL1
некоторые умудряются таким способом восстановить МК. Также фьюзами можно выставить тактирование от
внешней RC цепочки. В таком случае придется собрать RC цепочку, чтобы опять запустить МК. Ещё есть
фьюзы DWEN, SPIEN..., установив которые, вы отключите возможность пользоваться ISP программатором,
тут поможет только параллельный программатор, другие программаторы (к примеру тритон) или приборы,
которые встречаются на просторах интернета: например этот, или ATmega Fusebit Doctor, данный прибор я
собирал, но почему то он не заработал должным образом, плата все еще валяется где то в ящике, как нибудь
снова надо взяться за него. Честно говоря, у меня до сих пор валяются три залоченных микроконтроллера,
две меги48 и одна мега8, однажды мегу 8ю, которая перестала определяться в программаторе PonyProg
(работает через COM порт) я восстановил собрав программатор STK200. Притом знаете ли, свой первый
купленный микроконтроллер я прошил с первого раза, и он до сих пор работает у меня в лабораторном блоке
питания.
Теги:
AVR
Микроконтроллер
CodeVisionAVR
Код
Команда Описание Действие Циклы Флаги ATtiny ATmega
операции
0000 11rd Z,C,S,
add Rd,Rr Add two Registers Rd←Rd+Rr 1 + +
dddd rrrr N,V,H
Add with Carry two 0001 11rd Z,C,S,
adc Rd,Rr Rd←Rd+Rr+C 1 + +
Registers dddd rrrr N,V,H
1001 0110
Add Immediate to Z,C,S,
adiw Rdl,K Rdh:Rdl←Rdh:Rdl+K 2 KKdd ± +
Word N,V
KKKK
Subtract two 0001 10rd Z,C,S,
sub Rd,Rr Rd←Rd-Rr 1 + +
Registers dddd rrrr N,V,H
sbc Rd,Rr Subtract with Carry Rd←Rd-Rr-C 1 0000 10rd Z,C,S, + +
two Registers dddd rrrr N,V,H
1010
Subtract Constant KKKK Z,C,S,
subi Rd,K Rd←Rd-K 1 + +
from Register dddd N,V,H
KKKK
0100
Subtract with Carry
KKKK Z,C,S,
sbci Rd,K Constant from Rd←Rd-K-C 1 + +
dddd N,V,H
Register
KKKK
1001 0111
Subtract Immediate Z,C,S,
sbiw Rdl,K Rdh:Rdl←Rdh:Rdl-K 2 KKdd ± +
from Word N,V
KKKK
Logical AND 0010 00rd
and Rd,Rr Rd←Rd AND Rr 1 + +
Registers dddd rrrr
Z,S,N
0111
Logical AND KKKK
andi Rd,K Rd←Rd AND K 1 + +
Register and Constant dddd
Z,S,N
KKKK
0010 10rd
or Rd,Rr Logical OR Registers Rd←Rd OR Rr 1 + +
dddd rrrr
Z,S,N
0110
Logical OR Register KKKK
ori Rd,K Rd←Rd OR K 1 + +
and Constant dddd
Z,S,N
KKKK
0010 01rd
eor Rd,Rr Rd←Rd EOR Rr 1 + +
Exclusive OR dddd rrrr
Z,S,N
Registers
1001 010d
com Rd One’s complement Rd←0xFF-Rd 1 + +
dddd 0000
Z,S,N
1001 010d Z,C,S,
neg Rd Two’s complement Rd←0x00-Rd 1 + +
dddd 0001 N,V,H
0110
KKKK
sbr Rd,K Set Bit(s) in Register Rd←Rd OR K 1 Z,S,N + +
dddd
KKKK
0111
Clear Bit(s) in Rd←Rd AND (0xFF- KKKK
cbr Rd,K 1 + +
Register K) dddd
Z,S,N
KKKK
1001 010d Z,S,
inc Rd Increment Rd←Rd+1 1 + +
dddd 0011 N,V
1001 010d Z,S,
dec Rd Decrement Rd←Rd-1 1 + +
dddd 1010 N,V
Test for Zero or 0010 00dd
tst Rd Rd←Rd AND Rd 1 + +
Minus dddd dddd
Z,S,N
clr Rd Clear Register Rd←Rd EOR Rd 1 0010 01dd + +
dddd dddd
Z,S,N
1110 1111
ser Rd Set Register Rd←0xFF 1 None + +
dddd 1111
1001 11rd
mul Rd,Rr R1:R0←Rd*Rr 2 Z,C - +
dddd rrrr
Multiply Unsigned
0000 0010
muls Rd,Rr Multiply Signed R1:R0←Rd*Rr 2 Z,C - +
dddd rrrr
Multiply Signed with 0000 0011
mulsu Rd,Rr R1:R0←Rd*Rr 2 Z,C - +
Unsigned 0ddd 0rrr
Fractional Multiply 0000 0011
fmul Rd,Rr R1:R0←(Rd*Rr)<<1 2 Z,C - +
Unsigned 0ddd 1rrr
Fractional Multiply 0000 0011
fmuls Rd,Rr R1:R0←(Rd*Rr)<<1 2 Z,C - +
Signed 1ddd 0rrr
fmulsu Fractional Multiply 0000 0011
R1:R0←(Rd*Rr)<<1 2 Z,C - +
Rd,Rr Signed with Unsigned 1ddd 1rrr
Как видно из табл.1, AVR имеют всего 3 разновидности команды сложения. Инструкции add Rd,Rr (Сложение
двух регистров), adc Rd,Rr (Сложение двух регистров с учётом переноса), позволяют складывать как
однобайтовые числа, так числа произвольной разрядности. В последнем случае для связи байтов
используется флаг переноса C в регистре SREG, который устанавливается всякий раз, когда разрядность
суммы превысит 8 бит. Этот перенос должен быть добавлен к сумме старших байтов командой adc Rd,Rr:
?
1 add R18,R16 ; сложение двухбайтовых чисел
2 adc R19,R17 ; R19:R18 = R19:R18 + R17:R16
Сложение регистра с константой, к сожалению, отсутствует. И это приносит некоторые неудобства, так как
приходится использовать две команды, одна из которых занесение числа во вспомогательный регистр, а уже
вторая - сложение регистров:
?
1 ldi R17,0x30 ; заносим в R17 константу 0x30
2 add R16,R17 ; R16 = R17 + 0x30
Во многих случаях вышеуказанную проблему можно решить, используя команду adiw Rdl,K (Сложение
константы со словом). Она позволяет прибавить 6-разрядную константу, лежащую в диапазоне 0…63, к одной
из 4х регистровых пар Rdh:Rdl (R25:R24, R27:R26, R29:R28, R31:R30). С её помощью также удобно
реализовать 16-разрядный счётчик событий:
?
1 key_press:
2 adiw R24,1 ; инкрементируем счётчик R25:R24
3 sbis PINB,0 ; пока на линии 0 порта PORTB низкий
rjmp key_press ; логический уровень
4
Имеется две команды с прямой адресацией РОН: sub Rd,Rr (Вычитание двух регистров), sbc Rd,Rr
(Вычитание двух регистров с учётом заема). При совместном использовании они позволяют получить
разность чисел любой разрядности. Для связи байт, при этом, и здесь служит флаг C. Но в отличие от
сложения он устанавливается, когда разность оказалась отрицательной(Rd < Rr) и имеет в этом случае
смысл заема, который должен быть вычтен из разности старших байт командой sbc Rd,Rr:
?
1 sub R18,R16 ; вычитание двухбайтовых чисел
2 sbc R19,R17 ; R19:R18 = R19:R18 - R17:R16
Ещё две команды с непосредственной адресацией используются для тех же целей: subi Rd,K (Вычитание
константы из регистра) и sbсi Rd,K (Вычитание константы из регистра c учётом заёма). Эти инструкции очень
универсальны. Их с одинаковым успехом можно применять как в целях вычитания, так и сложения. В
последнем случае необходимо использовать представление числа K в дополнительном коде (т.е. изменить
знак числа –K = 0xFF+1-K):
?
1 subi R16,-0x30 ; R16 = R16 + 0x30 = R16 – (-0x30)
?
1 delay:
2 sbiw R24,1 ; декрементируем счётчик R25:R24 пока
3 brne delay ; его содержимое не станет 0, формируя
. ; задержку времени 4*R25:R24 циклов
4
?
1 eor R16,R17 ; R16 = R16 XOR R17
2 eor R17,R16 ; R17 = R17 XOR (R16 XOR R17) = R16
3 eor R16,R17 ; R16 = (R16 XOR R17) XOR R16 = R17
?
1 in R16,PORTB ;заносим для изменения РВВ PORTB в РОН R16
2 ori R16,(1«PB0)|(1«PB5) ;устанавливаем биты 0,5 в R16
3 andi R16,~((1«PB1)|(1«PB2));сбрасываем биты 1,2 в R16
4 out PORTB,R16 ;выводим изменённое значение R16 в PORTB
5 eor R16,R16 ;обнуляем R16
6 out PORTC,R16 ;заносим 0 в PORTC
Как во многих других случаях, у инструкции ori Rd,K существует другая форма написания sbr Rd,K (Установка
бита(ов) в регистре), числящаяся, однако, как самостоятельная команда. Применяя её необходимо помнить,
что, несмотря на аббревиатуру, на самом деле производится именно операция “Логическое ИЛИ” регистра и
битовой маски, а не установка битов в регистре. Так, например, для установки бита n в регистре Rd
правильно писать sbr Rd,1<
?
1 in R16,PORTB ; заносим для изменения РВВ PORTB в РОН R16
2 sbr R16,(1«PB0)|(1«PB5) ; устанавливаем биты 0,5 в R16
3 cbr R16,(1«PB1)|(1«PB2) ; сбрасываем биты 1,2 в R16
4 out PORTB,R16 ; выводим изменённое значение R16 в PORTB
5 clr R16 ; обнуляем R16
6 out PORTC,R16 ; заносим 0 в PORTC
Когда надо установить все биты регистра, то можно воспользоваться инструкцией ser Rd (Установка
регистра), которая заносит константу 0xFF в регистр Rd. Она является частной формой написания команды
ldi Rd,K (Загрузка константы в регистр), относящейся к группе команд пересылки. Подобное действие можно
осуществить и другими способами. Например, в результате команды ori R16,0xFF все биты регистра R16
также будут установленными.
Но в отличие от ser R16 или, что тоже самое, ldi R16,0xFF - будут изменены значения флагов N и Z, что может
повлиять на правильный ход выполнение программы. По той же причине в ряде случаев для обнуления
регистра лучше использовать ldi R16,0 вместо clr R16 (eor R16,R16); все флаги в регистре SREG останутся
неизменными. Единственное, что мешает повсеместному применению команды ldi Rd,K, это ограниченный
набор РОН с которыми она работает. Только регистры R16…R31 могут использоваться с этой инструкцией.
Это существенное ограничение наложено и на все арифметические и логические команды, которые
используют непосредственную адресацию (действие над регистром и константой).
На практике часто необходимо осуществить проверку регистра на нуль. Проще всего это сделать если
произвести операцию “Логическое И” регистра самим с собой (and Rd,Rd). При этом содержимое Rd
останется неизменным, а в регистре SREG будет установлен флаг нулевого результата Z если Rd = 0.
Параллельно с ним будет переопределён и флаг N, который в случае использования знаковых чисел будет
свидетельствовать об отрицательном содержимом регистра. Для инструкции and Rd,Rn где Rd = Rn, может
быть использована другая форма записи в виде псевдокоманды tst Rd (Проверка на нуль и минус).
В тех случаях, когда необходимо изменить знак однобайтового числа используют команду neg Rd
(Дополнение до двух). Она осуществляет действие Rd←0x00-Rd и используется только для чисел
представленных дополнительном коде.
У AVR имеются также команды прямого и обратного счёта. Первая из них inc Rd (Инкремент) увеличивает на
единицу содержимое регистра, а вторая dec Rd (Декремент), соответственно, уменьшает. Конечно, действия
инкрементирования и декрементирования могут быть заменены, например, на прибавление 1 и вычитание 1
из регистра Rd (subi R16,-1 эквивалентно inc R16, а subi R16,1 тоже, что и dec Rd). Но, несмотря на это обе
команды имеют большое самостоятельное значение. Главная их особенность в том, что они не влияют на
флаг переноса C и тем самым оптимизированы для формирования циклов, в которых он может
использоваться:
?
1 ldi R16,SIZE ; производим сдвиг числа,
2 clc ; находящегося в ОЗУ и состоящего из
3 rleft: ld R17,X ; SIZE байт на один разряд влево
4 rol R17
5 st X+,R18
6 dec R16
7 brne rleft
8 .
Для умножения целых чисел разработаны три команды: mul Rd,Rr (Умножение беззнаковых чисел), muls
Rd,Rr (Умножение знаковых чисел), mulsu Rd,Rr (Умножение знакового числа на беззнаковое).
Знаковые числами должны быть представлены в дополнительном коде. При этом для первых двух команд
безразлично на месте каких операндов будут находиться множитель и множимое. К одному и тому же
результату приведут, например, mul R16,R17 и mul R17,R16 (muls R16,R17 и muls R17,R16). В случае mulsu
Rd,Rr на месте Rd обязательно должно стоять знаковое число. Помимо флага Z (один из операндов 0),
умножение оказывает влияние и на флаг переноса C. В него заносится старший бит регистра R1. Таким
образом, в командах muls Rd,Rr, mulsu Rd,Rr он фактически является флагом знака и устанавливается когда
произведение отрицательное. Необходимо также помнить, что инструкция muls Rd, Rr может использоваться
только с регистрами R16…R31. Ещё меньше поддерживает команда mulsu Rd,Rr, только R16…R23.
Существует ряд специфических приложений, в которых может понадобиться умножения дробных чисел с
фиксированной запятой. Главная задача, где это может быть необходимо, - цифровая обработка сигналов.
Для дробного умножения имеются команды: fmul Rd,Rr (Умножение дробных беззнаковых чисел), fmuls Rd,Rr
(Умножение дробных знаковых чисел), fmulsu Rd,Rr (Умножение дробного знакового числа на беззнаковое).
Все команды работают с числами в формате (1.7). Старший бит регистра является целой частью числа, а
младшие 7 битов отведены под дробную. Произведение приводится к формату (2.14). Для этого
производится сдвиг результата на один разряд влево так, что старший разряд оказывается во флаге C. Все
инструкции дробного умножения работают только с регистрами R16…R23.
Теги:
AVR
Микроконтроллер
Синтаксис ассемблера
Исходный текст программы на языке ассемблера может быть создан в любом текстовом редакторе и
должен быть размещен в файлах с расширениями .asm, .inc или .txt. Удобнее всего для этих целей
использовать встроенный в AVR Studio редактор.
Алфавит ассемблера состоит из набора символов ACSII. Однако в комментариях к программам
допускается использовать любой удобный язык, основанный на наборе символов в кодировке
UNICODE. Изначально ассемблер не чувствителен к регистру символов. Так, например, между тремя
именами переменной TEMP, temp, Temp нет ни какой разницы.
Ассемблер может работать с числами, представленными в одной из четырех системах исчисления:
десятичной (примеры чисел: 10, 255),
шеснадцатиричной (примеры чисел: 0x0A, $FF),
двоичной (примеры чисел: 0b00001010, 0b11111111),
восмиричной (примеры чисел: 012, 0377).
По умолчанию применяется десятичная система, и любое число без префикса будет воспринято
именно как десятичное. Необходимо обратить внимание также на то, что ассемблер AVR не
поддерживает распространенную форму записи 16-тиричных чисел c постфиксом “H” (1AH, 0FFH).
В табл.1 приведен список операторов, доступных для использования в числовых выражениях.
Операторы расположены в порядке убывания приоритета. Как и всегда, очередность следования
арифметических и логических операций можно изменять, применяя круглые скобки.
Табл 1. Арифметические и логические операторы, использующие в числовых выражениях:
Обозначение
Описание Пример использования
оператора
Унарный оператор. Изменяет знак числа, используя
- ldi R16,-3 R16 <- 0xFD
дополнительный код.
ldi R16,x1 * x2 ;R16 <-
* Бинарный оператор. Производит умножение двух чисел.
x1*x2
ldi R16,x1 / x2 ;R16 <-
/ Бинарный оператор. Производит деление двух чисел.
x1/x2
Бинарный оператор. Возвращает целочисленный остаток ldi R16,x1 % x2 ;R16 <-
%
от деления двух чисел. {x1/x2}
ldi R16,!x R16 <- 1 если
Унарный оператор сравнения. Возвращает 1 если число
! x=0
было равно нулю и 0 если число не было равно нулю.
;R16 <- 0 если x ≠ 0
Унарный оператор. Производит инвертирование всех ldi R16,~0xF0 R16 <-
~
битов в числе. 0x0F
ldi R16,x1 + x2 ;R16 <-
+ Бинарный оператор. Производит сложение двух чисел.
x1 + x2
ldi R16,x1 - x2 ;R16 <- x1
- Бинарный оператор. Производит вычитание двух чисел.
- x2
Бинарный оператор. Производит сдвиг выражения
ldi R16,1<<5 R16 <-
<< стоящего слева от оператора на число разрядов, стоящих
0b00100000
справа от оператора. Сдвиг происходит в левую сторону.
Бинарный оператор. Производит сдвиг выражения
стоящего слева от оператора на число разрядов, стоящих ldi R16,0x50>>4 ;R16 <-
>>
справа от оператора. Сдвиг происходит в правую 0b00000101
сторону.
Бинарный оператор сравнения. Возвращает 1 если число, ldi R16,x1 < x2 ;R16 <- 1
< стоящее слева от оператора, меньше числа, стоящего если x1 < x2
справа. В противном случае возвращает 0. ;R16 <- 0 если x1 >=x2
Бинарный оператор сравнения. Возвращает 1 если число, ldi R16,x1 <= x2 ;R16 <-
<= стоящее слева от оператора, меньше или равно числу, 1 если x1 <=x2
стоящему справа. В противном случае возвращает 0. ;R16 <- 0 если x1 > x2
Бинарный оператор сравнения. Возвращает 1 если число, ldi R16,x1 > x2 ;R16 <- 1
> стоящее слева от оператора, больше числа, стоящего если x1 > x2
справа. В противном случае возвращает 0. ;R16 <- 0 если x1 <=x2
Бинарный оператор сравнения. Возвращает 1 если число, ldi R16,x1 >= x2 ;R16 <-
>= стоящее слева от оператора, больше или равно числу, 1 если x1 >=x2
стоящему справа. В противном случае возвращает 0. ;R16 <- 0 если x1 < x2
Бинарный оператор сравнения. Возвращает 1 если числа, ldi R16,x1 == x2 ;R16 <-
== стоящие по обе стороны от оператора, равны. В 1 если x1 = x2
противном случае возвращает 0. ;R16 <- 0 если x1 ≠ x2
Бинарный оператор сравнения. Возвращает 1 если числа, ldi R16,x1 != x2 ;R16 <- 1
!= стоящие по обе стороны от оператора, не равны. В если x1 ≠ x2
противном случае возвращает 0. ;R16 <- 0 если x1 = x2
Бинарный оператор. Производит побитовую операцию ldi R16,x1 | x2 ;R16 <- x1|
&
“Логическое И” между двух чисел. x2
Бинарный оператор. Производит побитовую операцию ldi R16,x1 ^ x2 ;R16 <-
^
“Исключающее ИЛИ” между двух чисел. x1^x2
Бинарный оператор. Производит побитовую операцию ldi R16,x1 & x2 ;R16 <-
|
“Логическое ИЛИ” между двух чисел. x1&x2
ldi R16,x1 && x2 ;R16 <-
Бинарный оператор сравнения. Возвращает 1 если числа,
1 если x1 и x2 ≠ 0
&& стоящие по обе стороны от оператора, не равны нулю. В
;R16 <- 0 если x1 или
противном случае возвращает 0.
x2 = 0
ldi R16,x1 || x2 ;R16 <- 1
Бинарный оператор сравнения. Возвращает 1 если хотя
если x1 или x2 ≠ 0
|| бы одно из чисел, стоящих по обе стороны от оператора,
;R16 <- 0 если x1 и x2 =
не равно нулю. В противном случае возвращает 0.
0
Ассемблер содержит также ряд встроенных функций. Некоторые из них, имеющие наибольшее
практическое значение, приведены в табл.2.
Табл 2. Некоторые встроенные в ассемблер функции:
Обозначение
Описание Пример использования
функции
Функция возвращает младший байт
low() low(0x1234) = 0x34
выражения.
Функция возвращает второй байт high(0x1234) = byte2(0x1234) =
high(), byte2()
выражения. 0x12
Функция возвращает третий байт
byte3() byte3(0xABCD1234) = 0xCD
выражения.
Функция возвращает четвертый байт
byte4() byte4(0xABCD1234) = 0xAB
выражения.
Функция возвращает младшее слово
lwrd() lwrd(0xABCD1234) = 0x1234
выражения.
Функция возвращает второе слово
hwrd() hwrd(0xABCD1234) = 0xABCD
выражения.
Функция возвращает абсолютное значение
abs() abs(-1000) = 1000
выражения.
Операторы арифметических и логических операций, а также символы “;”, “.”, “(”, “)”, “[”, “]”, “{”, “}”, “?” -
зарезервированы компилятором. Они не должны встречаться в обозначениях констант, инструкций и
меток. Кроме этого, пользовательские имена, не могут начинаться с цифр и не должны совпадать с
названиями регистров (R0…R31,X,Y,Z) и встроенными в ассемблер функциями.
Перейти к следующей части: Директивы ассемблера
Директивы ассемблера
Директивы представляют собой команды управления компилятором. Объявление каждой из них
должно начинается с точки. Практика показывает, что в любом ассемблере наиболее интенсивно
используется только порядка 10…20 директив. Все остальные либо не являются обязательными,
либо отвечают за управление, лишь незначительными свойствами компилятора. К “основным”,
характерным и для ассемблеров других процессоров, относятся директивы .equ, .org, .def, .сseg, .dseg
и т.д. Ну, а такие директивы, как .dq, .exit, .listmac в реальных программах встречаются
действительно очень редко. Ниже приведен перечень, описание и примеры использования директив
фирменного ассемблера микроконтроллеров AVR.
Директива .include подставляет текстовый файл в то место программы, где происходит ее
употребление. В дополнении к этому сам файл подстановки также может содержать директиву
.include. Если файл расположен в директории проекта или в одной из служебных папок, то вместо
полного пути, допускается указывать, лишь ссылку на его имя.
Директива .include
Синтаксис написания:
.include "{путь к файлу}"
Пример использования:
?
1 .include "m8def.inc" ;вставка стандартного заголовочного файла
Директива .exit указывает ассемблеру место окончания файла исходного текста. Все операторы,
находящиеся после директивы, становятся невидимыми для компилятора. Если .exit встречается в
подключаемом файле, то сборка проекта заканчивается строкой, где расположена директива .include.
В случае отсутствия директивы .exit, конечной точкой сборки считается последняя строка исходного
текста.
Директива .exit
Синтаксис написания:
.exit
Пример использования:
?
1 .exit ;конец файла
Директивы .nolist и .list служат для управления файлом листинга, который обычно генерируется после
сборки проекта. Первая из них запрещает, а другая, соответственно, разрешает вывод информации в
файл. Директива .list отменяет действие .nolist и наоборот.
Директивы .nolist, .list
Синтаксис написания:
.nolist, .list
Пример использования:
?
1 .nolist ;запретить вывод текста файла “m8def.inc”
2 .include "m8def.inc" ;в файл листинга программы
3 .list ;продолжить вывод информации
Директива .equ присваивает символьному имени некоторое числовое значение. Символьное имя
должно быть уникальным и не может быть изменено в процессе написания программы. Директива не
может применяться для назначения символьных имен регистрам общего назначения.
Директива .equ
Синтаксис написания:
.equ {символьное имя} = {выражение}
Пример использования:
?
1 .equ DDRB = 0x17 ;присвоение имени DDRB значения 0x17
2 .equ PORTB = DDRB + 1 ;присвоение имени PORTB значения 0x18
Директива .set производит то же самое действие, что и .equ. Но в отличии от последней, символьное
имя может быть переопределено в любом месте программы.
Директива .set
Синтаксис написания:
.set {символьное имя} = {выражение}
Пример использования:
?
1 .set OFFSET = 0x100 ;присвоение имени OFFSET значения 0x100
2 .
3 .set OFFSET = OFFSET + 1 ;переопределение значения OFFSET
Директива .def присваивает символьное имя одному из регистров общего назначения. В дальнейшем
ходе программы данное имя может быть отменено директивой .undef.
Директивы .def, .undef
Синтаксис написания:
.def {символьное имя} = {регистр}
.undef {символьное имя}
Пример использования:
?
1 .def temp = R16 ;присвоение регистру R16 имя temp
2 .undef temp ;отмена дальнейшего использования имени temp
Директивы .db, .dw, .dd, .dq предназначены для резервирования памяти микроконтроллера под
инициализированные данные. Все они могут применяться только в сегментах кода и EEPROM-памяти.
Разница между этими директивами заключается в разрядности, представляемых данных.
Директива .db резервирует байты, .dw – слова, .dd – двойные слова. В редких случаях может так же
оказаться удобным использование директивы .dq, резервирующей 64-разрядные данные.
Директивы .db, .dw, .dd, .dq
Синтаксис написания:
{метка}: .db {8-разрядные данные}
{метка}: .dw {16-разрядные данные}
{метка}: .dd {32-разрядные данные}
{метка}: .dq {64-разрядные данные}
Пример использования:
?
1 label:
2 .db 0xFA, 250, -6, 0b11111010
3 .dw 0xFADE, 64222, -1314, 0b1111101011011110
4 .dd 0xFADEEFCA, 4208914378, -86052918
.dq 0xFADEEFCAEFBACDEF, 18077149609196178927, -521103510453211
5
Директива .org позволяет задать компилятору начальный адрес в пределах сегментов кода, данных и
EEPROM-памяти. В случае применения в сегменте кода, директива определяет адрес размещения
16-разрядного слова программ.
Директива .org
Синтаксис написания:
.org {начальный адрес}
Пример использования:
?
1
2 .equ SRAM_START = 0x60
3 .equ RAMEND = 0x045F
4
5 .dseg ;начало сегмента данных
.org SRAM_START ;резервирование 32 байт в SRAM под буфер,
6 buffer: . byte 32 ;начиная с адреса 0x60
7
8 .cseg ;начало сегмента кода
9 .org 0 ;вектор сброса по адресу 0
10 rjmp initial
.
11 .org 0x50 ;начало основной программы с адреса 0x50
12 initial:
13 ldi temp,high(RAMEND) ;инициализация стека
14 out SPH,temp
15 ldi temp,low(RAMEND)
16 out SPL,temp
17 .
18
Директива .listmac разрешает расширенный вывод текста макросов в файле листинга. В этом случае
содержимое каждого макроопределения, встретившегося в программе, отображается целиком. Если
директива не используется, то код в нутрии макроса не приводится.
Директива .listmac
Синтаксис написания:
.listmac
Пример использования:
?
1 .listmac ;разрешить разворачивать текст макросов в файле листинга
Директивы .message, .warning, .error предназначены для вывода в окно сборки проекта
дополнительной информации о ходе компиляции программы. Директива .message генерирует
сообщение для строки, в которой был встречен ее вызов. Применение .warning приводит к выдачи
предупреждения, а .error – к сообщению об ошибки. В последнем случае сборка проекта
прекращается.
Директивы .message, .warning, .error
Синтаксис написания:
.message "{текст сообщение}"
.warning "{текст предупреждения}"
.error "{текст соодщения об ошибки}"
Пример использования:
?
1 .message "Macros has been called here."
2 .warning "Too high frequency!"
3 .error "Wrong macro argument!"
Группа директив условной компиляции .ifdef, .ifndef, .if, .else, elif, .endif используются для вставок
программного кода в зависимости от различных условий. Директива .ifdef проверяют наличие
объявления некоторого символьного имени. За директивой может следовать набор команд, которые
будут подставлены в текст, если условие проверки “истина” (имя было объявлено). Директива .ifndef
противоположна .ifdef проверяет отсутствие объявления символьного имени. Директива .if производит
подстановку кода, когда выполняется условие сравнения, указанное в качестве ее параметра.
Команды, которые должны выполняться, в случае если условие директивы .if “ложно” –
располагаются после директивы .else. Ветвление типа “если” - “то” может иметь несколько уровней
вложения благодаря директиве .elif. Каждый блок проверки, начинающийся с .ifdef, .ifndef, .if, должен
быть закрыт директивой .endif.
Директивы if, .ifdef, .ifndef, .else, elif, .endif
Синтаксис написания:
.ifdef {символ} (или .ifndef {символ})
.if {условие}
.else {выражение} (или .elif { условие})
.endif
Пример использования:
?
1 .macro del_ms ;макрос, формирующий задержку времени в мс
.ifndef FREQ ;если не объявлена константа FREQ (частота в Гц),
2 .warning "Undefined FREQ constan!" ;выдаем предупреждение и
3 .equ FREQ = 1000000 ;присваиваем по умолчание значение 1 МГц
4 .endif
5 .equ DELAY = (@0*FREQ)/4000 ;величина задания задержки времени
6 .if DELAY > 65535 ;если DELAY размером больше 2 байт, то
.error “Integer overflow in DELAY!” ;реализация макроса не возможна
7
.else
8 push XL ;сохраняем в стеке рабочие регистры XL, XH
9 push XH
10 ldi XH,high(DELAY) ;цикл задержки времени
11 ldi XL,low(DELAY)
12 sbiw XH:XL,1
13 brne PC-1
14
15
pop XH
16
pop XL ;восстанавливаем из стека рабочие регистры XH, XL
17 .endif
18 .endm
19 .
20 .equ FREQ = 2000000 ;объявление тактовой частоты 2 МГц
21 .
del_ms 25 ;формирование задержки времени в 25 мс
22
23
Структура программы
Рассмотрим синтаксические особенности ассемблера AVR на примере небольшой тестовой
программы для микроконтроллера ATmega8, приведенной ниже. Программа формирует на выводе
PB2 импульсы с частотой следования ≈ 2.5 Гц при частоте внутреннего RC-генератора 1 МГц.
?
1. ; Тестовая программа для ATmega8.
2. // Светодиод подключен к выводу PB0 микроконтроллера.
3. /* Биты конфигурации:
Low Fuse High Fuse
BODLEVEL = 1 RSTDISBL = 1
BODEN = 1 WDTON = 1
SUT1 = 1 SPIEN = 1
SUT0 = 0 CKOPT = 1
CKSEL3 = 0 | EESAVE = 1
CKSEL2 = 0 |_ RC-генератор BOOTSZ1 = 1
CKSEL1 = 0 | 1 МГц BOOTSZ0 = 1
CKSEL0 = 1 | BOOTRST = 1 */
4. .nolist ;подключение стандартного заголовочного файла
5. .include "m8def.inc"
6. .list
7. .equ PAUSE = 50000 ;задержка времени
8. .equ LED = PB2 ;вывод для подключения светодиода
9. .def temp = R16 ;регистр для промежуточных операций
10. .def buffer = R17 ;регистр для чтения порта
11. .cseg
12. .org 0
13. rjmp initial ;0xC01F
14. rjmp 0 ;rjmp service_INT0 ;внешнее прерывание 0 0xCFFE
15. rjmp 0 ;rjmp service_INT1 ;внешнее прерывание 1 0xCFFD
16. rjmp 0 ;rjmp service_OC2 ;совпадение TCNT2 и OCR2 0xCFFC
17. rjmp 0 ;rjmp service_OVF2 ;переполнение TCNT2 0xCFFB
18. rjmp 0 ;rjmp service_ICP1 ;захват в ICP1 0xCFFA
19. rjmp 0 ;rjmp service_OC1A ;совпадение TCNT1 и OCR1A 0xCFF9
20. rjmp 0 ;rjmp service_OC1B ;совпадение TCNT1 и OCR1B 0xCFF8
21. rjmp 0 ;rjmp service_OVF1 ;переполнение TCNT1 0xCFF7
22. rjmp 0 ;rjmp service_OVF0 ;переполнение TCNT0 0xCFF6
23. rjmp 0 ;rjmp service_SPI ;прерывание от модуля SPI 0xCFF5
24. rjmp 0 ;rjmp service_URXC ;получение байта по USART 0xCFF4
25. rjmp 0 ;rjmp service_UDRE ;опустошение UDR в USART 0xCFF3
26. rjmp 0 ;rjmp service_UTXC ;передача байта по USART 0xCFF2
27. rjmp 0 ;rjmp service_ADCC ;прерывание от АЦП 0xCFF1
28. rjmp 0 ;rjmp service_ERDY ;завершение записи в EEPROM 0xCFF0
29. rjmp 0 ;rjmp service_ACI ;прерывание от компаратора 0xCFEF
30. rjmp 0 ;rjmp service_TWI ;прерывание от модуля TWI 0xCFEE
31. rjmp 0 ;rjmp service_SPMR ;завершение выполнения spm 0xCFED
32. .org 0x20
33. initial: ldi temp,low(RAMEND) ;0xE50F
34. out SPL,temp ;0xBF0D
35. ldi temp,high(RAMEND) ;0xE004
36. out SPH,temp ;0xBF0E
37. cbi PORTB,LED ;0x98C2
38. sbi DDRB,LED ;0x9ABA
39. ldi temp,1«LED ;0xE004
40. main: in buffer,PORTB ;0xB318
41. eor buffer,temp ;0x2710
42. out PORTB,buffer ;0xBB18
43. rcall delay ;0xD001
44. rjmp main ;0xCFFB
45. delay: ldi XH,high(PAUSE) ;0xECB3
46. ldi XL,low(PAUSE) ;0xE5A0
47. sbiw XH:XL,1 ;0x9711
48. brne PC-1 ;0xF7F1
49. ret