全能電路設計實戰

2015年2月5日 星期四

Raspberry Pi b+ 用Python 控制LCD 16x2 (HD44780) 簡介 Part 4 自訂文字或圖示


接續上一篇,在已知如何傳送指令和資料給LCM模組之後,這裡要介紹如何在模組上畫出自已要的圖並顯示出來。

先觀察一下LCM模組的螢幕,如下圖所示,每個"字"是
由左右5個(編號:0~4)、上下8個(編號:0~7),共 5 x 8 = 40 個點所組成成的。




而在LCM模組之中劃分了一塊記憶體空間提供給使用者儲存多達7個自訂字形。每個字都會占用連續8個byte的記憶體空間。言下之意,會有 7 x 8 = 56 個連續 byte 是使用者自行定義的字形檔空間,其中每個字用8個byte來表示,一個字之中的每條左右橫向5個點由一個byte裡面的前5個bit來控制。每一個點都代表著一個 bit 值,而該值則代表了該點黑(1)或不黑(0)。
如上一行所說,每個byte只會用到5個bit( bit0 ~ bit4 )有3個bit不會用到(bit7 ~ bit5 棄而不用)舉個例子來說,在記憶體某個字形檔空間的 byte 裡面就像下面這樣:

===============================
 bit : 7 6 5 4 3 2 1 0
------------------------------
value: x x x 1 0 1 0 1
pixel:                  
===============================

如上所示點的黑(1)/白(0)由 bit 4 ~ bit 0 所控制的情形,而該byte的值就是0b00010101 = 0x15。 (bit7~bit5沒用到就都設為0)


然後假設我現在要 "點" 一個形狀怪異的 "A" 字,所以按照上面的所說的一個字
要準備好 8 個連續 byte,按照順序點好要點的bit....用文字很難描述。下面用一張試著用圖表來顯示一個格(字)由八個橫條(byte)在LCD的記憶體裡面的情形:





由上圖所示,要準備連續 8 個byte的值(上圖最右側為十六進位值)依序為 0x04, 0x0A, 0x11, 0x11, 0x11, 0x1F, 0x11, 0x11。(一般正常人都會用一個Array裝起來...)但是~~準備好值之後,接下來要怎麼寫到 LCM 的記憶體裡面?

這裡說一下,LCM存放自訂字型資料的記憶體就是 Datasheet 裡面所說的 "CGRAM", 7 個字共 56 byte 的 CGRAM 位置範圍為第 0 個到第 55 個byte (0x00 ~ 0x37)。

所以寫第一個字就要從 CGRAM 位置 0x00 開始寫到 0x07 ,第二個字CGRAM位置:0x08~0x0F,第三個字 CGRAM 位置:0x10~0x17 ... 接下來依此類推

現在自訂字形的 "資料" 已經準備好了,那程式要怎麼寫才行?如果我要把這個辛苦點出來的"A"字放到 CGRAM 的第二個自訂字形位置(0x08~0x0F)。又要如何顯示這個自定字形到LCM螢幕上的指定位置去?

寫資料到指定記憶體位置依序要做兩個動作: 1.先指定記憶體位置。2.寫入資料。


1. 指定記憶体位置:

指定 CGRAM 位置為 0x08 的完整指令寫法為:

指定 CGRAM "指令" 是: 0x40 = (0b 01XX XXXX)。
    CGRAM "位置" 是: 0x08 = (0b 0000 1000)。

黃色部份是指令,而綠色的部份是 CGRAM 位置: 0x08。故可以得到完整的指令如下:

  (0b 0100 0000) 0x40 "指令"
+ (0b 0000 1000) 0x08 "位置"
--------------------------
  (0b 0100 1000) 0x48 "完整指令"

所以我們要先寫入
完整指令:"0x48",之後再寫入byte資料:

如果在開機的時候有做好設定的話,則每寫入一個byte之後,指定的記憶體位置會自動往前遞增一個。所以要連續寫入八個byte,就只需要在寫入第一個byte時指定一次起始位址就好,之後就只需要單純地連續做寫入的動作就好。

上述的一系列個動作,我們利用上一篇教學文已經寫好的函示 write_command() 和 write_data(),在下面呈現出程式碼實作:

myWord = [ 0b00000100, 0b00001010, 0b00010001, 0b00010001, 0b00010001, 0b00011111, 0b00010001, 0b00010001] command_set_CGRAM = 0x40 CGRAM_addr = 0x08 write_command(command_set_CGRAM + CGRAM_addr) write_data(myWord[0]) # <== 寫資料到CGRAM:0x00+0x08 write_data(myWord[1]) # <== 寫資料到CGRAM:0x01+0x08 write_data(myWord[2]) # <== 寫資料到CGRAM:0x02+0x08 write_data(myWord[3]) # <== 寫資料到CGRAM:0x03+0x08 write_data(myWord[4]) # <== 寫資料到CGRAM:0x04+0x08 write_data(myWord[5]) # <== 寫資料到CGRAM:0x05+0x08 write_data(myWord[6]) # <== 寫資料到CGRAM:0x06+0x08 write_data(myWord[7]) # <== 寫資料到CGRAM:0x07+0x08 # 第二個自訂字形寫入完畢 # 把第二個自定字顯示到LCM螢幕的第一行第一個字 write_command(0x80 + 0x00) # 移動到LCM顯示螢幕的第一行第一個字的位置 write_data(0x01) # 顯示第二個字定字


再接著給第二個實例,要自定一個如下不明意義的字符到CGRAM中的放置第三個自定字形的位置:

先把要的字符的數值給"點"出來,結果如下。

   bit : 7 6 5 4 3 2 1 0          binary       Hex

----------------------------------------------------
byte 0 :                 ==> 0 0 0 1 1 0 1 1 = 0x1B
byte 1 :                 ==> 0 0 0 1 1 0 1 1 = 0x1B
byte 2 :                 ==> 0 0 0 0 0 0 0 0 = 0x00
byte 3 :                 ==> 0 0 0 1 1 0 1 1 = 0x1B
byte 4 :                 ==> 0 0 0 1 1 0 1 1 = 0x1B
byte 5 :                 ==> 0 0 0 0 0 0 0 0 = 0x00
byte 6 :                 ==> 0 0 0 1 1 0 1 1 = 0x1B
byte 7 :                 ==> 0 0 0 1 1 0 1 1 = 0x1B

而CGRAM中第三個自訂字形位置範圍為 0x10 ~ 0x17 所以要先移動到 CGRAM 的 0x10 位置。然後連續寫入0x1B, 0x1B, 0x00, 0x1B, 0x1B, 0x00, 0x1B, 0x1B 八個點陣數值。



下面來看如何用Python實現這樣的事情: 


myWord = [ 0b00011011, 0b00011011, 0b00000000, 0b00011011, 0b00011011, 0b00000000, 0b00011011, 0b00011011] command_set_CGRAM = 0x40 CGRAM_addr = 0x10 write_command(command_set_CGRAM + CGRAM_addr) write_data(myWord[0]) # <== 寫資料到CGRAM:0x00+0x08 write_data(myWord[1]) # <== 寫資料到CGRAM:0x01+0x08 write_data(myWord[2]) # <== 寫資料到CGRAM:0x02+0x08 write_data(myWord[3]) # <== 寫資料到CGRAM:0x03+0x08 write_data(myWord[4]) # <== 寫資料到CGRAM:0x04+0x08 write_data(myWord[5]) # <== 寫資料到CGRAM:0x05+0x08 write_data(myWord[6]) # <== 寫資料到CGRAM:0x06+0x08 write_data(myWord[7]) # <== 寫資料到CGRAM:0x07+0x08 # 第二個自訂字形寫入完畢 # 把第二個自定字顯示到LCM螢幕的第一行第一個字 write_command(0x80 + 0x01) # 移動到LCM顯示螢幕的第一行第二個字的位置 write_data(0x02) # 顯示第三個自定字



--part1: 硬體設定篇
--part2: 基本指令介紹
--part3: 初始化動作
--part4: 自訂字形

下圖是從Datasheet抓下來的指令表。




下面是這個範例的完整程式碼,請注意PIN腳的設定是否正確。


#!/usr/bin/env python #-*- coding:utf-8 -*- import RPi.GPIO as GPIO from time import sleep RS = 20 RW = 21 EN = 26 D4 = 19 D5 = 13 D6 = 6 D7 = 5 def init(): GPIO.setmode(GPIO.BCM) GPIO.setup(EN, GPIO.OUT) GPIO.setup(RS, GPIO.OUT) GPIO.setup(RW, GPIO.OUT) GPIO.setup(D4, GPIO.OUT) GPIO.setup(D5, GPIO.OUT) GPIO.setup(D6, GPIO.OUT) GPIO.setup(D7, GPIO.OUT) GPIO.output(D4,0) GPIO.output(D5,0) GPIO.output(D6,0) GPIO.output(D7,0) GPIO.output(RS,0) GPIO.output(RW,0) GPIO.output(EN,0) sleep(0.1) GPIO.output(EN,1) sleep(0.000001) GPIO.output(EN,0) sleep(0.002) GPIO.output(D5,1) GPIO.output(EN,1) sleep(0.000001) GPIO.output(EN,0) sleep(0.005) GPIO.output(EN,1) sleep(0.000001) GPIO.output(EN,0) sleep(0.0002) GPIO.output(EN,1) sleep(0.000001) GPIO.output(EN,0) sleep(0.0002) write_command(0x28) sleep(0.0001) write_command(0x0c) sleep(0.0001) write_command(0x01) sleep(0.002) def write_command(cmd): GPIO.output(EN,0) GPIO.output(RW,0) GPIO.output(RS,0) GPIO.output(D7, 1 if (0x80 & cmd) else 0) GPIO.output(D6, 1 if (0x40 & cmd) else 0) GPIO.output(D5, 1 if (0x20 & cmd) else 0) GPIO.output(D4, 1 if (0x10 & cmd) else 0) GPIO.output(EN,1) sleep(0.000001) GPIO.output(EN,0) sleep(0.000001) GPIO.output(D7, 1 if (0x08 & cmd) else 0) GPIO.output(D6, 1 if (0x04 & cmd) else 0) GPIO.output(D5, 1 if (0x02 & cmd) else 0) GPIO.output(D4, 1 if (0x01 & cmd) else 0) GPIO.output(EN,1) sleep(0.000001) GPIO.output(EN,0) sleep(0.00005) def write_data(data): GPIO.output(EN,0) GPIO.output(RW,0) GPIO.output(RS,1) GPIO.output(D7, 1 if (0x80 & data) else 0) GPIO.output(D6, 1 if (0x40 & data) else 0) GPIO.output(D5, 1 if (0x20 & data) else 0) GPIO.output(D4, 1 if (0x10 & data) else 0) GPIO.output(EN,1) sleep(0.000001) GPIO.output(EN,0) sleep(0.000001) GPIO.output(D7, 1 if (0x08 & data) else 0) GPIO.output(D6, 1 if (0x04 & data) else 0) GPIO.output(D5, 1 if (0x02 & data) else 0) GPIO.output(D4, 1 if (0x01 & data) else 0) GPIO.output(EN,1) sleep(0.000001) GPIO.output(EN,0) sleep(0.00005) if __name__=="__main__": init() myWord = [ 0b00000100, 0b00001010, 0b00010001, 0b00010001, 0b00010001, 0b00011111, 0b00010001, 0b00010001] command_set_CGRAM = 0x40 CGRAM_addr = 0x08 write_command(command_set_CGRAM + CGRAM_addr) write_data(myWord[0]) # <== 寫資料到CGRAM:0x00+0x08 write_data(myWord[1]) # <== 寫資料到CGRAM:0x01+0x08 write_data(myWord[2]) # <== 寫資料到CGRAM:0x02+0x08 write_data(myWord[3]) # <== 寫資料到CGRAM:0x03+0x08 write_data(myWord[4]) # <== 寫資料到CGRAM:0x04+0x08 write_data(myWord[5]) # <== 寫資料到CGRAM:0x05+0x08 write_data(myWord[6]) # <== 寫資料到CGRAM:0x06+0x08 write_data(myWord[7]) # <== 寫資料到CGRAM:0x07+0x08 # 第二個自訂字形寫入完畢 # 把第二個自定字顯示到LCM螢幕的第一行第一個字 write_command(0x80 + 0x00) # 移動到LCM顯示螢幕的第一行第一個字的位置 write_data(0x01) # 顯示第二個字定字 myWord = [ 0b00011011, 0b00011011, 0b00000000, 0b00011011, 0b00011011, 0b00000000, 0b00011011, 0b00011011] command_set_CGRAM = 0x40 CGRAM_addr = 0x10 write_command(command_set_CGRAM + CGRAM_addr) write_data(myWord[0]) # <== 寫資料到CGRAM:0x00+0x08 write_data(myWord[1]) # <== 寫資料到CGRAM:0x01+0x08 write_data(myWord[2]) # <== 寫資料到CGRAM:0x02+0x08 write_data(myWord[3]) # <== 寫資料到CGRAM:0x03+0x08 write_data(myWord[4]) # <== 寫資料到CGRAM:0x04+0x08 write_data(myWord[5]) # <== 寫資料到CGRAM:0x05+0x08 write_data(myWord[6]) # <== 寫資料到CGRAM:0x06+0x08 write_data(myWord[7]) # <== 寫資料到CGRAM:0x07+0x08 # 第二個自訂字形寫入完畢 # 把第二個自定字顯示到LCM螢幕的第一行第一個字 write_command(0x80 + 0x01) # 移動到LCM顯示螢幕的第一行第二個字的位置 write_data(0x02) # 顯示第三個自定字




Python 控制 LCM模組的Code 可在 Github 下載。
https://github.com/itrobotics/python_LCM_control



沒有留言 :

張貼留言