Skip to content

Instantly share code, notes, and snippets.

@jepler
Created September 4, 2024 22:29
Show Gist options
  • Save jepler/b3b639d222c6b31df01ddf141bdcc8d4 to your computer and use it in GitHub Desktop.
Save jepler/b3b639d222c6b31df01ddf141bdcc8d4 to your computer and use it in GitHub Desktop.
import array
import rp2pio
import board
import adafruit_pioasm
import time
# MIT licensed https://github.com/No0ne/ps2pico/blob/main/LICENSE
# https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol
KBD_DATA = board.GP27
KBD_CLK = board.GP26
debug_raw = const(True)
program = adafruit_pioasm.Program("""
; from https://raw.githubusercontent.com/PaulW/rp2040-keyboard-converter/main/src/protocols/at-ps2/interface.pio, license: GPLv3
; Pins: 0 = data, 1 = clock
init:
; Wait until CLK is high before we continue. Keyboards seem to pull this LOW briefly
; during power on. This prevents the first bit read being shifted in this scenario.
wait 1 pin 1
check:
; Wait for incoming data, but jump to bitLoopOut if the Output Shift Register isn't empty
jmp !osre, bitLoopOut
jmp pin, check ; Loop back to check if pin high
; Receiving Data
; IBM AT:
; ____ 1 _ 2 _ 3 _ 4 _ 5 _ 6 _ 7 _ 8 _ 9 _ A _ B _____
; CLK \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
; ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ________
; DAT \___/___X___X___X___X___X___X___X___X___/
; S 0 1 2 3 4 5 6 7 P s
; Read Start bit
in pins, 1
; Wait for Clock to go High
wait 1 pin 1
; Set x to 9 (to read in 8 x Data bits, 1 x Parity and 1 x Stop bit)
set x, 9
bitLoopIn:
; Wait for clock signal to go low
wait 0 pin 1 [1]
; Read data pin and store in pins register
in pins, 1
; Wait for clock signal to go high
wait 1 pin 1
; Decrement x and jump back to bitLoopIn if it's not zero
jmp x--, bitLoopIn
; Jump back to check now all data read
jmp check
bitLoopOut:
; Sending Data
; IBM AT:
; __ _ 1 _ 2 _ 3 _ 4 _ 5 _ 6 _ 7 _ 8 _ 9 _ A __ B ____
; CLK \____/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
; ______ ___ ___ ___ ___ ___ ___ ___ ___ ______ ____
; DAT \___/___X___X___X___X___X___X___X___X___/ \___/
; H R 0 1 2 3 4 5 6 7 P s ACK
; Set data pin low and configure pins
set pins, 0
set pindirs 2 [14] ; Set data pin to output mode
set pindirs 1 [1] ; Set clock pin to input mode
; Set x to 8 (to write out 8 Data Bits and 1 Parity Bit)
set x, 8
bitLoopOutLoop:
; Wait for clock signal to go low and start sending bit
wait 0 pin 1 [1]
out pins, 1
; Wait for clock signal to go high
wait 1 pin 1
; Decrement x and jump back to bitLoopOutLoop if it's not zero
jmp x--, bitLoopOutLoop
; Send stop bit
wait 0 pin 1 [1]
set pins, 1
wait 1 pin 1
; Wait for ACK to acknowledge
set pindirs, 0 ; Set data pin to input mode so we can read ACK
wait 0 pin 0 [1]
wait 1 pin 0 [5]
""", build_debuginfo=True)
program.print_c_program("program")
cycles_per_bit = 11
poll_rate_khz = 20_000
sm = rp2pio.StateMachine(program.assembled,
first_in_pin = KBD_DATA,
first_out_pin = KBD_DATA,
first_set_pin = KBD_DATA,
jmp_pin = KBD_CLK,
in_pin_count = 2,
out_pin_count = 2,
set_pin_count = 2,
in_shift_right=True,
frequency=8_000_000,
exclusive_pin_use=False,
auto_pull=True,
pull_threshold=9,
auto_push=True,
push_threshold=11,
initial_out_pin_direction=0,
initial_set_pin_direction=0,
**program.pio_kwargs)
def parity(x):
x = x ^ (x >> 4)
x = x ^ (x >> 2)
x = x ^ (x >> 1)
return (x & 1) ^ 1
def get_kbd_byte():
sm.readinto(buf, swap=False)
r = (buf[0] >> 6) & 0xff
if debug_raw:
print(f"RAW {r:02x} {buf[0]:016b}")
return r
def get_kbd_code():
val = get_kbd_byte()
is_extended = (val == 0xe0)
if is_extended:
val = get_kbd_byte()
is_break = (val == 0xf0)
if is_break:
val = get_kbd_byte()
return val | (is_extended << 8) | (is_break << 9)
def write_cmd(*args):
for b0 in args:
p = parity(b0)
print(b0, p)
buf[0] = b0 | (parity(b0) << 8)
print(f"WRITE {b0:08b} {buf[0]:011b}")
sm.background_write(buf)
buf = array.array('H', [0])
#write_cmd(0xed, 0xe0)
#raise SystemExit()
write_cmd(0xed, 0xe0)
offset = sm.offset
print(f"State machine loaded at {sm.offset}")
i = 7
while True:
pc = sm.pc
print(f"{pc - offset:2d} [{pc:2d}]", end="\r")
if sm.in_waiting:
print(f"{get_kbd_byte():02x}")
#else:
# write_cmd(0xed, i << 5)
# i = (i + 1) & 0x7
# time.sleep(.5)
#break
while True:
code = get_kbd_code()
print(f"{'BREAK' if code & 0x200 else 'MAKE':5} {code & 0x1ff:03x}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment