Apenas uma atualização rápida neste projeto: a geração dos pulsos agora está sendo feita usando a PIO (Programmable Input/Output) do RP2040.
A referência principal para a programação foi este livro aqui: https://leanpub.com/rp2040 😉
O primeiro passo foi escolher o clock da PIO. Este clock define o tempo que cada instrução demora para executar (o tempo de um ciclo). Cada instrução requer um ciclo, mais um tempo adicional (delay) que você pode adicionar. Um detalhe importante é que cada instrução possui cinco bits que são compartilhados entre o valor de delay e o controle (opcional) de pinos (o chamado side-set).
No meu caso eu usei um 1 bit de side-set para controlar o pino que gera o sinal, restando quatro bits para o delay. Portanto o tempo máximo para execução de uma instrução é de 16 ciclos (1 da instrução mais 15 de delay).
Para quem não lembra da parte anterior, um bit "0" gera 4 pulsos (150us em nível ALTO e 150us em nível BAIXO); um bit 1 gera 9 pulsos (com os mesmos tempos). Nos dois casos, após os pulsos tem 1300us de nível BAIXO.
Portanto, os tempos que eu preciso gerar são 150us e 1300useg. Usando um clock de 50us, os tempos curtos correspondem a 3 ciclos e o longo a 26 ciclos.
A passagem de dados do processador para uma máquina de estado da PIO é feito através de uma fila. A instrução OUT extrai "n" bits do primeiro da fila. A cada "m" bits extraídos a "fila anda" (se estiver com o modo autopull ligado). Se a fila estiver vazia quando o OUT é executado, o programa para (stall) até algo ser colocado na fila. No meu caso, "n" é 1 e "m" é 8: OUT vai pegar um bit de cada vez e pegar outro dado na fila a cada 8 bits.
O programa completo da PIO está aqui:
.program k7 .side_set 1 .wrap_target GET_BIT: OUT X,1 SIDE 1 [7] ; 8 silence SET Y,4 SIDE 1 [15] ; 16 silence JMP !X, PULSE_TEST SIDE 1 ; 1 silence SET Y,8 SIDE 1 ; 1 silence PULSE: NOP SIDE 0 [2] ; 3 on NOP SIDE 1 [1] ; 2 off PULSE_TEST: JMP Y--, PULSE SIDE 1 ; 1 off .wrap
O programa usa os dois registradores auxiliares X e Y. X é usado para guardar o bit a transmitir e Y contar os pulsos. A instrução "JMP !X, PULSE_TEST" desvia para PULSE_TEST se X for zero. A instrução "JMP Y--, PULSE" primeira testa Y e depois o decrementa; se Y era diferente de zero, desvia para PULSE.
O .wrap no final faz com que o programa retorne automaticamente para o ponto marcado com .wrap_target, economizando um JMP. No total são apenas 8 instruções (cada uma das duas PIOs do RP2040 possui uma memória para até 32 instruções).
A versão que está aí foi o resultado de algumas iterações de otimização feitas "no papel". Para minha surpresa e alegria, o programa da PIO funcionou de primeira.
Aonde eu precisei fazer um acerto foi no programa C que interage com a PIO. Como os bits precisam ser tratados do mais significativo para o menos, a instrução OUT foi configurada para fazer deslocamento para a esquerda do dado obtido da fila. Portanto o que interessa são os 8 bits mais significativos do que foi escrito na fila. Como cada item da fila tem 32 bits, é preciso deslocar o byte a enviar 24 bits para a esquerda, o que eu tinha esquecido.
Um teste com osciloscópio confirmou que os tempos e a quantidade de pulsos estavam corretos. A carga de um programa fixo no ZX81 ocorreu sem problemas.
Os programas estão no meu github: https://github.com/dquadros/PicoK7
Agora é hora de tratar o cartão SD.
Nenhum comentário:
Postar um comentário