稼働中

Raspberry Pi Pico(d_17)LCD 1602  HD44780

LCD 1602 HD44780(I2C)

LCD 1602は2行16桁表示できる液晶ディスプレイ(Liquid Crystal Display)です。
LCD1602 モジュールのドライバーはHITACHI HD44780(Dot Matrix Liquid Crystal Display Controller/Driver)の互換4bitモードらしいです。実質、HD44780を設定している感じになります。そのためHD44780のデータシートが参考になります。
※micro:bitの記事e_21からRaspberry Pi Picoで使えるように追記、修正等をしました。

外観

LCD 1602モジュールの外観写真です。(以後はLCD 1602と記載します)
LCD1602の外観

接続例

LCD 1602を5Vで動作させるので別途電源が必要です。
レベルコンバータ経由でI2C接続するだけです。そのため、接続図などは省きました。
I2C通信にはI2C1のSDA(GP10)SCL(GP11)を使いました。
接続が出来たらI2Cデバイスのスレーブアドレスを調べてみます。
ThonnyのShellで確認しました。
LCD 1602のスレーブアドレスは以下のように63(0x3f)でした。


>>> from machine import I2C,Pin
>>> i2c=I2C(1, scl=Pin(11), sda=Pin(10), freq=100_000)
>>> i2c.scan()
[63]

HD44780の説明

LCDドライバーのHD44780のデータシートから抜粋して説明します。
■4ビットモード
HD44780は4-bit or 8-bit MPU interfaceだそうです。
LCD 1602を駆動するためには4-bitモードに初期設定をする必要があります。
実際には、Instruction(コマンド)のFunction set 0x30(8-bit lengths)を3回送って初期化をした後、Function set 0x20(4-bit lengths)を送って4ビットバスモードにします。
Function set |0|0|1|DL|N|F|*|*|
LCD1602 Function set
※ HD44780のデータシートから抜粋
DL:(0)4-bit lengths (1)8-bit lengths /send or received
N  :(0)1-line (1)2-line /number of display lines.
F   :character font 2-line * 5x8dots

■コマンド・データ送信
8bitのデータ、コマンドを上下4bitで送信する4bitモードを使います。
上下の4bitデータの下位4bitに「BL|E|R/W|RS」のデータを付加して送信します。
Enableが1から0に変化した時点でLCDにデータが書き込まれるそうです。
そのため付加データのEのbitを’1’のデータ、続けてEのbitを’0’にしたデータを送信します。

4bitモードでコマンド(Instruction|RS=0)の1byteデータ(D7~D0)を送信(write|R/W=0)する場合、
|D7|D6|D5|D4|BL=1|EN=1|R/W=0|RS=0|   (|D7|D6|D5|D4|1|1|0|0|)
|D7|D6|D5|D4|BL=1|EN=0|R/W=0|RS=0|   (|D7|D6|D5|D4|1|0|0|0|)
|D3|D2|D1|D0|BL=1|EN=1|R/W=0|RS=0|   (|D3|D2|D1|D0|1|1|0|0|)
|D3|D2|D1|D0|BL=1|EN=0|R/W=0|RS=0|   (|D3|D2|D1|D0|1|0|0|0|)
の4回を送信します。

4bitモードでデータ(文字データ)を送信する場合はRS=1にします。
|D7|D6|D5|D4|BL=1|EN=1|R/W=0|RS=1|  (|D7|D6|D5|D4|1|1|0|1|)
|D7|D6|D5|D4|BL=1|EN=0|R/W=0|RS=1|  (|D7|D6|D5|D4|1|0|0|1|)
|D3|D2|D1|D0|BL=1|EN=1|R/W=0|RS=1|  (|D3|D2|D1|D0|1|1|0|1|)
|D3|D2|D1|D0|BL=1|EN=0|R/W=0|RS=1|  (|D3|D2|D1|D0|1|0|0|1|)
の4回を送信します。
LCD1602 コマンド送信

■表示モードを設定
[0x28,0x0F,0x06,0x01]のコマンドを送信することにしました。
Function set      (0x28)   4ビットバスモード(DL=’0′)、Display-Lines 2行(N=’1′)
Display on/off   (0x0F)  オン、カーソル、点滅(D=’1′,C=’1′,B=’1′)
Entry mode set (0x06)    increment(I/D=’1′)
Clear display     (0x01)    クリア

■Display position
Entry mode set 0x06 にすると2-lineなのでDDRAMのaddress(カーソル、文字位置)は以下になります。
SetDDRAMのコマンドはDB7=’1’にしてDDRAMのaddressを指定します。
LCD1602 SetDDRAM
※ HD44780のデータシートから抜粋

列を’x’、行を’y’にするとDDRAMのaddressは y* 0x40 + x になります。
そのため、Display位置を指定するコマンドは (0x80 | (y* 0x40 + x))を送信することになります。
LCD1602 Display position
※ HD44780のデータシートから抜粋

■文字データの表示
文字データの送信例を示します。
LCD1602文字表示
※ HD44780のデータシートから抜粋 に加筆
’H’を表示する場合には、’H’に対応する文字コード’0x48’データを送信します。
送信データは先に示したような型で4回送信します。
|0|1|0|0|1|1|0|1| ‘0x4d’
|0|1|0|0|1|0|0|1| ‘0x49’
|1|0|0|0|1|1|0|1| ‘0x8d’
|1|0|0|0|1|0|0|1| ‘0x89’

※詳細は HD44780のデータシートを参照ください。

スクリプト

LCD 1602のスクリプトは以下のようにしました。
0行1列から’0123456789012345’を表示
1行1列から’abcdefghABCDEFGH’を表示
0行1列から’!#$%&()=|@+-*/<>’を上書き表示
1行1列からコード0xb0~0xbf(-アイ…ソ)を上書き表示
を試しています。
※開発環境はThonnyです。ThonnyでMicroPythonをRaspberry Pi Pico with RP2040にインストールして使っています。
※Raspberry Pi Pico単独で動作させるには’main.py’としてRaspberry Pi Picoにuploadして使います。


pico_lcd1602_02b.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from machine import I2C,Pin
import time
# LCD 1602
# HD44780U Dot Matrix Liquid Crystal Display Controller/Driver
adr_lcd=0x3f
i2c=I2C(1, scl=Pin(11), sda=Pin(10), freq=100_000)

# 1byte送信(write)
def lcd_wr(dat):
    buf = bytearray(1)          # 1byteごとに送る
    buf[0] = dat
    i2c.writeto(adr_lcd,buf)
    time.sleep_ms(2)

# 上下4bitにEN,RSなど付加して4回送る
#|D7/3|D6/2|D5/1|D4/1|BL|EN|R/W|RS| 8bit
def lcd_dat(dat,RS=0):          #デフォルトはコマンド送信(RS=0)、データ送信はRS=1
    f= (dat & 0xf0)|0x0C|RS     # |BL|EN|R/W|RS|=|1|1|0|RS| 
    s= (dat & 0xf0)|0x08|RS     # |BL|EN|R/W|RS|=|1|0|0|RS| 
    return f,s

# コマンドを送る Instruction
def lcd_cmd(dat):
    f,s=lcd_dat(dat)
    lcd_wr(f)
    lcd_wr(s)
    f,s=lcd_dat(dat<<4)         # LSB4bitをシフト
    lcd_wr(f)
    lcd_wr(s)

# 初期化 4bitモード、2行、カーソル点滅
def lcd_init():
    # function_initilize 4bit  4-bit interface reset
    fset=[0x30,0x30,0x30,0x20]
    for c in fset:
        f,s=lcd_dat(c)
        lcd_wr(f)
        lcd_wr(s)

    # 4bit init set data
    # 28:4bit col2,disp-cur,blink,entry-increase,disp-clear
    bset=[0x28,0x0F,0x06,0x01]
    for c in bset:
        f,s=lcd_dat(c)
        lcd_wr(f)
        lcd_wr(s)
        f,s=lcd_dat(c<<4)
        lcd_wr(f)
        lcd_wr(s)

# カーソル位置のセット (DDRAM address)
def lcd_pos(x, y):
    pos=(0x80 | (y* 0x40 + x))
    lcd_cmd(pos)

# 1文字を表示 (1文字データを送る RS=1)
def lcd_chr(dat):
    #RS=1 
    f,s=lcd_dat(dat,RS=1)
    lcd_wr(f)
    lcd_wr(s)
    f,s=lcd_dat(dat<<4,RS=1)
    lcd_wr(f)
    lcd_wr(s)

# 文字列の表示
def lcd_print(pr):
    m_dat=pr.encode()   # 英数字ならencodeが使えるようだ
    for i in m_dat:
        lcd_chr(i)

time.sleep(5)
# 実行 lcd に文字を表示
lcd_init()
lcd_cmd(0x01) #Clear
time.sleep(2)

# 0行1列から'0123456789012345'を表示
lcd_pos(0,0)
time.sleep(2)
moj='0123456789012345'
lcd_print(moj)
# 1行1列から'abcdefghABCDEFGH'を表示
lcd_pos(0,1)
time.sleep(2)
moj='abcdefghABCDEFGH'
lcd_print(moj)

# 0行1列から'!#$%&()=|@+-*/<>'を上書き表示
lcd_pos(0, 0)
time.sleep(2)
moj='!#$%&()=|@+-*/<>'
lcd_print(moj)

# 1行1列からコード0xb0~0xbf(-アイ...ソ)を上書き表示
# カタカナはROM CODEのデータを送る
lcd_pos(0, 1)
time.sleep(2)
for i in range(16):
    code=0xb0+i
    lcd_chr(code)

関数 部分説明

lcd_init()
LCD 1602(HD44780U)を初期化します。4bitモード、2行、カーソル点滅の設定にしています。
lcd_cmd(dat)
コマンドデータを送ります。
lcd_pos(x, y)
カーソル位置をセットします。
lcd_print(pr)
英数字の文字列(pr)を表示します。
lcd_chr(dat)
1文字のコードデータを送って表示します。
例えば’ア’を表示する場合、下表のROM Code表から upper ‘1011’ lower ‘0001’のコード’0xb1’を送ります。


>>> %Run -c $EDITOR_CONTENT
>>> lcd_chr(0xb1)

LCD1602 ROM Code

実行結果

結果は以下のようになりました。LCD 1602に文字を表示できました。
LCD1602 実行結果
※初期化後の
カーソルが見えない場合はLCD 1602裏面のコントラストを調整して見てください。

動作確認の動画です。

まとめ

Raspberry Pi PicoでLCD 1602を使ってLCDディスプレイに文字表示ができました。