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:
Krzysztof Cieślik
2026-05-21 20:20:21 +02:00
commit 438fca0ace
20 changed files with 739 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
build/

9
.gitmodules vendored Normal file
View File

@@ -0,0 +1,9 @@
[submodule "vendor/nrfx"]
path = vendor/nrfx
url = https://github.com/NordicSemiconductor/nrfx.git
[submodule "vendor/CMSIS_5"]
path = vendor/CMSIS_5
url = https://github.com/ARM-software/CMSIS_5.git
[submodule "vendor/tiny-aes-c"]
path = vendor/tiny-aes-c
url = https://github.com/kokke/tiny-AES-c.git

71
CMakeLists.txt Normal file
View File

@@ -0,0 +1,71 @@
cmake_minimum_required(VERSION 3.22)
# must be set before project()
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/cmake/arm-none-eabi.cmake")
project(ptt_fhss C ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
# sources
add_executable(firmware
src/startup.c
src/main.c
src/radio.c
src/fhss.c
src/power.c
vendor/tiny-aes-c/aes.c
)
# include paths
target_include_directories(firmware PRIVATE
include
vendor/nrfx/mdk # nRF52840 device headers (nrf52840.h etc.)
vendor/CMSIS_5/CMSIS/Core/Include # core_cm4.h, cmsis_gcc.h etc.
vendor/tiny-aes-c
)
# preprocessor defines
target_compile_definitions(firmware PRIVATE
NRF52840_XXAA # device variant required by nrfx/mdk headers
)
# compiler flags
set(CPU_FLAGS
-mcpu=cortex-m4
-mthumb
-mfpu=fpv4-sp-d16
-mfloat-abi=hard
)
target_compile_options(firmware PRIVATE
${CPU_FLAGS}
-Os
-ffunction-sections
-fdata-sections
-fno-exceptions
-Wall
-Wextra
-Werror
)
# linker
target_link_options(firmware PRIVATE
${CPU_FLAGS}
-T ${CMAKE_SOURCE_DIR}/link/nrf52840.ld
-Wl,--gc-sections
-Wl,--print-memory-usage
-nostartfiles
-specs=nano.specs
-specs=nosys.specs
)
# post-build: .hex + size report
add_custom_command(TARGET firmware POST_BUILD
COMMAND arm-none-eabi-objcopy
-O ihex $<TARGET_FILE:firmware>
${CMAKE_BINARY_DIR}/firmware.hex
COMMAND arm-none-eabi-size $<TARGET_FILE:firmware>
VERBATIM
)

11
Dockerfile Normal file
View File

@@ -0,0 +1,11 @@
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc-arm-none-eabi \
binutils-arm-none-eabi \
libnewlib-arm-none-eabi \
cmake \
ninja-build \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /src

123
README.md Normal file
View File

@@ -0,0 +1,123 @@
# ptt-fhss
Bare-metal PTT (Push-to-Talk) firmware for the **Seeed XIAO BLE (nRF52840)** with
frequency-hopping spread spectrum (FHSS) on the 2.4 GHz band.
Designed to be low-power, hard to detect, and built with a fully open toolchain -
no Zephyr, no Nordic SDK, no SoftDevice.
## Hardware
| Component | Part |
|-----------|------|
| Target | Seeed XIAO BLE (nRF52840) |
| Programmer | RP2040 running DAPLink |
## How it works
### FHSS
The radio hops across 40 channels (2402-2480 MHz, 2 MHz steps) every 2 ms.
The hopping sequence is generated by AES-128-ECB keyed with a shared secret -
both devices derive identical sequences independently without any synchronisation
traffic. To an observer with an SDR the transmissions appear as short, scattered
impulses with no identifiable pattern.
### Low power
The nRF52840 stays in SYSTEM_ON sleep (`WFI`) with the DC/DC converter active
(~1.5 uA quiescent). A GPIOTE edge event on the PTT button wakes the CPU;
the radio is active only during the burst.
### Register access
All hardware register writes use typed bitfield unions defined in `include/regs.h`
rather than raw bit-shift expressions. The union layout is guaranteed correct with
`arm-none-eabi-gcc` (LSB-first bitfields on Cortex-M).
## Toolchain
Everything that produces the binary runs inside a container. The host only needs
tools that talk to hardware (pyocd) or manage the build (just).
| Tool | Purpose | Install |
|------|---------|---------|
| podman or docker | Container runtime | distro package |
| just | Task runner | `cargo install just` or distro package |
| pyocd | Flash / debug via DAPLink | `pip install pyocd` |
| git | Source control + submodules | distro package |
Inside the container: `debian:bookworm-slim` + `gcc-arm-none-eabi` from apt +
`cmake` + `ninja`.
## Quick start
```sh
git clone <repo-url> ptt
cd ptt
git submodule update --init --depth=1
just build # build firmware.hex inside container
just flash # flash via DAPLink (host pyocd)
```
On first run `just build` pulls the container image and compiles. Subsequent
builds reuse the cached image and only recompile changed files.
## Tasks
```
just build compile firmware inside the container
just flash build + flash via DAPLink
just gdbserver start pyocd GDB server on port 3333
just clean remove build/
just clean-all remove build/ and the container image
```
## Project structure
```
.
|-- Dockerfile container image (debian + arm-gcc from apt)
|-- justfile build / flash / debug tasks
|-- CMakeLists.txt
|-- cmake/
| \-- arm-none-eabi.cmake CMake toolchain file
|-- link/
| \-- nrf52840.ld linker script (no SoftDevice, Flash @ 0x00000000)
|-- include/
| |-- regs.h hardware register bitfield unions
| |-- radio.h
| |-- fhss.h
| \-- power.h
|-- src/
| |-- startup.c vector table (64 entries) + Reset_Handler
| |-- main.c
| |-- radio.c RADIO peripheral driver (stub)
| |-- fhss.c AES-ECB hopping sequence generator
| \-- power.c DC/DC, GPIOTE wakeup, WFI sleep
\-- vendor/
|-- nrfx/ Nordic HAL headers, pinned to v2.9.0 (includes mdk/)
|-- CMSIS_5/ ARM core headers (core_cm4.h etc.)
\-- tiny-aes-c/ Public domain AES-128 implementation
```
## Before first flash
The XIAO BLE ships with a SoftDevice which occupies the start of Flash.
Erase it before flashing bare-metal firmware:
```sh
pyocd erase --target nrf52840 --chip
```
## Status
| Module | State |
|--------|-------|
| Startup / vector table | done |
| Linker script | done |
| Power management (sleep + wakeup) | done |
| FHSS sequence generator | done |
| RADIO driver | stub - TX loop not yet implemented |
| Sync protocol | not started |

16
cmake/arm-none-eabi.cmake Normal file
View File

@@ -0,0 +1,16 @@
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
set(CMAKE_ASM_COMPILER arm-none-eabi-gcc)
set(CMAKE_OBJCOPY arm-none-eabi-objcopy)
set(CMAKE_SIZE arm-none-eabi-size)
# prevent cmake from link-testing the compiler against the host
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
# do not search host paths for libraries or headers
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

6
include/fhss.h Normal file
View File

@@ -0,0 +1,6 @@
#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 */

4
include/power.h Normal file
View File

@@ -0,0 +1,4 @@
#pragma once
void power_init(void);
void power_sleep_until_button(void); /* SYSTEM_ON WFI, woken by GPIOTE event */

7
include/radio.h Normal file
View File

@@ -0,0 +1,7 @@
#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 */

90
include/regs.h Normal file
View File

@@ -0,0 +1,90 @@
#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 */
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 */
} 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
/* INTENSET / INTENCLR: interrupt enable */
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 */
} bit;
uint32_t reg;
} gpiote_inten_t;
/* RADIO */
/* FREQUENCY: radio channel */
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 */
} 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 */
/* TXPOWER: transmit power */
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 */
} bit;
uint32_t reg;
} radio_txpower_t;
/* MODE: radio data rate and modulation */
typedef union {
struct {
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

44
justfile Normal file
View File

@@ -0,0 +1,44 @@
# variables
image := "ptt-builder"
build_dir := "build"
# podman takes priority over docker
engine := `command -v podman 2>/dev/null \
|| command -v docker 2>/dev/null \
|| { echo "error: podman or docker required" >&2; exit 1; }`
# user-namespace mapping: podman rootless vs docker
user_ns := `command -v podman >/dev/null 2>&1 \
&& echo "--userns=keep-id" \
|| echo "--user $(id -u):$(id -g)"`
# recipes
# build container image (only rebuilt when Dockerfile changes)
image-build:
{{engine}} build -t {{image}} .
# compile firmware inside the container
build: image-build
{{engine}} run --rm \
{{user_ns}} \
-v "{{justfile_directory()}}:/src:z" \
{{image}} \
sh -c "cmake -B /src/{{build_dir}} -G Ninja \
-DCMAKE_BUILD_TYPE=MinSizeRel /src \
&& ninja -C /src/{{build_dir}}"
# flash firmware via pyocd on the host (requires USB / DAPLink)
flash: build
pyocd flash --target nrf52840 {{build_dir}}/firmware.hex
# start GDB server for debugging
gdbserver:
pyocd gdbserver --target nrf52840 --port 3333
# remove build artifacts
clean:
rm -rf {{build_dir}}
# remove build artifacts AND the container image
clean-all: clean
{{engine}} rmi {{image}} 2>/dev/null || true

66
link/nrf52840.ld Normal file
View File

@@ -0,0 +1,66 @@
/*
* nRF52840 - no SoftDevice
* Flash: 1 MB @ 0x00000000
* RAM: 256 KB @ 0x20000000
*
* Before first flash, erase the entire chip to remove any SoftDevice:
* pyocd erase --target nrf52840 --chip
*/
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 1024K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K
}
/* stack grows downward from the top of RAM */
_estack = ORIGIN(RAM) + LENGTH(RAM);
ENTRY(Reset_Handler)
SECTIONS
{
/* vector table must be at the start of Flash */
.isr_vector :
{
KEEP(*(.isr_vector))
. = ALIGN(4);
} > FLASH
/* code and read-only data */
.text :
{
*(.text .text.*)
*(.rodata .rodata.*)
. = ALIGN(4);
_etext = .;
} > FLASH
/* load address of .data in Flash - Reset_Handler copies from here */
_sidata = LOADADDR(.data);
/* initialized variables: stored in Flash, run from RAM */
.data :
{
_sdata = .;
*(.data .data.*)
. = ALIGN(4);
_edata = .;
} > RAM AT > FLASH
/* zero-initialized variables: Reset_Handler clears this region */
.bss (NOLOAD) :
{
_sbss = .;
*(.bss .bss.*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} > RAM
/DISCARD/ :
{
*(.ARM.exidx*)
*(.gnu.linkonce.armexidx.*)
}
}

41
src/fhss.c Normal file
View 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
View 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
View 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
View 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
View 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);
}

1
vendor/CMSIS_5 vendored Submodule

Submodule vendor/CMSIS_5 added at 55b19837f5

1
vendor/nrfx vendored Submodule

Submodule vendor/nrfx added at 16756cadac

1
vendor/tiny-aes-c vendored Submodule

Submodule vendor/tiny-aes-c added at 23856752fb