prettyprint

2022年10月1日 星期六

Raspberry Pi Pico PIO(Programmable IO) Episode 1: 4x4 matrix keypad

一、Raspberry PIO簡述: 

Raspberry Pi pico比較獨特的是具有PIO(programmable IO),相當於具有8小型的專門處理IO的處理器,分為兩個PIO區塊,每個區塊有4個StateMachine,其架構如下圖所示:

(PIO block, 取自RP2040 datasheet)
  1. 每個PIO block的4個StateMachine共用32 Instructions 的Instruction Memory,雖然小但也夠用。
  2. 每個StateMachine有4個32 bits output FIFO與4個32 bits input FIFO,有可結合成單一方向的8個32 bits FIFO。
  3. 有兩個通用32 bits register: X and Y
  4. ISR 與OSR: input shift register and output shift register
  5. Clock Div: 可除頻1~65536。

(StateMachine, 取自RP2040 datasheet)
指令集:
(取自RP2040 datasheet)


二、4x4 Keypad:


腳位從左到右為pin1~pin8,pin1~4同一時間只能有一個腳位設為High,當pin1為high時,按下key 1,則pin5輸出為hight。

三、實做Raspberry Pi Pico PIO:

使用MicroPython程式語言來實作。
4x4 keypad與raspberry pi pico腳位接線如上圖所示。

pin 1~4同時間只能有一個腳位為高電位,使用PIO1 block的StateMachine(4) set(pins,4)指令達成需求,程式碼如下:

@rp2.asm_pio(set_init=(rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW))
def scanRowHigh():
    wrap_target()
    set(pins, 1) [3] # each pin is high for at least 8 cycles (1+3+1+1+1+1)
    wait(0,pin,0)
    wait(0,pin,1)
    wait(0,pin,2)
    wait(0,pin,3) 
    set(pins, 2) [3] 
    wait(0,pin,0)
    wait(0,pin,1)
    wait(0,pin,2)
    wait(0,pin,3) 
    set(pins, 4) [3] 
    wait(0,pin,0)
    wait(0,pin,1)
    wait(0,pin,2)
    wait(0,pin,3) 
    set(pins, 8) [3]
    wait(0,pin,0)
    wait(0,pin,1)
    wait(0,pin,2)
    wait(0,pin,3) 
    wrap()

smr = rp2.StateMachine(4, scanRowHigh, freq=2000, set_base=Pin(2), in_base=col1pin)
smr.active(1)

第一行(1,4,7,*)鍵按下時,則pin5為高電位,使用PIO0 StateMachine(0) 的指令jmp(pin, "which_row")來判斷是否有按鍵按下。若有按鍵按下則使用in_(pins,4)來讀取GPIO2~5是那一個pin是高電位,則可判斷是那一個按鍵按下。接下來觸發irq,使用指令irq(rel(0))輸出按鍵內容。最後等待按鍵釋放(wait(0, pin, 0)。第二行按鍵使用PIO0 StateMachine(1) ,第三行按鍵使用PIO0 StateMachine(2),第四行按鍵使用PIO0 StateMachine(3)。程式碼如下所示。

@rp2.asm_pio()
def keypad():
    wrap_target()
    label("start")
    jmp(pin, "which_row") 
    jmp("start")
    label("which_row")
    in_(pins,4)
    push() 
    irq(rel(0)) [31] #wait for button bounce(at least 30ms: (32+32)*0.5ms)
    label("delay")
    nop() [31]
    jmp(pin, "delay") # wait for key to be released
    wrap()
    
def getkeyvalue(sm):
    row=-1
    if sm == rp2.StateMachine(0):
        row=0
    elif sm == rp2.StateMachine(1):
        row=1
    elif sm == rp2.StateMachine(2):
        row=2
    elif sm == rp2.StateMachine(3):
        row=3
    
    while sm.rx_fifo() :
        term = sm.get()
        for i in range(4):
            if term >> i == 1:
                print(keyvalues[row][i])
                break;

keyvalues=[["1","4","7","*"],
           ["2","5","8","0"],
           ["3","6","9","#"],
           ["A","B","C","D"]]

col1pin=Pin(6, Pin.IN, Pin.PULL_DOWN)
col2pin=Pin(7, Pin.IN, Pin.PULL_DOWN)
col3pin=Pin(8, Pin.IN, Pin.PULL_DOWN)
col4pin=Pin(9, Pin.IN, Pin.PULL_DOWN)

smc1=rp2.StateMachine(0, keypad, freq=2000,  in_base=Pin(2),jmp_pin=col1pin)
smc2=rp2.StateMachine(1, keypad, freq=2000,  in_base=Pin(2),jmp_pin=col2pin)
smc3=rp2.StateMachine(2, keypad, freq=2000,  in_base=Pin(2),jmp_pin=col3pin)
smc4=rp2.StateMachine(3, keypad, freq=2000,  in_base=Pin(2),jmp_pin=col4pin)

smc1.irq(getkeyvalue)
smc2.irq(getkeyvalue)
smc3.irq(getkeyvalue)
smc4.irq(getkeyvalue)
smc1.active(1)
smc2.active(1)
smc3.active(1)
smc4.active(1)

四、成果影片:


五、完整程式碼:

第二個程式碼為修正第二版,程式碼更簡潔。
👉第一版
from machine import Pin
import rp2

@rp2.asm_pio(set_init=(rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW,rp2.PIO.OUT_LOW))
def scanRowHigh():
    wrap_target()
    set(pins, 1) [3] 
    wait(0,pin,0)
    wait(0,pin,1)
    wait(0,pin,2)
    wait(0,pin,3) 
    set(pins, 2) [3] 
    wait(0,pin,0)
    wait(0,pin,1)
    wait(0,pin,2)
    wait(0,pin,3) 
    set(pins, 4) [3] 
    wait(0,pin,0)
    wait(0,pin,1)
    wait(0,pin,2)
    wait(0,pin,3) 
    set(pins, 8) [3]
    wait(0,pin,0)
    wait(0,pin,1)
    wait(0,pin,2)
    wait(0,pin,3) 
    wrap()
    
@rp2.asm_pio()
def keypad():
    wrap_target()
    label("start")
    jmp(pin, "which_row") 
    jmp("start")
    label("which_row")
    in_(pins,4)
    push() 
    irq(rel(0)) [31] 
    label("delay")
    nop() [31]
    jmp(pin, "delay") # wait for key to be released
    wrap()
    
def getkeyvalue(sm):
    row=-1
    if sm == rp2.StateMachine(0):
        row=0
    elif sm == rp2.StateMachine(1):
        row=1
    elif sm == rp2.StateMachine(2):
        row=2
    elif sm == rp2.StateMachine(3):
        row=3
    
    while sm.rx_fifo() :
        term = sm.get()
        for i in range(4):
            if term >> i == 1:
                print(keyvalues[row][i])
                break;

keyvalues=[["1","4","7","*"],
           ["2","5","8","0"],
           ["3","6","9","#"],
           ["A","B","C","D"]]

col1pin=Pin(6, Pin.IN, Pin.PULL_DOWN)
col2pin=Pin(7, Pin.IN, Pin.PULL_DOWN)
col3pin=Pin(8, Pin.IN, Pin.PULL_DOWN)
col4pin=Pin(9, Pin.IN, Pin.PULL_DOWN)

smc1=rp2.StateMachine(0, keypad, freq=2000,  in_base=Pin(2),jmp_pin=col1pin)
smc2=rp2.StateMachine(1, keypad, freq=2000,  in_base=Pin(2),jmp_pin=col2pin)
smc3=rp2.StateMachine(2, keypad, freq=2000,  in_base=Pin(2),jmp_pin=col3pin)
smc4=rp2.StateMachine(3, keypad, freq=2000,  in_base=Pin(2),jmp_pin=col4pin)

smr = rp2.StateMachine(4, scanRowHigh, freq=2000, set_base=Pin(2), in_base=col1pin)
smr.active(1)

smc1.irq(getkeyvalue)
smc2.irq(getkeyvalue)
smc3.irq(getkeyvalue)
smc4.irq(getkeyvalue)
smc1.active(1)
smc2.active(1)
smc3.active(1)
smc4.active(1)
👉第二版
from machine import Pin
import rp2

@rp2.asm_pio(set_init=(rp2.PIO.OUT_LOW,)*4)
def keypad():
    wrap_target()
    label("set_row_1")
    set(pins, 1) [31]  #set row_1 High and wait for button bounce
    set(x,0x1)
    in_(pins,4)
    mov(y, isr)
    jmp(not_y,"set_row_2")
    jmp("rx_fifo")
    
    label("set_row_2")
    set(pins, 2) [31]
    set(x,0x2)
    in_(pins,4)
    mov(y, isr)
    jmp(not_y,"set_row_3")
    jmp("rx_fifo")
    
    label("set_row_3")
    set(pins, 4) [31]
    set(x,0x4)
    in_(pins,4)
    mov(y, isr)
    jmp(not_y,"set_row_4")
    jmp("rx_fifo")
    
    label("set_row_4")
    set(pins, 8) [31]
    set(x,0x8)
    in_(pins,4)
    mov(y, isr)
    jmp(not_y,"set_row_1")
  
    
    label("rx_fifo")
    push()              #push y(col) 
    in_(x,4)[2]
    push()              #and then x(row)
    irq(rel(0))
    
    wait(0,pin,0)  # check whether key is released
    wait(0,pin,1)
    wait(0,pin,2)
    wait(0,pin,3)
    wrap()   # total 31 instructions
    
keys=[["1","2","3","A"],
      ["4","5","6","B"],
      ["7","8","9","C"],
      ["*","0","#","D"]]

def keypad_irq(sm):
    y=sm.get()
    x=sm.get()
    for i in range(4):
        if x >> i == 1:
            break;
    for j in range(4):
        if y >> j == 1:
            break;
    print(keys[i][j])
    
smkeypad=rp2.StateMachine(0, keypad, freq=2000, set_base=Pin(2), in_base=Pin(6))
smkeypad.irq(keypad_irq)
smkeypad.active(1)

沒有留言:

張貼留言