[トップページへ戻る]

キャラクタLCDモジュール I2C化アダプタ

2014年1月
お馴染みのキャラクタLCDモジュール。操作するには6本のI/Oピンが必要で、ピン数が少ないマイコンで使うには少々不便です。そこで、キャラクタLCDモジュールをI2C化するアダプタを作りました。使用するI/Oピンが2本で済みます。ついでに、I2Cバスの電圧を気にせず使えるようにしました。


寄り道開発
I2C-EEPROM使用 湿度温度ロガーお試し版」の最後に書いたロガー第2弾の構成を検討したところ、3.3Vと5VのI2Cデバイスを利用することになりました。電圧変換の仕組みが必要です。とりあえずOLED(3.3VのI2Cデバイス)の代わりにLCD(5V)を使って開発を進めまし……進めようとしたところで気付きました。「I2C接続のLCDを持ってない」。というわけで、まずそれを作ることにしました。ちょうどそのタイミングでI2Cレベル変換モジュールを入手することができたので、練習を兼ねて使ってみることにしました。結局、
・任意のキャラクターLCDモジュール(5V)をI2C化するアダプタ。
・I2Cバスが3.3Vでも5Vでも気にせず接続できる。
というものを作ることにしました。

I2Cレベル変換の必要性
I2Cに限らず一般に、異なる電圧で動作するデバイスを接続するには注意が必要です。そのまま接続すると、低電圧側のデバイスでは受信するHiの信号レベルが許容値以上の電圧かもしれず、デバイスが壊れる可能性があります。高電圧側のデバイスでは受信するHiの信号レベルが足りないかもしれず、Loと判定する可能性があります。

(1)
本アダプタは、I2Cのスレーブデバイスです。図のように、内部的にはマイコン(AVR)がLCDモジュール(以下LCD)を操作したりI2Cバスと接続して通信を行ったりします。LCDは5Vで動作します。マイコンは2.7〜5Vで動作します。
今、3.3Vで動作するマスターデバイスに本アダプタを接続することを考えます。


(2a)
マスターデバイスの電圧に合わせ、マイコンを3.3Vで動作させるとします。このときI2Cバスの信号レベルも3.3Vになります。一方、LCDとも3.3Vで通信することになりますが、LCDは5Vで動作しているため信号レベルも5Vが基準となります。ここで電圧の不整合が起こります。

(2b)
ではLCDの電圧に合わせ、マイコンを5Vで動作させるとします。それならLCDとの間に信号レベルの問題はありません。しかしマスターデバイスとも5Vで通信することになり、ここで電圧の不整合が起こります。

(3)
LCDはいつでも5V動作なのでマイコンをそれに合わせるとすれば、I2Cバスの方で3.3V〜5V間のつじつまを合わせることになります。そのための専用ICがあります。I2Cの信号電圧を双方向に変換するICです。これをI2Cデバイス間に挟めば、電圧の不整合を起こさず通信することができるようになります。

条件付きで…
条件付きですが、I2Cレベル変換なしでデバイスを接続する方法もあります。
上図(2a)において、LCD(5V)からマイコン(3.3V)への出力信号がなければ、マイコンはダメージを受けません。ここで、LCDがHiを受信するときにHiであると認める信号レベル(閾値)は、通常のキャラクターLCDモジュールだと2.2〜2.5Vです(TTLレベル。データシートを確認すること)。マイコンからLCDへ送信するHi(3.3V)がLoと誤認されることはないと考えられます。
この仕組みを利用する場合、マイコン(3.3V)とLCD(5V)の電圧が安定していることが必要です。少なくとも、ノイズや負荷によって電圧が変動することがあったときに、マイコンのHiがLCDのHiの閾値を下回ってはいけません。

回路図とプログラム
回路図


動作テスト時にVCCとGNDのワイヤーを逆に挿してI2Cレベル変換モジュールを壊してしまいました。その失敗から電源ラインにSBDを入れています。0.4Vの電圧降下がありました。I2Cレベル変換モジュールは1.7Vから動作することになっていますが、SBDの電圧降下とHT7750の変換効率を考慮し、現実的には2.7Vの回路から利用可といったところです。

動作電圧と通信速度から、I2Cのプルアップ抵抗は2kΩが妥当なようです。実験中、4.7kΩでも問題ありませんでした。
※工作物にはうっかり1kΩを付けてしまったが問題なく動いている。でも少し後悔。

本アダプタのI2Cスレーブアドレスは 0111100 または 0111101 です。JMPを切り替えてどちらか選べます。これにより、I2Cバスに本アダプタを2個まで接続することができます。また、回路内に同じスレーブアドレスのデバイスがあるときに、こちら側で回避するという使い方もあります。※アドレスの値はI2CのOLED(グラフィックディスプレイ)を参考にした。

本アダプタの中心となるマイコンはAVR ATtiny20です。I2Cのスレーブ機能を内蔵しています(マスター機能はない)。ピン数もLCDを動かすのに十分で、まさにこういった目的のためにあるようなマイコンです。
ATtiny20の性能上、通信速度は100kHzが限度のようです。マスターから400kHzの通信を試みてもエラーにはなりませんが、期待した速さは出ません。※[Application Note] Atmel AVR290: Avoid Clock Stretch with Atmel tinyAVR - (pdf)
ATtiny20を基板に取り付けた状態でファームの書き込みができます。

本アダプタはLCDのコントラスト(文字の濃さ)調整の抵抗と、バックライトの明るさ調整の抵抗を含みません。コントラスト調整の抵抗は、LCDモジュールにより取り付け方(配線)が異なることがあるためです。

参考までに… もし本アダプタを5V専用として作るなら、HT7750の昇圧部とI2Cレベル変換部を省略できます。主要な部品がATtiny20とプルアップ抵抗2本だけというシンプルな構成になります。

配線図


完成/動作中の様子

計画変更により、昇圧回路をモジュール化したことに意味が無くなりましたが、せっかくなので使うことにしました。それでこんな形の完成品です。使用しているLCDは「LCDモジュール SC162シリーズ」で紹介したものです。

動作デモは、I2CマスターデバイスとしてATmega88Pが3.3Vで動作しています。LCDは16x2サイズのものに対し、10x2の表示範囲で、2行つながり上書きモード、カーソル表示、を指定しました。右上から左下にかけて「65535」が自動改行されています。また、右下「P4」の次は左上「P5」に続いています。最初に左上に書いてあった文字は上書きされています。「abc」の右側の「P」がぼやけているのは、その位置でカーソルがブリンクしているためです。おかげで、この「P」が画面内で最も古い文字で、手前の「c」が最も新しい(最後に書かれた)文字だということが分かります。カーソルを表示しないモードだと画面上のどこまで上書きされたのか分かりません。

【参考】
秋月のI2Cレベル変換モジュール[M-05825]は穴が半分の形でピンが付けにくいです(写真・右)。スルーホール基板に表面実装する分には良さそうですが。
16穴基板[P-02515]と重ねて(間にポリイミドテープを挟んで絶縁する)、基板用リードフレーム[C-07011]を使ってみました(写真・左)。手間はかかりますが、ICソケットに挿せる足が付いて少し便利になりました。

元々付属している細ピンヘッダはブレッドボードに挿すには適しています。しかし基板にハンダ付けするには長いし、ICソケットに挿すには太いし、ピンソケットに挿すには細くて緩いしで、基板の工作には向いていません。

その後、手直ししました
上記の完成品がどうしても使いづらかったので、自分が本当に望む形に作り替えました。LCDモジュールのピンが手前に出ていたのを基板の裏側へ出すようにしました。LCDと重ねて取り付けられるようになり、使いやすくなりました。




写真のLCD(DigiTron SC162C)の端子の位置と並び順はメジャーではないかもしれません。多くのLCDは左側2列か、上辺1列(左から1:GND, 2:VCC, 3:Vo, …, 14:DB7, 15:LED-A, 16:LED-K)のようです。

スレーブ側/マスター側のプログラム
本アダプタ(スレーブデバイス)のファーム、マスターデバイスのサンプルプログラムとAVR用ライブラリを公開します。
ダウンロード I2cLcdMaster_and_Slave.zip 開発環境: Windows7SP1/64bit, AtmelStudio6

ここで公開するプログラムも、後述するプロトコルの説明も、本アダプタを作らない限り役立つことはないでしょう。似たようなものを作ろうとする人には参考になるかもしれません。実際のところ本アダプタのようなデバイスは、自作するより既製品を用意し、Arduinoから使う人が多いのではないかと思います。素(す)のAVR(ATmega48〜328P)向けのライブラリが活用される機会は、ますますないでしょう。

 
通信プロトコル(基本)
LCDモジュール(以下 LCD)を操作するための、最小限の(低レベルの)通信手順を以下に示します。LCDのコントローラを直接操作する感覚です。コマンド、データの詳細はコントローラのデータシートを参照してください。

S SlaveAddress+W A コントロールバイト A データバイト A P
. 0111100 0 または
0111101 0
. 0x00(RS_CMD) または
0x80(RS_DATA)
. 任意の1byte . .
S…スタートコンディション(マスターからスレーブへ送信する)
P…ストップコンディション(マスターからスレーブへ送信する)
A…アクノリッジ(スレーブからマスターへ送信される)

I2Cの1回の通信(SからPまで)でマスターから送信する内容は固定長です。
LCDにコマンドを送信するには、コントロールバイトを0x00とし、コマンドの具体値を送信します。
LCDにデータを送信するには、コントロールバイトを0x80とし、目的のデータを送信します。具体例としては文字コードです。仮に8byteのデータを送信する場合、連続8回の通信を行うことになります。

スレーブデバイス(本アダプタ)は基本的にACKを返します。ただし、スレーブアドレス送信時に誤ってリード(R)指定した場合はNACKを返します。その他、通信エラーが発生した場合にNACKを返すことがあります。

コントロールバイトの値(マクロ定義)
マクロ定義 定義値 データバイトの内容 動作内容
RS_CMD 0x00 1byte
LCDのコマンド
LCDのRS線を「コマンド状態」にセットし、LCDのコマンドを送信する。
RS_DATA 0x80 1byte
LCDのデータ
LCDのRS線を「データ状態」にセットし、LCDのデータを送信する。

【参考】 AVR用ライブラリ内の関数
関数名 内容
I2CCHARLCD_SendCmd(uint8_t cmd) LCDにコマンドを送信する。
I2CCHARLCD_SendData(uint8_t data) LCDにデータを送信する。


通信プロトコル(拡張)
LCDモジュール(以下 LCD)を操作するための、拡張された(高レベルの)通信手順を以下に示します。LCDのコントローラに対する複雑な操作なしに、LCD操作/文字表示をすることができます。先述した低レベルの操作も拡張プロトコルの手順に含まれるので、先と同様に使うことができます。

S SlaveAddress+W A コントロールバイト A データバイト1 A データバイト2 A データバイト32 A P
. 0111100 0 または
0111101 0
. RS_CMD または
RS_DATA または
LCD_FUNC_XXXXX
. 任意の1byte . 任意の1byte . 任意の1byte . .
S…スタートコンディション(マスターからスレーブへ送信する)
P…ストップコンディション(マスターからスレーブへ送信する)
A…アクノリッジ(スレーブからマスターへ送信される)

I2Cの1回の通信(SからPまで)でマスターから送信する内容は不定長です。基本プロトコルの形が最小パターンです。
データバイトは通常、1byteか2byteです。文字列を送信する場合、最大32文字(32byte)まで一括して送信できます。1文字ずつ通信するより効率的です。

スレーブデバイス(本アダプタ)は基本的にACKを返します。ただし、スレーブアドレス送信時に誤ってリード(R)指定した場合はNACKを返します。32byteを越えるデータバイトを一度に送信すると、越えた分すべてにNACKを返します。その他、通信エラーが発生した場合にNACKを返すことがあります。

拡張コントロールバイトの値(マクロ定義)
マクロ定義 データバイトの内容 動作内容
LCD_FUNC_INIT 2byte
表示桁数,行数
表示領域の大きさを指定し、LCDを初期化する。
例)
(16, 2) (16, 1) (8, 2) など。
16x2のLCDに対して(10, 2)などと指定してもよい。
LCD_FUNC_SETLINEMODE 2byte
行の動作を示す値
行の表示動作をセットする。
例) ※後述、引数の説明を参照。
LCD_LINEMODE_COMBINE | LCD_LINEMODE_OVERWRITE,
LCD_CURSOR_ON
LCD_FUNC_SETPOS_XY 2byte
表示位置 桁,行
表示位置をセットする。桁(x/column),行(y/row)の順。
0から始まる値。グラフィック座標のように見る。
例)
左上、1行目の1文字目は (0, 0)
2行目の5文字目は (1, 4)
LCD_FUNC_PUTCHAR 1byte
文字コード
1文字表示する。LCDのデータシートのコード表にある値。
例)
'A' 'b' '+' など。
LCD_FUNC_PUTINT 2byte
8bit/16bitの値
(-32768〜32767)
符号あり整数を10進数で表示する。
16bit整数はHi-Loの順に2byte送信する。
8bit整数はHi=0x00とした16bit値と見る。
LCD_FUNC_PUTUINT 2byte
8bit/16bitの値
(0〜65535)
符号なし整数を10進数で表示する。
16bit整数はHi-Loの順に2byte送信する。
8bit整数はHi=0x00とした16bit値と見る。
LCD_FUNC_PUTBYTE 1byte
8bitの値
(0x00〜0xFF)
8bit(1byte)の値を16進数で表示する。
接頭辞「0x」や接尾辞「h」は表示されない。
RS_CMD 1byte
LCDのコマンド
LCDのRS線を「コマンド状態」にセットし、
LCDのコマンドを送信する。
RS_DATA 1byte
LCDのデータ
LCDのRS線を「データ状態」にセットし、
LCDのデータを送信する。
※通常のコントロールバイト(RS_CMD, RS_DATA)も区別なく一緒に使うことができる。

LCD_FUNC_SETLINEMODE の引数(マクロ定義)
マクロ定義 意味(16x2のLCDとして説明)
LCD_LINEMODE_SEPARATE 1行目と2行目は独立している(2行が一続きではない)。
1行ごとに先頭と末尾があるということ。
A
LCD_LINEMODE_COMBINE 1行目行末から2行目行頭へ自動的に続く。
全体として1行目行頭が表示の先頭、2行目行末が末尾ということ。
LCD_LINEMODE_STOP 末尾で停止する。末尾を越える書き込みは無視する。 B
LCD_LINEMODE_OVERWRITE 末尾から先頭へ戻り、上書きで書き込みを続行する。
LCD_CURSOR_ON カーソルを表示する。上書きモードのとき、どこまで表示したか目印になる。 C
LCD_CURSOR_OFF カーソルを表示しない。
※(A | B, C)の形で使用する。

【参考】 AVR用ライブラリ内の関数
関数名 内容
I2CCHARLCD_Init(uint8_t slaveAddress,
uint8_t columnSize, uint8_t rowSize)
スレーブアドレスとLCDの表示サイズを指定して、
LCDを初期化する。
I2CCHARLCD_SlaveSelect(uint8_t slaveAddress) I2Cバスに本アダプタを2個接続している場合に、
操作するデバイスを切り替える。
I2CCHARLCD_SendCmd(uint8_t cmd) LCDにコマンドを送信する。
I2CCHARLCD_SendData(uint8_t data) LCDにデータを送信する。
I2CCHARLCD_SetLineMode(uint8_t lineMode,
uint8_t cursorFlag)
行の表示モードを指定する。
I2CCHARLCD_SetPos(uint8_t row_y, uint8_t column_x) 表示位置を指定する。
I2CCHARLCD_Cls() 画面を消去する。
I2CCHARLCD_PutChar(char cc) 1文字表示する。
I2CCHARLCD_PutString(const char* str) 文字列を表示する。
I2CCHARLCD_PutStringP(const char* strpgm) プログラム領域内の文字列を表示する。
I2CCHARLCD_PutInt(int16_t n) 符号あり整数を表示する。10進数。
I2CCHARLCD_PutUInt(uint16_t n) 符号なし整数を表示する。10進数。
I2CCHARLCD_PutByte(uint8_t n) 0詰め2桁の16進数を表示する。
I2CCHARLCD_PutWord(uint16_t n) 0詰め4桁の16進数を表示する。
I2CCHARLCD_DefChar(uint8_t id, const uint8_t* dots) ユーザー定義文字を設定する。
id = 0〜7
dots = 5x8ドットの文字パターンを示す8byteの配列。
※詳細はLCDのデータシートを参照。



◆ ◆ ◆
I2Cのスレーブ側のファームを初めて書きました。どんなものか分かって面白かったです。
I2Cレベル変換モジュールがデリケートで参りました。4個買って2個破損。悔しかったです。
最初に完成したアダプタは想像以上に大きく、作った後で使いにくいことに気付きました。
とりあえず作ってみよう、で失敗。実際に使っている様子(イメージ)をしっかり固めてから
作った方がよいということを、改めて確認することとなりました。


(C) 『昼夜逆転』工作室 [トップページへ戻る]
inserted by FC2 system