こんばんは。KKTです。テスト期間中なので少し引き気味で記事を書いております。時が流れるのは速いものでもうこのテストが終われば今年度も終わりです。こんな感じで時が流れるのに無意味に反発して時間を浪費している(=記事を執筆している)感覚は、エスカレータに乗った時逆方向に歩いて静止する遊びの感覚に似ている気がします。エスカレータで思い出したのですが冬場防寒着とエスカレータの段の横の壁(段と一緒に動かない)を絶妙にこすり合わせることで連続的に近い形で静電気を発生させ刺激を得るという遊びを最近考案しました。
三連続ですがAVRの記事の続きです。前回のアセンブラの解説の続きを書きます。
5 LDI R16, 0x00
6 OUT PORTB, R16
6 OUT PORTB, R16
たぶんここからですね。5行目はもうお分かりの通り汎用レジスタR16に0x00を代入しています。6行目は何やらPORTBというI/Oレジスタに値を書き込んでいますがPORTBというのはチップのI/O端子のHIGH/LOWをコントロールするレジスタで0でLOW、1でHIGHを出力します。前回の下手くそなDDRBの図と同様下位ビットから順番にピンが割り当てられていてそれぞれのピンを制御できます。ここでは0x00を書き込んでいますので全ピンLOWになります。ちなみにDDRBレジスタに0を書き込んで入力にしたピンをPORTBで1を書き込むと内部プルアップが有効になります。
7 main:
プログラムはジャンプ系の命令が無ければ上から順番に実行されていくので次は7行目となります。main関数の始まりです。
8 RCALL delay
RCALLというのは「サブルーチン呼び出し」の命令です。サブルーチンというのは特定の処理を行うひとかたまりの命令郡のようなものです。呼び出して、その処理が終わると呼び出した箇所に戻ってきます。特定の処理をひとかたまりで呼び出せるようにしておくと後からその処理をもう一度行いたい時に簡単にできたり、プログラムが読みやすくなったりします。ここではdelayというラベル名のサブルーチンを呼び出しています。
15 delay:
ここからdelayラベルで呼び出される命令群が始まります。
16 LDI R16, 25
汎用レジスタR16に25を代入します。
17 dly2:
新たなラベルdly2を作りました。上から順番に事項されるのでこれが実行されます。
18 LDI R17,50
汎用レジスタR17に50を代入します。
19 dly1:
また新たなラベルdly2を作ります。
20 LDI R18,234
版用レジスタR18に234を代入します。
21 dly0:
ラベルを作りました。
22 NOP
NOPという命令は何もしないという命令です。ここでは待ち時間を作るために1クロックの間何もさせていません。
23 DEC R18
DECという命令はデクリメントでR18レジスタから1引きます。
24 BRNE dly0
BRNEは「不一致で分岐」です。直前の演算結果が0でなければ分岐します。分岐先は命令の直後に書いた「dly0」です。直前の演算結果というのはDEC R18ですね。つまりR18から順番に1引いていって0にならないうちは分岐して1引くを繰り返しますが0になると次の行に進みます。つまりdly0は234回繰り返されることになります。
25 DEC R17
同様R17から1引きます。
26 BRNE dly1
R17が0でないうちはdly1に分岐します。
27 DEC R16
R16から1引きます。
28 BRNE dly2
R16が0でないうちはdly2に分岐します。
29 RET
RETという命令は「サブルーチンから復帰」です。
復帰するので先ほどRCALLで呼び出した場所に戻ります。
15 delay:
ここからdelayラベルで呼び出される命令群が始まります。
16 LDI R16, 25
汎用レジスタR16に25を代入します。
17 dly2:
新たなラベルdly2を作りました。上から順番に事項されるのでこれが実行されます。
18 LDI R17,50
汎用レジスタR17に50を代入します。
19 dly1:
また新たなラベルdly2を作ります。
20 LDI R18,234
版用レジスタR18に234を代入します。
21 dly0:
ラベルを作りました。
22 NOP
NOPという命令は何もしないという命令です。ここでは待ち時間を作るために1クロックの間何もさせていません。
23 DEC R18
DECという命令はデクリメントでR18レジスタから1引きます。
24 BRNE dly0
BRNEは「不一致で分岐」です。直前の演算結果が0でなければ分岐します。分岐先は命令の直後に書いた「dly0」です。直前の演算結果というのはDEC R18ですね。つまりR18から順番に1引いていって0にならないうちは分岐して1引くを繰り返しますが0になると次の行に進みます。つまりdly0は234回繰り返されることになります。
25 DEC R17
同様R17から1引きます。
26 BRNE dly1
R17が0でないうちはdly1に分岐します。
27 DEC R16
R16から1引きます。
28 BRNE dly2
R16が0でないうちはdly2に分岐します。
29 RET
RETという命令は「サブルーチンから復帰」です。
復帰するので先ほどRCALLで呼び出した場所に戻ります。
9 LDI R16, 0xFF
10 OUT PORTB, R16
なので9行目に戻ります。汎用レジスタR16に0xFFを代入して、PORTBに書き込みます。この処理でマイコンチップの全出力ピンがHIGHになるのでLEDが点灯します。
11 RCALL delay
もう一度delayサブルーチンを呼び出します。
12 LDI R16,0x00
13 OUT PORTB,R16
全出力ピンをLOWにしてLEDを消灯させます。
14 RJMP main
RJMPは相対分岐といって指定した箇所に無条件でジャンプします。mainというラベルにジャンプしているのでまた7行目に戻って上記の動作を延々と繰り返します。
以上で大体の流れは理解できた方もいると思います。Delayの部分では三重ループになっており最初R18の値を引いていき0になったらR17から1を引いてからR18を234にセットしなおしてまた234回ループ。さらにそれを繰り返してR17も0になるとようやくR16から1を引いて……R16が0になるまで繰り返します。全ループ回数は292500回で、デフォルトのクロックが1.2MHzで周期833nsです。ループ内でNOPとDECでそれぞれ1クロック、条件分岐で1クロック使うので単純計算で730msです。これにさらに途中のdly1とかの分もあるので実際はもう少し長くなります。
10 OUT PORTB, R16
なので9行目に戻ります。汎用レジスタR16に0xFFを代入して、PORTBに書き込みます。この処理でマイコンチップの全出力ピンがHIGHになるのでLEDが点灯します。
11 RCALL delay
もう一度delayサブルーチンを呼び出します。
12 LDI R16,0x00
13 OUT PORTB,R16
全出力ピンをLOWにしてLEDを消灯させます。
14 RJMP main
RJMPは相対分岐といって指定した箇所に無条件でジャンプします。mainというラベルにジャンプしているのでまた7行目に戻って上記の動作を延々と繰り返します。
以上で大体の流れは理解できた方もいると思います。Delayの部分では三重ループになっており最初R18の値を引いていき0になったらR17から1を引いてからR18を234にセットしなおしてまた234回ループ。さらにそれを繰り返してR17も0になるとようやくR16から1を引いて……R16が0になるまで繰り返します。全ループ回数は292500回で、デフォルトのクロックが1.2MHzで周期833nsです。ループ内でNOPとDECでそれぞれ1クロック、条件分岐で1クロック使うので単純計算で730msです。これにさらに途中のdly1とかの分もあるので実際はもう少し長くなります。
ほとんど僕のメモ的なモノなのでわかりにくくても僕が分かれば大丈夫なんですね。
それでは、今日はこの辺で。
0 件のコメント:
コメントを投稿
何かあればお気軽にどうぞ。