ラズパイによるA/D入力をLCD(ACM1602)にレベルバーで表示  
       ロジアナによるI2Cのモニタ  AQM0802, 1602に表示、WEB Upへ  CCS-C学習のTOPへ
 LCD(ACM1602NI) に、A/D(MCP3208) から入力した値を、擬似レベルバーで表示したいと思います。このテストは、上のリンク(CCS-Cの学習)の中で PIC16F1938を使って実現したことがあります。ラズパイでも同じことができることを確かめたいと思いました。A/D(MCP3208) は、SPI インターフェイスで、12 ビットの精度を持ち、最大 8CHの A/D変換ができます。このデバイスの使用方法(Python版)は、金丸隆志氏の「最新 Raspberry Pi で学ぶ電子工作」の第6章の中にそのソフトモジュールを提示されていますので、それを使わさせていただきます。さて、
LCD(ACM1602NI) についてですが、このデバイスはひと癖あり、PIC で動かしたときに、さんざん苦労しました。I2Cインターフェイスなのですが、他の I2Cデバイスで使ったソフトモジュールをそのまま使っても動かすことができずに、色々な箇所にタイマーを入れて、やっと動かすことができました。ですので、たぶんラスパイにおいても普通には動かすことはできないだろうと、あらかじめWEB検索すると、案の定、I2C のボーレートをディフォールトの 100kHzから 50kHz に落として使っておられることが分かりました。後で、それらの設定を書きます。こう書くと「このデバイスは使っても大丈夫だろうか」と心配されるかもしれませんが、一旦動き出したらまったく問題はありません。バックライトもあり、サイズも大きな、見た目にも素敵なこの LCDを是非お使いください。ラズパイZero のようなロースペックのもので、ボーレートを落としても、レベルバーをリアルタイムで表示するという、かなりの負荷に十分応えていると思います。

[ 使用する物 ]
・ Raspberry Pi Zero (WiFi なしの古いもの、WEBに接続するために USBの LANアダプタを付ける)
・ I2C接続・バックライト付きキャラクタLCD 16 x 2 行 (ACM1602NI)
・ SPI接続・12ビット 8CH A/Dコンバータ (MCP3208)
基本的にはこれだけです。PC とラスパイ間で、ファイルのやり取りをしたり、ラズパイのターミナルからソフトを起動したりなどをするのに是非、リモートディスクトップは構築しておいてください。
ここに、概要は書いております。


【 1 】回路図と組み立て図
 回路図は下の図の通りです。下の CN-RASP は、ラズパイの GPIO コネクタです。 I2C と SPI インターフェイスを使用するので、やや接続が多くなっています。左の四角枠が LCD(ACM1602NI)で、IC1 は A/Dコンバータ (MCP3208)です。 VR1, VR2, VR3 はすべて 10kΩを使っています。バックライトを点灯させなくても良ければ 22Ωは不要です、電流は 10mAほどです。コンデンサは付けなくとも動作します。



下の写真は、動作している状態です。LCDの下の VR3は、LCDのコントラスト調整用です。AQMシリーズのコントラスト調整は、ソフト設定でしたが、このACM1602NI は VR になります。左の上側の VR1 は CH0 の入力調整用であり、下側の VR2は CH1 の入力調整用です。




【 2 】ファームウェアとファイルの配置
 これからの作業に入る前に、 「ラズパイで I2C接続と GPIOを利用するための設定」は済ませておいて下さい。
I2C デバイスは、それぞれが固有のアドレスを持っています。そのアドレスを調べるのに通常は ・・・
sudo i2cdetect -y 1
・・・ とのコマンドをターミナルから打って調べます。しかし、ACM1602NI を接続して、これを実行するとターミナルに表は表示されるのですが、検出はされておらず、 LCD がハングアップして、その後、LCD にアクセスしても何の応答もしなくなります。結局、ラズパイの電源を一旦落とさなくてはならなくなります。したがって
このコマンドは発行しない方が良いです、発行しても壊れることはありませんが・・・。このあたりが、ACM1602NI は、他の I2C デバイスとはかなり違っているようですね。しかし、そうは言っても、一旦動き出したら問題なくスムーズに利用できますので心配はいりません。この LCD の I2C アドレスは、マニュアルに記載されており、それは 0x50 となっています。最初に、I2C のボーレートをディフォールトの 100kHzから 50kHz に落とさなければならないと書きましたが、その設定をいたしましょう。以下のコマンドで config.txt を編集します。
sudo nano /boot/config.txt
 このファイルの最後の行に・・・
dtparam=i2c_baudrate=50000
・・・ を追加してください。これをセーブして、ラズパイを再起動してください。再起動後、I2C のボーレートは 50kHz になります。ただこの時に注意があります。それは一度 ACM1602 にアクセスしているとこのデバイスの電源を一度落とさないと初期状態に戻らないので、再起動時に LCDの電源も一度落としてください。

下記のリストに、このテストで使う LevelDSP_LCD.py を示します。このコードをマウスをドラッグさせてすべて選択して右クリックでコピーしてエディタに貼り付け、とりあえず任意の場所に、「名前を付けて保存」で名前を LevelDSP_LCD.py としてください。ご承知とは思いますが、ラスパイに入れるテキストファイルの文字コードは UTF-8 でなければなりません。このリストは UTF-8 で貼り付けているので、そのまま行けば良いのですが、時にエディタによっては変わってしまい、ラズパイ上でエラーを起こすことがないように気を付けてください。Windows 10 のメモ帳も、ディフォールトは UTF-8 になったと聞いています。秀丸エディタや TeraPAD は、コード変換ができます。私の環境ではめったに起きないのですが、たまに WEB からファイルをコピーするとコード違いで動かないことが、今でもたまにあります。その場合は UTF-8 にコード変換しています。

LevelDSP_LCD.py


さて、このファイルをラズパイ上のどの場所に置くかですが、ここでは /home/pi/work/test2 に入れました。


【 3 】起動と動作確認
  「ラズパイで I2C接続と GPIOを利用するための設定」が実行されており、回路図通りの接続がしてあれば、リモートディスクトップのターミナルから、次のコマンドを打ってファームウェアを走らせてください。

cd /home/pi/work/test2
・・・ これで test2 ホルダにはいりました。次に・・・
python LevelDSP_LCD.py
・・・ これで、LCDの画面に下の写真のような表示がされましたでしょうか。コントラストを見やすいように調整してください。表示されない場合は配線が正しいかを回路図と入念に照らし合わせてください。

LCD の表示画像には、チラつきはないと思います。これがもし I2C の Python モジールで、余分のタイマーを入れていると画像更新の時にチラつきが出ます。これで使っているモジュールは以前、PIC で動かした時に、どこまで I2C モジュールのタイマーを小さくできるかの
確認を繰り返ししている数値を使いましたので、これでは、たぶん必要最小限のタイマー(もう少し削れるかも)になっており、高速に動作すると思います。VOL1 や VOL2 を動かして見てください、疑似レベルバーがスムースに動くと思います。ループの中で、更新のタイミングは、これではループボトムに 0.5秒(sleep(0.5))のスリープを入れています。
 この while ループの始めから最後まで、実際、どれくらい処理時間が掛かっているかを計測して見ましょう。
つまり、while True: の下の #t1 = time() の #を外して活かし、次に、計測結果を表示する #print(time() - t1) の #を外して生かして下さい。これでループのトップからボトムまでの処理時間がターミナルに表示されます。
 このラズパイ(Raspberry Pi Zero)で値は変動していますが、大方、0.04 〜 0.05秒が計測されています。ということは、
この一連の処理時間は、50mSec未満となっています。ロースペックの ラズパイ Zero としては、結構、速いものだと思います。処理時間計測の処理をまた元のレムに戻して、スリープ時間は、sleep(0.3) などのお気に入りのタイマーにしてください。この数値を小さくすると、VOL1 や VOL2 を動かした時のレスポンスが速くなります。

 A/D から実際に取得したデータは、0〜4095 です。それを、0〜3.3Vとなるように、「電圧値へのスケール変換」をしている部分が while True: ループの中にありますので、ご確認ください。同じループの中に・・・
#print("CH0:" + res0 + " / CH1:" + res1)
・・・ とレムにしている行のレムを解除すると、ターミナルに現在取得している値が表示されます。ご自分で、その他の変数の val0 や val1 を print すると、A/D から取得した生の値を見ることもできます。その場合は ・・・
print("CH0:" + str(val0) + " / CH1:" + str(val1)) ・・・ とします。res0, res1 は文字列ですが、 val0, val1 は int ですから ・・・
レベルバーの描画のための元データは、リストトップの cgram_levHdt リストで定義しています。これを一括して CGRAM に登録しています。呼び出しコード 0x0D 〜 0x0F は予備分になります。


【 4 】ファームウェアの簡単な説明
 上で、ファームウェアの説明の入り口を少し始めました。この LevelDSP_LCD.py の中に、I2C を使った LCD への制御 と SPI を使った A/D の読み込み部分が入っています。コードのトップで、I2Cのアドレス(LCD) を adr_lcd として定義しています。
コードではその後、最初に 6個の I2C制御の基本関数を定義しています。さらに続いて、4個の便利に使える関数を定義しています。続いて、LCDの初期化関数 lcd_init()、疑似レベルバーの表示関数 DspLevelHorizontal(x, y, Data) を定義し、続いて、MCP3208からSPI通信で A/D値を取得する関数を定義しています。
区切りマーク
# ======== main ======== からは、上記の関数群を使っての実作業のルーチンが始まり、最後に while True: の無限ループとなっています。「Ctrl + c」 で、起動したソフトを停止できます。
この、ACM1602NI の CGRAM は、8文字分の RAMエリアが用意されています(下のマニュアルの表を参照)。一文字は、 5 x 8 dot で構成されます(X x Y)。横(X)の 5は 1byteの使用箇所で表現すると、0b00011111 の 1の部分に当り、縦(Y)の 8は、8バイトで表わせます。それらの8文字分のデータを定義しているリストが下記です。

cgram_levHdt= [     # 水平方向レベル表示用のCGRAMデータ
          0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,   # 08 |
          0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,   # 09 ||
          0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,    # 0A |||
          0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,    # 0B ||||
          0x1f, 0x1f, 0x1f,0x1f, 0x1f,0x1f,0x1f,0x1f,     # 0C |||||
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,   # 0D
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,   # 0E
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ]  # 0F

ちなみに、上のデータの「# 0C」の行は、その文字領域のドットが全部塗りつぶされたデータになっています。呼び出しコード 0D〜0F は予備ですが、結果としてスペースと同じデータになっています。
下の関数で一括登録されますので、予備の 0D〜0F などに自分で文字データを記入して、カーソル位置を指定した上で、呼び出しコード0Dの場合、 write_byte(adr_lcd, 0x0D) で呼び出せば、そのカーソルの位置に描画されますので試して見てください。

create_all_char(adr_lcd, cgram_levHdt)   # 8キャラクタの一括登録
・・・の行で、上のリストの 64バイトが一括して登録されます。このデバイスの場合、00〜07 と 08〜0F は重なっており、どちらでアクセスしても同じところを指定することになります、下の表をご参照ください。登録場所は、下の表の左端の一列です。

この表に加えて、LCD 関連のコマンドを知るのに使うのが、同じ
マニュアルにある Instruction Table です。19頁にあります。チェックして見てください。

def set_cursol(adr, col, row):   # LCDのカーソルを移動 col:0~7(15), row:0~1
上の関数を使用することで、カーソルの位置を目的の場所に自由に持っていくことができます。この LCD の場合 col(x)は 0〜15 、, row(y) は 0〜1です、範囲外を指定しても何も実行されません。

以上、簡単な説明をいたしました。これらのモジュールを使えば、I2C のボーレートをディフォールトの 100kHzから 50kHz に落としてはいますが、結構、高速に処理ができていると思います。

 最後に参考のために、この ACM1602NI に対して I2C のボーレートをどのくらい上げられるか試してみました。 結果としては、75kHz はダメ、70kHzは不安定、 65kHz ではOKだったことを報告いたしておきます(ロジアナで解析している方もおられます)。一つ注意があります。それは config.txt を編集してボーレートを変更し、その変更を有効にするために OSを再起動しますが、その時に再起動すると同時に、
ACM1602NI の電源を一旦落とさなければいけないことにご注意ください。そのことに気が付くまでに余計な時間がかかりましたのでお伝えしておきます。


【 Appendix 】
昔は、ロジアナと言えば数十万円以上、その後に出た USBを使った安価なものでも数万円でした。この安価版の USBを使ったロジアナを長い間使っていましたが、Windows10 に対応しなくなったので使わなくなっていました。

 先日、Amazon で 「KeeYees USBロジックアナライザ 24MHz 8チャンネル 価格:¥1,380」 というのを見かけて、この価格ならダメで元々というつもりで購入し少々テストをして見ました。その結果、驚きました、結構使えそうです。

この演習で使用している I2Cインターフェイス の解析をしてみましたので、簡単に報告します。
まずは、購入してから、8CH がすべてが大丈夫かをテストしました。その動作は OKでした、このチェックは後でしても良いですが、するべきです。

次に LCDの I2Cインターフェイスをロジアナに取り込むため、CH1を I2Cの SCLに、CH2を SDAに接続し GNDを接続しました。トリガは、アプリ(PulseView)の画面の信号(Dx)の上で左クリックすると立ち上がりや立ち下がりなどが選択できますので、D0(SCL) を立ち上がりに設定しました、下図の D0行の右端にトリガの立ち上がりのマークが表示されます。「samples」の数値はデータ総数です。その右となりはサンプリングの周期です。それぞれを設定しマウスポインタを samplesや周期のメニュー上にオーバーするとトータルの測定時間がツールチップに表示されます。
初めはどれほどに設定すれば良いかが分からないと思いますが、色々と実際に設定して繰り返し測定(Run)すると大体わかってまいります。Run してデータ数を獲得終了するか、Stop を押すことで停止します。
下図に、この演習の I2C の測定をした結果を示します。



 一番上にある、赤で SCL, SDA と記入している2行が、最初に表示されたデータです。マウスのスクロールで、ズームアップ・ダウンができますので、上図のように必要な箇所を拡大できます。ここでは、2段階のズームアップを示しています。一番下の表示では SCLの周期を計測しています。現在、I2C のボーレートは、65KHz に設定しています。測定結果のボーレートも、ほぼ 65KHzが測定されています。また、while ループのボトムにあるタイマーは sleep(0.3) に設定しています。つまり一連の動作の間隔は 300ms にしていますのでこれも、一番上の表示から合っていることが確認できます。
ACM1602NI のマニュアルを見ながら詳細に調べて行けば、どのようにコマンドを送っているか調べることもできますが、今回は、上の図に書き込んでいることくらいのチェックしかしておりません。
しかし、たったの 1,380円のツールでこれらのことができるような時代になったので、改めて驚いています。

Reported by TokioYamada@ADK
汎用製品通販のページへ        ラズパイのハードウェアWDTの製作へ         USB-IOのページへ