Uma das características mais interessantes da PiPico é o PIO - Programmable Input/Output, que permite executar pequenos programas de manipulação de entrada e saída digital em paralelo à execução dos núcleos ARM Cortex M0+.
Resolvi estudar o PIO fazendo a interface com um teclado PS/2, que já examinamos aqui no blog. Neste post anterior nós usamos um Arduino e os dados eram lidos bit a bit em uma interrupção gerada pelo sinal de clock. Com a PIO o processador vai receber direto o dado completo.
Sou sujinho mas funciono, viu? |
A descrição da PIO está no datasheet do RP2040 (o microcontrolador usado na PiPico). Resumindo as informações que estão lá:
- O RP2040 possui dois blocos PIO
- Cada bloco PIO possui quatro máquinas de estado que rodam de forma independente
- Cada bloco PIO possui uma memória de programa com capacidade de 32 instruções, esta memória é compartilhada pelas quatro máquinas de estado
Cada máquina de estado da PIO
- Possui dois registradores de deslocamento de 32 bits
- Tem acesso aos 32 pinos de GPIO para entrada e saída digital
- Tem acesso a duas filas de 4 posições de 32 bits para interface bidirecional com o processador, que podem ser reconfiguradas como uma única fila de 8 posições unidirecional. Estas filas podem interagir com a interrupção e o DMA.
Os programas para a PIO utilizam somente 9 instruções: JMP, WAIT, IN, OUT, PUSH, PULL, MOV, IRQ, e SET. Mesmo assim, pode ser complexo construir um programa a partir do zero. Felizmente a Raspberry Foundation fornece vários exemplos em https://github.com/raspberrypi/pico-examples/tree/master/pio.
No nosso caso, o que interessa é o exemplo "clocked input": leitura de bits indicados por pulsos de clock. Neste exemplo, o programa na PIO são apenas três instruções (wrap_target e wrap são configurações):
wrap_target() wait 0 pin 1 wait 1 pin 1 in pins, 1 wrap()
O que isso faz é esperar o sinal de clock ir para o nível 0, esperar o clock ir para o nível 1, ler o dado, colocar no registrador de deslocamento e voltar ao começo (cortesia das configurações feitas por wrap).
O resto é tratado na configuração da máquina de estados. Lá são especificados os pinos de dado e clock, a direção do deslocamento e o número de bits a receber para considerar o dado completo e colocá-lo na fila.
Para tratar o teclado PS/2 precisamos fazer pequenos ajustes neste exemplo:
- O estado de repouso do clock é o nível alto, o dado deve ser pego na borda de descida
- Temos um total de 11 bits (start, dados, paridade e stop).
Antes de partir para o C/C++, resolvi fazer um teste usando MicroPython. A boa notícia é que existe suporte ao PIO no MicroPython, a má é que a documentação é um pouco escassa. O manual do Python SDK tem algumas coisas, existem exemplos no github e tem mais informações no site do Micro Python. Juntando tudo, cheguei ao seguinte código:
import time import rp2 from machine import Pin # in_shiftdir=rp2.PIO.SHIFT_RIGHT -> deslocar os bits recebidos para a direita # autopush=True, push_thresh=11 -> colocar na fila de recepção a cada 11 bits recebidos # fifo_join=rp2.PIO.JOIN_RX -> juntar a fila de transmissão na de recepção @rp2.asm_pio(in_shiftdir=rp2.PIO.SHIFT_RIGHT, autopush=True, push_thresh=11, fifo_join=rp2.PIO.JOIN_RX) def rdKbd(): wrap_target() wait (1, pin, 1) wait (0, pin, 1) in_ (pins, 1) wrap() # Configura os pinos de entrada pin14 = Pin(14, Pin.IN, Pin.PULL_UP) pin15 = Pin(15, Pin.IN, Pin.PULL_UP) # O clock de 100KHz para a SM é adequado para um clock de cerca de 10KHz do teclado # Os pinos de entrada começam no pino 14 sm = rp2.StateMachine(0, rdKbd, freq=100000, in_base=pin14) # Ativa a State Machine sm.active(1) while True: # sm.get() espera ter um valor na fila de recepção e o pega # O shift e o and jogam fora start, paridade e stop e alinham o scancode à direita lido = (sm.get() >> 22) & 0xFF print ('Lido = {:02X}'.format(lido))
A montagem para teste ficou assim:
- A alimentação do teclado vem do VBus do PiPico
- O terra do teclado vai para qualquer pino GND do Pi Pico
- O sinal de dado do teclado vai para um resistor de 22K cuja outra ponta está ligada ao GPIO 14 do PiPico e a um resistor de 33K com a outra ponta em GND. Os resistores formam um divisor de tensão para reduzir os 5V do teclado para os 3V que o PiPico aguenta.
- O sinal de clock do teclado vai para um resistor de 22K cuja outra ponta está ligada ao GPIO 15 do PiPico e a um resistor de 33K com a outra ponta em GND.
Executando o código através da IDE Thonny podemos observar os "scancodes" recebidos do teclado. Por exemplo, apertando F9 é gerado o scancode 01, soltando o F9 são gerados os scancodes F0 01.
01/05/22: Revisão geral, inclusão da figura da montagem
Nenhum comentário:
Postar um comentário