summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'firmware')
-rw-r--r--firmware/Makefile58
-rw-r--r--firmware/README.txt53
-rw-r--r--firmware/inc/bsp.h18
-rw-r--r--firmware/inc/cmd.h74
-rw-r--r--firmware/inc/driver/device.h25
-rw-r--r--firmware/inc/driver/f1956.h41
-rw-r--r--firmware/inc/driver/mcp3202.h82
-rw-r--r--firmware/inc/driver/mcp4725.h48
-rw-r--r--firmware/inc/driver/radio_config.h680
-rw-r--r--firmware/inc/driver/si4468.h54
-rw-r--r--firmware/inc/module/drain.h37
-rw-r--r--firmware/inc/module/gate.h32
-rw-r--r--firmware/inc/peripheral/ring.h77
-rw-r--r--firmware/inc/peripheral/spi.h34
-rw-r--r--firmware/inc/peripheral/timer.h22
-rw-r--r--firmware/inc/peripheral/twi.h35
-rw-r--r--firmware/inc/peripheral/uart.h56
-rw-r--r--firmware/inc/setup.h39
-rw-r--r--firmware/inc/status.h83
-rw-r--r--firmware/inc/util.h46
-rw-r--r--firmware/src/bsp.c38
-rw-r--r--firmware/src/cmd.c27
-rw-r--r--firmware/src/driver/f1956.c61
-rw-r--r--firmware/src/driver/mcp3202.c129
-rw-r--r--firmware/src/driver/mcp4725.c101
-rw-r--r--firmware/src/driver/si4468.c201
-rw-r--r--firmware/src/main.c343
-rw-r--r--firmware/src/module/drain.c123
-rw-r--r--firmware/src/module/gate.c78
-rw-r--r--firmware/src/peripheral/ring.c115
-rw-r--r--firmware/src/peripheral/spi.c106
-rw-r--r--firmware/src/peripheral/timer.c57
-rw-r--r--firmware/src/peripheral/twi.c114
-rw-r--r--firmware/src/peripheral/uart.c239
-rw-r--r--firmware/src/status.c46
-rw-r--r--firmware/src/util.c152
36 files changed, 3524 insertions, 0 deletions
diff --git a/firmware/Makefile b/firmware/Makefile
new file mode 100644
index 0000000..6045e7e
--- /dev/null
+++ b/firmware/Makefile
@@ -0,0 +1,58 @@
+all: compile
+
+SHELL := /bin/bash
+
+MCU = atmega328p
+PROG = USBasp
+AVRDUDE = avrdude.exe
+
+LFUSE = 0xFF
+HFUSE = 0xD9
+EFUSE = 0xFF
+LOCK = 0xFF
+
+SCRIPTS_DIR = ../scripts
+SCRIPT_RESET = reset.py
+
+SRC_DIR = src
+INC_DIR = inc
+
+PYTHON = python.exe
+CC = avr-gcc
+CFLAGS = -mmcu=$(MCU) -Wall -Os -I$(INC_DIR)
+OBJ_C = avr-objcopy
+AVR_SIZE = avr-size
+
+SRC_FILES += $(SRC_DIR)/*.c
+SRC_FILES += $(SRC_DIR)/peripheral/*.c
+SRC_FILES += $(SRC_DIR)/module/*.c
+SRC_FILES += $(SRC_DIR)/driver/*.c
+
+BUILD_DIR = bin
+OUTPUT = $(BUILD_DIR)/FW_DEBUG_V_1_0
+
+reset:
+ $(PYTHON) $(SCRIPTS_DIR)/$(SCRIPT_RESET)
+
+build:
+ mkdir $(BUILD_DIR)
+ $(CC) $(CFLAGS) -o $(OUTPUT).lffw $(SRC_FILES)
+ $(AVR_SIZE) -C -x $(OUTPUT).lffw
+ $(OBJ_C) -j .text -j .data -O ihex $(OUTPUT).lffw $(OUTPUT).hex
+
+flash:
+ @read -p "ENTER TO BEGIN FLASH..."
+ $(AVRDUDE) -p $(MCU) -c $(PROG) -v -V -u -U flash:w:$(OUTPUT).hex
+
+fuze:
+ $(AVRDUDE) -p $(MCU) -c $(PROG) -v -V -u -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m -U lock:w:$(LOCK):m
+
+clean:
+ rm -fr $(BUILD_DIR)
+
+upload: reset clean build flash
+force-upload: clean build flash
+
+deps:
+ sudo apt install gcc-avr binutils-avr gdb-avr avr-libc avrdude make git
+
diff --git a/firmware/README.txt b/firmware/README.txt
new file mode 100644
index 0000000..f90b1f0
--- /dev/null
+++ b/firmware/README.txt
@@ -0,0 +1,53 @@
+Dependencies
+------------
+
+gcc-avr binutils-avr gdb-avr avr-libc avrdude make git
+
+To install all dependencies, run:
+make deps
+
+Fuse Settings
+-------------
+
+Fuse settings are defined in the Makefile as follows:
+
+LFUSE = 0xFF
+HFUSE = 0xD9
+EFUSE = 0xFF
+LOCK = 0xFF
+
+System Reset
+------------
+
+To exclusively execute a soft reset, run:
+make reset
+
+Firmware Compilation
+--------------------
+
+To exclusively build the firmware, run:
+make build
+
+Firmware Flash
+--------------
+
+To exclusively execute a firmware flash, run:
+make flash
+
+Usage
+-----
+
+Only two commands are required to build and flash
+the firmware onto the ATmega328P.
+
+Simply run:
+make
+
+Then run:
+make upload
+
+Note:
+
+If you encounter issues performing a soft reset, use:
+make force-upload
+
diff --git a/firmware/inc/bsp.h b/firmware/inc/bsp.h
new file mode 100644
index 0000000..2f38cfb
--- /dev/null
+++ b/firmware/inc/bsp.h
@@ -0,0 +1,18 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#ifndef BSP_H_
+#define BSP_H
+
+void bsp_soft_reset(void);
+
+#endif
diff --git a/firmware/inc/cmd.h b/firmware/inc/cmd.h
new file mode 100644
index 0000000..11d25e0
--- /dev/null
+++ b/firmware/inc/cmd.h
@@ -0,0 +1,74 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#ifndef CMD_H_
+#define CMD_H_
+
+#include <avr/pgmspace.h>
+
+#define CMD_MAX_ARGS 5
+#define CMD_NEWLINE "\n"
+
+#define CMD_DEF_ARG(x) x
+#define CMD_DEF_SINGLE(x) x CMD_NEWLINE
+
+#define CMD_ERROR(x) printf("ERROR %s\n", x)
+#define CMD_PRINT(x) printf("INFO %s\n", x)
+#define CMD_PRINT_FMT(x, y) printf("INFO " x CMD_NEWLINE, y)
+#define CMD_SWITCH(x, y) !strcmp(x, y)
+
+#define CMD_POWER_GET_STR "POWER?"
+#define CMD_POWER_GET_DESC "QUERY RF POWER"
+
+#define CMD_POWER_SET_STR "POWER"
+#define CMD_POWER_SET_DESC "SET RF POWER"
+
+#define CMD_TX_STR "TX"
+#define CMD_TX_DESC "SWITCH TO TX MODE"
+
+#define CMD_RX_STR "RX"
+#define CMD_RX_DESC "SWITCH TO RX MODE"
+
+#define CMD_DRAIN_SET_STR "DRAIN"
+#define CMD_DRAIN_SET_DESC "SET DRAIN COUNTER"
+
+#define CMD_PROBE_STR "PROBE"
+#define CMD_PROBE_DESC "START GATE PROBE"
+
+#define CMD_RESET_STR "RESET"
+#define CMD_RESET_DESC "EXECUTE SOFT RESET"
+
+#define CMD_REPORT_STR "REPORT"
+#define CMD_REPORT_DESC "DUMP SYSTEM REPORT"
+
+#define CMD_HELP_STR "HELP"
+#define CMD_HELP_DESC "DUMP COMMAND LIST"
+
+#define CMD_LINK_REPLY_STR "REPLY"
+
+#define CMD_SIZE 9
+#define CMD_ENTRY_SIZE 2
+
+#define CMD_POWER_GET CMD_DEF_SINGLE(CMD_POWER_GET_STR)
+#define CMD_POWER_SET CMD_DEF_ARG(CMD_POWER_SET_STR)
+#define CMD_TX CMD_DEF_SINGLE(CMD_TX_STR)
+#define CMD_RX CMD_DEF_SINGLE(CMD_RX_STR)
+#define CMD_DRAIN_SET CMD_DEF_ARG(CMD_DRAIN_SET_STR)
+#define CMD_PROBE CMD_DEF_SINGLE(CMD_PROBE_STR)
+#define CMD_RESET CMD_DEF_SINGLE(CMD_RESET_STR)
+#define CMD_REPORT CMD_DEF_SINGLE(CMD_REPORT_STR)
+#define CMD_HELP CMD_DEF_SINGLE(CMD_HELP_STR)
+#define CMD_LINK_REPLY CMD_DEF_SINGLE(CMD_LINK_REPLY_STR)
+
+extern const char* const cmd_lut[][2];
+
+#endif
diff --git a/firmware/inc/driver/device.h b/firmware/inc/driver/device.h
new file mode 100644
index 0000000..b8528c2
--- /dev/null
+++ b/firmware/inc/driver/device.h
@@ -0,0 +1,25 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#ifndef DEVICE_H_
+#define DEVICE_H_
+
+#include <stdint.h>
+
+typedef struct
+{
+ uint8_t dev_id;
+ uint8_t dev_ss;
+ uint8_t* dev_port;
+} t_dev;
+
+#endif
diff --git a/firmware/inc/driver/f1956.h b/firmware/inc/driver/f1956.h
new file mode 100644
index 0000000..79c5f0b
--- /dev/null
+++ b/firmware/inc/driver/f1956.h
@@ -0,0 +1,41 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#ifndef F1956_H_
+#define F1956_H_
+
+#include "setup.h"
+
+#include <stdint.h>
+#include <avr/io.h>
+
+#define F1956_ID_INPUT_ATT 0xC1
+
+#define F1956_DEV_0 0
+#define F1956_DEV_INPUT_ATT F1956_DEV_0
+
+#define F1956_DEV_INPUT_ATT_ADDR 0x0
+
+#define F1956_DEV_0_PORT PORTC
+#define F1956_DEV_0_DDR DDRC
+#define F1956_DEV_0_SS PORTC2
+
+void att_init(
+ uint8_t init_value
+);
+
+void att_set(
+ uint8_t id,
+ uint8_t value
+);
+
+#endif
diff --git a/firmware/inc/driver/mcp3202.h b/firmware/inc/driver/mcp3202.h
new file mode 100644
index 0000000..b995834
--- /dev/null
+++ b/firmware/inc/driver/mcp3202.h
@@ -0,0 +1,82 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#ifndef MCP3202_H_
+#define MCP3202_H_
+
+#include <stdint.h>
+#include <avr/io.h>
+
+#define MCP3202_BIT_MSBBF(x) (x << 5)
+#define MCP3202_BIT_ODD(x) (x << 6)
+#define MCP3202_BIT_SGL(x) (x << 7)
+#define MCP3202_BIT_START (1 << 0)
+
+#define MCP3202_DAQ_MSB 0x2
+#define MCP3202_DAQ_LSB 0x1
+
+#define MCP3202_BUILD_REQ(MSBF, ODD, SGL) \
+ (uint8_t)(MCP3202_BIT_MSBBF(MSBF) | \
+ MCP3202_BIT_ODD(ODD) | \
+ MCP3202_BIT_SGL(SGL))
+
+#define MCP3202_REQ_START (uint8_t)(MCP3202_BIT_START)
+#define MCP3202_REQ_CH0 MCP3202_BUILD_REQ(1, 0, 1)
+#define MCP3202_REQ_CH1 MCP3202_BUILD_REQ(1, 1, 1)
+#define MCP3202_REQ_PAD (uint8_t)(0x0)
+
+#define MCP3202_ID_FW_POWER 0xA1
+#define MCP3202_ID_REV_POWER 0xA2
+#define MCP3202_ID_DRAIN_VOLT 0xA3
+#define MCP3202_ID_DRAIN_AMP 0xA4
+
+#define MCP3202_CHN_0 0
+#define MCP3202_CHN_1 1
+
+#define MCP3202_CHN_DRAIN_VOLT MCP3202_CHN_0
+#define MCP3202_CHN_DRAIN_AMP MCP3202_CHN_1
+#define MCP3202_CHN_FW_POWER MCP3202_CHN_1
+#define MCP3202_CHN_REV_POWER MCP3202_CHN_0
+
+#define MCP3202_DEV_0 0
+#define MCP3202_DEV_1 1
+
+#define MCP3202_DEV_DRAIN_VOLT MCP3202_DEV_0
+#define MCP3202_DEV_DRAIN_AMP MCP3202_DEV_0
+#define MCP3202_DEV_FW_POWER MCP3202_DEV_1
+#define MCP3202_DEV_REV_POWER MCP3202_DEV_1
+
+#define MCP3202_DEV_0_PORT PORTC
+#define MCP3202_DEV_0_DDR DDRC
+#define MCP3202_DEV_0_SS PORTC0
+#define MCP3202_DEV_1_PORT PORTC
+#define MCP3202_DEV_1_DDR DDRC
+#define MCP3202_DEV_1_SS PORTC1
+
+#define MCP3202_DRAIN_VOLT_GAIN 26
+#define MCP3202_DRAIN_AMP_GAIN 25
+
+
+void adc_init(void);
+
+uint16_t adc_read(
+ uint8_t id,
+ uint8_t conv_bypass
+);
+
+uint16_t adc_read_n(
+ uint8_t id,
+ uint8_t n
+);
+
+
+#endif
diff --git a/firmware/inc/driver/mcp4725.h b/firmware/inc/driver/mcp4725.h
new file mode 100644
index 0000000..06f6281
--- /dev/null
+++ b/firmware/inc/driver/mcp4725.h
@@ -0,0 +1,48 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#ifndef MCP4725_H_
+#define MCP4725_H_
+
+#define MCP4725_BIT_PD0(x) (x << 4)
+#define MCP4725_BIT_PD1(x) (x << 5)
+
+#define MCP4725_ID_GATE_VOLT 0xB1
+#define MCP4725_ID_DRAIN_VOLT 0xB2
+
+#define MCP4725_DEV_0 0
+#define MCP4725_DEV_1 1
+
+#define MCP4725_DEV_GATE_VOLT MCP4725_DEV_0
+#define MCP4725_DEV_DRAIN_VOLT MCP4725_DEV_1
+
+#define MCP4725_DEV_GATE_VOLT_ADDR 0x60
+#define MCP4725_DEV_DRAIN_VOLT_ADDR 0x61
+
+#define MCP4725_DRAIN_GAIN 23
+
+void dac_init(
+ uint16_t init_value
+);
+
+void dac_write(
+ uint8_t id,
+ uint16_t value,
+ uint8_t conv_bypass
+);
+
+uint16_t dac_read(
+ uint8_t id,
+ uint8_t conv_bypass
+);
+
+#endif
diff --git a/firmware/inc/driver/radio_config.h b/firmware/inc/driver/radio_config.h
new file mode 100644
index 0000000..ff079a7
--- /dev/null
+++ b/firmware/inc/driver/radio_config.h
@@ -0,0 +1,680 @@
+/*! @file radio_config.h
+ * @brief This file contains the automatically generated
+ * configurations.
+ *
+ * @n WDS GUI Version: 3.2.11.0
+ * @n Device: Si4468 Rev.: A2
+ *
+ * @b COPYRIGHT
+ * @n Silicon Laboratories Confidential
+ * @n Copyright 2017 Silicon Laboratories, Inc.
+ * @n http://www.silabs.com
+ */
+
+#ifndef RADIO_CONFIG_H_
+#define RADIO_CONFIG_H_
+
+// USER DEFINED PARAMETERS
+// Define your own parameters here
+
+// INPUT DATA
+/*
+// Crys_freq(Hz): 30000000 Crys_tol(ppm): 20 IF_mode: 2 High_perf_Ch_Fil: 1 OSRtune: 0 Ch_Fil_Bw_AFC: 0 ANT_DIV: 0 PM_pattern: 0
+// MOD_type: 2 Rsymb(sps): 1000 Fdev(Hz): 2000 RXBW(Hz): 150000 Manchester: 0 AFC_en: 0 Rsymb_error: 0.0 Chip-Version: 2
+// RF Freq.(MHz): 169.4 API_TC: 29 fhst: 250000 inputBW: 0 BERT: 0 RAW_dout: 0 D_source: 0 Hi_pfm_div: 1
+// API_ARR_Det_en: 0 Fdev_error: 0 API_ETSI: 0
+//
+// # RX IF frequency is -468750 Hz
+// # WB filter 1 (BW = 19.08 kHz); NB-filter 1 (BW = 19.08 kHz)
+//
+// Modulation index: 4
+*/
+
+
+// CONFIGURATION PARAMETERS
+#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ 30000000L
+#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER 0x00
+#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH 0x07
+#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP 0x03
+#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET 0xF000
+
+
+// CONFIGURATION COMMANDS
+
+/*
+// Command: RF_POWER_UP
+// Description: Command to power-up the device and select the operational mode and functionality.
+*/
+#define RF_POWER_UP 0x02, 0x01, 0x00, 0x01, 0xC9, 0xC3, 0x80
+
+/*
+// Command: RF_GPIO_PIN_CFG
+// Description: Configures the GPIO pins.
+*/
+#define RF_GPIO_PIN_CFG 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+/*
+// Set properties: RF_GLOBAL_XO_TUNE_2
+// Number of properties: 2
+// Group ID: 0x00
+// Start ID: 0x00
+// Default values: 0x40, 0x00,
+// Descriptions:
+// GLOBAL_XO_TUNE - Configure the internal capacitor frequency tuning bank for the crystal oscillator.
+// GLOBAL_CLK_CFG - Clock configuration options.
+*/
+#define RF_GLOBAL_XO_TUNE_2 0x11, 0x00, 0x02, 0x00, 0x52, 0x00
+
+/*
+// Set properties: RF_GLOBAL_CONFIG_1
+// Number of properties: 1
+// Group ID: 0x00
+// Start ID: 0x03
+// Default values: 0x20,
+// Descriptions:
+// GLOBAL_CONFIG - Global configuration settings.
+*/
+#define RF_GLOBAL_CONFIG_1 0x11, 0x00, 0x01, 0x03, 0x20
+
+/*
+// Set properties: RF_INT_CTL_ENABLE_2
+// Number of properties: 2
+// Group ID: 0x01
+// Start ID: 0x00
+// Default values: 0x04, 0x00,
+// Descriptions:
+// INT_CTL_ENABLE - This property provides for global enabling of the three interrupt groups (Chip, Modem and Packet Handler) in order to generate HW interrupts at the NIRQ pin.
+// INT_CTL_PH_ENABLE - Enable individual interrupt sources within the Packet Handler Interrupt Group to generate a HW interrupt on the NIRQ output pin.
+*/
+#define RF_INT_CTL_ENABLE_2 0x11, 0x01, 0x02, 0x00, 0x01, 0xFF
+
+/*
+// Set properties: RF_FRR_CTL_A_MODE_4
+// Number of properties: 4
+// Group ID: 0x02
+// Start ID: 0x00
+// Default values: 0x01, 0x02, 0x09, 0x00,
+// Descriptions:
+// FRR_CTL_A_MODE - Fast Response Register A Configuration.
+// FRR_CTL_B_MODE - Fast Response Register B Configuration.
+// FRR_CTL_C_MODE - Fast Response Register C Configuration.
+// FRR_CTL_D_MODE - Fast Response Register D Configuration.
+*/
+#define RF_FRR_CTL_A_MODE_4 0x11, 0x02, 0x04, 0x00, 0x0A, 0x09, 0x00, 0x00
+
+/*
+// Set properties: RF_PREAMBLE_TX_LENGTH_9
+// Number of properties: 9
+// Group ID: 0x10
+// Start ID: 0x00
+// Default values: 0x08, 0x14, 0x00, 0x0F, 0x21, 0x00, 0x00, 0x00, 0x00,
+// Descriptions:
+// PREAMBLE_TX_LENGTH - Configure length of TX Preamble.
+// PREAMBLE_CONFIG_STD_1 - Configuration of reception of a packet with a Standard Preamble pattern.
+// PREAMBLE_CONFIG_NSTD - Configuration of transmission/reception of a packet with a Non-Standard Preamble pattern.
+// PREAMBLE_CONFIG_STD_2 - Configuration of timeout periods during reception of a packet with Standard Preamble pattern.
+// PREAMBLE_CONFIG - General configuration bits for the Preamble field.
+// PREAMBLE_PATTERN_31_24 - Configuration of the bit values describing a Non-Standard Preamble pattern.
+// PREAMBLE_PATTERN_23_16 - Configuration of the bit values describing a Non-Standard Preamble pattern.
+// PREAMBLE_PATTERN_15_8 - Configuration of the bit values describing a Non-Standard Preamble pattern.
+// PREAMBLE_PATTERN_7_0 - Configuration of the bit values describing a Non-Standard Preamble pattern.
+*/
+#define RF_PREAMBLE_TX_LENGTH_9 0x11, 0x10, 0x09, 0x00, 0x08, 0x14, 0x00, 0x0F, 0x31, 0x00, 0x00, 0x00, 0x00
+
+/*
+// Set properties: RF_SYNC_CONFIG_10
+// Number of properties: 10
+// Group ID: 0x11
+// Start ID: 0x00
+// Default values: 0x01, 0x2D, 0xD4, 0x2D, 0xD4, 0x00, 0x2D, 0xD4, 0x2D, 0xD4,
+// Descriptions:
+// SYNC_CONFIG - Sync Word configuration bits.
+// SYNC_BITS_31_24 - Sync word.
+// SYNC_BITS_23_16 - Sync word.
+// SYNC_BITS_15_8 - Sync word.
+// SYNC_BITS_7_0 - Sync word.
+// SYNC_CONFIG2 - Sync Word configuration bits.
+// SYNC_BITS2_31_24 - Sync word 2.
+// SYNC_BITS2_23_16 - Sync word 2.
+// SYNC_BITS2_15_8 - Sync word 2.
+// SYNC_BITS2_7_0 - Sync word 2.
+*/
+#define RF_SYNC_CONFIG_10 0x11, 0x11, 0x0A, 0x00, 0x01, 0xB4, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+/*
+// Set properties: RF_PKT_CRC_CONFIG_12
+// Number of properties: 12
+// Group ID: 0x12
+// Start ID: 0x00
+// Default values: 0x00, 0x01, 0x08, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
+// Descriptions:
+// PKT_CRC_CONFIG - Select a CRC polynomial and seed.
+// PKT_WHT_POLY_15_8 - 16-bit polynomial value for the PN Generator (e.g., for Data Whitening)
+// PKT_WHT_POLY_7_0 - 16-bit polynomial value for the PN Generator (e.g., for Data Whitening)
+// PKT_WHT_SEED_15_8 - 16-bit seed value for the PN Generator (e.g., for Data Whitening)
+// PKT_WHT_SEED_7_0 - 16-bit seed value for the PN Generator (e.g., for Data Whitening)
+// PKT_WHT_BIT_NUM - Selects which bit of the LFSR (used to generate the PN / data whitening sequence) is used as the output bit for data scrambling.
+// PKT_CONFIG1 - General configuration bits for transmission or reception of a packet.
+// PKT_CONFIG2 - General packet configuration bits.
+// PKT_LEN - Configuration bits for reception of a variable length packet.
+// PKT_LEN_FIELD_SOURCE - Field number containing the received packet length byte(s).
+// PKT_LEN_ADJUST - Provides for adjustment/offset of the received packet length value (in order to accommodate a variety of methods of defining total packet length).
+// PKT_TX_THRESHOLD - TX FIFO almost empty threshold.
+*/
+#define RF_PKT_CRC_CONFIG_12 0x11, 0x12, 0x0C, 0x00, 0x00, 0x01, 0x08, 0xFF, 0xFF, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x30
+
+/*
+// Set properties: RF_PKT_RX_THRESHOLD_12
+// Number of properties: 12
+// Group ID: 0x12
+// Start ID: 0x0C
+// Default values: 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// Descriptions:
+// PKT_RX_THRESHOLD - RX FIFO Almost Full threshold.
+// PKT_FIELD_1_LENGTH_12_8 - Unsigned 13-bit Field 1 length value.
+// PKT_FIELD_1_LENGTH_7_0 - Unsigned 13-bit Field 1 length value.
+// PKT_FIELD_1_CONFIG - General data processing and packet configuration bits for Field 1.
+// PKT_FIELD_1_CRC_CONFIG - Configuration of CRC control bits across Field 1.
+// PKT_FIELD_2_LENGTH_12_8 - Unsigned 13-bit Field 2 length value.
+// PKT_FIELD_2_LENGTH_7_0 - Unsigned 13-bit Field 2 length value.
+// PKT_FIELD_2_CONFIG - General data processing and packet configuration bits for Field 2.
+// PKT_FIELD_2_CRC_CONFIG - Configuration of CRC control bits across Field 2.
+// PKT_FIELD_3_LENGTH_12_8 - Unsigned 13-bit Field 3 length value.
+// PKT_FIELD_3_LENGTH_7_0 - Unsigned 13-bit Field 3 length value.
+// PKT_FIELD_3_CONFIG - General data processing and packet configuration bits for Field 3.
+*/
+#define RF_PKT_RX_THRESHOLD_12 0x11, 0x12, 0x0C, 0x0C, 0x30, 0x00, 0x01, 0x04, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00
+
+/*
+// Set properties: RF_PKT_FIELD_3_CRC_CONFIG_12
+// Number of properties: 12
+// Group ID: 0x12
+// Start ID: 0x18
+// Default values: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// Descriptions:
+// PKT_FIELD_3_CRC_CONFIG - Configuration of CRC control bits across Field 3.
+// PKT_FIELD_4_LENGTH_12_8 - Unsigned 13-bit Field 4 length value.
+// PKT_FIELD_4_LENGTH_7_0 - Unsigned 13-bit Field 4 length value.
+// PKT_FIELD_4_CONFIG - General data processing and packet configuration bits for Field 4.
+// PKT_FIELD_4_CRC_CONFIG - Configuration of CRC control bits across Field 4.
+// PKT_FIELD_5_LENGTH_12_8 - Unsigned 13-bit Field 5 length value.
+// PKT_FIELD_5_LENGTH_7_0 - Unsigned 13-bit Field 5 length value.
+// PKT_FIELD_5_CONFIG - General data processing and packet configuration bits for Field 5.
+// PKT_FIELD_5_CRC_CONFIG - Configuration of CRC control bits across Field 5.
+// PKT_RX_FIELD_1_LENGTH_12_8 - Unsigned 13-bit RX Field 1 length value.
+// PKT_RX_FIELD_1_LENGTH_7_0 - Unsigned 13-bit RX Field 1 length value.
+// PKT_RX_FIELD_1_CONFIG - General data processing and packet configuration bits for RX Field 1.
+*/
+#define RF_PKT_FIELD_3_CRC_CONFIG_12 0x11, 0x12, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+/*
+// Set properties: RF_PKT_RX_FIELD_1_CRC_CONFIG_12
+// Number of properties: 12
+// Group ID: 0x12
+// Start ID: 0x24
+// Default values: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// Descriptions:
+// PKT_RX_FIELD_1_CRC_CONFIG - Configuration of CRC control bits across RX Field 1.
+// PKT_RX_FIELD_2_LENGTH_12_8 - Unsigned 13-bit RX Field 2 length value.
+// PKT_RX_FIELD_2_LENGTH_7_0 - Unsigned 13-bit RX Field 2 length value.
+// PKT_RX_FIELD_2_CONFIG - General data processing and packet configuration bits for RX Field 2.
+// PKT_RX_FIELD_2_CRC_CONFIG - Configuration of CRC control bits across RX Field 2.
+// PKT_RX_FIELD_3_LENGTH_12_8 - Unsigned 13-bit RX Field 3 length value.
+// PKT_RX_FIELD_3_LENGTH_7_0 - Unsigned 13-bit RX Field 3 length value.
+// PKT_RX_FIELD_3_CONFIG - General data processing and packet configuration bits for RX Field 3.
+// PKT_RX_FIELD_3_CRC_CONFIG - Configuration of CRC control bits across RX Field 3.
+// PKT_RX_FIELD_4_LENGTH_12_8 - Unsigned 13-bit RX Field 4 length value.
+// PKT_RX_FIELD_4_LENGTH_7_0 - Unsigned 13-bit RX Field 4 length value.
+// PKT_RX_FIELD_4_CONFIG - General data processing and packet configuration bits for RX Field 4.
+*/
+#define RF_PKT_RX_FIELD_1_CRC_CONFIG_12 0x11, 0x12, 0x0C, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+/*
+// Set properties: RF_PKT_RX_FIELD_4_CRC_CONFIG_5
+// Number of properties: 5
+// Group ID: 0x12
+// Start ID: 0x30
+// Default values: 0x00, 0x00, 0x00, 0x00, 0x00,
+// Descriptions:
+// PKT_RX_FIELD_4_CRC_CONFIG - Configuration of CRC control bits across RX Field 4.
+// PKT_RX_FIELD_5_LENGTH_12_8 - Unsigned 13-bit RX Field 5 length value.
+// PKT_RX_FIELD_5_LENGTH_7_0 - Unsigned 13-bit RX Field 5 length value.
+// PKT_RX_FIELD_5_CONFIG - General data processing and packet configuration bits for RX Field 5.
+// PKT_RX_FIELD_5_CRC_CONFIG - Configuration of CRC control bits across RX Field 5.
+*/
+#define RF_PKT_RX_FIELD_4_CRC_CONFIG_5 0x11, 0x12, 0x05, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00
+
+/*
+// Set properties: RF_PKT_CRC_SEED_31_24_4
+// Number of properties: 4
+// Group ID: 0x12
+// Start ID: 0x36
+// Default values: 0x00, 0x00, 0x00, 0x00,
+// Descriptions:
+// PKT_CRC_SEED_31_24 - 32-bit seed value for the 32-bit CRC engine
+// PKT_CRC_SEED_23_16 - 32-bit seed value for the 32-bit CRC engine
+// PKT_CRC_SEED_15_8 - 32-bit seed value for the 32-bit CRC engine
+// PKT_CRC_SEED_7_0 - 32-bit seed value for the 32-bit CRC engine
+*/
+#define RF_PKT_CRC_SEED_31_24_4 0x11, 0x12, 0x04, 0x36, 0x00, 0x00, 0x00, 0x00
+
+/*
+// Set properties: RF_MODEM_MOD_TYPE_12
+// Number of properties: 12
+// Group ID: 0x20
+// Start ID: 0x00
+// Default values: 0x02, 0x80, 0x07, 0x0F, 0x42, 0x40, 0x01, 0xC9, 0xC3, 0x80, 0x00, 0x06,
+// Descriptions:
+// MODEM_MOD_TYPE - Selects the type of modulation. In TX mode, additionally selects the source of the modulation.
+// MODEM_MAP_CONTROL - Controls polarity and mapping of transmit and receive bits.
+// MODEM_DSM_CTRL - Miscellaneous control bits for the Delta-Sigma Modulator (DSM) in the PLL Synthesizer.
+// MODEM_DATA_RATE_2 - Unsigned 24-bit value used to determine the TX data rate
+// MODEM_DATA_RATE_1 - Unsigned 24-bit value used to determine the TX data rate
+// MODEM_DATA_RATE_0 - Unsigned 24-bit value used to determine the TX data rate
+// MODEM_TX_NCO_MODE_3 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus.
+// MODEM_TX_NCO_MODE_2 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus.
+// MODEM_TX_NCO_MODE_1 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus.
+// MODEM_TX_NCO_MODE_0 - TX Gaussian filter oversampling ratio and Byte 3 of unsigned 26-bit TX Numerically Controlled Oscillator (NCO) modulus.
+// MODEM_FREQ_DEV_2 - 17-bit unsigned TX frequency deviation word.
+// MODEM_FREQ_DEV_1 - 17-bit unsigned TX frequency deviation word.
+*/
+#define RF_MODEM_MOD_TYPE_12 0x11, 0x20, 0x0C, 0x00, 0x00, 0x00, 0x07, 0x00, 0x27, 0x10, 0x01, 0xC9, 0xC3, 0x80, 0x00, 0x01
+
+/*
+// Set properties: RF_MODEM_FREQ_DEV_0_1
+// Number of properties: 1
+// Group ID: 0x20
+// Start ID: 0x0C
+// Default values: 0xD3,
+// Descriptions:
+// MODEM_FREQ_DEV_0 - 17-bit unsigned TX frequency deviation word.
+*/
+#define RF_MODEM_FREQ_DEV_0_1 0x11, 0x20, 0x01, 0x0C, 0xA3
+
+/*
+// Set properties: RF_MODEM_TX_RAMP_DELAY_12
+// Number of properties: 12
+// Group ID: 0x20
+// Start ID: 0x18
+// Default values: 0x01, 0x00, 0x08, 0x03, 0xC0, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x4B,
+// Descriptions:
+// MODEM_TX_RAMP_DELAY - TX ramp-down delay setting.
+// MODEM_MDM_CTRL - MDM control.
+// MODEM_IF_CONTROL - Selects Fixed-IF, Scaled-IF, or Zero-IF mode of RX Modem operation.
+// MODEM_IF_FREQ_2 - the IF frequency setting (an 18-bit signed number).
+// MODEM_IF_FREQ_1 - the IF frequency setting (an 18-bit signed number).
+// MODEM_IF_FREQ_0 - the IF frequency setting (an 18-bit signed number).
+// MODEM_DECIMATION_CFG1 - Specifies three decimator ratios for the Cascaded Integrator Comb (CIC) filter.
+// MODEM_DECIMATION_CFG0 - Specifies miscellaneous parameters and decimator ratios for the Cascaded Integrator Comb (CIC) filter.
+// MODEM_DECIMATION_CFG2 - Specifies miscellaneous decimator filter selections.
+// MODEM_IFPKD_THRESHOLDS -
+// MODEM_BCR_OSR_1 - RX BCR/Slicer oversampling rate (12-bit unsigned number).
+// MODEM_BCR_OSR_0 - RX BCR/Slicer oversampling rate (12-bit unsigned number).
+*/
+#define RF_MODEM_TX_RAMP_DELAY_12 0x11, 0x20, 0x0C, 0x18, 0x01, 0x80, 0x08, 0x02, 0x80, 0x00, 0x70, 0x10, 0x14, 0xE8, 0x02, 0x71
+
+/*
+// Set properties: RF_MODEM_BCR_NCO_OFFSET_2_12
+// Number of properties: 12
+// Group ID: 0x20
+// Start ID: 0x24
+// Default values: 0x06, 0xD3, 0xA0, 0x06, 0xD3, 0x02, 0xC0, 0x00, 0x00, 0x23, 0x83, 0x69,
+// Descriptions:
+// MODEM_BCR_NCO_OFFSET_2 - RX BCR NCO offset value (an unsigned 22-bit number).
+// MODEM_BCR_NCO_OFFSET_1 - RX BCR NCO offset value (an unsigned 22-bit number).
+// MODEM_BCR_NCO_OFFSET_0 - RX BCR NCO offset value (an unsigned 22-bit number).
+// MODEM_BCR_GAIN_1 - The unsigned 11-bit RX BCR loop gain value.
+// MODEM_BCR_GAIN_0 - The unsigned 11-bit RX BCR loop gain value.
+// MODEM_BCR_GEAR - RX BCR loop gear control.
+// MODEM_BCR_MISC1 - Miscellaneous control bits for the RX BCR loop.
+// MODEM_BCR_MISC0 - Miscellaneous RX BCR loop controls.
+// MODEM_AFC_GEAR - RX AFC loop gear control.
+// MODEM_AFC_WAIT - RX AFC loop wait time control.
+// MODEM_AFC_GAIN_1 - Sets the gain of the PLL-based AFC acquisition loop, and provides miscellaneous control bits for AFC functionality.
+// MODEM_AFC_GAIN_0 - Sets the gain of the PLL-based AFC acquisition loop, and provides miscellaneous control bits for AFC functionality.
+*/
+#define RF_MODEM_BCR_NCO_OFFSET_2_12 0x11, 0x20, 0x0C, 0x24, 0x00, 0xD1, 0xB7, 0x00, 0x69, 0x02, 0xC2, 0x00, 0x04, 0x23, 0x80, 0x09
+
+/*
+// Set properties: RF_MODEM_AFC_LIMITER_1_3
+// Number of properties: 3
+// Group ID: 0x20
+// Start ID: 0x30
+// Default values: 0x00, 0x40, 0xA0,
+// Descriptions:
+// MODEM_AFC_LIMITER_1 - Set the AFC limiter value.
+// MODEM_AFC_LIMITER_0 - Set the AFC limiter value.
+// MODEM_AFC_MISC - Specifies miscellaneous AFC control bits.
+*/
+#define RF_MODEM_AFC_LIMITER_1_3 0x11, 0x20, 0x03, 0x30, 0x24, 0x15, 0x80
+
+/*
+// Set properties: RF_MODEM_AGC_CONTROL_1
+// Number of properties: 1
+// Group ID: 0x20
+// Start ID: 0x35
+// Default values: 0xE0,
+// Descriptions:
+// MODEM_AGC_CONTROL - Miscellaneous control bits for the Automatic Gain Control (AGC) function in the RX Chain.
+*/
+#define RF_MODEM_AGC_CONTROL_1 0x11, 0x20, 0x01, 0x35, 0xE0
+
+/*
+// Set properties: RF_MODEM_AGC_WINDOW_SIZE_12
+// Number of properties: 12
+// Group ID: 0x20
+// Start ID: 0x38
+// Default values: 0x11, 0x10, 0x10, 0x0B, 0x1C, 0x40, 0x00, 0x00, 0x2B, 0x0C, 0xA4, 0x03,
+// Descriptions:
+// MODEM_AGC_WINDOW_SIZE - Specifies the size of the measurement and settling windows for the AGC algorithm.
+// MODEM_AGC_RFPD_DECAY - Sets the decay time of the RF peak detectors.
+// MODEM_AGC_IFPD_DECAY - Sets the decay time of the IF peak detectors.
+// MODEM_FSK4_GAIN1 - Specifies the gain factor of the secondary branch in 4(G)FSK ISI-suppression.
+// MODEM_FSK4_GAIN0 - Specifies the gain factor of the primary branch in 4(G)FSK ISI-suppression.
+// MODEM_FSK4_TH1 - 16 bit 4(G)FSK slicer threshold.
+// MODEM_FSK4_TH0 - 16 bit 4(G)FSK slicer threshold.
+// MODEM_FSK4_MAP - 4(G)FSK symbol mapping code.
+// MODEM_OOK_PDTC - Configures the attack and decay times of the OOK Peak Detector.
+// MODEM_OOK_BLOPK - Configures the slicing reference level of the OOK Peak Detector.
+// MODEM_OOK_CNT1 - OOK control.
+// MODEM_OOK_MISC - Selects the detector(s) used for demodulation of an OOK signal, or for demodulation of a (G)FSK signal when using the asynchronous demodulator.
+*/
+#define RF_MODEM_AGC_WINDOW_SIZE_12 0x11, 0x20, 0x0C, 0x38, 0x11, 0x89, 0x89, 0x80, 0x02, 0xFF, 0xFF, 0x00, 0x2B, 0x0C, 0xA4, 0x22
+
+/*
+// Set properties: RF_MODEM_RAW_CONTROL_10
+// Number of properties: 10
+// Group ID: 0x20
+// Start ID: 0x45
+// Default values: 0x02, 0x00, 0xA3, 0x02, 0x80, 0xFF, 0x0C, 0x01, 0x00, 0x40,
+// Descriptions:
+// MODEM_RAW_CONTROL - Defines gain and enable controls for raw / nonstandard mode.
+// MODEM_RAW_EYE_1 - 11 bit eye-open detector threshold.
+// MODEM_RAW_EYE_0 - 11 bit eye-open detector threshold.
+// MODEM_ANT_DIV_MODE - Antenna diversity mode settings.
+// MODEM_ANT_DIV_CONTROL - Specifies controls for the Antenna Diversity algorithm.
+// MODEM_RSSI_THRESH - Configures the RSSI threshold.
+// MODEM_RSSI_JUMP_THRESH - Configures the RSSI Jump Detection threshold.
+// MODEM_RSSI_CONTROL - Control of the averaging modes and latching time for reporting RSSI value(s).
+// MODEM_RSSI_CONTROL2 - RSSI Jump Detection control.
+// MODEM_RSSI_COMP - RSSI compensation value.
+*/
+#define RF_MODEM_RAW_CONTROL_10 0x11, 0x20, 0x0A, 0x45, 0x83, 0x00, 0xCC, 0x01, 0x00, 0xFF, 0x06, 0x00, 0x18, 0x40
+
+/*
+// Set properties: RF_MODEM_RAW_SEARCH2_2
+// Number of properties: 2
+// Group ID: 0x20
+// Start ID: 0x50
+// Default values: 0x00, 0x08,
+// Descriptions:
+// MODEM_RAW_SEARCH2 - Defines and controls the search period length for the Moving Average and Min-Max detectors.
+// MODEM_CLKGEN_BAND - Select PLL Synthesizer output divider ratio as a function of frequency band.
+*/
+#define RF_MODEM_RAW_SEARCH2_2 0x11, 0x20, 0x02, 0x50, 0x84, 0x0D
+
+/*
+// Set properties: RF_MODEM_SPIKE_DET_2
+// Number of properties: 2
+// Group ID: 0x20
+// Start ID: 0x54
+// Default values: 0x00, 0x00,
+// Descriptions:
+// MODEM_SPIKE_DET - Configures the threshold for (G)FSK Spike Detection.
+// MODEM_ONE_SHOT_AFC - Configures parameters for th e One Shot AFC function and for BCR timing/acquisition.
+*/
+#define RF_MODEM_SPIKE_DET_2 0x11, 0x20, 0x02, 0x54, 0x03, 0x07
+
+/*
+// Set properties: RF_MODEM_RSSI_MUTE_1
+// Number of properties: 1
+// Group ID: 0x20
+// Start ID: 0x57
+// Default values: 0x00,
+// Descriptions:
+// MODEM_RSSI_MUTE - Configures muting of the RSSI to avoid false RSSI interrupts.
+*/
+#define RF_MODEM_RSSI_MUTE_1 0x11, 0x20, 0x01, 0x57, 0x00
+
+/*
+// Set properties: RF_MODEM_DSA_CTRL1_5
+// Number of properties: 5
+// Group ID: 0x20
+// Start ID: 0x5B
+// Default values: 0x00, 0x00, 0x00, 0x00, 0x00,
+// Descriptions:
+// MODEM_DSA_CTRL1 - Configures parameters for the Signal Arrival Detection circuit block and algorithm.
+// MODEM_DSA_CTRL2 - Configures parameters for the Signal Arrival Detection circuit block and algorithm.
+// MODEM_DSA_QUAL - Configures parameters for the Eye Opening qualification m ethod of the Signal Arrival Detection algorithm.
+// MODEM_DSA_RSSI - Signal Arrival Detect RSSI Qualifier Config
+// MODEM_DSA_MISC - Miscellaneous detection of signal arrival bits.
+*/
+#define RF_MODEM_DSA_CTRL1_5 0x11, 0x20, 0x05, 0x5B, 0x40, 0x04, 0x09, 0x78, 0x20
+
+/*
+// Set properties: RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12
+// Number of properties: 12
+// Group ID: 0x21
+// Start ID: 0x00
+// Default values: 0xFF, 0xBA, 0x0F, 0x51, 0xCF, 0xA9, 0xC9, 0xFC, 0x1B, 0x1E, 0x0F, 0x01,
+// Descriptions:
+// MODEM_CHFLT_RX1_CHFLT_COE13_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE12_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE11_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE10_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE9_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE8_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE7_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE6_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE5_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE4_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE3_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE2_7_0 - Filter coefficients for the first set of RX filter coefficients.
+*/
+#define RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12 0x11, 0x21, 0x0C, 0x00, 0xFF, 0xBA, 0x0F, 0x51, 0xCF, 0xA9, 0xC9, 0xFC, 0x1B, 0x1E, 0x0F, 0x01
+
+/*
+// Set properties: RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12
+// Number of properties: 12
+// Group ID: 0x21
+// Start ID: 0x0C
+// Default values: 0xFC, 0xFD, 0x15, 0xFF, 0x00, 0x0F, 0xFF, 0xC4, 0x30, 0x7F, 0xF5, 0xB5,
+// Descriptions:
+// MODEM_CHFLT_RX1_CHFLT_COE1_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COE0_7_0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COEM0 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COEM1 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COEM2 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX1_CHFLT_COEM3 - Filter coefficients for the first set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE13_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE12_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE11_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE10_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE9_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE8_7_0 - Filter coefficients for the second set of RX filter coefficients.
+*/
+#define RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12 0x11, 0x21, 0x0C, 0x0C, 0xFC, 0xFD, 0x15, 0xFF, 0x00, 0x0F, 0xFF, 0xBA, 0x0F, 0x51, 0xCF, 0xA9
+
+/*
+// Set properties: RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12
+// Number of properties: 12
+// Group ID: 0x21
+// Start ID: 0x18
+// Default values: 0xB8, 0xDE, 0x05, 0x17, 0x16, 0x0C, 0x03, 0x00, 0x15, 0xFF, 0x00, 0x00,
+// Descriptions:
+// MODEM_CHFLT_RX2_CHFLT_COE7_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE6_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE5_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE4_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE3_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE2_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE1_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COE0_7_0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COEM0 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COEM1 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COEM2 - Filter coefficients for the second set of RX filter coefficients.
+// MODEM_CHFLT_RX2_CHFLT_COEM3 - Filter coefficients for the second set of RX filter coefficients.
+*/
+#define RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12 0x11, 0x21, 0x0C, 0x18, 0xC9, 0xFC, 0x1B, 0x1E, 0x0F, 0x01, 0xFC, 0xFD, 0x15, 0xFF, 0x00, 0x0F
+
+/*
+// Set properties: RF_PA_MODE_4
+// Number of properties: 4
+// Group ID: 0x22
+// Start ID: 0x00
+// Default values: 0x08, 0x7F, 0x00, 0x5D,
+// Descriptions:
+// PA_MODE - Selects the PA operating mode, and selects resolution of PA power adjustment (i.e., step size).
+// PA_PWR_LVL - Configuration of PA output power level.
+// PA_BIAS_CLKDUTY - Configuration of the PA Bias and duty cycle of the TX clock source.
+// PA_TC - Configuration of PA ramping parameters.
+*/
+#define RF_PA_MODE_4 0x11, 0x22, 0x04, 0x00, 0x08, 0x7F, 0x00, 0x1D
+
+/*
+// Set properties: RF_SYNTH_PFDCP_CPFF_7
+// Number of properties: 7
+// Group ID: 0x23
+// Start ID: 0x00
+// Default values: 0x2C, 0x0E, 0x0B, 0x04, 0x0C, 0x73, 0x03,
+// Descriptions:
+// SYNTH_PFDCP_CPFF - Feed forward charge pump current selection.
+// SYNTH_PFDCP_CPINT - Integration charge pump current selection.
+// SYNTH_VCO_KV - Gain scaling factors (Kv) for the VCO tuning varactors on both the integrated-path and feed forward path.
+// SYNTH_LPFILT3 - Value of resistor R2 in feed-forward path of loop filter.
+// SYNTH_LPFILT2 - Value of capacitor C2 in feed-forward path of loop filter.
+// SYNTH_LPFILT1 - Value of capacitors C1 and C3 in feed-forward path of loop filter.
+// SYNTH_LPFILT0 - Bias current of the active amplifier in the feed-forward loop filter.
+*/
+#define RF_SYNTH_PFDCP_CPFF_7 0x11, 0x23, 0x07, 0x00, 0x2C, 0x0E, 0x0B, 0x04, 0x0C, 0x73, 0x03
+
+/*
+// Set properties: RF_MATCH_VALUE_1_12
+// Number of properties: 12
+// Group ID: 0x30
+// Start ID: 0x00
+// Default values: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// Descriptions:
+// MATCH_VALUE_1 - Match value to be compared with the result of logically AND-ing (bit-wise) the Mask 1 value with the received Match 1 byte.
+// MATCH_MASK_1 - Mask value to be logically AND-ed (bit-wise) with the Match 1 byte.
+// MATCH_CTRL_1 - Enable for Packet Match functionality, and configuration of Match Byte 1.
+// MATCH_VALUE_2 - Match value to be compared with the result of logically AND-ing (bit-wise) the Mask 2 value with the received Match 2 byte.
+// MATCH_MASK_2 - Mask value to be logically AND-ed (bit-wise) with the Match 2 byte.
+// MATCH_CTRL_2 - Configuration of Match Byte 2.
+// MATCH_VALUE_3 - Match value to be compared with the result of logically AND-ing (bit-wise) the Mask 3 value with the received Match 3 byte.
+// MATCH_MASK_3 - Mask value to be logically AND-ed (bit-wise) with the Match 3 byte.
+// MATCH_CTRL_3 - Configuration of Match Byte 3.
+// MATCH_VALUE_4 - Match value to be compared with the result of logically AND-ing (bit-wise) the Mask 4 value with the received Match 4 byte.
+// MATCH_MASK_4 - Mask value to be logically AND-ed (bit-wise) with the Match 4 byte.
+// MATCH_CTRL_4 - Configuration of Match Byte 4.
+*/
+#define RF_MATCH_VALUE_1_12 0x11, 0x30, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+/*
+// Set properties: RF_FREQ_CONTROL_INTE_8
+// Number of properties: 8
+// Group ID: 0x40
+// Start ID: 0x00
+// Default values: 0x3C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFF,
+// Descriptions:
+// FREQ_CONTROL_INTE - Frac-N PLL Synthesizer integer divide number.
+// FREQ_CONTROL_FRAC_2 - Frac-N PLL fraction number.
+// FREQ_CONTROL_FRAC_1 - Frac-N PLL fraction number.
+// FREQ_CONTROL_FRAC_0 - Frac-N PLL fraction number.
+// FREQ_CONTROL_CHANNEL_STEP_SIZE_1 - EZ Frequency Programming channel step size.
+// FREQ_CONTROL_CHANNEL_STEP_SIZE_0 - EZ Frequency Programming channel step size.
+// FREQ_CONTROL_W_SIZE - Set window gating period (in number of crystal reference clock cycles) for counting VCO frequency during calibration.
+// FREQ_CONTROL_VCOCNT_RX_ADJ - Adjust target count for VCO calibration in RX mode.
+*/
+#define RF_FREQ_CONTROL_INTE_8 0x11, 0x40, 0x08, 0x00, 0x42, 0x0E, 0x14, 0x7A, 0xCC, 0xCD, 0x20, 0xFA
+
+
+// AUTOMATICALLY GENERATED CODE!
+// DO NOT EDIT/MODIFY BELOW THIS LINE!
+// --------------------------------------------
+
+#ifndef FIRMWARE_LOAD_COMPILE
+#define RADIO_CONFIGURATION_DATA_ARRAY { \
+ 0x07, RF_POWER_UP, \
+ 0x08, RF_GPIO_PIN_CFG, \
+ 0x06, RF_GLOBAL_XO_TUNE_2, \
+ 0x05, RF_GLOBAL_CONFIG_1, \
+ 0x06, RF_INT_CTL_ENABLE_2, \
+ 0x08, RF_FRR_CTL_A_MODE_4, \
+ 0x0D, RF_PREAMBLE_TX_LENGTH_9, \
+ 0x0E, RF_SYNC_CONFIG_10, \
+ 0x10, RF_PKT_CRC_CONFIG_12, \
+ 0x10, RF_PKT_RX_THRESHOLD_12, \
+ 0x10, RF_PKT_FIELD_3_CRC_CONFIG_12, \
+ 0x10, RF_PKT_RX_FIELD_1_CRC_CONFIG_12, \
+ 0x09, RF_PKT_RX_FIELD_4_CRC_CONFIG_5, \
+ 0x08, RF_PKT_CRC_SEED_31_24_4, \
+ 0x10, RF_MODEM_MOD_TYPE_12, \
+ 0x05, RF_MODEM_FREQ_DEV_0_1, \
+ 0x10, RF_MODEM_TX_RAMP_DELAY_12, \
+ 0x10, RF_MODEM_BCR_NCO_OFFSET_2_12, \
+ 0x07, RF_MODEM_AFC_LIMITER_1_3, \
+ 0x05, RF_MODEM_AGC_CONTROL_1, \
+ 0x10, RF_MODEM_AGC_WINDOW_SIZE_12, \
+ 0x0E, RF_MODEM_RAW_CONTROL_10, \
+ 0x06, RF_MODEM_RAW_SEARCH2_2, \
+ 0x06, RF_MODEM_SPIKE_DET_2, \
+ 0x05, RF_MODEM_RSSI_MUTE_1, \
+ 0x09, RF_MODEM_DSA_CTRL1_5, \
+ 0x10, RF_MODEM_CHFLT_RX1_CHFLT_COE13_7_0_12, \
+ 0x10, RF_MODEM_CHFLT_RX1_CHFLT_COE1_7_0_12, \
+ 0x10, RF_MODEM_CHFLT_RX2_CHFLT_COE7_7_0_12, \
+ 0x08, RF_PA_MODE_4, \
+ 0x0B, RF_SYNTH_PFDCP_CPFF_7, \
+ 0x10, RF_MATCH_VALUE_1_12, \
+ 0x0C, RF_FREQ_CONTROL_INTE_8, \
+ 0x00 \
+ }
+#else
+#define RADIO_CONFIGURATION_DATA_ARRAY { 0 }
+#endif
+
+// DEFAULT VALUES FOR CONFIGURATION PARAMETERS
+#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ_DEFAULT 30000000L
+#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER_DEFAULT 0x00
+#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH_DEFAULT 0x10
+#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP_DEFAULT 0x01
+#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET_DEFAULT 0x1000
+
+#define RADIO_CONFIGURATION_DATA_RADIO_PATCH_INCLUDED 0x00
+#define RADIO_CONFIGURATION_DATA_RADIO_PATCH_SIZE 0x00
+#define RADIO_CONFIGURATION_DATA_RADIO_PATCH { }
+
+#ifndef RADIO_CONFIGURATION_DATA_ARRAY
+#error "This property must be defined!"
+#endif
+
+#ifndef RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ
+#define RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ RADIO_CONFIGURATION_DATA_RADIO_XO_FREQ_DEFAULT
+#endif
+
+#ifndef RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER
+#define RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER_DEFAULT
+#endif
+
+#ifndef RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH
+#define RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH_DEFAULT
+#endif
+
+#ifndef RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP
+#define RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP_DEFAULT
+#endif
+
+#ifndef RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET
+#define RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET_DEFAULT
+#endif
+
+#define RADIO_CONFIGURATION_DATA { \
+ Radio_Configuration_Data_Array, \
+ RADIO_CONFIGURATION_DATA_CHANNEL_NUMBER, \
+ RADIO_CONFIGURATION_DATA_RADIO_PACKET_LENGTH, \
+ RADIO_CONFIGURATION_DATA_RADIO_STATE_AFTER_POWER_UP, \
+ RADIO_CONFIGURATION_DATA_RADIO_DELAY_CNT_AFTER_RESET \
+ }
+
+#endif /* RADIO_CONFIG_H_ */
diff --git a/firmware/inc/driver/si4468.h b/firmware/inc/driver/si4468.h
new file mode 100644
index 0000000..61e72e5
--- /dev/null
+++ b/firmware/inc/driver/si4468.h
@@ -0,0 +1,54 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#ifndef SI4468_H_
+#define SI4468_H_
+
+#include "setup.h"
+
+#include <stdint.h>
+#include <avr/io.h>
+
+#define SI4468_ID_RADIO 0xD1
+
+#define SI4468_CMD_READ_CMD_BUFF 0x44
+#define SI4468_CMD_PART_INFO 0x01
+#define SI4468_CMD_FUNC_INFO 0x10
+#define SI4468_CMD_SET_PROPERTY 0x11
+#define SI4468_CMD_IRCAL 0x17
+#define SI4468_CMD_START_TX 0x31
+#define SI4468_CMD_CHANGE_STATE 0x34
+
+#define SI4468_CTS 0xFF
+#define SI4468_GET_RESP_LEN 0x2
+
+#define SI4468_DEV_0 0
+#define SI4468_DEV_RADIO SI4468_DEV_0
+#define SI4468_DEV_0_PORT PORTB
+#define SI4468_DEV_0_DDR DDRB
+#define SI4468_DEV_0_SS PORTB2
+
+typedef struct {
+ uint8_t chipRev;
+ uint16_t part;
+ uint8_t partBuild;
+ uint16_t id;
+ uint8_t customer;
+ uint8_t romId; ///< ROM ID (3 = revB1B, 6 = revC2A)
+} si4468_info_t;
+
+void si4468_init(void);
+void si4468_get_info(si4468_info_t* info);
+void si4468_apply_startup_config(void);
+void si4468_tx_mode(void);
+void si4468_rx_mode(void);
+#endif
diff --git a/firmware/inc/module/drain.h b/firmware/inc/module/drain.h
new file mode 100644
index 0000000..7b9d66d
--- /dev/null
+++ b/firmware/inc/module/drain.h
@@ -0,0 +1,37 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#ifndef DRAIN_H_
+#define DRAIN_H_
+
+#include <stdint.h>
+
+#define DRAIN_PROBE_START 0 // mV
+#define DRAIN_PROBE_LIMIT 3000
+#define DRAIN_PROBE_INC_FINE 1
+#define DRAIN_PROBE_INC_COARSE 5
+#define DRAIN_PROBE_INC_THRESHOLD 100
+#define DRAIN_Q_AMP 100 // mA
+#define DRAIN_TEST_COUNT 10
+#define DRAIN_TEST_BOUNDARY 10
+#define DRAIN_POWER_SAMPLES 10
+#define DRAIN_LOOP_DELAY_US 1000
+#define DRAIN_SETPOINT_TIMEOUT_MS 1500
+#define DRAIN_POWER_LOW_LIM 0
+#define DRAIN_POWER_HIGH_LIM 3000
+#define DRAIN_POWER_MODULUS 50
+
+uint16_t drain_sweep_pwr(
+ int16_t target_fw_pwr
+);
+
+#endif
diff --git a/firmware/inc/module/gate.h b/firmware/inc/module/gate.h
new file mode 100644
index 0000000..9f399c4
--- /dev/null
+++ b/firmware/inc/module/gate.h
@@ -0,0 +1,32 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#ifndef GATE_H_
+#define GATE_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <util/delay.h>
+
+#define GATE_PROBE_START 2800 // mV
+#define GATE_PROBE_LIMIT 3300 // mV
+#define GATE_PROBE_DRAIN 1000 // mV
+#define GATE_PROBE_INC 1 // COUNTS
+#define GATE_PA_POWER_DOWN 1
+#define GATE_LOOP_DELAY_US 1000
+
+uint16_t gate_probe(
+ uint16_t target_amp, // mA
+ uint16_t* probe_result
+);
+
+#endif
diff --git a/firmware/inc/peripheral/ring.h b/firmware/inc/peripheral/ring.h
new file mode 100644
index 0000000..d2a713d
--- /dev/null
+++ b/firmware/inc/peripheral/ring.h
@@ -0,0 +1,77 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#ifndef RING_H_
+#define RING_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct
+{
+ uint8_t *buf;
+ uint8_t *end;
+ uint8_t *write;
+ uint8_t *read;
+} ring_t;
+
+#define RING_INIT(buf_, len_) \
+ ((ring_t){ \
+ .buf = (buf_), \
+ .end = (buf_)+(len_), \
+ .write = (buf_), \
+ .read = (buf_)} \
+ )
+
+ring_t ring_init(
+ uint8_t *buf,
+ size_t len
+);
+
+bool ring_is_empty(
+ ring_t ring
+);
+
+bool ring_is_full(
+ ring_t ring
+);
+
+size_t ring_push_available(
+ ring_t ring
+);
+
+size_t ring_pop_available(
+ ring_t ring
+);
+
+bool ring_push(
+ ring_t *ring,
+ uint8_t data
+);
+
+bool ring_push_over(
+ ring_t *ring,
+ uint8_t data
+);
+
+bool ring_pop(
+ ring_t *ring,
+ uint8_t *data
+);
+
+bool ring_peek(
+ ring_t *ring,
+ uint8_t *data
+);
+
+#endif
diff --git a/firmware/inc/peripheral/spi.h b/firmware/inc/peripheral/spi.h
new file mode 100644
index 0000000..003fc04
--- /dev/null
+++ b/firmware/inc/peripheral/spi.h
@@ -0,0 +1,34 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#ifndef SPI_H_
+#define SPI_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+void spi_init(void);
+
+bool spi_busy(void);
+
+void spi_flush(void);
+
+void spi_start(
+ uint8_t *out,
+ uint8_t *in,
+ size_t len,
+ uint8_t *port,
+ uint8_t pin
+);
+
+#endif
diff --git a/firmware/inc/peripheral/timer.h b/firmware/inc/peripheral/timer.h
new file mode 100644
index 0000000..ff45c85
--- /dev/null
+++ b/firmware/inc/peripheral/timer.h
@@ -0,0 +1,22 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#ifndef TIMER_H_
+#define TIMER_H_
+
+#include <stdint.h>
+
+void timer_init(void);
+void timer_reset(void);
+uint32_t timer_millis(void);
+
+#endif
diff --git a/firmware/inc/peripheral/twi.h b/firmware/inc/peripheral/twi.h
new file mode 100644
index 0000000..8f08777
--- /dev/null
+++ b/firmware/inc/peripheral/twi.h
@@ -0,0 +1,35 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#ifndef TWI_H_
+#define TWI_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#define TWI_ADDRESS_W(x) (((x) << 1) & ~0x01)
+#define TWI_ADDRESS_R(x) (((x) << 1) | 0x01)
+
+void twi_init(void);
+
+bool twi_busy(void);
+
+void twi_flush(void);
+
+void twi_start(
+ uint8_t address,
+ uint8_t *data,
+ size_t len
+);
+
+#endif
diff --git a/firmware/inc/peripheral/uart.h b/firmware/inc/peripheral/uart.h
new file mode 100644
index 0000000..e2a0f3d
--- /dev/null
+++ b/firmware/inc/peripheral/uart.h
@@ -0,0 +1,56 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#ifndef UARTINT_H_
+#define UARTINT_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+extern FILE uart_out;
+extern FILE uart_in;
+
+void uart_init(void);
+
+bool uart_tx(
+ uint8_t data
+);
+
+bool uart_rx(
+ uint8_t* data
+);
+
+size_t uart_tx_burst(
+ uint8_t* data,
+ size_t size
+);
+
+size_t uart_rx_burst(
+ uint8_t* data,
+ size_t size
+);
+
+size_t uart_tx_available(void);
+
+size_t uart_rx_available(void);
+
+bool uart_rx_peek(
+ uint8_t* data
+);
+
+void uart_tx_flush(void);
+
+char *uart_ngets(char *s, size_t n);
+
+#endif
diff --git a/firmware/inc/setup.h b/firmware/inc/setup.h
new file mode 100644
index 0000000..e01bcd3
--- /dev/null
+++ b/firmware/inc/setup.h
@@ -0,0 +1,39 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+/* Global Peripheral Configuration*/
+
+#define __AVR_ATmega328__
+
+#define F_CPU 8000000UL
+
+#define BAUD 19200
+#define UART_BUF_SIZE 25
+#define UART_OVERWRITE
+
+#define TWI_FREQUENCY 350000
+#define TWI_PRESCALER 1
+#define TWPS0_VALUE 0
+#define TWPS1_VALUE 0
+
+#define SPI_FREQUENCY 500000
+#define SPI_PRESCALER 16
+#define SPR0_VALUE 1
+#define SPR1_VALUE 0
+#define SPI2X_VALUE 0
+#define SPI_MODE 0
+#define SPI_DORD 0
+
+#define TRANSACT_DELAY_MS 5
+#define INIT_DELAY_MS 10
+
+#define SYS_VER_STR "1.01.5"
diff --git a/firmware/inc/status.h b/firmware/inc/status.h
new file mode 100644
index 0000000..b9c647b
--- /dev/null
+++ b/firmware/inc/status.h
@@ -0,0 +1,83 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#ifndef STATUS_H_
+#define STATUS_H_
+
+#include <stdint.h>
+#include <avr/pgmspace.h>
+
+extern const char* const gate_lut[];
+extern const char* const drain_lut[];
+extern const char* const system_lut[];
+
+enum
+{
+ GATE_STATUS_INACTIVE = 0,
+ GATE_STATUS_PROBE_ERROR,
+ GATE_STATUS_PROBE_SUCCESS,
+ GATE_STATUS_PROBE_TIMEOUT
+};
+
+typedef struct {
+ uint8_t status;
+ uint16_t q_volt;
+ uint16_t q_amp;
+ uint16_t q_time;
+ uint16_t v_set;
+ uint16_t q_drain;
+ uint8_t func;
+} t_gate_status;
+
+enum
+{
+ DRAIN_STATUS_INACTIVE = 0,
+ DRAIN_STATUS_POWER_CONTROL,
+ DRAIN_STATUS_OVERRIDE,
+ DRAIN_STATUS_ADC_ERROR,
+ DRAIN_STATUS_CURRENT_ALARM
+};
+
+typedef struct {
+ uint8_t status;
+ uint16_t v_set;
+ uint16_t v_adc;
+ uint16_t amp;
+ uint8_t func;
+} t_drain_status;
+
+enum
+{
+ SYSTEM_STATUS_IDLE = 0,
+ SYSTEM_STATUS_BUSY,
+ SYSTEM_STATUS_LOCKED,
+ SYSTEM_STATUS_SETPOINT_FAIL,
+ SYSTEM_STATUS_SETPOINT_REACHED,
+ SYSTEM_STATUS_SETPOINT_TIMEOUT,
+ SYSTEM_STATUS_VSWR_ALARM
+};
+
+typedef struct {
+ uint8_t status;
+ uint16_t v_fw;
+ int16_t p_fw;
+ uint16_t v_rev;
+ int16_t p_rev;
+ uint16_t swr;
+ uint16_t swr_lim;
+ uint16_t atten;
+ uint8_t p_strat;
+ int16_t p_set;
+ uint16_t d_time;
+} t_system_status;
+
+#endif
diff --git a/firmware/inc/util.h b/firmware/inc/util.h
new file mode 100644
index 0000000..5936a3a
--- /dev/null
+++ b/firmware/inc/util.h
@@ -0,0 +1,46 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include <stdint.h>
+
+#define UTIL_SYSTEM_VOLT ((uint32_t)3300) // (mV)
+#define UTIL_CONV_BITS 12
+#define UTIL_CONV_BIT(x) (1 << x)
+
+#define UTIL_COUPLER_ATTEN ((int16_t)4250) // BI-DIR COUPL CAL
+#define UTIL_POWER_THRESHOLD 2000
+
+uint8_t util_volt_to_count(
+ uint16_t volt,
+ uint16_t* count
+);
+
+uint8_t util_count_to_volt(
+ uint16_t count,
+ uint16_t* volt
+);
+
+int8_t util_count_to_pwr(
+ uint16_t count,
+ int16_t* cpwr
+);
+
+void util_print_pwr(
+ int16_t cpwr
+);
+
+uint16_t util_get_free_mem(void);
+
+#endif
diff --git a/firmware/src/bsp.c b/firmware/src/bsp.c
new file mode 100644
index 0000000..fdb8a39
--- /dev/null
+++ b/firmware/src/bsp.c
@@ -0,0 +1,38 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "setup.h"
+#include "bsp.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+#include <avr/interrupt.h>
+#include <avr/wdt.h>
+
+void bsp_soft_reset(void)
+{
+ cli();
+
+ MCUSR &= ~(1 << WDRF);
+
+ WDTCSR = (1 << WDCE) | (1 << WDE);
+ WDTCSR = (1 << WDP1);
+ WDTCSR = WDTCSR | 0x40;
+
+ MCUSR &= ~(1 << WDRF);
+
+ sei();
+
+ while(1){};
+}
diff --git a/firmware/src/cmd.c b/firmware/src/cmd.c
new file mode 100644
index 0000000..f55ed80
--- /dev/null
+++ b/firmware/src/cmd.c
@@ -0,0 +1,27 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "cmd.h"
+
+#include <avr/pgmspace.h>
+
+const char* const cmd_lut[CMD_SIZE][CMD_ENTRY_SIZE] PROGMEM = {
+ {CMD_POWER_GET_STR, CMD_POWER_GET_DESC},
+ {CMD_POWER_SET_STR, CMD_POWER_SET_DESC},
+ {CMD_TX_STR, CMD_TX_DESC},
+ {CMD_RX_STR, CMD_RX_DESC},
+ {CMD_DRAIN_SET_STR, CMD_DRAIN_SET_DESC},
+ {CMD_PROBE_STR, CMD_PROBE_DESC},
+ {CMD_RESET_STR, CMD_RESET_DESC},
+ {CMD_REPORT_STR, CMD_REPORT_DESC},
+ {CMD_HELP_STR, CMD_HELP_DESC}
+ };
diff --git a/firmware/src/driver/f1956.c b/firmware/src/driver/f1956.c
new file mode 100644
index 0000000..3af5fba
--- /dev/null
+++ b/firmware/src/driver/f1956.c
@@ -0,0 +1,61 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "setup.h"
+
+#include "peripheral/spi.h"
+#include "driver/f1956.h"
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+
+void att_init(uint8_t init_value)
+{
+ F1956_DEV_0_DDR |= (1 << F1956_DEV_0_SS);
+ F1956_DEV_0_PORT |= (1 << F1956_DEV_0_SS);
+
+ att_set(F1956_ID_INPUT_ATT, init_value);
+ _delay_ms(10);
+}
+
+void att_set(
+ uint8_t id,
+ uint8_t value
+)
+{
+ uint8_t dev_id = 0x0;
+ uint8_t dev_ss = 0x0;
+ uint8_t* dev_port = 0x0;
+ uint8_t cmd_buf[2] = {0x0, 0x0};
+
+ switch (id)
+ {
+ case F1956_ID_INPUT_ATT:
+ dev_id = F1956_DEV_INPUT_ATT;
+ break;
+ }
+
+ switch (dev_id)
+ {
+ case F1956_DEV_INPUT_ATT:
+ dev_ss = F1956_DEV_0_SS;
+ dev_port = (uint8_t*)&F1956_DEV_0_PORT;
+ break;
+ }
+
+ cmd_buf[0] = value & 0x7F;
+ cmd_buf[1] = F1956_DEV_INPUT_ATT_ADDR;
+
+ spi_start(cmd_buf, cmd_buf, sizeof(cmd_buf), dev_port, dev_ss);
+ spi_flush();
+}
diff --git a/firmware/src/driver/mcp3202.c b/firmware/src/driver/mcp3202.c
new file mode 100644
index 0000000..07f84db
--- /dev/null
+++ b/firmware/src/driver/mcp3202.c
@@ -0,0 +1,129 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "setup.h"
+
+#include "peripheral/spi.h"
+#include "driver/mcp3202.h"
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+
+void adc_init(void)
+{
+ MCP3202_DEV_0_DDR |= (1 << MCP3202_DEV_0_SS);
+ MCP3202_DEV_1_DDR |= (1 << MCP3202_DEV_1_SS);
+
+ MCP3202_DEV_0_PORT &= ~(1 << MCP3202_DEV_0_SS);
+ MCP3202_DEV_1_PORT &= ~(1 << MCP3202_DEV_1_SS);
+
+ _delay_ms(TRANSACT_DELAY_MS);
+
+ MCP3202_DEV_0_PORT |= (1 << MCP3202_DEV_0_SS);
+ MCP3202_DEV_1_PORT |= (1 << MCP3202_DEV_1_SS);
+
+ _delay_ms(TRANSACT_DELAY_MS);
+}
+
+uint16_t adc_read(
+ uint8_t id,
+ uint8_t conv_bypass
+)
+{
+ uint8_t dev_id = 0x0;
+ uint8_t dev_ch = 0x0;
+ uint8_t dev_ss = 0x0;
+ uint8_t* dev_port = 0x0;
+ uint8_t cmd_buf[3] = {0x0, 0x0, 0x0};
+ uint16_t adc_daq = 0x0;
+
+ switch(id)
+ {
+ case MCP3202_ID_FW_POWER:
+ dev_id = MCP3202_DEV_FW_POWER;
+ dev_ch = MCP3202_CHN_FW_POWER;
+ break;
+
+ case MCP3202_ID_REV_POWER:
+ dev_id = MCP3202_DEV_REV_POWER;
+ dev_ch = MCP3202_CHN_REV_POWER;
+ break;
+
+ case MCP3202_ID_DRAIN_VOLT:
+ dev_id = MCP3202_DEV_DRAIN_VOLT;
+ dev_ch = MCP3202_CHN_DRAIN_VOLT;
+ break;
+
+ case MCP3202_ID_DRAIN_AMP:
+ dev_id = MCP3202_DEV_DRAIN_AMP;
+ dev_ch = MCP3202_CHN_DRAIN_AMP;
+ break;
+ }
+
+ switch(dev_id)
+ {
+ case MCP3202_DEV_0:
+ dev_port = (uint8_t*)&MCP3202_DEV_0_PORT;
+ dev_ss = MCP3202_DEV_0_SS;
+ break;
+
+ case MCP3202_DEV_1:
+ dev_port = (uint8_t*)&MCP3202_DEV_1_PORT;
+ dev_ss = MCP3202_DEV_1_SS;
+ break;
+ }
+
+ cmd_buf[0] = MCP3202_REQ_START;
+ cmd_buf[1] = (dev_ch == MCP3202_CHN_0) ? MCP3202_REQ_CH0 : MCP3202_REQ_CH1;
+ cmd_buf[2] = MCP3202_REQ_PAD;
+
+ spi_start(cmd_buf, cmd_buf, sizeof(cmd_buf), dev_port, dev_ss);
+ spi_flush();
+
+ adc_daq = (uint16_t)(cmd_buf[MCP3202_DAQ_MSB]);
+ adc_daq |= ((uint16_t)(cmd_buf[MCP3202_DAQ_LSB] & 0xF) << 8);
+ adc_daq = adc_daq & 0xFFF;
+
+ if (conv_bypass == 0)
+ {
+ switch(id)
+ {
+ case MCP3202_ID_DRAIN_VOLT:
+ adc_daq = (uint32_t)(((uint32_t)adc_daq * MCP3202_DRAIN_VOLT_GAIN)/10);
+ break;
+ case MCP3202_ID_DRAIN_AMP:
+ adc_daq = (uint32_t)(((uint32_t)adc_daq * 10)/MCP3202_DRAIN_AMP_GAIN);
+ break;
+ }
+ }
+
+ return (uint16_t)adc_daq;
+}
+
+uint16_t adc_read_n(
+ uint8_t id,
+ uint8_t n
+)
+{
+ uint32_t x = 0;
+ uint8_t i = 0;
+
+ for (i = 0; i < n; i++)
+ {
+ x += adc_read(id, 0);
+ }
+
+ x /= n;
+
+ return (uint16_t)x;
+}
diff --git a/firmware/src/driver/mcp4725.c b/firmware/src/driver/mcp4725.c
new file mode 100644
index 0000000..4cb9bd6
--- /dev/null
+++ b/firmware/src/driver/mcp4725.c
@@ -0,0 +1,101 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "setup.h"
+
+#include "peripheral/twi.h"
+#include "driver/mcp4725.h"
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+
+void dac_init(uint16_t init_value)
+{
+ dac_write(MCP4725_ID_GATE_VOLT, init_value, 0);
+ dac_write(MCP4725_ID_DRAIN_VOLT, init_value, 0);
+
+ _delay_ms(TRANSACT_DELAY_MS);
+}
+
+void dac_write(
+ uint8_t id,
+ uint16_t value,
+ uint8_t conv_bypass
+)
+{
+ uint8_t dev_addr = 0x0;
+ uint8_t cmd_buf[2] = {0x0, 0x0};
+ uint8_t drain_mode = 0x0;
+
+ switch(id)
+ {
+ case MCP4725_ID_GATE_VOLT:
+ dev_addr = TWI_ADDRESS_W(MCP4725_DEV_GATE_VOLT_ADDR);
+ break;
+ case MCP4725_ID_DRAIN_VOLT:
+ dev_addr = TWI_ADDRESS_W(MCP4725_DEV_DRAIN_VOLT_ADDR);
+ drain_mode = 1;
+ break;
+ }
+
+ if (conv_bypass == 0)
+ {
+ if (drain_mode == 1)
+ {
+ value = (uint16_t)(((uint32_t)value * 10)/MCP4725_DRAIN_GAIN);
+ }
+ }
+
+ cmd_buf[0] = ((value >> 8) & 0xF) | MCP4725_BIT_PD0(0) | MCP4725_BIT_PD1(0);
+ cmd_buf[1] = value & 0xFF;
+
+ twi_start(dev_addr, cmd_buf, sizeof(cmd_buf));
+ twi_flush();
+}
+
+uint16_t dac_read(
+ uint8_t id,
+ uint8_t conv_bypass
+)
+{
+ uint8_t dev_addr = 0x0;
+ uint8_t cmd_buf[3] = {0x0, 0x0, 0x0};
+ uint16_t value = 0x0;
+ uint8_t drain_mode = 0x0;
+
+ switch(id)
+ {
+ case MCP4725_ID_GATE_VOLT:
+ dev_addr = TWI_ADDRESS_R(MCP4725_DEV_GATE_VOLT_ADDR);
+ break;
+ case MCP4725_ID_DRAIN_VOLT:
+ dev_addr = TWI_ADDRESS_R(MCP4725_DEV_DRAIN_VOLT_ADDR);
+ drain_mode = 1;
+ break;
+ }
+
+ twi_start(dev_addr, cmd_buf, sizeof(cmd_buf));
+ twi_flush();
+
+ value = (((uint16_t)cmd_buf[1]) << 4) | (cmd_buf[2] >> 4);
+
+ if (conv_bypass == 0)
+ {
+ if (drain_mode)
+ {
+ value = (uint16_t)(((uint32_t)value * MCP4725_DRAIN_GAIN)/10);
+ }
+ }
+
+ return (value & 0xFFF);
+}
diff --git a/firmware/src/driver/si4468.c b/firmware/src/driver/si4468.c
new file mode 100644
index 0000000..17dd77d
--- /dev/null
+++ b/firmware/src/driver/si4468.c
@@ -0,0 +1,201 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "setup.h"
+
+#include "peripheral/spi.h"
+
+#include "driver/si4468.h"
+#include "driver/radio_config.h"
+#include "driver/device.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+#include <util/atomic.h>
+#include <avr/pgmspace.h>
+
+
+static const uint8_t config[] PROGMEM = RADIO_CONFIGURATION_DATA_ARRAY;
+
+void si4468_init(void)
+{
+ SI4468_DEV_0_DDR |= (1 << SI4468_DEV_0_SS);
+ SI4468_DEV_0_PORT |= (1 << SI4468_DEV_0_SS);
+}
+
+void si44468_get_device(
+ int id,
+ t_dev* device
+)
+{
+ uint8_t dev_id = 0x0;
+ uint8_t dev_ss = 0x0;
+ uint8_t* dev_port = 0x0;
+
+ device->dev_id = 0;
+ device->dev_ss = 0;
+ device->dev_port = 0;
+
+ switch (id)
+ {
+ case SI4468_ID_RADIO:
+ dev_id = SI4468_DEV_RADIO;
+ break;
+ }
+
+ switch (dev_id)
+ {
+ case SI4468_DEV_RADIO:
+ dev_ss = SI4468_DEV_0_SS;
+ dev_port = (uint8_t*)&SI4468_DEV_0_PORT;
+ break;
+ }
+
+ device->dev_id = dev_id;
+ device->dev_ss = dev_ss;
+ device->dev_port = dev_port;
+}
+
+static uint8_t si4468_get_response(
+ void* buf,
+ uint8_t len
+)
+{
+ t_dev device;
+ uint8_t cts = 0x0;
+ uint8_t* cmd_buf = 0x0;
+ uint16_t cmd_size = 0x0;
+ uint8_t i = 0;
+
+ si44468_get_device(SI4468_ID_RADIO, &device);
+
+ cmd_size = len + SI4468_GET_RESP_LEN;
+ cmd_buf = (uint8_t*)malloc((cmd_size));
+
+ cmd_buf[0] = SI4468_CMD_READ_CMD_BUFF;
+ cmd_buf[1] = SI4468_CTS;
+
+ spi_start(cmd_buf, cmd_buf, SI4468_GET_RESP_LEN, device.dev_port, device.dev_ss);
+ spi_flush();
+
+ cts = (cmd_buf[0] == SI4468_CTS);
+
+ if(cts)
+ {
+ if (len > 0)
+ {
+ for(i = 0x0; i < cmd_size; i++)
+ {
+ cmd_buf[i] = SI4468_CTS;
+ }
+
+ cmd_buf[0] = SI4468_CMD_READ_CMD_BUFF;
+
+ spi_start(cmd_buf, cmd_buf, cmd_size, (uint8_t*)&SI4468_DEV_0_PORT, SI4468_DEV_0_SS);
+ spi_flush();
+
+ for(i = SI4468_GET_RESP_LEN; i < cmd_size; i++)
+ {
+ ((uint8_t*)buf)[i - SI4468_GET_RESP_LEN] = cmd_buf[i];
+ }
+
+ }
+ }
+
+ free(cmd_buf);
+
+ return cts;
+}
+
+static uint8_t si4468_wait_for_response(
+ void* buf,
+ uint8_t buf_len
+)
+{
+ while(!si4468_get_response(buf, buf_len))
+ {
+ _delay_ms(10);
+ }
+
+ return 1;
+}
+
+static void si4468_exec_api(
+ void* in,
+ size_t in_len,
+ void* out,
+ uint8_t out_len
+)
+{
+ t_dev device;
+ si44468_get_device(SI4468_ID_RADIO, &device);
+
+ if (si4468_wait_for_response(0, 0))
+ {
+ spi_start(in, 0, in_len, device.dev_port, device.dev_ss);
+ spi_flush();
+
+ if (out != 0)
+ {
+ si4468_wait_for_response(out, out_len);
+ }
+ }
+}
+
+void si4468_apply_startup_config(void)
+{
+ uint16_t i = 0;
+ uint8_t buf[100];
+
+ for(i = 0; i < sizeof(config); i++)
+ {
+ memcpy_P(buf, &config[i], sizeof(buf));
+ uint8_t tmp = buf[0];
+ si4468_exec_api(&buf[1], tmp, 0, 0);
+ i += tmp;
+ }
+}
+
+void si4468_get_info(
+ si4468_info_t* info
+)
+{
+ uint8_t data[8] = {
+ SI4468_CMD_PART_INFO
+ };
+
+ si4468_exec_api(data, 1, data, 8);
+
+ info->chipRev = data[0];
+ info->part = (data[1]<<8) | data[2];
+ info->partBuild = data[3];
+ info->id = (data[4]<<8) | data[5];
+ info->customer = data[6];
+ info->romId = data[7];
+}
+
+void si4468_tx_mode(void)
+{
+ uint8_t packet[] = {SI4468_CMD_START_TX, 0,0,0, 0,0,0 };
+ si4468_exec_api(packet, sizeof(packet), 0, 0);
+}
+
+void si4468_rx_mode(void)
+{
+ uint8_t packet[] = {SI4468_CMD_CHANGE_STATE, 0x3};
+ si4468_exec_api(packet, sizeof(packet), 0, 0);
+}
diff --git a/firmware/src/main.c b/firmware/src/main.c
new file mode 100644
index 0000000..02d103d
--- /dev/null
+++ b/firmware/src/main.c
@@ -0,0 +1,343 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "setup.h"
+
+#include "peripheral/timer.h"
+#include "peripheral/uart.h"
+#include "peripheral/twi.h"
+#include "peripheral/spi.h"
+
+#include "driver/mcp3202.h"
+#include "driver/mcp4725.h"
+#include "driver/f1956.h"
+#include "driver/si4468.h"
+
+#include "module/gate.h"
+#include "module/drain.h"
+
+#include "cmd.h"
+#include "bsp.h"
+#include "status.h"
+#include "util.h"
+
+#include <avr/io.h>
+#include <util/delay.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h>
+#include <avr/wdt.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+extern t_gate_status gate_status;
+extern t_drain_status drain_status;
+extern t_system_status system_status;
+
+void system_info(void)
+{
+ CMD_PRINT("SYSTEM BOOT v" SYS_VER_STR);
+ CMD_PRINT("SYSTEM INIT");
+ printf("%d BYTES AVAILABLE\n", util_get_free_mem());
+}
+
+void setup(void)
+{
+ cli();
+
+ gate_status.status = GATE_STATUS_INACTIVE;
+ drain_status.status = DRAIN_STATUS_INACTIVE;
+ system_status.status = SYSTEM_STATUS_IDLE;
+
+ wdt_reset();
+ wdt_disable();
+
+ timer_init();
+
+ uart_init();
+ twi_init();
+ spi_init();
+
+ sei();
+
+ _delay_ms(INIT_DELAY_MS);
+
+ si4468_init();
+
+ adc_init();
+ att_init(0);
+ dac_init(0);
+
+ _delay_ms(INIT_DELAY_MS);
+
+ si4468_apply_startup_config();
+
+ _delay_ms(INIT_DELAY_MS);
+
+ si4468_rx_mode();
+}
+
+void report_system_status()
+{
+ uint16_t gate_v_set = 0x0;
+ uint16_t gate_v_count = 0x0;
+
+ uint16_t drain_v_set = 0x0;
+ uint16_t drain_v_adc = 0x0;
+ uint16_t drain_amp_count = 0x0;
+ uint16_t drain_amp = 0x0;
+ uint16_t drain_v_count = 0x0;
+
+ uint16_t fw_pwr_adc = 0x0;
+ uint16_t fw_pwr_volt = 0x0;
+ int16_t fw_pwr = 0x0;
+
+ // gate status
+
+ gate_status.q_drain = GATE_PROBE_DRAIN;
+ gate_v_count = dac_read(MCP4725_ID_GATE_VOLT, 0);
+ util_count_to_volt(gate_v_count, &gate_v_set);
+ gate_status.v_set = gate_v_set;
+
+
+ printf_P(PSTR("- GATE -\n"));
+ printf_P(PSTR("STATUS : %s\n"),
+ pgm_read_word(&gate_lut[gate_status.status]));
+ printf_P(PSTR("Q-VOLT (mV) : %d\n"), gate_status.q_volt);
+ printf_P(PSTR("Q-AMP (mA) : %d\n"), gate_status.q_amp);
+ printf_P(PSTR("Q-TIME (ms) : %d\n"), gate_status.q_time);
+ printf_P(PSTR("V-SET (mV) : %d\n"), gate_status.v_set);
+ printf_P(PSTR("Q-DRAIN (mV) : %d\n"), gate_status.q_drain);
+
+ // drain status
+ drain_v_count = dac_read(MCP4725_ID_DRAIN_VOLT, 0);
+ util_count_to_volt(drain_v_count, &drain_v_set);
+ drain_status.v_set = drain_v_set;
+
+ drain_v_count = adc_read(MCP3202_ID_DRAIN_VOLT, 0);
+ util_count_to_volt(drain_v_count, &drain_v_adc);
+ drain_status.v_adc = drain_v_adc;
+
+ drain_amp_count = adc_read(MCP3202_ID_DRAIN_AMP, 0);
+ util_count_to_volt(drain_amp_count, &drain_amp);
+ drain_status.amp = drain_amp;
+
+ printf_P(PSTR("- DRAIN -\n"));
+ printf_P(PSTR("STATUS : %s\n"),
+ pgm_read_word(&drain_lut[drain_status.status]));
+ printf_P(PSTR("V-ADC (mV) : %d\n"), drain_status.v_adc);
+ printf_P(PSTR("V-SET (mV) : %d\n"), drain_status.v_set);
+ printf_P(PSTR("AMP (mA) : %d\n"), drain_status.amp);
+
+ // system status
+ fw_pwr_adc = adc_read_n(MCP3202_ID_FW_POWER, DRAIN_POWER_SAMPLES);
+ util_count_to_volt(fw_pwr_adc, &fw_pwr_volt);
+ util_count_to_pwr(fw_pwr_adc, &fw_pwr);
+ system_status.v_fw = fw_pwr_volt;
+ system_status.p_fw = fw_pwr;
+
+ printf_P(PSTR("- SYSTEM -\n"));
+ printf_P(PSTR("STATUS : %s\n"),
+ pgm_read_word(&system_lut[system_status.status]));
+ printf_P(PSTR("V-FWD (mV) : %d\n"), system_status.v_fw);
+ printf_P(PSTR("P-FWD (cdBm) : %d\n"), system_status.p_fw);
+ printf_P(PSTR("P-SET (cdBm) : %d\n"), system_status.p_set);
+ printf_P(PSTR("T-DELTA (ms) : %d\n"), system_status.d_time);
+}
+
+void loop()
+{
+ char uart_buffer[UART_BUF_SIZE];
+ char cmd_buffer[UART_BUF_SIZE];
+ char* cmd_tok = 0x0;
+ int16_t argv[CMD_MAX_ARGS];
+ uint16_t argc = 0x0;
+ bool gate_probed = false;
+
+ while (1)
+ {
+ cmd_tok = 0x0;
+ argc = 0x0;
+
+ if(uart_ngets(uart_buffer, UART_BUF_SIZE))
+ {
+ cmd_tok = strtok(uart_buffer, " ");
+ strcpy(cmd_buffer, cmd_tok);
+
+ while ((cmd_tok = strtok(NULL, " ")) != NULL) {
+ argv[argc++] = atoi(cmd_tok);
+ }
+
+ if (CMD_SWITCH(CMD_REPORT, uart_buffer))
+ {
+ CMD_PRINT("DUMP SYSTEM REPORT");
+ report_system_status();
+ }
+
+ else if (CMD_SWITCH(CMD_POWER_GET, uart_buffer))
+ {
+ CMD_PRINT("DUMP POWER ADC");
+ uint16_t fw_pwr_adc = adc_read_n(MCP3202_ID_FW_POWER,10);
+ CMD_PRINT_FMT("%d", fw_pwr_adc);
+ }
+
+ else if (CMD_SWITCH(CMD_PROBE, uart_buffer))
+ {
+ uint16_t gate_probe_count = 0x0;
+ uint16_t drain_current = 0x0;
+ uint32_t time_start = 0x0;
+ uint16_t time_delta = 0x0;
+
+ if (!gate_probed)
+ {
+ CMD_PRINT("STARTING Q-PROBE");
+ CMD_PRINT_FMT("DRAIN_Q_AMP = %d mA", DRAIN_Q_AMP);
+
+ time_start = timer_millis();
+
+ si4468_rx_mode();
+ drain_current = gate_probe(DRAIN_Q_AMP, &gate_probe_count);
+
+ time_delta = (uint16_t)(timer_millis() - time_start);
+ gate_status.q_time = time_delta;
+
+ CMD_PRINT_FMT("PROBE COMPLETED IN %d ms", time_delta);
+
+ if (drain_current >= DRAIN_Q_AMP)
+ {
+ gate_status.status = GATE_STATUS_PROBE_SUCCESS;
+ CMD_PRINT_FMT("Q-PROBE SUCCEEDED, IQ = %d mA", drain_current);
+ gate_probed = true;
+ }
+ else
+ {
+ gate_status.status = GATE_STATUS_PROBE_ERROR;
+ CMD_PRINT_FMT("Q-PROBE FAILED, IQ = %d mA", drain_current);
+ continue;
+ }
+ }
+ else
+ {
+ CMD_ERROR("ACTIVE GATE PROBE");
+ }
+ }
+
+ else if (CMD_SWITCH(CMD_POWER_SET, cmd_buffer))
+ {
+ if (argc != 1)
+ {
+ CMD_ERROR("USAGE: POWER <cdBm>");
+ continue;
+ }
+
+ int16_t rf_power = argv[0];
+
+ if (rf_power < DRAIN_POWER_LOW_LIM ||
+ rf_power > DRAIN_POWER_HIGH_LIM ||
+ (rf_power % DRAIN_POWER_MODULUS) != 0)
+ {
+ CMD_ERROR("INVALID INPUT POWER");
+ continue;
+ }
+
+ if (!gate_probed)
+ {
+ CMD_PRINT("NO GATE PROBE");
+ continue;
+
+ }
+
+ system_status.p_set = rf_power;
+
+ CMD_PRINT_FMT("POWER SETPOINT = %d cdBm", rf_power);
+ CMD_PRINT("STARTING LINEAR SWEEP");
+ drain_sweep_pwr(rf_power);
+ CMD_PRINT_FMT("SWEEP COMPLETED IN %d ms", system_status.d_time);
+ }
+
+ else if (CMD_SWITCH(CMD_DRAIN_SET, cmd_buffer))
+ {
+ if (argc != 1)
+ {
+ CMD_ERROR("USAGE: DRAIN <count>");
+ continue;
+ }
+
+ uint16_t drain_count = argv[0];
+ if (drain_count > 4096)
+ {
+ CMD_ERROR("RANGE ERROR");
+ continue;
+ }
+
+ dac_write(MCP4725_ID_DRAIN_VOLT, drain_count, 1);
+ }
+
+ else if (CMD_SWITCH(CMD_RX, uart_buffer))
+ {
+ CMD_PRINT("RADIO RX MODE");
+ si4468_rx_mode();
+ }
+
+ else if (CMD_SWITCH(CMD_TX, uart_buffer))
+ {
+ CMD_PRINT("RADIO TX MODE");
+ si4468_tx_mode();
+ }
+
+ else if (CMD_SWITCH(CMD_HELP, uart_buffer))
+ {
+ uint16_t i = 0x0;
+
+ CMD_PRINT("DUMP COMMANDS");
+ printf("- COMMANDS -\n");
+ for(i = 0; i < CMD_SIZE; i++)
+ {
+ printf_P(PSTR("%-10s - %-10s\n"),
+ pgm_read_word(&cmd_lut[i][0]),
+ pgm_read_word(&cmd_lut[i][1]));
+ }
+ }
+
+ else if (CMD_SWITCH(CMD_LINK_REPLY, uart_buffer))
+ {
+ CMD_PRINT("HELLO");
+ }
+
+ else if (CMD_SWITCH(CMD_RESET, uart_buffer))
+ {
+ CMD_PRINT("SOFT RESET");
+ _delay_ms(10);
+
+ bsp_soft_reset();
+ }
+
+ else
+ {
+ CMD_PRINT("UNKNOWN COMMAND");
+ }
+ }
+ }
+}
+
+int main()
+{
+ setup();
+ system_info();
+
+ loop();
+
+ return 0;
+}
+
+
diff --git a/firmware/src/module/drain.c b/firmware/src/module/drain.c
new file mode 100644
index 0000000..1cb6110
--- /dev/null
+++ b/firmware/src/module/drain.c
@@ -0,0 +1,123 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "setup.h"
+
+#include "peripheral/timer.h"
+
+#include "driver/mcp3202.h"
+#include "driver/mcp4725.h"
+#include "driver/si4468.h"
+
+#include "module/gate.h"
+#include "module/drain.h"
+
+#include "util.h"
+#include "status.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+
+extern t_drain_status drain_status;
+extern t_system_status system_status;
+
+uint16_t drain_sweep_pwr(
+ int16_t target_fw_pwr
+)
+{
+ uint16_t drain_count = 0x0;
+ uint16_t drain_inc = 0x0;
+ uint16_t fw_pwr_adc = 0x0;
+ int16_t fw_pwr = 0x0;
+ uint16_t test_count = 0x0;
+ int16_t test_boundary = DRAIN_TEST_BOUNDARY;
+
+ system_status.status = SYSTEM_STATUS_BUSY;
+ drain_status.status = DRAIN_STATUS_POWER_CONTROL;
+
+ drain_count = dac_read(MCP4725_ID_DRAIN_VOLT, 1);
+ _delay_ms(TRANSACT_DELAY_MS);
+
+ si4468_tx_mode();
+ _delay_ms(TRANSACT_DELAY_MS);
+
+ dac_write(MCP4725_ID_DRAIN_VOLT, drain_count, 1);
+ _delay_ms(TRANSACT_DELAY_MS);
+
+ fw_pwr_adc = adc_read_n(MCP3202_ID_FW_POWER, 10);
+ util_count_to_pwr(fw_pwr_adc, &fw_pwr);
+
+ _delay_ms(TRANSACT_DELAY_MS);
+
+ timer_reset();
+ uint32_t time_start = timer_millis();
+ bool timeout = false;
+
+ while ((test_count < DRAIN_TEST_COUNT) &&
+ (drain_count < DRAIN_PROBE_LIMIT))
+ {
+ if ((timer_millis() - time_start) > DRAIN_SETPOINT_TIMEOUT_MS)
+ {
+ timeout = true;
+ break;
+ }
+
+ if (abs(fw_pwr - target_fw_pwr) > DRAIN_PROBE_INC_THRESHOLD)
+ {
+ drain_inc = DRAIN_PROBE_INC_COARSE;
+ }
+ else
+ {
+ drain_inc = DRAIN_PROBE_INC_FINE;
+ }
+
+ if (fw_pwr < (target_fw_pwr))
+ {
+ drain_count += drain_inc;
+ }
+ else if (fw_pwr > (target_fw_pwr))
+ {
+ drain_count -= drain_inc;
+ }
+
+ dac_write(MCP4725_ID_DRAIN_VOLT, drain_count, 1);
+ _delay_us(DRAIN_LOOP_DELAY_US);
+
+ fw_pwr_adc = adc_read_n(MCP3202_ID_FW_POWER, DRAIN_POWER_SAMPLES);
+ util_count_to_pwr(fw_pwr_adc, &fw_pwr);
+
+ if (fw_pwr <= (target_fw_pwr + test_boundary) &&
+ fw_pwr >= (target_fw_pwr - test_boundary))
+ {
+ test_count += 1;
+ }
+ }
+
+ system_status.d_time = (uint16_t)(timer_millis() - time_start);
+
+ if (timeout)
+ {
+ system_status.status = SYSTEM_STATUS_SETPOINT_TIMEOUT;
+ }
+ else if(test_count >= DRAIN_TEST_COUNT)
+ {
+ system_status.status = SYSTEM_STATUS_SETPOINT_REACHED;
+ }
+ else
+ {
+ system_status.status = SYSTEM_STATUS_SETPOINT_FAIL;
+ }
+
+ return test_count;
+}
diff --git a/firmware/src/module/gate.c b/firmware/src/module/gate.c
new file mode 100644
index 0000000..b143eab
--- /dev/null
+++ b/firmware/src/module/gate.c
@@ -0,0 +1,78 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "setup.h"
+
+#include "driver/mcp3202.h"
+#include "driver/mcp4725.h"
+
+#include "module/gate.h"
+
+#include "util.h"
+#include "status.h"
+
+#include <avr/interrupt.h>
+#include <util/delay.h>
+
+extern t_gate_status gate_status;
+extern t_system_status system_status;
+
+uint16_t gate_probe(
+ uint16_t target_amp,
+ uint16_t* probe_result
+)
+{
+ uint16_t probe_volt = 0x0;
+ uint16_t drain_amp = 0x0;
+ uint16_t gate_count = 0x0;
+ uint16_t drain_count = 0x0;
+
+ system_status.status = SYSTEM_STATUS_BUSY;
+
+ util_volt_to_count(GATE_PROBE_DRAIN, &drain_count);
+ util_volt_to_count(GATE_PROBE_START, &gate_count );
+
+ dac_write(MCP4725_ID_GATE_VOLT, gate_count, 0);
+ _delay_ms(TRANSACT_DELAY_MS);
+ dac_write(MCP4725_ID_DRAIN_VOLT, drain_count, 0);
+ _delay_ms(TRANSACT_DELAY_MS);
+
+ while((drain_amp < target_amp) && probe_volt < GATE_PROBE_LIMIT)
+ {
+ gate_count += GATE_PROBE_INC;
+ dac_write(MCP4725_ID_GATE_VOLT, gate_count, 0);
+ _delay_us(GATE_LOOP_DELAY_US);
+
+ drain_count = adc_read(MCP3202_ID_DRAIN_AMP, 0);
+ util_count_to_volt(drain_count, &drain_amp);
+ util_count_to_volt(gate_count, &probe_volt);
+ }
+
+ _delay_ms(TRANSACT_DELAY_MS);
+ drain_count = adc_read(MCP3202_ID_DRAIN_AMP, 0);
+ util_count_to_volt(drain_count, &drain_amp);
+
+ if(GATE_PA_POWER_DOWN)
+ {
+ _delay_ms(TRANSACT_DELAY_MS);
+ dac_write(MCP4725_ID_DRAIN_VOLT, 0, 0);
+ }
+
+ gate_status.q_amp = drain_amp;
+ gate_status.q_volt = probe_volt;
+
+ *probe_result = probe_volt;
+
+ system_status.status = SYSTEM_STATUS_IDLE;
+
+ return drain_amp;
+}
diff --git a/firmware/src/peripheral/ring.c b/firmware/src/peripheral/ring.c
new file mode 100644
index 0000000..eacb654
--- /dev/null
+++ b/firmware/src/peripheral/ring.c
@@ -0,0 +1,115 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "peripheral/ring.h"
+
+#define RING_INC_ROLL_OVER(n, s, e) (((n)+1>=(e)) ? (s) : (n)+1)
+
+ring_t ring_init(
+ uint8_t *buf,
+ size_t len
+)
+{
+ return RING_INIT(buf, len);
+}
+
+bool ring_is_empty(
+ ring_t ring
+)
+{
+ return ring.read == ring.write;
+}
+
+bool ring_is_full(
+ ring_t ring
+)
+{
+ return RING_INC_ROLL_OVER(ring.write, ring.buf, ring.end) == ring.read;
+}
+
+size_t ring_push_available(
+ ring_t ring
+)
+{
+ if(ring.write < ring.read)
+ return ring.read - ring.write - 1;
+ else
+ return (ring.end - ring.buf) - (ring.write - ring.read) - 1;
+}
+
+size_t ring_pop_available(
+ ring_t ring
+)
+{
+ if(ring.read <= ring.write)
+ return ring.write - ring.read;
+ else
+ return (ring.end - ring.buf) - (ring.read - ring.write);
+}
+
+bool ring_push(
+ ring_t *ring,
+ uint8_t data
+)
+{
+ if(ring_is_full(*ring))
+ return 1;
+
+ *ring->write = data;
+ ring->write = RING_INC_ROLL_OVER(ring->write, ring->buf, ring->end);
+
+ return 0;
+}
+
+bool ring_push_over(
+ ring_t *ring,
+ uint8_t data
+)
+{
+ *ring->write = data;
+ ring->write = RING_INC_ROLL_OVER(ring->write, ring->buf, ring->end);
+
+ if(ring->read == ring->write)
+ {
+ ring->read = RING_INC_ROLL_OVER(ring->read, ring->buf, ring->end);
+ return 1;
+ }
+
+ return 0;
+}
+
+bool ring_pop(
+ ring_t *ring,
+ uint8_t *data
+)
+{
+ if(ring_is_empty(*ring))
+ return 1;
+
+ *data = *ring->read;
+ ring->read = RING_INC_ROLL_OVER(ring->read, ring->buf, ring->end);
+
+ return 0;
+}
+
+bool ring_peek(
+ ring_t *ring,
+ uint8_t *data
+)
+{
+ if(ring_is_empty(*ring))
+ return 1;
+
+ *data = *ring->read;
+
+ return 0;
+}
diff --git a/firmware/src/peripheral/spi.c b/firmware/src/peripheral/spi.c
new file mode 100644
index 0000000..884985d
--- /dev/null
+++ b/firmware/src/peripheral/spi.c
@@ -0,0 +1,106 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "setup.h"
+
+#include "peripheral/spi.h"
+
+#include <stdio.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+
+#if SPI_MODE == 0
+ #define CPOL_VALUE 0
+ #define CPHA_VALUE 0
+#elif SPI_MODE == 1
+ #define CPOL_VALUE 0
+ #define CPHA_VALUE 1
+#elif SPI_MODE == 2
+ #define CPOL_VALUE 1
+ #define CPHA_VALUE 0
+#elif SPI_MODE == 3
+ #define CPOL_VALUE 1
+ #define CPHA_VALUE 1
+#else
+ #error "No valid SPI_MODE defined!"
+#endif
+
+#if SPI_DORD == 0
+ #define DORD_VALUE 0
+#elif SPI_DORD == 1
+ #define DORD_VALUE 1
+#else
+ #error "No valid SPI_DORD defined!"
+#endif
+
+static volatile uint8_t *spi_out;
+static volatile uint8_t *spi_in;
+static volatile size_t spi_len;
+static volatile uint8_t *spi_port;
+static volatile uint8_t spi_pin;
+
+void spi_init(void)
+{
+ DDRB |= (1 << DDB2) | (1 << DDB3) | (1 << DDB5);
+
+ SPSR |= (SPI2X_VALUE << SPI2X);
+ SPCR |= (1 << SPIE) | (1 << SPE) | (DORD_VALUE << DORD) | (1 << MSTR)
+ | (CPOL_VALUE << CPOL) | (CPHA_VALUE << CPHA)
+ | (SPR1_VALUE << SPR1) | (SPR0_VALUE << SPR0);
+}
+
+bool spi_busy(void)
+{
+ return spi_len;
+}
+
+void spi_flush(void)
+{
+ while(spi_len);
+}
+
+void spi_start(
+ uint8_t *out,
+ uint8_t *in,
+ size_t len,
+ uint8_t *port,
+ uint8_t pin
+)
+{
+ spi_flush();
+
+ spi_out = out;
+ spi_in = in;
+ spi_len = len;
+ spi_port = port;
+ spi_pin = pin;
+
+ if(spi_port)
+ *spi_port &= ~(1 << spi_pin);
+
+ SPDR = *spi_out++;
+}
+
+ISR(SPI_STC_vect)
+{
+ if(spi_in)
+ {
+ *spi_in++ = SPDR;
+ }
+
+ if(--spi_len)
+ SPDR = *spi_out++;
+ else
+ if(spi_port)
+ *spi_port |= (1 << spi_pin);
+}
diff --git a/firmware/src/peripheral/timer.c b/firmware/src/peripheral/timer.c
new file mode 100644
index 0000000..1caff1c
--- /dev/null
+++ b/firmware/src/peripheral/timer.c
@@ -0,0 +1,57 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "setup.h"
+
+#include "peripheral/timer.h"
+
+#include <stdint.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/atomic.h>
+
+uint32_t millis_count = 0;
+
+void timer_init(void)
+{
+ uint32_t ctc_overflow;
+
+ ctc_overflow = ((F_CPU / 1000) / 8);
+ TCCR1B |= (1 << WGM12) | (1 << CS11);
+
+ OCR1AH = (ctc_overflow >> 8);
+ OCR1AL = ctc_overflow;
+
+ TIMSK1 |= (1 << OCIE1A);
+}
+
+void timer_reset(void)
+{
+ ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
+ millis_count = 0;
+ }
+}
+
+uint32_t timer_millis(void)
+{
+ uint32_t millis;
+
+ ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
+ millis = millis_count;
+ }
+ return millis;
+}
+
+ISR(TIMER1_COMPA_vect)
+{
+ millis_count++;
+}
diff --git a/firmware/src/peripheral/twi.c b/firmware/src/peripheral/twi.c
new file mode 100644
index 0000000..671490b
--- /dev/null
+++ b/firmware/src/peripheral/twi.c
@@ -0,0 +1,114 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "setup.h"
+
+#include "peripheral/twi.h"
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+#include <util/twi.h>
+
+#define TWBR_VALUE ((F_CPU/TWI_FREQUENCY - 16) / (2 * TWI_PRESCALER))
+
+static uint8_t twi_address;
+static uint8_t* twi_data;
+static size_t twi_index;
+static size_t twi_len;
+
+void twi_init(void)
+{
+ TWBR = TWBR_VALUE;
+ TWSR = (TWPS1_VALUE << TWPS1) | (TWPS0_VALUE << TWPS0);
+
+ TWCR = (1 << TWINT) | (1 << TWEN);
+}
+
+bool twi_busy(void)
+{
+ return TWCR & (1<<TWIE);
+}
+
+void twi_flush(void)
+{
+ while(TWCR & (1<<TWIE));
+}
+
+void twi_start(
+ uint8_t address,
+ uint8_t *data,
+ size_t len
+)
+{
+ twi_flush();
+
+ twi_address = address;
+ twi_data = data;
+ twi_len = len;
+
+ TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN) | (1 << TWIE);
+}
+
+ISR(TWI_vect)
+{
+ switch(TW_STATUS)
+ {
+ case TW_START:
+ case TW_REP_START:
+ twi_index = 0;
+ TWDR = twi_address;
+ TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE);
+ break;
+
+ case TW_MT_SLA_ACK:
+ case TW_MT_DATA_ACK:
+ if(twi_index < twi_len)
+ {
+ TWDR = twi_data[twi_index++];
+ TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE);
+ }
+ else
+ {
+ TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
+ }
+ break;
+
+ case TW_MR_DATA_ACK:
+ twi_data[twi_index++] = TWDR;
+ case TW_MR_SLA_ACK:
+ if(twi_index < twi_len-1)
+ {
+ TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWEN) | (1 << TWIE);
+ }
+ else
+ {
+ TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE);
+ }
+ break;
+
+ case TW_MR_DATA_NACK:
+ twi_data[twi_index++] = TWDR;
+ TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
+ break;
+
+ case TW_MT_ARB_LOST:
+ TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN) | (1 << TWIE);
+ break;
+
+ case TW_MT_SLA_NACK:
+ case TW_MT_DATA_NACK:
+ case TW_MR_SLA_NACK:
+ default:
+ TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
+ }
+}
diff --git a/firmware/src/peripheral/uart.c b/firmware/src/peripheral/uart.c
new file mode 100644
index 0000000..defcf54
--- /dev/null
+++ b/firmware/src/peripheral/uart.c
@@ -0,0 +1,239 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "setup.h"
+
+#include "peripheral/ring.h"
+#include "peripheral/uart.h"
+
+#include "status.h"
+#include "cmd.h"
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/atomic.h>
+#include <util/setbaud.h>
+
+extern t_system_status system_status;
+
+static int uart_putc(
+ char c,
+ FILE *stream
+)
+{
+ (void)stream;
+
+ if(uart_tx(c))
+ return _FDEV_EOF;
+
+ return c;
+}
+
+static int uart_getc(
+ FILE *stream
+)
+{
+ uint8_t c;
+ (void)stream;
+
+ if(uart_rx(&c))
+ return _FDEV_EOF;
+
+ return c;
+}
+
+FILE uart_out = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE);
+FILE uart_in = FDEV_SETUP_STREAM(NULL, uart_getc, _FDEV_SETUP_READ);
+
+static volatile ring_t uart_tx_ring;
+static volatile ring_t uart_rx_ring;
+
+static volatile uint8_t uart_tx_buf[UART_BUF_SIZE];
+static volatile uint8_t uart_rx_buf[UART_BUF_SIZE];
+
+void uart_init(void)
+{
+ uart_tx_ring = ring_init((uint8_t*)uart_tx_buf, UART_BUF_SIZE - 1);
+ uart_rx_ring = ring_init((uint8_t*)uart_rx_buf, UART_BUF_SIZE - 1);
+
+ UBRR0H = UBRRH_VALUE;
+ UBRR0L = UBRRL_VALUE;
+ UCSR0B |= (1 << RXCIE0) | (1 << RXEN0) | (1 << TXEN0);
+ UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
+
+ stdout = &uart_out;
+ stdin = &uart_in;
+}
+
+bool uart_tx(
+ uint8_t data
+)
+{
+ bool ret;
+
+ while(ring_is_full(uart_tx_ring));
+
+ ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
+ {
+ ret = ring_push((ring_t*)&uart_tx_ring, data);
+ }
+
+ if(!ret)
+ UCSR0B |= (1 << UDRIE0);
+
+ return ret;
+}
+
+bool uart_rx(
+ uint8_t* data
+)
+{
+ bool ret;
+
+ ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
+ {
+ ret = ring_pop((ring_t*)&uart_rx_ring, data);
+ }
+
+ return ret;
+}
+
+size_t uart_tx_burst(
+ uint8_t *data,
+ size_t size
+)
+{
+ size_t i = 0;
+
+ while(i<size && !uart_tx(*data++))
+ i++;
+
+ return i;
+}
+
+size_t uart_rx_burst(
+ uint8_t* data,
+ size_t len
+)
+{
+ size_t i = 0;
+
+ while(i<len && !uart_rx(data++))
+ i++;
+
+ return i;
+}
+
+size_t uart_tx_available(void)
+{
+ size_t ret;
+
+ ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
+ {
+ ret = ring_push_available(uart_tx_ring);
+ }
+
+ return ret;
+}
+
+size_t uart_rx_available(void)
+{
+ size_t ret;
+
+ ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
+ {
+ ret = ring_pop_available(uart_rx_ring);
+ }
+
+ return ret;
+}
+
+bool uart_rx_peek(
+ uint8_t *data
+)
+{
+ bool ret;
+
+ ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
+ {
+ ret = ring_peek((ring_t*)&uart_rx_ring, data);
+ }
+
+ return ret;
+}
+
+void uart_tx_flush(void)
+{
+ bool empty;
+ do
+ {
+ ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
+ {
+ empty = ring_is_empty(uart_tx_ring);
+ }
+ } while(!empty);
+}
+
+char *uart_ngets(char *s, size_t n)
+{
+ uint8_t c;
+ static size_t i = 0;
+
+
+ while(!uart_rx(&c))
+ {
+ s[i++] = c;
+
+ if(c == '\n' || i >= n-1)
+ {
+ s[i] = '\0';
+ i = 0;
+
+ return s;
+ }
+ }
+
+ return NULL;
+}
+
+ISR(USART_UDRE_vect)
+{
+ uint8_t c;
+
+ if(!ring_pop((ring_t*)&uart_tx_ring, &c))
+ UDR0 = c;
+
+ else
+ UCSR0B &= ~(1 << UDRIE0);
+}
+
+ISR(USART_RX_vect)
+{
+ if (system_status.status == SYSTEM_STATUS_BUSY)
+ {
+ // system busy.. lock now!
+ CMD_ERROR("SYSTEM BUSY");
+ system_status.status = SYSTEM_STATUS_LOCKED;
+ }
+
+ if (system_status.status != SYSTEM_STATUS_LOCKED)
+ {
+ #ifdef UART_OVERWRITE
+ ring_push_over((ring_t*)&uart_rx_ring, UDR0);
+ #else
+ ring_push((ring_t*)&uart_rx_ring, UDR0);
+ #endif
+ }
+ else{
+ (uint8_t)UDR0;
+ }
+}
diff --git a/firmware/src/status.c b/firmware/src/status.c
new file mode 100644
index 0000000..d3bb9eb
--- /dev/null
+++ b/firmware/src/status.c
@@ -0,0 +1,46 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "status.h"
+#include <avr/pgmspace.h>
+
+t_gate_status gate_status;
+t_drain_status drain_status;
+t_system_status system_status;
+
+const char* const gate_lut[] PROGMEM =
+{
+ "INACTIVE",
+ "PROBE_ERR",
+ "PROBE_OK",
+ "PROBE_TIME"
+};
+
+const char* const drain_lut[] PROGMEM =
+{
+ "INACTIVE",
+ "POWER_SET",
+ "OVERRIDE",
+ "ADC_ERROR",
+ "AMP_ALARM"
+};
+
+const char* const system_lut[] PROGMEM =
+{
+ "IDLE",
+ "BUSY",
+ "LOCKED",
+ "SET_FAIL",
+ "SET_REACHED",
+ "SET_TIME",
+ "VSWR_ALARM"
+};
diff --git a/firmware/src/util.c b/firmware/src/util.c
new file mode 100644
index 0000000..af6ffed
--- /dev/null
+++ b/firmware/src/util.c
@@ -0,0 +1,152 @@
+/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "util.h"
+
+#include <stdio.h>
+#include <avr/pgmspace.h>
+
+extern int __heap_start, *__brkval;
+
+const uint16_t fw_pwr_gradient[] = { 249, 356, 374, 388, 400, 402, 435, 460, 500, 553 };
+const int16_t fw_pwr_intercept[] = { -5418, -3810, -3602, -3431, -3289, -3261, -2877, -2601, -2189, -1725 };
+const uint16_t fw_pwr_threshold[] = { 1288, 1548, 1704, 1812, 1897, 1967, 2164, 2301, 2411, 2505 };
+
+uint8_t util_volt_to_count(
+ uint16_t volt,
+ uint16_t* count
+)
+{
+ uint8_t bits = 0x0;
+ uint32_t x = 0x0;
+ uint32_t y = 0x0;
+ uint16_t i = 0x0;
+
+ bits = UTIL_CONV_BITS;
+
+ if (volt > UTIL_SYSTEM_VOLT)
+ {
+ return 1;
+ }
+
+ for (i = 0; i < bits; i++)
+ {
+ x |= UTIL_CONV_BIT(i);
+ }
+
+ y = ((uint32_t)volt) * x;
+ y /= UTIL_SYSTEM_VOLT;
+
+ *count = (uint16_t)y;
+ return 0;
+}
+
+uint8_t util_count_to_volt(
+ uint16_t count,
+ uint16_t* volt
+)
+{
+ uint8_t bits = 0x0;
+ uint32_t x = 0x0;
+ uint32_t y = 0x0;
+ uint16_t i = 0x0;
+
+ bits = UTIL_CONV_BITS;
+
+ for (i = 0; i < bits; i++)
+ {
+ x |= UTIL_CONV_BIT(i);
+ }
+
+ if (count > x)
+ {
+ return 1;
+ }
+
+ y = ((uint32_t)count) * UTIL_SYSTEM_VOLT;
+ y /= x;
+
+ *volt = (uint16_t)y;
+ return 0;
+}
+
+int8_t util_count_to_pwr(
+ uint16_t count,
+ int16_t* cpwr
+)
+{
+ uint32_t x = 0x0;
+ int16_t y = 0x0;
+ uint16_t slope = 0x0;
+ int16_t intercept = 0x0;
+ uint16_t i = 0;
+
+ uint8_t pwr_cal_size = sizeof(fw_pwr_gradient) / sizeof(uint16_t);
+ uint8_t pwr_last_element = pwr_cal_size - 1;
+
+ if(count >= fw_pwr_threshold[pwr_last_element])
+ {
+ slope = fw_pwr_gradient[pwr_last_element];
+ intercept = fw_pwr_intercept[pwr_last_element];
+ }
+ else if (count <= fw_pwr_threshold[0])
+ {
+ slope = fw_pwr_gradient[0];
+ intercept = fw_pwr_intercept[0];
+ }
+ else
+ {
+ for(i = 0; i < pwr_cal_size; i++)
+ {
+ if(count < fw_pwr_threshold[i])
+ {
+ slope = fw_pwr_gradient[i];
+ intercept = fw_pwr_intercept[i];
+ break;
+ }
+ }
+ }
+
+ x = (((uint32_t)count * 1000) / slope);
+ y = (int16_t)x;
+
+ y = y + intercept;
+ *cpwr = y;
+
+ return 0;
+}
+
+void util_print_pwr(
+ int16_t cpwr
+)
+{
+ int16_t integer_part = cpwr / 100;
+
+ int16_t fractional_part = cpwr % 100;
+
+ if (fractional_part < 0) {
+ fractional_part = -fractional_part;
+ }
+
+ printf("%d.%02d dBm\n", integer_part, fractional_part);
+}
+
+uint16_t util_get_free_mem(void)
+{
+ uint16_t free_mem;
+ if ((int)__brkval == 0) {
+ free_mem = (int)&free_mem - (int)&__heap_start;
+ } else {
+ free_mem = (int)&free_mem - (int)__brkval;
+ }
+ return free_mem;
+}