星期六, 10月 17, 2015

自製 I²C 介面的數字鍵盤

自製 I²C 週邊的想法
早在我將舊型電話按鍵盤改做數字鍵盤之前,就覺得直接驅動 3x4 數字鍵盤至少需要用到 7 條 GPIO 接線,這樣太過佔用 GPIO 腳位了,便有了該將之改造成 I²C (Inter-Integrated Circuit) 介面的想法。



I²C 是一種串列通訊匯流排 (bus),使用兩條信號接線-串列資料 (SDA) 與串列時脈 (SCL),便可讓一個主裝置與多個從屬裝置進行雙向通信,週邊更可以在系統仍然在運作的同時加入或移出匯流排。現在的嵌入式系統和微控制器普遍都提供了這個通信介面,它常被應用在簡單且其製造成本較傳輸速度更為要求的週邊裝置上。

想要讓我的 3x4 數字鍵盤能接上 I²C bus,還需要一個可以接受/回應串列通信協定並轉換成並列 GPIO 輸入/輸出信號的電路元件。

尋找到的 MCP23008 是一個具有將串列介面擴充成 8 位元輸入/輸出的 IC,查看了它的 data sheet,直覺應該可以達成目的,便先買了兩顆來試試。


在 Raspberry Pi 的測試
若不能確認 Raspbian 是否已啟用 I²C,可參考 Using the I2C Interface 的說明,該文也列出了稍後即將使用到的 SMBus Functions。
再來要在麵包板上接線-將 Raspberry Pi 與 MCP23008 的 I²C 信號腳位連接,MCP23008 的 GPIO 與數字鍵盤的接腳連接,如以下所示。
RPi      MCP23008     3x4 Keypad
---      --------     ----------
SCL <--->  SCL
SDA <--->  SDA
3V3 <--->  VDD
GND <--->  VSS
           GP0  ---->  C1
           GP1  ---->  C2
           GP2  ---->  C3
           GP3  <----  R1
           GP4  <----  R2
           GP5  <----  R3
           GP6  <----  R4

MCP23008 有三個接腳 (A0, A1, A2) 是用來設定從屬裝置在 I²C bus 上的位址 (address),將其皆接在 GND 可得位址為 0x20,事後可用 i2cdetect 指令確認位址是否如預期。另外還有 i2cgeti2cset 兩個指令可用來對從屬裝置做讀取與寫入。
pi@raspberrypi ~ $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --                         

MCP23008 內部有 11 個暫存器做設定與控制用的,以下稱 CCR (Configuration and Control Registers),程式將對這些 CCR 進行讀取或寫入以達成與所連接的裝置通信或控制的目的。

我將這些基本功能編寫成一個 MCP23008 類別以方便讀取或寫入 CCR,基於此再編寫了一個 MyNumPad 類別,使應用程式輕易讀取數字鍵盤的輸入。程式範例如下:
#!/usr/bin/python
from mcp23008 import MCP23008
import time

class MyNumPad:
    keys= [['1','2','3'],['4','5','6'],['7','8','9'],['*','0','#']]
    def __init__(self, address):
        self.mcp= MCP23008(address)
    def read(self):
        self.mcp.writeByte('IODIR', 0b11111000)
        self.mcp.writeByte('GPIO', 0)
        self.mcp.writeByte('GPPU', 0b11111111)
        for i in range(3):
            self.mcp.writeByte('GPIO', (1 << i) ^ 0b11111111)
            time.sleep(0.05)
            v= (self.mcp.readByte('GPIO') | 0b00000111) ^ 0b11111111
            for j in range(4):
                if (v & (0b00001000 << j)) != 0:
                    time.sleep(0.1)
                    return self.keys[j][i]
        return 0
    
# test read input from MyNumPad
try:
    npad= MyNumPad(0x20)
    while True:
        ch= npad.read()
        if ch != 0:
            print ch
        
except KeyboardInterrupt:
    pass


使用 MCP23008 的小提示
各 GPIO 接腳僅可做內部 PULL-UP 的控制,不像直接使用 Raspberry Pi 的 GPIO 可以控制做 PULL-UP 或 PULL-DOWN,所以程式在掃瞄鍵盤按下時是要判斷電位為 LOW。
另外,RESET 與三個位址接腳不要空接,否則 MC23008 可能會處在不穩定的狀態。

下次,來試試接 16x2 LCD 顯示器模組。

沒有留言:

張貼留言