このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。
特定のLEDをONにしなさい。
I/Oポートなどで一つのビットだけを On にするにはいくつかの方法が あります。
例えば、 PORTB の 1番ポートだけを On にするには、AVR なら
sbi portb,1
とすれば On になります。
レジスタの値に従って出力を変化させる場合、単純にレジスタの値を出 力してしまうと、既に書き込んである値を消してしまったりする場合が ありますので、手順を追う必要があります。 これは一命令ではないので、後で学ぶ割り込みの影響を受けないように するため、割り込みを適宜禁止する必要があります。
PORTBの1番ポートをOnにするために、 r17レジスタに 1<<1 =0b00000010 が入っている時、次のようにすると On になります。
cli
in r16,portb
or r16,r17
out portb,r16
sei
I/Oポートの下位4bitだけ r17 の下位4ビットの値にしたい。 但し、I/Oポート、r17とも上位4bitの値は任意とし、値を変えてはいけ ない。
この場合、上位4bit と下位4bit の扱いが異なるので、それを取り扱う ための情報が必要となります。 そこで、 mask = 0b11110000 となる値を使用します。 r16 に I/O ポートの値を読み込んだ跡、 mask を使用して r16 の下位4bit を 0 にします。 一方、 r17 の値に mask の反転パターンを使用して r18 に r17 の上 位 4bit を 0 にしたものを入れます。 そして、 r16 に r16とr18のorを取ったものを入れます。 最後に r16 を出力します。
.equ mask = 0x11110000
cli
in r16,portb
andi r16,mask
ldi r18,~mask
and r18,r17
or r16,r18
out portb,r16
sei
すべてのLED が一個ずつ順番に1秒ずつ点灯するプログラムを書きなさ い。
最初、LEDが一つだけ点灯し、その後、一秒ごとに一つずつ光るLED増え ていき、全点灯したら、最初に戻るプログラムを書きなさい。
すべてのLEDが一個ずつ順番に1秒ずつ点灯するが、ボタンを押している 間は消灯するプログラムを書きなさい
最初、LEDが一つだけ点灯し、その後、押ボタンを押すたびに一つずつ光るLED増え ていき、全点灯したら、最初に戻るプログラムを書きなさい。
特定のパターン 0b00000001, 0b00000011, 0b00000111, 0b00001111 を順にレジスタr16に入れて rcall sub をしなさい
ldi r16,0b00000001
rcall sub
ldi r16,0b00000011
rcall sub
ldi r16,0b00000111
rcall sub
ldi r16,0b00001111
rcall sub
定形のコマンドを繰り返し使用する時、マクロが便利です。
.macro mac
ldi r16,@0
rcall sub
.endmacro
mac 0b00000001
mac 0b00000011
mac 0b00000111
mac 0b00001111
プログラミングのコツとして、入出力部分を一箇所にまとめて、最小限にする ことが考えられます。 ループ変数を用意して、出力パターンを計算して出力することを考えます。 C 言語で書くと次のようになります。
for(;;){
pattern=0;
for(counter=1; counter<256; counter*=2){
pattern = counter | i;
pattern の出力;
}
}
counter *= 2 を実現するには rotateあるいは shift
と呼ばれる命令を使います。
これはレジスタの内容をビット毎に右又は左に移動する命令です。
単なる移動は shift で、溢れた bit を反対側の bit に回すのが rotate です。
avr では STATUS レジスタの C を含めた rotate 命令のみがあります。
bit 毎に移動するということは、二進数で桁をずらすことになるので、 2 倍、
あるいは 1/2 にするということになります。
つまり、値を 2 倍するには左に rotate すれば良いことになります。
また、rotate した値が 256 を越えると STATUS レジスタの C が 1 になりま
す。
なお、C 言語にも shift 演算 << と >> があります。
例えば、 count*=2
(2倍する) は
count<<=1
(左に 1 bit シフトする) とも書けます。
clr r16
sec
rol r16
rcall sub
sec
rol r16
rcall sub
sec
rol r16
rcall sub
sec
rol r16
rcall sub
またはマクロを使って
.macro next
sec
rol r16
rcall sub
.endmacro
clr r16
next
next
next
next
前の解法はエレガントですが、パターンを計算して出力する点で応用が効きま せん。 そこで、パターンはデータとして列挙し、パラメータで呼び出すことにします。 そして、出力はやはり一箇所で行うようにします。
データを列挙する場所はプログラム領域になります。 データを列挙するディレクティヴは .db や .dw になります。 .db は 8bit ずつ、 .dw は 16bit ずつデータを列挙できます。 一方、プログラム領域の値をレジスタに読み込む命令は lpm です。 これは定数の値のほか、インデックスレジスタ Z だけは間接読み出し(レジス タの指す番地の値を読む)が可能です。 特に、アドレッシングとして事前に Z を一つ減算したり、読み出した後で一 つ加算することもできます。
但し、 AVR はハーバードアーキテクチャなので、ここで単純に話は終わりま せん。 AVR ではレジスタ、データメモリは 8bit ですが、プログラムは 16bit です。 そのため、次のような注意点があります。
このよう自明でないな事情があることから、プログラム中で Z レジスタの値 と実アドレスを関連付ける(代入、比較など)をする場合に表現が複雑になりま す。 これを避けるために、あらかじめデータの入っている領域のそばでアドレス計 算をしておくようにします。 パターンの最大数はパターンの終了アドレスからパターンの開始アドレスを引 いて求めると、あとでパターンの変更をしてもパターンを出力するプログラム を変更する必要が無くなります。
インデックスレジスタは16bitなので、16bit の処理ができる命令がいろいろ あると便利ですが、AVR にはあまりありません。 つぎのようなものだけです
したがって、比較をする際も 8bit ごとに分ける必要があります。 大小比較するしようとするとかなり複雑です。 但し、等しいかどうかを判定するのは cp 命令二つでできます。
また、 16bit のデータを分けて処理する場合、読み込みは下位から、書き込 みは上位から行うようにします。
main:
ldi ZH,high(startdataadr)
ldi ZL,low(startdataadr)
loop:
lpm r16,Z+
rcall sub
lpm r16,Z+
rcall sub
lpm r16,Z+
rcall sub
lpm r16,Z+
rcall sub
startdata:
.db 0b00000001, 0b00000011
.db 0b00000111, 0b00001111
enddata:
.equ startdataadr = startdata<<1
.equ enddataadr = enddata<<1
7SEG LED に 0,1,2,3,4,5,6,7,8,9,A,b,C,d,E,F が一秒ごとに光り、 また最初に戻るプログラムを書きなさい
7SEG LED に 0 を表示し、右のボタンを押したら数が 1,2,3,4,5,6,7,8,9,A,b,C,d,E,F,0,1 ... と一つずつ増え、左のボタンを押した ら F,E,d,C,b,A,9,8,7,6,5,4,3,2,1,0,F,E ... と減るようなプログラムを書きなさい
すべてのLED が一個ずつ順番に1秒ずつ点灯するプログラムを書きなさ い。
; 演習7-1.asm
;
; Created: 2018/10/25 15:00:00
; Author : sakamoto
;
.cseg
.org 0x0000
rjmp reset
.org INT_VECTORS_SIZE
reset:
ldi r16,high(RAMEND)
out SPH,r16
ldi r16,low(RAMEND)
out SPL,r16
.equ onb = 0b00111111
.equ ond = 0b11000000
.equ pullup = 0b00110000
.equ onc = 0b00001111
ldi r16,onb
out ddrb, r16
ldi r16,ond
out ddrd, r16
ldi r16,pullup
out portd, r16
ldi r16,onc
out ddrc, r16
.macro clrport
in r16,@0
andi r16,~@1
out @0,r16
.endmacro
.macro setport
in r16,@0
andi r16,~@1
ori r16,@2
out @0,r16
.endmacro
main:
setport portb,onb,0b00000001
rcall wait
setport portb,onb,0b00000010
rcall wait
setport portb,onb,0b00000100
rcall wait
setport portb,onb,0b00001000
rcall wait
setport portb,onb,0b00010000
rcall wait
setport portb,onb,0b00100000
rcall wait
clrport portb,onb
setport portd,ond,0b01000000
rcall wait
setport portd,ond,0b10000000
rcall wait
clrport portd,ond
setport portc,onc,0b00000001
rcall wait
setport portc,onc,0b00000010
rcall wait
setport portc,onc,0b00000100
rcall wait
setport portc,onc,0b00001000
rcall wait
clrport portc,onc
rjmp main
.equ time = 10
.def wreg0 = r20
.def wreg1 = r21
.def wreg2 = r22
wait:
ldi wreg0,time
wait0:
ldi wreg1,0
wait1:
ldi wreg2,0
wait2:
nop
dec wreg2
brne wait2
dec wreg1
brne wait1
dec wreg0
brne wait0
ret
.exit
最初、LEDが一つだけ点灯し、その後、一秒ごとに一つずつ光るLED増え ていき、全点灯したら、最初に戻るプログラムを書きなさい。
;
; ex72.asm
;
; Created: 2018/10/18 17:34:30
; Author : sakamoto
;
;
.cseg
.org 0x0000
rjmp reset
.org INT_VECTORS_SIZE
reset:
ldi r16,high(RAMEND)
out SPH,r16
ldi r16,low(RAMEND)
out SPL,r16
.equ onb = 0b00111111
.equ ond = 0b11000000
.equ pullup = 0b00110000
.equ onc = 0b00001111
ldi r16,onb
out ddrb, r16
ldi r16,ond
out ddrd, r16
ldi r16,pullup
out portd, r16
ldi r16,onc
out ddrc, r16
.macro clrport
in r16,@0
andi r16,~@1
out @0,r16
.endmacro
.macro setport
in r16,@0
andi r16,~@1
ori r16,@2
out @0,r16
.endmacro
.equ time = 10
main:
setport portb,onb,0b00000001
rcall wait
setport portb,onb,0b00000011
rcall wait
setport portb,onb,0b00000111
rcall wait
setport portb,onb,0b00001111
rcall wait
setport portb,onb,0b00011111
rcall wait
setport portb,onb,0b00111111
rcall wait
setport portd,ond,0b01000000
rcall wait
setport portd,ond,0b11000000
rcall wait
setport portc,onc,0b00000001
rcall wait
setport portc,onc,0b00000011
rcall wait
setport portc,onc,0b00000111
rcall wait
setport portc,onc,0b00001111
rcall wait
clrport portb,onb
clrport portd,ond
clrport portc,onc
rjmp main
.def wreg0 = r20
.def wreg1 = r21
.def wreg2 = r22
wait:
ldi wreg0,time
wait0:
ldi wreg1,0
wait1:
ldi wreg2,0
wait2:
nop
dec wreg2
brne wait2
dec wreg1
brne wait1
dec wreg0
brne wait0
ret
.exit
すべてのLEDが一個ずつ順番に1秒ずつ点灯するが、ボタンを押している 間は消灯するプログラムを書きなさい
; 演習7-3.asm
;
; Created: 2018/10/25 15:00:00
; Author : sakamoto
;
.cseg
.org 0x0000
rjmp reset
.org INT_VECTORS_SIZE
reset:
ldi r16,high(RAMEND)
out SPH,r16
ldi r16,low(RAMEND)
out SPL,r16
.equ onb = 0b00111111
.equ ond = 0b11000000
.equ pullup = 0b00110000
.equ onc = 0b00001111
ldi r16,onb
out ddrb, r16
ldi r16,ond
out ddrd, r16
ldi r16,pullup
out portd, r16
ldi r16,onc
out ddrc, r16
.def prtb = r17
.def prtd = r18
.def prtc = r19
in prtb,portb
in prtd,portd
in prtc,portc
.macro clrport
in r16,@0
andi r16,~@1
out @0,r16
.endmacro
.macro clrreg
andi @0,~@1
.endmacro
.macro setport
andi @0,~@1
ori @0,@2
.endmacro
.macro setreg
andi @0,~@1
ori @0,@2
.endmacro
main:
setreg prtb,onb,0b00000001
rcall wait
setreg prtb,onb,0b00000010
rcall wait
setreg prtb,onb,0b00000100
rcall wait
setreg prtb,onb,0b00001000
rcall wait
setreg prtb,onb,0b00010000
rcall wait
setreg prtb,onb,0b00100000
rcall wait
clrreg prtb,onb
andi prtb,~onb
setreg prtd,ond,0b01000000
rcall wait
setreg prtd,ond,0b10000000
rcall wait
clrreg prtd,ond
andi prtd,~ond
setreg prtc,onc,0b00000001
rcall wait
setreg prtc,onc,0b00000010
rcall wait
setreg prtc,onc,0b00000100
rcall wait
setreg prtc,onc,0b00001000
rcall wait
clrreg prtc,onc
andi prtc,~onc
rjmp main
.equ time = 5
.def wreg0 = r20
.def wreg1 = r21
.def wreg2 = r22
wait:
ldi wreg0,time
wait0:
ldi wreg1,0
wait1:
ldi wreg2,0
wait2:
sbis pind,pind5
rjmp swoff
sbis pind,pind4
rjmp swoff
swon:
out portb,prtb
out portd,prtd
out portc,prtc
rjmp sw
swoff:
clrport portb,onb
clrport portd,ond
clrport portc,onc
sw:
dec wreg2
brne wait2
dec wreg1
brne wait1
dec wreg0
brne wait0
ret
.exit
最初、LEDが一つだけ点灯し、その後、押ボタンを押すたびに一つずつ光るLED増え ていき、全点灯したら、最初に戻るプログラムを書きなさい。
;
; Created: 2018/10/25 15:00:00
; Author : sakamoto
;
.cseg
.org 0x0000
rjmp reset
.org INT_VECTORS_SIZE
reset:
ldi r16,high(RAMEND)
out SPH,r16
ldi r16,low(RAMEND)
out SPL,r16
.equ onb = 0b00111111
.equ ond = 0b11000000
.equ pullup = 0b00110000
.equ onc = 0b00001111
ldi r16,onb
out ddrb, r16
ldi r16,ond
out ddrd, r16
ldi r16,pullup
out portd, r16
ldi r16,onc
out ddrc, r16
.macro clrport
in r16,@0
andi r16,~@1
out @0,r16
.endmacro
.macro setport
in r16,@0
andi r16,~@1
ori r16,@2
out @0,r16
.endmacro
.macro outport
ldi r20,@0
and r20,r16
in r21,@1
andi r21,~@0
or r21,r20
out @1,r21
.endmacro
.def counter=r17
.def last = r18
.def current = r19
main:
rcall dispcount
in current,PIND
sbrc last,PIND4
rjmp pressed
sbrs current,PIND4
rjmp notpressed
pressednow:
rcall inccounter
pressed:
notpressed:
mov last,current
rjmp main
dispcount: ;counter の値を表示
ldi ZH,high(startdata<<1)
ldi ZL,low(startdata<<1)
add ZL,counter
brcc dispcount1
inc ZH
dispcount1:
lpm r16,Z
outport onb,portb
outport ond,portd
ret
inccounter:
inc counter
cpi counter,countmax
breq inccounter1
ret
inccounter1:
clr counter
ret
.equ padding = 0
startdata:
.db 0b00000000, 0b00000001
.db 0b00000011, 0b00000111
.db 0b00000111, 0b00001111
.db 0b00011111, 0b00111111
.db 0b01111111, 0b11111111
enddata:
.equ num_start_adr = startdata <<1
.equ num_end_adr = enddata << 1
.equ countmax = low(num_end_adr - num_start_adr)
.exit
7SEG LED に 0,1,2,3,4,5,6,7,8,9,A,b,C,d,E,F が一秒ごとに光り、 また最初に戻るプログラムを書きなさい
;
; ex75.asm
;
; Created: 2018/11/29 16:56:24
; Author : sakamoto
;
;
; Created: 2018/10/25 15:00:00
; Author : sakamoto
;
.cseg
.org 0x0000
rjmp reset
.org INT_VECTORS_SIZE
reset:
ldi r16,high(RAMEND)
out SPH,r16
ldi r16,low(RAMEND)
out SPL,r16
.equ onb = 0b00111111
.equ ond = 0b11000000
.equ pullup = 0b00110000
.equ onc = 0b00001111
ldi r16,onb
out ddrb, r16
ldi r16,ond
out ddrd, r16
ldi r16,pullup
out portd, r16
ldi r16,onc
out ddrc, r16
.def counter=r17
ldi r17,0
.macro clrport
in r16,@0
andi r16,~@1
out @0,r16
.endmacro
.macro setport
in r16,@0
andi r16,~@1
ori r16,@2
out @0,r16
.endmacro
.macro outport
ldi r20,@0
and r20,r16
in r21,@1
andi r21,~@0
or r21,r20
out @1,r21
.endmacro
main:
rcall dispcount
rcall wait
rcall inccounter
rjmp main
dispcount: ;counter の値を表示
ldi ZH,high(startdata<<1)
ldi ZL,low(startdata<<1)
add ZL,counter
brcc dispcount1
inc ZH
dispcount1:
lpm r16,Z
outport onb,portb
outport ond,portd
ret
inccounter:
inc counter
cpi counter,countmax
breq inccounter1
ret
inccounter1:
clr counter
ret
.equ padding = 0
startdata:
.db 0b10110111,0b00100100
.db 0b01110011,0b01110110
.db 0b11100100,0b11010110
.db 0b11010111,0b00110100
.db 0b11110111,0b11110110
.db 0b11110101,0b11000111
.db 0b10010011,0b01100111
.db 0b11010011,0b11010001
enddata:
.equ num_start_adr = startdata <<1
.equ num_end_adr = enddata << 1
.equ countmax = low(num_end_adr - num_start_adr)
.equ time = 20
.def wreg0 = r20
.def wreg1 = r21
.def wreg2 = r22
wait:
ldi wreg0,time
wait0:
ldi wreg1,0
wait1:
ldi wreg2,0
wait2:
nop
dec wreg2
brne wait2
dec wreg1
brne wait1
dec wreg0
brne wait0
ret
.exit
7SEG LED に 0 を表示し、右のボタンを押したら数が 1,2,3,4,5,6,7,8,9,A,b,C,d,E,F,0,1 ... と一つずつ増え、左のボタンを押した ら F,E,d,C,b,A,9,8,7,6,5,4,3,2,1,0,F,E ... と減るようなプログラムを書きなさい
;
; ex76.asm
;
; Created: 2018/11/29 18:43:55
; Author : sakamoto
;
.cseg
.org 0x0000
rjmp reset
.org INT_VECTORS_SIZE
reset:
ldi r16,high(RAMEND)
out SPH,r16
ldi r16,low(RAMEND)
out SPL,r16
.equ onb = 0b00111111
.equ ond = 0b11000000
.equ pullup = 0b00110000
ldi r16,onb
out ddrb, r16
ldi r16,ond
out ddrd, r16
ldi r16,pullup
out portd, r16
.def counter=r17
clr counter
.def last = r18
.def current = r19
main:
rcall dispcount
in current,PIND
sbrc last,PIND4
rjmp lpressed
sbrs current,PIND4
rjmp lnotpressed
lpressednow:
rcall inccounter
lpressed:
lnotpressed:
sbrc last,PIND5
rjmp rpressed
sbrs current,PIND5
rjmp rnotpressed
rpressednow:
rcall deccounter
rpressed:
rnotpressed:
mov last,current
rjmp main
.macro outport
ldi r20,@0
and r20,r16
in r21,@1
andi r21,~@0
or r21,r20
out @1,r21
.endmacro
dispcount: ;counterの値を表示
ldi ZH,high(startdata<<1)
ldi ZL,low(startdata<<1)
add ZL,counter
brcc dispcount1
inc ZH
dispcount1:
lpm r16,Z
outport onb,portb
outport ond,portd
ret
inccounter:
inc counter
cpi counter,countmax
breq inccounter1
ret
inccounter1:
clr counter
ret
deccounter:
cpi counter,0
breq deccounter1
dec counter
ret
deccounter1:
ldi counter,countmax-1
ret
startdata:
.db 0b10110111,0b00100100
.db 0b01110011,0b01110110
.db 0b11100100,0b11010110
.db 0b11010111,0b00110100
.db 0b11110111,0b11110110
.db 0b11110101,0b11000111
.db 0b10010011,0b01100111
.db 0b11010011,0b11010001
enddata:
.equ num_start_adr = startdata <<1
.equ num_end_adr = enddata << 1
.equ countmax = low(num_end_adr - num_start_adr)
.exit