sxkbd

Firmware for RP2040-based corne split keyboard
git clone https://git.sinitax.com/sinitax/sxkbd
Log | Files | Refs | Submodules | README | LICENSE | sfeed.txt

split.c (7034B)


      1 #include "split.h"
      2 #include "util.h"
      3 #include "keymat.h"
      4 #include "uart_rx.pio.h"
      5 #include "uart_tx.pio.h"
      6 
      7 #include "device/usbd.h"
      8 #include "hardware/pio.h"
      9 #include "hardware/irq.h"
     10 #include "hardware/gpio.h"
     11 #include "hardware/regs/intctrl.h"
     12 #include "hardware/clocks.h"
     13 
     14 #include <stdint.h>
     15 
     16 #define UART_AWAIT_TIMEOUT_US 1000
     17 #define UART_RECV_TIMEOUT_US 200
     18 #define UART_SEND_TIMEOUT_US 200
     19 
     20 #define UART_BAUD 115200
     21 
     22 #define CMD_START 0x8a
     23 
     24 #if SPLIT_SIDE == LEFT
     25 #define UART_TX_PIN 0
     26 #define UART_RX_PIN 1
     27 #elif SPLIT_SIDE == RIGHT
     28 #define UART_TX_PIN 1
     29 #define UART_RX_PIN 0
     30 #else
     31 #error "SPLIT_SIDE not set"
     32 #endif
     33 
     34 enum {
     35 	CMD_SCAN_KEYMAT_REQ = 0x80,
     36 	CMD_SCAN_KEYMAT_RESP,
     37 	CMD_SLAVE_WARN
     38 };
     39 
     40 static uint uart_tx_sm;
     41 static uint uart_tx_sm_offset;
     42 
     43 static uint uart_rx_sm;
     44 static uint uart_rx_sm_offset;
     45 
     46 static uint32_t halfmat;
     47 
     48 int split_role;
     49 
     50 static void
     51 irq_rx(void)
     52 {
     53 	if (pio_interrupt_get(pio0, 0)) {
     54 		DEBUG(LOG_SPLIT, "UART RX ERR");
     55 		pio_interrupt_clear(pio0, 0);
     56 	}
     57 }
     58 
     59 static void
     60 uart_tx_sm_init(void)
     61 {
     62 	pio_sm_config config;
     63 
     64 	uart_tx_sm = claim_unused_sm(pio0);
     65 	uart_tx_sm_offset = pio_add_program(pio0, &uart_tx_program);
     66 
     67 	config = uart_tx_program_get_default_config(uart_tx_sm_offset);
     68 	sm_config_set_out_shift(&config, true, false, 32);
     69 	sm_config_set_out_pins(&config, UART_TX_PIN, 1);
     70 	sm_config_set_sideset_pins(&config, UART_TX_PIN);
     71 	sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_TX);
     72 	sm_config_set_clkdiv(&config,
     73 		((float) clock_get_hz(clk_sys)) / (8 * UART_BAUD));
     74 
     75 	pio_sm_init(pio0, uart_tx_sm, uart_tx_sm_offset, &config);
     76 	pio_sm_set_enabled(pio0, uart_tx_sm, false);
     77 }
     78 
     79 static void
     80 uart_rx_sm_init(void)
     81 {
     82 	pio_sm_config config;
     83 
     84 	uart_rx_sm = claim_unused_sm(pio0);
     85 	uart_rx_sm_offset = pio_add_program(pio0, &uart_rx_program);
     86 
     87 	config = uart_rx_program_get_default_config(uart_rx_sm_offset);
     88 	sm_config_set_in_pins(&config, UART_RX_PIN);
     89 	sm_config_set_jmp_pin(&config, UART_RX_PIN);
     90 	sm_config_set_in_shift(&config, true, false, 32);
     91 	sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_RX);
     92 	sm_config_set_clkdiv(&config,
     93 		((float) clock_get_hz(clk_sys)) / (8 * UART_BAUD));
     94 
     95 	pio_sm_init(pio0, uart_rx_sm, uart_rx_sm_offset, &config);
     96 	pio_sm_set_enabled(pio0, uart_rx_sm, false);
     97 }
     98 
     99 static void
    100 uart_full_init(void)
    101 {
    102 	pio_gpio_init(pio0, UART_RX_PIN);
    103 	pio_sm_set_pins_with_mask(pio0, uart_rx_sm, 1U, 1U << UART_RX_PIN);
    104 	pio_sm_set_consecutive_pindirs(pio0, uart_rx_sm, UART_RX_PIN, 1, false);
    105 
    106 	pio_gpio_init(pio0, UART_TX_PIN);
    107 	gpio_set_slew_rate(UART_TX_PIN, GPIO_SLEW_RATE_FAST);
    108 	gpio_set_drive_strength(UART_TX_PIN, GPIO_DRIVE_STRENGTH_4MA);
    109 	pio_sm_set_pins_with_mask(pio0, uart_tx_sm, 1U, 1U << UART_TX_PIN);
    110 	pio_sm_set_consecutive_pindirs(pio0, uart_tx_sm, UART_TX_PIN, 1, true);
    111 
    112 	uart_rx_sm_init();
    113 	uart_tx_sm_init();
    114 
    115 	pio_set_irq0_source_enabled(pio0,
    116 		pis_sm0_rx_fifo_not_empty + uart_rx_sm, false);
    117 	pio_set_irq0_source_enabled(pio0,
    118 		pis_sm0_tx_fifo_not_full + uart_tx_sm, false);
    119 	pio_set_irq0_source_enabled(pio0, pis_interrupt0, true);
    120 
    121 	irq_set_priority(PIO0_IRQ_0, PICO_HIGHEST_IRQ_PRIORITY);
    122 	irq_set_exclusive_handler(PIO0_IRQ_0, irq_rx);
    123 	irq_set_enabled(PIO0_IRQ_0, true);
    124 
    125 	pio_sm_set_enabled(pio0, uart_rx_sm, true);
    126 	pio_sm_set_enabled(pio0, uart_tx_sm, true);
    127 }
    128 
    129 static bool
    130 uart_await_rx(uint64_t timeout_us)
    131 {
    132 	uint64_t start_us;
    133 	bool empty;
    134 
    135 	if (!pio_sm_is_rx_fifo_empty(pio0, uart_rx_sm))
    136 		return true;
    137 
    138 	start_us = board_micros();
    139 	do {
    140 		tud_task();
    141 		empty = pio_sm_is_rx_fifo_empty(pio0, uart_rx_sm);
    142 	} while (empty && board_micros() < start_us + timeout_us);
    143 
    144 	return !empty;
    145 }
    146 
    147 static bool
    148 uart_await_tx(uint64_t timeout_us)
    149 {
    150 	uint64_t start_us;
    151 	bool full;
    152 
    153 	if (!pio_sm_is_tx_fifo_full(pio0, uart_tx_sm))
    154 		return true;
    155 
    156 	start_us = board_micros();
    157 	do {
    158 		tud_task();
    159 		full = pio_sm_is_tx_fifo_full(pio0, uart_tx_sm);
    160 	} while (full && board_micros() < start_us + timeout_us);
    161 
    162 	return !full;
    163 }
    164 
    165 static uint8_t
    166 uart_rx_byte(void)
    167 {
    168 	return *(uint8_t*)((uintptr_t)&pio0->rxf[uart_rx_sm] + 3);
    169 }
    170 
    171 static void
    172 uart_tx_byte(uint8_t c)
    173 {
    174 	pio_sm_put(pio0, uart_tx_sm, c);
    175 }
    176 
    177 static uint
    178 uart_recv(uint8_t *data, uint len)
    179 {
    180 	uint recv;
    181 
    182 	for (recv = 0; recv < len; recv++) {
    183 		if (!uart_await_rx(UART_RECV_TIMEOUT_US))
    184 			break;
    185 		*data++ = uart_rx_byte();
    186 	}
    187 
    188 	return recv;
    189 }
    190 
    191 static uint
    192 uart_recv_str(uint8_t *data, uint max)
    193 {
    194 	uint recv;
    195 
    196 	for (recv = 0; recv < max; recv++) {
    197 		if (!uart_await_rx(UART_RECV_TIMEOUT_US))
    198 			break;
    199 		*data++ = uart_rx_byte();
    200 		if (!*data) break;
    201 	}
    202 
    203 	return recv;
    204 }
    205 
    206 static uint
    207 uart_send(const uint8_t *data, uint len)
    208 {
    209 	uint sent;
    210 
    211 	for (sent = 0; sent < len; sent++) {
    212 		if (!uart_await_tx(UART_SEND_TIMEOUT_US))
    213 			break;
    214 		uart_tx_byte(*data++);
    215 	}
    216 
    217 	return sent;
    218 }
    219 
    220 static int
    221 handle_cmd(void)
    222 {
    223 	static uint8_t msgbuf[128];
    224 	uint8_t cmd, start;
    225 	uint len;
    226 
    227 	start = uart_rx_byte();
    228 	if (start != CMD_START) {
    229 		WARN(LOG_SPLIT, "Got bad start byte: %02u", start);
    230 		return -1;
    231 	}
    232 
    233 	if (!uart_recv(&cmd, 1)) {
    234 		WARN(LOG_SPLIT, "Got start byte without command");
    235 		return -1;
    236 	}
    237 
    238 	switch (cmd) {
    239 	case CMD_SCAN_KEYMAT_REQ:
    240 		if (split_role != SLAVE) {
    241 			WARN(LOG_SPLIT, "Got SCAN_KEYMAT_REQ as master");
    242 			return -1;
    243 		}
    244 		break;
    245 	case CMD_SCAN_KEYMAT_RESP:
    246 		if (split_role != MASTER) {
    247 			WARN(LOG_SPLIT, "Got SCAN_KEYMAT_RESP as slave");
    248 			return -1;
    249 		}
    250 		if (uart_recv((uint8_t *) &halfmat, 4) != 4) {
    251 			WARN(LOG_SPLIT, "Incomplete matrix received");
    252 			return -1;
    253 		}
    254 		break;
    255 	case CMD_SLAVE_WARN:
    256 		if (split_role != MASTER) {
    257 			WARN(LOG_SPLIT, "Got SLAVE_WARN as slave");
    258 			return -1;
    259 		}
    260 		len = uart_recv_str(msgbuf, sizeof(msgbuf)-1);
    261 		msgbuf[len] = '\0';
    262 		WARN(LOG_SPLIT, "SLAVE: %s\n", msgbuf);
    263 		break;
    264 	default:
    265 		WARN(LOG_SPLIT, "Unknown uart cmd: %i", cmd);
    266 		return -1;
    267 	}
    268 
    269 	return cmd;
    270 }
    271 
    272 static bool
    273 send_cmd(uint8_t cmd)
    274 {
    275 	uint8_t buf[2];
    276 
    277 	buf[0] = CMD_START;
    278 	buf[1] = cmd;
    279 
    280 	return uart_send(buf, 2) == 2;
    281 }
    282 
    283 void
    284 split_init(void)
    285 {
    286 	uart_full_init();
    287 #ifdef SPLIT_ROLE
    288 	split_role = SPLIT_ROLE;
    289 #else
    290 	split_role = SLAVE;
    291 #endif
    292 }
    293 
    294 static void
    295 split_task_master(void)
    296 {
    297 	int cmd;
    298 
    299 	keymat_scan();
    300 
    301 	if (uart_await_rx(UART_AWAIT_TIMEOUT_US)) {
    302 		if ((cmd = handle_cmd()) == CMD_SCAN_KEYMAT_RESP) {
    303 			keymat_decode_half(SPLIT_OPP(SPLIT_SIDE), halfmat);
    304 		} else {
    305 			WARN(LOG_SPLIT, "Got unexpected command %02x", cmd);
    306 		}
    307 	}
    308 
    309 	keymat_debug();
    310 }
    311 
    312 static void
    313 split_task_slave(void)
    314 {
    315 	if (keymat_scan()) {
    316 		DEBUG(LOG_SPLIT, "Sending SCAN_KEYMAT_RESP");
    317 		halfmat = keymat_encode_half(SPLIT_SIDE);
    318 		if (!send_cmd(CMD_SCAN_KEYMAT_RESP)
    319 				|| !uart_send((uint8_t *) &halfmat, 4)) {
    320 			WARN(LOG_SPLIT,
    321 				"UART send SCAN_KEYMAT_RESP failed");
    322 		}
    323 	}
    324 }
    325 
    326 void
    327 split_task(void)
    328 {
    329 	if (split_role == MASTER) {
    330 		split_task_master();
    331 	} else {
    332 		split_task_slave();
    333 	}
    334 }
    335 
    336 void
    337 split_warn_master(const char *msg)
    338 {
    339 	uint32_t len;
    340 
    341 	if (!send_cmd(CMD_SLAVE_WARN)) {
    342 		WARN(LOG_SPLIT, "UART send SLAVE_WARN failed");
    343 		return;
    344 	}
    345 
    346 	len = strlen(msg) + 1;
    347 	if (uart_send((const uint8_t *) msg, len) != len)
    348 		WARN(LOG_SPLIT, "UART send warning failed");
    349 }