В этой статье я хочу представить еще один вариант таблиц команд ассемблера для микроконтроллеров 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 | |
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 | 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 Post-Inc. | (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 |
spm | 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 |
Понравилась статья? Поделиться с друзьями:
Мнемоника | Описание | Операция | Флаги |
---|---|---|---|
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 | — |
Система команд микроконтроллеров 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 имеются следующие биты:
Уффф… Что-то длинная страничка получается. Наверное, остальные команды опишу в >>> следующем уроке >>>
|
Теперь предстоит разобраться – как именно программист может выполнять операции с регистрами 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
Так вот, представляем такую ситуацию. Васе говоришь — «Слушай, ну короче такое дело — я калькулятор дома забыл, раздели 56983 на 2 и скажи Стиву, чтобы он столько раз отжался на кулаках» и Вася на калькуляторе считает и говорит Стиву по-английски » Отожмись на кулаках 28491 раз» Это называется «ДИРЕКТИВА» — другими словами директива это задание для Васи, результат выполнения которой это действие Стива.
Есть другая ситуация — Вы говорите Васе «Скажи Стиву, чтобы он отжался 28491 раз» и Вася просто переводит Ваши слова на английский. Это называется ОПЕРАТОР
Всё просто — есть директива и есть оператор. Оператор — это Ваше прямое указание что делать Стиву — Вася тут только переводит Ваше требование на инглиш. А Директива — это задание для самого Васи — и Вася сначала делает то, что Вы ему сказали, а потом уже в зависимости от результата говорит Стиву что-либо.
Теперь мы будем мучать англичанина регулярно! Но предварительно нужно получше познакомиться с нашим переводчиком Васей. Нужно знать следующее — Вася всегда Вас слушается беспрекословно — что ему сказали, то он и делает. Васин калькулятор не имеет десятичных знаков — если вы глянете пример с отжиманиями то 56983 \ 2 = 28491.5 — но у Васи всё после запятой обрубается — и он видит только целое число — причём неважно там будет 28491.000001 или там будет 28491.9999999 — для Васи это один фиг будет 28491 в обоих случаях. Ничего не округляется. Ещё важная информация про Васю. Вася жесток — ему пофиг на то, что Стив затрахается отжиматься двадцать восемь тысяч раз. Ему сказали — Вася перевёл. Причём не только перевёл — но и заставил сделать то, что Вы попросили. Так что если Стив помрёт на двадцать три тысячи пятьсот тринадцатом отжимании — то это будет исключительно Ваша вина.
Собственно это пока что всё. В следующем посте будем копать глубже — пока же просто достаточно понять это. Просто представить эту ситуацию и понять что к чему, кто исполняет какую роль и чем директива отличается от оператора.
А дальше мы постараемся называть всё своими именами и примерно прикинуть как же ассемблер работает с микроконтроллером по взрослому.
Рано или поздно для любого «ардуиньщика» (при условии что он хочет выйти на более высокий профессиональный уровень) наступает момент когда в рамках платформы Arduino ему становится тесно и он начинает задумываться о том а что же в действительности происходит под капотом. И ответы на все его вопросы уже есть, например в виде замечательного курса «AVR. Учебный курс» от глубокоуважаемого DIHALT. Если вы пользователь OS Windows, то и недостатка в инструментах разработки у вас не будет, достаточно бесплатной Atmel Studio, закрывающей все вопросы разработки ПО для МК AVR.
Хоть я и тимлид в коллективе разработчиков ПО прикладного уровня, вопросы «железа» которое в нашей конторе делается на базе AVR с недавних пор стали интересовать меня очень остро. Возникло желание хорошо разобраться во всех аспектах разработки ПО для МК. И так как я являюсь убежденным приверженцем использования в разработке OS на базе ядра Linux, меня заинтересовал вопрос, а как там в линуксах: можно/нельзя ли писать и отлаживать ПО, зашивать его в кристалл с тем же (или примерно тем же) уровнем удобства, который нам доступен в Windows. Тех кого тоже интересует этот вопрос, и в особенности тех, кому тема поста кажется надуманной, приглашаю под кат.
Что касается разработки на C/C++, в среде линукс с этим особых проблем и нет, в виду общей ориентированности этой системы на использование данного языка и наличия достойного набора инструментария. Однако, тот же DIHALT, например, утверждает что программирование для МК неотделимо от знания ассемблера, в чем я с ним соглашусь, в виду логичности его тезисов, изложенных в «Учебном курсе» и собственного (пусть небольшого) опыта системной разработки под x86.
Руководствуясь тезисом, что от асма AVR нам никуда не уйти и сидим мы под линуксом, попробуем посмотреть на то, как можно писать и отлаживать программы. Я использую дистрибутив Arch Linux, поэтому в своем повествовании буду опираться на его экосистему.
$ yaourt -S gavrasm
$ yaourt -S avra
test.S
;---- Определяем целевое устройство
.device atmega16
;---- Сегмент данных
.dseg
;---- Сегмент кода
.cseg
.org 0x0000
ldi r16, 10
M1:
inc r16
rjmp M1
;---- Сегмент EEPROM
.eseg
$ gavrasm test.S
+------------------------------------------------------------+
| gavrasm gerd's AVR assembler Version 3.5 (C)2015 by DG4FAC |
+------------------------------------------------------------+
Compiling Source file: test.S
-------
Pass: 1
14 lines done.
Pass 1 ok.
-------
Pass: 2
14 lines done.
3 words code, 0 words constants, total=3 = 0.0%
No warnings!
Compilation completed, no errors. Bye, bye ...
$ ls -l
итого 12
-rw-rw----+ 1 maisvendoo users 52 июл 29 15:46 test.hex
-rw-rw----+ 1 maisvendoo users 741 июл 29 15:46 test.lst
-rw-rw----+ 1 maisvendoo users 92 июл 29 15:46 test.S
Содержимое hex-файла прошивки
:020000020000FC
:060000000AE00395FECFAB
:00000001FF
Содержимое файла листинга
gavrasm Gerd's AVR assembler version 3.5 (C)2015 by DG4FAC
----------------------------------------------------------
Source file: test.S
Hex file: test.hex
Eeprom file: test.eep
Compiled: 29.07.2017, 15:46:38
Pass: 2
1: .device atmega16
2:
3: .dseg
4:
5: .cseg
6: .org 0x0000
7:
8: 000000 E00A ldi r16, 10
9: M1:
10: 000001 9503 inc r16
11: 000002 CFFE rjmp M1
12:
13: .eseg
14:
Program : 3 words.
Constants : 0 words.
Total program memory: 3 words.
Eeprom space : 0 bytes.
Data segment : 0 bytes.
Compilation completed, no errors.
Compilation endet 29.07.2017, 15:46:38
$ avra -l test.lst test.S
AVRA: advanced AVR macro assembler Version 1.3.0 Build 1 (8 May 2010)
Copyright (C) 1998-2010. Check out README file for more info
AVRA is an open source assembler for Atmel AVR microcontroller family
It can be used as a replacement of 'AVRASM32.EXE' the original assembler
shipped with AVR Studio. We do not guarantee full compatibility for avra.
AVRA comes with NO WARRANTY, to the extent permitted by law.
You may redistribute copies of avra under the terms
of the GNU General Public License.
For more information about these matters, see the files named COPYING.
Pass 1...
Pass 2...
done
Used memory blocks:
Code : Start = 0x0000, End = 0x0002, Length = 0x0003
Assembly complete with no errors.
Segment usage:
Code : 3 words (6 bytes)
Data : 0 bytes
EEPROM : 0 bytes
$ ls -l
итого 16
-rw-rw----+ 1 maisvendoo users 92 июл 29 15:46 test.S
-rw-rw----+ 1 maisvendoo users 0 июл 29 15:55 test.S.cof
-rw-rw----+ 1 maisvendoo users 13 июл 29 15:55 test.S.eep.hex
-rw-rw----+ 1 maisvendoo users 55 июл 29 15:55 test.S.hex
-rw-rw----+ 1 maisvendoo users 61 июл 29 15:55 test.S.obj
:020000020000FC
:060000000AE00395FECFAB
:00000001FF
$ cat test.lstAVRA Ver. 1.3.0 test.S Sat Jul 29 16:02:05 2017
.device atmega16
.dseg
.cseg
.org 0x0000
C:000000 e00a ldi r16, 10
M1:
C:000001 9503 inc r16
C:000002 cffe rjmp M1
.eseg
Segment usage:
Code : 3 words (6 bytes)
Data : 0 bytes
EEPROM : 0 bytes
Assembly completed with no errors.
.device atmel16
Оба рассмотренных компилятора имеют существенный фатальный недостаток — они не генерируют отладочной информации. Хотя в документации по avra такая возможность заявлена
Debugging support AVRA creates a coff file everytime the assembly was sucessful. This file allows AVR Studio or any coff compatible debugger to simulate or emulate the program.
$ sudo pacman -S avr-gcc avr-libc avr-binutils
Применительно к AVR gas не отходит от нотации Atmel в части оформления команд — порядок операнд здесь привычный, например команда:
ldi r16, 10
test.S
#include "/usr/avr/include/avr/io.h"
/* Секция данных */
.data
/* Секция кода */
.section .text
.org 0x0000
/* Точка входа, обязательная при вызове avr-gcc вместо avr-as */
.global main
main:
ldi r16, 10
M1:
inc r16
rjmp M1
Мы же теперь соберем hex-файл, пригодный для прошивки МК. Команда:
$ avr-as -mmcu=atmega16 -o test.o test.S
$ avr-objcopy -O ihex test.o test.hex
test.hex
:060000000AE00395FECFAB
:00000001FF
Тем не менее, мы не ответили на главный вопрос и не обозначили преимущество gas по возможности организации пошаговой отладки. Для этого прежде всего
$ sudo pacman -S avr-gdb
Первое, что нужно для использования gdb — собрать код соответствующим образом, сгенерировав ELF-образ, содержащий отладочные символы
$ avr-as -mmcu=atmega16 -g --gstabs -o test.o test.S
$ avr-ld -m avr4 -o test.elf test.o
Полученный образ мы используем для отладки нашего теста. Из него же можно сгенерировать и hex-файл
$ avr-objcopy -j .text -j .data -O ihex test.elf test.hex
test.hex
:060000000AE00395FECFAB
:00000001FF
Осталось загрузить полученный код в эмулятор и проверить доступные возможности отладки.
$ sudo pacman -S simavr
$ debtap simulavr_0.1.2.2-7+b2_amd64.deb
Версия эта довольно древняя, а текущий релиз (судя по дате последнего коммита в репозитории) ушел довольно далеко.
Что же касается simavr, то он ставится без проблем, поддерживает большее число МК, но на попытки подключения к нему отладчиком выдает в консоль сообщения о крэше эмулятора и разобраться с этим мне пока не удалось. Тут я снова апеллирую к сведующему в вопросе читателю и рассчитываю на подсказку.
Пока же мы используем для отладки костыльно поставленный simulavr
$ simulavr -d atmega16 -c 8000000 -g -P simulavr-disp
наблюдаем в консоли запуска сообщение
Waiting on port 1212 for gdb client to connect...
$ avr-gdb -q -tui
Подлючаемся к эмулятору
(gdb) target remote:1212
(gdb) load test.elf
(gdb) file test.elf
Что же, мы видим наш исходник и отладчик, готовый к приему команд. Даем команду next
(gdb) n
можем посмотреть изменившееся состояние регистров контроллера как в консоли эмулятора
так и в окне отладчика:
(gdb) info registers
.
.
.
r16 0xa 10
.
.
.
SREG 0x0 0
SP 0x0 0x0 <main>
PC2 0x2 2
pc 0x2 0x2 <M1>
Тем не менее, с точки зрения решаемой нами задачи она вполне удовлетворяет её требованиям. Для работы с AVR в Eclipse потребуется установка следующих плагинов
и, дабы не перегружать статью я отсылаю читателя к поиску, в котором установка плагинов к Eclipse описывается очень подробно. Важным здесь представляется настройка проекта, о чем я расскажу подробно.
Eclipse по своей сути ориентирован на C/C++ разработку, поэтому для создания ассемблерного проекта воспользуемся генерацией проекта через имеющийся Makefile, который и напишем для нашего теста. Вот он
Makefile
# Декларируем необходимые переменные
DEVICE = atmega16
TARGET = test
OBJECTS = $(TARGET).o
ELF = $(TARGET).elf
HEX = $(TARGET).hex
# Задаем правила компиляции
COMPILE = avr-as -mmcu=$(DEVICE) -g --gstabs
# Главная цель - HEX-файл прошивки
all: hex
# Правило сборки объекрных молулей: беруться все исходники
# с расширением *.S и компилятся в объектные модули *.o
.S.o:
$(COMPILE) -c $< -o $@
# Правило очитски - удаляем все продукты сборки
clean:
rm -f $(HEX) $(ELF) $(OBJECTS)
# Компоновка всех объектных модулей в ELF
elf: $(OBJECTS)
avr-ld -m avr4 -o $(ELF) $(OBJECTS)
# Преобразование ELF в HEX
hex: elf
avr-objcopy -j .text -j .data -O ihex $(ELF) $(HEX)
жмем Next, в следующем окне выбирая расположение каталога с исходниками и Makefile
Шлепаем Finish и видим наш проект во всей красе
Идем в меню Project -> Build all и получаем все необходимые нам бинарники:
19:34:51 **** Build of configuration Default for project test ****
make all
avr-as -mmcu=atmega16 -g --gstabs -c test.S -o test.o
avr-ld -m avr4 -o test.elf test.o
avr-objcopy -j .text -j .data -O ihex test.elf test.hex
19:34:51 Build Finished (took 128ms)
Тут мы сообщаем среде, что хотим запускать эмулятор с нужными нам параметрами командной строки, поместив пункт запуска в меню Run → External Tools.
Применяем настройки, идем в меню Run → External Tools → atmega16 и наблюдаем запуск эмулятора:
Хорошо, теперь настроим конфигурацию отладки нашего проекта. Идем в меню Run → Debug Configuratuions и настраиваем аппаратную отладку через GDB.
не забывая указать тип соединения с сервером симуляции:
и указав, какие действия следует выполнить при запуске отладки:
Обязательно ставим галки на Load image и Load Symbols — это позволяет отладчику загрузить прошивку в эмулятор и прочесть отладочные символы. Ставим точку останова на метку M1.
Жмем кнопки Apply и Debug и… вуаля!
Отладчик послушно стал на указанной точке останова. Доступен просмотр дампа памяти и содержимого регистров. Жмем F5 (или F6) и трассируем наш код по шагам.
Теперь можно хоть до посинения трассировать код, смотреть значения регистров и переменных, править код, снова трассировать, в общем заниматься привычным процессом разработки.
Тут много о чем не сказано, например о прошивке через avrdude, который (sic!) есть кроссплатформенная утилита прошивки для семейства AVR. Если у читателей будет желание, а у меня возможность, мы рассмотрим и её, помигаем светодиодами, пошлем слово «жопа» «счастье» через USART и так далее. Тема неисчерпаема и достойна продолжения. Время покажет.
А пока, благодарю моего читателя за внимание и надеюсь на новую встречу!
 
 
 
Урок 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 View) откроет нам окно с данными наших портов ввода-вывода и прочей периферии
Нажмем в данном окне на строку PORTD и увидим в нижней половине окна, что весь наш регистр DDRD, отвечающий за направление отдельных ножек порта выставился весь в единички, то есть на выход
А дальше уже проблема. Чтобы нам посмотреть, как сработает следующая команда, которая включит нам нулевую ножку, отладчику необходимо остановиться на следующей строке кода, а у нас её нет, у нас только бесконечный цикл, который для отладчика – не строка. Поэтому мы пока остановим отладчик следующей кнопкой
И мы напишем какую-нибудь ещё строку, например, мы захотим, чтобы данный светодиод у нас погас, а зажегся следующий, который присоединен к следующей лапке, чтобы создать эффект бегущего огня
PORTD = 0b00000001;
PORTD = 0b00000010;
Конечно всё это на практике у нас не сработает, так как мы не успеем заметить свечение предыдущего светодиода. Чтобы задумка заработала практически, мы должны ещё с вами включить между данными командами задержку, а это тема уже других более поздних занятий. Но тем не менее мы данную команду включим, чтобы отладчику было где остановиться. Затем мы запустим заново отладку. Точка останова у нас также находится пока на той строке, на какой и была до этого. Запустим опять отладчик. Собирать проект перед отладкой необязательно, так как отладчик сам его пересоберет. Дожидаемся остановке отладчика на точке. В окошке с вводом-выводом опять нажмём на строке с нашим портом. Дальше начинаем шагать по программе. Для этого нажимаем следующую кнопку или функциональную клавишу F10, о чем нам подсказывает студия, как только мы подносим указатель мыши к данной кнопке
Теперь отладчик остановится на следующей строке
И теперь в окне ввода-вывода мы видим уже следующую картину
Мы видим, что самый левый бит, соответствующий нулевой ножке порта переключился в высокое логическое состояние, причём мы это видим не только в регистре PORTD, но и в регистре PIND, который отвечает за считывание состояния ножек порта D при его работе на ввод. Вот таким вот образом мы и отлаживаем наши программы.
Остановим наш отладчик, уберём все брекпоинты, а, самое главное, не забываем удалить ненужную команду, которая включает другую лапку.
После этого текст кода у нас должен будет остаться вот таким
Предыдущий урок Программирование МК AVR Следующий урок
Исходный код
Купить программатор можно здесь (продавец надёжный) USBASP USBISP 3.3
Смотреть ВИДЕОУРОК
Post Views: 28 580
Проект электронной машины для голосования (EVM) на базе микроконтроллераВсякий раз, когда мы идем голосовать на выборах, мы видим электронные машины для голосования. В этом проекте мы собираемся спроектировать и разработать простую машину для голосования с использованием микроконтроллера ATmega32A . Хотя мы можем использовать контроллер, чтобы получить машину для голосования более 32 человек, для простоты мы собираемся сделать систему голосования для четырех человек. У нас будет четыре кнопки для четырех человек, и при каждом нажатии кнопки будет Голосование идет за соответствующего человека, и количество голосов, поданных каждым человеком, отображается на ЖК-дисплее.
Оборудование:
АТМЕГА32
Блок питания (5в)
ПРОГРАММАТОР AVR-ISP
JHD_162ALCD (ЖК-дисплей 16×2)
Конденсатор 100 нФ (пять штук), конденсатор 100 мкФ (подключен к источнику питания)
кнопка (пять штук),
резистор 10КОм (пять штук).
Программное обеспечение:
Атмель студия 6.1
прогисп или флеш магия.
Как показано на приведенной выше схеме электронного устройства для голосования , PORTA микроконтроллера ATMEGA32 подключается к порту данных ЖК-дисплея 16×2. Здесь следует не забыть отключить связь JTAG в PORTC ATMEGA, изменив байты предохранителя, если кто-то хочет использовать PORTC в качестве обычного порта связи. В ЖК-дисплее 16×2 всего 16 контактов, если есть подсветка, если нет подсветки, будет 14 контактов.Можно включить или оставить контакты подсветки. Теперь в 14 контактах 8 контактов данных (7-14 или D0-D7), 2 контакта источника питания (1 и 2 или VSS & VDD или gnd & + 5v), 3 контакта rd для контроля контрастности (VEE-контролирует толщину символов. должен быть показан), 3 контакта управления (RS, RW и E).
В схеме вы можете заметить, что я взял только два управляющих контакта, поскольку это дает гибкость для лучшего понимания. Бит контраста и READ / WRITE используются нечасто, поэтому их можно замкнуть на массу.Это переводит ЖК-дисплей в режим максимальной контрастности и чтения. Нам просто нужно управлять контактами ENABLE и RS, чтобы отправлять символы и данные соответственно.
Подключения, которые выполняются для ЖК-дисплея, приведены ниже:
PIN1 или VSS — земля
PIN2 или VDD или VCC — питание +5 В
PIN3 или VEE — заземление (дает максимальный контраст лучше всего для новичков)
PIN4 или RS (выбор регистра) — PD6 uC
PIN5 или RW (чтение / запись) — земля (переводит ЖК-дисплей в режим чтения, что упрощает обмен данными для пользователя)
PIN6 или E (включить) — PD5 uC
PIN7 или D0 — PA0 из uC
PIN8 или D1 — PA1 из uC
PIN9 или D2 — PA2 из uC
PIN10 или D3 — PA3 из uC
PIN11 или D4 — PA4 из uC
PIN12 или D5 — PA5 из uC
PIN13 или D6 — PA6 из uC
PIN14 или D7 — PA7 из ОК
В схеме вы можете видеть, что мы использовали 8-битную связь (D0-D7), однако это не обязательно.Мы можем использовать 4-битную связь (D4-D7), но с 4-битной коммуникацией программа становится немного сложной, поэтому я просто выбрал 8-битную связь.
Итак, просто наблюдая за таблицей выше, мы подключаем 10 контактов ЖК-дисплея к контроллеру, в котором 8 контактов являются контактами данных и 2 контакта для управления. Здесь присутствуют пять кнопок, четыре для увеличения голосов кандидатов и пятая для обнуления голосов кандидата.
Конденсаторы, представленные здесь, предназначены для устранения дребезга кнопок.Если их удалить, контроллер может считать больше одного при каждом нажатии кнопки. Резисторы, подключенные к контактам, предназначены для ограничения тока, когда кнопка нажата, чтобы опустить контакт к земле.
Каждый раз, когда нажимается кнопка, соответствующий штифт контроллера опускается на землю, и, таким образом, контроллер распознает, что определенная кнопка нажата и соответствующее действие должно быть предпринято, это может быть увеличение количества голосов кандидатов или сброс голосов в зависимости от нажатой кнопки.
Когда кнопка, представляющая соответствующего человека, нажата, контроллер выбирает ее и увеличивает соответствующий номер человека в своей памяти, после того, как приращение показывает соответствующее количество людей на ЖК-дисплее 16×2.
Принцип работы устройства для электронного голосования на базе микроконтроллера объясняется шаг за шагом кода C ниже,
,
— — DOC — — — — — — — |
Нам нравится смотреть на хардкорные проекты электроники с мощным микроконтроллером и сотнями, если не тысячами, строк кода в его центре. Но всем нужно как-то туда попасть.
Эта серия руководств призвана помочь вам при программировании микроконтроллеров линейки Atmel AVR. Если вы никогда раньше не прикасались к микроконтроллеру или работали над десятками проектов Arduino, это поможет вам сразу перейти к аппаратному обеспечению и придаст вам уверенности в создании чего угодно.
План развития серии:
Необходимые знания
Хорошие новости: я установил довольно низкую планку. Вам необходимы базовые знания по установке программ на ваш компьютер и их использованию. Вы должны иметь некоторое представление о том, как работает беспаечная макетная плата, и желательно иметь мультиметр и знать, как с его помощью измерять напряжение. И не бойтесь использовать Google для исследования вопросов, на которые здесь нет прямого ответа.
Что на самом деле делает микроконтроллер?
Это загруженный вопрос.Для понимания я свожу это к самому простому объяснению:
Микроконтроллер делает то, что вы его запрограммировали. Делает это быстро и надежно.
Как это работает?
В этой серии руководств я буду обсуждать цифровую логику.То есть, все входные и выходные контакты будут оцениваться на основе нулевого напряжения или 5 В. Это дает наши цифровые единицы и нули, при этом 5 вольт за единицу и ноль вольт как ноль.
Итак, если вы хотите зажечь светодиод, просто подключите схему к контакту, сделайте этот контакт выходом и установите высокий логический уровень (5 вольт). Если вы хотите добавить кнопку, подключите ее к выводу, который установлен как вход, и запрограммируйте микросхему на измерение уровня напряжения этого вывода. Это действительно так просто, если вы научитесь писать правильные команды, чтобы чип понимал ваши пожелания.
Посмотрите на сам чип
Я решил использовать микроконтроллер ATmega168. Это мощный чип, но его не сложнее использовать, чем его младшие собратья. Это даст вам много возможностей для развития ваших проектов, оставаясь при этом доступным (менее 4,50 долларов США). Вот схема:
Это часто называют распиновкой, поскольку она показывает, что на самом деле делает каждый из 28 контактов на микросхеме. Все эти контакты выполняют несколько функций, поэтому рядом с каждой из них есть длинные строки текста, за исключением пяти, у которых есть только одно имя.Это контакты, связанные с напряжением и землей (VCC, GND, AVCC, AREF, AGND), важная проблема с микроконтроллерами.
Для интегральных схем требуется постоянный источник напряжения. Это означает, что в рамках нашего проекта нам потребуется построить регулятор напряжения. Это легко сделать на макетной плате, и вы сможете достать детали на месте. Также стоит отметить, что наверху чипа есть полукруглая ямочка. Это то, что вы найдете в пластиковом корпусе этих микросхем с двойным расположением линий, и оно используется, чтобы убедиться, что вы не вставляете его задом наперед.
Еще раз взгляните на распиновку и найдите контакты, имена которых начинаются с PD. Всего их должно быть восемь, с обозначениями от PD0 до PD 7. Это фантастический пример 8-битной природы этих микросхем. PD обозначает порт D, один из регистров ввода и вывода. Все в этих микросхемах ориентировано на 8 бит. Это последовательность из восьми единиц или нулей в различных комбинациях. Если вы хотите включить или выключить определенные функции, вы меняете один или несколько битов в 8-битном регистре.Каждый раз, когда вы хотите изменить один пин, вы должны адресовать все восемь в регистре. Мы узнаем об этом гораздо больше, но не раньше третьей части серии.
Программирование
ATmega168 — это программируемый микроконтроллер. Но еще лучше, его можно перепрограммировать. Фактически, когда вы работаете над проектом, вы, скорее всего, перепрограммируете его несколько раз в час.
Этот чип имеет ограничение на размер программного пространства в 16 килобайт. В наше время 64-гигабайтных плееров iPod 16 килобайт может показаться ничтожным.Но на самом деле это 16 килобайт машинного кода. Вы можете многое сделать с этим… поверьте мне.
Вам нужно какое-то оборудование, чтобы поместить код на эти микросхемы. Обычно это приходит в виде программиста AVR. Во второй части этого руководства мы рассмотрим несколько различных вариантов программирования, а затем построим и запрограммируем тестовую схему.
Чтобы подготовиться к оставшейся части этой серии руководств, мне нужно, чтобы вы собрали несколько инструментов. У вас должен быть компьютер какого-то типа, будь то Linux, Mac или ПК с Windows.Это запустит программное обеспечение, которое берет наш код, компилирует его во что-то, что может использовать микроконтроллер, а затем сообщает программисту, как записать его в наш чип.
Составитель
В конечном итоге мы собираемся написать наш собственный код для AVR, который использует архитектуру RISC. Но мы делаем это на компьютере с архитектурой x86. Инструмент, необходимый для этого, называется кросс-компилятором. Это, пожалуй, лучшая причина выбрать AVR для разработки, поскольку существует отличная цепочка инструментов, которую можно легко установить на нескольких платформах.
Это не единственный вариант. Многие пользователи Windows доверяют бесплатному программному обеспечению AVR Studio от Atmel. Это единственный раз, когда я буду ссылаться на него, поскольку у меня нет компьютера с Windows, и я никогда не пробовал этот пакет.
Программное обеспечение для программирования
Наше программное обеспечение для запуска аппаратного программатора называется AVRdude. Если вы установили одну из указанных выше цепочек инструментов, у вас уже должна быть эта программа. Перейдите в окно терминала или командную строку и введите следующее, чтобы убедиться:
avrdude -h
Появится экран справки. Если вы получили сообщение об ошибке, вам следует убедиться, что вы правильно установили набор инструментов на предыдущем шаге, или загрузите AVRdude самостоятельно.
На этом завершается вводная часть этой серии.
Часть 2: В следующей части этой серии мы рассмотрим несколько аппаратных средств, которые можно использовать для программирования микроконтроллера AVR. Я написал программу hello world и расскажу, как построить схему на макетной плате, подключить микросхему к программатору и использовать AVRdude для записи этой простой прошивки на устройство. Я не хочу вас слишком волновать, но это действительно связано с миганием светодиода.
Часть 3: Предварительно скомпилированный HEX-файл использовался для программирования микроконтроллера AVR во второй части данной серии статей. В этой части мы рассмотрим исходный код языка C, из которого состояла эта прошивка. Я также подробно расскажу о периферийных устройствах, имеющихся в чипе, и подробно расскажу, как их использовать. В завершение мы добавим функциональность к исходной программе, перекомпилируем ее и перепрограммируем чип с обновленной версией.
Часть 4: Теперь, когда вы приобрели навыки программирования AVR, я покажу вам, как начать создавать с их помощью классные вещи.
@szczys
,Переключить навигацию