天天看點

一起玩轉樹莓派(11)——使用LCD屏一起玩轉樹莓派(11)——使用LCD屏

一起玩轉樹莓派(11)——使用LCD屏

通過本系列部落格前幾篇文章的介紹,我們已經體驗過了許多傳感器元件,它們大多非常簡單,可以直接對其進行資料讀取或寫入,無需複雜的指令配置。本篇部落格,我們将介紹一個相對複雜的元件:LCD屏。當今大多數常見的電子裝置為了便于使用者操作,都會配備一塊LCD液晶顯示屏,使用者通過螢幕可以擷取到裝置的相關資訊友善使用。下面,我們将嘗試使用樹莓派來在LCD屏上展示資訊。

一、LCD 1602

LCD屏是Liquid Crystal Display的簡稱,即液晶顯示屏。LCD 1602是一種點陣式的給付型液晶顯示屏,其型号為1602本身也是有意義的,表示其可以顯示2行資訊,每行可以顯示16個字元。LCD1602最多可以顯示32個字元,價格上也并不昂貴,十幾元錢即可買到。LCD1602有16個引腳,如下圖所示:

一起玩轉樹莓派(11)——使用LCD屏一起玩轉樹莓派(11)——使用LCD屏

LCD1602的16個引腳看上去很多,但實際上使用起來并不複雜,我們首先将上圖這些引腳的功能來介紹一下。

  • 引腳1:接地引腳
  • 引腳2:接5V電壓
  • 引腳3:VE引腳為螢幕對比度調整引腳,接地時對比度最大,接正極電源時對比度最小。
  • 引腳4:RS引腳為功能模式引腳(也被稱為寄存器選擇引腳),為其加高電平時為資料模式(存取螢幕展示的資料),為其加低電平時為指令模式(讀取指令)。
  • 引腳5:RW引腳為讀寫模式引腳,為其加高電平時為讀操作,為其加低電平時為寫操作。
  • 引腳6:Enable引腳,此引腳用來觸發動作,負跳變時進行資料處理或指令的執行。
  • 引腳7-引腳14:這8個引腳為資料引腳,用來進行資料傳輸。
  • 引腳15:背光電源引腳。
  • 引腳16:背光接地引腳。

上面所介紹的引腳中,引腳1,引腳2,引腳15和引腳16比較好了解,其都是作為供電功能,無需程式設計操作。引腳3是一個單獨調增對比度功能的引腳,作用也相對獨立,無需複雜的指令操作。引腳4和引腳5是比較核心的兩個引腳,這兩個引腳的高低電平狀态組合共有4種,分别會将LCD1602設定為讀指令模式,寫指令模式,讀資料模式和寫資料模式。引腳6可以了解為一個觸發引腳,通過操作這個引腳的電平跳變來讓LCD1602執行具體的功能。引腳7-引腳14用來進行資料的存取或指令的存取。

現在請你務必将上面所介紹的内容完全了解,否則後面的内容可能會更加令你迷惑。對于LCD1206的讀資料模式和寫資料模式你應該沒有什麼疑惑,隻要通過引腳4和引腳5設定正确的模式後,再通過GPIO來寫和讀引腳7到引腳14的電平資料,即可得到一個8位的資料。我們核心需要了解的是指令模式,LCD1602的指令集如下:

一起玩轉樹莓派(11)——使用LCD屏一起玩轉樹莓派(11)——使用LCD屏

上圖中的RS和R/W就是引腳4和引腳5,其控制模式,與指令本身無關,我們可以先不關心。從DB7到DB0是真正的指令部分。我們下面來逐一介紹。

1. 指令一:0000 0001

清屏指令,響應時間為1.53ms。

2.指令二:0000 001- (最後一位’-‘表示0和1都可以,不被關心)

光标歸位指令,執行後光标的位置會回到起點,但是資料寄存器中的資料不會清空。

3.指令三:0000 01[I/D][SH]

光标移動模式設定指令,I/D和SH兩個控制為光标或螢幕移動模式。

I/D設定為0:每次讀取一個字元後光标左移。

I/D設定為1:每次讀取一個字元後光标右移。

SH設定為0:螢幕不移動。

SH設定為1:螢幕移動,方向與I/D的設定一緻。

4.指令四:0000 1[D][C][B]

顯示模式設定指令,D,C,B這三個位分别設定主顯示功能,光标顯示功能,光标閃爍功能。

D:設定為0則關閉螢幕,設定為1開啟螢幕。

C:設定為0關閉光标,設定為1顯示光标。

B:設定為0光标不閃爍,設定為1光标閃爍。

5.指令五:0001  [S/C][R/L]--

設定光标和顯示屏移動方向。

S/C設定為0時,R/L設定為0則光标左移,RL設定為1時光标右移。

S/C設定為1時,R/L設定為0則顯示内容左移,R/L設定為1則顯示内容右移。

6.指令六:001[DL] [N][F]--

功能模式設定指令。

DL:設定為1時采用8位總線讀資料,設定為0時采用4位總線讀資料。

N:設定為0時是單行顯示模式,設定為1時是雙行顯示模式。

F:設定為0時為5*8的點陣字元,設定為1時為5*11的點陣字元。

7.指令七:01[A5][A4] [A3][A2][A1][A0]

設定下一個字元要顯示的位置。A5位設定要定位到的行,A0到A4位定位要顯示的位置,取值為0-16之間。

8.指令八:1[A6][A5][A4] [A3][A2][A1][A0]

資料寄存器位址設定。

了解了LD1602顯示屏上面的指令用法,我們就可以程式設計來控制顯示屏顯示的内容了。

二、帶I2C子產品的LCD 1602

前面我們說過,LCD1602有16個引腳。原則上我們已經可以使用樹莓派來控制顯示屏的顯示了,但是16個引腳全部連接配接到樹莓派會使接線十分的複雜,而且程式代碼的編寫也非常繁瑣,要對太多的GPIO引腳進行操作,十分不便。本次實驗,我們采用的是帶I2C子產品的LCD元件,I2C子產品本身将一些獨立的功能進行了封裝,通過I2C子產品,我們可以以8位資料為标準來傳輸任何我們想要執行的指令或讓LCD顯示的字元,非常友善。

一起玩轉樹莓派(11)——使用LCD屏一起玩轉樹莓派(11)——使用LCD屏

如上圖所示,有了IC2子產品的LCD1602元件,隻需要4個引腳即可實作顯示功能。下面我們來分析下如何使用此I2C子產品。

首先關于I2C通信的相關内容,之前部落格已經有詳細的介紹,這裡不再贅述。我們先來介紹下為何通過4個引腳通過I2C總線傳輸8位的資料集合實作所有功能。

LCD引腳1與引腳2:用I2C子產品的電源引腳和接地引腳代替。

LCD引腳15和引腳16:LCD的這兩個引腳功能為控制背光,此邏輯被封裝進了I2C子產品中,I2C子產品每次寫入的8位資料中的第4位用來控制背光。

LCD的引腳3:LCD的此引腳用來設定顯示的對比度,在I2C子產品中,通過一個可調節的電阻來實作此功能,在後面的實驗中如果發現螢幕顯示不清,可以嘗試調節此電阻器。

LCD的引腳4,引腳5和引腳6:這幾個功能引腳也被封裝進了I2C子產品中,I2C子產品每次寫入的8位資料中的第1位,的2位和第3位分别用來控制這些引腳。

LCD剩下的資料引腳的資料由I2C傳輸的8位資料中的高4位來對應,在LCD的8位資料模式下,I2C分兩次傳輸一次完整的資料,前傳輸的4位為LCD所需資料的低4位,後傳輸的資料為LCD所需資料的高4位。在LCD的4位資料模式下,因為LCD需要擷取到完整的8位資料,是以也需要通過兩次資料傳輸,隻是此時先傳輸的資料為LCD所需資料的高4位,後傳輸的資料為LCD所需資料的低4位,這點需要特别注意。

下面總結了I2C在傳輸資料時每一位的意義:

第8位 第7位 第6位 第5位 第4位 第3位 第2位 第1位
資料/指令 背光控制位 Enable控制位 RW控制位 RS控制位

 三、編碼實驗

    使用I2C子產品封裝的LCD1602隻有4個引腳,接線非常簡單,如下:

LCD 樹莓派
GND
VCC +5V
SDA 樹莓派SDA功能引腳
SCL 樹莓派SCL功能引腳

将LCD1602與樹莓派連接配接完成後,在樹莓派的終端執行如下指令檢視I2C裝置:

sudo i2cdetect -y 1           

輸出入下圖所示:

一起玩轉樹莓派(11)——使用LCD屏一起玩轉樹莓派(11)——使用LCD屏

可以看到,目前我們隻連接配接了一個I2C裝置,裝置号為27。

    由于背光的控制位相對獨立,我們封裝單獨的函數來處理,如下:

# 是否開啟背光 由PCF8574T的低4位中的第4位決定
BLEN = 1 
# 補充背光控制位
def addBlenControl(data):
    global BLEN
    tmpData = data
    if BLEN:
        # 将第4位背光控制位強制設定1
        tmpData = data | 0b00001000
    else:
        # 将第4位背光控制位強制設定為0
        tmpData = data & 0b11110111
    return tmpData               

同理,可以将Enable位的控制,RS位的控制都封裝成函數:

# 補充Enable控制位
def addEnableControl(data, high):
    tempData = data
    # 第3位控制Enable
    if high:
        tempData |= 0b00000100
    else:
        tempData &= 0b11111011
    return tempData

# 補充RS控制位
def addRSControl(data, high):
    tempData = data
    # 第1位控制RS
    if high:
        tempData |= 0b00000001
    else:
        tempData &= 0b11111110
    return tempData           

在向I2C發送資料前,根據配置的背光設定來決定背光控制位的值:

# 通過I2C總線寫入資料
def writeI2C(addr, data):
    # 添加背光控制
    temp = addBlenControl(data)
    # 寫資料到I2C總線
    BUS.write_byte(addr ,temp)           

準備好了這些工具函數,我們隻需要根據LCD1602的指令手冊來設定具體的功能,發送要展示的資料即可,完整的代碼如下:

#coding:utf-8
import time
import smbus
BUS = smbus.SMBus(1)
# LCD螢幕的硬體位址
LCD_ADDR = 0x27
# 是否開啟背光 由PCF8574T的低4位中的第4位決定
BLEN = 1 

# 補充背光控制位
def addBlenControl(data):
    global BLEN
    tmpData = data
    if BLEN:
        # 将第4位背光控制位強制設定1
        tmpData = data | 0b00001000
    else:
        # 将第4位背光控制位強制設定為0
        tmpData = data & 0b11110111
    return tmpData    

# 補充Enable控制位
def addEnableControl(data, high):
    tempData = data
    # 第3位控制Enable
    if high:
        tempData |= 0b00000100
    else:
        tempData &= 0b11111011
    return tempData

# 補充RS控制位
def addRSControl(data, high):
    tempData = data
    # 第1位控制RS
    if high:
        tempData |= 0b00000001
    else:
        tempData &= 0b11111110
    return tempData

# 通過I2C總線寫入資料
def writeI2C(addr, data):
    # 添加背光控制
    temp = addBlenControl(data)
    # 寫資料到I2C總線
    BUS.write_byte(addr ,temp)

# 發送指令到LCD1602
def sendCommand(comm):
    # comm高4位資料傳輸
    # 低4位先清空
    buf = comm & 0b11110000
    # 先将Enable置為高電平
    buf = addEnableControl(buf, 1)
    # 設定為指令模式
    buf = addRSControl(buf, 0)
    # 寫入指令
    writeI2C(LCD_ADDR ,buf)
    time.sleep(0.002)
    # 将Enable置為低電平 使産生低電平跳變來執行指令
    buf = addEnableControl(buf, 0)          
    writeI2C(LCD_ADDR ,buf)
     
    # comm低4位資料傳輸
    # 高4位先清空 并将低4位的資料移動到高4位
    buf = (comm & 0b00001111) << 4
    # 當次指令的低4位用來 做enable re rew的控制
    # 先将Enable置為高電平
    buf = addEnableControl(buf, 1)
    writeI2C(LCD_ADDR ,buf)
    time.sleep(0.002)
    # 将Enable置為低電平 使産生低電平跳變來執行指令
    buf = addEnableControl(buf, 0)     
    writeI2C(LCD_ADDR ,buf)

# 發送資料到LCD 
def sendData(data):
    # data高4位資料傳輸
    # 低4位先清空
    buf = data & 0b11110000
    # 先将Enable置為高電平
    buf = addEnableControl(buf, 1)
    # 設定為資料模式
    buf = addRSControl(buf, 1)   
    writeI2C(LCD_ADDR ,buf)
    time.sleep(0.002)
    # 将Enable置為低電平 使産生低電平跳變來執行指令
    buf = addEnableControl(buf, 0)  
    writeI2C(LCD_ADDR ,buf)
     
    # data低4位資料傳輸
    buf = (data & 0b00001111) << 4
    # 先将Enable置為高電平
    buf = addEnableControl(buf, 1)
    # 設定為資料模式
    buf = addRSControl(buf, 1) 
    writeI2C(LCD_ADDR ,buf)
    time.sleep(0.002)
    # 将Enable置為低電平 使産生低電平跳變來執行指令
    buf = addEnableControl(buf, 0)  
    writeI2C(LCD_ADDR ,buf)

# 初始化方法
def initLCD():
    # 啟動時,LCD1602為8位模式 I2C傳輸資料時先傳輸的為低位資料
    # 是以實際上的指令為 0b00100011
    # 為指令6 将LCD1602設定為4位總線模式
    sendCommand(0b00110010) 
    time.sleep(0.005)

    # 之後的指令都是4位總線模式
    sendCommand(0b00110010) 
    time.sleep(0.005)
    # 指令4 設定螢幕開啟,無光标,無閃爍
    sendCommand(0b00001100)
    time.sleep(0.005)
    # 指令1 清屏
    sendCommand(0b00000001) 

# 設定螢幕要展示的文案 x,y确定位置
def printLCD(x, y, str):
    # 2行 16 列
    if x < 0:
        x = 0
    if x > 15:
        x = 15
    if y <0:
        y = 0
    if y > 1:
        y = 1
    # 指令7 設定資料要展示的位置
    addr = 0b10000000 + 0b00000100 * y + x
    sendCommand(addr)
    # 開始發送字元資料到LCD1602的資料寄存器
    for chr in str:
        # ord函數可以擷取字元的ascil
        sendData(ord(chr))
 
# 主程式
initLCD()
printLCD(0, 0, 'Hello, world!')
time.sleep(2)
sendCommand(0b00000001)
time.sleep(0.002)
printLCD(0, 0, 'Love China!')
time.sleep(2)
sendCommand(0b00000001)
time.sleep(0.002)
printLCD(0, 0, 'Great Raspberry!')           

如上代碼所示,所有的指令都采用的二進制的方式,便于對比指令手冊進行了解。更多時候我們會采用十六進制數字來編寫指令,這樣會使代碼看的幹淨很多。上面的示例代碼是一個簡單的應用程式,運作後可以直接在LCD螢幕上展示3句話:

Hello World!
Love China!
Great Raspberry!           

其實此程式也是一個完整的LCD1602驅動,初始化完成後,我們可以通過其提供額sendData方法來實作各種各樣的顯示需求。效果如下圖所示:

一起玩轉樹莓派(11)——使用LCD屏一起玩轉樹莓派(11)——使用LCD屏
專注技術,懂的熱愛,願意分享,做個朋友