|
私達の身近にある家電機器の多くにコンピュータが内蔵されており、このようなコンピュータは「マイコン」と呼ばれます。マイクロコンピュータあるいはマイクロコントローラの略です。マイコンにはメーカーが開発したプログラムが書き込まれています。リモコンの中に入っているマイコンには、押したボタンに応じて決まったパターンで赤外線をオンオフするプログラムが書き込まれています。このプログラムはマイコン製造時に書き込まれ、後から書き換えることはできませんが、フラッシュメモリ(電気的に書き換え可能なプログラムメモリ)を搭載したマイコンを使えば、私達もさまざまな機器を作ることができます。
マイコンを使った機器を設計・製作するには、電子回路、論理回路、コンピュータの仕組み、プログラミングなど広範囲の知識が要求されます。これは大変なことではありますが、一方ではこれらのことを学ぶ格好の教材であるということを意味しています。
このテキストでは、AVRマイコン(ATtiny2313)を使った実習を行いながら、主にコンピュータの内部動作について学習します。理解を深めていただきたい箇所は2章です。ATtiny2313のマニュアル(英文)[1](1~14ページ, 211~214ページ, および必要に応じて他のページ)を読みながら学習を進めます。
3章~8章はプログラミングを扱っています。プログラミングについて本格的に学ぶというよりも、原始的な命令を組み合わせて、どのようにして複雑な処理が実現されるのかを見ていきます。
☆ さまざまな「ものづくり」に興味のある方は、9章や付録5や「ブレッドボードを使ったArduino流ものづくり」[2]を参考にしてください。
☆ ブレッドボードを初めて使う方は、「ブレッドボードの使い方」[3]をご覧ください。
☆ AVRライターはHIDaspx(ヒダピオ)を使います。「AVRライター」[4]をご覧ください。
☆ ここでは複雑な電子回路は登場していませんが、電気回路の最低限の知識は前提としています。必要な場合はこちらの資料[5]を参考にしてください。
☆ このテキストは実習時に適宜解説や補足を行うことを前提にしています。自学自習には適していないかもしれません。
ファイル名 | 内容 |
---|---|
prog.asm | ソースプログラム(サンプル) |
tn2313def.inc | アセンブラ用マイコン定義ファイル |
avra.bat | アセンブルバッチコマンド |
avrw.bat | ライタバッチコマンド(ヒダピオ用) |
toolsフォルダ |
AVRアセンブラ avrasm32.exe[1] ライタソフト HIDspx.exe [4] |
(1) avra.bat のショートカットを作成し、名前を「アセンブル」にします。
(2) avrw.bat のショートカットを作成し、名前を「書き込み」にします。
ここでは下図左のブレッドボードを使います。ボードの内部は下図右のように接続されています。
(外観) | (内部の接続) |
---|---|
電子工作の第一歩として、下図左の回路図を組み立ててみましょう。実際の配線は下図右のようになります。これを実体配線図と言います。なお、結線はジャンパーワイヤを使って行います。配線を行う時は、必ず電池のスイッチを切るようにします。
(回路図) | (実体配線図) |
---|---|
電池のスイッチを入れてLEDは点灯しましたか? 下の写真は実際の様子です。
ここで、ブレッドボード内部の接続を念頭に、下図のような電流の流れをイメージすることが重要です。LEDは電流が流れるから点灯するのです。
下図のように押しボタンスイッチを追加し、スイッチを押すとLEDが点灯するようにしてみましょう。スイッチを押すと電流が流れるわけですが、ここでも「電流が流れる経路」を(ブレッドボードの内部結線を含めて)イメージしてください。
(回路図) | (実体配線図) |
---|---|
次のステップへの準備として、AVRマイコン(ATtiny2313)を接続しましょう。電池のスイッチは切り、AVRマイコンの向きに気を付けて差し込んでください。
(回路図) | (実体配線図) |
---|---|
ここで、AVRマイコンにラベルを貼っておきます(テプラファイル)。
プログラムは回路と密接に関係しています。例えば、LEDをマイコンのどのピンに接続するかでプログラムは変わってきます。ここから先の実習を行うにあたり、以下の回路を組み立ててください。以降のプログラムはこの回路を前提としています。
(回路図) | (実体配線図) |
---|---|
(1)メモ帳などでソースプログラムを作成・編集します。ここでは prog.asm というファイル名とします。
(2)ソースプログラム( prog.asm )を「アセンブル」にドラッグ&ドロップすると、リストファイル( list.txt )と機械語ファイル( prog.hex)が作られます。
下図はリストファイル( list.txt )をメモ帳で開いた様子です。
リストファイルを見ることにより、ソースプログラムがどのような機械語プログラムに変換されるかがわかります。例えば、「LDI R16,0b00001000」は機械語「e008H」に変換されます。
コンピュータのプログラム領域(Program Flash)に書き込む機械語のみをファイルにしたものが機械語ファイル( prog.hex )で、下図はこれをメモ帳で開いた様子です。
機械語ファイルの書式は次のようになっています。
ここで用いるAVRマイコンは、ISP(In System Programming)可能なもので、マイコンを装置に組み込んだ状態で、マイコン内にプログラムを書き込みできます。
ブレッドボード上のAVRマイコンにライタを接続し、電池のスイッチを入れます。電源が入っていないと書き込みができないので注意してください。ソースプログラムから変換して作った機械語ファイル prog.hex を「書き込み」にドラッグ&ドロップします。書き込み終了と同時に、書き込んだプログラムがスタートします。
書き込まれたプログラムは 1 MHzの内蔵クロックで動作します。つまりほとんどの命令は1μsで実行されます。
ライタを切り離し、電源スイッチを入れたり切ったりして、確かに単体で動作していることを確認してください。
マイコンを埋めこんだ機器の開発プロセスは以下のようになります。
本テキストで使用するATtiny2313の内部構造を下図に示します。
(1) プログラムメモリ(Program Flash)は、1ワード16ビットで1024ワード($000~$3FF)の領域があります。
(2) データメモリ(SRAM)は、128バイトの領域($60~$DF)があります。
(3) 不揮発データメモリ(EEPROM)は、128バイトの領域($00~$7F)があります。
(4) 汎用レジスタ(Registers)は、8ビットのものが32個あります。
(5) プログラムカウンタ(Program Counter)には、次に実行すべき命令語のアドレス($000~$3FF)が入っています。
(6) 命令レジスタ(Instruction Register)には、プログラムメモリから、プログラムカウンタが指し示す番地の内容つまり次に実行すべき命令語が転送されます。その内容は命令デコーダ(Instruction Decoder)で解読され、実行されます。
(7) スタックポインタ(Stack Pointer)は、データメモリ(SRAM)の後ろから確保するスタック領域の先頭アドレスを指し示しています(~$DF)。スタックを利用するためには、あらかじめデータメモリ(SRAM)の最終番地$DFにセットしておく必要があります。サブルーチンコールを行った場合、戻り番地はスタック領域に保存されます。
(8) 算術論理演算ユニット(ALU)は、主にレジスタに格納された値相互の演算を行います。
(9) 状態レジスタ(Status Rester)は、ALUの演算結果が「0」ならZフラグ(Bit 1)が1になり、キャリーを生じたらCフラグ(Bit 0)が1になります。
(10) DDRBレジスタは、PB0~PB7の各端子を入力とするか出力とするかを指定するために用います。PB0~PB7に対応するDDRBレジスタのビットを、入力とする場合は「0」、出力とする場合は「1」にします。
(11) DDRDレジスタは、PD0~PD6の各端子を入力とするか出力とするかを指定するために用います。PD0~PD6に対応するDDRDレジスタのビットを、入力とする場合は「0」、出力とする場合は「1」にします。
(12) PORTBレジスタは、各ビットを「0」または「1」にすると、PB0~PB7のうち出力に指定されているビットにはその値を出力します。「0」が出力されるとそのピンは「低い電圧(0V)」、「1」が出力されると「高い電圧(+3V)」となります。
(13) PORTDレジスタは、各ビットを「0」または「1」にすると、PD0~PD6のうち出力に指定されているビットにはその値を出力します。
(14) Timer/Counter0は、8ビットカウンタで、クロック周波数(またはその1/8, 1/64, 1/256, 1/1024)をカウントします。
(15) Timer/Counter1は、16ビットカウンタで、クロック周波数(またはその1/8, 1/64, 1/256, 1/1024)をカウントします。
(16) 8MHzの内蔵オシレータを1/8の周波数にした1MHzのクロックで動作します。つまり、ほとんどの命令は1μsで実行されます。
次のソースプログラムからどのような機械語が作られ、その機械語プログラムがコンピュータ内部でどのように動作するかを詳しくみてみます。
prog.asm:
ソースプログラムの1行目はアセンブラに対する制御命令で、ここで用いているATtiny2313に関する情報が定義された「tn2313def.inc」というファイルを読み込みます。
2行目の「.cseg」はアセンブラに対する制御命令で、以下がプログラムコードであることを宣言しています。
3行目以降が機械語になるプログラム部分です。
このソースプログラムは、アセンブラソフト(ATMEL社のavrasm32.exe)を用いてアセンブルすると5ワード(5×16bit)の機械語プログラムとなります。プログラムのどの箇所がどのような機械語になるかは、以下のようにリストファイル(list.txt)で確認できます。
|
それでは、「書き込まれたプログラムから命令が一つずつ取り出されて実行される仕組み」を詳しく見てみましょう。動作の仕組みはどのコンピュータにも共通する基本的な事項ですから、丁寧に見てください。
■ 000000 e008 LDI R16,0b00001000
(1) 電源投入(またはリセット)により、Program Counterの内容が「0000000000」になります。Program Counterの内容はProgram Flashの実行すべきアドレスを表わしています。
(2) Program Counterの内容が指し示すProgram Flashのアドレスの内容「11100000 00001000」がInstruction Register(命令レジスタ)に取り込まれます。この動作はフェッチ(fetch)と呼ばれます(「取ってくる」という意味です)。
(3) Program Counterの内容に1が加えられます。
(4) Instruction Register(命令レジスタ)の内容がInstruction Decoder(命令デコーダ)によってデコードされます。デコードは具体的には、次のように行われます。
1110 | 0000 | 0000 | 1000 |
LDI命令 | 代入する値の上位4bit | R16~R31のうちのR16 | 代入する値の下位4bit |
「LDI」は「LoaD Immediate」の略というように、働きが連想できるような名前(符号)が付けられていて、ニーモニックコードと呼ばれます。また、「LDI(および対応する1110)」はOpCode(オペコード)、「R16,0b00001000」はOperand(オペランド)と呼ばれます。「R16(および対応する0000)」は第1オペランド、「0b00001000」は第2オペランドとなります。
(5) デコードの結果を受けて、その命令の実行に必要なタイミング信号が作られ、実行(execute)されます。この場合はレジスタR16の中が「00001000」となります。
以上の(2)~(5)は1クロックサイクルで処理されます。1クロックサイクルは1μsです。AVRマイコンの場合は、命令を実行(excecute)している間に次の命令をInstruction Register(命令レジスタ)にフェッチ(プリフェッチ)することにより高速化が図られています。
このように、コンピュータがプログラムを実行する仕組みは、
| → |
| → |
|
の繰り返しで、フェッチ実行サイクル(fetch-execute cycle)と呼ばれます
以下は、フェッチと実行(execute)が繰り返される様子を示したものです。
■ 000001 bb07 OUT DDRB,R16
(1) 上記(3)でProgram Counterの内容が「0000000001」となっているので、その値が指し示すProgram Flashのアドレスの内容「10111011 00000111」がInstruction Register(命令レジスタ)に取り込まれます。
(2) Program Counterの内容に1が加えられます。
(3) Instruction Register(命令レジスタ)の内容がInstruction Decoder(命令デコーダ)によってデコードされます。デコードは具体的には次のように行われます。
10111 | 01 | 10000 | 0111 |
OUT命令 | DDRBを表わすIOアドレス $17(010111)の上位2bit |
R0~R31のうちのR16 | DDRBを表わすIOアドレス $17(010111)の下位4bit |
「OUT」は「OUTput」を意味するニーモニックコードです。
(4) デコードの結果を受けて、その命令の実行に必要なタイミング信号が作られ、実行(execute)されます。この場合はDDRBレジスタの内容が「00001000」となります。
IOポートに直接値を出力する命令が無いために、このように初めの命令でレジスタに値をセットし、次の命令でレジスタの内容をIOポートに出力します。ここで行ったDDRBレジスタの設定は、「PB0~PB2, PB4~PB7を入力として使い、PB3を出力として使う」ということを指示するものです。
■ 000002 e008 LDI R16,0b00001000
(1) Program Counterの内容が「0000000010」となっているので、その値が指し示すProgram Flashのアドレスの内容「11100000 00001000」がInstruction Register(命令レジスタ)に取り込まれます。
(2) Program Counterの内容に1が加えられます。
(3) Instruction Register(命令レジスタ)の内容がInstruction Decoder(命令デコーダ)によってデコードされます。デコードは具体的には次のように行われます。
1110 | 0000 | 0000 | 1000 |
LDI命令 | 代入する値の上位4bit | R16~R31のうちのR16 | 代入する値の下位4bit |
(4) デコードの結果を受けて、その命令の実行に必要なタイミング信号が作られ、実行(execute)されます。この場合はレジスタR16の中が「00001000」となります。
■ 000003 bb08 OUT PORTB,R16
(1) Program Counterの内容が「0000000011」となっているので、その値が指し示すProgram Flashのアドレスの内容「10111011 00001000」がInstruction Register(命令レジスタ)に取り込まれます。
(2) Program Counterの内容に1が加えられます。
(3) Instruction Register(命令レジスタ)の内容がInstruction Decoder(命令デコーダ)によってデコードされます。デコードは具体的には次のように行われます。
10111 | 01 | 10000 | 1000 |
OUT命令 | PORTBを表わすIOアドレス $18(011000)の上位2bit |
R0~R31のうちのR16 | PORTBを表わすIOアドレス $18(011000)の下位4bit |
(4) デコードの結果を受けて、その命令の実行に必要なタイミング信号が作られ、実行(execute)されます。ここではPORTBのbit3(PB3ピン)が1(高い電圧)となり、外部回路のLEDが点灯します。PORTBはレジスタになっているので、一旦値をセットすると保持されます。
■ 000004 cffd RJMP LOOP
(1) Program Counterの内容が「0000000100」となっているので、その値が指し示すProgram Flashのアドレスの内容「11001111 11111101」がInstruction Register(命令レジスタ)に取り込まれます。
(2) Program Counterの内容に1が加えられます。
(3) Instruction Register(命令レジスタ)の内容がInstruction Decoder(命令デコーダ)によってデコードされます。デコードは具体的には次のように行われます。
1100 | 111111111101 |
RJMP命令 | ジャンプ先の相対アドレスで、2の補数表現の「-3」 |
「RJMP」は「Relative JuMP」を意味するニーモニックコードです。
(4) デコードの結果を受けて、その命令の実行に必要なタイミング信号が作られ、実行(execute)されます。通常であれば、この命令実行後のProgram Counterの内容が「0000000101」となるわけですが、相対的に「-3」された「0000000010」がProgram Counterの内容となります。
■ 以降「LDI R16,0b00001000」「OUT PORTB,R16」「RJMP LOOP」が繰り返されます
(練習) 以下のように、プログラム6行目の「LDI R16,0b00001000」を「LDI R16,0b00000000」に変更し、動作を確認しなさい。これは、PORTBのbit3を1から0に変更するということを意味しています。その結果、プログラムの一連の命令が実行されると、それまで点灯していたLEDが消灯します。
| → |
| → |
| → |
|
(練習) PB2に黄色のLEDと抵抗を追加接続し(回路図に追記)、
(1)2つのLEDを同時に点灯させるプログラム(prog1a.asm)
(2)一方だけ点灯させるプログラム(prog1b.asm)
を作り、動作を確かめなさい。
次のようにLEDを点灯させる正味のプログラムをサブルーチンとして分離し、これを例に、サブルーチンの呼び出しがどのように実行されるかを詳しくみてみます。
prog.asm:
|
このソースプログラムは、アセンブルすると9ワードの機械語プログラムとなります。プログラムのどの箇所がどのような機械語になるかは、以下のようにリストファイル(list.txt)で確認できます。
|
それでは、プログラムがどのように実行されるかを詳しく見てみましょう。
■ 000000 e008 LDI R16,0b00001000
■ 000001 bb07 OUT DDRB,R16
先頭の2つの命令は冒頭の例と同様ですので説明は省略します。
■ 000002 ed0f LDI R16,RAMEND
(1) Program Counterの内容が「0000000010」となっているので、その値が指し示すProgram Flashのアドレスの内容「1110110100001111」がInstruction Register(命令レジスタ)に取り込まれます。
(2) Program Counterの内容に1が加えられます。
(3) Instruction Register(命令レジスタ)の内容がInstruction Decoder(命令デコーダ)によってデコードされます。デコードは具体的には次のように行われます。
1110 | 1101 | 0000 | 1111 |
LDI命令 | 代入する値の上位4bit | R16~R31のうちのR16 | 代入する値の下位4bit |
(4) デコードの結果を受けて、R16に「11011111(0xDF)」が代入されます。この値はSRAMの最終番地を示しています。
■ 000003 bf0d OUT SPL,R16
(1) Program Counterの内容が「0000000011」となっているので、その値が指し示すProgram Flashのアドレスの内容「10111111 00001101」がInstruction Register(命令レジスタ)に取り込まれます。
(2) Program Counterの内容に1が加えられます。
(3) Instruction Register(命令レジスタ)の内容がInstruction Decoder(命令デコーダ)によってデコードされます。デコードは具体的には次のように行われます。
10111 | 11 | 10000 | 1101 |
OUT命令 | SPLを表わすIOアドレス $3D(111101)の上位2bit |
R0~R31のうちのR16 | SPLを表わすIOアドレス $3D(111101)の下位4bit |
(4) デコードの結果を受けて、SPLレジスタの内容が「11011111」となります。SPLレジスタ(スタックポインタ)の働きについては、次のRCALL命令の箇所で説明します。
■ 000004 d001 RCALL LEDON
(1) Program Counterの内容が「0000000100」となっているので、その値が指し示すProgram Flashのアドレスの内容「11010000 00000001」がInstruction Register(命令レジスタ)に取り込まれます。
(2) Program Counterの内容に1が加えられます。
(3) Instruction Register(命令レジスタ)の内容がInstruction Decoder(命令デコーダ)によってデコードされます。デコードは具体的には次のように行われます。
1101 | 0000 0000 0001 |
RCALL命令 | 呼び出すサブルーチンの相対アドレスで「1」 |
「RCALL」は「Relative CALL」を意味するニーモニックコードです。
(4) 通常であれば、この命令実行後のProgram Counterの内容が「0000000101」となるところですが、相対的に「+1」された「0000000110」がProgram Counterの内容となります。これだけだとRJMP命令と同じですが、RCALL命令の場合は、呼び出し先の末尾に記載されたRET命令で「呼び出した次の番地(この場合は0000000101番地)」に戻る点が異なります。この「戻り番地」はスタック(積み重ね)という方法を使ってSRAMに記憶されています。Stack Pointer(SPL)の値「11011111」が指し示すSRAMの番地に戻るべきProgram Counterの値をしまいます。Program Counterの値(10ビット)を保存するために、SRAMの2バイトを使います。そして、Stack Pointer(SPL)の値を「-2」します。もしもサブルーチンの中で別のサブルーチンを呼び出した場合は、その戻り番地は同じ方法でSRAM中に積み重ね(stack)られます。このような方法で、多重のサブルーチン呼び出し(サブルーチンの中で他のサブルーチンを呼び出すこと)が可能になっているのです。
■ 000006 e008 LDI R16,0b00001000
(1) Program Counterの内容が「0000000110」となっているので、その値が指し示すProgram Flashのアドレスの内容「11100000 00001000」がInstruction Register(命令レジスタ)に取り込まれます。
(2) Program Counterの内容に1が加えられます。
(3) Instruction Register(命令レジスタ)の内容がInstruction Decoder(命令デコーダ)によってデコードされます。デコードは具体的には次のように行われます。
1110 | 0000 | 0000 | 1000 |
LDI命令 | 代入する値の上位4bit | R16~R31のうちのR16 | 代入する値の下位4bit |
(4) デコードの結果を受けて、その命令の実行に必要なタイミング信号が作られ、実行(execute)されます。この場合はレジスタR16の中が「00001000」となります。
■ 000007 bb08 OUT PORTB,R16
(1) Program Counterの内容が「0000000111」となっているので、その値が指し示すProgram Flashのアドレスの内容「10111011 00001000」がInstruction Register(命令レジスタ)に取り込まれます。
(2) Program Counterの内容に1が加えられます。
(3) Instruction Register(命令レジスタ)の内容がInstruction Decoder(命令デコーダ)によってデコードされます。デコードは具体的には次のように行われます。
10111 | 01 | 10000 | 1000 |
OUT命令 | PORTBを表わすIOアドレス $18(011000)の上位2bit |
R0~R31のうちのR16 | PORTBを表わすIOアドレス $18(011000)の下位4bit |
(4) デコードの結果を受けて、PORTBのbit3(PB3ピン)が1(高い電圧)となり、外部回路のLEDが点灯します。
■ 000008 9508 RET
(1) Program Counterの内容が「0000001000」となっているので、その値が指し示すProgram Flashのアドレスの内容「10010101 00001000」がInstruction Register(命令レジスタ)に取り込まれます。
(2) Program Counterの内容に1が加えられます。
(3) Instruction Register(命令レジスタ)の内容がInstruction Decoder(命令デコーダ)によってデコードされます。デコードは具体的には次のように行われます。
1001010100001000 |
RET命令 |
「RET」は「RETurn」を意味するニーモニックコードです。
(4) デコードの結果を受けて、Stack Pointer(SPL)の値「11011101」に「+1」および「+2」した値が指し示すSRAMの番地の内容「0000000101」をProgram Counterにしまいます。つまり、次には5番地の命令が実行されることになります。そして、Stack Pointer(SPL)の値を「+2」します。
■ 000005 cffe RJMP LOOP
(1) Program Counterの内容が「0000000101」となっているので、その値が指し示すProgram Flashのアドレスの内容「11001111 11111110」がInstruction Register(命令レジスタ)に取り込まれます。
(2) Program Counterの内容に1が加えられます。
(3) Instruction Register(命令レジスタ)の内容がInstruction Decoder(命令デコーダ)によってデコードされます。デコードは具体的には次のように行われます。
1100 | 111111111110 |
RJMP命令 | ジャンプ先の相対アドレスで、2の補数表現の「-2」 |
(4) デコードの結果を受けて、通常であれば、この命令実行後のProgram Counterの内容が「0000000110」となるところ、相対的に「-2」された「0000000100」がProgram Counterの内容となります。
前章で、コンピュータが動作する仕組みが理解できたことと思います。3~8章では原始的な命令を組み合わせて条件分岐や繰り返しの処理がどのように行われるのか、そして、それがどのような応用に結びつくのかを、例題を通して理解してほしいと思います。
1章~2章のはじめで扱った回路は下図右のようになっていました。
また、プログラムprog.asmは下図左のようなものでした。
prog.asm:
|
マイコンの入出力ポートPB0~PB7の各ピン(ビット)は以下のようになっています。
bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 | 機能 | |
---|---|---|---|---|---|---|---|---|---|
PORTB | - | - | - | - | LED (1で点灯, 0で消灯) |
- | - | - | 入出力ピンに値を出力 |
DDRB | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 入出力の方向設定 |
PINB | - | - | - | - | - | - | - | - | 入出力ピンの値を入力 |
プログラムの初めでDDRBに0b00001000を出力しています。これは、PB3を出力、他を入力に設定することを指示しています。
それぞれの命令は1クロックサイクル(1μs)で実行されます。
「LOOP:」は、「LOOPの箇所に...」のようにアドレスを参照するための「ラベル」です。
次にPB3(PORTBのビット3)を1にしています。この命令が実行されると、PB3ピンが1(高い電圧)になります。
次に、「RJMP LOOP」でLOOPのところにジャンプしています。LDI命令とOUT命令は1クロックサイクル、RJMPは2クロックサイクルで実行されますから、計4クロックサイクル 4μs周期という超高速で、この部分が繰り返されることになります。
(参考) Arduinoのスケッチ
prog.ino:
|
(練習) LEDの接続をPB3からPD2に変更し、LEDを点灯・消灯させなさい。その際、上の説明を参考にして回路図、表、プログラムを自分で書いて実行してみなさい。
条件によってプログラムの流れを変える場合はどうするのかをみてみます。
スイッチと抵抗を追加し、下図右の回路にします。
button.asm:
|
マイコンの入出力ポートPORTBとPORTDの各ピン(ビット)は以下のようになっています。
bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 | 機能 | |
---|---|---|---|---|---|---|---|---|---|
PORTB | - | - | - | - | LED (1で点灯, 0で消灯) |
- | - | - | 入出力ピンに値を出力 |
DDRB | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 入出力の方向設定 |
PINB | - | - | - | - | - | - | - | - | 入出力ピンの値を入力 |
bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 | 機能 | |
PORTD | - | - | - | - | - | - | - | - | 入出力ピンに値を出力 |
DDRD | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 入出力の方向設定 |
PIND | - | - | - | - | - | - | - | スイッチ (オンで1, オフで0) |
入出力ピンの値を入力 |
スイッチが押されているとLEDが点灯、つまりスイッチ(PD0)が1ならばLEDを点灯、0ならば消灯するプログラムを考えてみましょう。
処理の流れをフローチャートで表すと下図、プログラムは上図左のようになります。
「BRNE(BRanch if Not Equal)」は、その前の演算結果が「0」でなければ(正確にはZフラグが1でなければ)指定箇所に分岐する命令です。「BRNE LEDON」を実行すると、その前の「ANDI R16,0b00000001」の結果が「0」であれば(つまりスイッチが押されていなければ)そのまま次の番地に進み、「0」でなければ(つまりスイッチが押されていれば)LEDONに分岐し、LEDを点灯してLOOPに戻ります。
(参考) Arduinoのスケッチ
Button.ino:
|
次に、下図右のようにスイッチを1個追加し、一方のスイッチ(SW1)を押すと点灯し、もう一方のスイッチ(SW2)を押すと消灯するようにしてみます。
button1.asm:
|
処理の流れをフローチャートで表すと下図、プログラムは上図左のようになります。
(参考) Arduinoのスケッチ
Button1.ino:
|
ここでは、サブルーチンを定義し、呼び出すプログラムを作ります。また、命令がクロック単位(ここでは1μs)で実行されることを利用して、タイマープログラムを作ります。
スイッチの回路をはずし、下図右の回路に戻してください。
blink.asm:
|
ここで、1秒の時間を費やすサブルーチン「T1S」が利用できるならば、上図左のようなプログラムでLEDを点滅させることができます。サブルーチンを利用する場合は、プログラムのはじめにSPL(スタックポインタ)の値をSRAMの末尾RAMENDにセットします。RAMENDはtn2313def.incの中で0xDF(0b11011111)に定義されています。
上のプログラムには「T1S(1秒待つサブルーチン)」がどこにもありませんので、このままではエラーになってしまいます。そこで、blink.asmに以下のプログラムを追加します。これにより次のサブルーチンが利用できるようになります。
blink.asm:
|
サブルーチンT100USの中で使われている命令について補足しておきます。
(1) 「DEC(DECrement)」は1減ずる命令で、「DEC R20」を実行すると「R20レジスタの内容が1減少」します。
(2) 「BRNE(BRanch if Not Equal)」は、その前の演算結果が「0」でなければ(正確にはZフラグが0であれば)指定箇所に分岐する命令です。「BRNE L100US」を実行すると、その前の「DEC R20」の結果が「0」であればそのまま次の命令(RET)が実行され、「0」でなければL100USに分岐します。
なお、コメント欄はクロックサイクルの計算値です。マイコンは1MHz(周期は1μs)のクロックで動作しており、それぞれ記載のクロックサイクルで命令が実行されます(ほとんどの命令は1クロックサイクル)。サブルーチン「T100US」は、次のようになります。
(1) このサブルーチンを呼び出す「RCALL」命令の実行に3クロックサイクル
(2) 「LDI」命令の実行に1クロックサイクル
(3) 「DEC」命令の実行が1、「BRNE(NotEqualの場合)」命令の実行が2、計3クロックサイクルで、これを30回繰り返すので、3*30クロックサイクル
(4) ただし、30回目は「DEC R20」の演算結果が「0」となり(Zフラグが1になる)、「BRNE」命令が1クロックサイクルで実行されるので(3)に対して「-1」
(5) 「RET」命令の実行が4クロックサイクル
(1)~(5)を合計すると97クロックサイクルとなります。これに1μsを掛けると97μs(概ね100μs)となります。このように、サブルーチンが呼び出されてから戻るまでにどれだけの時間がかかるかを正確に計算することができます。内蔵クロックが正確でないと、計算どおりの時間にならないわけですが、正確を期したい場合は内蔵クロックではなく水晶発振子を使います。また、次節のようにハードウェアタイマーを使うとクロックサイクルの計算をせずに済みます。
(参考) Arduinoのスケッチ
blink.ino:
|
LEDの点灯時間と消灯時間を変更してみます。
「RCALL T1S」の箇所(2箇所)を「RCALL T1S」→「RCALL T10mS」→「RCALL T1msS」と変えて、点滅の様子を確認してください。「RCALL T10mS」の時は10ms点灯、10ms消灯を繰り返し、50Hzの点滅になります。この状態ではほとんど点滅を感じません。人間の目が感じる「点滅のちらつき」はフリッカと呼ばれ、50~60Hzより高速になるとちらつきを感じなくなります。
通常の蛍光灯は100Hz(50Hzの2倍)で点滅しますが、蛍光管の状態によっては発光にムラが生じ50Hzで点滅し、その場合はちらつきが気になることがあります。インバーター式の蛍光灯はちらつきを抑えるために超高速(数10kHz)で点滅させています。また、ブラウン管テレビが毎秒30フレーム(実際にはインターレースで60Hzのフリッカ)、映画のフィルムが毎秒24フレーム(実際には同じフレームを2度映すことで48Hzのフリッカ)になっているのは、このような目の特性を考慮してのことです。
これまでのタイマープログラムは、プログラムの実行に所定の時間を費やすように調整したもので、「ソフトウェアタイマー」と呼ばれます。
AVRマイコンにはハードウェアタイマーが内蔵されており、以下はこれを利用したプログラムです。見かけの動作はまったく同じです。
プログラムの冒頭、TCCR0Bの設定で、TIMER0に入るクロックを1/64に遅くしています。TIMER0のクロックは周期64μsになり、156カウントすると約10msになります。L10MSの箇所のループでTCNT0の値が156になるのを待っています。
blink1.asm:
テレビなどのリモコンは、ボタンを押すと赤外線LEDが「決められたタイミングで点滅」するように作られています。どのように点滅しているかは、リモコン受光器に向けてリモコンのボタンを押し、受光器の信号をオシロスコープで観測するとわかります。
実際に観測してみると、各社テレビのリモコン信号は以下のようになっています。図の着色した部分が信号が「オン」の箇所で、「オン」とは「赤外線LEDを13μs点灯し、13μs消灯し、これを一定時間繰り返す」ことです。また、「オフ」とは「一定時間消灯する」ことです。これを参考に、赤外線LEDを点滅させるプログラムを作ればよいわけです。
電源 ON/OFF | 40 bf 12 ed |
チャンネルUP | 40 bf 1b e4 |
チャンネルDOWN | 40 bf 1f e0 |
ボリュームUP | 40 bf 1a e5 |
ボリュームDOWN | 40 bf 1e e1 |
電源 ON/OFF | 02 20 80 00 3d bd |
チャンネルUP | 02 20 80 00 34 b4 |
チャンネルDOWN | 02 20 80 00 35 b5 |
ボリュームUP | 02 20 80 00 20 a0 |
ボリュームDOWN | 02 20 80 00 21 a1 |
電源 ON/OFF | 8f 12 16 d1 |
チャンネルUP | 8f 12 11 a1 |
チャンネルDOWN | 8f 12 12 91 |
ボリュームUP | 8f 12 14 f1 |
ボリュームDOWN | 8f 12 15 e1 |
電源 ON/OFF | 95 0 |
チャンネルUP | 90 0 |
チャンネルDOWN | 91 0 |
ボリュームUP | 92 0 |
ボリュームDOWN | 93 0 |
リモコンの回路は、4.2の時の下図から、LEDを「赤外線LED」に交換し、直列抵抗の値を「100Ω」に交換します(1kΩのままでもテレビに近づければ動作します)。
ここでは東芝のテレビの「チャンネルUP」専用リモコンを作ってみます。赤外線LEDを以下のように光らせると、テレビのチャンネルがUPします。
(1) リモコン信号の始まりの目印として、9ms「オン」続く4.5ms「オフ」とします。(練習) 4の時と同様にスイッチを1個追加し、「チャンネルUP/DOWN」のリモコンを作りなさい。
(練習) 身近な機器のリモコン信号を、リモコン受光器とオシロスコープを用いて観測しなさい。また、そのリモコンを作ってみなさい。
ここではブザー音で電子オルゴールを作ってみましょう。仕組みに興味のある方は「電子オルゴールの仕組み」[6]、和音など音にこだわる方は「私だけの電子オルゴール」[7]を参考にしてください。
下図右のように、LEDと同じ箇所に圧電ブザー(Bzz)を接続してください。
tone.asm:
|
5.1の「1秒ごとにLEDを点滅させるプログラム」を実行してみてください。するとブザーからカチカチと音が聞こえます。このプログラムを「1秒ごと」ではなく、もっと高速に「1msごとに」点滅させてみましょう(上図左のプログラム)。この時の波形は下図のように500Hzの矩形波となります。
ブザーからは500Hzの「音」が聞こえます(500Hzといってもその倍音成分が含まれていますが)。
(参考) Arduinoのスケッチ
Tone.ino:
|
(練習) オシロスコープでトーン信号の波形を観測しなさい。また、WaveGeneなどの信号発生ソフトを用い、パソコンで500Hzの矩形波を出し、音を比べてみなさい。マイコンの内蔵クロックはそれほど正確ではありませんので多少の違いがあるかもしれません。
(練習) ラの音440Hz(A4)のトーンを出すサブルーチンを作り、動作を確かめなさい。また、WaveGeneで発生させた440Hzの矩形波の音や身近な楽器のラ(A4)の音と比べてみなさい。
440Hzのラの音(A4)に対し1オクターブ高いラ(A5)の音は880Hzで、その間は隣同士(半音)の周波数の比が一定(21/12)となるように音階が作られます。表計算ソフトを使い、440Hzを基点に各音の周波数F、周期T(ms)、T/2を計算すると次のようになります。
表中、「T/2/0.005」の箇所は、5μsのサブルーチンがあった時に、「0」または「1」の時間(T/2)はその何回分に相当するかを計算したものです。また、「200/T」の箇所は、200ms音を継続させるには何周期繰り返せばいいかを計算したものです。
次のプログラムは、上の計算結果に基づき、指定した音階で波形が200ms繰り返すようにTONEというサブルーチンを呼び出し、「ドレミファミレド」のメロディーにしています。
tone1.asm:
また、以下のプログラムは、「DO」「RE」「MI」...という命令をマクロ定義して、メロディーをプログラムしやすくしたものです。マクロ定義については「付録3」を参照してください。このような工夫をすると、長いメロディーもプログラミングが簡単になります。
tone2.asm:
(練習) tone2.asmの音階を拡張し、好きな曲の電子オルゴールを作りなさい。
これまでは、LEDが点灯するか消灯するかでした。しかし、点灯時の明るさを変化させたいこともあります。
圧電ブザーをはずし、再び下図右の回路に戻します。
fade.asm:
|
5.2ではLEDの点灯時間と消灯時間を変えてどのように見えるかを確認しました。ここでは「RCALL T1S」の箇所(2箇所)を「RCALL T1MS」にしてください(上図左)。
既に経験したように、点灯と消灯を高速に切り替えると、私達の目は点滅を認識できなくなります。代わりに、常時点灯している場合よりも少しだけ暗く感じます。「平均的に」半分の明るさになるからです。私達の目は、明るさが半分になっても、「ちょっと暗くなった」ぐらいにしか感じません。
tone.asm では、点灯時間が50%ですが、これを「デューティ比50%」といいます(下図左)。prog.asm (または prog.c )のように点灯しっぱなしの時は「デューティ比100%」といいます。
次に、1つ目の「RCALL T1MS」をそのままにして、2つ目を「RCALL T10MS」としてみてください。こうすることにより、1ms点灯、10ms消灯を繰り返すようになり、明るさはより暗く感じられます。この時の波形が上図中央です。
点灯時間と消灯時間が等しい時「デューティ比50%」、常時点灯の時「デューティ比100%」といいます。点灯時間1msで消灯時間9msなら「デューティ比10%」になります。この技術は「パルス幅変調(PWM)」と呼ばれ、モータのスピードコントロールや、デジタルオーディオアンプなどにも利用されています。
更に、1つ目の「RCALL T1MS」を「RCALL T100US」としてみてください。「デューティ比約1%」で明るさは更に暗くなります。
デューティ比を連続的に変化させれば下図のように任意の波形を表すことができ、モータのスピードコントロールや、デジタルオーディオアンプなどに利用されています。
なお、ここでは、「0」の時間も「1」の時間もプログラムで調整しましたが、マイコン内蔵のハードウェアタイマーにPWMの機能が備わっているので、これを使えば、高速で正確なPWM波形を得ることができます。
(参考) Arduinoのスケッチ
Fade.ino:
|
以下は、デューティ比を0%から100%まで連続的に変化させるプログラムです。暗い状態から次第に明るくなり、1秒後に最大の明るさになり、これを繰り返します。
fade1.asm:
(参考) Arduinoのスケッチ
Fade1.ino:
|
(練習) LEDが、だんだん明るくなり、だんだん暗くなるプログラムを作りなさい。
数字表示LED(7セグメントLED)に数字を表示してみます。以下の回路を組み立ててください。
count.asm:
|
PB0~PB6はLEDのa~gのセグメントに接続されていて、それぞれ1を出力した時に点灯する回路となっています。各数字を表示する際にPB0~PB6に何を出力するとよいか、そしてPORTBに出力すべき16進値を表にしたものです。
数字 | PB6(g) | PB5(f) | PB4(e) | PB3(d) | PB2(c) | PB1(b) | PB0(a) | HEX |
---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | $3F |
1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | $06 |
2 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | $5B |
3 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | $4F |
4 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | $66 |
5 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | $6D |
6 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | $7D |
7 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | $27 |
8 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | $7F |
9 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | $67 |
上図左のプログラムは、数字表示LEDに「5」を表示するものです。
(参考) Arduinoのスケッチ
Count.ino:
(練習) 表示する数字を他の数字に変更してみなさい。
1秒毎に表示される数字がカウントアップするようにしてみましょう。プログラムは以下のようになります。
countup.asm:
これまではプログラム中に表示データが埋めこまれていましたが、参照テーブルを用いることにより、データとプログラムを分離することができます。以下は、表を参照する方法に書き換えたものです。
countup.asm:
表の参照は「間接アドレッシング」により行っています。LOOPの冒頭で、Zレジスタに参照テーブルの先頭アドレスLUTを入れます。そして、LOOP1のループで、LPM命令でZ番地の内容をR0に取得してPORTBに出力し、Zに1を加えることを10回繰り返しています。
(参考) Arduinoのスケッチ
Countup.ino:
(練習) 数字表示がカウントダウンするプログラムcountdown.asmを作りなさい。
(練習) スイッチを押すとカウントダウンが始まり、カウントが「0」になるとアラーム(ブザー音)が鳴るようにしなさい。
(練習) 8.2のプログラム countup.asmを、1秒毎ではなく10ms毎に高速でカウントアップするようにし、スイッチが押されるとそこで停止するようにしなさい。
(練習) スイッチを押すと数字表示がカウントアップするプログラムを作りなさい。スイッチを押した時や放した時に接点がバタバタとオンオフしてしまう「チャタリング」という現象が生じ、誤動作が起きるかもしれませんが、このような問題もプログラムの工夫で回避することができます。今の段階では、このような問題があることを体験するだけで構いません。
(練習) 1秒毎に「H」「E」「L」「L」「O」を表示し、その後3秒間消え、再びこれを繰り返すプログラムを作りなさい。また、「E」「r」「r」「o」「r」の表示にしてみなさい。
これまでの例では、マイコンの出力ピンにLEDや圧電ブザーを接続しました。AVRマイコンは最大20mAの電流を流すことができるので、この範囲であれば直接接続できます。この章では、モータなど大きな電流が流れる素子を駆動する例を、今まで製作した中から紹介します。大きな電流が流れる回路を制御する場合は、「電流が流れる経路」を意識することが回路の誤動作を防ぐことにつながります。
さまざまな機器をオンオフ制御したい場合は、「リレー」が最も汎用性に富んでいます。ただし、ここでは交流100Vの機器ではなく、電池で動作する機器のみを考えます。おもちゃやラジオなど、制御対象の電圧や電流や極性などを気にせずに接続することができます。その際、「BDアダプタ」と組み合わせると、簡単に接続できるかもしれません(BDアダプタは製作も容易です)。
下図に2種類のリードリレーSS1A05(5V 10mA 接点容量0.5A), Y14H-1C-3DS(3V 50mA 接点容量1A)を使った回路例を示します。マイコンに逆電圧がかからないよう、リレーに並列にダイオードを入れます。
(練習) 上の回路を組み立て、電池で動くオモチャをつないで、blink.asmの動作を確かめなさい。また、4を参考に、スイッチを押すと動くようにしてみなさい。
DCモータ(小型の直流モータ)は大きな電流が流れるので、マイコンに直接接続することはできず、トランジスタやFETを使って駆動します。「ミニドラムマシン」では、バチを動かすのにDCモータを使い、2SC2120で駆動しました。赤外線リモコン自動車ではDCモータを2SK3142で駆動しました。
モータのオンオフだけでなく、正転逆転させたい場合は、Hブリッジ回路の専用IC(例えばTA7291P)を使います。IC内部での電圧降下には注意が必要です。
Midiコントローラ「ジングルベル」では、振動モータFM34F(100mA以下)を使い、「鈴」を鳴らました。2SC2120(最大800mA)や2SC735など、少し多目に電流を流せるトランジスタを使っています。
サーボモータは5Vで動作しますが、4.5Vでも支障なく動作します。
「ミニドラムマシン」では、ドラムの位置にバチを動かすためにPICO/STD/Fを使いました。サーボモータの制御端子はマイコンに直接接続できます。
それほど大電流ではなく、しかし駆動する素子がたくさんある場合は専用のドライバICを使うと便利です。「>交通安全教室用信号機」では、高輝度LED(12V 60mA)を駆動するために、専用のドライバIC TD62083(最大500mA)を使っています。
テレビのビデオ信号をプログラムで作り、画像を表示させます。内蔵クロックにはジッタがあるため、表示されるビデオ画像はゆらゆらと揺らぎます。また、規格外のビデオ信号なのでテレビによってはうまく表示できないかもしれません。でも、とにもかくにもテレビに画像が表示されるので、それだけで感激します。
video1line.asm:
以下は、「イ」の文字を表示させた例です。プログラム冒頭のvdataの箇所に以下のように8×8ビットのデータを置いています。
video8x8.asm:
このプログラムで作られるビデオ信号は以下のようなものです。ビデオのラインは0~255の256本で、0~2を垂直同期信号(VSYNC)にしています。video1line.asmでは、第64ラインだけに所定のドットパターン(プログラム中のdot patternの箇所)を表示させています。video8x8.asmでは、同じ表示を16ライン繰り返すことで、8ライン分を128ライン(第64~191ライン)に表示させています。
はじめに書いたようにここで作ったビデオ信号は規格外のものでテレビがうまく表示してくれる保証はありません。しかし、かつて広く利用されたVHSなどのビデオデッキは、テープ走行の揺らぎなどから、かなりヨレヨレのビデオ信号になっていて、それでもテレビは可能な限り表示してくれるように「寛容に」できています。上記のプログラム中のコメント欄には参考までクロック数を記載してあります。水平1ラインを63クロック(63μs)、1画面を63μs×256=16msにしています。
(練習) 画面に幾何学模様を表示させるプログラムを作りなさい。
(練習) videoline.asmに
[1] ATtiny2313 データシート, Microchip.
[2] ブレッドボードを使ったArduino流ものづくり, https://koyama.verse.jp/elecraft/avr/arduino4313.html
[3] ブレッドボードの使い方, https://koyama.verse.jp/elecraft/avr/bb.html
[4] AVRライター, https://koyama.verse.jp/elecraft/avr/avrwriter.html
[5] 私だけの電子オルゴール, https://koyama.verse.jp/elecraft/mymelo/
[6] ワンチップマイコンで作るビデオ信号ジェネレータ, https://koyama.verse.jp/elecraft/video/
名称 | 概観 | 備考 |
---|---|---|
ブレッドボード(EIC-301) | 秋月 150円 | |
ブレッドボード ジャンパーワイヤ | 秋月 10本 300円 | |
LED | 秋月 10個 100円 | |
赤外線LED | 秋月 10個 100円 | |
数字表示LED(C-551SR) | 秋月 50円 | |
圧電スピーカー | 秋月 2個 100円 | |
プッシュスイッチ(DS-660R-C) | 千石 84円 | |
抵抗 1/4W 1kΩ, 220Ω, 100Ω | 秋月 100本 100円 | |
AVRマイコン(ATtiny2313V) | 秋月 100円 | |
電池ケース(単3×2本 スイッチ付) | 秋月 60円 | |
ベニア板 | 4.5cm×15cm | ブレッドボード 電池ケース 圧電スピーカー を貼り付けます。 |
ここでは、マクロ定義について説明します。
「OUT」命令は値を直接出力することはできないため、
以下の例では、「.macro」~「.endm」の箇所で、新しい命令「OUTI」をマクロ定義しています。そして、「.cseg」以降のプログラムの中で、通常の命令と同じように「OUTI」という命令を使っています。
prog.asm:
ここで、「OUTI」命令の1つ目のパラメータ「DDRB」はマクロ定義の「@0」に、2つ目のパラメータ「0b00001000」はマクロ定義の「@1」の箇所に代入されます。
このソースプログラムは、アセンブルするとどのような機械語プログラムとなるのでしょうか。以下のようにリストファイル(list.txt)で確認することができます。
|
(ニーモニックと意味) | (例と説明) | |||
転送命令 | ||||
MOV | MOVe | MOV R16,R17 | R17の内容をR16に格納 | |
LDI | LoaD Immediate | LDI R16,100 | R16に100を格納 | |
LD | LoaD indirect | LD R16,X | Xレジスタで示される番地の内容をR16に格納 | |
LDS | Load Direct from SRAM | LDS R16,DATA | DATA番地の内容をR16に格納 | |
ST | STore indirect | ST X,R16 | R16の内容をXレジスタで示される番地に格納 | |
STS | STore direct to SRAM | STS DATA,R16 | R16の内容をDATA番地に格納 | |
LPM | Load Program Memory | LPM | Zレジスタで示される番地の内容をR0に格納 | |
入出力命令 | ||||
IN | INput | IN R16,PIND | PINDから入力しR16に格納 | |
OUT | OUTput | OUT PORTB,R16 | R16の内容をPORTBに出力 | |
算術演算命令 | ||||
ADD | ADD | ADD R16,R17 | R16にR17の内容を足す | |
SUB | SUB | SUB R16,R17 | R16からR17の内容を引く | |
SUBI | SUB Immediate | SUBI R16,100 | R16から100を引く | |
CLR | CLeaR | CLR R16 | R16を0にする | |
論理演算命令 | ||||
AND | AND | AND R16,R17 | R16をR17の内容とAND | |
ANDI | AND Immediate | ANDI R16,0b00000001 | R16を0b00000001とAND | |
OR | OR | OR R16,R17 | R16をR17の内容とOR | |
ORI | OR Immediate | ORI R16,0b00000010 | R16を0b00000010とOR | |
EOR | Excluseve OR | EOR R16,R17 | R16をR17の内容とExclusiveOR | |
COM | COMplement | COM R16 | R16のビットを反転(1の補数) | |
比較演算命令 | ||||
CP | ComPare | CP R16,R17 | R16をR17の内容と比較 | |
CPI | ComPare Immediate | CPI R16,100 | R16を100と比較 | |
シフト演算命令 | ||||
LSL | Logical Shift Left | LSL R16 | R16の内容を1ビット左にシフト | |
LSR | Logical Shift Right | LSR R16 | R16の内容を1ビット右にシフト | |
分岐命令 | ||||
RJMP | Relative JuMP | RJMP LOOP | LOOPにジャンプ | |
BREQ | BRanch if EQual | BREQ LOOP | Zフラグが1ならLOOPにジャンプ | |
BRNE | BRanch if Not Equal | BRNE LOOP | Zフラグが0ならLOOPにジャンプ | |
BRCS | BRanch if Carry Set | BRCS LOOP | Cフラグが1ならLOOPにジャンプ | |
BRCC | BRanch if Carry Cleared | BRCC LOOP | Cフラグが0ならLOOPにジャンプ | |
スタック操作命令 | ||||
PUSH | PUSH | PUSH R16 | R16の内容をスタックに待避 | |
POP | POP | POP R16 | R16にスタックから値を復旧 | |
サブルーチン命令 | ||||
RCALL | Relative CALL | RCALL SUB | サブルーチンSUBの呼び出し | |
RET | RETurn | RET | サブルーチンが呼び出されたアドレスに戻る | |
その他 | ||||
NOP | No OPeration | NOP | 何もしないで1クロック費やす |