From 0e348414f8264d3b45c474b700f399db4117af9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Cie=C5=9Blik?= Date: Thu, 21 May 2026 22:53:32 +0200 Subject: [PATCH] Implement basic radio TX on fixed channel radio_init() configures RADIO in NRF_1Mbit proprietary mode: 8-bit length field, 4-byte address (BASE0+PREFIX0), 2-byte CRC-16/CCITT, 0 dBm TX power, channel 20 (2420 MHz). READY->START and END->DISABLE shortcuts let radio_tx() trigger the full ramp-up/TX/disable sequence by writing TASKS_TXEN once, then polling EVENTS_END. main.c sends a fixed 4-byte test frame on every button press. FHSS and AES remain compiled but are not called. New bitfield unions in regs.h: radio_pcnf0_t, radio_pcnf1_t, radio_crccnf_t, radio_shorts_t. --- include/regs.h | 64 ++++++++++++++++++++++++++++++++++++++ src/main.c | 7 +++-- src/radio.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 146 insertions(+), 8 deletions(-) diff --git a/include/regs.h b/include/regs.h index 4b5ee0b..ff21679 100644 --- a/include/regs.h +++ b/include/regs.h @@ -88,3 +88,67 @@ typedef union { #define RADIO_MODE_NRF_1MBIT 0u #define RADIO_MODE_NRF_2MBIT 1u #define RADIO_MODE_BLE_1MBIT 4u + +/* PCNF0: packet configuration register 0 */ +typedef union { + struct { + uint32_t LFLEN : 4; /* [3:0] length of LENGTH field in bits */ + uint32_t : 4; /* [7:4] reserved */ + uint32_t S0LEN : 1; /* [8] length of S0 field in bytes (0 or 1) */ + uint32_t : 7; /* [15:9] reserved */ + uint32_t S1LEN : 4; /* [19:16] length of S1 field in bits */ + uint32_t S1INCL : 1; /* [20] include S1 field in RAM even if zero length */ + uint32_t : 3; /* [23:21] reserved */ + uint32_t PLEN : 1; /* [24] 0=8-bit preamble 1=16-bit preamble */ + uint32_t : 6; /* [30:25] reserved */ + uint32_t CRCINC : 1; /* [31] include CRC in LENGTH field */ + } bit; + uint32_t reg; +} radio_pcnf0_t; + +/* PCNF1: packet configuration register 1 */ +typedef union { + struct { + uint32_t MAXLEN : 8; /* [7:0] maximum payload length in bytes */ + uint32_t STATLEN : 8; /* [15:8] static length added to payload */ + uint32_t BALEN : 3; /* [18:16] base address length (2-4 bytes) */ + uint32_t : 5; /* [23:19] reserved */ + uint32_t ENDIAN : 1; /* [24] 0=little-endian 1=big-endian */ + uint32_t WHITEEN : 1; /* [25] 1=enable data whitening */ + uint32_t : 6; /* [31:26] reserved */ + } bit; + uint32_t reg; +} radio_pcnf1_t; + +/* CRCCNF: CRC configuration */ +typedef union { + struct { + uint32_t LEN : 2; /* [1:0] 0=disabled 1=1 byte 2=2 bytes 3=3 bytes */ + uint32_t : 6; /* [7:2] reserved */ + uint32_t SKIPADDR : 1; /* [8] 1=skip address field in CRC calculation */ + uint32_t : 23; /* [31:9] reserved */ + } bit; + uint32_t reg; +} radio_crccnf_t; + +#define RADIO_CRCCNF_LEN_DISABLED 0u +#define RADIO_CRCCNF_LEN_ONE 1u +#define RADIO_CRCCNF_LEN_TWO 2u +#define RADIO_CRCCNF_LEN_THREE 3u + +/* SHORTS: shortcut register */ +typedef union { + struct { + uint32_t READY_START : 1; /* [0] READY -> TASKS_START */ + uint32_t END_DISABLE : 1; /* [1] END -> TASKS_DISABLE */ + uint32_t DISABLED_TXEN : 1; /* [2] DISABLED -> TASKS_TXEN */ + uint32_t DISABLED_RXEN : 1; /* [3] DISABLED -> TASKS_RXEN */ + uint32_t ADDRESS_RSSISTART : 1; /* [4] ADDRESS -> TASKS_RSSISTART */ + uint32_t END_START : 1; /* [5] END -> TASKS_START */ + uint32_t ADDRESS_BCSTART : 1; /* [6] ADDRESS -> TASKS_BCSTART */ + uint32_t : 1; /* [7] reserved */ + uint32_t DISABLED_RSSISTOP : 1; /* [8] DISABLED -> TASKS_RSSISTOP */ + uint32_t : 23; /* [31:9] reserved */ + } bit; + uint32_t reg; +} radio_shorts_t; diff --git a/src/main.c b/src/main.c index f77b10e..de9c36d 100644 --- a/src/main.c +++ b/src/main.c @@ -1,15 +1,16 @@ #include "radio.h" -#include "fhss.h" #include "power.h" +#include + +static const uint8_t test_frame[] = { 0xDE, 0xAD, 0xBE, 0xEF }; int main(void) { power_init(); radio_init(); - fhss_init(); while (1) { power_sleep_until_button(); - radio_tx_burst(); + radio_tx(test_frame, sizeof(test_frame)); } } diff --git a/src/radio.c b/src/radio.c index 5a26f54..64549a2 100644 --- a/src/radio.c +++ b/src/radio.c @@ -1,11 +1,73 @@ #include "radio.h" -#include "fhss.h" #include "regs.h" #include +#include + +/* + * Packet buffer layout (S0=1B, LENGTH=8-bit, S1=0, payload up to 255B): + * [0] LENGTH - payload byte count (written by radio_tx) + * [1..1+len] payload - caller-supplied data + * + * RADIO is configured for NRF_1Mbit proprietary mode, fixed channel, + * no data whitening, 2-byte CRC over payload only. + * TX is synchronous: function returns after EVENTS_END fires. + */ + +#define MAX_PAYLOAD 255u +#define BUF_SIZE (1u + MAX_PAYLOAD) + +static uint8_t pkt_buf[BUF_SIZE]; + +/* Logical channel 20 -> 2400 + 20 = 2420 MHz (MAP=0) */ +#define DEFAULT_CHANNEL 20u + +/* 4-byte base address (3-byte BALEN field means 3+1=4 total address bytes) */ +#define RADIO_BASE0 0x12345678u +#define RADIO_PREFIX0 0xABu /* logical address 0: RADIO_BASE0 + RADIO_PREFIX0[7:0] */ void radio_init(void) { - /* TODO: configure RADIO peripheral (MODE, PCNF0/1, BASE/PREFIX, CRC) */ + NRF_RADIO->MODE = (radio_mode_t){ + .bit = { .MODE = RADIO_MODE_NRF_1MBIT } + }.reg; + + /* 8-bit LENGTH field, no S0/S1, 8-bit preamble, CRC not part of LENGTH */ + NRF_RADIO->PCNF0 = (radio_pcnf0_t){ + .bit = { .LFLEN = 8, .S0LEN = 0, .S1LEN = 0, .PLEN = 0, .CRCINC = 0 } + }.reg; + + /* max 255-byte payload, 3-byte base address, little-endian, no whitening */ + NRF_RADIO->PCNF1 = (radio_pcnf1_t){ + .bit = { .MAXLEN = MAX_PAYLOAD, .STATLEN = 0, .BALEN = 3, + .ENDIAN = 0, .WHITEEN = 0 } + }.reg; + + NRF_RADIO->BASE0 = RADIO_BASE0; + NRF_RADIO->PREFIX0 = RADIO_PREFIX0; + NRF_RADIO->TXADDRESS = 0; /* transmit on logical address 0 */ + NRF_RADIO->RXADDRESSES = 1; /* receive on logical address 0 */ + + /* 2-byte CRC, skip address field */ + NRF_RADIO->CRCCNF = (radio_crccnf_t){ + .bit = { .LEN = RADIO_CRCCNF_LEN_TWO, .SKIPADDR = 1 } + }.reg; + NRF_RADIO->CRCPOLY = 0x11021u; /* CRC-16/CCITT */ + NRF_RADIO->CRCINIT = 0xFFFFu; + + NRF_RADIO->TXPOWER = (radio_txpower_t){ + .bit = { .TXPOWER = 0 } /* 0 dBm */ + }.reg; + + NRF_RADIO->FREQUENCY = (radio_frequency_t){ + .bit = { .FREQUENCY = DEFAULT_CHANNEL, .MAP = RADIO_MAP_DEFAULT } + }.reg; + + /* READY -> START and END -> DISABLE shortcuts so CPU only triggers TXEN */ + NRF_RADIO->SHORTS = (radio_shorts_t){ + .bit = { .READY_START = 1, .END_DISABLE = 1 } + }.reg; + + NRF_RADIO->PACKETPTR = (uint32_t)pkt_buf; } void radio_set_channel(uint8_t ch) @@ -17,11 +79,22 @@ void radio_set_channel(uint8_t ch) void radio_tx(const uint8_t *data, uint8_t len) { - (void)data; (void)len; - /* TODO: load packet, enable TX, wait for END event, disable */ + /* len is uint8_t so it cannot exceed MAX_PAYLOAD=255 by type guarantee */ + pkt_buf[0] = len; + memcpy(&pkt_buf[1], data, len); + + NRF_RADIO->EVENTS_END = 0; + NRF_RADIO->TASKS_TXEN = 1; + + /* Busy-wait for END event (ramp-up ~40us + TX time, total <1ms for short frames) */ + while (!NRF_RADIO->EVENTS_END) + ; + + NRF_RADIO->EVENTS_END = 0; + /* RADIO is now DISABLED via the END_DISABLE shortcut */ } void radio_tx_burst(void) { - /* TODO: hop + TX loop driven by fhss_next_channel() */ + /* placeholder: FHSS TX loop not yet implemented */ }