Skip to content
Snippets Groups Projects
keyboard.rs 6.86 KiB
Newer Older
Stephen D's avatar
Stephen D committed
use core::{convert::Infallible, fmt::Display};
Stephen D's avatar
Stephen D committed

Stephen D's avatar
Stephen D committed
use embedded_hal::{
    blocking::delay::{DelayMs, DelayUs},
    digital::v2::{InputPin, OutputPin},
};
use rp2040_hal::Timer;
Stephen D's avatar
Stephen D committed
use rtt_target::{rprint, rprintln};

const NONE: char = '\0';
const CALL_END: char = '\x01';
const L_SHIFT: char = '\x02';
const R_SHIFT: char = '\x03';
const ALT: char = '\x04';
const SYM: char = '\x05';
const BACK: char = '\x06';
const MENU: char = '\x07';
const BACKSPACE: char = '\x08';
const CALL_START: char = '\x09';
const TOUCHPAD_BUTTON: char = '\x0B';
const ENTER: char = '\n';
const KEYMAP: [[char; 8]; 6] = [
    [CALL_END, ENTER, 'b', 'y', 'n', 'j', 'u', 'h'],
    [NONE, BACKSPACE, '$', 'i', 'm', 'k', 'o', 'l'],
    [NONE, 'p', 'x', 'd', 'z', L_SHIFT, 'e', 's'],
    [NONE, R_SHIFT, 'v', 't', 'c', 'f', 'r', 'g'],
    [NONE, 'a', ALT, SYM, ' ', '0', 'q', 'w'],
    [
        NONE,
        NONE,
        BACK,
        MENU,
        NONE,
        CALL_START,
        NONE,
        TOUCHPAD_BUTTON,
    ],
];

enum KeyCode {
    CallStart,
    Menu,
    Touchpad,
    Back,
    CallEnd,
    Backspace,
    Enter,
    LeftShift,
    RightShift,
    Alt,
    Sym,
    Char(char),
}

impl KeyCode {
    fn from_keyboard(c: char, alt: bool, shift: bool) -> Self {
        match (alt, shift, c) {
            (_, _, CALL_END) => KeyCode::CallEnd,
            (_, _, L_SHIFT) => KeyCode::LeftShift,
            (_, _, R_SHIFT) => KeyCode::RightShift,
            (_, _, ALT) => KeyCode::Alt,
            (_, _, SYM) => KeyCode::Sym,
            (_, _, BACK) => KeyCode::Back,
            (_, _, MENU) => KeyCode::Menu,
            (_, _, BACKSPACE) => KeyCode::Backspace,
            (_, _, CALL_START) => KeyCode::CallStart,
            (_, _, TOUCHPAD_BUTTON) => KeyCode::Touchpad,
            (_, _, ENTER) => KeyCode::Enter,
            (false, false, _) => KeyCode::Char(c),
            (true, _, _) => KeyCode::Char(altify(c)),
            (false, true, _) => KeyCode::Char(c.to_ascii_uppercase()),
        }
    }
}

impl Display for KeyCode {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            KeyCode::CallEnd => write!(f, "<CALLEND>"),
            KeyCode::Backspace => write!(f, "<BACKSPACE>"),
            KeyCode::Enter => write!(f, "\n"),
            KeyCode::CallStart => write!(f, "<CALLSTART>"),
            KeyCode::Menu => write!(f, "<MENU>"),
            KeyCode::Touchpad => write!(f, "<TOUCHPAD>"),
            KeyCode::Back => write!(f, "<BACK>"),
            KeyCode::LeftShift => write!(f, "<LEFTSHIFT>"),
            KeyCode::RightShift => write!(f, "<RIGHTSHIFT>"),
            KeyCode::Alt => write!(f, "<ALT>"),
            KeyCode::Sym => write!(f, "<SYM>"),
            KeyCode::Char(c) => write!(f, "{c}"),
        }
    }
}
Stephen D's avatar
Stephen D committed

pub struct Keyboard<
    // col
    CCLK: OutputPin<Error = Infallible>,
    CSER: OutputPin<Error = Infallible>,
    CRCLK: OutputPin<Error = Infallible>,
    // row
    RCLK: OutputPin<Error = Infallible>,
    RSER: InputPin,
    RLD: OutputPin<Error = Infallible>,
> {
    timer: Timer,
    col_clk: CCLK,
    col_ser: CSER,
    col_rclk: CRCLK,
    row_clk: RCLK,
    row_ser: RSER,
    row_ld: RLD,
Stephen D's avatar
Stephen D committed
    last_state: u64,
Stephen D's avatar
Stephen D committed
}

Stephen D's avatar
Stephen D committed
impl<
        CCLK: OutputPin<Error = Infallible>,
        CSER: OutputPin<Error = Infallible>,
        CRCLK: OutputPin<Error = Infallible>,
        RCLK: OutputPin<Error = Infallible>,
        RSER: InputPin<Error = Infallible>,
        RLD: OutputPin<Error = Infallible>,
    > Keyboard<CCLK, CSER, CRCLK, RCLK, RSER, RLD>
{
Stephen D's avatar
Stephen D committed
    pub fn new(
Stephen D's avatar
Stephen D committed
        timer: Timer,
        col_clk: CCLK,
        col_ser: CSER,
        col_rclk: CRCLK,
        row_clk: RCLK,
        row_ser: RSER,
        row_ld: RLD,
Stephen D's avatar
Stephen D committed
    ) -> Self {
Stephen D's avatar
Stephen D committed
        Self {
            timer,
Stephen D's avatar
Stephen D committed

Stephen D's avatar
Stephen D committed
            col_clk,
            col_ser,
            col_rclk,

            row_clk,
            row_ser,
            row_ld,
Stephen D's avatar
Stephen D committed

            last_state: 0,
Stephen D's avatar
Stephen D committed
        }
Stephen D's avatar
Stephen D committed
    }

    pub fn read_state(&mut self) -> u64 {
Stephen D's avatar
Stephen D committed
        let mut state = 0;
Stephen D's avatar
Stephen D committed

Stephen D's avatar
Stephen D committed
        for x in 0..6 {
Stephen D's avatar
Stephen D committed
            // +2 gets us to QF (col 6)
            self.write_cols(1 << (x + 2));
Stephen D's avatar
Stephen D committed

Stephen D's avatar
Stephen D committed
            self.timer.delay_ms(1);
Stephen D's avatar
Stephen D committed

Stephen D's avatar
Stephen D committed
            let y = read_rows(
Stephen D's avatar
Stephen D committed
                &mut self.timer,
                &mut self.row_clk,
                &mut self.row_ser,
                &mut self.row_ld,
Stephen D's avatar
Stephen D committed
            );

            state |= u64::from(y) << (x * 8);
        }

        let delta = (!self.last_state) & state;
        for x in 0..6 {
            for y in 0..8 {
                if delta & (1 << (x * 8 + y)) > 0 {
                    let key = KEYMAP[usize::try_from(x).unwrap()][usize::try_from(y).unwrap()];
                    if key != NONE {
                        let keycode = KeyCode::from_keyboard(
                            key,
                            is_alt_pressed(state),
                            is_shift_pressed(state),
                        );
                        rprint!("{}", keycode);
                    } else {
                        rprint!("\n({}, {})", x, y);
                    }
                }
            }
Stephen D's avatar
Stephen D committed
        }

Stephen D's avatar
Stephen D committed
        self.last_state = state;
        state
Stephen D's avatar
Stephen D committed
    }
Stephen D's avatar
Stephen D committed

Stephen D's avatar
Stephen D committed
    // 1s in val are low
    // 0s are high
    // indexes from furthest pin (QH on second shift register)
    fn write_cols(&mut self, val: u16) {
        for i in 0..16 {
Stephen D's avatar
Stephen D committed
            self.col_ser
Stephen D's avatar
Stephen D committed
                .set_state(((val & (1 << i)) == 0).into())
Stephen D's avatar
Stephen D committed
                .unwrap();
Stephen D's avatar
Stephen D committed

Stephen D's avatar
Stephen D committed
            self.col_clk.set_low().unwrap();
            self.col_clk.set_high().unwrap();
        }

        self.col_rclk.set_low().unwrap();
        self.col_rclk.set_high().unwrap();
    }
}

fn read_rows<
    RCLK: OutputPin<Error = Infallible>,
    RSER: InputPin<Error = Infallible>,
    RLD: OutputPin<Error = Infallible>,
>(
    timer: &mut Timer,
    row_clk: &mut RCLK,
    row_ser: &mut RSER,
    row_ld: &mut RLD,
) -> u8 {
    row_ld.set_low().unwrap();
    row_ld.set_high().unwrap();

    let mut out = 0;
    for i in 0..8 {
        timer.delay_us(10);

        if row_ser.is_low().unwrap() {
            out |= 1 << i;
        }

        row_clk.set_low().unwrap();
        row_clk.set_high().unwrap();
    }

    out
Stephen D's avatar
Stephen D committed
}
Stephen D's avatar
Stephen D committed

fn is_alt_pressed(state: u64) -> bool {
    (state & (1 << (4 * 8 + 2))) > 0
}

fn is_shift_pressed(state: u64) -> bool {
    let left = (state & (1 << (2 * 8 + 5))) > 0;
    let right = (state & (1 << (3 * 8 + 1))) > 0;

    left || right
}

fn altify(c: char) -> char {
    match c {
        'q' => '#',
        'w' => '1',
        'e' => '2',
        'r' => '3',
        't' => '(',
        'y' => ')',
        'u' => '_',
        'i' => '-',
        'o' => '+',
        'p' => '@',
        'a' => '*',
        's' => '4',
        'd' => '5',
        'f' => '6',
        'g' => '/',
        'h' => ':',
        'j' => ';',
        'k' => ',',
        'l' => '"',
        'z' => '7',
        'x' => '8',
        'c' => '9',
        'v' => '?',
        'b' => '!',
        'n' => ',',
        'm' => '.',
        _ => c,
    }
}