8-900-374-94-44
[email protected]
Slide Image
Меню

Машинные коды команд avr – AVR програмирование в кодах / AVR / Сообщество EasyElectronics.ru

Таблицы команд ассемблера AVR — (ЭЛЕКТРОНИКА

В этой статье я хочу представить еще один вариант таблиц команд ассемблера для микроконтроллеров AVR.

Из дополнительных материалов у приобретателей курса уже есть pdf документ с набором таблиц команд. Так в чем же основное отличие набора команд, который представлен ниже?

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

Сразу стоит отметить, что здесь представлены в основном только команды семейства tiny. Я намерено убрал команды семейства mega, что бы лишний раз не вносить путаницу.

Арифметические и логические команды

КомандаОписаниеДействиеЦиклыКод операцииФлаги
add Rd,RrAdd two RegistersRd←Rd+Rr10000 11rd dddd rrrrZ,C,S,N,V,H
adc Rd,RrAdd with Carry two RegistersRd←Rd+Rr+C10001 11rd dddd rrrrZ,C,S,N,V,H
adiw Rdl,KAdd Immediate to WordRdh:Rdl←Rdh:Rdl+K21001 0110 KKdd KKKKZ,C,S,N,V
sub Rd,RrSubtract two RegistersRd←Rd-Rr10001 10rd dddd rrrr Z,C,S,N,V,H
sbc Rd,RrSubtract with Carry two RegistersRd←Rd-Rr-C10000 10rd dddd rrrrZ,C,S,N,V,H
subi Rd,KSubtract Constant from RegisterRd←Rd-K11010 KKKK dddd KKKKZ,C,S,N,V,H
sbci Rd,KSubtract with Carry Constant from RegisterRd←Rd-K-C10100 KKKK dddd KKKKZ,C,S,N,V,H
sbiw Rdl,KSubtract Immediate from WordRdh:Rdl←Rdh:Rdl-K21001 0111 KKdd KKKKZ,C,S,N,V
and Rd,RrLogical AND RegistersRd←Rd AND Rr10010 00rd dddd rrrrZ,S,N
andi Rd,KLogical AND Register and ConstantRd←Rd AND K10111 KKKK dddd KKKKZ,S,N
or Rd,RrLogical OR RegistersRd←Rd OR Rr10010 10rd dddd rrrrZ,S,N
ori Rd,KLogical OR Register and ConstantRd←Rd OR K10110 KKKK dddd KKKKZ,S,N
eor Rd,RrExclusive OR RegistersRd←Rd EOR Rr10010 01rd dddd rrrrZ,S,N
com RdOne’s complementRd←0xFF-Rd11001 010d dddd 0000Z,S,N
neg RdTwo’s complementRd←0x00-Rd11001 010d dddd 0001Z,C,S,N,V,H
sbr Rd,KSet Bit (s) in Register Rd←Rd OR K10110 KKKK dddd KKKKZ,S,N
cbr Rd,KClear Bit (s) in RegisterRd←Rd AND (0xFF- K)10111 KKKK dddd KKKKZ,S,N
inc RdIncrementRd←Rd+111001 010d dddd 0011Z,S,N,V
dec RdDecrementRd←Rd-111001 010d dddd 1010Z,S,N,V
tst RdTest for Zero or MinusRd←Rd AND Rd10010 00dd dddd ddddZ,S,N
clr RdClear RegisterRd←Rd EOR Rd10010 01dd dddd ddddZ,S,N
ser RdSet RegisterRd←0xFF11110 1111 dddd 1111None

Команды пересылки данных

КомандаОписаниеДействиеЦиклыКод операцииФлаги
mov Rd,RrMove Between RegistersRd←Rr10010 11rd dddd rrrrNone
movw Rd,RrCopy Register WordRd+1:Rd←Rr+1:Rr10000 0001 dddd rrrrNone
ldi Rd,KLoad ImmediateRd←K11110 KKKK dddd KKKKNone
ld Rd,XLoad IndirectRd← (X)21001 000d dddd 1100None
ld Rd,X+Load Indirect and
Post-Inc.
Rd← (X), X←X+121001 000d dddd 1101None
ld Rd, -XLoad Indirect and
Pre-Dec.
X←X-1, Rd← (X)21001 000d dddd 1110None
ld Rd,YLoad IndirectRd← (Y)21000 000d dddd 1000None
ld Rd,Y+Load Indirect and
Post-Inc.
Rd← (Y), Y←Y+121001 000d dddd 1001None
ld Rd, -YLoad Indirect and
Pre-Dec.
Y←Y-1, Rd← (Y)21001 000d dddd 1010None
ldd Rd,Y+qLoad Indirect with DisplacementRd← (Y+q)210q0 qq0d dddd 1qqqNone
ld Rd,ZLoad IndirectRd← (Z)21000 000d dddd 0000None
ld Rd,Z+Load Indirect and
Post-Inc.
Rd← (Z), Z←Z+121001 000d dddd 0001None
ld Rd, -ZLoad Indirect and
Pre-Dec.
Z←Z-1, Rd← (Z)21001 000d dddd 0010None
ldd Rd,Z+qLoad Indirect with DisplacementRd← (Z+q)210q0 qq0d dddd 0qqq
None
lds Rd,kLoad Direct from SRAMRd← (k)21001 000d dddd 0000kkkk kkkk kkkk kkkkNone
st X,RrStore Indirect(X) ←Rr21001 001r rrrr 1100None
st X+,RrStore Indirect and
Post-Inc.
(X) ←Rr, X←X+121001 001r rrrr 1101None
st -X,RrStore Indirect and
Pre-Dec.
X←X-1, (X) ←Rr21001 001r rrrr 1110None
st Y,RrStore Indirect(Y) ←Rr21000 001r rrrr 1000None
st Y+,RrStore Indirect and
Post-Inc.
(Y) ←Rr, Y←Y+121001 001r rrrr 1001None
st -Y,RrStore Indirect and
Pre-Dec.
Y←Y-1, (Y) ←Rr21001 001r rrrr 1010None
std Y+q,RrStore Indirect with Displacement(Y+q) ← Rr210q0 qq1r rrrr 1qqqNone
st Z,RrStore Indirect(Z) ←Rr21000 001r rrrr 0000None
st Z+,RrStore Indirect and
Post-Inc.
(Z) ←Rr, Z←Z+121001 001r rrrr 0001None
st -Z,RrStore Indirect and
Pre-Dec.
Z←Z-1, (Z) ←Rr2
1001 001r rrrr 0010
None
std Z+q,RrStore Indirect with Displacement(Z+q) ← Rr210q0 qq1r rrrr 0qqqNone
sts k,RrStore Direct to SRAM(k) ←Rr21001 001r rrrr 0000kkkk kkkk kkkk kkkkNone
lpmLoad Program MemoryR0← (Z)31001 0101 1100 1000None
lpm Rd,ZLoad Program MemoryRd← (Z)31001 000d dddd 0100None
lpm Rd,Z+Load Program Memory
and Post-Inc.
Rd← (Z), Z←Z+131001 000d dddd 0101None
spm
Store Program Memory(Z) ←R1:R01001 0101 1110 1000None
in Rd,PIn PortRd←P11011 0PPd dddd PPPPNone
out P,RrOut PortP←Rr11011 1PPr rrrr PPPPNone
push RrPush Register in StackSTACK←Rr, SP←SP-121001 001r rrrr 1111None
pop RdPop Register from StackSP←SP+1, Rd←STACK21001 000d dddd 1111None

Команды передачи управления

КомандаОписаниеДействиеЦиклыКод операцииФлаги
rjmp kRelative JumpPC←PC+k+121100 kkkk kkkk kkkkNone
ijmpIndirect Jump to (Z)PC← (Z)21001 0100 0000 1001None
*jmp kDirect JumpPC←k31001 010k kkkk 110kkkkk kkkk kkkk kkkkNone
rcall kRelative Subroutine CallSTACK←PC+1,PC←PC+k+1,SP←SP-2 or 3¾1101 kkkk kkkk kkkkNone
icallIndirect Call to (Z)STACK←PC+1, PC← (Z),SP←SP-2 or 3¾1001 0101 0000 1001None
*call kDirect Subroutine CallSTACK←PC+1, PC←k,SP←SP-2 or 34/51001 010k kkkk 111kkkkk kkkk kkkk kkkkNone
retSubroutine ReturnPC←STACK,
SP←SP+2 or 3
4/51001 0101 0000 1000None
retiInterrupt ReturnPC←STACK,
SP←SP+2 or 3
4/51001 0101 0001 1000I
cpse Rd,RrCompare, Skip if Equalif (Rd=Rr)
PC←PC+2 or 3
½/30001 00rd dddd rrrrNone
cp Rd,RrCompareRd-Rr10001 01rd dddd rrrrZ,C,S,
N,V,H
cpc Rd,RrCompare with CarryRd-Rr-C10000 01rd dddd rrrrZ,C,S,
N,V,H
cpi Rd,KCompare Register with ImmediateRd-Rr-K10011 KKKK dddd KKKKZ,C,S,
N,V,H
sbrc Rr,bSkip if Bit in
Register is Cleared
if (Rr (b)=0)
PC←PC+2 or 3
½/31111 110r rrrr obbbNone
sbrs Rr,bSkip if Bit in
Register is Set
if (Rr (b)=1)
PC←PC+2 or 3
½/31111 111r rrrr obbbNone
sbic P,bSkip if Bit in IO
Register is Cleared
if (P (b)=0)
PC←PC+2 or 3
½/31001 1001 PPPP PbbbNone
sbis P,bSkip if Bit in IO
Register is Set
if (P (b)=1)
PC←PC+2 or 3
½/31001 1011 PPPP PbbbNone
brbc s,kBranch if Status
Flag is Cleared
if (SREG (s)=0)
PC←PC+k+1
½1111 01kk kkkk ksssNone
brbs s,kBranch if Status
Flag is Set
if (SREG (s)=1)
PC←PC+k+1
½1111 00kk kkkk ksssNone
brcc kBranch if Carry
Flag is Clearsd
if (C=0) PC←PC+k+1½1111 01kk kkkk k000None
brcs kBranch if Carry
Flag is Set
if (C=1) PC←PC+k+1½1111 00kk kkkk k000None
brsh kBranch if Same
or Higher
if (C=0) PC←PC+k+1½1111 01kk kkkk k000None
brlo kBranch if Lowerif (C=1) PC←PC+k+1½1111 00kk kkkk k000None
brne kBranch if Not Equalif (Z=0) PC←PC+k+1½1111 01kk kkkk k001None
breq kBranch if Equalif (Z=1) PC←PC+k+1½1111 00kk kkkk k001None
brpl kBranch if Plusif (N=0) PC←PC+k+1½1111 01kk kkkk k010None
brmi kBranch if Minusif (N=1) PC←PC+k+1½1111 00kk kkkk k010None
brvc kBruach if Overflow
Flag is Cleared
if (V=0) PC←PC+k+1½1111 01kk kkkk k011None
brvs kBranch if Overflow
Flag is Set
if (V=1) PC←PC+k+1½1111 00kk kkkk k011None
brge kBranch if Greate or
Equal, Signed
if (S=0) PC←PC+k+1½1111 01kk kkkk k100None
brlt kBranch if Less than
Zero, Signed
if (S=1) PC←PC+k+1½1111 00kk kkkk k100None
brhc kBranch if Half Carry
Flag is Cleared
if (H=0) PC←PC+k+1½1111 01kk kkkk k101None
brhs kBranch if Half Carry
Flag is Set
if (H=1) PC←PC+k+1½1111 00kk kkkk k101None
brtc kBranch if Transfer
Flag is Cleared
if (T=0) PC←PC+k+1½1111 01kk kkkk k110None
brts kBranch if Transfer
Flag is Set
if (T=1) PC←PC+k+1½1111 00kk kkkk k110None
brid kBranch if Interrupt
Disable
if (T=0) PC←PC+k+1½1111 01kk kkkk k111None
brie kBranch if Interrupt
Enable
if (T=1) PC←PC+k+1½1111 00kk kkkk k111None

*Обратите внимание! Команды jmp и call не поддерживаются микроконтроллерами семейства tiny, но так как они часто используются при программировании семейства mega, то я решил их так же внести в таблицу, что бы вы не забывали о их существовании.

 

Команды условных переходов по состоянию флагов SREG

ПроверкафлагаКоманда условногопереходаАльтернативнаяФорма написанияУсловие перехода
Cbrbc 0,kbrcc kПереход если флаг переноса установлен
brsh kПереход если больше или равно
brbs 0,kbrcs kПереход если флаг переноса сброшен
brlo kПереход если меньше
Zbrbc 1,kbreq kПереход если равно
brbs 1,kbrne kПереход если не равно
Nbrbc 2,kbrpl kПереход если плюс
brbs 2,kbrmi kПереход если минус
Vbrbc 3,kbrvc kПереход если флаг дополнительного кода сброшен
brbs 3,kbrvs kПереход если флаг дополнительного кода установлен
Sbrbc 4,kbrge kПереход если больше или равно нулю (знаковое)
brbs 4,kbrlt kПереход если меньше нуля (знаковое)
Hbrbc 5,kbrhc kПереход если флаг половинного переноса сброшен
brbs 5,kbrhs kПереход если флаг половинного переноса установлен
Tbrbc 6,kbrtc kПереход если флаг хранения бита сброшен
brbs 6,kbrts kПереход если флаг хранения бита установлен
Ibrbc 7,kbrid kПереход если прерывания запрещены
brbs 7,kbrie kПереход если прерывания разрешены

Команд битовых операций

КомандаОписаниеДействиеЦиклыКод операцииФлаги
sbi P,bSet Bit in I/O ReristerI/O (P,b) ←121001 1010 PPPP PbbbNone
cbi P,bClear Bit in I/OReristerI/O (P,b) ←021001 1000 PPPP PbbbNone
lsl RdLogical Shift LeftRd (n+1) ←Rd (n), Rd (0) ←010000 11dd dddd ddddZ,C,N,V
lsr RdLogical Shift RightRd (n) ←Rd (n+1), Rd (7) ←011001 010d dddd 0110Z,C,N,V
rol RdRotate Left through CarryRd (0) ←C, Rd (n+1) ←Rd (n), C←Rd (7)10001 11dd dddd ddddZ,C,N,V
ror RdRotate Right through CarryRd (7) ←C, Rd (n) ←Rd (n+1), C←Rd (0)11001 010d dddd 0111Z,C,N,V
asr RdArithmetic Shift RightRd (n) ←Rd (n+1),
n=0…6
11001 010d dddd 0101Z,C,N,V
swap RdSwap NibblesRd (3…0) ←Rd (7…4),Rd (7…4) ←Rd (3…0)11001 010d dddd 0010None
bst Rr,bBit Store from
Rerister to T
T←Rr (b)11111 101b bbbb 0bbbT
bld Rd,bBit Load from T
to Rerister
Rd (b) ←T11111 100b bbbb 0bbbNone
bset sFlag SetSREG (s) ←111001 0100 0sss 1000SREG (s)
bclr sFlag ClearSREG (s) ←011001 0100 1sss 1000SREG (s)
secSet CarryC←111001 0100 0000 1000C
clcClear CarryC←011001 0100 1000 1000C
sezSet Zero FlagZ←111001 0100 0001 1000Z
clzClear Zero FlagZ←011001 0100 1001 1000Z
senSet Negative FlagN←111001 0100 0010 1000N
clnClear Negative FlagN←011001 0100 1010 1000N
sevSet Twos Complement OverflowV←111001 0100 0011 1000V
clvClear Twos Complement OverflowV←011001 0100 1011 1000V
sesSet Signed Test FlagS←111001 0100 0100 1000S
clsClear Signed Test FlagS←011001 0100 1100 1000S
sehSet Half Carry FlagH←111001 0100 0101 1000H
clhClear Half Carry FlagH←011001 0100 1101 1000H
setSet Transfer bitT←111001 0100 0110 1000T
cltClear Transfer bitT←011001 0100 1110 1000T
seiGlobal Interrupt EnableI←111001 0100 0111 1000I
cliGlobal Interrupt DisableI←011001 0100 1111 1000I

Команды управления процессором

КомандаОписаниеДействиеЦиклыКод операцииФлаги
nopNo operation10000 0000 0000 0000None
sleepSleep11001 0101 1000 1000None
wdrWatchdog Reset11001 0101 1010 1000None

 

 

Раздел: Программирование микроконтроллеров для начинающих

www.mcu4you.ru

Справочник по командам ассемблера AVR

Все команды этой группы выполняют переход (PC ← PC + A + 1) при разных условиях.

МнемоникаОписаниеОперацияФлаги
ADD Rd, RrСложение двух регистровRd ← Rd + RrZ, C, N, V, H
ADC Rd, RrСложение двух регистров с переносомRd ← Rd + Rr + СZ, C, N, V, H
SUB Rd, RrВычитание двух регистровRd ← Rd — RrZ, 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 + KZ, C, N, V, S
SBIW Rd, KВычитание константы из регистровой парыR(d+1):Rdl ← R(d+1):Rd — KZ, C, N, V, S
SUBI Rd, KВычитание константы из регистраRd ← Rd — KZ, C, N, V, H
SBCI Rd, KВычитание константы из регистра с заёмомRd ← Rd — K — СZ, C, N, V, H
INC RdИнкремент регистраRd ← Rd + 1Z, N, V
DEC RdДекремент регистраRd ← Rd – 1Z, N, V
MUL Rd, RrУмножение чисел без знакаR1:R0 ← Rd * RrZ, C
MULS Rd, RrУмножение чисел со знакомR1:R0 ← Rd * RrZ, C
MULSU Rd, RrУмножение числа со знаком с числом без знакаR1:R0 ← Rd * RrZ, C
FMUL Rd, RrУмножение дробных чисел без знакаR1:R0 ← (Rd * Rr) << 1Z, C
FMULS Rd, RrУмножение дробных чисел со знакомR1:R0 ← (Rd * Rr) << 1Z, C
FMULSU Rd, RrУмножение дробного числа со знаком с числом без знакаR1:R0 ← (Rd * Rr) << 1Z, C
МнемоникаОписаниеОперацияФлаги
CBR Rd, KОчистка разрядов регистраRd ← Rd and (0FFH – K)Z, N, V
SBR Rd, KУстановка разрядов регистраRd ← Rd or KZ, N, V
CBI P, bСброс разряда I/O-регистраP.b ← 0
SBI P, bУстановка разряда I/O-регистраP.b ← 1
BCLR sСброс флага SREGSREG.s ← 0SREG.s
BSET sУстановка флага SREGSREG.s ← 1SREG.s
BLD Rd, bЗагрузка разряда регистра из флага TRd.b ← T
BST Rr, bЗапись разряда регистра во флаг TT ← Rd.bT
CLCСброс флага переносаC ← 0C
SECУстановка флага переносаC ← 1C
CLNСброс флага отрицательного числаN ← 0N
SENУстановка флага отрицательного числаN ← 1N
CLZСброс флага нуляZ ← 0Z
SEZУстановка флага нуляZ ← 1Z
CLIОбщий запрет прерыванийI ← 0I
SEIОбщее разрешение прерыванийI ← 1I
CLSСброс флага знакаS ← 0S
SESУстановка флага знакаS ← 1S
CLVСброс флага переполнения дополнительного кодаV ← 0V
SEVУстановка флага переполнения дополнительного кодаV ← 1V
CLTСброс пользовательского флага TT ← 0T
SETУстановка пользовательского флага TT ← 1T
CLHСброс флага половинного переносаH ← 0H
SEHУстановка флага половинного переносаH ← 1H
МнемоникаОписаниеОперацияФлаги
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+Загрузка данных из памяти программы с пост-инкрементом ZRd ← {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. Часть 2. — Информация по МК, FAQ — Микроконтроллеры — Каталог статей

.

Теперь предстоит разобраться – как именно программист может выполнять операции с регистрами 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. Максимальный размер программы для конкретного МК как раз и ограничивается объёмом его памяти программ.


Откуда МК узнаёт – какую именно команду ему предстоит выполнить сейчас? Адрес ячейки памяти программ со следующей исполняемой командой находится в специальном регистре, который называется ПРОГРАММНЫЙ СЧЁТЧИК или 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 МГц. Подробности, как обычно, можете узнать из даташита на конкретный МК.

Адрес $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 части:


1. Для перевода удобных человеку команд в машинные коды предназначен ассемблер.
2. Набор возможных действий программиста с микроконтроллером определяется системой команд этого МК.
3. Каждая команда имеет определённую мнемонику для лучшего запоминания.
4. При написании программы на ассемблере применяются метки, комментарии и директивы.
5. Максимальный размер программы ограничивается объёмом памяти программ выбранного МК.
6. Порядок выполнения находящихся в памяти программ команд соответствует последовательности значений программного счётчика PC.
7. Скорость работы МК определяется тактовой частотой. 1 такт работы AVR выполняется за 1 период тактовой частоты.
8. Тактирование AVR возможно от внутреннего RC-генератора, подключение внешних деталей при этом не требуется.
9. Выполнение программы после включения питания или сброса МК начинается с адреса вектора сброса (для AVR обычно $0000).
10. Сброс может быть вызван падением напряжения питания ниже определенной величины, воздействием на вывод _RESET, а также некоторыми другими причинами.
11. Программа, как правило, начинается с команды безусловного перехода к инициализационной части.
12. Инициализация предназначена для программной настройки МК после сброса под нужды программиста.

© picmaniac

cxema21.ru

AVR Пишем код на СИ. Зажигаем светодиод

&nbsp

&nbsp

&nbsp

Урок 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 микроконтроллеры — система команд

Система команд микроконтроллеров AVR

Перед тем, как приступить  к рассмотрению системы команд, давайте вспомним некоторые основные архитектурные особенности микроконтроллера.

Итак, микроконтроллер имеет  своем составе 32 регистра. Первая их половина (R0-R15) не может быть использована в операциях с непосредственным операндом. Во второй половине есть специфические регистровые пары, которые могут использоваться в операциях пересылки данных между регистрами и памятью и некоторых других действий (X,Y и Z). Заметим к тому же, что «возможности» этих регистровых пар различны!

Кроме регистров, микроконтроллер может иметь память данных (ОЗУ), обращение к которой производится при помощи регистровых пар (индексная адресация) или указанием 16-ти разрядного адреса. Микроконтроллер может только прочесть память данных в регистр или записать туда из регистра, никакие арифметические или логические операции с памятью данных невозможны.

Ну и последнее — периферия, или регистры ввода-вывода (I/O). Можно прочитать данные из I/O в регистр общего назначения и записать из регистра общего назначения в I/O. Кроме этого, у части регистров ввода-вывода, а точнее — у тех, чей адрес не превышает 0x1F, возможна установка отдельных бит в состояние 0 или 1.

Операнды команд будем обозначать следующим (стандартным) способом:

Rdрегистр — приемник, место, куда сохраняется результат выполнения команды
Rsрегистр — источник в двухоперандных командах. Его значение после выполнения команды не изменяется.
I/Oрегистр ввода-вывода, или периферия. Это порты, таймеры и т.д.
K8-ми разрядная константа в операциях со «старшими» регистрами общего назначения (R16-R31)
bНомер бита в операциях с регистрами ввода-вывода
A16-ти разрядный адрес при работе с памятью данных
q 6-ти разрядное смещение при работе с памятью данных
X Регистровая пара X. Состоит их регистров XL (R26) и XH (R27)
Y Регистровая пара Y. Состоит их регистров YL (R28) и YH (R29)
Z Регистровая пара Z. Состоит их регистров ZL (R30) и ZH (R31)

Итак, приступим. Для начала рассмотрим команды передачи данных.

MOV Rd,RsЭта команда копирует содержимое регистра Rs в регистр Rd. Содержимое Rs не изменяется, предыдущее содержимое Rd теряется. Пример:

mov R3,R19 ; содержимое R19 копируется в R3

Работает со всеми регистрами. Биты признаков не изменяет.

LDI Rd,KЗагружает в регистр Rd 8-ми разрядную константу. Работает со старшими регистрами (R16-R31). Пример:

ldi R16,1 ; загружает в R16 значение 1

Биты признаков не изменяет. Если необходимо загрузить константу в младший регистр, то это делается двумя командами:

ldi R16,1 ; загружает в R16 значение 1
mov R4,R16; и копирует в R4

LD Rd,XЗагружает в регистр Rd байт из памяти данных, адрес ячейки памяти в регистровой паре X. Содержимое регистровой пары X не изменяется. Например:

ldi XL,0 ; загружает младший байт регистровой пары Х
ldi XH,2 ; -//- старший байт регистровой пары Х
ld R5,X  ; байт из ОЗУ с адресом 0x200 загружается в R5

Биты признаков не изменяет.

LD Rd,X+Аналогично предыдущей команде, но содержимое регистровой пары X после выполнения пересылки данных увеличивается на 1. Например:

ldi XL,0 ; загружает младший байт регистровой пары Х
ldi XH,2 ; -//- старший байт регистровой пары Х
ld R5,X+  ; байт из ОЗУ с адресом 0x200 загружается в R5
ld R6,X+  ; байт из ОЗУ с адресом 0x201 загружается в R6

Биты признаков не изменяет.

LD Rd,-XАналогично предыдущей команде, но содержимое регистровой пары X перед выполнением пересылки данных уменьшается на 1. Например:

ldi XL,0 ; загружает младший байт регистровой пары Х
ldi XH,2 ; -//- старший байт регистровой пары Х
ld R5,-X  ; байт из ОЗУ с адресом ox1FF загружается в R5
ld R6,-X  ; байт из ОЗУ с адресом 0x1FE загружается в R6

Биты признаков не изменяет.

LD Rd,Y

LD Rd,Y+

LD Rd,-Y

LD Rd,Z

LD Rd,Z+

LD Rd,-Z

Эти команды работают абсолютно идентично трем ранее описанным, за исключением того, что индексным регистром является не X, а Y и Z. Наличие трех пар регистров дает возможность эффективной работы с блоками памяти, например:

ldi XL,0x00 ;\первый блок памяти
ldi XH,0x02 ; регистровая пара X указывает на адрес 0x200
ldi YL,0x80 ;\второй блок памяти
ldi YH,0x01 ; регистровая пара Y указывает на адрес 0x180
ldi R16,10  ; счетчик на 10

LOOP:

ld R5,X+  ; в R5 из первого блока, X указывает на следующий!
st Y+,R5  ; из R5 во второй блок, Y также — на следующий
dec R16  ; и так — 10 раз!
brne LOOP

В результате выполнения этого цикла 10 байт памяти, начиная с адреса 0x200 будут скопированы в область памяти с адресом 0x180

LDD Rd,Y+q

LDD Rd,Z+q

Регистровые пары Y и Z, кроме вышеописанных методов обращения к памяти данных, имеют еще один. В этом случае в регистр Rd загружается байт из ячейки памяти, чей адрес вычисляется как содержимое регистровой пары плюс 6-ти разрядное смещение. Содержимое регистровой пары не изменяется! Например:

ldi YL,0     ; \
ldi YH,2     ; регистровая пара Y указывает на адрес 0x200
ldd R5,Y+5    ; байт из ОЗУ с адресом 0x205 загружается в R5
ldd R6,Y+10   ; байт из ОЗУ с адресом 0x210 загружается в R6

Такой режим адресации невозможен для регистровой пары X. Значение смещения q — от 0 до 63.

Мы рассмотрели команды LD и LDD, которые обеспечивают пересылку данных из памяти данных в регистр общего назначения. Естественно, что для каждой команды LD и LDD имеется «обратная» ей команда — записи в память данных из регистра. Эти команды имеют мнемоники соотвественно ST и STD (store). Например:

ldd R5,Y+5    ; байт из ОЗУ с адресом 0x205 загружается в R5
std Y+6,R5    ; байт из R5 записывается в ОЗУ с адресом 0x206

Думаю, что совершенно нет необходимости расписывать каждую из них в отдельности…

LDS Rd,A

STS A,Rs

Команда LDS загрузит в регистр Rd содержимое ячейки памяти данных с адресом A, где A — шестнадцатиразрядная константа. В этом случае нет необходимости предварительно загружать регистровую пару, но сама команда займет два слова программной памяти, а не одно, как предыдущие. Например:

lds R5,0x240  ; байт из ОЗУ с адресом 0x240 загружается в R5
sts 0x060,R5 ; байт R5 в ОЗУ с адресом 0x060

Парой для команды LDS является команда STS — записывающая содержимое регистра в память.

LPMК командам пересылки данных надо отнести и очень специфичную команду LPM, которая пересылает в R0 байт памяти программ, на который указывает региcтровая пара Z. Напомню, что память программ и память данных между собой никак не пересекаются. Данная команда используется в основном для чтения таблиц констант, располагаемых в памяти программ. Например:

TABLE: db 4,6,8,2,3,5,0

;……
ldi ZL,low(TABLE*2)
ldi ZH,hi(TABLE*2)
LPM  ; в R0 будет занесено число 4

Содержимое регистровой пары Z не изменяется, биты признаков — тоже. Вообще, ни одна команда пересылки данных не изменяет  признаков.

Важно! Поскольку для команды LPM адресация побайтная, а память программ адресуется словами (слово = 2 байта), то необходимо при загрузке адреса таблицы адрес умножить на 2!

IN Rd, I/OКоманда IN прочтет байт из регистра ввода-вывода в регистр общего назначения, например:

in R18,PINA ; прочитать состояние входных линий порта A в R18
in R1,TCCR0 ; прочитать в R1 счетчик таймера 0

Работает со всеми регистрами, биты признаков не изменяет.

OUT I/O, Rs А эта — из регистра выведет в порт.
PUSH Rs

POP Rd

 Эти команды предназначены для работы со стеком. Команда PUSH поместит Rs в стек, после выполнения команды указатель стека уменьшается на единицу. Команда POP извлечет байт из стека и поместит его в Rd. Соответственно, указатель стека увеличится на единицу.

Указатель стека должен быть установлен (как правило — на последний байт ОЗУ) при старте программы!

Теперь рассмотрим арифметические и логические команды. Но перед этим освежим в памяти регистр состояния SREG — поскольку все команды будут изменять какие-либо биты в SREG. 

Регистр SREG находится в области регистров ввода-вывода, по адресу 0x3F (0x5F). Чтение и запись производится командами IN / OUT, кроме того, есть специальные команды установки и очистки конкретного бита в SREG.  Ну и, естественно, команды условного перехода (ветвления) выполняются в зависимости от соcтояния битов SREG, но о ветвлениях — в следующем подразделе…

Итак, в SREG имеются следующие биты:

ISREG.7

Бит разрешения прерывания. Если он = 0, то все прерывания в МК запрещены. Если он =1, то разрешением прерываний будут управлять соответствующие биты периферии.

TSREG.6

Битовый аккумулятор. С этим битом работают команды BST  и BLD

HSREG.5

Флаг переноса из младшей тетрады

SSREG.4

Sign — ислючающее ИЛИ битов N и V

VSREG.3

oVerflow — переполнение

NSREG.2

Negative — Результат операции < 0

ZSREG.1

Zero — Результат операции равен нулю

CSREG.0

Carry — Флаг переноса

 

ADD Rd,Rs

Сложение Rd и Rs, результат помещается в Rd. Изменяемые признаки: H V N Z C

ADC Rd,Rs

То же, что и ADD, но еще прибавляется C-разряд. Используется при работе с числами разрядностью более байта:

add R18,R20 ; сложили мл байты — может быть перенос!
adc R19,R21 ; сложили старшие с учетом этого переноса

Изменяемые признаки: H V N Z C

ADIW Rdl,q

Сложение пары регистров с константой (q — от 0 до 63). Работает с четырьмя старшими парами регистров, то есть Z,Y,X и R25:R24 и используется в основном для операций с указателями.

Изменяемые признаки: V N Z C

SUB Rd,Rs

Вычитание Rs из Rd, результат помещается в Rd. Изменяемые признаки: H V N Z C

SUBI Rd,K

Вычитание из Rd константы K. Изменяемые признаки: H V N Z C. Отметим, что команды сложения с константой в системе команд почему-то нет! Что, конечно, очень неудобно. Если нужно прибавить к регистру, например, число 10 — следует написать

subi  R16, -10

Но тут надо помнить, что признаки будут установлены «неправильно»!  Работает со старшими регистрами

SBC Rd,Rs

Вычитание Rs из Rd с учетом переноса. Результат в Rd. Изменяемые признаки: H V N Z C

SBCI Rd,K

Вычитание константы K из Rd с учетом переноса. Результат в Rd.  Работает со старшими регистрами. Изменяемые признаки: H V N Z C

SBIW Rdl,q

Вычитание из пары регистров константы. См. описание ADIW

AND Rd,Rs

Логическое «И» Rd и Rs, результат помещается в Rd. Изменяемые признаки: V N Z

Суть логического «И» — в Rd будут установлены в состояние лог. 1 те биты, которые были равны 1 и в Rd и в Rs, остальные сбрасываются в 0

ANDI Rd,K

То же, только вместо Rs — константа K.  Работает со старшими регистрами

OR Rd,Rs

Логическое «ИЛИ» Rd и Rs, результат помещается в Rd. Изменяемые признаки: V N Z

Суть логического «ИЛИ» — в Rd будут установлены в состояние лог. 1 те биты, которые были равны 1 или в Rd, или в Rs, остальные сбрасываются в 0

ORI Rd,K

Логическое «ИЛИ» Rd и константы K.   Работает со старшими регистрами

EOR Rd,Rs

Исключающее «ИЛИ» Rd и Rs, результат помещается в Rd. Изменяемые признаки: V N Z

Суть исключающего «ИЛИ» — в Rd будут установлены в состояние лог. 1 те биты, которые были не равны  в Rd, и в Rs, Следует заметить, что нет команды «исключающее ИЛИ» с константой!

COM Rd

Изменит все биты Rd на противоположные. Внимание! На самом деле эта команда выполняется как 0xFF-Rd !  Результат — то один, но в результате выполнения команды будет установлен C-разряд!   Изменяемые признаки: V N Z С

NEG Rd

Изменение знака Rd. Вычисляется как 0x00 — Rd 
Изменяемые признаки: H V N Z С 

SBR Rd,K

Совершенно непонятно, зачем в систему команд введена эта мнемоника. Set Bit(s) in Register  — это та же операция «логическое ИЛИ». Наверное, для того, чтобы в даташите гордо заявить — 118 Powerful Instructions!, хотя на самом деле добрая пятая часть дублируется. Короче, см. описание ORI Rd,K

CBR Rd,KПо сути то же самое. На самом деле — ANDI Rd,Not(K)
INC Rd
DEC
Rd

Инкремент / декремент Rd. Думаю, тут все ясно… Изменяемые признаки: N Z V

TST Rs

Установка признаков по содержимому Rs. На самом деле вычисляется как AND Rs, Rs. Изменяемые признаки: N Z V

CLR Rd

Очистка Rd (занесение в Rd нуля). Выполняется как EOR Rd,Rd , поэтому изменяет признаки: N Z V

SER Rd

Занесение константы 0xFF в Rd. Именно так и выполняется — LDI Rd, 0xFF
Соответственно признаков не меняет, смысла в этой мнемонике также не наблюдается.

 Уффф… Что-то длинная страничка получается. 

Наверное, остальные команды опишу в >>> следующем уроке  >>>

 

nikolaew.org

Питание, система команд и архитектура МК AVR (Лекция) | ЛЕКЦИИ ПО МК

В начало

 

ПЛАН ЛЕКЦИИ

1. Режимы пониженного энергопотребления

2. Система команд AVR

3. Архитектура микроконтроллеров AVR

 

1. Режимы пониженного энергопотребления

AVR  микроконтроллеры могут быть переведены программным путем в один из шести режимов пониженного энергопотребления.

  • Режим холостого хода (IDLE), в котором прекращает работу только процессор и фиксируется содержимое памяти данных, а внутренний генератор синхросигналов, таймеры, система прерываний и WATCHDOG-таймер продолжают функционировать.
  • Режим микропотребления (Power Down), в котором сохраняется содержимое регистрового файла, но останавливается внутренний генератор синхросигналов. Выход из Power Down возможен либо по общему сбросу микроконтроллера, либо по сигналу (уровень) от внешнего источника прерывания. При включенном WATCHDOG-таймере ток потребления в этом режиме составляет около 60…80 мкА, а при выключенном — менее 1 мкА для всех типов AVR. Вышеприведенные значения справедливы для величины питающего напряжения 5 В.
  • Режим сохранения энергии (Power Save), который реализован только у тех AVR, которые имеют в своем составе систему реального времени. В основном, режим Power Save идентичен Power Down, но здесь допускается независимая работа дополнительного таймера/счетчика RTC. Выход из режима Power Save возможен по прерыванию, вызванному или переполнением таймера/счетчика RTC, или срабатыванием блока сравнения этого счетчика. Ток потребления в этом режиме составляет 6…10 мкА при напряжении питания 5 В на частоте 32,768 кГц.
  • Режим подавления шума при работе аналого-цифрового преобразователя (ADC Noise Reduction). Как уже отмечалось, в этом режиме останавливается процессорное ядро, но разрешена работа АЦП, двухпроводного интерфейса I2C и сторожевого таймера.
  • Основной режим ожидания (Standby). Идентичен режиму Power Down, но здесь работа тактового генератора не прекращается. Это гарантирует быстрый выход микроконтроллера из режима ожидания всего за 6 тактов генератора.
  • Дополнительный режим ожидания (Extended Standby). Идентичен режиму Power Save, но здесь работа тактового генератора тоже не прекращается. Это гарантирует быстрый выход микроконтроллера из режима ожидания всего за 6 тактов генератора.

Микроконтроллеры 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

Ассемблер – переводчик удобных для восприятия человеком команд в машинные коды для мк. Инструкция «загрузить в регистр с именем r16 константу 0x12» на ассемблере выглядит так: ldi

Ассемблер – переводчик удобных для восприятия человеком команд в машинные коды для — страница №1/1


Ликбез по микроконтроллерам AVR. Часть 2. Автор статьи picmaniac.
Эти статьи я буду писать специально для тех, кто только начинает освоение микроконтроллеров и намеревается поработать с микроконтроллерами AVR. Сам я раньше программировал только PIC. Сейчас взялся за освоение AVR. Шаги, пройденные мной на пути освоения микроконтроллерной премудрости, я и намерен оформить в виде цикла статей. Речь в статье будет именно о 8-битных AVR, все упоминания о других микроконтроллерах будут отмечены особо.
Теперь предстоит разобраться – как именно программист может выполнять операции с регистрами 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. Максимальный размер программы для конкретного МК как раз и ограничивается объёмом его памяти программ.


Рис.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


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 части:

  1. Для перевода удобных человеку команд в машинные коды предназначен ассемблер.

  2. Набор возможных действий программиста с микроконтроллером определяется системой команд этого МК.

  3. Каждая команда имеет определённую мнемонику для лучшего запоминания.

  4. При написании программы на ассемблере применяются метки, комментарии и директивы.

  5. Максимальный размер программы ограничивается объёмом памяти программ выбранного МК.

  6. Порядок выполнения находящихся в памяти программ команд соответствует последовательности значений программного счётчика PC.

  7. Скорость работы МК определяется тактовой частотой. 1 такт работы AVR выполняется за 1 период тактовой частоты.

  8. Тактирование AVR возможно от внутреннего RC-генератора, подключение внешних деталей при этом не требуется.

  9. Выполнение программы после включения питания или сброса МК начинается с адреса вектора сброса (для AVR обычно $0000).

  10. Сброс может быть вызван падением напряжения питания ниже определенной величины, воздействием на вывод _RESET, а также некоторыми другими причинами.

  11. Программа, как правило, начинается с команды безусловного перехода к инициализационной части.

  12. Инициализация предназначена для программной настройки МК после сброса под нужды программиста.

umotnas.ru

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *