commit 20c38dd87e618713ff9bd131e39cc95dfdb949c8
parent a90347438e51bbd5f65c1c58c76ba339df7fc814
Author: Louis Burda <quent.burda@gmail.com>
Date: Tue, 29 Nov 2022 04:02:41 +0100
Adapt ChibiOS uart pio implementation (untested)
Diffstat:
2 files changed, 242 insertions(+), 28 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -28,6 +28,7 @@ target_include_directories(${PROJECT} PUBLIC
${PICO_SDK_PATH}/src/rp2_common/hardware_pio/include
${PICO_SDK_PATH}/src/rp2_common/hardware_timer/include
${PICO_SDK_PATH}/src/rp2_common/hardware_uart/include
+ ${PICO_SDK_PATH}/src/rp2_common/hardware_clocks/include
)
target_link_libraries(${PROJECT} PRIVATE
diff --git a/src/split.c b/src/split.c
@@ -2,13 +2,20 @@
#include "util.h"
#include "matrix.h"
+#include "hardware/pio.h"
+#include "hardware/irq.h"
#include "hardware/gpio.h"
#include "hardware/regs/intctrl.h"
#include "hardware/uart.h"
#include "hardware/timer.h"
+#include "hardware/clocks.h"
+#include "pico/time.h"
#include "bsp/board.h"
#include "tusb.h"
+#include <stdint.h>
+#define UART_TIMEOUT 20
+#define UART_BAUD 115200
#define UART_TX_PIN 0
#define UART_RX_PIN 1
@@ -21,13 +28,217 @@ enum {
enum { SLAVE, MASTER };
enum { LEFT, RIGHT };
-static void split_rx_cmd(uint8_t cmd);
-static void split_rx(void);
+static void uart_tx_init(void);
+static void uart_rx_init(void);
+static void uart_enter_rx(void);
+static void uart_leave_rx(void);
+static int uart_sync_rx(void);
+static int uart_sync_tx(void);
+static bool uart_recv(uint8_t *data, uint len);
+static bool uart_send(const uint8_t *data, uint len);
+
+static void irq_rx_cmd(uint8_t cmd);
+static void irq_rx(void);
+
+static const uint16_t uart_tx_program_asm[] = {
+ // .wrap_target
+ 0x9fa0, // 0: pull block side 1 [7]
+ 0xf727, // 1: set x, 7 side 0 [7]
+ 0x6081, // 2: out pindirs, 1
+ 0x0642, // 3: jmp x--, 2 [6]
+ // .wrap
+};
+
+static const uint16_t uart_rx_program_asm[] = {
+ // .wrap_target
+ 0x2020, // 0: wait 0 pin, 0
+ 0xea27, // 1: set x, 7 [10]
+ 0x4001, // 2: in pins, 1
+ 0x0642, // 3: jmp x--, 2 [6]
+ 0x00c8, // 4: jmp pin, 8
+ 0xc020, // 5: irq wait 0
+ 0x20a0, // 6: wait 1 pin, 0
+ 0x0000, // 7: jmp 0
+ 0x8020, // 8: push block
+ // .wrap
+};
+
+static const pio_program_t uart_tx_program = {
+ .instructions = uart_tx_program_asm,
+ .length = 4,
+ .origin = -1,
+};
+
+static const pio_program_t uart_rx_program = {
+ .instructions = uart_rx_program_asm,
+ .length = 9,
+ .origin = -1,
+};
+
+static uint uart_tx_sm;
+static uint uart_rx_sm;
static bool scan_pending = false;
void
-split_rx_cmd(uint8_t cmd)
+uart_tx_init(void)
+{
+ pio_sm_config config;
+ uint offset;
+ int sm;
+
+ sm = pio_claim_unused_sm(pio0, true);
+ ASSERT(sm >= 0);
+ uart_tx_sm = (uint) sm;
+
+ offset = pio_add_program(pio0, &uart_tx_program);
+ pio_sm_set_pins_with_mask(pio0, uart_tx_sm, 0, 1 << UART_TX_PIN);
+ pio_sm_set_consecutive_pindirs(pio0,
+ uart_tx_sm, UART_TX_PIN, 1, true);
+
+ config = pio_get_default_sm_config();
+ sm_config_set_wrap(&config, offset,
+ offset + ARRLEN(uart_tx_program_asm) - 1);
+ sm_config_set_sideset(&config, 2, true, true);
+ sm_config_set_out_shift(&config, true, false, 32);
+ sm_config_set_out_pins(&config, UART_TX_PIN, 1);
+ sm_config_set_sideset_pins(&config, UART_TX_PIN);
+ sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_TX);
+ sm_config_set_clkdiv(&config,
+ (float) clock_get_hz(clk_sys) / (8.f * UART_BAUD));
+
+ pio_sm_init(pio0, uart_tx_sm, offset, &config);
+ pio_sm_set_enabled(pio0, uart_tx_sm, true);
+}
+
+void
+uart_rx_init(void)
+{
+ pio_sm_config config;
+ uint offset;
+ int sm;
+
+ sm = pio_claim_unused_sm(pio0, true);
+ ASSERT(sm >= 0);
+ uart_rx_sm = (uint) sm;
+
+ offset = pio_add_program(pio0, &uart_rx_program);
+
+ config = pio_get_default_sm_config();
+ sm_config_set_wrap(&config, offset,
+ offset + ARRLEN(uart_rx_program_asm) - 1);
+ sm_config_set_in_pins(&config, UART_RX_PIN);
+ sm_config_set_jmp_pin(&config, UART_RX_PIN);
+ sm_config_set_in_shift(&config, true, false, 32);
+ sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_RX);
+ sm_config_set_clkdiv(&config,
+ (float) clock_get_hz(clk_sys) / (8.f * UART_BAUD));
+
+ pio_sm_init(pio0, uart_rx_sm, offset, &config);
+ pio_sm_set_enabled(pio0, uart_rx_sm, true);
+}
+
+void
+uart_enter_rx(void)
+{
+ while (!pio_sm_is_tx_fifo_empty(pio0, uart_tx_sm));
+ /* even after fifo is empty, we still need to wait until last byte
+ * = max (1 start + 8 data + 1 stop + 1 par) is fully transmitted */
+ sleep_us(1000000U * 11 / UART_BAUD);
+
+ pio_sm_set_enabled(pio0, uart_tx_sm, false);
+ gpio_set_drive_strength(UART_TX_PIN, GPIO_DRIVE_STRENGTH_2MA);
+ pio_sm_set_pins_with_mask(pio0,
+ uart_tx_sm, 1U << UART_TX_PIN, 1U << UART_TX_PIN);
+ pio_sm_set_consecutive_pindirs(pio0,
+ uart_tx_sm, UART_TX_PIN, 1, false);
+ pio_sm_set_enabled(pio0, uart_rx_sm, true);
+}
+
+void
+uart_leave_rx(void)
+{
+ /* disable rx to not receive when we send */
+ pio_sm_set_enabled(pio0, uart_rx_sm, false);
+ pio_sm_set_consecutive_pindirs(pio0,
+ uart_tx_sm, UART_TX_PIN, 1, true);
+ pio_sm_set_pins_with_mask(UART_TX_PIN,
+ uart_tx_sm, 0, 1U << UART_TX_PIN);
+ gpio_set_drive_strength(UART_TX_PIN, GPIO_DRIVE_STRENGTH_12MA);
+ pio_sm_restart(pio0, uart_tx_sm);
+ pio_sm_set_enabled(pio0, uart_tx_sm, true);
+}
+
+int
+uart_sync_rx(void)
+{
+ uint32_t start_ms;
+ bool empty;
+
+ start_ms = board_millis();
+ do {
+ empty = pio_sm_is_rx_fifo_empty(pio0, uart_rx_sm);
+ if (!empty) break;
+ tud_task();
+ } while (board_millis() < start_ms + UART_TIMEOUT);
+
+ return empty;
+}
+
+int
+uart_sync_tx(void)
+{
+ uint32_t start_ms;
+ bool full;
+
+ start_ms = board_millis();
+ do {
+ full = pio_sm_is_tx_fifo_full(pio0, uart_tx_sm);
+ if (!full) break;
+ tud_task();
+ } while (board_millis() < start_ms + UART_TIMEOUT);
+
+ return full;
+}
+
+bool
+uart_recv(uint8_t *data, uint len)
+{
+ uint recv;
+
+ recv = 0;
+ while (recv < len) {
+ if (pio_sm_is_rx_fifo_empty(pio0, uart_rx_sm)) {
+ if (uart_sync_rx()) break;
+ }
+ *data++ = *(uint8_t*)((uintptr_t)&pio0->rxf[uart_rx_sm] + 3);
+ recv++;
+ }
+
+ return recv == len;
+}
+
+bool
+uart_send(const uint8_t *data, uint len)
+{
+ uint sent;
+
+ uart_leave_rx();
+ sent = 0;
+ while (sent < len) {
+ if (pio_sm_is_tx_fifo_full(pio0, uart_tx_sm)) {
+ if (uart_sync_tx()) break;
+ }
+ pio_sm_put(pio0, uart_tx_sm, *data++);
+ sent++;
+ }
+ uart_enter_rx();
+
+ return sent == len;
+}
+
+void
+irq_rx_cmd(uint8_t cmd)
{
char buf[64];
uint8_t c;
@@ -35,20 +246,20 @@ split_rx_cmd(uint8_t cmd)
switch (cmd) {
case CMD_SCAN_MATRIX_REQ:
- if (SPLIT_ROLE != SLAVE);
+ if (SPLIT_ROLE != SLAVE)
break;
scan_pending = true;
return;
case CMD_SCAN_MATRIX_RESP:
- if (SPLIT_ROLE != MASTER);
+ if (SPLIT_ROLE != MASTER)
break;
scan_pending = false;
return;
case CMD_STDIO_PUTS:
- if (SPLIT_ROLE != MASTER);
+ if (SPLIT_ROLE != MASTER)
break;
len = 0;
- while (uart_is_readable(uart0) && (c = uart_getc(uart0))) {
+ while (uart_recv(&c, 1) && c) {
if (len == ARRLEN(buf)) {
tud_cdc_write(buf, len);
buf[0] = c;
@@ -68,51 +279,53 @@ split_rx_cmd(uint8_t cmd)
}
void
-split_rx(void)
+irq_rx(void)
{
- while (uart_is_readable(uart0))
- split_rx_cmd(uart_getc(uart0));
+ uint8_t cmd;
+
+ while (uart_is_readable(uart0)) {
+ uart_recv(&cmd, 1);
+ irq_rx_cmd(cmd);
+ }
}
void
split_init(void)
{
- uart_init(uart0, 2400);
-
- gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);
- gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);
-
- uart_set_baudrate(uart0, 115200);
-
- uart_set_hw_flow(uart0, false, false);
+ uart_tx_init();
+ uart_rx_init();
- uart_set_format(uart0, 8, 1, UART_PARITY_NONE);
+ pio_set_irq0_source_enabled(pio0,
+ pis_sm0_rx_fifo_not_empty + uart_rx_sm, true);
+ pio_set_irq0_source_enabled(pio0,
+ pis_sm0_tx_fifo_not_full + uart_tx_sm, true);
+ pio_set_irq0_source_enabled(pio0, pis_interrupt0, true);
- uart_set_fifo_enabled(uart0, true);
+ irq_set_priority(PIO0_IRQ_0, PICO_HIGHEST_IRQ_PRIORITY);
+ irq_set_exclusive_handler(PIO0_IRQ_0, irq_rx);
+ irq_set_enabled(PIO0_IRQ_0, true);
- irq_set_exclusive_handler(UART0_IRQ, split_rx);
- irq_set_enabled(UART0_IRQ, true);
+ uart_enter_rx();
- uart_set_irq_enables(uart0, true, false);
}
void
split_task(void)
{
uint32_t start_ms;
+ uint8_t cmd;
if (SPLIT_ROLE == SLAVE) {
if (scan_pending) {
scan_matrix();
- uart_putc_raw(uart0, CMD_SCAN_MATRIX_RESP);
+ cmd = CMD_SCAN_MATRIX_RESP;
+ uart_send(&cmd, 1);
scan_pending = false;
}
- uart_putc_raw(uart0, CMD_STDIO_PUTS);
- uart_putc_raw(uart0, '.');
- uart_putc_raw(uart0, 0);
} else if (SPLIT_ROLE == MASTER) {
scan_pending = true;
- uart_putc_raw(uart0, CMD_SCAN_MATRIX_REQ);
+ cmd = CMD_SCAN_MATRIX_REQ;
+ uart_send(&cmd, 1);
scan_matrix(); /* scan our side in parallel */
start_ms = board_millis();
while (scan_pending && board_millis() < start_ms + 50)