Код:
1. В командах sbr и cbr нужно применять не номер бита, а маску.
Надо писать (1<<Bit) или exp2(Bit).
2. В команде lpm Z операнд должен иметь значение метки * 2.
Память программ организована как слова, а lpm работает с байтами.
Нужно загружать Z так: ldi ZL,low(Label*2) ldi ZH,high(Label*2).
3. Для сброса флага прерывания нужно в него записать 1.
4. У AVR нет приоритетов прерываний. То, что зависит от адреса вектора,
и что обычно называют приоритетами, всего лишь порядок обработки прерываний
при ОДНОВРЕМЕННОМ поступлении запросов. Если бы существовали приоритеты,
то обработчик менее приортетного прерывания мог бы быть прерванным обработчиком
более приоритетного. Но это аппаратно не реализовано (в отличие, например, от MCS-51).
Реализовать приоритеты прерываний можно программно. В простейшем случае,
когда требуются всего два уровня приоритета, в обработчиках менее
приоритетных прерываний нужно разрешать вложенные прерывания командой SEI.
В IAR для этого служит ключевое слово __nested.
5. Питание портов, на которых расположены входы АЦП, осуществляется от
AVCC/AGND. Если не подключить эти пины, то не будет работать и цифровой IO.
6. В ранних AVR для EEPROM лучше не использовать ячейку 0. После операций
с EEPROM адрес оставлять = 0. Желательно использовать внешний супервизор питания.
7. Прямой операнд sbr, cbr, ldi, ori, andi, subi, sbci возможен с регистрами R16 - R31.
8. Побитовая работа с портами sbi, cbi возможна в диапазоне адресов 00 - 1F.
9. ADIW, SUBIW работают только с R24, 26, 28, 30.
10. MOVW работает с парами четный:нечетный регистр R0:R1 и т.д.
11. Команды INC/DEC не устанавливают флаг переноса.
12. Команды с автоинкрементом (автодекрементом) адреса не изменяют старший байт
регистра адреса в устройствах с памятью до 256 байт. ADIW, SUBIW - изменяют.
13. Директива DB должна описывать четное количество байт, иначе добавятся 00h.
14. При переходе с маленьких контроллеров без стека не забывать инициализировать стек.
15. Запись в EEPROM time-controlled, надо запрещать прерывавания.
16. У некоторых кристаллов (ATmega8, например) надо специально разрешать не только
выключение WDT, но и его перепрограммирование. Эти операции time-controlled,
надо запрещать прерывавания.
17. Для 16-разрядных регистров таймера читать первым надо L, записывать первым - H.
18. Если у кристалла есть JTAG, то его надо выключать фузом, иначе не работает
соответствующие порты.
19. Чтение ножки порта делается через регистр PINx, а не PORTx.
20. ATmega128 программируется через RXD, TXD (это у нее SPI).
21. ATmega128 имеет OC2 и OC1C на одной ноге, использовать их совместно нельзя.
22. У новых ATMega UART имеет FIFO, в котором сохраняются данные и флаги ошибок (FE, DOR).
Поэтому UDR нужно читать только один раз на один принятый байт. Флаги ошибок нужно читать
ПЕРЕД чтением UDR.
23. При использовании SPI в режиме мастера нельзя использовать вывод SS как вход для
других целей.
24. У новых кристаллов часть SFR перенесена в память. В файлах inc это отражено как
MEMORY MAPPED. Вместо in, out нужно использовать sts, lds. Но эти команды длиннее
на цикл. И самое главное, обращение к таким портам перестало быть атомарной операцией!
25. Максимальный ток защитных диодов - 1 мА (документ AVR182).
26. Чтобы сбросить пин OC после события Set OCxx on Compare Match, надо его перенастроить
на Clear OCxx on Compare Match и дождаться события. Или инициировать событие программно
с помощью бита Force Output Compare.
27. ATmega48 не имеет поддержки бутлоадера.
28. ATmega168 в отличие от ATmega48/88 имеет длину векторов прерываний 2 слова,
а также имеет команды CALL, JMP. Все это требует перекомпиляции проекта.
29. При генерации сигналов с помощью PWM нужно учесть, что допустимый диапазон
кодов не 0..65535, а на единицу меньший.
30. У новых ATmega и ATtiny при установке бита регистра PINx командой SBI
производится инвертирование ножки порта (инвертирование бита в PORTx).
В каких именно - надо смотреть DS, в структуре порта должен быть сигнал WPx.
В старых кристаллах для инвертирования ножки порта надо было минимум 4 слова и 4 такта:
in Rx,PORTx
ldi Ry,1 << PINX
eor Rx,Ry
out PORTx,Rx
31. Во время действия сигнала сброса потребление AVR примерно как в активном режиме,
но может быть даже больше. Это одни из граблей при питании AVR от маломощных источников.
32. В новых ATmega есть General Purpose I/O Registers: GPIOR0, GPIOR1, GPIOR2.
Эти регистры можно использовать по своему усмотрению: для хранения глобальных
переменных, флагов и т.д. Они бит-адресуемы и к ним могут применяться инструкции
SBI, CBI, SBIS, и SBIC.
Подобным образом для нужд программы можно использовать регистры периферии, которая
не используется.
33. Команды установки/сброса битов в регистре SBR, CBR влияют на регистр статуса,
а команды установки/сброса битов в IO SBI, CBI - не влияют.
34. Когда в прерывании таймера читаем АЦП, затем запускаем его и т.д., то может
показаться, что всё нормально. Но если в системе есть другие прерывания,
то прерывание таймера может быть отложено. Вроде бы, это не страшно,
так как интервал на преобразование АЦП только удлиняется. Но ведь
следующий интервал будет короче, преобразование не успеет завершиться и тогда
прочитаем ошибочное значение. Поэтому таймер нужно сбрасывать после старта АЦП,
чтобы получить гарантированное время для преобразования.
35. В AVRStudio есть галочка "Wrap Relative Jumps" (Project->Project Settings->Code Generation).
Дело в следующем. В теле команды RJMP/RCALL содержится 12-битное поле
(11 бит + знак), значение которого в момент исполнения такой команды знаково
суммируется с текущим значением програмного счетчика. Результат суммирования
записывается обратно в програмный счетчик, который, например, в ATmega8 имеет
длину 12 бит (4096 слов), т.е. суммирование идет в этом случае по модулю 4096.
Если метка перехода отстояла от места ее вызова более чем на -2048/+2047,
то ассемблер должен сообщать об ошибке типа "Relative jump too large".
Но если разрешить "Wrap Relative Jumps", то вместо кода, эквивалентного
RJMP PC-3456 (что некорректно), будет сгенерирован код RJMP PC+(4096-3456),
т.е. RJMP (PC+640)mod 4096, и все будет корректно. Это можно разрешать абсолютно
безболезненно для всех AVR с длиной Flash 4 КБ и более, а для меньшего размера Flash
это просто не будет иметь смысла.
36. При добавлении однобайтового числа к двухбайтовому обычно используют дополнительный
обнуленный регистр:
add zl,xl
adc zh,r3 ;r3=0
Можно обойтись без этого регистра:
add zl,xl
adc zh,xl
sub zh,xl
37. Для проверки 16-разрядного значения на ноль можно использовать такой код:
sbiw R28,0
breq is_zero
Если "sbiw" не поддерживается, то:
cpi R28,0 ;здесь можно использовать sub вместо cpi
sbci R29,0
breq is_zero
Или:
mov R16,R29
or R16,R30
breq is_zero
Если исходного содержимого не жалко, то:
or R28,R29
breq is_zero
38. Для проверки 16-разрядного значения на $FFFF можно использовать такой код:
movw R16,R29
adiw R16,1
breq is_ffff
В этом требуется свободный верхний регистр:
mov R16,R29
cpi R28,$FF
sbci R16,$FF
breq is_ffff
В этом требуется любой регистр:
mov R16,R28
and R16,R29
inc R16
breq is_ffff
39. Сформировать импульс, длительность которого можно регулировать с дискретностью
в один такт, можно так:
ldi xl,5 ;длительность импульса (r3=0)
ldi zl,Low(stab)
ldi zh,High(stab)
add zl,xl
adc zh,r3
sbi portb,1 ;начало импульса
ijmp
stab: nop
nop
nop
nop
nop
nop
nop
nop
nop
...
nop
nop
nop
nop
nop
strob: cbi portb,1 ;конец импульса
40. В AVRASM2 имеется C-style preprocessor,
работа с константами может выглядеть двумя способами:
Alternative 1:
----------------------------------
.equ Fclk = 16000 ;Fclk, kHz
.equ Tbas = 5 ;time base, mS
.equ T1Div = Fclk*Tbas
.equ MAXWORD = 0xFFFF
.if T1Div > MAXWORD
.error "out of range constant"
.endif
-----------------------------------
Alternative 2:
-----------------------------------
#define Fclk 16000 //Fclk, kHz
#define Tbas 5 //time base, mS
#define T1Div Fclk*Tbas
#define MAXWORD 0xFFFF
#if T1Div > MAXWORD
#error "out of range constant"
#endif
-----------------------------------
Выражения с константами вычисляются в 64-разрядной сетке. Поддерживаются
выражения с константами с плавающей запятой, хотя сами символы могут быть
только целыми. Для приведения к целым числам есть ряд функций: int(), frac(),
q7(), q15(), abs().
Социальные закладки