最近マイコンいじりたい欲が出てきているので今回もAVRの記事です。
毎度毎度次回予告から外れているのですが、今回は予告通りI2Cと液晶の記事を書きます。
急にLCDがいじりたくなったのですが、僕がよく使うATtiny13AではI2Cの液晶以外はピン数の関係で接続できません。しかし、ATtiny13AにはI2C機能はありません。
ということで、まずI2Cの仕組みを理解してから、アセンブラで同じ機能を実装することにします。I2Cの説明はggればいくらでも出てくるので割愛しまして、具体的にマイコンにやらせることをメモ的に書いていきます。使用したのは秋月のこの液晶です。
・ピンの設定
I2Cにはマスタとスレーブなるものがありまして、マスタが通信を統括して、スレーブはそれに従うというルールがあります。データの送受信はマスタスレーブ無関係ですので、マスタがデータを読みたいと言えばスレーブは送信を開始しますし、マスタがこれを読めと言えばスレーブはデータを受信します。
I2Cには
ということは、I2Cの信号ラインはLowレベルかHiインピーダンスの二通りということになります。AVRのピン設定では、Lowは出力ピンモードで、Hiインピーダンスは入力ピンモードにすればそれぞれ設定可能です。要はオープンコレクタ/ドレインみたいになれば良いということです。以下Hiインピーダンス状態をHi、LowはそのままLowと記述します。
・スタートコンディション
まずI2Cの信号ラインは何も無い時はクロック、データともHiで待機します。次にマスタとして通信を開始したいデバイスがデータをLowにしてからクロックをLowにします。
簡単ですね。ちなみにデータをLowにしてからどれくらいの時間待ってからクロックをLowにすれば良いというのは各デバイスのデータシートに書いてあると思います。
・ストップコンディション
スタートコンディション同様通信の終了も同じようなのがあります。今度はクロックをHiにしてからデータをHiにします。
簡単ですね。これに関する時間もデータシートにあると思います。
以上をアセンブラで実装するのはまぁ簡単ですね。I/Oレジスタのbit操作だけでできます。続いてデータ関連の話です。
・スレーブアドレス送信
通信がスタートされたら、まず最初にマスタが誰を相手にするか、というのを送信します。I2Cはマスタに対して複数のスレーブが存在することができますが、同時に通信できるのは1人なのでまず誰と通信するかを指定します。誰を差すのがスレーブアドレスと呼ばれる7bitのデータです。デバイスごとに固有の値が振られていて、これもデータシートに記載されています。そして、実際にデータを送信する時は、この7bitのデータの後に更にもう1bit加えて合計8bitのデータをまず最初に送信します。スレーブアドレスの尻尾に付け足した1bitはマスタが受信するか、送信するかの指定に使用します。RWなどと呼ばれているbitでマスタが送信なら0、マスタが受信なら1を書かれます。
後術しますがこの操作で呼ばれたスレーブは「いますよ」を返します。
上の図ではスレーブアドレスとして「0111110」を、RWのbitは「0」を送信しています(この液晶は読み出しは出来ないようです)。ですからデータはRW→スレーブアドレス0bit→スレーブアドレス1bit…というように下位から順番に送られていいきます。アセンブラの操作では、まずレシスタにスレーブアドレスにRWを加えた8bitデータを入れて、それを左シフトさせて順番にクロックに乗せていくという感じでしょうか。
実際に信号の波形を見てみるとこんな感じになります。
上がデータ、下がクロックです。手書き図同様0x7C(01111100)を送信しています。クロックがHiのときにはデータを変化させてはいけないので(→スタートコンディションになってしまう)注意しましょう。
・Acknowledge
ACK、アク、アクナレッジなどとも呼ばれます。認めるみたいな意味の英単語ですが、このbitはマスタ又はスレーブが発行する「正常にデータを受信しました」のしるしですね。最初スレーブを呼ぶときには「いますよ」のようなものです。実際にどのようにACKを出すのかというと先程の図の続きで見てみましょう。
詳細は省いてますがこんな感じになります。マスタ側はACKを確認するために、第7クロックを出し終えたらデータ線をHiにしておきます。もしスレーブが正常にデータを受信していたら、データ線をLowにし異常ならHiのままにします。よってプルアップのデータバスなのでLowが優先され、正常に受信出来ていていたらLow、異常ならHiということになります。ここでマスタが第8クロック目を立ち上げてACKを読みます。つまりマスタ側は第8クロックを立ち上げてLowなら正常、Hiなら異常とわかるわけです。この動作は8bit(モードによっては10bit)のデータを受信したらマスタ、スレーブ関わらず必ず行われます。実際にACKが返ってきている様子を以下に。
最後のチョンと出ているのがACKですね(ホントは出ない方が良いと思う)。最後のクロックの立ち上がりできちんとLowになっています。スレーブのLowはマイコンのポートほどシンク電流が流せないようで若干電圧が残っていますが許容内ですので問題ありません。最後のクロックの立ち下がりでデータがピョコっと跳ね上がっているのは、スレーブが「もういいかな」と思ってLowをやめたからだと思います。なおACKを確認した後はクロックをLow→データをLowにして待機させましょう。ACKを確認できなかったらこれから送信するデータ等も意味を成さないのでエラー表示するなりなんなりしましょう。実は最初に貼ったオシロのキャプチャと同じのを送信しているのですが、最初のはスレーブを繋がず、こちらはスレーブを繋いで撮影しました。
今回は省きますが、スレーブのプログラミングをする時は、スレーブアドレスと称号して合ってたらACKを出す、間違ってたら出さないというようにすれば良いと思います。その後のデータのやり取りでは、単にクロックを数えてちゃんと8クロック来てたらACKを出すようにすれば良いんじゃ無いかと思います。
・コントロールバイト
ここからは通信するデバイスによりますが、冒頭で書いた秋月のLCD準拠で説明します。LCDに送るデータには実際に表示させる文字データの他に、コントラストの設定値やDRAMアドレスの指定などがあります。これらを単に送るだけでは表示させたら良いのかコントラストの設定値に使えば良いのかわかりませんのでコントロールバイトというのを送ってから、データを送ります。コントロールバイトでは「この次に送るデータが(コマンド/DRAMアドレス/表示させるデータ)ですよ」というのを指定します。データシートを読むと書いてありますが、単一コマンドを送る前は「0x00」、連続コマンドを送る前は「0x80」、データを送る前は「0x40」を送ります。
もう少し詳しく書くと、コントロールバイトの次に送るデータをデータバイトと言って、さらにこのコントールバイトとデータバイトをまとめて「コントロールワード」と呼びます。つまり単一コマンド(=データバイトに書く)を送る場合は、0x00→任意のコマンドとなります。連続して送る場合は、0x80→任意のコマンド→0x80→任意のコマンド→0x80→任意のコマンド→…→0x00→任意のコマンド でおしまいとなります。要はコマンドワードを繰り返すかという感じです。この辺はよく分からなくてちょっと悩みましたね。
・データバイト
8bitのコントールバイトを送信し終わったら次はコマンドや文字データ等のデータビットを送信します。これと言った注意は無いです。
・初期化について
LCDのデータシートを見ると初期化処理は大体載っているのでこの通りすれば問題なく表示できます。しかし、僕がデータシート通りにやったところ、LCDのコントラストが異常に高く画面が■で埋められて文字が読めませんでした。色々いじっているとどうやら「Follower control」の値「6C」を「69」に変更することで解決しました。取り敢えず治りましたがよくわかりません。
他にも機能としてはCGRAMなどがあるのですが、まだやっていないのでよくわかりません。
ソースは縦に長いのでこちらのページに記載しました。興味のある方は御覧ください。
原理がわかれば処理自体は簡単ですので大したことは無いですが、待ち時間とかクッソ適当なのでもし参考にされる場合はご注意ください。あらぬところへジャンプしたり読みにくいですが、バグ等有りましたら報告していただけると助かります。
アセンブラの練習用にプログラム領域にデータ置いたりしてるのでたぶんまた解説記事を書くと思います。なんとなくコメントは書いてありますがあんまり真剣に見ないようお願い致します。
ちなみに上記のソースは実行するとこうなります。
Web拍手反応(もう届いているのか…(うれしい))
>おぱっい
プルンプルン!!
>◯◯たです
どう?
それでは、今日はこの辺で。
11/26追記:誤字を修正 参考リンクを貼り忘れていたので貼っておきます。
I2C通信の使い方:http://www.picfun.com/c15.html