星期五, 6月 10, 2016

使用 SPI 介面的 LCD12864

先前曾用 LCD1602 當做自製操作面板的顯示元件,不過那只能顯示英文數字,這次因為有顯示中文字元的需要,所以尋找了這款 LCD12864 不但內建了 8192 個繁體中文字形 (Big5) 還可讓我們選擇以串列週邊介面 (Serial Peripheral Interface, SPI) 輸出指令及資料,如此省去我自製 I²C 介面或 SPI 的功夫了。



什麼是 SPI?簡言之,也是一種串列傳輸資料的介面,和 I²C 介面有些類似但需要三線信號 (SCLK, MOSI, MISO) 做主從裝置間的雙向傳輸,一組匯流排上只允許一個主裝置 (Master),可有多個從屬裝置 (Slave) 但需要再各有一線選擇信號 (CS),使用上較簡易。欲了解較詳盡的可看這篇介紹 Serial Peripheral Interface (SPI) 的內文。另外,要啟用 Raspberry Pi 的 SPI 介面之前也可參考這篇範例以確認 SPI 相關的模組是否已安裝載入。

LCD12864 在市面上有多種版本,20 線腳位定義偶有不同,我用的這版若選擇使用 SPI 則腳位定義如下:
  pin#  description
  ====  ========================================
    1   GND   - 電源輸入, 0V
    2   VCC   - 電源輸入, 5V
    3   V0    - LCD偏壓輸入, 控制顯示對比
    4   CS    - 晶片選擇輸入, 1:enable 0:disable
    5   SID   - 串列資料輸入
    6   SCLK  - 串列時脈輸入
   15   PSB   - 控制介面選擇輸入, 0:串列 1:並列
   17   /RST  - 重設輸入, 低態動作
   18   VOUT  - LCD倍壓輸出
   19   LED_A - 背光LED電源正極
   20   LED_K - 背光LED電源負極

與 Raspberry Pi 之間的 SPI 接線:
  Raspberry Pi  LCD12864
  ============  ========
   #19 MOSI   -- #5 SID
   #23 SCLK   -- #6 SCLK
   #24 CE0    -- #4 CS

參考 LCD12864 的串列模式時序圖,每次傳送封包的第一個 byte 的前 5 個位元必須固定為 1 (High),接下來兩個位元是 RW 與 RS,而最末位元固定為 0 (Low)。要傳送的指令或資料緊接在其後,每一個 byte 將被當做 4 位元模式分拆組成 2 個 byte。

Python 程式使用 SPI 要借重 spidev 模組,撰寫測試程式如下:
import spidev
import time

class LCD12864B:
    def __init__(self, port, cs):
        self.spi = spidev.SpiDev()
        self.spi.open(port, cs)
        self.spi.cshigh = True
        
    def writeCommand(self, cmd):
        self.spi.xfer2([0xf8, cmd & 0xf0, (cmd << 4) & 0xf0])
        time.sleep(0.001)
        
    def writeByte(self, byte):
        self.spi.xfer2([0xfa, byte & 0xf0, (byte << 4) & 0xf0])
        
    def writeData(self, data):
        bytes = [0xfa]
        for c in data:
            b = c.encode('big5')
            if len(b) == 1:
                bytes.append(ord(c) & 0xf0)
                bytes.append((ord(c) << 4) & 0xf0)
            elif len(b) == 2:
                bytes.append(ord(b[0]) & 0xf0)
                bytes.append((ord(b[0]) << 4) & 0xf0)
                bytes.append(ord(b[1]) & 0xf0)
                bytes.append((ord(b[1]) << 4) & 0xf0)
        self.spi.xfer2(bytes)
        time.sleep(0.001)
        
    def reset(self):
        self.writeCommand(0x30)
        self.writeCommand(0x0c)
        self.writeCommand(0x01)
        self.writeCommand(0x06)
        
    def close(self):
        self.spi.close()
        
    def position(self, row, col):
        addr = {
             0: 0x80,
             1: 0x90,
             2: 0x88,
             3: 0x98,
        }
        ac = addr.get(row) + col
        self.writeCommand(ac)
        
    def display(self, row, col, str):
        self.position(row, col)
        self.writeData(str)


lcd = LCD12864B(0, 0)
lcd.reset()
lcd.display(0, 0, u"三山半落青天外")
lcd.display(1, 0, u'二水中分白鷺洲')
lcd.display(2, 0, u"總為浮雲能蔽日")
lcd.display(3, 0, u"長安不見使人愁")
lcd.close()

這次僅簡單的做了顯示中文字型的測試,LCD12864 也能有做圖像顯示的能力,以後有機會再試試。

6 則留言:

  1. hi;
    打擾;
    能否跟您請教這款 LCD 在哪可以買到?
    感謝

    回覆刪除
    回覆
    1. 在露天拍賣上可以找到有幾個賣家有賣,我買過的是 http://goods.ruten.com.tw/item/show?21537639838971
      供您參考。

      刪除
  2. 請問能否協助提供 C 程式? 因我不熟悉Python, 感謝

    回覆刪除
    回覆
    1. 抱歉,本文的實驗程式範例僅有python。

      刪除
  3. 我使用了您的python code, 但LCD沒有顯示任何字, 我多加了 PSB pin = low, 不知道有沒有建議?

    回覆刪除
    回覆
    1. 使用SPI介面需將PSB接地(low)沒錯。
      看不到輸出的情況,通常我會再考慮檢視所有信號與電源(包含背光)的接線。
      通電後程式執行前會用一個RC電路先做RST重設,另外顯示對比V0會用一個電位器做調節。
      以上提供您參考,希望有幫助。

      刪除