2002.11.27-

AVRマイコンで学ぶ コンピュータの仕組み

小山智史(koyama88@cameo.plala.or.jp)

コンピュータの仕組み ATtiny2313版 - - - - -
Arduinoでものづくり ATtiny4313版 ATmega328P版 NANO版 - ESP8266版 XIAO版
tinyBasicでものづくり - - NANO版 ProMicro版 ESP8266版 XIAO版
Pythonでものづくり - - - - - XIAO版
Javascriptでものづくり - - - ProMicro版 - XIAO版

目次



0. 準備
1. プログラム開発の手順
2. コンピュータの動作
3. デジタル出力(1): 光るものを作る(LEDの点灯・消灯)
4. デジタル入力: スイッチ操作できるものを作る(LEDのオンオフ)
5. デジタル出力(2): 光るものを作る(LEDの点滅)
6. 音の出るものを作る
7. アナログ出力: LEDの明るさを変える
8. デジタル出力(3): 数字を表示する
9. 動くものを作る
10. テレビに表示する

(付録1) 使用する主な部品
(付録2) バッチプログラムの内容
(付録3) マクロ定義とマクロ展開
(付録4) ATtiny2313の主な命令

 私達の身近にある家電機器の多くにコンピュータが内蔵されており、このようなコンピュータは「マイコン」と呼ばれます。マイクロコンピュータあるいはマイクロコントローラの略です。マイコンにはメーカーが開発したプログラムが書き込まれています。リモコンの中に入っているマイコンには、押したボタンに応じて決まったパターンで赤外線をオンオフするプログラムが書き込まれています。このプログラムはマイコン製造時に書き込まれ、後から書き換えることはできませんが、フラッシュメモリ(電気的に書き換え可能なプログラムメモリ)を搭載したマイコンを使えば、私達もさまざまな機器を作ることができます。

 マイコンを使った機器を設計・製作するには、電子回路、論理回路、コンピュータの仕組み、プログラミングなど広範囲の知識が要求されます。これは大変なことではありますが、一方ではこれらのことを学ぶ格好の教材であるということを意味しています。

 このテキストでは、AVRマイコン(ATtiny2313)を使った実習を行いながら、主にコンピュータの内部動作について学習します。理解を深めていただきたい箇所は2章です。ATtiny2313のマニュアル(英文)[1](1~14ページ, 211~214ページ, および必要に応じて他のページ)を読みながら学習を進めます。

 3章~8章はプログラミングを扱っています。プログラミングについて本格的に学ぶというよりも、原始的な命令を組み合わせて、どのようにして複雑な処理が実現されるのかを見ていきます。

☆ さまざまな「ものづくり」に興味のある方は、9章や付録5や「ブレッドボードを使ったArduino流ものづくり」[2]を参考にしてください。
☆ ブレッドボードを初めて使う方は、「ブレッドボードの使い方」[3]をご覧ください。
☆ AVRライターはHIDaspx(ヒダピオ)を使います。「AVRライター」[4]をご覧ください。
☆ ここでは複雑な電子回路は登場していませんが、電気回路の最低限の知識は前提としています。必要な場合はこちらの資料[5]を参考にしてください。
☆ このテキストは実習時に適宜解説や補足を行うことを前提にしています。自学自習には適していないかもしれません。


0. 準備

0.1 プログラム開発環境の準備

  1. ここでは、HIDaspxというAVRライタを用います(ライタソフトはHIDspx)。他のライタを使用する場合は適宜読み替えてください。

  2. avrdevフォルダには以下のファイルが入っています。

    ファイル名内容
    prog.asmソースプログラム(サンプル)
    tn2313def.incアセンブラ用マイコン定義ファイル
    avra.batアセンブルバッチコマンド
    avrw.batライタバッチコマンド(ヒダピオ用)
    toolsフォルダ AVRアセンブラ avrasm32.exe[1]
    ライタソフト HIDspx.exe [4]
  3. ショートカットの作成

    (1) avra.bat のショートカットを作成し、名前を「アセンブル」にします。

    操作の説明図

    (2) avrw.bat のショートカットを作成し、名前を「書き込み」にします。

    操作の説明図

0.2 ブレッドボードの使い方

 ここでは下図左のブレッドボードを使います。ボードの内部は下図右のように接続されています。

(外観)(内部の接続)

 電子工作の第一歩として、下図左の回路図を組み立ててみましょう。実際の配線は下図右のようになります。これを実体配線図と言います。なお、結線はジャンパーワイヤを使って行います。配線を行う時は、必ず電池のスイッチを切るようにします。

(回路図)(実体配線図)

電池のスイッチを入れてLEDは点灯しましたか? 下の写真は実際の様子です。


(12cm×5cm 程の板を用意し、ブレッドボードと電池ケースを貼りつけています。)

 ここで、ブレッドボード内部の接続を念頭に、下図のような電流の流れをイメージすることが重要です。LEDは電流が流れるから点灯するのです。

 下図のように押しボタンスイッチを追加し、スイッチを押すとLEDが点灯するようにしてみましょう。スイッチを押すと電流が流れるわけですが、ここでも「電流が流れる経路」を(ブレッドボードの内部結線を含めて)イメージしてください。

(回路図)(実体配線図)

 次のステップへの準備として、AVRマイコン(ATtiny2313)を接続しましょう。電池のスイッチは切り、AVRマイコンの向きに気を付けて差し込んでください。

(回路図)(実体配線図)

 ここで、AVRマイコンにラベルを貼っておきます(テプラファイル)。

1. プログラム開発の手順

1.1 回路の組み立て

 プログラムは回路と密接に関係しています。例えば、LEDをマイコンのどのピンに接続するかでプログラムは変わってきます。ここから先の実習を行うにあたり、以下の回路を組み立ててください。以降のプログラムはこの回路を前提としています。

(回路図)(実体配線図)

1.2 アセンブラプログラムから機械語への変換(アセンブル)

(1)メモ帳などでソースプログラムを作成・編集します。ここでは prog.asm というファイル名とします。

prog.asmをメモ帳で編集

(2)ソースプログラム( prog.asm )を「アセンブル」にドラッグ&ドロップすると、リストファイル( list.txt )と機械語ファイル( prog.hex)が作られます。

アセンブル操作

 下図はリストファイル( list.txt )をメモ帳で開いた様子です。

list.txtをメモ帳で見る

リストファイルを見ることにより、ソースプログラムがどのような機械語プログラムに変換されるかがわかります。例えば、「LDI R16,0b00001000」は機械語「e008H」に変換されます。

 コンピュータのプログラム領域(Program Flash)に書き込む機械語のみをファイルにしたものが機械語ファイル( prog.hex )で、下図はこれをメモ帳で開いた様子です。

prog.hexをメモ帳で見る

 機械語ファイルの書式は次のようになっています。

1.3 AVRマイコンへの機械語プログラムの書き込み

 ここで用いるAVRマイコンは、ISP(In System Programming)可能なもので、マイコンを装置に組み込んだ状態で、マイコン内にプログラムを書き込みできます。

 ブレッドボード上のAVRマイコンにライタを接続し、電池のスイッチを入れます。電源が入っていないと書き込みができないので注意してください。ソースプログラムから変換して作った機械語ファイル prog.hex を「書き込み」にドラッグ&ドロップします。書き込み終了と同時に、書き込んだプログラムがスタートします。

 書き込まれたプログラムは 1 MHzの内蔵クロックで動作します。つまりほとんどの命令は1μsで実行されます。

 ライタを切り離し、電源スイッチを入れたり切ったりして、確かに単体で動作していることを確認してください。

AVRライタで書き込み中の写真
書き込みのための操作

1.4 機器の開発プロセス

 マイコンを埋めこんだ機器の開発プロセスは以下のようになります。


2. コンピュータの動作

2.1 AVRマイコンの内部構造

 本テキストで使用するATtiny2313の内部構造を下図に示します。

ATtiny2313の内部構造

2.2 プログラムの実行(フェッチ実行サイクル)

 次のソースプログラムからどのような機械語が作られ、その機械語プログラムがコンピュータ内部でどのように動作するかを詳しくみてみます。

prog.asm:

 ソースプログラムの1行目はアセンブラに対する制御命令で、ここで用いているATtiny2313に関する情報が定義された「tn2313def.inc」というファイルを読み込みます。

 2行目の「.cseg」はアセンブラに対する制御命令で、以下がプログラムコードであることを宣言しています。

 3行目以降が機械語になるプログラム部分です。

 このソースプログラムは、アセンブラソフト(ATMEL社のavrasm32.exe)を用いてアセンブルすると5ワード(5×16bit)の機械語プログラムとなります。プログラムのどの箇所がどのような機械語になるかは、以下のようにリストファイル(list.txt)で確認できます。

正味の機械語プログラムは で、これに若干の付加情報を付け加えたものが機械語ファイル(prog.hex)です。短いプログラムであれば、アセンブラソフトを用いずに、機械語の一覧表を見ながら人間が機械語プログラムを直接書くこともできます。機械語プログラムは、ライタとライタソフトを用いてコンピュータのプログラムメモリ(Program Flash) 1Kワード($000~$3FF番地)の先頭部分($000~$004番地)に書き込まれます(下図参照)。

 それでは、「書き込まれたプログラムから命令が一つずつ取り出されて実行される仕組み」を詳しく見てみましょう。動作の仕組みはどのコンピュータにも共通する基本的な事項ですから、丁寧に見てください。

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 R16R31のうちの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)
デコード(decode)
実行(execute)

の繰り返しで、フェッチ実行サイクル(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
R0R31のうちのR16 DDRBを表わすIOアドレス
$17(010111)の下位4bit

OUT」は「OUTput」を意味するニーモニックコードです。

(4) デコードの結果を受けて、その命令の実行に必要なタイミング信号が作られ、実行(execute)されます。この場合はDDRBレジスタの内容が「00001000」となります。

 IOポートに直接値を出力する命令が無いために、このように初めの命令でレジスタに値をセットし、次の命令でレジスタの内容をIOポートに出力します。ここで行ったDDRBレジスタの設定は、「PB0~PB2, PB4~PB7を入力として使い、PB3を出力として使う」ということを指示するものです。

OUT命令実行の説明図

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 R16R31のうちのR16 代入する値の下位4bit

(4) デコードの結果を受けて、その命令の実行に必要なタイミング信号が作られ、実行(execute)されます。この場合はレジスタR16の中が「00001000」となります。

LDI命令実行の説明図

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
R0R31のうちのR16 PORTBを表わすIOアドレス
$18(011000)の下位4bit

(4) デコードの結果を受けて、その命令の実行に必要なタイミング信号が作られ、実行(execute)されます。ここではPORTBのbit3(PB3ピン)が1(高い電圧)となり、外部回路のLEDが点灯します。PORTBはレジスタになっているので、一旦値をセットすると保持されます。

OUT命令実行の説明図

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の内容となります。

RJMP命令実行の説明図

■ 以降「LDI R16,0b00001000」「OUT PORTB,R16」「RJMP LOOP」が繰り返されます

(練習) 以下のように、プログラム6行目の「LDI R16,0b00001000」を「LDI R16,0b00000000」に変更し、動作を確認しなさい。これは、PORTBのbit3を1から0に変更するということを意味しています。その結果、プログラムの一連の命令が実行されると、それまで点灯していたLEDが消灯します。

メモ帳を使ってprog.asm中の
LDI R16,0b00001000」を
LDI R16,0b00000000」に変更
prog.asmのアセンブル
prog.hexの書き込み
動作の確認

(練習) PB2に黄色のLEDと抵抗を追加接続し(回路図に追記)、
(1)2つのLEDを同時に点灯させるプログラム(prog1a.asm)
(2)一方だけ点灯させるプログラム(prog1b.asm)
を作り、動作を確かめなさい。

2.3 プログラムの実行(サブルーチンの呼び出し)

 次のようにLEDを点灯させる正味のプログラムをサブルーチンとして分離し、これを例に、サブルーチンの呼び出しがどのように実行されるかを詳しくみてみます。

prog.asm:

 このソースプログラムは、アセンブルすると9ワードの機械語プログラムとなります。プログラムのどの箇所がどのような機械語になるかは、以下のようにリストファイル(list.txt)で確認できます。

正味の機械語プログラムは です。機械語プログラムは、ライタとライタソフトを用いてコンピュータのプログラムメモリ(Program Flash) 1Kワード($000~$3FF番地)の先頭部分($000~$008番地)に書き込まれます(下図参照)。

 それでは、プログラムがどのように実行されるかを詳しく見てみましょう。

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 R16R31のうちの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
R0R31のうちのR16 SPLを表わすIOアドレス
$3D(111101)の下位4bit

(4) デコードの結果を受けて、SPLレジスタの内容が「11011111」となります。SPLレジスタ(スタックポインタ)の働きについては、次のRCALL命令の箇所で説明します。

OUT命令実行の説明図

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)られます。このような方法で、多重のサブルーチン呼び出し(サブルーチンの中で他のサブルーチンを呼び出すこと)が可能になっているのです。

RCALL命令実行の説明図

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 R16R31のうちの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
R0R31のうちのR16 PORTBを表わすIOアドレス
$18(011000)の下位4bit

(4) デコードの結果を受けて、PORTBのbit3(PB3ピン)が1(高い電圧)となり、外部回路のLEDが点灯します。

CBI命令実行の説明図

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」します。

RET命令実行の説明図

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の内容となります。

RJMP命令実行の説明図

3. デジタル出力(1): 光るものを作る(LEDの点灯・消灯)

 前章で、コンピュータが動作する仕組みが理解できたことと思います。3~8章では原始的な命令を組み合わせて条件分岐や繰り返しの処理がどのように行われるのか、そして、それがどのような応用に結びつくのかを、例題を通して理解してほしいと思います。

 1章~2章のはじめで扱った回路は下図右のようになっていました。

 また、プログラムprog.asmは下図左のようなものでした。

prog.asm:

マイコンの入出力ポートPB0PB7の各ピン(ビット)は以下のようになっています。

  bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 機能
PORTB - - - - LED
(1で点灯,
0で消灯)
- - - 入出力ピンに値を出力
DDRB 0 0 0 0 1 0 0 0 入出力の方向設定
PINB - - - - - - - - 入出力ピンの値を入力

 プログラムの初めでDDRB0b00001000を出力しています。これは、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を点灯・消灯させなさい。その際、上の説明を参考にして回路図、表、プログラムを自分で書いて実行してみなさい。


4. デジタル入力: スイッチ操作できるものを作る(LEDのオンオフ)

 条件によってプログラムの流れを変える場合はどうするのかをみてみます。

 スイッチと抵抗を追加し、下図右の回路にします。

button.asm:

 マイコンの入出力ポートPORTBPORTDの各ピン(ビット)は以下のようになっています。

  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:


5. デジタル出力(2): 光るものを作る(LEDの点滅)

5.1 LEDの点滅(サブルーチン呼び出し)

 ここでは、サブルーチンを定義し、呼び出すプログラムを作ります。また、命令がクロック単位(ここでは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:

5.2 LEDを高速に点滅させる

 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のフリッカ)になっているのは、このような目の特性を考慮してのことです。

5.3 LEDの点滅(内蔵のハードウェアタイマーを使用)

 これまでのタイマープログラムは、プログラムの実行に所定の時間を費やすように調整したもので、「ソフトウェアタイマー」と呼ばれます。

 AVRマイコンにはハードウェアタイマーが内蔵されており、以下はこれを利用したプログラムです。見かけの動作はまったく同じです。

 プログラムの冒頭、TCCR0Bの設定で、TIMER0に入るクロックを1/64に遅くしています。TIMER0のクロックは周期64μsになり、156カウントすると約10msになります。L10MSの箇所のループでTCNT0の値が156になるのを待っています。

blink1.asm:

5.4 応用: テレビのリモコンを作る

 テレビなどのリモコンは、ボタンを押すと赤外線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
SONY製テレビのリモコン信号

 リモコンの回路は、4.2の時の下図から、LEDを「赤外線LED」に交換し、直列抵抗の値を「100Ω」に交換します(1kΩのままでもテレビに近づければ動作します)。

 ここでは東芝のテレビの「チャンネルUP」専用リモコンを作ってみます。赤外線LEDを以下のように光らせると、テレビのチャンネルがUPします。

(1) リモコン信号の始まりの目印として、9ms「オン」続く4.5ms「オフ」とします。
(2) 0x40のデータを下のビットから順に送信します。ビット0は「0」なので、560μs「オン」、565μs「オフ」とします。
(3) ビット1~5も「0」なので、560μs「オン」、565μs「オフ」を繰り返します。
(4) ビット6は「1」なので、560μs「オン」、1690μs「オフ」とします。
(5) ビット7は「0」なので、560μs「オン」、565μs「オフ」とします。
(6) (2)~(5)と同様の方法で0xbfを送信します。
(7) 同じように0x1bを送信します。
(8) 同じように0xe4を送信します。
(9) 最後に終わりの目印として、560μs「オン」、565μs「オフ」とします。

(練習) 4の時と同様にスイッチを1個追加し、「チャンネルUP/DOWN」のリモコンを作りなさい。

(練習) 身近な機器のリモコン信号を、リモコン受光器とオシロスコープを用いて観測しなさい。また、そのリモコンを作ってみなさい。


6. 音の出るものを作る

 ここではブザー音で電子オルゴールを作ってみましょう。仕組みに興味のある方は「電子オルゴールの仕組み」[6]、和音など音にこだわる方は「私だけの電子オルゴール」[7]を参考にしてください。

6.1 トーンを鳴らす

 下図右のように、LEDと同じ箇所に圧電ブザー(Bzz)を接続してください。

tone.asm:

5.1の「1秒ごとにLEDを点滅させるプログラム」を実行してみてください。するとブザーからカチカチと音が聞こえます。このプログラムを「1秒ごと」ではなく、もっと高速に「1msごとに」点滅させてみましょう(上図左のプログラム)。この時の波形は下図のように500Hzの矩形波となります。

トーン波形

 ブザーからは500Hzの「音」が聞こえます(500Hzといってもその倍音成分が含まれていますが)。

(参考) Arduinoのスケッチ

Tone.ino:

(練習) オシロスコープでトーン信号の波形を観測しなさい。また、WaveGeneなどの信号発生ソフトを用い、パソコンで500Hzの矩形波を出し、音を比べてみなさい。マイコンの内蔵クロックはそれほど正確ではありませんので多少の違いがあるかもしれません。

(練習) ラの音440Hz(A4)のトーンを出すサブルーチンを作り、動作を確かめなさい。また、WaveGeneで発生させた440Hzの矩形波の音や身近な楽器のラ(A4)の音と比べてみなさい。

6.2 音階を演奏する(楽譜を命令の並びで表現)

 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の音階を拡張し、好きな曲の電子オルゴールを作りなさい。


7. アナログ出力: LEDの明るさを変える

 これまでは、LEDが点灯するか消灯するかでした。しかし、点灯時の明るさを変化させたいこともあります。

7.1 LEDの明るさを変える

 圧電ブザーをはずし、再び下図右の回路に戻します。

fade.asm:

 5.2ではLEDの点灯時間と消灯時間を変えてどのように見えるかを確認しました。ここでは「RCALL T1S」の箇所(2箇所)を「RCALL T1MS」にしてください(上図左)。

 既に経験したように、点灯と消灯を高速に切り替えると、私達の目は点滅を認識できなくなります。代わりに、常時点灯している場合よりも少しだけ暗く感じます。「平均的に」半分の明るさになるからです。私達の目は、明るさが半分になっても、「ちょっと暗くなった」ぐらいにしか感じません。

 tone.asm では、点灯時間が50%ですが、これを「デューティ比50%」といいます(下図左)。prog.asm (または prog.c )のように点灯しっぱなしの時は「デューティ比100%」といいます。

PWM波形

 次に、1つ目の「RCALL T1MS」をそのままにして、2つ目を「RCALL T10MS」としてみてください。こうすることにより、1ms点灯、10ms消灯を繰り返すようになり、明るさはより暗く感じられます。この時の波形が上図中央です。

 点灯時間と消灯時間が等しい時「デューティ比50%」、常時点灯の時「デューティ比100%」といいます。点灯時間1msで消灯時間9msなら「デューティ比10%」になります。この技術は「パルス幅変調(PWM)」と呼ばれ、モータのスピードコントロールや、デジタルオーディオアンプなどにも利用されています。

 更に、1つ目の「RCALL T1MS」を「RCALL T100US」としてみてください。「デューティ比約1%」で明るさは更に暗くなります。

 デューティ比を連続的に変化させれば下図のように任意の波形を表すことができ、モータのスピードコントロールや、デジタルオーディオアンプなどに利用されています。

PWM波形

 なお、ここでは、「0」の時間も「1」の時間もプログラムで調整しましたが、マイコン内蔵のハードウェアタイマーにPWMの機能が備わっているので、これを使えば、高速で正確なPWM波形を得ることができます。

(参考) Arduinoのスケッチ

Fade.ino:

7.2 LEDの明るさを連続的に変える

 以下は、デューティ比を0%から100%まで連続的に変化させるプログラムです。暗い状態から次第に明るくなり、1秒後に最大の明るさになり、これを繰り返します。

fade1.asm:

(参考) Arduinoのスケッチ

Fade1.ino:

(練習) LEDが、だんだん明るくなり、だんだん暗くなるプログラムを作りなさい。


8. デジタル出力(3): 数字を表示する

8.1 数字表示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:

(練習) 表示する数字を他の数字に変更してみなさい。

8.2 数字表示をカウントアップする

 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」の表示にしてみなさい。


9. 動くものを作る

 これまでの例では、マイコンの出力ピンにLEDや圧電ブザーを接続しました。AVRマイコンは最大20mAの電流を流すことができるので、この範囲であれば直接接続できます。この章では、モータなど大きな電流が流れる素子を駆動する例を、今まで製作した中から紹介します。大きな電流が流れる回路を制御する場合は、「電流が流れる経路」を意識することが回路の誤動作を防ぐことにつながります。

9.1 リレーの駆動

 さまざまな機器をオンオフ制御したい場合は、「リレー」が最も汎用性に富んでいます。ただし、ここでは交流100Vの機器ではなく、電池で動作する機器のみを考えます。おもちゃやラジオなど、制御対象の電圧や電流や極性などを気にせずに接続することができます。その際、「BDアダプタ」と組み合わせると、簡単に接続できるかもしれません(BDアダプタは製作も容易です)。

 下図に2種類のリードリレーSS1A05(5V 10mA 接点容量0.5A), Y14H-1C-3DS(3V 50mA 接点容量1A)を使った回路例を示します。マイコンに逆電圧がかからないよう、リレーに並列にダイオードを入れます。

(練習) 上の回路を組み立て、電池で動くオモチャをつないで、blink.asmの動作を確かめなさい。また、4を参考に、スイッチを押すと動くようにしてみなさい。

9.2 DCモータの駆動

 DCモータ(小型の直流モータ)は大きな電流が流れるので、マイコンに直接接続することはできず、トランジスタやFETを使って駆動します。「ミニドラムマシン」では、バチを動かすのにDCモータを使い、2SC2120で駆動しました。赤外線リモコン自動車ではDCモータを2SK3142で駆動しました。

 モータのオンオフだけでなく、正転逆転させたい場合は、Hブリッジ回路の専用IC(例えばTA7291P)を使います。IC内部での電圧降下には注意が必要です。

9.3 振動モータの駆動

 Midiコントローラ「ジングルベル」では、振動モータFM34F(100mA以下)を使い、「鈴」を鳴らました。2SC2120(最大800mA)や2SC735など、少し多目に電流を流せるトランジスタを使っています。

9.4 サーボモータの駆動

 サーボモータは5Vで動作しますが、4.5Vでも支障なく動作します。

 「ミニドラムマシン」では、ドラムの位置にバチを動かすためにPICO/STD/Fを使いました。サーボモータの制御端子はマイコンに直接接続できます。

9.5 高輝度LEDの駆動

 それほど大電流ではなく、しかし駆動する素子がたくさんある場合は専用のドライバICを使うと便利です。「>交通安全教室用信号機」では、高輝度LED(12V 60mA)を駆動するために、専用のドライバIC TD62083(最大500mA)を使っています。


10. テレビに表示する

 テレビのビデオ信号をプログラムで作り、画像を表示させます。内蔵クロックにはジッタがあるため、表示されるビデオ画像はゆらゆらと揺らぎます。また、規格外のビデオ信号なのでテレビによってはうまく表示できないかもしれません。でも、とにもかくにもテレビに画像が表示されるので、それだけで感激します。

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

ldi R20, 0b00000010 ;GRAY を追加し、また、「gray」命令をマクロ定義し、灰色も表示できるようにしてみなさい。

(参考資料)

[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/


(付録1) 使用する主な部品

部品表
名称 概観 備考
ブレッドボード(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 ブレッドボード
電池ケース
圧電スピーカー
を貼り付けます。

(付録2) バッチプログラムの内容

avra.bat(抜粋):
tools\avrasm32 -l list.txt -fI %1
avrw.bat(抜粋...ヒダピオ用):
tools\hidspx -php -d4 %1

(付録3) マクロ定義とマクロ展開

 ここでは、マクロ定義について説明します。

 「OUT」命令は値を直接出力することはできないため、

LDI R16,0b00001000 OUT DDRB,R16 のように値を一旦レジスタに入れ、そのレジスタの内容を「OUT」で出力しました。このようなパターンは度々現れるため、「OUTI」のような命令があれば OUTI DDRB,0b00001000 と書くことができて便利なのにと思います。マクロ機能を使うとこのような新しい命令を作り出す(定義する)ことができます。

 以下の例では、「.macro」~「.endm」の箇所で、新しい命令「OUTI」をマクロ定義しています。そして、「.cseg」以降のプログラムの中で、通常の命令と同じように「OUTI」という命令を使っています。

prog.asm:

 ここで、「OUTI」命令の1つ目のパラメータ「DDRB」はマクロ定義の「@0」に、2つ目のパラメータ「0b00001000」はマクロ定義の「@1」の箇所に代入されます。

 このソースプログラムは、アセンブルするとどのような機械語プログラムとなるのでしょうか。以下のようにリストファイル(list.txt)で確認することができます。

OUTI」の箇所は、その定義に従ってパラメータを含めてマクロ展開され、そして展開されたプログラムから機械語が作られていることがわかります。なお、プログラム冒頭に書き加えた「.listmac」は、マクロ展開の内容をリストファイルに出力するための指示です。

(付録4) ATtiny2313の主な命令

(ニーモニックと意味) (例と説明)
転送命令
MOVMOVe MOV R16,R17 R17の内容をR16に格納
LDILoaD Immediate LDI R16,100 R16100を格納
LDLoaD indirect LD R16,X Xレジスタで示される番地の内容をR16に格納
LDSLoad Direct from SRAM LDS R16,DATA DATA番地の内容をR16に格納
STSTore indirect ST X,R16 R16の内容をXレジスタで示される番地に格納
STSSTore direct to SRAM STS DATA,R16 R16の内容をDATA番地に格納
LPMLoad Program Memory LPM Zレジスタで示される番地の内容をR0に格納
入出力命令
ININput IN R16,PIND PINDから入力しR16に格納
OUTOUTput OUT PORTB,R16 R16の内容をPORTBに出力
算術演算命令
ADDADD ADD R16,R17 R16R17の内容を足す
SUBSUB SUB R16,R17 R16からR17の内容を引く
SUBISUB Immediate SUBI R16,100 R16から100を引く
CLRCLeaR CLR R16 R16を0にする
論理演算命令
ANDAND AND R16,R17 R16R17の内容とAND
ANDIAND Immediate ANDI R16,0b00000001 R160b00000001とAND
OROR OR R16,R17 R16R17の内容とOR
ORIOR Immediate ORI R16,0b00000010 R160b00000010とOR
EORExcluseve OR EOR R16,R17 R16R17の内容とExclusiveOR
COMCOMplement COM R16 R16のビットを反転(1の補数)
比較演算命令
CPComPare CP R16,R17 R16R17の内容と比較
CPIComPare Immediate CPI R16,100 R16100と比較
シフト演算命令
LSLLogical Shift Left LSL R16 R16の内容を1ビット左にシフト
LSRLogical Shift Right LSR R16 R16の内容を1ビット右にシフト
分岐命令
RJMPRelative JuMP RJMP LOOP LOOPにジャンプ
BREQBRanch if EQual BREQ LOOP Zフラグが1ならLOOPにジャンプ
BRNEBRanch if Not Equal BRNE LOOP Zフラグが0ならLOOPにジャンプ
BRCSBRanch if Carry Set BRCS LOOP Cフラグが1ならLOOPにジャンプ
BRCCBRanch if Carry Cleared BRCC LOOP Cフラグが0ならLOOPにジャンプ
スタック操作命令
PUSHPUSH PUSH R16 R16の内容をスタックに待避
POPPOP POP R16 R16にスタックから値を復旧
サブルーチン命令
RCALLRelative CALL RCALL SUB サブルーチンSUBの呼び出し
RETRETurn RET サブルーチンが呼び出されたアドレスに戻る
その他
NOPNo OPeration NOP 何もしないで1クロック費やす

koyama88@cameo.plala.or.jp