raspberry pi と I2C LCD モニターを使ってHello World を出力するプログラムを書いたんですが,モニターにHello World が出力されるまでに何が起きているのでしょうか.→ ラズパイでモニター出力
余談ですが,今回PythonではなくC言語を使ってみたのはハードを意識してみたかったからです.結論から言うとものすごく勉強になりました.
さて,組み立てた回路の全体像,実際の出力は次のようになりました.
また,書いたプログラムも見てみましょう.
#include <stdio.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>
#include <string.h>
int LCDAddr = 0x27;
int BLEN = 1;
int fd;
void write_word(int data){
int temp = data;
if ( BLEN == 1 )
temp |= 0x08;
else
temp &= 0xF7;
wiringPiI2CWrite(fd, temp);
}
void send_command(int comm){
int buf;
buf = comm & 0xF0;
buf |= 0x04;
write_word(buf);
delay(2);
buf &= 0xFB;
write_word(buf);
buf = (comm & 0x0F) << 4;
buf |= 0x04;
write_word(buf);
delay(2);
buf &= 0xFB;
write_word(buf);
}
void send_data(int data){
int buf;
buf = data & 0xF0;
buf |= 0x05;
write_word(buf);
delay(2);
buf &= 0xFB;
write_word(buf);
buf = (data & 0x0F) << 4;
buf |= 0x05;
write_word(buf);
delay(2);
buf &= 0xFB;
write_word(buf);
}
void init(){
send_command(0x33);
delay(5);
send_command(0x32);
delay(5);
send_command(0x28);
delay(5);
send_command(0x0C);
delay(5);
send_command(0x01);
wiringPiI2CWrite(fd, 0x08);
}
void clear(){
send_command(0x01);
}
void write(int x, int y, char data[]){
int addr, i;
int tmp;
if (x < 0) x = 0;
if (x > 15) x = 15;
if (y < 0) y = 0;
if (y > 1) y = 1;
addr = 0x80 + 0x40 * y + x;
send_command(addr);
tmp = strlen(data);
for (i = 0; i < tmp; i++){
send_data(data[i]);
}
}
void main(){
fd = wiringPiI2CSetup(LCDAddr);
init();
write(0, 0, "HELLO WORLD");
write(1, 1, "BY OGAWA");
}
モニターにHello World を出力するプログラム,一連の流れについて理解を深めるために,いくつかのトピックをまとめてみました.
I2C通信について
I2C通信とは,データの通信を行う同期式のシリアル通信のことです.クロックライン(SCL)とデータライン(SDA)の2本の信号線を用います.今回はraspberry pi と I2C LCDモニターを用いていますが,raspberry pi には通信を開始しクロックを生成する’マスタ’と呼ばれる役割を持たせ,I2C LCDモニターにはマスタからの指示を受けデータを送受信する’スレーブ’と呼ばれる役割を持たせています.
I2C通信の全体の流れとしては,
1.マスタが通信を開始.
2.スレーブアドレスの送信.マスタはアドレスと読み書き指示(R/Wビット)を送信.Rは1,Wは0
3.スレーブのACK.スレーブがアドレスを認識し,ACKを返す.
4.データの送受信.書き込みの場合はマスタがデータを送信し,スレーブがACKを返す.
5.通信の終了.
というような流れになっています.(らしい)
さて,I2C通信が関わっている部分のコードを見ます.
fd = wiringPiI2CSetup(LCDAddr);
wiringPiI2CSetup関数は,指定されたI2Cアドレスに基づき,デバイスを初期化します.引数となっている LCDAddr はスレーブデバイスのアドレスです.fd はI2C通信を行うに際して使用します.
wiringPiI2CWrite(fd, temp);
この関数では指定されたデータをI2Cデバイスに送信しています.fd は先ほど定義したファイルディスクリプタで,I2Cデバイスを示します.tempは送信するデータです.
void write_word(int data){
int temp = data;
if ( BLEN == 1 )
temp |= 0x08;
else
temp &= 0xF7;
wiringPiI2CWrite(fd, temp);
}
この関数ではデータをフォーマットし,I2Cデバイスに送信します.data に対して,バックライトがオンの時はビット3をセットし,オフの時はビット3をクリアします.そして先ほどのwiringPiI2CWrite(fd, temp)を使用してデータを送信します.
LCDディスプレイの制御
LCDディスプレイには複数の制御ビットがあります.
RSビット:コマンドとデータの判別をするビット
RWビット:読み書きの判別をするビット
ENビット:データの有効化ビット
バックライト制御ビット:バックライトのオンオフを制御するビット
関数でどんな処理が行われているか.
このコードでは,main関数以外に,write_word関数,send_command関数,send_data関数,init関数,clear関数を定義しています.それぞれの関数でどんな処理が行われているか見てみます.
void write_word(int data){
int temp = data;
if ( BLEN == 1 )
temp |= 0x08;
else
temp &= 0xF7;
wiringPiI2CWrite(fd, temp);
}
この関数ではBLEN(バックライト)の状況に応じてLCDに送信するデータを変更しています.BLENが1の場合はバックライトをオンにし,1でない場合にバックライトをオフにしています.temp |= 0x08, temp &= 0xF7 で第3ビットを変更しています.条件分岐でtempの値を修正し,wringPiI2CWrite関数を通じてLCDに送信し,バックライトを制御します.
void send_command(int comm){
int buf;
buf = comm & 0xF0;
buf |= 0x04;
write_word(buf);
delay(2);
buf &= 0xFB;
write_word(buf);
buf = (comm & 0x0F) << 4;
buf |= 0x04;
write_word(buf);
delay(2);
buf &= 0xFB;
write_word(buf);
}
この関数ではI2C通信によりLCDにコマンドを送信しています.buf = comm & 0xF0;でコマンドの上位4ビットを抽出しています.また,buf |= 0x04;でRS,RW,ENビットを設定しています.そしてbufの値をwrite_word()に渡してLCDに送信します.
send_commandではLCDの動作を制御する命令を,send_dataではディスプレイに表示する文字や情報のデータを送ります.
また,init関数では,LCDのビットモードの設定,5×7ドットの文字表示をする設定,カーソルを非表示にする設定等の,基本的な設定を行っています.
このように,main関数以外の関数でLCDディスプレイとインターフェースを制御していることがわかります.
機能を追加してみよう.
機能を追加したり,動作を変更したりするにはmain関数を書き換えていきます.例としてmain関数の中身を変えて,入力した文字を出力するプログラムにしてみます.(1列のみです,すみません)
int main(){
char input[32];
fd = wiringPiI2CSetup(LCDAddr);
init();
printf("Enter text to display on the LCD: ");
fgets(input, sizeof(input), stdin);
write(0, 0, input);
return 0;
}
C言語で遊ぶラズパイ
長々と書きました.ここでPythonで同じことをやろうとするとどうなるのか,コードを見てみます.
import LCD1602
import time
def setup():
LCD1602.init(0x27, 1)
LCD1602.write(0, 0, 'Hello World')
LCD1602.write(1, 1, 'By Ogawa')
time.sleep(2)
def destroy():
LCD1602.clear()
if __name__ == "__main__":
try:
setup()
except KeyboardInterrupt:
destroy()
たったこれだけになります.Python(のライブラリ),優秀ですね.
いくらPythonよりC言語が早いからといってPythonを使わない.というのではなく,処理速度を求める部分はC言語であったりハードウェアロジックを求め,複雑な部分はPythonで開発できるといいですね.
とはいえ今回C言語でI2C LCDモニターを制御したわけですが,すごく勉強になりました.