Add Doxygen, clang-format, cppcheck, and Gitea CI
Some checks failed
CI / Build firmware (push) Failing after 2m1s
CI / Check formatting (push) Successful in 5s
CI / Static analysis (push) Failing after 5s
CI / Build documentation (push) Successful in 4s

Doxygen:
- Doxyfile: minimal config, HTML output to docs/, no LaTeX
- @file/@brief on all source files, full @param/@return on public API
- docs/ added to .gitignore

clang-format (14, Linux brace style, 4-space, column 100):
- .clang-format added
- Applied to entire codebase; this commit is the canonical baseline
- just format rewrites in-place; just format-check is the CI gate

cppcheck (--enable=warning,style,performance,portability):
- Linker-symbol pointer comparisons in startup.c suppressed with
  inline cppcheck-suppress (false positives, not real bugs)
- just lint runs cppcheck; zero warnings required to pass

Dockerfile gains clang-format, cppcheck, doxygen packages so all
tools run inside the existing container -- host stays clean.

Gitea Actions (.gitea/workflows/ci.yml):
- Four parallel jobs: build, format, lint, docs
- All jobs use the same Dockerfile-based image
- Doxygen job fails on any warning line in output
This commit is contained in:
Krzysztof Cieślik
2026-05-21 23:07:05 +02:00
parent 0e348414f8
commit 39a89036cc
15 changed files with 460 additions and 213 deletions

View File

@@ -1,6 +1,27 @@
/**
* @file fhss.h
* @brief FHSS channel sequencer based on AES-128-ECB.
*
* Both link endpoints derive the same hopping sequence independently from a
* shared 128-bit key and a monotonically increasing slot counter. No
* synchronisation traffic is required as long as both sides start from the
* same slot.
*/
#pragma once
#include <stdint.h>
void fhss_init(void);
uint8_t fhss_next_channel(void); /* next channel from AES-ECB PRNG sequence */
void fhss_sync_tick(void); /* call every FHSS_DWELL_MS ms */
/** @brief Reset the slot counter to zero. */
void fhss_init(void);
/**
* @brief Return the next channel in the hopping sequence.
*
* Encrypts the current slot counter big-endian with AES-128-ECB, returns
* @c block[0] % 40, and advances the slot counter.
*
* @return Channel index in [0, 39].
*/
uint8_t fhss_next_channel(void);
/** @brief Advance the slot counter without transmitting (receiver side). */
void fhss_sync_tick(void);

View File

@@ -1,4 +1,16 @@
/**
* @file power.h
* @brief Power management: DC/DC regulator, GPIOTE wakeup, SYSTEM_ON sleep.
*/
#pragma once
/** @brief Enable the DC/DC converter and configure GPIOTE wakeup on the PTT button. */
void power_init(void);
void power_sleep_until_button(void); /* SYSTEM_ON WFI, woken by GPIOTE event */
/**
* @brief Enter SYSTEM_ON low-power sleep and return on the next GPIOTE event.
*
* Sets TASKS_LOWPWR then executes WFI. The CPU wakes when the GPIOTE
* interrupt fires (button press) and resumes from here.
*/
void power_sleep_until_button(void);

View File

@@ -1,7 +1,30 @@
/**
* @file radio.h
* @brief RADIO peripheral driver -- NRF_1Mbit proprietary mode.
*/
#pragma once
#include <stdint.h>
void radio_init(void);
void radio_set_channel(uint8_t ch); /* 0-39, maps to 2402-2480 MHz */
void radio_tx(const uint8_t *data, uint8_t len);
void radio_tx_burst(void); /* TX with FHSS hopping */
/** @brief Configure the RADIO peripheral (mode, packet format, address, CRC, power, channel). */
void radio_init(void);
/**
* @brief Set the RF channel.
* @param ch Channel index 0-39, maps to 2400+ch MHz (MAP=0).
*/
void radio_set_channel(uint8_t ch);
/**
* @brief Transmit one packet synchronously.
*
* Loads @p data into the internal packet buffer, asserts TASKS_TXEN, and
* returns after EVENTS_END fires. The RADIO is DISABLED automatically via
* the END_DISABLE shortcut before the function returns.
*
* @param data Payload bytes.
* @param len Payload length (0-255 bytes).
*/
void radio_tx(const uint8_t *data, uint8_t len);
/** @brief Transmit a burst with FHSS hopping (not yet implemented). */
void radio_tx_burst(void);

View File

@@ -1,154 +1,155 @@
/**
* @file regs.h
* @brief Hardware register bitfield unions for nRF52840 peripherals.
*
* Layout is guaranteed correct only with arm-none-eabi-gcc (LSB-first
* bitfields). Bit ranges match nRF52840 Product Specification v1.7.
*/
#pragma once
#include <stdint.h>
/*
* Hardware register bitfield unions for nRF52840 peripherals.
* Layout is guaranteed correct only with arm-none-eabi-gcc (LSB-first bitfields).
* Bit ranges match nRF52840 Product Specification v1.7.
*/
/* GPIOTE */
/* CONFIG[n]: channel configuration */
/** @brief GPIOTE CONFIG[n]: channel configuration register. */
typedef union {
struct {
uint32_t MODE : 2; /* [1:0] 0=Disabled 1=Event 3=Task */
uint32_t : 6; /* [7:2] reserved */
uint32_t PSEL : 5; /* [12:8] pin number within port */
uint32_t PORT : 1; /* [13] 0=Port0 1=Port1 */
uint32_t : 2; /* [15:14] reserved */
uint32_t POLARITY : 2; /* [17:16] 0=None 1=LoToHi 2=HiToLo 3=Toggle */
uint32_t : 2; /* [19:18] reserved */
uint32_t OUTINIT : 1; /* [20] initial output value for Task mode */
uint32_t : 11; /* [31:21] reserved */
uint32_t MODE : 2; /* [1:0] 0=Disabled 1=Event 3=Task */
uint32_t : 6; /* [7:2] reserved */
uint32_t PSEL : 5; /* [12:8] pin number within port */
uint32_t PORT : 1; /* [13] 0=Port0 1=Port1 */
uint32_t : 2; /* [15:14] reserved */
uint32_t POLARITY : 2; /* [17:16] 0=None 1=LoToHi 2=HiToLo 3=Toggle */
uint32_t : 2; /* [19:18] reserved */
uint32_t OUTINIT : 1; /* [20] initial output value for Task mode */
uint32_t : 11; /* [31:21] reserved */
} bit;
uint32_t reg;
} gpiote_config_t;
#define GPIOTE_MODE_DISABLED 0u
#define GPIOTE_MODE_EVENT 1u
#define GPIOTE_MODE_TASK 3u
#define GPIOTE_POL_NONE 0u
#define GPIOTE_POL_LOTOHI 1u
#define GPIOTE_POL_HITOLO 2u
#define GPIOTE_POL_TOGGLE 3u
#define GPIOTE_MODE_DISABLED 0u
#define GPIOTE_MODE_EVENT 1u
#define GPIOTE_MODE_TASK 3u
#define GPIOTE_POL_NONE 0u
#define GPIOTE_POL_LOTOHI 1u
#define GPIOTE_POL_HITOLO 2u
#define GPIOTE_POL_TOGGLE 3u
/* INTENSET / INTENCLR: interrupt enable */
/** @brief GPIOTE INTENSET / INTENCLR: interrupt enable register. */
typedef union {
struct {
uint32_t IN0 : 1; /* [0] channel 0 input event */
uint32_t IN1 : 1; /* [1] channel 1 input event */
uint32_t IN2 : 1; /* [2] */
uint32_t IN3 : 1; /* [3] */
uint32_t IN4 : 1; /* [4] */
uint32_t IN5 : 1; /* [5] */
uint32_t IN6 : 1; /* [6] */
uint32_t IN7 : 1; /* [7] */
uint32_t : 23; /* [30:8] reserved */
uint32_t PORT : 1; /* [31] PORT event */
uint32_t IN0 : 1; /* [0] channel 0 input event */
uint32_t IN1 : 1; /* [1] channel 1 input event */
uint32_t IN2 : 1; /* [2] */
uint32_t IN3 : 1; /* [3] */
uint32_t IN4 : 1; /* [4] */
uint32_t IN5 : 1; /* [5] */
uint32_t IN6 : 1; /* [6] */
uint32_t IN7 : 1; /* [7] */
uint32_t : 23; /* [30:8] reserved */
uint32_t PORT : 1; /* [31] PORT event */
} bit;
uint32_t reg;
} gpiote_inten_t;
/* RADIO */
/* FREQUENCY: radio channel */
/** @brief RADIO FREQUENCY: RF channel selection register. */
typedef union {
struct {
uint32_t FREQUENCY : 7; /* [6:0] offset from base frequency in MHz */
uint32_t : 1; /* [7] reserved */
uint32_t MAP : 1; /* [8] 0: base=2400 MHz 1: base=2360 MHz */
uint32_t : 23; /* [31:9] reserved */
uint32_t FREQUENCY : 7; /* [6:0] offset from base frequency in MHz */
uint32_t : 1; /* [7] reserved */
uint32_t MAP : 1; /* [8] 0: base=2400 MHz 1: base=2360 MHz */
uint32_t : 23; /* [31:9] reserved */
} bit;
uint32_t reg;
} radio_frequency_t;
#define RADIO_MAP_DEFAULT 0u /* channel n -> 2400+n MHz */
#define RADIO_MAP_BLE 1u /* channel n -> 2360+n MHz */
#define RADIO_MAP_DEFAULT 0u /* channel n -> 2400+n MHz */
#define RADIO_MAP_BLE 1u /* channel n -> 2360+n MHz */
/* TXPOWER: transmit power */
/** @brief RADIO TXPOWER: transmit power register. */
typedef union {
struct {
int32_t TXPOWER : 8; /* [7:0] signed dBm: +8, +7, +6, +5, +4, +3, +2,
0, -4, -8, -12, -16, -20, -40 */
uint32_t : 24; /* [31:8] reserved */
int32_t TXPOWER : 8; /* [7:0] signed dBm: +8, +7, +6, +5, +4, +3, +2,
0, -4, -8, -12, -16, -20, -40 */
uint32_t : 24; /* [31:8] reserved */
} bit;
uint32_t reg;
} radio_txpower_t;
/* MODE: radio data rate and modulation */
/** @brief RADIO MODE: data rate and modulation register. */
typedef union {
struct {
uint32_t MODE : 4; /* [3:0] 0=NRF_1Mbit 1=NRF_2Mbit 4=BLE_1Mbit ... */
uint32_t : 28; /* [31:4] reserved */
uint32_t MODE : 4; /* [3:0] 0=NRF_1Mbit 1=NRF_2Mbit 4=BLE_1Mbit ... */
uint32_t : 28; /* [31:4] reserved */
} bit;
uint32_t reg;
} radio_mode_t;
#define RADIO_MODE_NRF_1MBIT 0u
#define RADIO_MODE_NRF_2MBIT 1u
#define RADIO_MODE_BLE_1MBIT 4u
#define RADIO_MODE_NRF_1MBIT 0u
#define RADIO_MODE_NRF_2MBIT 1u
#define RADIO_MODE_BLE_1MBIT 4u
/* PCNF0: packet configuration register 0 */
/** @brief RADIO PCNF0: packet configuration register 0 (header fields). */
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 */
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 */
/** @brief RADIO PCNF1: packet configuration register 1 (payload and address). */
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 */
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 */
/** @brief RADIO CRCCNF: CRC configuration register. */
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 */
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
#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 */
/** @brief RADIO SHORTS: hardware 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 */
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;