В этой статье я хочу представить еще один вариант таблиц команд ассемблера для микроконтроллеров AVR.
Из дополнительных материалов у приобретателей курса уже есть pdf документ с набором таблиц команд. Так в чем же основное отличие набора команд, который представлен ниже?
В первую очередь, тем что в колонке «Описание» дается описание команд на английском. И как не трудно заметить, многие мнемоники команд образованы как раз от этих сокращений. Поэтому, тем кто знает английский язык, тем будет проще запомнить написание команд. И так же присутствует колонка «Код операции», где можно посмотреть каким образом та или иная команда выглядит в двоичном виде. Это на тот случай, если вам вдруг захочется по-программировать в машинных кодах.
Сразу стоит отметить, что здесь представлены в основном только команды семейства tiny. Я намерено убрал команды семейства mega, что бы лишний раз не вносить путаницу.
Команда | Описание | Действие | Циклы | Код операции | Флаги |
add Rd,Rr | Add two Registers | Rd←Rd+Rr | 1 | 0000 11rd dddd rrrr | Z,C,S,N,V,H |
adc Rd,Rr | Add with Carry two Registers | Rd←Rd+Rr+C | 1 | 0001 11rd dddd rrrr | Z,C,S,N,V,H |
adiw Rdl,K | Add Immediate to Word | Rdh:Rdl←Rdh:Rdl+K | 2 | 1001 0110 KKdd KKKK | Z,C,S,N,V |
sub Rd,Rr | Subtract two Registers | Rd←Rd-Rr | 1 | 0001 10rd dddd rrrr | Z,C,S,N,V,H |
sbc Rd,Rr | Subtract with Carry two Registers | Rd←Rd-Rr-C | 1 | 0000 10rd dddd rrrr | Z,C,S,N,V,H |
subi Rd,K | Subtract Constant from Register | Rd←Rd-K | 1 | 1010 KKKK dddd KKKK | Z,C,S,N,V,H |
sbci Rd,K | Subtract with Carry Constant from Register | Rd←Rd-K-C | 1 | 0100 KKKK dddd KKKK | Z,C,S,N,V,H |
sbiw Rdl,K | Subtract Immediate from Word | Rdh:Rdl←Rdh:Rdl-K | 2 | 1001 0111 KKdd KKKK | Z,C,S,N,V |
and Rd,Rr | Logical AND Registers | Rd←Rd AND Rr | 1 | 0010 00rd dddd rrrr | Z,S,N |
andi Rd,K | Logical AND Register and Constant | Rd←Rd AND K | 1 | 0111 KKKK dddd KKKK | Z,S,N |
or Rd,Rr | Logical OR Registers | Rd←Rd OR Rr | 1 | 0010 10rd dddd rrrr | Z,S,N |
ori Rd,K | Logical OR Register and Constant | Rd←Rd OR K | 1 | 0110 KKKK dddd KKKK | Z,S,N |
eor Rd,Rr | Exclusive OR Registers | Rd←Rd EOR Rr | 1 | 0010 01rd dddd rrrr | Z,S,N |
com Rd | One’s complement | Rd←0xFF-Rd | 1 | 1001 010d dddd 0000 | Z,S,N |
neg Rd | Two’s complement | Rd←0x00-Rd | 1 | 1001 010d dddd 0001 | Z,C,S,N,V,H |
sbr Rd,K | Set Bit (s) in Register | Rd←Rd OR K | 1 | 0110 KKKK dddd KKKK | Z,S,N |
cbr Rd,K | Clear Bit (s) in Register | Rd←Rd AND (0xFF- K) | 1 | 0111 KKKK dddd KKKK | Z,S,N |
inc Rd | Increment | Rd←Rd+1 | 1 | 1001 010d dddd 0011 | Z,S,N,V |
dec Rd | Decrement | Rd←Rd-1 | 1 | 1001 010d dddd 1010 | Z,S,N,V |
tst Rd | Test for Zero or Minus | Rd←Rd AND Rd | 1 | 0010 00dd dddd dddd | Z,S,N |
clr Rd | Clear Register | Rd←Rd EOR Rd | 1 | 0010 01dd dddd dddd | Z,S,N |
ser Rd | Set Register | Rd←0xFF | 1 | 1110 1111 dddd 1111 | None |
Команды пересылки данных
Команда | Описание | Действие | Циклы | Код операции | Флаги |
mov Rd,Rr | Move Between Registers | Rd←Rr | 1 | 0010 11rd dddd rrrr | None |
movw Rd,Rr | Copy Register Word | Rd+1:Rd←Rr+1:Rr | 1 | 0000 0001 dddd rrrr | None |
ldi Rd,K | Load Immediate | Rd←K | 1 | 1110 KKKK dddd KKKK | None |
ld Rd,X | Load Indirect | Rd← (X) | 2 | 1001 000d dddd 1100 | None |
ld Rd,X+ | Load Indirect and Post-Inc. | Rd← (X), X←X+1 | 2 | 1001 000d dddd 1101 | None |
ld Rd, -X | Load Indirect and Pre-Dec. | X←X-1, Rd← (X) | 2 | 1001 000d dddd 1110 | None |
ld Rd,Y | Load Indirect | Rd← (Y) | 2 | 1000 000d dddd 1000 | None |
ld Rd,Y+ | Load Indirect and Post-Inc. | Rd← (Y), Y←Y+1 | 2 | 1001 000d dddd 1001 | None |
ld Rd, -Y | Load Indirect and Pre-Dec. | Y←Y-1, Rd← (Y) | 2 | 1001 000d dddd 1010 | None |
ldd Rd,Y+q | Load Indirect with Displacement | Rd← (Y+q) | 2 | 10q0 qq0d dddd 1qqq | None |
ld Rd,Z | Load Indirect | Rd← (Z) | 2 | 1000 000d dddd 0000 | None |
ld Rd,Z+ | Load Indirect and Post-Inc. | Rd← (Z), Z←Z+1 | 2 | 1001 000d dddd 0001 | None |
ld Rd, -Z | Load Indirect and Pre-Dec. | Z←Z-1, Rd← (Z) | 2 | 1001 000d dddd 0010 | None |
ldd Rd,Z+q | Load Indirect with Displacement | Rd← (Z+q) | 2 | 10q0 qq0d dddd 0qqq | None |
lds Rd,k | Load Direct from SRAM | Rd← (k) | 2 | 1001 000d dddd 0000kkkk kkkk kkkk kkkk | None |
st X,Rr | Store Indirect | (X) ←Rr | 2 | 1001 001r rrrr 1100 | None |
st X+,Rr | Store Indirect and Post-Inc. | (X) ←Rr, X←X+1 | 2 | 1001 001r rrrr 1101 | None |
st -X,Rr | Store Indirect and Pre-Dec. | X←X-1, (X) ←Rr | 2 | 1001 001r rrrr 1110 | None |
st Y,Rr | Store Indirect | (Y) ←Rr | 2 | 1000 001r rrrr 1000 | None |
st Y+,Rr | Store Indirect and | (Y) ←Rr, Y←Y+1 | 2 | 1001 001r rrrr 1001 | None |
st -Y,Rr | Store Indirect and Pre-Dec. | Y←Y-1, (Y) ←Rr | 2 | 1001 001r rrrr 1010 | None |
std Y+q,Rr | Store Indirect with Displacement | (Y+q) ← Rr | 2 | 10q0 qq1r rrrr 1qqq | None |
st Z,Rr | Store Indirect | (Z) ←Rr | 2 | 1000 001r rrrr 0000 | None |
st Z+,Rr | Store Indirect and Post-Inc. | (Z) ←Rr, Z←Z+1 | 2 | 1001 001r rrrr 0001 | None |
st -Z,Rr | Store Indirect and Pre-Dec. | Z←Z-1, (Z) ←Rr | 2 | 1001 001r rrrr 0010 | None |
std Z+q,Rr | Store Indirect with Displacement | (Z+q) ← Rr | 2 | 10q0 qq1r rrrr 0qqq | None |
sts k,Rr | Store Direct to SRAM | (k) ←Rr | 2 | 1001 001r rrrr 0000kkkk kkkk kkkk kkkk | None |
lpm | Load Program Memory | R0← (Z) | 3 | 1001 0101 1100 1000 | None |
lpm Rd,Z | Load Program Memory | Rd← (Z) | 3 | 1001 000d dddd 0100 | None |
lpm Rd,Z+ | Load Program Memory and Post-Inc. | Rd← (Z), Z←Z+1 | 3 | 1001 000d dddd 0101 | None |
Store Program Memory | (Z) ←R1:R0 | — | 1001 0101 1110 1000 | None | |
in Rd,P | In Port | Rd←P | 1 | 1011 0PPd dddd PPPP | None |
out P,Rr | Out Port | P←Rr | 1 | 1011 1PPr rrrr PPPP | None |
push Rr | Push Register in Stack | STACK←Rr, SP←SP-1 | 2 | 1001 001r rrrr 1111 | None |
pop Rd | Pop Register from Stack | SP←SP+1, Rd←STACK | 2 | 1001 000d dddd 1111 | None |
Команда | Описание | Действие | Циклы | Код операции | Флаги |
rjmp k | Relative Jump | PC←PC+k+1 | 2 | 1100 kkkk kkkk kkkk | None |
ijmp | Indirect Jump to (Z) | PC← (Z) | 2 | 1001 0100 0000 1001 | None |
*jmp k | Direct Jump | PC←k | 3 | 1001 010k kkkk 110kkkkk kkkk kkkk kkkk | None |
rcall k | Relative Subroutine Call | STACK←PC+1,PC←PC+k+1,SP←SP-2 or 3 | ¾ | 1101 kkkk kkkk kkkk | None |
icall | Indirect Call to (Z) | STACK←PC+1, PC← (Z),SP←SP-2 or 3 | ¾ | 1001 0101 0000 1001 | None |
*call k | Direct Subroutine Call | STACK←PC+1, PC←k,SP←SP-2 or 3 | 4/5 | 1001 010k kkkk 111kkkkk kkkk kkkk kkkk | None |
ret | Subroutine Return | PC←STACK, SP←SP+2 or 3 | 4/5 | 1001 0101 0000 1000 | None |
reti | Interrupt Return | PC←STACK, SP←SP+2 or 3 | 4/5 | 1001 0101 0001 1000 | I |
cpse Rd,Rr | Compare, Skip if Equal | if (Rd=Rr) PC←PC+2 or 3 | ½/3 | 0001 00rd dddd rrrr | None |
cp Rd,Rr | Compare | Rd-Rr | 1 | 0001 01rd dddd rrrr | Z,C,S, N,V,H |
cpc Rd,Rr | Compare with Carry | Rd-Rr-C | 1 | 0000 01rd dddd rrrr | Z,C,S, N,V,H |
cpi Rd,K | Compare Register with Immediate | Rd-Rr-K | 1 | 0011 KKKK dddd KKKK | Z,C,S, N,V,H |
sbrc Rr,b | Skip if Bit in Register is Cleared | if (Rr (b)=0) PC←PC+2 or 3 | ½/3 | 1111 110r rrrr obbb | None |
sbrs Rr,b | Skip if Bit in Register is Set | if (Rr (b)=1) PC←PC+2 or 3 | ½/3 | 1111 111r rrrr obbb | None |
sbic P,b | Skip if Bit in IO Register is Cleared | if (P (b)=0) PC←PC+2 or 3 | ½/3 | 1001 1001 PPPP Pbbb | None |
sbis P,b | Skip if Bit in IO Register is Set | if (P (b)=1) PC←PC+2 or 3 | ½/3 | 1001 1011 PPPP Pbbb | None |
brbc s,k | Branch if Status Flag is Cleared | if (SREG (s)=0) PC←PC+k+1 | ½ | 1111 01kk kkkk ksss | None |
brbs s,k | Branch if Status Flag is Set | if (SREG (s)=1) PC←PC+k+1 | ½ | 1111 00kk kkkk ksss | None |
brcc k | Branch if Carry Flag is Clearsd | if (C=0) PC←PC+k+1 | ½ | 1111 01kk kkkk k000 | None |
brcs k | Branch if Carry Flag is Set | if (C=1) PC←PC+k+1 | ½ | 1111 00kk kkkk k000 | None |
brsh k | Branch if Same or Higher | if (C=0) PC←PC+k+1 | ½ | 1111 01kk kkkk k000 | None |
brlo k | Branch if Lower | if (C=1) PC←PC+k+1 | ½ | 1111 00kk kkkk k000 | None |
brne k | Branch if Not Equal | if (Z=0) PC←PC+k+1 | ½ | 1111 01kk kkkk k001 | None |
breq k | Branch if Equal | if (Z=1) PC←PC+k+1 | ½ | 1111 00kk kkkk k001 | None |
brpl k | Branch if Plus | if (N=0) PC←PC+k+1 | ½ | 1111 01kk kkkk k010 | None |
brmi k | Branch if Minus | if (N=1) PC←PC+k+1 | ½ | 1111 00kk kkkk k010 | None |
brvc k | Bruach if Overflow Flag is Cleared | if (V=0) PC←PC+k+1 | ½ | 1111 01kk kkkk k011 | None |
brvs k | Branch if Overflow Flag is Set | if (V=1) PC←PC+k+1 | ½ | 1111 00kk kkkk k011 | None |
brge k | Branch if Greate or Equal, Signed | if (S=0) PC←PC+k+1 | ½ | 1111 01kk kkkk k100 | None |
brlt k | Branch if Less than Zero, Signed | if (S=1) PC←PC+k+1 | ½ | 1111 00kk kkkk k100 | None |
brhc k | Branch if Half Carry Flag is Cleared | if (H=0) PC←PC+k+1 | ½ | 1111 01kk kkkk k101 | None |
brhs k | Branch if Half Carry Flag is Set | if (H=1) PC←PC+k+1 | ½ | 1111 00kk kkkk k101 | None |
brtc k | Branch if Transfer Flag is Cleared | if (T=0) PC←PC+k+1 | ½ | 1111 01kk kkkk k110 | None |
brts k | Branch if Transfer Flag is Set | if (T=1) PC←PC+k+1 | ½ | 1111 00kk kkkk k110 | None |
brid k | Branch if Interrupt Disable | if (T=0) PC←PC+k+1 | ½ | 1111 01kk kkkk k111 | None |
brie k | Branch if Interrupt Enable | if (T=1) PC←PC+k+1 | ½ | 1111 00kk kkkk k111 | None |
*Обратите внимание! Команды jmp и call не поддерживаются микроконтроллерами семейства tiny, но так как они часто используются при программировании семейства mega, то я решил их так же внести в таблицу, что бы вы не забывали о их существовании.
Проверкафлага | Команда условногоперехода | АльтернативнаяФорма написания | Условие перехода |
C | brbc 0,k | brcc k | Переход если флаг переноса установлен |
brsh k | Переход если больше или равно | ||
brbs 0,k | brcs k | Переход если флаг переноса сброшен | |
brlo k | Переход если меньше | ||
Z | brbc 1,k | breq k | Переход если равно |
brbs 1,k | brne k | Переход если не равно | |
N | brbc 2,k | brpl k | Переход если плюс |
brbs 2,k | brmi k | Переход если минус | |
V | brbc 3,k | brvc k | Переход если флаг дополнительного кода сброшен |
brbs 3,k | brvs k | Переход если флаг дополнительного кода установлен | |
S | brbc 4,k | brge k | Переход если больше или равно нулю (знаковое) |
brbs 4,k | brlt k | Переход если меньше нуля (знаковое) | |
H | brbc 5,k | brhc k | Переход если флаг половинного переноса сброшен |
brbs 5,k | brhs k | Переход если флаг половинного переноса установлен | |
T | brbc 6,k | brtc k | Переход если флаг хранения бита сброшен |
brbs 6,k | brts k | Переход если флаг хранения бита установлен | |
I | brbc 7,k | brid k | Переход если прерывания запрещены |
brbs 7,k | brie k | Переход если прерывания разрешены |
Команда | Описание | Действие | Циклы | Код операции | Флаги |
sbi P,b | Set Bit in I/O Rerister | I/O (P,b) ←1 | 2 | 1001 1010 PPPP Pbbb | None |
cbi P,b | Clear Bit in I/ORerister | I/O (P,b) ←0 | 2 | 1001 1000 PPPP Pbbb | None |
lsl Rd | Logical Shift Left | Rd (n+1) ←Rd (n), Rd (0) ←0 | 1 | 0000 11dd dddd dddd | Z,C,N,V |
lsr Rd | Logical Shift Right | Rd (n) ←Rd (n+1), Rd (7) ←0 | 1 | 1001 010d dddd 0110 | Z,C,N,V |
rol Rd | Rotate Left through Carry | Rd (0) ←C, Rd (n+1) ←Rd (n), C←Rd (7) | 1 | 0001 11dd dddd dddd | Z,C,N,V |
ror Rd | Rotate Right through Carry | Rd (7) ←C, Rd (n) ←Rd (n+1), C←Rd (0) | 1 | 1001 010d dddd 0111 | Z,C,N,V |
asr Rd | Arithmetic Shift Right | Rd (n) ←Rd (n+1), n=0…6 | 1 | 1001 010d dddd 0101 | Z,C,N,V |
swap Rd | Swap Nibbles | Rd (3…0) ←Rd (7…4),Rd (7…4) ←Rd (3…0) | 1 | 1001 010d dddd 0010 | None |
bst Rr,b | Bit Store from Rerister to T | T←Rr (b) | 1 | 1111 101b bbbb 0bbb | T |
bld Rd,b | Bit Load from T to Rerister | Rd (b) ←T | 1 | 1111 100b bbbb 0bbb | None |
bset s | Flag Set | SREG (s) ←1 | 1 | 1001 0100 0sss 1000 | SREG (s) |
bclr s | Flag Clear | SREG (s) ←0 | 1 | 1001 0100 1sss 1000 | SREG (s) |
sec | Set Carry | C←1 | 1 | 1001 0100 0000 1000 | C |
clc | Clear Carry | C←0 | 1 | 1001 0100 1000 1000 | C |
sez | Set Zero Flag | Z←1 | 1 | 1001 0100 0001 1000 | Z |
clz | Clear Zero Flag | Z←0 | 1 | 1001 0100 1001 1000 | Z |
sen | Set Negative Flag | N←1 | 1 | 1001 0100 0010 1000 | N |
cln | Clear Negative Flag | N←0 | 1 | 1001 0100 1010 1000 | N |
sev | Set Twos Complement Overflow | V←1 | 1 | 1001 0100 0011 1000 | V |
clv | Clear Twos Complement Overflow | V←0 | 1 | 1001 0100 1011 1000 | V |
ses | Set Signed Test Flag | S←1 | 1 | 1001 0100 0100 1000 | S |
cls | Clear Signed Test Flag | S←0 | 1 | 1001 0100 1100 1000 | S |
seh | Set Half Carry Flag | H←1 | 1 | 1001 0100 0101 1000 | H |
clh | Clear Half Carry Flag | H←0 | 1 | 1001 0100 1101 1000 | H |
set | Set Transfer bit | T←1 | 1 | 1001 0100 0110 1000 | T |
clt | Clear Transfer bit | T←0 | 1 | 1001 0100 1110 1000 | T |
sei | Global Interrupt Enable | I←1 | 1 | 1001 0100 0111 1000 | I |
cli | Global Interrupt Disable | I←0 | 1 | 1001 0100 1111 1000 | I |
Команда | Описание | Действие | Циклы | Код операции | Флаги |
nop | No operation | — | 1 | 0000 0000 0000 0000 | None |
sleep | Sleep | — | 1 | 1001 0101 1000 1000 | None |
wdr | Watchdog Reset | — | 1 | 1001 0101 1010 1000 | None |
Раздел: Программирование микроконтроллеров для начинающих
www.mcu4you.ru
Мнемоника | Описание | Операция | Флаги |
---|---|---|---|
ADD Rd, Rr | Сложение двух регистров | Rd ← Rd + Rr | Z, C, N, V, H |
ADC Rd, Rr | Сложение двух регистров с переносом | Rd ← Rd + Rr + С | Z, C, N, V, H |
SUB Rd, Rr | Вычитание двух регистров | Rd ← Rd — Rr | Z, C, N, V, H |
SBC Rd, Rr | Вычитание двух регистров с заёмом | Rd ← Rd — Rr — С | Z, C, N, V, H |
ADIW Rd, K | Сложение регистровой пары с константой | R(d+1):Rd ← R(d+1):Rd + K | Z, C, N, V, S |
SBIW Rd, K | Вычитание константы из регистровой пары | R(d+1):Rdl ← R(d+1):Rd — K | Z, C, N, V, S |
SUBI Rd, K | Вычитание константы из регистра | Rd ← Rd — K | Z, C, N, V, H |
SBCI Rd, K | Вычитание константы из регистра с заёмом | Rd ← Rd — K — С | Z, C, N, V, H |
INC Rd | Инкремент регистра | Rd ← Rd + 1 | Z, N, V |
DEC Rd | Декремент регистра | Rd ← Rd – 1 | Z, N, V |
MUL Rd, Rr | Умножение чисел без знака | R1:R0 ← Rd * Rr | Z, C |
MULS Rd, Rr | Умножение чисел со знаком | R1:R0 ← Rd * Rr | Z, C |
MULSU Rd, Rr | Умножение числа со знаком с числом без знака | R1:R0 ← Rd * Rr | Z, C |
FMUL Rd, Rr | Умножение дробных чисел без знака | R1:R0 ← (Rd * Rr) << 1 | Z, C |
FMULS Rd, Rr | Умножение дробных чисел со знаком | R1:R0 ← (Rd * Rr) << 1 | Z, C |
FMULSU Rd, Rr | Умножение дробного числа со знаком с числом без знака | R1:R0 ← (Rd * Rr) << 1 | Z, C |
Мнемоника | Описание | Операция | Флаги |
CBR Rd, K | Очистка разрядов регистра | Rd ← Rd and (0FFH – K) | Z, N, V |
SBR Rd, K | Установка разрядов регистра | Rd ← Rd or K | Z, N, V |
CBI P, b | Сброс разряда I/O-регистра | P.b ← 0 | — |
SBI P, b | Установка разряда I/O-регистра | P.b ← 1 | — |
BCLR s | Сброс флага SREG | SREG.s ← 0 | SREG.s |
BSET s | Установка флага SREG | SREG.s ← 1 | SREG.s |
BLD Rd, b | Загрузка разряда регистра из флага T | Rd.b ← T | — |
BST Rr, b | Запись разряда регистра во флаг T | T ← Rd.b | T |
CLC | Сброс флага переноса | C ← 0 | C |
SEC | Установка флага переноса | C ← 1 | C |
CLN | Сброс флага отрицательного числа | N ← 0 | N |
SEN | Установка флага отрицательного числа | N ← 1 | N |
CLZ | Сброс флага нуля | Z ← 0 | Z |
SEZ | Установка флага нуля | Z ← 1 | Z |
CLI | Общий запрет прерываний | I ← 0 | I |
SEI | Общее разрешение прерываний | I ← 1 | I |
CLS | Сброс флага знака | S ← 0 | S |
SES | Установка флага знака | S ← 1 | S |
CLV | Сброс флага переполнения дополнительного кода | V ← 0 | V |
SEV | Установка флага переполнения дополнительного кода | V ← 1 | V |
CLT | Сброс пользовательского флага T | T ← 0 | T |
SET | Установка пользовательского флага T | T ← 1 | T |
CLH | Сброс флага половинного переноса | H ← 0 | H |
SEH | Установка флага половинного переноса | H ← 1 | H |
Мнемоника | Описание | Операция | Флаги |
ASR Rd | Арифметический сдвиг вправо | Rd(i) ← Rd(i+1) (n=0..6), C ← Rd(0) | Z, C, N, V |
LSL Rd | Логический сдвиг влево | Rd(i+1) ← Rd(i), Rd(0) ← 0, C ← Rd(7) | Z, C, N, V |
LSR Rd | Логический сдвиг вправо | Rd(i) ← Rd(i+1), Rd(7) ← 0, C ← Rd(0) | Z, C, N, V |
ROL Rd | Сдвиг влево через перенос | Rd(i+1) ← Rd(i), Rd(0) ← C, C ← Rd(7) | Z, C, N, V |
ROR Rd | Сдвиг вправо через перенос | Rd(i) ← Rd(i+1), Rd(7) ← C, C ← Rd(0) | Z, C, N, V |
SWAP Rd | Обмен местами тетрад | Rd(3..0) ↔ Rd(7..4) | — |
Мнемоника | Описание | Операция | Флаги |
MOV Rd, Rr | Пересылка между регистрами | Rd ← Rr | — |
MOVW Rd, Rr | Пересылка между парами регистров | R(d +1):Rd ← R(r+1):Rr | — |
LDI Rd, K | Загрузка константы в регистр | Rd ← K | — |
LD Rd, X | Косвенное чтение | Rd ← [X] | — |
LD Rd, X+ | Косвенное чтение с пост-инкрементом | Rd ← [X], X ← X + 1 | — |
LD Rd, -X | Косвенное чтение с пред-декрементом | X ← X — 1, Rd ← [X] | — |
LD Rd, Y | Косвенное чтение | Rd ← [Y] | — |
LD Rd, Y+ | Косвенное чтение с пост-инкрементом | Rd ← [Y], Y ← Y + 1 | — |
LD Rd, -Y | Косвенное чтение с пред-декрементом | Y ← Y — 1, Rd ← [Y] | — |
LDD Rd, Y+q | Косвенное чтение со смещением | Rd ← [Y+q] | — |
LD Rd, Z | Косвенное чтение | Rd ← [Z] | — |
LD Rd, Z+ | Косвенное чтение с пост-инкрементом | Rd ← [Z], Z ← Z + 1 | — |
LD Rd, -Z | Косвенное чтение с пред-декрементом | Z ← Z — 1, Rd ← [Z] | — |
LDD Rd, Z+q | Косвенное чтение со смещением | Rd ← [Z+q] | — |
LDS Rd, A | Непосредственное чтение из ОЗУ | Rd ← [A] | — |
ST X, Rr | Косвенная запись | [X] ← Rr | — |
ST X+, Rr | Косвенная запись с пост-инкрементом | [X] ← Rr, X ← X + 1 | — |
ST -X, Rr | Косвенная запись с пред-декрементом | X ← X — 1, [X] ← Rr | — |
ST Y, Rr | Косвенная запись | [Y] ← Rr | — |
ST Y+, Rr | Косвенная запись с пост-инкрементом | [Y] ← Rr, Y ← Y + 1 | — |
ST -Y, Rr | Косвенная запись с пред-декрементом | Y ← Y — 1, [Y] ← Rr | — |
STD Y+q, Rr | Косвенная запись со смещением | [Y+q] ← Rr | — |
ST Z, Rr | Косвенная запись | [Z] ← Rr | — |
ST Z+, Rr | Косвенная запись с пост-инкрементом | [Z] ← Rr, Z ← Z + 1 | — |
ST -Z, Rr | Косвенная запись с пред-декрементом | Z ← Z — 1, [Z] ← Rr | — |
STD Z+q, Rr | Косвенная запись со смещением | [Z+q] ← Rr | — |
STS A, Rr | Непосредственная запись в ОЗУ | [A] ← Rr | — |
LPM | Загрузка данных из памяти программы | R0 ← {Z} | — |
LPM Rd, Z | Загрузка данных из памяти программы в регистр | Rd ← {Z} | — |
LPM Rd, Z+ | Загрузка данных из памяти программы с пост-инкрементом Z | Rd ← {Z}, Z ← Z + 1 | — |
SPM | Запись в программную память | {Z} ← R1:R0 | — |
IN Rd, P | Пересылка из I/O-регистра в регистр | Rd ← P | — |
OUT P, Rr | Пересылка из регистра в I/O-регистр | P ← Rr | — |
PUSH Rr | Сохранение регистра в стеке | STACK ← Rr | — |
POP Rd | Извлечение регистра из стека | Rd ← STACK | — |
Мнемоника | Описание | Условие | Флаги |
BRBC s, A | Переход если флаг S сброшен | Если SREG(S) = 0 | — |
BRBS s, A | Переход если флаг S установлен | Если SREG(S) = 1 | — |
BRCS A | Переход по переносу | Если C = 1 | — |
BRCC A | Переход если нет переноса | Если C = 0 | — |
BREQ A | Переход если равно | Если Z = 1 | — |
BRNE A | Переход если не равно | Если Z = 0 | — |
BRSH A | Переход если больше или равно | Если C = 0 | — |
BRLO A | Переход если меньше | Если C = 1 | — |
BRMI A | Переход если отрицательное значение | Если N = 1 | — |
BRPL A | Переход если положительное значение | Если N = 0 | — |
BRGE A | Переход если больше или равно (со знаком) | Если (N и V) = 0 | — |
BRLT A | Переход если меньше (со знаком) | Если (N или V) = 1 | — |
BRHS A | Переход по половинному переносу | Если H = 1 | — |
BRHC A | Переход если нет половинного переноса | Если H = 0 | — |
BRTS A | Переход если флаг T установлен | Если T = 1 | — |
BRTC A | Переход если флаг T сброшен | Если T = 0 | — |
BRVS A | Переход по переполнению дополнительного кода | Если V = 1 | — |
BRVC A | Переход если нет переполнения дополнительного кода | Если V = 0 | — |
BRID A | Переход если прерывания запрещены | Если I = 0 | — |
BRIE A | Переход если прерывания разрешены | Если I = 1 | — |
SBRC Rd, K | Пропустить следующую команду если бит в регистре очищен | Если Rd[K] = 0 | — |
SBRS Rd, K | Пропустить следующую команду если бит в регистре установлен | Если Rd[K] = 1 | — |
SBIC A, b | Пропустить еследующую команду если бит в регистре ввода/вывода очищен | Если I/O(A, b) = 0 | — |
SBIS A, b | Пропустить следующую команду если бит в регистре ввода/вывода установлен | Если I/O(A, b) = 1 | — |
trolsoft.ru
Теперь предстоит разобраться – как именно программист может выполнять операции с регистрами AVR при помощи написанной им программы. Для начала немного поговорим об ассемблере, прошу сторонников Си не ругаться, до ЯВУ мы ещё доберёмся.
Как я уже писал в первой части, программа – это заданная программистом последовательность инструкций, каждая из которых предписывает МК выполнить определённое действие. Пример инструкции: загрузить в регистр с именем r16 константу 0x12. Приказ этот для МК в виде 16-битного числа выглядит так: 1110000100000010. Именно это число, которое называется КОД ОПЕРАЦИИ, и будет занесено в 16-битную ячейку памяти программ по соответствующему адресу. Как именно распределяется программа по ячейкам памяти программ – разберём немного позже. Последовательность инструкций в виде 16-битных чисел представляет собой программу для AVR в машинных кодах. Однако, процесс написания программы в машинных кодах очень неудобен для человека. Поэтому в помощь программисту существует АССЕМБЛЕР – переводчик удобных для восприятия человеком команд в машинные коды для МК. Инструкция «загрузить в регистр с именем r16 константу 0x12» на ассемблере выглядит так:
ldi r16,0x12
Сокращение «ldi» образовано от английских слов «load data immediate» — «загрузка данных непосредственная». МНЕМОНИКА команды – запись её в виде ldi Rd, K – достаточно легко запоминается человеком. СИСТЕМА КОМАНД микроконтроллера – это совокупность всех команд, которые могут быть применены к данному МК. Число входящих в систему команд различно для разных семейств МК. Мнемоники также различаются. Поэтому ассемблер для одного семейства МК существенно отличается от предназначенного для другого семейства.
Для ознакомления с системой команд AVR откройте главу 3 книги А.В. Евстифеева «Микроконтроллеры AVR семейства Mega. Руководство пользователя».
Программа на ассемблере представляет собой заданную программистом последовательность команд, записанных в виде мнемоник, с указанием конкретных констант, регистров, битов. Вот фрагмент такой программы:
start: ldi r16,0x02 ; Загрузить константу 0х02 в регистр общего назначения r16
out DDRB,r16 ; Записать в регистр DDRB данные из регистра r16
; Теперь вывод PB1 оказался настроен на выход
ldi r16,0x1D ; Аналогичные действия с регистрами. Выключить
out PORTB,r16 ; светодиод, который подключен к между PB1 и «общим»
rjmp start ; Перейти на метку start
Как видите, ничего сложного. Команды ассемблера не должны начинаться с самого начала строки, следует делать отступ. С самой первой строки пишутся МЕТКИ. Метки – это придуманные программистом для своего удобства слова. Ими помечаются те адреса в памяти программ, на которые возможны переходы. Встретив команду перехода на какую-либо метку, ассемблер подставит в код операции соответствующий этой метке адрес. Никто не запрещает программисту указывать адрес непосредственно в виде числа. Просто применение меток делает программу более наглядной и позволяет не следить за возможными изменениями адресов. Подобно тому, как Пупкин может сначала поселиться в гостиничном номере 105, а затем его переселят в номер 209. Но если просто сказать «Пойдём в номер Пупкина», мы разберёмся, по какому адресу идти в данный момент. После имени метки в ассемблере AVR следует ставить двоеточие. Недопустимо ставить в программе 2 метки с одинаковым именем.
После точки с запятой пишутся КОММЕНТАРИИ. Это пояснения, которые программист пишет для себя и тех, кто будет смотреть программу. Микроконтроллеру они не нужны, поэтому ассемблер их просто игнорирует.
Адреса ячеек памяти программ в AVR начинаются с нуля ($0000). Адрес последней ячейки и их общее количество зависят от модели МК. Например, у ATmega16 объём памяти программ составляет 8192 ячейки. Ячейки 16-битные, поэтому память программ занимает 16 килобайт. Память может измеряться и в словах. 1 слово занимает 16 бит или 2 байта. Таким образом, объём памяти ATmega16 составляет 8K слов. Адреса ячеек – от $0000 до $1FFF. Большинство инструкций занимают по 1 ячейке, некоторым необходимо 2. Максимальный размер программы для конкретного МК как раз и ограничивается объёмом его памяти программ.
Скорость работы МК определяется тактовой частотой. 1 машинный цикл AVR выполняется за время 1 периода тактового генератора. Таким образом, при частоте тактового генератора 1 МГц за 1 секунду произойдет 1 миллион тактов. Большая часть команд выполняется микроконтроллерами AVR за 1 машинный цикл. Это стало возможным благодаря конвейерной обработке команд. При отработке команд перехода и ещё некоторых нормальная работа конвейера нарушается, и возникают задержки от 1 до 4 тактов. Подробнее о работе конвейера и причинах возникновения задержек почитайте в книге А.В. Евстифеева «Микроконтроллеры AVR семейства Mega. Руководство пользователя», глава 2, раздел 2.3.
Источники тактового сигнала могут быть различными, по выбору программиста. Самые распространённые варианты – это внутренний RC-генератор (не требует подключения внешних деталей), генератор с внешней RC-цепочкой, или встроенный кварцевый генератор с подключаемым внешним резонатором. Возможно также использование тактового сигнала от внешнего источника. Нижняя граница тактовой частоты архитектурой самого МК не ограничивается, она может быть сколь угодно малой – хоть 1 такт в год от внешнего источника. Верхняя граница зависит от модели МК и нередко составляет 8 или 16 МГц. Подробности, как обычно, можете узнать из даташита на конкретный МК.
Адрес $0000 – это ВЕКТОР СБРОСА. Именно с размещённой по этому адресу команды начинается выполнение программы после включения питания или сброса МК. Значение адреса вектора сброса в некоторых МК может быть изменено, но этот случай нас пока не интересует. Сброс может быть вызван внутренними или внешними причинами. О внутренних разговор будет позже. Одной из причин сброса является падение напряжения питания Vcc ниже определённой величины. Для внешнего сброса предназначен вывод _RESET. Сброс происходит при удержании низкого уровня (не более 0,1Vcc) на этом выводе не менее 1…3 мкс. При работе МК на выводе _RESET должно поддерживаться напряжение (0,9…1,0)Vcc. Во многих AVR имеется внутренний подтягивающий резистор, соединяющий _RESET c Vcc. В этом случае _RESET допустимо никуда не подключать, но для большей надёжности можно установить и дополнительный внешний резистор. Подробности о сбросе можете прочитать в книге А.В. Евстифеева «Микроконтроллеры AVR семейства Mega. Руководство пользователя», глава 4, раздел 4.4.
При сбросе во все регистры ввода/вывода (РВВ) заносятся начальные значения, заданные фирмой-производителем, а в PC загружается адрес вектора сброса. Выполнение программы после сброса начинается не мгновенно, а после некоторой задержки, для надёжности. По адресу $0000 программист обычно размещает команду безусловного перехода к инициализационной части программы, начало которой он обозначает меткой, например, «start”. Это делается для того, чтобы обойти размещённую в начале памяти программ таблицу векторов прерываний.
.org 0
reset: jmp start
……
…… (здесь переходы на обработку прерываний)
……
start: ldi r16,0x12
…… (отсюда продолжается программа)
В этом примере reset и start – метки. Директивой org программист указывает ассемблеру, по какому адресу следует поместить следующую команду в памяти программ. Таким образом, команда jmp start будет размещена по адресу $0000. Перед директивами в ассемблере AVR следует ставить точку. Какую команду следует применить здесь для перехода – двухбайтовую jmp или однобайтовую rjmp – зависит от конкретной модели AVR, смотрите в даташите. По какому адресу будет находиться первая команда инициализационной части (в примере это ldi) – нам в данном случае неважно, пусть ассемблер её сам расположит, а нам достаточно знать метку.
После сброса МК в PC загрузится вектор сброса $0000 и будет выполнена находящаяся по этому адресу команда безусловного перехода к метке start. Следовательно, в PC загрузится соответствующий метке start адрес, и выполнение программы продолжится с команды ldi r16,0x12
Инициализационная часть программы (далее я буду называть её просто инициализацией МК) предназначена для программной настройки микроконтроллера после сброса под нужды программиста. Здесь выполняется настройка выводов портов на вход/выход, включение и настройка периферийных модулей, занесение требуемых начальных значений в регистры. После инициализации МК выполняет возложенные на него программистом задачи.
Выводы по 2 части:
© picmaniac
cxema21.ru
 
 
 
Урок 3
Сегодня мы научимся писать код на C в среде Atmel Studio, для примера мы возьмём тот же проект, который мы создали на прошлом занятии.
Прежде чем мы начнём непосредственно заниматься написанием кода, мы изучим те строки кода, которые нам уже сгенерировала студия в нашем файле Test01.c.
В самом начале кода мы видим строку в виде следующей директивы
#include <avr/io.h>
Посмотрим определение данной директивы
Директива #include просит препроцессор (компилятор) включить файл, объявленный после нее в исходный код. Имя файла может заключаться либо в треугольные скобки <> либо в кавычки ””. Треугольные скобки используются в случае включения в код файла из самой среды разработки, а кавычки – пользовательского файла.
#include <имя файла>
В нашем случае в текст кода включается файл io.h. Если мы откроем данный файл, то мы увидим, что в зависимости от каких-то условий в наш код включаются ещё определённые заголовочные файлы. В нашем частном случае условием является использование нами определённого микроконтроллера. Так как мы используем Atmega8a, то соответственно включится следующий файл:
#elif defined (__AVR_ATmega8A__)
# include <avr/iom8a.h>
В данном файле находятся макросы для векторов прерываний, для определённых шин. Что такое макросы и как их использовать, мы разберём немного позднее. Движемся дальше по файлу io.h и видим, что в него также включены ещё 3 файла, но нам будет интересен следующий файл:
#include <avr/portpins.h>
В данном файле находятся также макроподстановки для наших лапок портов и прочей периферии, чтобы нам было удобнее писать и читать наш код. Откроем данный файл и изучим в нём данные строки
/* Port Data Register (generic) */
#define PORT7 7
#define PORT6 6
#define PORT5 5
#define PORT4 4
#define PORT3 3
#define PORT2 2
#define PORT1 1
#define PORT0 0
Данные строки нам говорят о том, что, например, если при компиляции проекта препроцессор (интерпретатор команд) встретит в коде слово PORT4, то он его сразу заменит на цифру 4.
Тем самым мы постепенно с вами подошли к изучению ещё одной директивы
Директива #define
Просит препроцессор (компилятор) в случае появления в тексте кода буквосочетания 1 заменить его на буквосочетание 2.
Данная директива используется для удобства чтения кода.
#define <буквосочетание 1> <буквосочетание 2>
Вернёмся в наш код. Далее мы видим следующее
int main(void)
{
…
}
То, что мы с вами наблюдаем в языке C и C++ называется функция. Функциями мы будем пользоваться постоянно. Функция — это такая подпрограмма, которая вызывается из какого-нибудь участка кода. Самое важное у функции — это её имя (в нашем случае main). По имени мы будем вызывать функцию из другого участка кода. Также у функции существуют входные аргументы, возвращаемые аргументы, а также тело. Входные аргументы находятся сразу после имени в скобках и перечисляются один за другим, а разделяются запятыми. В нашем случае стоит один тип «void», обозначающий, что у нашей функции вообще нет входных аргументов. Поэтому если мы подобную функцию будем вызывать в другом участке кода, то мы в скобках вообще ничего не ставим. Возвращаемый аргумент у функции один. Если нам потребуется больше чем один тип переменных, то мы будем пользоваться глобальными переменными, о которых мы узнаем позже. Изучение переменных вообще не входит в рамки наших уроков, как правило это объясняется непосредственно в уроках и литературе по языкам программирования. Тип возвращаемого аргумента указывается перед именем функции. В нашем случае — это int (целочисленная знаковая переменная). Также у функции существует тело — это участок кода, находящийся между открывающей и закрывающей фигурными скобками. Вот этот участок кода и будет выполняться в случае вызова функции.
Функцию main мы явно нигде не вызываем. Это главная функция нашего приложения, недаром она и называется main, что по английски значит главный. Встретив данное имя, компилятор и начинает выполнение программы с данного места. То есть это своего рода входная точка нашей программы. Отсюда всё и начинается. Сюда мы и начинаем писать свой код.
Давайте же что-нибудь сюда и напишем. Мы пока не будем обращать внимание на строки, уже содержащиеся в теле данной функции.
У программистов, которые пишут программы под ПК, начинать занятия принято с вывода строки «Hello World!», а у тех программистов, которые пишут под мелкие чипы, принято начинать работу с подключения и управления свечением светодиодами. Затем они учат их мигать просто, мигать по очереди, а уже после этого приступать к программированию каких-то более серьёзных вещей. Мы также не будем отступать от данного правила.
Давайте сначала подключим светодиод к какой-нибудь ножке контроллера, например к ножке 0 порта D
У порта D, как мы видим из данной распиновки, существует как раз 8 ножек, что соответствует байту (8 бит). Также как биты в байты, ножки портов отсчитываются от 0.
Напишем мы сначала следующую строку
int main(void)
{
DDRD = 0xFF;
Впредь я буду выделять жирным шрифтом то, что мы добавляем в код или изменяем в коде. Так как я кроме этого использую в написании блогов ещё кусок кода, находящийся или до или после написанного кода для того, чтобы читателю было понятно, куда именно мы пишем код. То есть то что не обозначено жирным шрифтом, это уже есть в коде.
DDRD — это команда, которая устанавливает состояние лапок порта D. Состояние лапки порта — это то, в каком направлении данная лапка будет работать — на выход или на вход, что соответствует установке определённого бита в 0 или в 1. Но так как мы будем зажигать светодиод, мы должны на определённой ножке порта выставить высокий логический уровень (в нашем случае 5 вольт). А чтобы управлять уровнями ножки, она должна быть определена, как работающая на выход или на вывод. То есть состоянием лапки мы будем управлять из контроллера, а не из внешнего источника уровня. Так как у нас лапка нулевая, то и бит мы и должны выставить в ноль нулевой бит нашего байта, соответствующего нашему порту D. Так как мы не пользуемся сегодня остальными лапками порта, то нам их состояние будет не важно и мы выставляем все лапки портов на вывод. Поэтому мы присваиваем переменной DDRD значение 0xFF. Данное значение мы написали в шестнадцатиричном виде. Этот вид очень удобен для программистов, так как визуально о многом говорит. FF — это в десятичной системе 255, а в двоичной — 11111111, что говорит о том, что все биты в данном числе выставлены в единицу. Также мы видим, что наша строка заканчивается точкой с запятой (;). Данный оператор — это разделитель команд, так как в одной строке мы можем писать не обязательно одну только команду, а можем и несколько, если они небольшие. Также в данной строке мы видим оператор «=» (знак равенства). Данный оператор заставляет наш препроцессор присвоить значение, находящееся справа возле него переменной, находящейся слева.
Ну, вообщем, переключили мы весь наш порт в состояние вывода данных. Теперь осталось включить на лапке PD0 высокий логический уровень. Сделать это мы можем следующей командой:
DDRD = 0xFF;
PORTD = 0b00000001;
Данная команда или переменная PORTD управляет записью или считыванием значений в порт или из порта в зависимости от состояния. То есть данной командой мы включили нулевую лапку в высокое логическое состояние (в единицу). Здесь мы с вами уже попробуем использовать написание значения в двоичном виде. Чтобы писать значения в данном виде, мы используем префикс 0b. Данный вид удобен тем, что мы здесь видим полностью, как выглядит наш байт побитно. Лапки портов в байте, также как и биты считаются справа налево. То есть данной командой мы выставили в высокое состояние нулевую лапку порта D, а остальные мы выставили в низкое. Вообщем, арифметическо-логическое устройство микроконтроллера сначала включит все ножки порта на выход, а затем установит на нулевой ножке высокое логическое состояние, и после этого у нас должен будет зажечься светодиод, так как через токоограничивающий резистор мы его анодом подключим к данной ножке, а катодом к общему проводу. Тем самым на контактах светодиода появится разность потенциалов, которая заставит его светиться. Кроме написанных нами двух строк далее в коде присутствует команда while. Данная команда является командой условного цикла.
PORTD = 0b00000001;
while(1)
{
}
В скобочках указывается условие, которое должно либо выполняться либо не выполняться. Также как у функции есть тело, то у условия также есть тело, также заключенное в фигурные скобки. И код, находящийся в теле цикла, будет выполняться бесконечно, пока условие, находящееся в скобках будет выполняться, то есть будет истинным. Как только условие перестанет выполняться, а проверяется это тогда, когда код выполнится до конца (до закрывающей фигурной скобки), то мы выходим из цикла и код продолжает выполняться уже дальше тот, который находится уже не в теле цикла, а после закрывающей фигурной скобки. А истина в информатике — это TRUE или 1. Поэтому в данном случае цикл будет бесконечным, так как там стоит единице, а единица всегда равна единице. Бесконечный цикл организован для того, чтобы код, написанный для контроллера, выполнялся постоянно, то есть чтобы наш контроллер постоянно работал и не останавливался. В нашем случае тело пустое, и наш контроллер, вернее его АЛУ, будет всё время висеть в данном цикле и ничего не делать до тех пор, пока мы не отключим питание, либо нам его не отключат в розетке, либо, не дай Бог, сгорит контроллер. То есть светодиод наш будет светиться постоянно.
Сегодня мы не будем пробовать нашу программу прошивать в микроконтроллер, и даже не будем пробовать ещё в виртуальном контроллере, то есть в программе симуляции, а попробуем симуляцию запустить в самой студии, так как на прошлом занятии мы в качестве отладчика и выбрали симулятор. Двойным щелчком мыши либо клавишей F9 мы установим точку останова на команде PORTD = 0b00000001; и, когда мы запустим отладку, то отладчик, как только увидит данную точку, должен будет в этом месте остановить выполнение программы, и мы сможем посмотреть, какие уровни и где у нас установились.
Чтобы запустить отладку, мы нажмём кнопку в виде зелёного треугольника и дождёмся, когда отладчик остановится на нашей красной точке
Здесь мы наблюдаем, что ещё у нас открылась карта нашей памяти, в которой нам пока ещё ничего не понятно. Если бы мы писали на ассемблере либо на машинном коде, то нам бы это было понятнее. Поэтому нас интересует другая. Карта. Для этого мы нажмём вот эту кнопочку
Данная кнопочка (I/O Wiev) откроет нам окно с данными наших портов ввода-вывода и прочей периферии
Нажмем в данном окне на строку PORTD и увидим в нижней половине окна, что весь наш регистр DDRD, отвечающий за направление отдельных ножек порта выставился весь в единички, то есть на выход
А дальше уже проблема. Чтобы нам посмотреть, как сработает следующая команда, которая включит нам нулевую ножку, отладчику необходимо остановиться на следующей строке кода, а у нас её нет, у нас только бесконечный цикл, который для отладчика — не строка. Поэтому мы пока остановим отладчик следующей кнопкой
И мы напишем какую-нибудь ещё строку, например, мы захотим, чтобы данный светодиод у нас погас, а зажегся следующий, который присоединен к следующей лапке, чтобы создать эффект бегущего огня
PORTD = 0b00000001;
PORTD = 0b00000010;
Конечно всё это на практике у нас не сработает, так как мы не успеем заметить свечение предыдущего светодиода. Чтобы задумка заработала практически, мы должны ещё с вами включить между данными командами задержку, а это тема уже других более поздних занятий. Но тем не менее мы данную команду включим, чтобы отладчику было где остановиться. Затем мы запустим заново отладку. Точка останова у нас также находится пока на той строке, на какой и была до этого. Запустим опять отладчик. Собирать проект перед отладкой необязательно, так как отладчик сам его пересоберет. Дожидаемся остановке отладчика на точке. В окошке с вводом-выводом опять нажмём на строке с нашим портом. Дальше начинаем шагать по программе. Для этого нажимаем следующую кнопку или функциональную клавишу F10, о чем нам подсказывает студия, как только мы подносим указатель мыши к данной кнопке
Теперь отладчик остановится на следующей строке
И теперь в окне ввода-вывода мы видим уже следующую картину
Мы видим, что самый левый бит, соответствующий нулевой ножке порта переключился в высокое логическое состояние, причём мы это видим не только в регистре PORTD, но и в регистре PIND, который отвечает за считывание состояния ножек порта D при его работе на ввод. Вот таким вот образом мы и отлаживаем наши программы.
Остановим наш отладчик, уберём все брекпоинты, а, самое главное, не забываем удалить ненужную команду, которая включает другую лапку.
После этого текст кода у нас должен будет остаться вот таким
Предыдущий урок Программирование МК AVR Следующий урок
Исходный код
Купить программатор можно здесь (продавец надёжный) USBASP USBISP 3.3
Смотреть ВИДЕОУРОК
Post Views: 14 264
narodstream.ru
Система команд микроконтроллеров AVR Перед тем, как приступить к рассмотрению системы команд, давайте вспомним некоторые основные архитектурные особенности микроконтроллера. Итак, микроконтроллер имеет своем составе 32 регистра. Первая их половина (R0-R15) не может быть использована в операциях с непосредственным операндом. Во второй половине есть специфические регистровые пары, которые могут использоваться в операциях пересылки данных между регистрами и памятью и некоторых других действий (X,Y и Z). Заметим к тому же, что «возможности» этих регистровых пар различны! Кроме регистров, микроконтроллер может иметь память данных (ОЗУ), обращение к которой производится при помощи регистровых пар (индексная адресация) или указанием 16-ти разрядного адреса. Микроконтроллер может только прочесть память данных в регистр или записать туда из регистра, никакие арифметические или логические операции с памятью данных невозможны. Ну и последнее — периферия, или регистры ввода-вывода (I/O). Можно прочитать данные из I/O в регистр общего назначения и записать из регистра общего назначения в I/O. Кроме этого, у части регистров ввода-вывода, а точнее — у тех, чей адрес не превышает 0x1F, возможна установка отдельных бит в состояние 0 или 1. Операнды команд будем обозначать следующим (стандартным) способом:
Итак, приступим. Для начала рассмотрим команды передачи данных.
Теперь рассмотрим арифметические и логические команды. Но перед этим освежим в памяти регистр состояния SREG — поскольку все команды будут изменять какие-либо биты в SREG. Регистр SREG находится в области регистров ввода-вывода, по адресу 0x3F (0x5F). Чтение и запись производится командами IN / OUT, кроме того, есть специальные команды установки и очистки конкретного бита в SREG. Ну и, естественно, команды условного перехода (ветвления) выполняются в зависимости от соcтояния битов SREG, но о ветвлениях — в следующем подразделе… Итак, в SREG имеются следующие биты:
Уффф… Что-то длинная страничка получается. Наверное, остальные команды опишу в >>> следующем уроке >>>
|
nikolaew.org
ПЛАН ЛЕКЦИИ
1. Режимы пониженного энергопотребления
2. Система команд AVR
3. Архитектура микроконтроллеров AVR
1. Режимы пониженного энергопотребления
AVR микроконтроллеры могут быть переведены программным путем в один из шести режимов пониженного энергопотребления.
Микроконтроллеры AVR mega64, mega103 и mega128 имеют еще одну примечательную архитектурную особенность, позволяющую значительно снизить энергопотребление всего кристалла в целом, когда в процессе работы возникают вынужденные паузы ожидания. В этом случае целесообразно уменьшить ток потребления центрального процессора и периферийных устройств как в активном режиме, так и в режиме холостого хода, понизив основную тактовую частоту микроконтроллера. Для этой цели на кристалле размещен специальный предделитель, позволяющий делить основную тактовую частоту на целое число в диапазоне от 2 до 129. Включение/выключение данной функции осуществляется одной короткой командой в программе.
AVR функционируют в широком диапазоне питающих напряжений от 1,8 до 6,0 Вольт. Температурные диапазоны работы микроконтроллеров AVR — коммерческий (0С…70С) и индустриальный (-40С…+85С). К сожалению, корпорация Atmel не выпускает и не планирует выпускать AVR для работы в автомобильном (-40С…+125С) и военном (-55С…+125С) температурных диапазонах.
Рис. 2. Структурная схема AVR
С точки зрения программиста AVR представляет собой 8-разрядный RISC микроконтроллер, имеющий быстрый Гарвардский процессор, память программ, память данных, порты ввода/вывода и различные интерфейсные схемы. Структурная схема микроконтроллера приведена на рис. 2. Гарвардская архитектура AVR реализует полное логическое и физическое разделение не только адресных пространств, но и информационных шин для обращения к памяти программ и к памяти данных, причем способы адресации и доступа к этим массивам памяти также различны. Подобное построение уже ближе к структуре цифровых сигнальных процессоров и обеспечивает существенное повышение производительности. Центральный процессор работает одновременно как с памятью программ, так и с памятью данных; разрядность шины памяти программ расширена до 16 бит. Следующим шагом на пути увеличения быстродействия AVR является использование технологии конвейеризации, вследствие чего цикл «выборка — исполнение» команды заметно сокращен. Например, у микроконтроллеров семейства MCS51 короткая команда выполняется за 12 тактов генератора (1 машинный цикл), в течение которого процессор последовательно считывает код операции и исполняет ее. В PIC-контроллерах фирмы Microchip, где уже реализован конвейер, короткая команда выполняется в течение 8 периодов тактовой частоты (2 машинных цикла). За это время последовательно дешифрируется и считывается код операции, исполняется команда, фиксируется результат и одновременно считывается код следующей операции (одноуровневый конвейер). Поэтому в общем потоке команд одна короткая команда реализуется за 4 периода тактовой частоты или за один машинный цикл.
В микроконтроллерах AVR тоже используется одноуровневый конвейер при обращении к памяти программ и короткая команда в общем потоке выполняется, как и в PIC-контроллерах, за один машинный цикл. Главное же отличие состоит в том, что этот цикл у AVR составляет всего один период тактовой частоты. Для сравнения, на рис. 3 приведены временные диаграммы при выполнении типовой команды для различных микроконтроллерных платформ.
2. Система команд AVR
Система команд AVR весьма развита и насчитывает до 133 различных инструкций. Почти все команды имеют фиксированную длину в одно слово (16 бит), что позволяет в большинстве случаев объединять в одной команде и код операции, и операнд(ы). Лишь немногие команды имеют размер в 2 слова (32 бит) и относятся к группе команд вызова процедуры CALL, длинных переходов в пределах всего адресного пространства JMP, возврата из подпрограмм RET и команд работы с памятью программ LPM.
Различают пять групп команд AVR: условного ветвления, безусловного ветвления, арифметические и логические операции, команды пересылки данных, команды работы с битами. В последних версиях кристаллов AVR семейства «mega» реализована функция аппаратного умножения, что придает новым микроконтроллерам еще больше привлекательности с точки зрения разработчика.
3. Архитектура микроконтроллеров AVR
Файл регистров быстрого доступа, содержит 32 8-разрядных рабочих регистра общего назначения связанных непосредственно с ALU. За один тактовый цикл из файла регистров выбираются два операнда, выполняется операция и результат вновь возвращается в файл регистров.
Шесть из 32 регистров могут быть использованы как три 16-разрядных регистра указателя косвенной адресации адресного пространства данных, обеспечивающие эффективное вычисление адресов. Один из этих указателей адреса используется, также, как указатель адреса для функции непрерывного просмотра таблиц. Эти 16-разрядные дополнительные регистры обозначаются X-регистр, Y-регистр и Z-регистр.
ALU поддерживает арифметические и логические операции между регистрами или между константой и регистром. Выполняются в ALU и операции с отдельными регистрами.
На рисунке показана AVR расширенная RISC архитектура микроконтроллеров ATmega603/103.
Рис. 5. Расширенная RISC архитектура микроконтроллеров ATmega603/103
В дополнение к операциям с регистрами, регистровый файл может использоваться и для обычной адресации памяти. Это объясняется тем, что файл регистров располагается по 32 самыми младшими адресами пространства данных, и к ним можно обращаться как к обычным ячейкам памяти.
Пространство памяти I/O содержит 64 адреса периферийных функций CPU таких как: регистры управления, таймеры/счетчики, аналого-цифровые преобразователи и другие I/O функции. К памяти I/O можно обращаться непосредственно или как к ячейкам пространства памяти соответствующим адресам регистра файлов $20 — $5F.
В микроконтроллерах AVR использованы принципы Гарвардской архитектуры — отдельные память и шины для программ и данных. При работе с памятью программ используется одноуровневый конвейер — в то время, как одна команда выполняется, следующая команда выбирается из памяти программ, Такой прием позволяет выполнять команду в каждом тактовом цикле. Памятью программ является внутрисистемно программируемая Flash память. За малым исключением AVR команды имеют формат одного 16-разрядного слова, в связи с чем каждый адрес памяти программ содержит одну 16-разрядную команду.
В процессе обработки прерываний и вызовов подпрограмм адрес возврата счетчика команд (PC) сохраняется в стеке. Стек размещается в SRAM данных и, следовательно размер стека ограничен только общим размером SRAM и уровнем ее использования. Все пользовательские программы в подпрограммах возврата (прежде, чем подпрограммы или прерывания будут выполняться) должны инициализировать указатель стека (SP). 16-разрядный указатель стека, с возможностью чтения/записи располагается в пространстве I/O.
AVR архитектура поддерживает пять различных режимов адресации 4000 байт SRAM данных. Гибкий модуль обработки прерываний имеет в пространстве I/O свой управляющий регистр с дополнительным битом разрешения глобального прерывания в регистре статуса. Все прерывания имеют свои векторы прерывания в таблице векторов прерывания, располагаемой в начале памяти программ. Приоритеты прерываний соответствуют положению векторов прерываний — прерывание с наименьшим адресом вектора имеет наивысший приоритет.
По разнообразию и количеству реализованных инструкций AVR больше похожи на CISC, чем на RISC процессоры. Например, у PIC-контроллеров система команд насчитывает до 75 различных инструкций, а у MCS51 она составляет 111. В целом, прогрессивная RISC архитектура AVR в сочетании с наличием регистрового файла и расширенной системы команд позволяет в короткие сроки создавать работоспособные программы с эффективным кодом как по компактности реализации, так и по скорости выполнения.
3.1.Регистровый файл быстрого доступа
Следующая отличительная черта архитектуры микроконтроллеров AVR — регистровый файл быстрого доступа, структурная схема которого показана на рис. 4. Каждый из 32-х регистров общего назначения длиной 1 байт непосредственно связан с арифметико-логическим устройством (ALU) процессора. Другими словами, в AVR существует 32 регистра — аккумулятора (сравните, например, с MCS51). Это обстоятельство позволяет в сочетании с конвейерной обработкой выполнять одну операцию в ALU за один машинный цикл. Так, два операнда извлекаются из регистрового файла, выполняется команда и результат записывается обратно в регистровый файл в течение только одного машинного цикла.
Рис. 3. Сравнительная характеристика некоторых микропроцессорных платформ
Шесть из 32-х регистров файла могут использоваться как три 16-разрядных указателя адреса при косвенной адресации данных. Один из этих указателей (Z Pointer) применяется также для доступа к данным, записанным в памяти программ микроконтроллера. Использование трех 16-битных указателей (X, Y и Z Pointers) существенно повышает скорость пересылки данных при работе прикладной программы.
Рис. 4. Регистровый файл
Регистровый файл занимает младшие 32 байта в общем адресном пространстве SRAM AVR. Такое архитектурное решение позволяет получать доступ к быстрой «регистровой» оперативной памяти микроконтроллера двумя путями — непосредственной адресацией в коде команды к любой ячейке и другими способами адресации ячеек SRAM.
В технической документации фирмы Atmel это полезное свойство носит название «быстрое контекстное переключение» и является еще одной отличительной особенностью архитектуры AVR, повышающей эффективность работы микроконтроллера и его производительность. Особенно заметно данное преимущество при реализации процедур целочисленной 16-битной арифметики, когда исключаются многократные пересылки между различными ячейками памяти данных при обработке арифметических операндов в ALU.
mc-plc.ru
Ассемблер – переводчик удобных для восприятия человеком команд в машинные коды для — страница №1/1
Как я уже писал в первой части, программа – это заданная программистом последовательность инструкций, каждая из которых предписывает МК выполнить определённое действие. Пример инструкции: загрузить в регистр с именем r16 константу 0x12. Приказ этот для МК в виде 16-битного числа выглядит так: 1110000100000010. Именно это число, которое называется КОД ОПЕРАЦИИ, и будет занесено в 16-битную ячейку памяти программ по соответствующему адресу. Как именно распределяется программа по ячейкам памяти программ – разберём немного позже. Последовательность инструкций в виде 16-битных чисел представляет собой программу для AVR в машинных кодах. Однако, процесс написания программы в машинных кодах очень неудобен для человека. Поэтому в помощь программисту существует АССЕМБЛЕР – переводчик удобных для восприятия человеком команд в машинные коды для МК. Инструкция «загрузить в регистр с именем r16 константу 0x12» на ассемблере выглядит так:
ldi r16,0x12
Сокращение «ldi» образовано от английских слов «load data immediate» — «загрузка данных непосредственная». МНЕМОНИКА команды – запись её в виде ldi Rd, K – достаточно легко запоминается человеком. СИСТЕМА КОМАНД микроконтроллера – это совокупность всех команд, которые могут быть применены к данному МК. Число входящих в систему команд различно для разных семейств МК. Мнемоники также различаются. Поэтому ассемблер для одного семейства МК существенно отличается от предназначенного для другого семейства.
Для ознакомления с системой команд AVR откройте главу 3 книги А.В. Евстифеева «Микроконтроллеры AVR семейства Mega. Руководство пользователя».
Программа на ассемблере представляет собой заданную программистом последовательность команд, записанных в виде мнемоник, с указанием конкретных констант, регистров, битов. Вот фрагмент такой программы:
out DDRB,r16 ; Записать в регистр DDRB данные из регистра r16
; Теперь вывод PB1 оказался настроен на выход
ldi r16,0x1D ; Аналогичные действия с регистрами. Выключить
out PORTB,r16 ; светодиод, который подключен к между PB1 и «общим»
rjmp start ; Перейти на метку start
Как видите, ничего сложного. Команды ассемблера не должны начинаться с самого начала строки, следует делать отступ. С самой первой строки пишутся МЕТКИ. Метки – это придуманные программистом для своего удобства слова. Ими помечаются те адреса в памяти программ, на которые возможны переходы. Встретив команду перехода на какую-либо метку, ассемблер подставит в код операции соответствующий этой метке адрес. Никто не запрещает программисту указывать адрес непосредственно в виде числа. Просто применение меток делает программу более наглядной и позволяет не следить за возможными изменениями адресов. Подобно тому, как Пупкин может сначала поселиться в гостиничном номере 105, а затем его переселят в номер 209. Но если просто сказать «Пойдём в номер Пупкина», мы разберёмся, по какому адресу идти в данный момент. После имени метки в ассемблере AVR следует ставить двоеточие. Недопустимо ставить в программе 2 метки с одинаковым именем.
После точки с запятой пишутся КОММЕНТАРИИ. Это пояснения, которые программист пишет для себя и тех, кто будет смотреть программу. Микроконтроллеру они не нужны, поэтому ассемблер их просто игнорирует.
Адреса ячеек памяти программ в AVR начинаются с нуля ($0000). Адрес последней ячейки и их общее количество зависят от модели МК. Например, у ATmega16 объём памяти программ составляет 8192 ячейки. Ячейки 16-битные, поэтому память программ занимает 16 килобайт. Память может измеряться и в словах. 1 слово занимает 16 бит или 2 байта. Таким образом, объём памяти ATmega16 составляет 8K слов. Адреса ячеек – от $0000 до $1FFF. Большинство инструкций занимают по 1 ячейке, некоторым необходимо 2. Максимальный размер программы для конкретного МК как раз и ограничивается объёмом его памяти программ.
Рис.2.1. Память программ AVR на примере ATmega16
Откуда МК узнаёт – какую именно команду ему предстоит выполнить сейчас? Адрес ячейки памяти программ со следующей исполняемой командой находится в специальном регистре, который называется ПРОГРАММНЫЙ СЧЁТЧИК или PC. Другое его название – СЧЁТЧИК КОМАНД. В микроконтроллерах AVR, в отличие от PIC, для программиста PC напрямую недоступен. При нормальной работе МК число в регистре PC после выполнения каждой команды автоматически увеличивается на 1 (или на 2, если команда занимает 2 ячейки). При переходах и ещё в некоторых случаях (прерывания, вызов подпрограмм и возврат из них) этот порядок нарушается, и в PC загружается тот адрес, на который выполняется переход. Таким образом, порядок выполнения инструкций, находящихся в памяти программ, соответствует последовательности значений PC.
Скорость работы МК определяется тактовой частотой. 1 машинный цикл AVR выполняется за время 1 периода тактового генератора. Таким образом, при частоте тактового генератора 1 МГц за 1 секунду произойдет 1 миллион тактов. Большая часть команд выполняется микроконтроллерами AVR за 1 машинный цикл. Это стало возможным благодаря конвейерной обработке команд. При отработке команд перехода и ещё некоторых нормальная работа конвейера нарушается, и возникают задержки от 1 до 4 тактов. Подробнее о работе конвейера и причинах возникновения задержек почитайте в книге А.В. Евстифеева «Микроконтроллеры AVR семейства Mega. Руководство пользователя», глава 2, раздел 2.3.
Источники тактового сигнала могут быть различными, по выбору программиста. Самые распространённые варианты – это внутренний RC-генератор (не требует подключения внешних деталей), генератор с внешней RC-цепочкой, или встроенный кварцевый генератор с подключаемым внешним резонатором. Возможно также использование тактового сигнала от внешнего источника. Нижняя граница тактовой частоты архитектурой самого МК не ограничивается, она может быть сколь угодно малой – хоть 1 такт в год от внешнего источника. Верхняя граница зависит от модели МК и нередко составляет 8 или 16 МГц. Подробности, как обычно, можете узнать из даташита на конкретный МК. Полезно прочитать главу 4 книги А.В. Евстифеева «Микроконтроллеры AVR семейства Mega. Руководство пользователя».
Адрес $0000 – это ВЕКТОР СБРОСА. Именно с размещённой по этому адресу команды начинается выполнение программы после включения питания или сброса МК. Значение адреса вектора сброса в некоторых МК может быть изменено, но этот случай нас пока не интересует. Сброс может быть вызван внутренними или внешними причинами. О внутренних разговор будет позже. Одной из причин сброса является падение напряжения питания Vcc ниже определённой величины. Для внешнего сброса предназначен вывод _RESET. Сброс происходит при удержании низкого уровня (не более 0,1 Vcc) на этом выводе не менее 1…3 мкс. При работе МК на выводе _RESET должно поддерживаться напряжение (0,9…1,0) Vcc. Во многих AVR имеется внутренний подтягивающий резистор, соединяющий _RESET c Vcc. В этом случае _RESET допустимо никуда не подключать, но для большей надёжности можно установить и дополнительный внешний резистор. Подробности о сбросе можете прочитать в книге А.В. Евстифеева «Микроконтроллеры AVR семейства Mega. Руководство пользователя», глава 4, раздел 4.4.
При сбросе во все регистры ввода/вывода (РВВ) заносятся начальные значения, заданные фирмой-производителем, а в PC загружается адрес вектора сброса. Выполнение программы после сброса начинается не мгновенно, а после некоторой задержки, для надёжности. По адресу $0000 программист обычно размещает команду безусловного перехода к инициализационной части программы, начало которой он обозначает меткой, например, “start”. Это делается для того, чтобы обойти размещённую в начале памяти программ таблицу векторов прерываний. О прерываниях ещё предстоит отдельный большой рассказ.
.org 0
……
…… (здесь переходы на обработку прерываний)
……
start: ldi r16,0x12
…… (отсюда продолжается программа)
В этом примере reset и start – метки. Директивой org программист указывает ассемблеру, по какому адресу следует поместить следующую команду в памяти программ. Таким образом, команда jmp start будет размещена по адресу $0000. Перед директивами в ассемблере AVR следует ставить точку. Какую команду следует применить здесь для перехода – двухбайтовую jmp или однобайтовую rjmp – зависит от конкретной модели AVR, смотрите в даташите. По какому адресу будет находиться первая команда инициализационной части (в примере это ldi) – нам в данном случае неважно, пусть ассемблер её сам расположит, а нам достаточно знать метку.
После сброса МК в PC загрузится вектор сброса $0000 и будет выполнена находящаяся по этому адресу команда безусловного перехода к метке start. Следовательно, в PC загрузится соответствующий метке start адрес, и выполнение программы продолжится с команды
ldi r16,0x12
Инициализационная часть программы (далее я буду называть её просто инициализацией МК) предназначена для программной настройки микроконтроллера после сброса под нужды программиста. Здесь выполняется настройка выводов портов на вход/выход, включение и настройка периферийных модулей, занесение требуемых начальных значений в регистры. После инициализации МК выполняет возложенные на него программистом задачи.
umotnas.ru