From b2cf5e4ba45c80ced4bce3bb5f0f1e53f9a02d1c Mon Sep 17 00:00:00 2001 From: Joseph Shetaye Date: Sat, 11 Jan 2025 15:04:14 -0800 Subject: [PATCH] Finish radio driver & task --- src/drivers/rfm9x.c | 64 +++++++++++++- src/drivers/rfm9x.h | 19 ++++- src/pins.h | 1 + src/scheduler/scheduler.c | 1 - src/slate.h | 7 ++ src/state_machine/tasks/radio_task.c | 120 ++++++++++++++++++++------- 6 files changed, 178 insertions(+), 34 deletions(-) diff --git a/src/drivers/rfm9x.c b/src/drivers/rfm9x.c index a6a7a00..51e9a4b 100644 --- a/src/drivers/rfm9x.c +++ b/src/drivers/rfm9x.c @@ -7,13 +7,15 @@ #include rfm9x_t rfm9x_mk(spi_inst_t *spi, uint reset_pin, uint cs_pin, uint spi_tx_pin, - uint spi_rx_pin, uint spi_clk_pin) + uint spi_rx_pin, uint spi_clk_pin, uint d0_pin, rfm9x_interrupt_func interrupt_func) { rfm9x_t r = {.reset_pin = reset_pin, .spi_cs_pin = cs_pin, .spi_tx_pin = spi_tx_pin, .spi_rx_pin = spi_rx_pin, .spi_clk_pin = spi_clk_pin, + .d0_pin = d0_pin, + .interrupt_func = interrupt_func, .spi = spi, /* * Default values @@ -583,6 +585,7 @@ uint8_t rfm9x_get_lna_boost(rfm9x_t *r) return c; } + void rfm9x_init(rfm9x_t *r) { // Setup reset line @@ -600,7 +603,12 @@ void rfm9x_init(rfm9x_t *r) gpio_set_function(r->spi_clk_pin, GPIO_FUNC_SPI); gpio_set_function(r->spi_tx_pin, GPIO_FUNC_SPI); gpio_set_function(r->spi_rx_pin, GPIO_FUNC_SPI); - gpio_set_function(17, GPIO_FUNC_SPI); + gpio_set_function(17, GPIO_FUNC_SPI); // ??? + + // Setup interrupt line + gpio_init(r->d0_pin); + gpio_set_dir(r->d0_pin, GPIO_IN); + gpio_pull_down(r->d0_pin); // Initialize SPI for the RFM9X @@ -675,6 +683,13 @@ void rfm9x_init(rfm9x_t *r) rfm9x_set_lna_boost(r, 0b11); ASSERT(rfm9x_get_lna_boost(r) == 0b11); + + // Setup interrupt + if(r->interrupt_func != NULL) + { + gpio_set_irq_enabled_with_callback(r->d0_pin, GPIO_IRQ_EDGE_RISE, + true, r->interrupt_func); + } } /* @@ -745,7 +760,7 @@ uint8_t rfm9x_rx_done(rfm9x_t *r) } } -uint8_t rfm9x_await_rx(rfm9x_t *r) +int rfm9x_await_rx(rfm9x_t *r) { rfm9x_listen(r); while (!rfm9x_rx_done(r)) @@ -753,6 +768,49 @@ uint8_t rfm9x_await_rx(rfm9x_t *r) return 1; } +uint8_t rfm9x_packet_to_fifo(rfm9x_t *r, uint8_t *buf, uint8_t n) { + uint8_t old_mode = rfm9x_get_mode(r); + rfm9x_set_mode(r, STANDBY_MODE); + + rfm9x_put8(r, _RH_RF95_REG_0D_FIFO_ADDR_PTR, 0x00); + + rfm9x_put_buf(r, _RH_RF95_REG_00_FIFO, buf, n); + rfm9x_put8(r, _RH_RF95_REG_22_PAYLOAD_LENGTH, n); + + rfm9x_set_mode(r, old_mode); + return 0; +} + + +uint8_t rfm9x_packet_from_fifo(rfm9x_t *r, uint8_t *buf) { + uint8_t n_read = 0; + uint8_t old_mode = rfm9x_get_mode(r); + rfm9x_set_mode(r, STANDBY_MODE); + + // Check for CRC error + if(rfm9x_is_crc_enabled(r) && rfm9x_crc_error(r)) { + // TODO report somehow + } else { + uint8_t fifo_length = rfm9x_get8(r, _RH_RF95_REG_13_RX_NB_BYTES); + if(fifo_length > 0) { + uint8_t current_addr = + rfm9x_get8(r, _RH_RF95_REG_10_FIFO_RX_CURRENT_ADDR); + rfm9x_put8(r, _RH_RF95_REG_0D_FIFO_ADDR_PTR, current_addr); + + // read the packet + rfm9x_get_buf(r, _RH_RF95_REG_00_FIFO, buf, + fifo_length); + } + n_read = fifo_length; + } + rfm9x_set_mode(r, old_mode); + return n_read; +} + +void rfm9x_clear_interrupts(rfm9x_t *r) { + rfm9x_put8(r, _RH_RF95_REG_12_IRQ_FLAGS, 0xFF); +} + uint8_t rfm9x_receive_packet(rfm9x_t *r, uint8_t node, char *buf) { while (1) diff --git a/src/drivers/rfm9x.h b/src/drivers/rfm9x.h index a0bd197..40821c8 100644 --- a/src/drivers/rfm9x.h +++ b/src/drivers/rfm9x.h @@ -20,6 +20,8 @@ typedef enum RX_MODE = 5, } rfm9x_mode_t; +typedef void (*rfm9x_interrupt_func)(uint, uint32_t); + typedef struct _rfm9x { uint reset_pin; @@ -27,6 +29,10 @@ typedef struct _rfm9x uint spi_rx_pin; uint spi_tx_pin; uint spi_clk_pin; + uint d0_pin; + + rfm9x_interrupt_func interrupt_func; + spi_inst_t *spi; uint8_t seq; /* current sequence number */ uint32_t high_power : 1, max_power : 1, debug : 1; @@ -36,7 +42,7 @@ typedef struct _rfm9x * Creates an RFM9X helper struct. Uninitialized. */ rfm9x_t rfm9x_mk(spi_inst_t *spi, uint reset_pin, uint cs_pin, uint spi_tx_pin, - uint spi_rx_pin, uint spi_clk_pin); + uint spi_rx_pin, uint spi_clk_pin, uint d0_pin, rfm9x_interrupt_func interrupt_func); /* * Initializes an RFM9X radio. @@ -83,6 +89,17 @@ uint8_t rfm9x_send_ack(rfm9x_t *r, char *data, uint32_t l, uint8_t destination, uint8_t rfm9x_receive(rfm9x_t *r, char *packet, uint8_t node, uint8_t keep_listening, uint8_t with_ack, bool blocking_wait_for_packet); + +void rfm9x_listen(rfm9x_t *r); +void rfm9x_transmit(rfm9x_t *r); + +uint8_t rfm9x_tx_done(rfm9x_t *r); +uint8_t rfm9x_rx_done(rfm9x_t *r); + +uint8_t rfm9x_packet_to_fifo(rfm9x_t *r, uint8_t *buf, uint8_t n); +uint8_t rfm9x_packet_from_fifo(rfm9x_t *r, uint8_t *buf); +void rfm9x_clear_interrupts(rfm9x_t *r); + typedef enum { _RH_RF95_REG_00_FIFO = 0x00, diff --git a/src/pins.h b/src/pins.h index 45b9eb3..0237ede 100644 --- a/src/pins.h +++ b/src/pins.h @@ -12,3 +12,4 @@ #define RFM9X_RX (SPI0_RX) #define RFM9X_RESET (21) #define RFM9X_CS (20) +#define RFM9X_D0 (28) diff --git a/src/scheduler/scheduler.c b/src/scheduler/scheduler.c index 0444761..409c99a 100644 --- a/src/scheduler/scheduler.c +++ b/src/scheduler/scheduler.c @@ -102,7 +102,6 @@ void sched_dispatch(slate_t *slate) task->next_dispatch = make_timeout_time_ms(task->dispatch_period_ms); - LOG_DEBUG("sched: Dispatching task %s", task->name); task->task_dispatch(slate); } } diff --git a/src/slate.h b/src/slate.h index 05c46fb..587036e 100644 --- a/src/slate.h +++ b/src/slate.h @@ -31,6 +31,13 @@ typedef struct samwise_slate * Radio */ rfm9x_t radio; + uint8_t radio_node; queue_t tx_queue; queue_t rx_queue; + uint32_t rx_bytes; + uint32_t rx_packets; + uint32_t rx_backpressure_drops; + uint32_t rx_bad_packet_drops; + uint32_t tx_bytes; + uint32_t tx_packets; } slate_t; diff --git a/src/state_machine/tasks/radio_task.c b/src/state_machine/tasks/radio_task.c index ed09230..52e3022 100644 --- a/src/state_machine/tasks/radio_task.c +++ b/src/state_machine/tasks/radio_task.c @@ -13,34 +13,90 @@ #include "slate.h" #include "pins.h" +#include "drivers/rfm9x.h" + #include "hardware/spi.h" #include "hardware/gpio.h" -const uint RADIO_INTERRUPT_PIN = 28; -const bool ENABLE_IRQ = true; +#include static slate_t* s; int receive(rfm9x_t radio_module); -void interrupt_recieved(uint gpio, uint32_t events) +static void tx_done() { + packet_t p; + if(queue_try_remove(&s->tx_queue, &p)) { + uint8_t p_buf[p.len + 4]; + p_buf[0] = p.dst; + p_buf[1] = p.src; + p_buf[2] = p.seq; + p_buf[3] = p.flags; + memcpy(p_buf + 4, &p.data[0], p.len); + + rfm9x_packet_to_fifo(&s->radio, p_buf, sizeof(p_buf)); + rfm9x_clear_interrupts(&s->radio); + + s->tx_packets++; + s->tx_bytes += sizeof(p_buf); + } else { + // No more TX packets, switch to receive mode + rfm9x_clear_interrupts(&s->radio); + rfm9x_listen(&s->radio); + } +} + +static void rx_done() { + // Copy packet into receive queue and unset interrupt + // TODO: Can we do this faster? + uint8_t p_buf[256]; + packet_t p; + + uint8_t n = rfm9x_packet_from_fifo(&s->radio, &p_buf[0]); + s->rx_bytes += n; + + if(n > 0) { + if(n < 4) { + s->rx_bad_packet_drops++; + } else { + p.dst = p_buf[0]; + p.src = p_buf[1]; + p.seq = p_buf[2]; + p.flags = p_buf[3]; + p.len = n - 4; + memcpy(&p.data[0], p_buf + 4, n - 4); + + if((p.dst == _RH_BROADCAST_ADDRESS || p.dst == s->radio_node)) { + s->rx_packets++; + + if(!queue_try_add(&s->rx_queue, &p)) s->rx_backpressure_drops++; + } + } + } + rfm9x_clear_interrupts(&s->radio); +} + +static void interrupt_received(uint gpio, uint32_t events) { - LOG_INFO("Interrupt received on pin %d\n", gpio); - if (gpio == RADIO_INTERRUPT_PIN) + if (gpio == RFM9X_D0) { - LOG_INFO("Radio interrupt received\n"); - //receive(radio_module); - - receive(s->radio); + + if(rfm9x_tx_done(&s->radio)) tx_done(); + else if(rfm9x_rx_done(&s->radio)) rx_done(); } } void radio_task_init(slate_t *slate) { s = slate; - gpio_init(RADIO_INTERRUPT_PIN); - gpio_set_dir(RADIO_INTERRUPT_PIN, GPIO_IN); - gpio_pull_down(RADIO_INTERRUPT_PIN); + + slate->rx_bytes = 0; + slate->rx_packets = 0; + slate->rx_backpressure_drops = 0; + slate->rx_bad_packet_drops = 0; + + slate->tx_bytes = 0; + slate->tx_packets = 0; // transmit queue queue_init(&slate->tx_queue, sizeof(packet_t), TX_QUEUE_SIZE); @@ -49,39 +105,45 @@ void radio_task_init(slate_t *slate) queue_init(&slate->rx_queue, sizeof(packet_t), RX_QUEUE_SIZE); // create the radio here - slate->radio = rfm9x_mk(RFM9X_SPI, RFM9X_RESET, RFM9X_CS, RFM9X_TX, RFM9X_RX, RFM9X_CLK); + slate->radio = rfm9x_mk( + RFM9X_SPI, + RFM9X_RESET, + RFM9X_CS, + RFM9X_TX, + RFM9X_RX, + RFM9X_CLK, + RFM9X_D0, + &interrupt_received + ); // initialize the radio here rfm9x_init(&slate->radio); - gpio_set_irq_enabled_with_callback(RADIO_INTERRUPT_PIN, GPIO_IRQ_EDGE_RISE, - ENABLE_IRQ, &interrupt_recieved); + // Switch to receive mode + rfm9x_listen(&slate->radio); LOG_INFO("Brought up RFM9X v%d", rfm9x_version(&slate->radio)); } -// When it sees something in the transmit queue, switches into recieve mode and +// When it sees something in the transmit queue, switches into transmit mode and // send a packet. Otherwise, be in recieve mode. When it recieves a packet, it // inturrupts the CPU to immediately recieve. void radio_task_dispatch(slate_t *slate) { - -} - -// screen /dev/tty.usbmodem1101 -int receive(rfm9x_t radio_module) -{ - char data[256]; - uint8_t n = rfm9x_receive(&radio_module, &data[0], 1, 0, 0, 1); - printf("Received %d\n", n); - - bool interruptPin = gpio_get(RADIO_INTERRUPT_PIN); - printf("Interrupt pin: %d\n", interruptPin); + // Switch to transmit mode if queue is not empty + if(!queue_is_empty(&slate->tx_queue)) { + rfm9x_transmit(&slate->radio); + // Since the interrupt only fires when done transmitting the last packet, we need + // to get it started manually + tx_done(); + } else { + rfm9x_receive(&slate->radio); + } } sched_task_t radio_task = {.name = "radio", - .dispatch_period_ms = 1000, + .dispatch_period_ms = 100, .task_init = &radio_task_init, .task_dispatch = &radio_task_dispatch,