Add Doxygen, clang-format, cppcheck, and Gitea CI
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:
15
.clang-format
Normal file
15
.clang-format
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
IndentWidth: 4
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: Never
|
||||||
|
ColumnLimit: 100
|
||||||
|
AlignConsecutiveBitFields: true
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowShortFunctionsOnASingleLine: None
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
BreakBeforeBraces: Linux
|
||||||
|
SortIncludes: false
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
99
.gitea/workflows/ci.yml
Normal file
99
.gitea/workflows/ci.yml
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE: ptt-builder
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build firmware
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Build container image
|
||||||
|
run: docker build -t $IMAGE .
|
||||||
|
|
||||||
|
- name: Compile firmware
|
||||||
|
run: |
|
||||||
|
docker run --rm \
|
||||||
|
--user "$(id -u):$(id -g)" \
|
||||||
|
-v "$PWD:/src" \
|
||||||
|
$IMAGE \
|
||||||
|
sh -c "cmake -B /src/build -G Ninja \
|
||||||
|
-DCMAKE_BUILD_TYPE=MinSizeRel /src \
|
||||||
|
&& ninja -C /src/build"
|
||||||
|
|
||||||
|
- name: Print size
|
||||||
|
run: |
|
||||||
|
docker run --rm \
|
||||||
|
-v "$PWD:/src" \
|
||||||
|
$IMAGE \
|
||||||
|
arm-none-eabi-size /src/build/firmware
|
||||||
|
|
||||||
|
format:
|
||||||
|
name: Check formatting
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build container image
|
||||||
|
run: docker build -t $IMAGE .
|
||||||
|
|
||||||
|
- name: clang-format check
|
||||||
|
run: |
|
||||||
|
docker run --rm \
|
||||||
|
--user "$(id -u):$(id -g)" \
|
||||||
|
-v "$PWD:/src" \
|
||||||
|
$IMAGE \
|
||||||
|
sh -c "find /src/src /src/include -name '*.c' -o -name '*.h' | \
|
||||||
|
xargs clang-format --dry-run --Werror \
|
||||||
|
--style=file:/src/.clang-format"
|
||||||
|
|
||||||
|
lint:
|
||||||
|
name: Static analysis
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build container image
|
||||||
|
run: docker build -t $IMAGE .
|
||||||
|
|
||||||
|
- name: cppcheck
|
||||||
|
run: |
|
||||||
|
docker run --rm \
|
||||||
|
--user "$(id -u):$(id -g)" \
|
||||||
|
-v "$PWD:/src" \
|
||||||
|
$IMAGE \
|
||||||
|
sh -c "cppcheck --error-exitcode=1 \
|
||||||
|
--enable=warning,style,performance,portability \
|
||||||
|
--suppress=missingInclude \
|
||||||
|
--inline-suppr \
|
||||||
|
--std=c11 \
|
||||||
|
-I /src/include \
|
||||||
|
/src/src/"
|
||||||
|
|
||||||
|
docs:
|
||||||
|
name: Build documentation
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build container image
|
||||||
|
run: docker build -t $IMAGE .
|
||||||
|
|
||||||
|
- name: Doxygen
|
||||||
|
run: |
|
||||||
|
docker run --rm \
|
||||||
|
--user "$(id -u):$(id -g)" \
|
||||||
|
-v "$PWD:/src" \
|
||||||
|
$IMAGE \
|
||||||
|
sh -c "cd /src && doxygen Doxyfile 2>&1 | tee /tmp/doxy.log && \
|
||||||
|
! grep -q 'warning:' /tmp/doxy.log"
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
build/
|
build/
|
||||||
|
docs/
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
libnewlib-arm-none-eabi \
|
libnewlib-arm-none-eabi \
|
||||||
cmake \
|
cmake \
|
||||||
ninja-build \
|
ninja-build \
|
||||||
|
clang-format \
|
||||||
|
cppcheck \
|
||||||
|
doxygen \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|||||||
21
Doxyfile
Normal file
21
Doxyfile
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
PROJECT_NAME = "ptt-fhss"
|
||||||
|
PROJECT_BRIEF = "Bare-metal PTT firmware for nRF52840 with FHSS"
|
||||||
|
PROJECT_NUMBER =
|
||||||
|
OUTPUT_DIRECTORY = docs
|
||||||
|
INPUT = include src
|
||||||
|
FILE_PATTERNS = *.h *.c
|
||||||
|
RECURSIVE = NO
|
||||||
|
EXTRACT_ALL = YES
|
||||||
|
EXTRACT_STATIC = YES
|
||||||
|
QUIET = YES
|
||||||
|
WARNINGS = YES
|
||||||
|
WARN_IF_UNDOCUMENTED = YES
|
||||||
|
WARN_NO_PARAMDOC = YES
|
||||||
|
GENERATE_HTML = YES
|
||||||
|
HTML_OUTPUT = html
|
||||||
|
HTML_TIMESTAMP = NO
|
||||||
|
GENERATE_LATEX = NO
|
||||||
|
HAVE_DOT = NO
|
||||||
|
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||||
|
JAVADOC_AUTOBRIEF = YES
|
||||||
|
PREDEFINED = NRF52840_XXAA
|
||||||
@@ -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
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/** @brief Reset the slot counter to zero. */
|
||||||
void fhss_init(void);
|
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 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);
|
||||||
|
|||||||
@@ -1,4 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @file power.h
|
||||||
|
* @brief Power management: DC/DC regulator, GPIOTE wakeup, SYSTEM_ON sleep.
|
||||||
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
/** @brief Enable the DC/DC converter and configure GPIOTE wakeup on the PTT button. */
|
||||||
void power_init(void);
|
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);
|
||||||
|
|||||||
@@ -1,7 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* @file radio.h
|
||||||
|
* @brief RADIO peripheral driver -- NRF_1Mbit proprietary mode.
|
||||||
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/** @brief Configure the RADIO peripheral (mode, packet format, address, CRC, power, channel). */
|
||||||
void radio_init(void);
|
void radio_init(void);
|
||||||
void radio_set_channel(uint8_t ch); /* 0-39, maps to 2402-2480 MHz */
|
|
||||||
|
/**
|
||||||
|
* @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);
|
void radio_tx(const uint8_t *data, uint8_t len);
|
||||||
void radio_tx_burst(void); /* TX with FHSS hopping */
|
|
||||||
|
/** @brief Transmit a burst with FHSS hopping (not yet implemented). */
|
||||||
|
void radio_tx_burst(void);
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @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
|
#pragma once
|
||||||
#include <stdint.h>
|
#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 */
|
/* GPIOTE */
|
||||||
|
|
||||||
/* CONFIG[n]: channel configuration */
|
/** @brief GPIOTE CONFIG[n]: channel configuration register. */
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
uint32_t MODE : 2; /* [1:0] 0=Disabled 1=Event 3=Task */
|
uint32_t MODE : 2; /* [1:0] 0=Disabled 1=Event 3=Task */
|
||||||
@@ -33,7 +34,7 @@ typedef union {
|
|||||||
#define GPIOTE_POL_HITOLO 2u
|
#define GPIOTE_POL_HITOLO 2u
|
||||||
#define GPIOTE_POL_TOGGLE 3u
|
#define GPIOTE_POL_TOGGLE 3u
|
||||||
|
|
||||||
/* INTENSET / INTENCLR: interrupt enable */
|
/** @brief GPIOTE INTENSET / INTENCLR: interrupt enable register. */
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
uint32_t IN0 : 1; /* [0] channel 0 input event */
|
uint32_t IN0 : 1; /* [0] channel 0 input event */
|
||||||
@@ -52,7 +53,7 @@ typedef union {
|
|||||||
|
|
||||||
/* RADIO */
|
/* RADIO */
|
||||||
|
|
||||||
/* FREQUENCY: radio channel */
|
/** @brief RADIO FREQUENCY: RF channel selection register. */
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
uint32_t FREQUENCY : 7; /* [6:0] offset from base frequency in MHz */
|
uint32_t FREQUENCY : 7; /* [6:0] offset from base frequency in MHz */
|
||||||
@@ -66,7 +67,7 @@ typedef union {
|
|||||||
#define RADIO_MAP_DEFAULT 0u /* channel n -> 2400+n MHz */
|
#define RADIO_MAP_DEFAULT 0u /* channel n -> 2400+n MHz */
|
||||||
#define RADIO_MAP_BLE 1u /* channel n -> 2360+n MHz */
|
#define RADIO_MAP_BLE 1u /* channel n -> 2360+n MHz */
|
||||||
|
|
||||||
/* TXPOWER: transmit power */
|
/** @brief RADIO TXPOWER: transmit power register. */
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
int32_t TXPOWER : 8; /* [7:0] signed dBm: +8, +7, +6, +5, +4, +3, +2,
|
int32_t TXPOWER : 8; /* [7:0] signed dBm: +8, +7, +6, +5, +4, +3, +2,
|
||||||
@@ -76,7 +77,7 @@ typedef union {
|
|||||||
uint32_t reg;
|
uint32_t reg;
|
||||||
} radio_txpower_t;
|
} radio_txpower_t;
|
||||||
|
|
||||||
/* MODE: radio data rate and modulation */
|
/** @brief RADIO MODE: data rate and modulation register. */
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
uint32_t MODE : 4; /* [3:0] 0=NRF_1Mbit 1=NRF_2Mbit 4=BLE_1Mbit ... */
|
uint32_t MODE : 4; /* [3:0] 0=NRF_1Mbit 1=NRF_2Mbit 4=BLE_1Mbit ... */
|
||||||
@@ -89,7 +90,7 @@ typedef union {
|
|||||||
#define RADIO_MODE_NRF_2MBIT 1u
|
#define RADIO_MODE_NRF_2MBIT 1u
|
||||||
#define RADIO_MODE_BLE_1MBIT 4u
|
#define RADIO_MODE_BLE_1MBIT 4u
|
||||||
|
|
||||||
/* PCNF0: packet configuration register 0 */
|
/** @brief RADIO PCNF0: packet configuration register 0 (header fields). */
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
uint32_t LFLEN : 4; /* [3:0] length of LENGTH field in bits */
|
uint32_t LFLEN : 4; /* [3:0] length of LENGTH field in bits */
|
||||||
@@ -106,7 +107,7 @@ typedef union {
|
|||||||
uint32_t reg;
|
uint32_t reg;
|
||||||
} radio_pcnf0_t;
|
} radio_pcnf0_t;
|
||||||
|
|
||||||
/* PCNF1: packet configuration register 1 */
|
/** @brief RADIO PCNF1: packet configuration register 1 (payload and address). */
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
uint32_t MAXLEN : 8; /* [7:0] maximum payload length in bytes */
|
uint32_t MAXLEN : 8; /* [7:0] maximum payload length in bytes */
|
||||||
@@ -120,7 +121,7 @@ typedef union {
|
|||||||
uint32_t reg;
|
uint32_t reg;
|
||||||
} radio_pcnf1_t;
|
} radio_pcnf1_t;
|
||||||
|
|
||||||
/* CRCCNF: CRC configuration */
|
/** @brief RADIO CRCCNF: CRC configuration register. */
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
uint32_t LEN : 2; /* [1:0] 0=disabled 1=1 byte 2=2 bytes 3=3 bytes */
|
uint32_t LEN : 2; /* [1:0] 0=disabled 1=1 byte 2=2 bytes 3=3 bytes */
|
||||||
@@ -136,7 +137,7 @@ typedef union {
|
|||||||
#define RADIO_CRCCNF_LEN_TWO 2u
|
#define RADIO_CRCCNF_LEN_TWO 2u
|
||||||
#define RADIO_CRCCNF_LEN_THREE 3u
|
#define RADIO_CRCCNF_LEN_THREE 3u
|
||||||
|
|
||||||
/* SHORTS: shortcut register */
|
/** @brief RADIO SHORTS: hardware shortcut register. */
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
uint32_t READY_START : 1; /* [0] READY -> TASKS_START */
|
uint32_t READY_START : 1; /* [0] READY -> TASKS_START */
|
||||||
|
|||||||
46
justfile
46
justfile
@@ -12,6 +12,8 @@ user_ns := `command -v podman >/dev/null 2>&1 \
|
|||||||
&& echo "--userns=keep-id" \
|
&& echo "--userns=keep-id" \
|
||||||
|| echo "--user $(id -u):$(id -g)"`
|
|| echo "--user $(id -u):$(id -g)"`
|
||||||
|
|
||||||
|
src_files := "find /src/src /src/include -name '*.c' -o -name '*.h'"
|
||||||
|
|
||||||
# recipes
|
# recipes
|
||||||
# build container image (only rebuilt when Dockerfile changes)
|
# build container image (only rebuilt when Dockerfile changes)
|
||||||
image-build:
|
image-build:
|
||||||
@@ -27,6 +29,44 @@ build: image-build
|
|||||||
-DCMAKE_BUILD_TYPE=MinSizeRel /src \
|
-DCMAKE_BUILD_TYPE=MinSizeRel /src \
|
||||||
&& ninja -C /src/{{build_dir}}"
|
&& ninja -C /src/{{build_dir}}"
|
||||||
|
|
||||||
|
# reformat all source files in-place with clang-format
|
||||||
|
format: image-build
|
||||||
|
{{engine}} run --rm \
|
||||||
|
{{user_ns}} \
|
||||||
|
-v "{{justfile_directory()}}:/src:z" \
|
||||||
|
{{image}} \
|
||||||
|
sh -c "{{src_files}} | xargs clang-format -i --style=file:/src/.clang-format"
|
||||||
|
|
||||||
|
# check formatting without modifying files (used in CI)
|
||||||
|
format-check: image-build
|
||||||
|
{{engine}} run --rm \
|
||||||
|
{{user_ns}} \
|
||||||
|
-v "{{justfile_directory()}}:/src:z" \
|
||||||
|
{{image}} \
|
||||||
|
sh -c "{{src_files}} | xargs clang-format --dry-run --Werror --style=file:/src/.clang-format"
|
||||||
|
|
||||||
|
# static analysis with cppcheck
|
||||||
|
lint: image-build
|
||||||
|
{{engine}} run --rm \
|
||||||
|
{{user_ns}} \
|
||||||
|
-v "{{justfile_directory()}}:/src:z" \
|
||||||
|
{{image}} \
|
||||||
|
sh -c "cppcheck --error-exitcode=1 \
|
||||||
|
--enable=warning,style,performance,portability \
|
||||||
|
--suppress=missingInclude \
|
||||||
|
--inline-suppr \
|
||||||
|
--std=c11 \
|
||||||
|
-I /src/include \
|
||||||
|
/src/src/"
|
||||||
|
|
||||||
|
# generate HTML documentation with Doxygen
|
||||||
|
docs: image-build
|
||||||
|
{{engine}} run --rm \
|
||||||
|
{{user_ns}} \
|
||||||
|
-v "{{justfile_directory()}}:/src:z" \
|
||||||
|
{{image}} \
|
||||||
|
sh -c "cd /src && doxygen Doxyfile"
|
||||||
|
|
||||||
# flash firmware via pyocd on the host (requires USB / DAPLink)
|
# flash firmware via pyocd on the host (requires USB / DAPLink)
|
||||||
flash: build
|
flash: build
|
||||||
pyocd flash --target nrf52840 {{build_dir}}/firmware.hex
|
pyocd flash --target nrf52840 {{build_dir}}/firmware.hex
|
||||||
@@ -35,10 +75,10 @@ flash: build
|
|||||||
gdbserver:
|
gdbserver:
|
||||||
pyocd gdbserver --target nrf52840 --port 3333
|
pyocd gdbserver --target nrf52840 --port 3333
|
||||||
|
|
||||||
# remove build artifacts
|
# remove build artifacts and generated docs
|
||||||
clean:
|
clean:
|
||||||
rm -rf {{build_dir}}
|
rm -rf {{build_dir}} docs
|
||||||
|
|
||||||
# remove build artifacts AND the container image
|
# remove build artifacts, docs AND the container image
|
||||||
clean-all: clean
|
clean-all: clean
|
||||||
{{engine}} rmi {{image}} 2>/dev/null || true
|
{{engine}} rmi {{image}} 2>/dev/null || true
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
/** @file fhss.c
|
||||||
|
* @brief FHSS channel sequencer implementation.
|
||||||
|
*/
|
||||||
#include "fhss.h"
|
#include "fhss.h"
|
||||||
#include <aes.h>
|
#include <aes.h>
|
||||||
|
|
||||||
@@ -6,8 +9,7 @@
|
|||||||
|
|
||||||
/* TODO: replace with a real shared secret before deployment */
|
/* TODO: replace with a real shared secret before deployment */
|
||||||
static const uint8_t shared_key[16] = {
|
static const uint8_t shared_key[16] = {
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint32_t slot;
|
static uint32_t slot;
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
/** @file main.c
|
||||||
|
* @brief Entry point: initialise peripherals and run the PTT event loop.
|
||||||
|
*/
|
||||||
#include "radio.h"
|
#include "radio.h"
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
/** @file power.c
|
||||||
|
* @brief Power management implementation.
|
||||||
|
*/
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
#include "regs.h"
|
#include "regs.h"
|
||||||
#include <nrf52840.h>
|
#include <nrf52840.h>
|
||||||
@@ -17,8 +20,7 @@ void power_init(void)
|
|||||||
.PSEL = BUTTON_PIN,
|
.PSEL = BUTTON_PIN,
|
||||||
.PORT = 0u,
|
.PORT = 0u,
|
||||||
.POLARITY = GPIOTE_POL_LOTOHI,
|
.POLARITY = GPIOTE_POL_LOTOHI,
|
||||||
}
|
}}.reg;
|
||||||
}.reg;
|
|
||||||
|
|
||||||
NRF_GPIOTE->INTENSET = (gpiote_inten_t){.bit.IN0 = 1u}.reg;
|
NRF_GPIOTE->INTENSET = (gpiote_inten_t){.bit.IN0 = 1u}.reg;
|
||||||
|
|
||||||
|
|||||||
44
src/radio.c
44
src/radio.c
@@ -1,3 +1,6 @@
|
|||||||
|
/** @file radio.c
|
||||||
|
* @brief RADIO peripheral driver implementation.
|
||||||
|
*/
|
||||||
#include "radio.h"
|
#include "radio.h"
|
||||||
#include "regs.h"
|
#include "regs.h"
|
||||||
#include <nrf52840.h>
|
#include <nrf52840.h>
|
||||||
@@ -27,20 +30,17 @@ static uint8_t pkt_buf[BUF_SIZE];
|
|||||||
|
|
||||||
void radio_init(void)
|
void radio_init(void)
|
||||||
{
|
{
|
||||||
NRF_RADIO->MODE = (radio_mode_t){
|
NRF_RADIO->MODE = (radio_mode_t){.bit = {.MODE = RADIO_MODE_NRF_1MBIT}}.reg;
|
||||||
.bit = { .MODE = RADIO_MODE_NRF_1MBIT }
|
|
||||||
}.reg;
|
|
||||||
|
|
||||||
/* 8-bit LENGTH field, no S0/S1, 8-bit preamble, CRC not part of LENGTH */
|
/* 8-bit LENGTH field, no S0/S1, 8-bit preamble, CRC not part of LENGTH */
|
||||||
NRF_RADIO->PCNF0 = (radio_pcnf0_t){
|
NRF_RADIO->PCNF0 =
|
||||||
.bit = { .LFLEN = 8, .S0LEN = 0, .S1LEN = 0, .PLEN = 0, .CRCINC = 0 }
|
(radio_pcnf0_t){.bit = {.LFLEN = 8, .S0LEN = 0, .S1LEN = 0, .PLEN = 0, .CRCINC = 0}}.reg;
|
||||||
}.reg;
|
|
||||||
|
|
||||||
/* max 255-byte payload, 3-byte base address, little-endian, no whitening */
|
/* max 255-byte payload, 3-byte base address, little-endian, no whitening */
|
||||||
NRF_RADIO->PCNF1 = (radio_pcnf1_t){
|
NRF_RADIO->PCNF1 =
|
||||||
.bit = { .MAXLEN = MAX_PAYLOAD, .STATLEN = 0, .BALEN = 3,
|
(radio_pcnf1_t){
|
||||||
.ENDIAN = 0, .WHITEEN = 0 }
|
.bit = {.MAXLEN = MAX_PAYLOAD, .STATLEN = 0, .BALEN = 3, .ENDIAN = 0, .WHITEEN = 0}}
|
||||||
}.reg;
|
.reg;
|
||||||
|
|
||||||
NRF_RADIO->BASE0 = RADIO_BASE0;
|
NRF_RADIO->BASE0 = RADIO_BASE0;
|
||||||
NRF_RADIO->PREFIX0 = RADIO_PREFIX0;
|
NRF_RADIO->PREFIX0 = RADIO_PREFIX0;
|
||||||
@@ -48,33 +48,29 @@ void radio_init(void)
|
|||||||
NRF_RADIO->RXADDRESSES = 1; /* receive on logical address 0 */
|
NRF_RADIO->RXADDRESSES = 1; /* receive on logical address 0 */
|
||||||
|
|
||||||
/* 2-byte CRC, skip address field */
|
/* 2-byte CRC, skip address field */
|
||||||
NRF_RADIO->CRCCNF = (radio_crccnf_t){
|
NRF_RADIO->CRCCNF = (radio_crccnf_t){.bit = {.LEN = RADIO_CRCCNF_LEN_TWO, .SKIPADDR = 1}}.reg;
|
||||||
.bit = { .LEN = RADIO_CRCCNF_LEN_TWO, .SKIPADDR = 1 }
|
|
||||||
}.reg;
|
|
||||||
NRF_RADIO->CRCPOLY = 0x11021u; /* CRC-16/CCITT */
|
NRF_RADIO->CRCPOLY = 0x11021u; /* CRC-16/CCITT */
|
||||||
NRF_RADIO->CRCINIT = 0xFFFFu;
|
NRF_RADIO->CRCINIT = 0xFFFFu;
|
||||||
|
|
||||||
NRF_RADIO->TXPOWER = (radio_txpower_t){
|
NRF_RADIO->TXPOWER =
|
||||||
|
(radio_txpower_t){
|
||||||
.bit = {.TXPOWER = 0} /* 0 dBm */
|
.bit = {.TXPOWER = 0} /* 0 dBm */
|
||||||
}.reg;
|
}
|
||||||
|
.reg;
|
||||||
|
|
||||||
NRF_RADIO->FREQUENCY = (radio_frequency_t){
|
NRF_RADIO->FREQUENCY =
|
||||||
.bit = { .FREQUENCY = DEFAULT_CHANNEL, .MAP = RADIO_MAP_DEFAULT }
|
(radio_frequency_t){.bit = {.FREQUENCY = DEFAULT_CHANNEL, .MAP = RADIO_MAP_DEFAULT}}.reg;
|
||||||
}.reg;
|
|
||||||
|
|
||||||
/* READY -> START and END -> DISABLE shortcuts so CPU only triggers TXEN */
|
/* READY -> START and END -> DISABLE shortcuts so CPU only triggers TXEN */
|
||||||
NRF_RADIO->SHORTS = (radio_shorts_t){
|
NRF_RADIO->SHORTS = (radio_shorts_t){.bit = {.READY_START = 1, .END_DISABLE = 1}}.reg;
|
||||||
.bit = { .READY_START = 1, .END_DISABLE = 1 }
|
|
||||||
}.reg;
|
|
||||||
|
|
||||||
NRF_RADIO->PACKETPTR = (uint32_t)pkt_buf;
|
NRF_RADIO->PACKETPTR = (uint32_t)pkt_buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void radio_set_channel(uint8_t ch)
|
void radio_set_channel(uint8_t ch)
|
||||||
{
|
{
|
||||||
NRF_RADIO->FREQUENCY = (radio_frequency_t){
|
NRF_RADIO->FREQUENCY =
|
||||||
.bit = { .FREQUENCY = ch, .MAP = RADIO_MAP_DEFAULT }
|
(radio_frequency_t){.bit = {.FREQUENCY = ch, .MAP = RADIO_MAP_DEFAULT}}.reg;
|
||||||
}.reg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void radio_tx(const uint8_t *data, uint8_t len)
|
void radio_tx(const uint8_t *data, uint8_t len)
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
/** @file startup.c
|
||||||
|
* @brief Vector table and Reset_Handler for nRF52840 (no SoftDevice).
|
||||||
|
*/
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
/* linker script symbols */
|
/* linker script symbols */
|
||||||
@@ -14,7 +17,8 @@ void Reset_Handler(void);
|
|||||||
|
|
||||||
static void __attribute__((used)) Default_Handler(void)
|
static void __attribute__((used)) Default_Handler(void)
|
||||||
{
|
{
|
||||||
while (1);
|
while (1)
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ARM Cortex-M4 core exceptions */
|
/* ARM Cortex-M4 core exceptions */
|
||||||
@@ -32,8 +36,10 @@ void SysTick_Handler(void) __attribute__((weak, alias("Default_Handler")));
|
|||||||
void POWER_CLOCK_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
void POWER_CLOCK_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||||
void RADIO_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 UARTE0_UART0_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||||
void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void)
|
||||||
void SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
__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 NFCT_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||||
void GPIOTE_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 SAADC_IRQHandler(void) __attribute__((weak, alias("Default_Handler")));
|
||||||
@@ -77,8 +83,7 @@ void SPIM3_IRQHandler(void) __attribute__((weak, al
|
|||||||
|
|
||||||
typedef void (*vector_fn)(void);
|
typedef void (*vector_fn)(void);
|
||||||
|
|
||||||
__attribute__((section(".isr_vector"), used))
|
__attribute__((section(".isr_vector"), used)) const vector_fn vectors[64] = {
|
||||||
const vector_fn vectors[64] = {
|
|
||||||
/* 0 */ (vector_fn)&_estack,
|
/* 0 */ (vector_fn)&_estack,
|
||||||
/* 1 */ Reset_Handler,
|
/* 1 */ Reset_Handler,
|
||||||
/* 2 */ NMI_Handler,
|
/* 2 */ NMI_Handler,
|
||||||
@@ -153,16 +158,19 @@ void Reset_Handler(void)
|
|||||||
/* copy .data initializers from Flash to RAM */
|
/* copy .data initializers from Flash to RAM */
|
||||||
uint32_t *src = &_sidata;
|
uint32_t *src = &_sidata;
|
||||||
uint32_t *dst = &_sdata;
|
uint32_t *dst = &_sdata;
|
||||||
|
// cppcheck-suppress comparePointers
|
||||||
while (dst < &_edata) {
|
while (dst < &_edata) {
|
||||||
*dst++ = *src++;
|
*dst++ = *src++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* zero .bss */
|
/* zero .bss */
|
||||||
dst = &_sbss;
|
dst = &_sbss;
|
||||||
|
// cppcheck-suppress comparePointers
|
||||||
while (dst < &_ebss) {
|
while (dst < &_ebss) {
|
||||||
*dst++ = 0u;
|
*dst++ = 0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
while (1);
|
while (1)
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user