概要
Raspberry Piでストロベリーリナックス社製LCD(LCDコントローラはST7032i)を制御するプログラムを作成しました。LCDは16文字×2行表示ですが、コントローラ自体はDDRAMに80文字まで文字を保持できるので、16文字×4行を画面切り替えによって制御できるようにしました。たった64文字を制御するだけですが、私にとっては難しくて大変でした。
動作動画
youtubeにアップした動作概要動画へのリンクです。まずはファンクションキーに割り当てたアイコンの表示/非表示動作、次にカーソルを矢印キーで移動して画面の切り替え動作及びカーソルがZ字型に移動する動作、次にカーソルに文字を入力する動作、最後にエンターキーで1・2行目と3・4行目を2秒おきに切り替える動作でCtrl+C押下で切り替え動作から入力モードへ戻ります。
構成
下記のように接続します、I/FはI2Cです。
| PinNo | LCD | RaspberryPi | 
|---|---|---|
| 1 | RST | 3.3V | 
| 2 | SCL | SCL1 | 
| 3 | SDA | SDA1 | 
| 4 | VSS | GND | 
| 5 | VDD | 3.3V | 
機能仕様
- Deleteキーでカーソル位置の文字を消去、消去後のカーソル(点滅、以下同様)は移動させない
- カーソルを矢印キーでリアルタイムに動かす
- 任意の文字キー入力でカーソル位置に文字を表示させる、入力後のカーソルは右に移動させる
- F1~F9キーに9種類のアイコンを割り当て押すたびに表示/非表示のトグル動作をさせる
- カーソルを2行目から3行目に移動させる時に画面が1・2行目表示から3・4行目表示に切り替わる
- エンターキーを押したら2秒おきに1・2行目と3・4行目表示を繰り返す、カーソルはOFFにする
- 1行目の右端で右キーを押すと2行目の左端のようにZ字型に遷移する
- Backspaceキーでカーソルの1つ左の文字を削除する、カーソルは左に移動ただしカーソルがはじの場合はZ字に遷移
ソース
- getch()関数はpythonの標準入力であるinput()関数とは違い、文字入力をするとEnterキー入力なしで処理してくれる関数です。詳しくは参考ページを参照してください。Linuxディストリビューションで動作するもののようです。
- メイン処理のwhile文でキー入力をポーリングしています。入力される文字コードはaなら97、Enterなら13などif文で条件分岐させていますが、矢印キーやファンクションキーは特殊なようで例えば左矢印なら[27,91,68]と1回のキー入力で文字コードが3回入力されたり、F1キーなら[27,91,49,49,126]が入力される等特殊になっています。また、これらの特殊なキーは環境によって同じキーボードでも入力コードがかわってしまったので注意が必要です。理由はわかりません。
- slide_show()関数で2秒おきの画面切り替えをしていますが、sleep関数でカウントしている最中はgetch()でキー入力ができず解除処理ができないので、Ctrl+Cでキーボード割り込みを発生させて解除しています。
- st7032iは、カーソルの位置(アドレスカウンターACに格納されているアドレス)に文字が入力されるので、基本的な制御方法としてはACに書き込みたい位置のアドレスをwrite(Set DDRAM Address Instruction)してから、文字コードを書き込み(Write Data to RAM Instruction)となります。16×4行の位置とアドレスの関係は下のほうに記載します。
LCD.py
import sys
from time import sleep
import smbus
import tty
import termios
def getch():
    fd = sys.stdin.fileno()
    old = termios.tcgetattr(fd)
    try:
        tty.setraw(fd)
        return sys.stdin.read(1)
    finally:
        termios.tcsetattr(fd,termios.TCSADRAIN,old)
def init():
    trials = 5
    for i in range(5):
        try:
            sleep(0.04)
            bus.write_i2c_block_data(st7032i,ctr_inst,[0x39,0x39,0x14,0x70|(contrust&0x0f),0x5c|(contrust&0x30)>>4,0x6c])
            sleep(0.2)
            bus.write_byte_data(st7032i,ctr_inst,0x0d)
        except IOError:
            if i==trials-1:
                sys.exit()
def view_icon(x):
    global icon
    global position
    bus.write_byte_data(st7032i,ctr_inst,0x39)
    if x == 0:
        if icon[x] == 0:
            bus.write_byte_data(st7032i,ctr_inst,0x40)
            bus.write_byte_data(st7032i,ctr_data,0x10)
            icon[x] = 1
        else:
            bus.write_byte_data(st7032i,ctr_inst,0x40)
            bus.write_byte_data(st7032i,ctr_data,0x00)
            icon[x] = 0
    elif x == 1:
        if icon[x] == 0:
            bus.write_byte_data(st7032i,ctr_inst,0x42)
            bus.write_byte_data(st7032i,ctr_data,0x10)
            icon[x] = 1
        else:
            bus.write_byte_data(st7032i,ctr_inst,0x42)
            bus.write_byte_data(st7032i,ctr_data,0x00)
            icon[x] = 0
    elif x == 2:
        if icon[x] == 0:
            bus.write_byte_data(st7032i,ctr_inst,0x44)
            bus.write_byte_data(st7032i,ctr_data,0x10)
            icon[x] = 1
        else:
            bus.write_byte_data(st7032i,ctr_inst,0x44)
            bus.write_byte_data(st7032i,ctr_data,0x00)
            icon[x] = 0
    elif x == 3:
        if icon[x] == 0:
            bus.write_byte_data(st7032i,ctr_inst,0x46)
            bus.write_byte_data(st7032i,ctr_data,0x10)
            icon[x] = 1
        else:
            bus.write_byte_data(st7032i,ctr_inst,0x46)
            bus.write_byte_data(st7032i,ctr_data,0x00)
            icon[x] = 0
    elif x == 4:
        if icon[x] == 0:
            bus.write_byte_data(st7032i,ctr_inst,0x47)
            bus.write_byte_data(st7032i,ctr_data,0x10)
            icon[x] = 1
        elif icon[x] == 1:
            bus.write_byte_data(st7032i,ctr_inst,0x47)
            bus.write_byte_data(st7032i,ctr_data,0x08)
            icon[x] = 2
        elif icon[x] == 2:
            bus.write_byte_data(st7032i,ctr_inst,0x47)
            bus.write_byte_data(st7032i,ctr_data,0x18)
            icon[x] = 3
        elif icon[x] == 3:
            bus.write_byte_data(st7032i,ctr_inst,0x47)
            bus.write_byte_data(st7032i,ctr_data,0x00)
            icon[x] = 0
    elif x == 5:
        if icon[x] == 0:
            bus.write_byte_data(st7032i,ctr_inst,0x49)
            bus.write_byte_data(st7032i,ctr_data,0x10)
            icon[x] = 1
        else:
            bus.write_byte_data(st7032i,ctr_inst,0x49)
            bus.write_byte_data(st7032i,ctr_data,0x00)
            icon[x] = 0
    elif x == 6:
        if icon[x] == 0:
            bus.write_byte_data(st7032i,ctr_inst,0x4b)
            bus.write_byte_data(st7032i,ctr_data,0x10)
            icon[x] = 1
        else:
            bus.write_byte_data(st7032i,ctr_inst,0x4b)
            bus.write_byte_data(st7032i,ctr_data,0x00)
            icon[x] = 0
    elif x == 7:
        if icon[x] == 0:
            bus.write_byte_data(st7032i,ctr_inst,0x4d)
            bus.write_byte_data(st7032i,ctr_data,0x1e)
            icon[x] = 1
        elif icon[x] == 1:
            bus.write_byte_data(st7032i,ctr_inst,0x4d)
            bus.write_byte_data(st7032i,ctr_data,0x1a)
            icon[x] = 2
        elif icon[x] == 2:
            bus.write_byte_data(st7032i,ctr_inst,0x4d)
            bus.write_byte_data(st7032i,ctr_data,0x12)
            icon[x] = 3
        elif icon[x] == 3:
            bus.write_byte_data(st7032i,ctr_inst,0x4d)
            bus.write_byte_data(st7032i,ctr_data,0x02)
            icon[x] = 4
        elif icon[x] == 4:
            bus.write_byte_data(st7032i,ctr_inst,0x4d)
            bus.write_byte_data(st7032i,ctr_data,0x00)
            icon[x] = 0
    elif x == 8:
        if icon[x] == 0:
            bus.write_byte_data(st7032i,ctr_inst,0x4f)
            bus.write_byte_data(st7032i,ctr_data,0x10)
            icon[x] = 1
        else:
            bus.write_byte_data(st7032i,ctr_inst,0x4f)
            bus.write_byte_data(st7032i,ctr_data,0x00)
            icon[x] = 0
    row = position/16
    col = position%16
    if row == 0:
        bus.write_byte_data(st7032i,ctr_inst,0x80|0x00|col&0x0f)
    elif row == 1:
        bus.write_byte_data(st7032i,ctr_inst,0x80|0x40|col&0x0f)
    elif row == 2:
        bus.write_byte_data(st7032i,ctr_inst,0x80|0x10|col&0x0f)
    elif row == 3:
        bus.write_byte_data(st7032i,ctr_inst,0x80|0x50|col&0x0f)
def clear_icon():
    bus.write_byte_data(st7032i,ctr_inst,0x39)
    bus.write_i2c_block_data(st7032i,0x80,[0x40,0x40,0x00, 0x80,0x42,0x40,0x00, 0x80,0x44,0x40,0x00, 0x80,0x46,0x40,0x00, 0x80,0x47,0x40,0x00])
    bus.write_i2c_block_data(st7032i,0x80,[0x49,0x40,0x00, 0x80,0x4b,0x40,0x00, 0x80,0x4d,0x40,0x00, 0x80,0x4f,0x40,0x00])
def clear():
    global position
    bus.write_byte_data(st7032i,ctr_inst,0x01)#clear Display
    sleep(0.001)
    clear_icon()
    sleep(0.001)
    return_home()
def move_left():
    global position
    if position%16 > 0 and  position%16 <= 15:
        position -= 1
        bus.write_i2c_block_data(st7032i,ctr_inst,[0x38,0x10])
        #print position
    elif position == 16:
        position -= 1
        bus.write_byte_data(st7032i,ctr_inst,0x80|0x0f)
    elif position == 32:
        position -= 1
        bus.write_byte_data(st7032i,ctr_inst,0x80|0x4f)
        bus.write_byte_data(st7032i,ctr_inst,0x38)
        for i in range(16):
            bus.write_byte_data(st7032i,ctr_inst,0x1c)
    elif position == 48:
        position -= 1
        bus.write_byte_data(st7032i,ctr_inst,0x80|0x1f)
    else:
        return -1
def move_right():
    global position
    if position%16 >=0 and position%16 < 15:
        position += 1
        bus.write_i2c_block_data(st7032i,ctr_inst,[0x38,0x14])
        #print position
    elif position == 15:
        position += 1
        bus.write_byte_data(st7032i,ctr_inst,0x80|0x40)
    elif position == 31:
        position += 1
        bus.write_byte_data(st7032i,ctr_inst,0x80|0x10)
        bus.write_byte_data(st7032i,ctr_inst,0x38)
        for i in range(16):
            bus.write_byte_data(st7032i,ctr_inst,0x18)
    elif position == 47:
        position += 1
        bus.write_byte_data(st7032i,ctr_inst,0x80|0x50)
    else:
        return -1
def move_up():
    global position
    if position/16 <> 0:
        position -=16
    else:
        return -1
    row = position / 16
    col = position % 16
    if row == 0:
        bus.write_byte_data(st7032i,ctr_inst,0x80|0x00|(col&0x0f))
    elif row == 1:
        bus.write_byte_data(st7032i,ctr_inst,0x38)
        for i in range(16):
            bus.write_byte_data(st7032i,ctr_inst,0x1c)
        bus.write_byte_data(st7032i,ctr_inst,0x80|0x40|(col&0x0f))
    elif row == 2:
        bus.write_byte_data(st7032i,ctr_inst,0x80|0x10|(col&0x0f))
    #print position
def move_down():
    global position
    if position/16 <> 3:
        position += 16
    else:
        return -1
    row = position / 16
    col = position % 16
    if row == 1:
        bus.write_byte_data(st7032i,ctr_inst,0x80|0x40|(col&0x0f))
    elif row == 2:
        bus.write_byte_data(st7032i,ctr_inst,0x38)
        for i in range(16):
            bus.write_byte_data(st7032i,ctr_inst,0x18)
        bus.write_byte_data(st7032i,ctr_inst,0x80|0x10|(col&0x0f))
    elif row == 3:
        bus.write_byte_data(st7032i,ctr_inst,0x80|0x50|(col&0x0f))
    #print position
def delete():
    bus.write_byte_data(st7032i,ctr_data,0x20)#write space
    bus.write_byte_data(st7032i,ctr_inst,0x38)
    bus.write_byte_data(st7032i,ctr_inst,0x10)#move cursor to left
def write_char(c):
    if c >= 0x00 and c <= 0xff:
        bus.write_byte_data(st7032i,ctr_data,c)
        bus.write_i2c_block_data(st7032i,ctr_inst,[0x38,0x10])#cursor left shift
        move_right()
    else:
        return -1
def backspace():
    move_left()
    delete()
def slide_show():
    global position
    print "Now presentation mode, Please press \"Ctrl+C\" to return edit mode."
    return_home()
    bus.write_byte_data(st7032i,ctr_inst,0x0c)#display on/off -> cursor off
    while True:
        try:
            sleep(2)
            move_down()
            move_down()
            sleep(2)
            move_up()
            move_up()
        except KeyboardInterrupt:
            print "Now edit mode"
            return_home()
            bus.write_byte_data(st7032i,ctr_inst,0x0d)#display on/off -> cursor on
            break
def write_string(s):
    for c in list(s):
        write_char(ord(c))
def return_home():
    global position
    bus.write_byte_data(st7032i,ctr_inst,0x02)
    sleep(0.001)
    position = 0
st7032i = 0x3e
ctr_inst = 0x00#(C0=0, RS=0)
ctr_data = 0x40#(C0=0, RS=1)
contrust = 36#(0-63)
bus = smbus.SMBus(1)
icon = [0,0,0,0,0,0,0,0,0]
position = 0
state = 0
clear_icon()
init()
clear()
write_string("Hello World!    This program is written by      Python. :)")
return_home()
print "ready now. Please press Ctrl+C to stop quit program."
while True:
    key = ord(getch())
    if key == 3:
        break
    elif key == 13:
        print "Enter"
        slide_show()
    elif key == 127:
        print "DEL"
        delete()
    elif key == 8:
        print "BS"
        backspace()
    elif key == 27:
        key = ord(getch())
        if key == 91:
            key = ord(getch())
            if key == 68:
                print "left"
                move_left()
            elif key == 67:
                print "right"
                move_right()
            elif key == 65:
                print "up"
                move_up()
            elif key == 66:
                print "down"
                move_down()
            elif key == 49:
                key = ord(getch())
                if key == 49:
                    key = ord(getch())
                    if key == 126:
                        print "F1"
                        view_icon(0)
                elif key == 50:
                    key = ord(getch())
                    if key == 126:
                        print "F2"
                        view_icon(1)
                elif key == 51:
                    key = ord(getch())
                    if key == 126:
                        print "F3"
                        view_icon(2)
                elif key == 52:
                    key = ord(getch())
                    if key == 126:
                        print "F4"
                        view_icon(3)
                elif key == 53:
                    key = ord(getch())
                    if key == 126:
                        print "F5"
                        view_icon(4)
                elif key == 55:
                    key = ord(getch())
                    if key == 126:
                        print "F6"
                        view_icon(5)
                elif key == 56:
                    key = ord(getch())
                    if key == 126:
                        print "F7"
                        view_icon(6)
                elif key == 57:
                    key = ord(getch())
                    if key == 126:
                        print "F8"
                        view_icon(7)
            elif key == 50:
                key = ord(getch())
                if key == 48:
                    key = ord(getch())
                    if key == 126:
                        print "F9"
                        view_icon(8)
                elif key == 49:
                    key = ord(getch())
                    if key == 126:
                        print "F10"
                        view_icon(9)
                elif key == 51:
                    key = ord(getch())
                    if key == 126:
                        print "F11"
                elif key == 52:
                    key = ord(getch())
                    if key == 126:
                        print "F12"
    else:
        print "character:{0},    code:{1}".format(chr(key),key)
        write_char(key)
    sleep(0.005)
文字表示座標の考え方
- 16文字×4行の配置とアドレスの関係を下記に示します。LCDの行数は表の行に、LCDの横の文字数は表の列にアドレスと管理用のPosition番号を記載しています。例えば1行目の1文字目はDDRAMアドレス0x00、3行目の2文字目は0x11といった感じです。
- わかりづらいですが、1行目の16文字目アドレス0x0Fの次の0x10アドレスは3行目の1文字目といったように、アドレス上は1行目→3行目→2行目→4行目になっています。これは本来、下記のようにst7032iのDDRAMアドレスの並びは40文字×2行なので、3行目以降の概念はないのですが17~32列目を3・4行目にしているからです。
- このLCDはアドレスカウンターの値をREADできないのでPositionという変数で座標を保持しています。DDRAMアドレスは16進数、Position変数は10進数です。
| 行数(※) | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1(0000) | DDRAM | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0A | 0B | 0C | 0D | 0E | 0F | 
| Position | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | |
| 2(0100) | DDRAM | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 4A | 4B | 4C | 4D | 4E | 4F | 
| Position | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | |
| 3(0001) | DDRAM | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 1A | 1B | 1C | 1D | 1E | 1F | 
| Position | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | |
| 4(0101) | DDRAM | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 5A | 5B | 5C | 5D | 5E | 5F | 
| Position | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 
※DDRAMアドレス上位4bits

