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
     34enum {
     35	CMD_SCAN_KEYMAT_REQ = 0x80,
     36	CMD_SCAN_KEYMAT_RESP,
     37	CMD_SLAVE_WARN
     38};
     39
     40static uint uart_tx_sm;
     41static uint uart_tx_sm_offset;
     42
     43static uint uart_rx_sm;
     44static uint uart_rx_sm_offset;
     45
     46static uint32_t halfmat;
     47
     48int split_role;
     49
     50static void
     51irq_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
     59static void
     60uart_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
     79static void
     80uart_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
     99static void
    100uart_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
    129static bool
    130uart_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
    147static bool
    148uart_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
    165static uint8_t
    166uart_rx_byte(void)
    167{
    168	return *(uint8_t*)((uintptr_t)&pio0->rxf[uart_rx_sm] + 3);
    169}
    170
    171static void
    172uart_tx_byte(uint8_t c)
    173{
    174	pio_sm_put(pio0, uart_tx_sm, c);
    175}
    176
    177static uint
    178uart_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
    191static uint
    192uart_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
    206static uint
    207uart_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
    220static int
    221handle_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
    272static bool
    273send_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
    283void
    284split_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
    294static void
    295split_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
    312static void
    313split_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
    326void
    327split_task(void)
    328{
    329	if (split_role == MASTER) {
    330		split_task_master();
    331	} else {
    332		split_task_slave();
    333	}
    334}
    335
    336void
    337split_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}