LCD1602をRaspberry PiとPCF8574を使って制御する方法
LCDディスプレイモジュール:LCD1602をRaspberry Piで制御して文字を表示してみたいと思います。
今回使ったLCDディスプレイは、I/OエキスパンダーIC:PCF8574がハンダ付けされて一体となったモジュールです。
ラズパイからPCF8574へI2Cで信号を送り、PCF8574で8bitのパラレル変換された信号でLCD1602を制御するという動きになります。
とりあえず動かしてみよう!
WEB上に公開されているサンプルプログラムを使って、とりあえず動かしてみましょう。
手順を順番に説明していきます。
はじめに、ちょっと改造が必要
今回のLCD1602モジュールの標準回路では、ラズパイのGPIO端子に5Vが接続されるようになっています。
このままの回路でも壊れはしなかったのですが、推奨される使い方ではありません。
PCF8574の抵抗を2つ取り外すことでこの問題は解消されるので、動かす前に取り外しておきましょう。
この改造が必要な理由はこちらの記事にまとめました。
回路図、接続図
I2Cで接続するので、ラズパイとの接続は電源、GND、SDA、SCLの4本だけ。
PCF8574のアドレスをチェック
接続が終われば、次のコマンドを実行してみてください。
i2cdetect -y 1
下図のようにアドレスが表示されます。
アドレスは0x27ということが分かりました。
PCF8574のアドレスはA0~A2ピンの状態によって決まります。
今回使ったモジュールでは全て5Vにプルアップされていたので、A0~A2は全て1ということになります。
データシートを見ると、スレーブアドレスはこのようになっているので、A0~A2が1なら、0100110、16進数なら27であることが分かります。
サンプルプログラムをダウンロード
電子工作キットの販売などを行っているosoyooのサンプルプログラムをダウンロードします。
wget http://osoyoo.com/driver/i2clcda.py
アドレスをチェック
プログラムの12行目にPCF8574のアドレスを指定している箇所があります。
この値が先程取得したPCF8574のアドレスと一致しているか確認してください。
一致していなければ先程取得したPCF8574のアドレスに書き換えておきます。
プログラムを実行
実行すると、91行目~98行目で指定されている文字が表示されます。
ここを書き換えることで好きな文字を表示させることができます。
文字が表示されない場合
プログラムを実行したのに何も表示されない場合は、文字の輝度が不足しているかもしれません。
PCF8574モジュールの基板にコントラストを調整するツマミが付いています。
ドライバーなどでゆっくり回すと、文字のコントラストが調整できるようになっています。
とりあえず動作させるだけならこれで十分です!
ここからは、LCDの仕組みやPythonプログラムの詳細を知りたい人向けに解説していきます。
LCDとは?
LCDとは、「Liquid Crystal Display」の略で液晶ディスプレイのことです。
液晶にかかる電圧を制御することで、光源(バックライト)からの光を遮断したり透過させたりしてディスプレイに文字を表示させます。
液晶ディスプレイの構造
LCDの構造はこのようになっています。
バックライトの光を液晶が通したり、通さなかったりすることで明るい部分と暗い部分を作ります。
図のように電圧がOFFの時に光が透過するものをノーマリーホワイトモード、逆に電圧がONの時に光が透過するものをノーマリーブラックモードと言います。
文字を表示する仕組み
今回使ったLCDでは、1文字あたり5×8ドットで表現されます。
一番下の行はカーソルに使われるので、文字として使える部分は5×7ドットとなります。
文字に見えるドットは光が透過しているので明るく見えます。
このように、どのドットが明るくなるかを制御して任意の文字を表示させることができるのです。
LCD1602とPCF8574の接続
今回使ったモジュールはLCD1602とPCF8574がはじめから接続された状態で、このような接続になっています。
PCF8574の出力ピンはP0~P7の8ピンあり、P0~P2がデータの読み書き制御のために使われます。
P3はNPNトランジスタを介してバックライトのカソード側に接続されています。
P4~P7までの4ピンがデータ送信に使われます。
LCD1602のデータは4bitで送信するモードと8bitで送信するモードがあります。
8bitで送信する場合はデータの送信が1回で済むのですが、4bitの場合は2回に分けて送信する必要があります。
今回のモジュールではデータ送信に使われるピンは4ピンなので4bitモードで使います。
4bitモードではプログラムが少し複雑になりますが、接続する信号線が少なくて済むのがメリットです。
Pythonコード
データシートを見ながら、Pythonのコードを作っていきましょう。
サンプルプログラムをベースに修正したものがこちらです。
import smbus
import time
# パラメータ設定
I2C_ADDR = 0x27 # PCF8574のアドレス
LCD_WIDTH = 16 # 1行に表示する文字数の上限
LCD_CMD = 0x00 # コマンド送信モード
LCD_CHR = 0x01 # データ送信モード
LCD_LINE_1 = 0x80 # 1行目のRAMアドレス
LCD_LINE_2 = 0xC0 # 2行目のRAMアドレス
LCD_BACKLIGHT = 0x08 # バックライトOFF
bus = smbus.SMBus(1) # SMBusの引数に1を指定する。ラズパイのI2Cバスの番号
def lcd_init():
lcd_byte(0x33,LCD_CMD,0.005,0.0001) # 初期化(0011を2回)
lcd_byte(0x32,LCD_CMD,0.0001,0.0001) # 初期化(0011、0010で4bitモードに設定)
lcd_byte(0x06,LCD_CMD,0.0001,0.0001) # データ書き込み時にカーソルを右にシフト
lcd_byte(0x0C,LCD_CMD,0.0001,0.0001) # ディスプレイ・オン、カーソル&BLINK・オフ
lcd_byte(0x28,LCD_CMD,0.0001,0.0001) # 2列 5x7ドット
lcd_byte(0x01,LCD_CMD,0.0001,0.002) # 全表示クリア、カーソルをホーム位置に戻す
time.sleep(0.0005)
def lcd_byte(bits, mode, tw1, tw2):
upper_bits = mode | (bits & 0xF0) | LCD_BACKLIGHT # 上位4bit(下位4bitを0にする)
lower_bits = mode | ((bits<<4) & 0xF0) | LCD_BACKLIGHT # 下位4bit(4bitシフトさせてから下位4bitを0にする)
bus.write_byte(I2C_ADDR, upper_bits) # 上位4bitを書き込み
lcd_toggle_enable(upper_bits, tw1, tw2)
bus.write_byte(I2C_ADDR, lower_bits) # 下位4bitを書き込み
lcd_toggle_enable(lower_bits, tw1, tw2)
# E(Enable)信号をトグル(LOW→HIGH→LOW)
def lcd_toggle_enable(bits, tw1, tw2):
time.sleep(0.0005)
bus.write_byte(I2C_ADDR, (bits | 0b00000100))
time.sleep(tw1)
bus.write_byte(I2C_ADDR,(bits & 0b11111011))
time.sleep(tw2)
def lcd_string(message,line):
message = message.ljust(LCD_WIDTH," ")
lcd_byte(line,LCD_CMD,0.0001,0.0001)
for i in range(LCD_WIDTH):
lcd_byte(ord(message[i]),LCD_CHR,0.0001,0.0001) # 文字のunicodeを送信
def main():
lcd_init()
lcd_string("LCD1602 TEST" , LCD_LINE_1) # 1行目に表示する文字
lcd_string("Success!!", LCD_LINE_2) # 2行目に表示する文字
time.sleep(10)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
pass
finally:
LCD_BACKLIGHT = 0x00 # バックライトOFF
lcd_byte(0x01,LCD_CMD,0.0001,0.0001) # LCDの表示内容をクリア
4bitモードで送信
4bitモードで送信するためには、8bitのデータを4bitずつに分けて2回送信する必要があります。
下記の部分がそれに該当します。
def lcd_byte(bits, mode, tw1, tw2):
upper_bits = mode | (bits & 0xF0) | LCD_BACKLIGHT # 上位4bit(下位4bitを0にする)
lower_bits = mode | ((bits<<4) & 0xF0) | LCD_BACKLIGHT # 下位4bit(4bitシフトさせてから下位4bitを0にする)
bus.write_byte(I2C_ADDR, upper_bits) # 上位4bitを書き込み
lcd_toggle_enable(upper_bits, tw1, tw2)
bus.write_byte(I2C_ADDR, lower_bits) # 下位4bitを書き込み
lcd_toggle_enable(lower_bits, tw1, tw2)
8bitのデータと0cF0(11110000)の&を取って下位4bitを0にしたものをupper_bits(上位bit)とします。
次に8bitのデータを4桁左にシフトしたものと0cF0の&を取ったものをlower_bits(下位bit)とします。
書き込む手順は、
- upper_bitsを送信
- E信号(書き込み信号)をHIGH
- E信号(書き込み信号)をLOW
- lower_bitsを送信
- E信号(書き込み信号)をHIGH
- E信号(書き込み信号)をLOW
という流れになります。
初期化
4bitモードの場合、初期化の流れはこのように記されています。
この部分のプログラムはこのようにしています。
def lcd_init():
lcd_byte(0x33,LCD_CMD,0.005,0.0001) # 初期化(0011を2回)
lcd_byte(0x32,LCD_CMD,0.0001,0.0001) # 初期化(0011、0010で4bitモードに設定)
lcd_byte(0x06,LCD_CMD,0.0001,0.0001) # データ書き込み時にカーソルを右にシフト
lcd_byte(0x0C,LCD_CMD,0.0001,0.0001) # ディスプレイ・オン、カーソル&BLINK・オフ
lcd_byte(0x28,LCD_CMD,0.0001,0.0001) # 2列 5x7ドット
lcd_byte(0x01,LCD_CMD,0.0001,0.002) # 全表示クリア、カーソルをホーム位置に戻す
はじめの0x33は2進数で00110011なので、4bitずつ送信すれば0011を二回繰り返すことになります。
次の0x32は2進数で00110010なので、4bitずつ送信すれば0011、0010と送信することになります。
一回目のデータ送信後に4.1ms以上、二回目は100us以上の待ち時間が必要になります。
その後は任意の初期設定を行います。
文字をUnicodeに変換して送信
Unicode(ユニコード)とは、世界中の文字に通し番号を割り当てたものです。
例えば、「A」は65番です。
この番号(コードポイント)に変換する関数がord関数です。
print(ord('A'))
と書いて実行すると、
と返ってきます。
この関数を使って文字をUnicodeに変換してLCDモジュールに送信しています。
def lcd_string(message,line):
message = message.ljust(LCD_WIDTH," ")
lcd_byte(line,LCD_CMD,0.0001,0.0001)
for i in range(LCD_WIDTH):
lcd_byte(ord(message[i]),LCD_CHR,0.0001,0.0001) # 文字のunicodeを送信
def main():
lcd_init()
lcd_string("LCD1602 TEST" , LCD_LINE_1) # 1行目に表示する文字
lcd_string("Success!!", LCD_LINE_2) # 2行目に表示する文字
time.sleep(10)
実行結果
このように指定した文字が表示されました!
次は日本語(カタカナ)を表示させてみたいと思います。