Initial bare-metal foundation for nRF52840 PTT-FHSS
- Fully open toolchain: arm-none-eabi-gcc inside Podman/Docker container - Platform-agnostic build via justfile (auto-detects podman vs docker) - CMake + Ninja build system with arm-none-eabi toolchain file - nRF52840 linker script and startup with full 64-entry vector table - Register access via typed bitfield unions (include/regs.h) - FHSS channel sequencer: AES-128-ECB PRNG over 40 channels (2402-2480 MHz) - Low-power sleep via SYSTEM_ON WFI + GPIOTE wakeup on button press - Vendor deps as shallow git submodules: nrfx, CMSIS_5, tiny-AES-c
This commit is contained in:
41
src/fhss.c
Normal file
41
src/fhss.c
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "fhss.h"
|
||||
#include <aes.h>
|
||||
|
||||
#define FHSS_CHANNELS 40u
|
||||
#define FHSS_DWELL_MS 2u
|
||||
|
||||
/* TODO: replace with a real shared secret before deployment */
|
||||
static const uint8_t shared_key[16] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static uint32_t slot;
|
||||
|
||||
void fhss_init(void)
|
||||
{
|
||||
slot = 0u;
|
||||
}
|
||||
|
||||
uint8_t fhss_next_channel(void)
|
||||
{
|
||||
uint8_t block[16] = {0};
|
||||
struct AES_ctx ctx;
|
||||
|
||||
/* encode slot counter big-endian into the AES input block */
|
||||
block[0] = (uint8_t)(slot >> 24);
|
||||
block[1] = (uint8_t)(slot >> 16);
|
||||
block[2] = (uint8_t)(slot >> 8);
|
||||
block[3] = (uint8_t)(slot );
|
||||
|
||||
AES_init_ctx(&ctx, shared_key);
|
||||
AES_ECB_encrypt(&ctx, block); /* encrypts block in-place */
|
||||
|
||||
slot++;
|
||||
return block[0] % FHSS_CHANNELS;
|
||||
}
|
||||
|
||||
void fhss_sync_tick(void)
|
||||
{
|
||||
slot++;
|
||||
}
|
||||
15
src/main.c
Normal file
15
src/main.c
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "radio.h"
|
||||
#include "fhss.h"
|
||||
#include "power.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
power_init();
|
||||
radio_init();
|
||||
fhss_init();
|
||||
|
||||
while (1) {
|
||||
power_sleep_until_button();
|
||||
radio_tx_burst();
|
||||
}
|
||||
}
|
||||
37
src/power.c
Normal file
37
src/power.c
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "power.h"
|
||||
#include "regs.h"
|
||||
#include <nrf52840.h>
|
||||
#include <cmsis_gcc.h>
|
||||
|
||||
/* P0.02 on XIAO BLE - adjust to match your schematic */
|
||||
#define BUTTON_PIN 2u
|
||||
|
||||
void power_init(void)
|
||||
{
|
||||
/* DC/DC converter has lower quiescent current than the LDO */
|
||||
NRF_POWER->DCDCEN = 1u;
|
||||
|
||||
NRF_GPIOTE->CONFIG[0] = (gpiote_config_t){
|
||||
.bit = {
|
||||
.MODE = GPIOTE_MODE_EVENT,
|
||||
.PSEL = BUTTON_PIN,
|
||||
.PORT = 0u,
|
||||
.POLARITY = GPIOTE_POL_LOTOHI,
|
||||
}
|
||||
}.reg;
|
||||
|
||||
NRF_GPIOTE->INTENSET = (gpiote_inten_t){ .bit.IN0 = 1u }.reg;
|
||||
|
||||
NVIC_EnableIRQ(GPIOTE_IRQn);
|
||||
}
|
||||
|
||||
void power_sleep_until_button(void)
|
||||
{
|
||||
NRF_POWER->TASKS_LOWPWR = 1u;
|
||||
__WFI();
|
||||
}
|
||||
|
||||
void GPIOTE_IRQHandler(void)
|
||||
{
|
||||
NRF_GPIOTE->EVENTS_IN[0] = 0u;
|
||||
}
|
||||
27
src/radio.c
Normal file
27
src/radio.c
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "radio.h"
|
||||
#include "fhss.h"
|
||||
#include "regs.h"
|
||||
#include <nrf52840.h>
|
||||
|
||||
void radio_init(void)
|
||||
{
|
||||
/* TODO: configure RADIO peripheral (MODE, PCNF0/1, BASE/PREFIX, CRC) */
|
||||
}
|
||||
|
||||
void radio_set_channel(uint8_t ch)
|
||||
{
|
||||
NRF_RADIO->FREQUENCY = (radio_frequency_t){
|
||||
.bit = { .FREQUENCY = ch, .MAP = RADIO_MAP_DEFAULT }
|
||||
}.reg;
|
||||
}
|
||||
|
||||
void radio_tx(const uint8_t *data, uint8_t len)
|
||||
{
|
||||
(void)data; (void)len;
|
||||
/* TODO: load packet, enable TX, wait for END event, disable */
|
||||
}
|
||||
|
||||
void radio_tx_burst(void)
|
||||
{
|
||||
/* TODO: hop + TX loop driven by fhss_next_channel() */
|
||||
}
|
||||
168
src/startup.c
Normal file
168
src/startup.c
Normal file
@@ -0,0 +1,168 @@
|
||||
#include <stdint.h>
|
||||
|
||||
/* linker script symbols */
|
||||
extern uint32_t _sidata; /* load address of .data in Flash */
|
||||
extern uint32_t _sdata; /* start of .data in RAM */
|
||||
extern uint32_t _edata; /* end of .data in RAM */
|
||||
extern uint32_t _sbss;
|
||||
extern uint32_t _ebss;
|
||||
extern uint32_t _estack; /* address equals initial SP value */
|
||||
|
||||
extern int main(void);
|
||||
|
||||
void Reset_Handler(void);
|
||||
|
||||
static void __attribute__((used)) Default_Handler(void)
|
||||
{
|
||||
while (1);
|
||||
}
|
||||
|
||||
/* ARM Cortex-M4 core exceptions */
|
||||
void NMI_Handler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void HardFault_Handler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void MemManage_Handler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void BusFault_Handler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void UsageFault_Handler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void SVC_Handler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void DebugMon_Handler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void PendSV_Handler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void SysTick_Handler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
|
||||
/* nRF52840 peripheral IRQs (IRQ0-IRQ47) */
|
||||
void POWER_CLOCK_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void RADIO_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void UARTE0_UART0_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void NFCT_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void GPIOTE_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void SAADC_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void TIMER0_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void TIMER1_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void TIMER2_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void RTC0_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void TEMP_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void RNG_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void ECB_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void CCM_AAR_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void WDT_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void RTC1_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void QDEC_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void COMP_LPCOMP_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void SWI0_EGU0_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void SWI1_EGU1_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void SWI2_EGU2_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void SWI3_EGU3_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void SWI4_EGU4_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void SWI5_EGU5_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void TIMER3_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void TIMER4_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void PWM0_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void PDM_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void MWU_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void PWM1_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void PWM2_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void SPIM2_SPIS2_SPI2_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void RTC2_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void I2S_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void FPU_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void USBD_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void UARTE1_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void QSPI_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void CRYPTOCELL_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void PWM3_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
void SPIM3_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||
|
||||
/* vector table: 64 entries (16 core + 48 IRQs) */
|
||||
|
||||
typedef void (*vector_fn)(void);
|
||||
|
||||
__attribute__((section(".isr_vector"), used))
|
||||
const vector_fn vectors[64] = {
|
||||
/* 0 */ (vector_fn)&_estack,
|
||||
/* 1 */ Reset_Handler,
|
||||
/* 2 */ NMI_Handler,
|
||||
/* 3 */ HardFault_Handler,
|
||||
/* 4 */ MemManage_Handler,
|
||||
/* 5 */ BusFault_Handler,
|
||||
/* 6 */ UsageFault_Handler,
|
||||
/* 7 */ 0,
|
||||
/* 8 */ 0,
|
||||
/* 9 */ 0,
|
||||
/* 10 */ 0,
|
||||
/* 11 */ SVC_Handler,
|
||||
/* 12 */ DebugMon_Handler,
|
||||
/* 13 */ 0,
|
||||
/* 14 */ PendSV_Handler,
|
||||
/* 15 */ SysTick_Handler,
|
||||
|
||||
/* IRQ0 */ POWER_CLOCK_IRQHandler,
|
||||
/* IRQ1 */ RADIO_IRQHandler,
|
||||
/* IRQ2 */ UARTE0_UART0_IRQHandler,
|
||||
/* IRQ3 */ SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler,
|
||||
/* IRQ4 */ SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler,
|
||||
/* IRQ5 */ NFCT_IRQHandler,
|
||||
/* IRQ6 */ GPIOTE_IRQHandler,
|
||||
/* IRQ7 */ SAADC_IRQHandler,
|
||||
/* IRQ8 */ TIMER0_IRQHandler,
|
||||
/* IRQ9 */ TIMER1_IRQHandler,
|
||||
/* IRQ10 */ TIMER2_IRQHandler,
|
||||
/* IRQ11 */ RTC0_IRQHandler,
|
||||
/* IRQ12 */ TEMP_IRQHandler,
|
||||
/* IRQ13 */ RNG_IRQHandler,
|
||||
/* IRQ14 */ ECB_IRQHandler,
|
||||
/* IRQ15 */ CCM_AAR_IRQHandler,
|
||||
/* IRQ16 */ WDT_IRQHandler,
|
||||
/* IRQ17 */ RTC1_IRQHandler,
|
||||
/* IRQ18 */ QDEC_IRQHandler,
|
||||
/* IRQ19 */ COMP_LPCOMP_IRQHandler,
|
||||
/* IRQ20 */ SWI0_EGU0_IRQHandler,
|
||||
/* IRQ21 */ SWI1_EGU1_IRQHandler,
|
||||
/* IRQ22 */ SWI2_EGU2_IRQHandler,
|
||||
/* IRQ23 */ SWI3_EGU3_IRQHandler,
|
||||
/* IRQ24 */ SWI4_EGU4_IRQHandler,
|
||||
/* IRQ25 */ SWI5_EGU5_IRQHandler,
|
||||
/* IRQ26 */ TIMER3_IRQHandler,
|
||||
/* IRQ27 */ TIMER4_IRQHandler,
|
||||
/* IRQ28 */ PWM0_IRQHandler,
|
||||
/* IRQ29 */ PDM_IRQHandler,
|
||||
/* IRQ30 */ 0, /* reserved */
|
||||
/* IRQ31 */ 0, /* reserved */
|
||||
/* IRQ32 */ MWU_IRQHandler,
|
||||
/* IRQ33 */ PWM1_IRQHandler,
|
||||
/* IRQ34 */ PWM2_IRQHandler,
|
||||
/* IRQ35 */ SPIM2_SPIS2_SPI2_IRQHandler,
|
||||
/* IRQ36 */ RTC2_IRQHandler,
|
||||
/* IRQ37 */ I2S_IRQHandler,
|
||||
/* IRQ38 */ FPU_IRQHandler,
|
||||
/* IRQ39 */ USBD_IRQHandler,
|
||||
/* IRQ40 */ UARTE1_IRQHandler,
|
||||
/* IRQ41 */ QSPI_IRQHandler,
|
||||
/* IRQ42 */ CRYPTOCELL_IRQHandler,
|
||||
/* IRQ43 */ 0, /* reserved */
|
||||
/* IRQ44 */ 0, /* reserved */
|
||||
/* IRQ45 */ PWM3_IRQHandler,
|
||||
/* IRQ46 */ 0, /* reserved */
|
||||
/* IRQ47 */ SPIM3_IRQHandler,
|
||||
};
|
||||
|
||||
/* Reset_Handler */
|
||||
|
||||
void Reset_Handler(void)
|
||||
{
|
||||
/* copy .data initializers from Flash to RAM */
|
||||
uint32_t *src = &_sidata;
|
||||
uint32_t *dst = &_sdata;
|
||||
while (dst < &_edata) {
|
||||
*dst++ = *src++;
|
||||
}
|
||||
|
||||
/* zero .bss */
|
||||
dst = &_sbss;
|
||||
while (dst < &_ebss) {
|
||||
*dst++ = 0u;
|
||||
}
|
||||
|
||||
main();
|
||||
while (1);
|
||||
}
|
||||
Reference in New Issue
Block a user