Skip to content

Instantly share code, notes, and snippets.

@jepler
Last active September 28, 2024 02:31
Show Gist options
  • Save jepler/7d2e3d53c1716d8307e98746c6cb0b76 to your computer and use it in GitHub Desktop.
Save jepler/7d2e3d53c1716d8307e98746c6cb0b76 to your computer and use it in GitHub Desktop.
import array
import rp2pio
import board
import adafruit_pioasm
import time
import supervisor
# 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.GP28
debug_raw = const(True)
if debug_raw:
log_last_direction = None
def log(direction, *args):
global log_last_direction
if log_last_direction != direction:
print()
print(direction, end="")
log_last_direction = direction
for a in args:
print(f" {a:02x}", end="")
else:
def log(*args):
return
program = adafruit_pioasm.Program.from_file("newps2.pio", 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=10,
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
assert parity(0) == 1
def get_kbd_byte():
log(f"pio pointer {sm.pc - sm.offset}")
sm.readinto(buf, swap=False)
r = (buf[0] >> 6) & 0xff
log("<", r)
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)
pending_led_value = None
def set_kbd_leds(value):
global pending_led_value, pending_led_flag
if value != pending_led_value:
write_cmd(0xed)
pending_led_value = value
pending_led_flag = True
def write_cmd(*args):
for b0 in args:
p = parity(b0)
log(">", b0)
buf[0] = b0 | (parity(b0) << 8)
#wop = "".join(reversed(f"{buf[0]:09b}"))
#print(f"WRITE {b0:08b} wire order with parity: {wop}")
sm.background_write(buf)
buf = array.array('H', [0])
offset = sm.offset
log(f"State machine loaded at {sm.offset}")
write_cmd(0xff)
b = get_kbd_byte()
if b != 0xfa:
raise SystemExit("keyboard didn't ack??")
b = get_kbd_byte()
if b != 0xaa:
raise SystemExit("selftest didn't go OK")
log("selftest done")
write_cmd(0xf0)
b = get_kbd_byte()
if b != 0xfa:
raise SystemExit("keyboard didn't ack??")
write_cmd(0x03)
b = get_kbd_byte()
if b != 0xfa:
raise SystemExit("keyboard didn't ack??")
log("set3 enabled")
write_cmd(0xfa)
b = get_kbd_byte()
if b != 0xfa:
raise SystemExit("keyboard didn't ack??")
log("set all typematic/make/break")
while True:
if sm.in_waiting:
b = get_kbd_byte()
if b == 0xFA:
if pending_led_flag:
write_cmd(pending_led_value)
pending_led_flag = False
else:
pass
#set_kbd_leds((supervisor.ticks_ms() >> 8) & 0x7)
while True:
code = get_kbd_code()
print(f"{'BREAK' if code & 0x200 else 'MAKE':5} {code & 0x1ff:03x}")
;; todo: use 'wait gpio' (or 'wait jmp' [rp2350]) so any pair of pins
;; can be used
;wait 1 pin 1 ; wait for keyboard to signal ready (not necessary?)
.wrap_target
start:
jmp !osre send_byte ; if a byte is pending to send, do that
jmp pin start ; otherwise wait for keyboard to send something
; keyboard has signaled start condition
set x 9 ; will receive 10 bits (data + parity + stop)
wait 1 pin 1 ; wait for clock pin to go high (discard start bit)
receive_bit:
wait 0 pin 1
in pins 1
wait 1 pin 1
jmp x-- receive_bit
jmp start
send_byte:
set pins 0 ; will be pulling lines low selectively
set pindirs 2 [14] ; pull clock low to request to send
set pindirs 1 [1] ; set data pin to output & send "R" bit
set x 8 ; will send 9 bits (data + parity)
send_bit:
wait 0 pin 1
out pins 1
wait 1 pin 1
jmp x-- send_bit
set pindirs 0 ; pins back to input mode
wait 0 pin 0
wait 1 pin 0 ; wait for keyboard ACK
.wrap
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment