#179 ATtiny2313 ミニ・ユニバーサル・ボード用サンプル・プログラム(アセンブラ)

#179 ATtiny2313ミニ・ユニバーサル基板(ATtiny2313)用のアセンブラ・プログラムのサンプルです。AVR Studio4 でアセンブルしてあります。プロジェクト・フォルダ毎に圧縮してあります。AVRSPX用のバッチ・ファイル、HEXファイルも含んでいます。

電源電圧は5V、クロックは10MHzのレゾネータを使用しています。LEDやスイッチを同じポートに接続すれば、#175/#175A ATtiny2313 USB DIPモジュールでも使用できます。

mega168用はこちら

 (1) LED点灯
 (2) LED点灯,スイッチ入力
 (3) LED点滅(TIMER0/CTC)
 (4) 高速PWM制御(TIMER0/PWM)
 (5) I2Cマスタ送信 #176 MCP23017制御
 (6) I2Cマスタ送受信 I2C-EEPROMのデータの読み出し
 (7) I2Cスレーブ送受信
 (8) I2Cマスタ送受信

このページのサンプル・プログラムは、AVR初心者の導入として用意しました。作者もAVR初心者です。実は#179は作者自身が学習に使用するために製作した基板です。ここに掲載しているプログラムはAVRで初めて作ったプログラムです。サンプル(6)の掲載時点でAVRプログラム暦4日ですので、まずいところがあるかもしれませんが、その点はご容赦ください。

I2C関係のプログラムは複雑になったため、初心者向けとはいえませんので、難しいと思ったら無視してもらって結構です。

学習の過程で作成したプログラムは初心者のかたに有用と思われるものはここに追加していきます。とりあえず、アセンブラで作成していますが、そのうちAVRのC言語を始めたらC言語のプログラムも掲載する予定です。

プログラムは無保証、無サポートです。改良などのため予告なく変更することがあります。

なお、ファイル名変更やロケーション変更などの可能性があるため、個別ファイルに直リンクされないようにお願いします。

(1) LED点灯 (07/08/11) 出力ポートの使用法
単純にLED1を点灯させるプログラムです。PORTDを出力ポートに設定して、そのLEDを点灯させるだけです。マイコンを使う上で最も基本となる処理です。

test1.zip
(2) LED点灯,スイッチ入力 (07/08/11) 入力ポートの使用法
スイッチの状態によりLEDをON/OFFさせます。タクト・スイッチを押したときにOFF、離したときにONします。入力はPORTDレジスタではなく、PINDレジスタから読み出すことに注意してください。また、入力に設定したPORTDのビットに"1"を書き込むとそのビットに対応するポートのプルアップが有効になります。マイコンを使う上で最も基本となる処理です。

test2.zip
(3) LED点滅(TIMER0/CTC) (07/08/11) CTCモードの使用法
タイマ0を使用してLEDを点滅させます。タイマ0のコンペア機能を利用して一定の周期タイミングを生成します。コンペア・マッチすると、タイマ0は自動的にクリアされます(CTCモード)。時計などを作るときの基本となる仕組みです。

test3.zip
(4) 高速PWM制御(TIMER0/PWM) (07/08/11) PWMの使用法
LED2を消灯状態からだんだん明るくしてまた消灯してと、それを繰り返します。PWMでLEDの明るさを制御するプログラムです。LEDが接続されている0Bチャネル(OC0B/RD5)を使用しています。LEDの代りにトランジスタなどを使ってDCモータをドライブすれば、回転速度制御などに応用できます。

test4.zip
(5) I2Cマスタ送信 #176 MCP23017制御 (07/08/13) USI使用I2Cマスタ
tiny2313のUSIモジュールを使用したI2Cマスタ(マスタ送信のみ)のプログラム・サンプルです。#176 I2C 16ビットI/Oエクスパンダ のGPIOAを出力ポートに設定して、そこに接続されたLEDの表示を約1秒毎に更新します。0〜255のバイナリ値を順番に表示し、それを繰り返します。
クロック・ストレッチはサポートしていません。
今回からスタックを定義してサブルーチンを使っています。
ロジアナ実測波形

配線

  • #179 AVR Pin19 SCL → #176 CN1 Pin3
  • #179 AVR Pin17 SDA → #176 CN1 Pin4
  • #179 VCC → #176 CN1 Pin2
  • #179 GND → #176 CN1 PIN1

TWIモジュール使用のプログラムこちらのページにあります。

test5.zip
PW="avr"
(6) I2Cマスタ送受信 I2C-EEPROMのデータの読み出し(07/08/23更新) USI使用I2Cマスタ
tiny2313のUSIモジュールを使用したI2Cマスタ(マスタ送信、マスタ受信)のプログラム・サンプルです。 I2C-EEPROMの24LC64のデータ読み出しの簡単な使用例です。データ書き込みは読み出しにくらべて簡単なので省略します(リピート・スタート・コンディションを発行しないで、続けてROMデータを送信し、最後にストップ・コンディションを発行するだけ)。
最初に1度だけデータを書き込む処理を追加したプログラム"test6-2"を用意しました。(07/08/26)
従来からの"test6"にはROM書き込みの処理は含まれていません。
クロック・ストレッチはサポートしていません。

動作中はスタート・コンディションを発行するたびにLED1の状態が反転します。ROMがつながっていない場合でも点滅します。このLED1はハングアップしていないか確認するためのものです。

ROMアドレス0x1234にROMデータ0x56が書き込まれている場合、それが正常に読み出されるとLED2が点灯します。それ以外は消灯した状態です。

初めに1度だけROMアドレスの0x1234、0x1235にROMデータ0x56、0x78の2バイトを書き込む処理を追加したプログラム test6-2を追加しました。(07/08/26)

ロジアナ実測波形

配線

  • #179 AVR Pin19 SCL → 24LC64 Pin6
  • #179 AVR Pin17 SDA → 24LC64 Pin5
  • #179 VCC → 24LC64 Pin8
  • #179 GND → 24LC64 PIN4

※SCL、SDAのラインは4.7K程度のプルアップ抵抗が必要です。
※24LC64のPin1,2,3(A0〜A2)はGNDに接続して、I2Cアドレスは0x50に設定してあるものとします。この場合、コントロールバイトは0xA0(W)、0xA1(R)となります。

BUG FIX 07/08/23 ファイルを差し替えました(ファイル名は同じ)。

TWIモジュール使用のプログラムこちらのページにあります。

更新
07/08/23
test6.zip
P/W="avr"

追加
07/08/26

test6-2.zip
PW="avr"

(7) I2Cスレーブ送受信 (07/08/23) USI使用I2Cスレーブ
tiny2313のUSIモジュールを使用したI2Cスレーブのプログラム・サンプルです。1バイトのデータをスレーブ受信し、そのデータを保持して次回マスタから読み出されたときに、そのデータをスレーブ送信します。
また、受信した内容の下位2ビットの値をLED1とLED2に表示します("1"でON、"0"でOFF)。

この処理は、コンパイラのライブラリにあるような、1つの関数で送受信を待ち受けるものとは違い、簡易マルチタスク型のイベント・ドリブン方式になっています。これは「マイコンの1線2線3線インターフェース活用入門」で作成したPIC、H8版のスレーブ・プログラムと同等なものです。このような構造にすることの利点は、いつでも送受信の処理ができることです。したがって他の処理(たとえば7セグメントLEDのダイナミック・ドライブの周期処理やRS-232Cシリアル受信など)とは非同期に処理ができます。
プログラムの内容については別のところで説明する予定ですので、ここでは簡単な説明にとどめておきます。

<メイン・ループ>
サブルーチン "I2CSlCom"はI2Cスレーブ通信の本体です。このルーチンはメイン・ループの中でコールします。メイン・ループはひたすらこのルーチンのコールを繰り返すだけです。

<イベント・ハンドラ>
サブルーチン"SlaveReceive"はマスタからデータをスレーブ受信したときにコールされます。受信したデータはレジスタAccに入っていますので、この値を使って必要な処理を行います。ここでは、レジスタ"I2CData"に値を保存して、下位2ビットの状態をLEDに反映させています。

サブルーチン"SlaveSend"はマスタがデータを読み出そうとしたときに、スレーブ送信する直前にコールされます。このルーチンがコールされたら、送信するデータをレジスタAccにセットして戻ります。ここでは、先に保存したレジスタ"I2CData"の値を返します。

TWIモジュール使用のプログラムこちらのページにあります。

test7.zip
PW="avr"
(8) I2Cマスタ送受信 マスタ-スレーブ送受信テスト用のマスタ (07/08/23) USI使用I2Cマスタ
(6)のマスタ送受信の処理を元に、(7)のスレーブと通信するように変更したものです。I2Cのサブルーチン部分は同じものです。

図のように2つの#179相当のボードに(7)のプログラムと(8)のプログラムをそれぞれ書き込み、2つをI2Cバスで接続して通信させます。

マスタからは、1バイトのデータをマスタ送信します。

スレーブはそのデータをスレーブ受信して、レジスタへ保存します。また、このデータの下位2ビットの状態をLEDに表示します。

マスタは、データをマスタ送信したあと、少し待ってから今度は1バイトのデータをマスタ受信します。この受信データの内容は先ほどマスタ自信が送信したものと同一ですが、この値に+1したものを次回のマスタ送信データとします。

スレーブはマスタからデータを読み出されたときは、先に受信してレジスタに保存してあるデータをスレーブ送信します。

マスタがわざわざ、マスタ受信した内容を+1したものを次回の送信データとしているのは、マスタとスレーブのそれぞれで送受信が正常に行われたことを確認するためです。マスタ受信した内容が不正なら、次回マスタ送信するデータも不正になり、結果的にスレーブのLEDの表示が乱れることになります。

test8.zip
PW="avr"

検索エンジン対策のため、一部パスワードがかかっているものがありますが、パスワードはzipファイルの下にかかれていますので、お手数ですが、それを使って解凍してください。

I2Cに関してはこちらで簡単に説明していますので、参照ください。


AVRのUSIモジュール vs PICのMSSPモジュール

PICのMSSPと違い、AVRのUSI(ユニバーサル・シリアル・インターフェース)はI2C制御のためにプログラムが介在する部分が多く、お世辞にも簡単に使えるというものではありません。ただ、専用モジュールを使わないで汎用I/Oポートだけで作るよりはましです(その分、ハードウェア的な規約があり、てこずることになるのですが)。USIを使えば内蔵のシフト・レジスタが使えるため、ソフトウェアで1ビットずつ順番にデータを取り出したりするような手間は省けますので、AVRの高速動作に相まって、高スルーレートなものを作ることも可能でしょう。シフトに関しては、クロック・ソースをタイマ出力や外部クロックにすることで、4ビットカウンタと組み合わせて、半自動的に出力することもできます。ただし、ACKの入力までは自動ではやってくれません。

マスタ・プログラムはまだ簡単なほうなので、スレーブ・プログラムを作成するときにはもっと手間取りそうです。今回I2Cマスタのプログラムを作りはじめてから、まともに動くまで、ロジアナを使いながら思考錯誤した結果、丸一日費やしました。ロジアナがなければ、手探り状態なので、もっと時間がかかったことでしょう。

スレーブ・プログラムの場合、スタート・コンディションを割り込みで検知したあと、スレーブ・アドレスのチェックなどはソフトウェアで処理しなければなりません。つまり、自分宛てかどうかはコントロール・バイトを受信しなければ分からないため、他のスレーブ宛ての通信でも、コントロール・バイトの受信処理だけは、バス上の全てデバイス(AVR USI使用デバイス)が実行しなければならないということです。たくさんのスレーブ・デバイスがつながっていて、かつ、通信の頻度が高いと、各スレーブ・デバイスは自分宛てでない通信でのオーバヘッドが増えるということになります。

片やPICのMSSPでマスタ送信する場合、基本的にはあるレジスタにデータを書き込むと、あとは8ビットのデータ出力からACKの入力まで自動でやってくれますので、割り込みやフラグで送信が終わったことをチェックするだけで済みます。またスレーブの場合でも、スタート・コンディションを受けて、アドレスが一致するまでは、すべてハードウェアが面倒を見てくれるため、自分宛ての受信があったことを割り込みやフラグで検知すれば、あとはデータを取り出すだけの簡単な処理で済みます。PICのMSSPを使ったあとにAVRのUSIを使うと、プログラムの煩雑さに少々戸惑うことになります。


USI モジュールについて

ようやくUSIモジュールでI2Cスレーブができました。マニュアルには直接的にかかれていないことや、どうみてもマニュアルの誤記と思われるものもあり、3日以上かかってしまいました。USIは基本的にはシフト・レジスタと4ビット・カウンタで、そこにスタート・コンディションの検出回路がついただけの単純なものです。また、ハードウェア的にはマスタ、スレーブの違いはありません。これらのことに気づくまでは、PICのMSSPのようなものを期待していたので、がっかりしたわけです。しかし、I2C関係の本を出している手前、ここはめげずにTWIの前になんとしてもUSIという思いでとりあえず完成させました。当然ながら、まだ改良の余地はたくさんあると思います。

USIにもいいところがあって、たとえば、シフト・レジスタのおかげで、1ビットずつ入出力するという手間が省けて、ソフトウェア処理ではどうしても時間のかかるビット処理の部分が短時間で処理できます。とくにスレーブ側のプログラムでSCLのエッジを監視しなくてよいのはかなり楽です。この部分はスルーレートにかかわりますので、ソフトウェアだけで作るよりはかなり有利です。

ところで、マニュアルの誤記と思われるものを1つ挙げておきます。まだtiny2313しか使ったことがないので、これがtiny2313に限られるかどうかはわかりません。
USICRのUSICS1とUSICS0でクロック・ソースを外部クロック(SCL)に設定する際、マニュアルには「立ち下がりエッジ」でシフトする、という設定が、実際は「立ち上がりエッジ」でシフトします。これは、回路のブロック図とも食い違っているのですが、実際にマニュアルの通りでは動きませんでした。他をいくらいじってもシフト・レジスタに入るデータが1ビットずれるので不審に思い、逆にしてみたらうまくいったという次第です。ターゲット・ボードには8ビットの数値を表示する手段がなにもないので、LEDのポートに8ビットのパルスを出力させて(0/1でパルス幅を変える)、それをロジアナで読んで確認したりしました。ずれているのは分かっても、何でずれるのかがさっぱり分かりませんでした。

まあ、時間はかかりましたが、ATtiny2313を使ってI2Cスレーブがつくられれば、安価なスレーブ・デバイスへの応用が期待できます。作者がUSIにこだわる理由はそこにあります。

別の案件として、そのうちtiny2313を使ってOne Wireのスレーブ・デバイスも作ってみたいと考えています。

ライタ・ソフト avrspxの使い方(ごく簡単な説明)


www.wsnak.com