init commit

This commit is contained in:
2026-02-05 00:37:55 -07:00
commit 2c87c57651
19 changed files with 1727 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
#ifndef STM32L4XX_HAL_CONF_H
#define STM32L4XX_HAL_CONF_H
/* ---- Oscillator values ---- */
#define HSE_VALUE 8000000U
#define HSE_STARTUP_TIMEOUT 100U
#define MSI_VALUE 4000000U
#define LSE_VALUE 32768U
#define LSE_STARTUP_TIMEOUT 5000U
#define HSI_VALUE 16000000U
#define LSI_VALUE 32000U
#define EXTERNAL_SAI1_CLOCK_VALUE 0U
#define EXTERNAL_SAI2_CLOCK_VALUE 0U
/* ---- HAL module selection ---- */
#define HAL_MODULE_ENABLED
#define HAL_CORTEX_MODULE_ENABLED
#define HAL_RCC_MODULE_ENABLED
#define HAL_GPIO_MODULE_ENABLED
#define HAL_DMA_MODULE_ENABLED
#define HAL_DAC_MODULE_ENABLED
#define HAL_ADC_MODULE_ENABLED
#define HAL_TIM_MODULE_ENABLED
#define HAL_UART_MODULE_ENABLED
#define HAL_PWR_MODULE_ENABLED
#define HAL_FLASH_MODULE_ENABLED
/* ---- Prefetch / caches ---- */
#define PREFETCH_ENABLE 1U
#define INSTRUCTION_CACHE_ENABLE 1U
#define DATA_CACHE_ENABLE 1U
/* ---- SysTick ---- */
#define TICK_INT_PRIORITY 15U
#define USE_RTOS 0U
/* ---- Include the required HAL headers ---- */
#ifdef HAL_RCC_MODULE_ENABLED
#include "stm32l4xx_hal_rcc.h"
#endif
#ifdef HAL_GPIO_MODULE_ENABLED
#include "stm32l4xx_hal_gpio.h"
#endif
#ifdef HAL_DMA_MODULE_ENABLED
#include "stm32l4xx_hal_dma.h"
#endif
#ifdef HAL_CORTEX_MODULE_ENABLED
#include "stm32l4xx_hal_cortex.h"
#endif
#ifdef HAL_ADC_MODULE_ENABLED
#include "stm32l4xx_hal_adc.h"
#endif
#ifdef HAL_DAC_MODULE_ENABLED
#include "stm32l4xx_hal_dac.h"
#endif
#ifdef HAL_TIM_MODULE_ENABLED
#include "stm32l4xx_hal_tim.h"
#endif
#ifdef HAL_UART_MODULE_ENABLED
#include "stm32l4xx_hal_uart.h"
#endif
#ifdef HAL_PWR_MODULE_ENABLED
#include "stm32l4xx_hal_pwr.h"
#endif
#ifdef HAL_FLASH_MODULE_ENABLED
#include "stm32l4xx_hal_flash.h"
#endif
/* ---- Assert ---- */
/* #define USE_FULL_ASSERT 1U */
#ifdef USE_FULL_ASSERT
#define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__))
void assert_failed(uint8_t *file, uint32_t line);
#else
#define assert_param(expr) ((void)0U)
#endif
#endif /* STM32L4XX_HAL_CONF_H */
+11
View File
@@ -0,0 +1,11 @@
[env:nucleo_l476rg]
platform = ststm32
board = nucleo_l476rg
framework = stm32cube
monitor_speed = 115200
build_flags =
-I../src
-Iinclude
build_src_filter =
+<*>
+<../../src/packet.c>
+233
View File
@@ -0,0 +1,233 @@
#include "hal_init.h"
/* ---- Handle instances ---- */
TIM_HandleTypeDef htim6;
DAC_HandleTypeDef hdac1;
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_dac1_ch1;
DMA_HandleTypeDef hdma_adc1;
UART_HandleTypeDef huart2;
/* ---- System clock: HSE(8MHz) -> PLL -> 80 MHz ---- */
static void clock_init(void)
{
RCC_OscInitTypeDef osc = {0};
osc.OscillatorType = RCC_OSCILLATORTYPE_HSE;
osc.HSEState = RCC_HSE_BYPASS; /* Nucleo uses ST-Link MCO as HSE */
osc.PLL.PLLState = RCC_PLL_ON;
osc.PLL.PLLSource = RCC_PLLSOURCE_HSE;
osc.PLL.PLLM = 1; /* 8 MHz / 1 = 8 MHz */
osc.PLL.PLLN = 20; /* 8 * 20 = 160 MHz VCO */
osc.PLL.PLLP = RCC_PLLP_DIV7;
osc.PLL.PLLQ = RCC_PLLQ_DIV2;
osc.PLL.PLLR = RCC_PLLR_DIV2; /* 160 / 2 = 80 MHz */
HAL_RCC_OscConfig(&osc);
RCC_ClkInitTypeDef clk = {0};
clk.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
clk.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
clk.AHBCLKDivider = RCC_SYSCLK_DIV1;
clk.APB1CLKDivider = RCC_HCLK_DIV1;
clk.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&clk, FLASH_LATENCY_4);
}
/* ---- GPIO ---- */
static void gpio_init(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef gpio = {0};
/* PA4 = DAC1_OUT1 (analog) */
gpio.Pin = GPIO_PIN_4;
gpio.Mode = GPIO_MODE_ANALOG;
gpio.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &gpio);
/* PA0 = ADC1_IN5 (analog) */
gpio.Pin = GPIO_PIN_0;
gpio.Mode = GPIO_MODE_ANALOG;
gpio.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &gpio);
/* PA2 = USART2_TX, PA3 = USART2_RX (AF7) */
gpio.Pin = GPIO_PIN_2 | GPIO_PIN_3;
gpio.Mode = GPIO_MODE_AF_PP;
gpio.Pull = GPIO_PULLUP;
gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
gpio.Alternate = GPIO_AF7_USART2;
HAL_GPIO_Init(GPIOA, &gpio);
}
/* ---- TIM6: 100 kHz trigger for DAC + ADC ---- */
static void tim6_init(void)
{
__HAL_RCC_TIM6_CLK_ENABLE();
htim6.Instance = TIM6;
htim6.Init.Prescaler = 0;
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
htim6.Init.Period = 799; /* 80 MHz / 800 = 100 kHz */
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_Base_Init(&htim6);
TIM_MasterConfigTypeDef master = {0};
master.MasterOutputTrigger = TIM_TRGO_UPDATE;
master.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim6, &master);
}
/* ---- DMA ---- */
static void dma_init(void)
{
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA1 Channel3 — DAC1 CH1 */
hdma_dac1_ch1.Instance = DMA1_Channel3;
hdma_dac1_ch1.Init.Request = DMA_REQUEST_6; /* DAC1_CH1 */
hdma_dac1_ch1.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_dac1_ch1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_dac1_ch1.Init.MemInc = DMA_MINC_ENABLE;
hdma_dac1_ch1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_dac1_ch1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_dac1_ch1.Init.Mode = DMA_CIRCULAR;
hdma_dac1_ch1.Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_Init(&hdma_dac1_ch1);
__HAL_LINKDMA(&hdac1, DMA_Handle1, hdma_dac1_ch1);
HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
/* DMA1 Channel1 — ADC1 */
hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Request = DMA_REQUEST_0; /* ADC1 */
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_Init(&hdma_adc1);
__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
/* ---- DAC1 CH1 on PA4 ---- */
static void dac_init(void)
{
__HAL_RCC_DAC1_CLK_ENABLE();
hdac1.Instance = DAC1;
HAL_DAC_Init(&hdac1);
DAC_ChannelConfTypeDef cfg = {0};
cfg.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
cfg.DAC_Trigger = DAC_TRIGGER_T6_TRGO;
cfg.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
cfg.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_DISABLE;
cfg.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
HAL_DAC_ConfigChannel(&hdac1, &cfg, DAC_CHANNEL_1);
}
/* ---- ADC1 IN5 on PA0 ---- */
static void adc_init(void)
{
__HAL_RCC_ADC_CLK_ENABLE();
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV1;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T6_TRGO;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc1.Init.OversamplingMode = DISABLE;
HAL_ADC_Init(&hadc1);
ADC_ChannelConfTypeDef ch = {0};
ch.Channel = ADC_CHANNEL_5;
ch.Rank = ADC_REGULAR_RANK_1;
ch.SamplingTime = ADC_SAMPLETIME_6CYCLES_5;
ch.SingleDiff = ADC_SINGLE_ENDED;
ch.OffsetNumber = ADC_OFFSET_NONE;
HAL_ADC_ConfigChannel(&hadc1, &ch);
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
}
/* ---- USART2: 115200 8N1 on PA2/PA3 ---- */
static void uart_init(void)
{
__HAL_RCC_USART2_CLK_ENABLE();
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart2);
}
/* ---- Public API ---- */
void hal_init_all(void)
{
HAL_Init();
clock_init();
gpio_init();
dma_init();
tim6_init();
dac_init();
adc_init();
uart_init();
}
void hal_dac_start(uint16_t *buf, uint32_t len)
{
HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t *)buf, len,
DAC_ALIGN_12B_R);
HAL_TIM_Base_Start(&htim6);
}
void hal_dac_stop(void)
{
HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1);
}
void hal_adc_start(uint16_t *buf, uint32_t len)
{
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)buf, len);
}
void hal_adc_stop(void)
{
HAL_ADC_Stop_DMA(&hadc1);
HAL_TIM_Base_Stop(&htim6);
}
/* ---- IRQ handlers ---- */
void DMA1_Channel3_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_dac1_ch1);
}
void DMA1_Channel1_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_adc1);
}
+23
View File
@@ -0,0 +1,23 @@
#ifndef HAL_INIT_H
#define HAL_INIT_H
#include "stm32l4xx_hal.h"
/* Peripheral handles — declared in hal_init.c */
extern TIM_HandleTypeDef htim6;
extern DAC_HandleTypeDef hdac1;
extern ADC_HandleTypeDef hadc1;
extern DMA_HandleTypeDef hdma_dac1_ch1;
extern DMA_HandleTypeDef hdma_adc1;
extern UART_HandleTypeDef huart2;
/* Full peripheral init sequence */
void hal_init_all(void);
/* DMA start/stop helpers */
void hal_dac_start(uint16_t *buf, uint32_t len);
void hal_dac_stop(void);
void hal_adc_start(uint16_t *buf, uint32_t len);
void hal_adc_stop(void);
#endif /* HAL_INIT_H */
+185
View File
@@ -0,0 +1,185 @@
#include "stm32l4xx_hal.h"
#include "hal_init.h"
#include "modem_hw.h"
#include "uart_print.h"
#include "packet.h"
#include <string.h>
/* ---- DMA buffers ---- */
static uint16_t dac_buf[MODEM_DMA_BUF_LEN];
static uint16_t adc_buf[MODEM_DMA_BUF_LEN];
/* ---- Modem state machines ---- */
static modem_tx_state_t tx_state;
static modem_rx_state_t rx_state;
/* ---- Flags set from ISR, processed in main loop ---- */
static volatile int tx_half_ready;
static volatile int tx_full_ready;
static volatile int rx_half_ready;
static volatile int rx_full_ready;
static volatile int rx_done_flag;
/* ---- DMA callbacks ---- */
void HAL_DAC_ConvHalfCpltCallbackCh1(DAC_HandleTypeDef *hdac)
{
(void)hdac;
tx_half_ready = 1;
}
void HAL_DAC_ConvCpltCallbackCh1(DAC_HandleTypeDef *hdac)
{
(void)hdac;
tx_full_ready = 1;
}
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
{
(void)hadc;
rx_half_ready = 1;
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
(void)hadc;
rx_full_ready = 1;
}
/* ---- Main ---- */
int main(void)
{
hal_init_all();
uart_printf("\r\n=== Cat-Radio STM32 Loopback Test ===\r\n");
uart_printf("Hello from Cat-Radio firmware!\r\n");
/* ---- Build test packet ---- */
packet_t pkt;
memset(&pkt, 0, sizeof(pkt));
pkt.header.dst = 0x01;
pkt.header.src = 0x02;
pkt.header.seq = 0x00;
pkt.header.flags = 0x00;
const char *msg = "Hello, Cat-Radio!";
pkt.payload_len = (uint8_t)strlen(msg);
memcpy(pkt.payload, msg, pkt.payload_len);
uint8_t frame[PACKET_MAX_FRAME];
size_t frame_len = packet_encode(&pkt, frame, sizeof(frame));
uart_printf("TX frame (%u bytes): ", (unsigned)frame_len);
uart_print_hex(frame, frame_len);
/* ---- Init modem state machines ---- */
modem_tx_init(&tx_state, frame, frame_len);
modem_rx_init(&rx_state);
/* Pre-fill the entire DAC buffer before starting DMA */
modem_tx_fill_buffer(&tx_state, &dac_buf[0]);
modem_tx_fill_buffer(&tx_state, &dac_buf[MODEM_HALF_BUF_LEN]);
/* Clear flags */
tx_half_ready = 0;
tx_full_ready = 0;
rx_half_ready = 0;
rx_full_ready = 0;
rx_done_flag = 0;
/* Start ADC DMA first (so it's ready when DAC starts outputting) */
hal_adc_start(adc_buf, MODEM_DMA_BUF_LEN);
/* Start DAC DMA (also starts TIM6, which triggers both DAC and ADC) */
hal_dac_start(dac_buf, MODEM_DMA_BUF_LEN);
uart_printf("DMA started. Waiting for loopback...\r\n");
/* ---- Main processing loop ---- */
uint32_t timeout_start = HAL_GetTick();
uint32_t timeout_ms = 30000; /* 30 second timeout */
while (!rx_done_flag) {
/* Process TX half-buffer requests */
if (tx_half_ready) {
tx_half_ready = 0;
modem_tx_fill_buffer(&tx_state, &dac_buf[0]);
}
if (tx_full_ready) {
tx_full_ready = 0;
modem_tx_fill_buffer(&tx_state, &dac_buf[MODEM_HALF_BUF_LEN]);
}
/* Process RX half-buffer requests */
if (rx_half_ready) {
rx_half_ready = 0;
if (modem_rx_process_buffer(&rx_state, &adc_buf[0]))
rx_done_flag = 1;
}
if (rx_full_ready) {
rx_full_ready = 0;
if (modem_rx_process_buffer(&rx_state, &adc_buf[MODEM_HALF_BUF_LEN]))
rx_done_flag = 1;
}
/* Timeout check */
if ((HAL_GetTick() - timeout_start) > timeout_ms) {
uart_printf("TIMEOUT: No frame detected after %lu ms\r\n",
timeout_ms);
break;
}
/* Sleep until next interrupt */
if (!rx_done_flag && !tx_half_ready && !tx_full_ready &&
!rx_half_ready && !rx_full_ready)
__WFI();
}
/* ---- Stop DMA ---- */
hal_adc_stop();
hal_dac_stop();
/* ---- Decode received frame ---- */
if (rx_state.phase == RX_DONE && rx_state.data_len > 0) {
uart_printf("RX raw data (%u bytes): ", (unsigned)rx_state.data_len);
uart_print_hex(rx_state.data, rx_state.data_len);
packet_t rx_pkt;
int rc = packet_decode(rx_state.data, rx_state.data_len, &rx_pkt);
if (rc == 0 && rx_pkt.crc_ok) {
uart_printf("CRC: OK\r\n");
uart_printf("Payload (%u bytes): ", rx_pkt.payload_len);
/* Print as string */
char payload_str[PACKET_MAX_PAYLOAD + 1];
memcpy(payload_str, rx_pkt.payload, rx_pkt.payload_len);
payload_str[rx_pkt.payload_len] = '\0';
uart_printf("%s\r\n", payload_str);
/* Verify content */
if (rx_pkt.payload_len == pkt.payload_len &&
memcmp(rx_pkt.payload, pkt.payload, pkt.payload_len) == 0) {
uart_printf("\r\n*** PASS: Loopback test successful! ***\r\n");
} else {
uart_printf("\r\n*** FAIL: Payload mismatch ***\r\n");
}
} else {
uart_printf("CRC: FAIL (rc=%d, crc_ok=%d)\r\n", rc,
rc == 0 ? rx_pkt.crc_ok : -1);
uart_printf("\r\n*** FAIL: CRC error ***\r\n");
}
} else {
uart_printf("\r\n*** FAIL: No frame detected ***\r\n");
}
/* Halt */
while (1)
__WFI();
}
/* ---- SysTick handler (HAL needs this) ---- */
void SysTick_Handler(void)
{
HAL_IncTick();
}
+196
View File
@@ -0,0 +1,196 @@
#include "modem_hw.h"
#include <math.h>
#include <string.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
/* ================================================================
* TX — streaming modulator
* ================================================================ */
void modem_tx_init(modem_tx_state_t *tx, const uint8_t *frame, size_t frame_len)
{
tx->frame = frame;
tx->frame_len = frame_len;
tx->byte_idx = 0;
tx->bit_idx = 7;
tx->phase = 0.0;
tx->done = 0;
}
void modem_tx_fill_buffer(modem_tx_state_t *tx, uint16_t *buf)
{
if (tx->done) {
/* Output mid-scale silence */
for (int i = 0; i < MODEM_HALF_BUF_LEN; i++)
buf[i] = 2048;
return;
}
/* Current bit determines frequency */
int b = (tx->frame[tx->byte_idx] >> tx->bit_idx) & 1;
float freq = b ? MODEM_FREQ_MARK : MODEM_FREQ_SPACE;
double phase_inc = 2.0 * M_PI * (double)freq / (double)MODEM_SAMPLE_RATE;
for (int i = 0; i < MODEM_HALF_BUF_LEN; i++) {
/* sin -> [-1, 1], map to [0, 4095] for 12-bit DAC */
float s = sinf((float)tx->phase);
buf[i] = (uint16_t)(((s + 1.0f) / 2.0f) * 4095.0f);
tx->phase += phase_inc;
}
/* Keep phase in [0, 2*PI) */
tx->phase = fmod(tx->phase, 2.0 * M_PI);
/* Advance to next bit */
if (tx->bit_idx == 0) {
tx->bit_idx = 7;
tx->byte_idx++;
if (tx->byte_idx >= tx->frame_len)
tx->done = 1;
} else {
tx->bit_idx--;
}
}
/* ================================================================
* RX — streaming demodulator with Goertzel
* ================================================================ */
static float goertzel_mag(const float *samples, size_t n,
float target_freq, float sample_rate)
{
float k = target_freq * (float)n / sample_rate;
float w = 2.0f * (float)M_PI * k / (float)n;
float coeff = 2.0f * cosf(w);
float s0 = 0.0f, s1 = 0.0f, s2 = 0.0f;
for (size_t i = 0; i < n; i++) {
s0 = samples[i] + coeff * s1 - s2;
s2 = s1;
s1 = s0;
}
return s1 * s1 + s2 * s2 - coeff * s1 * s2;
}
void modem_rx_init(modem_rx_state_t *rx)
{
memset(rx, 0, sizeof(*rx));
rx->phase = RX_HUNT;
rx->expected_bytes = -1;
rx->last_bit = -1;
}
int modem_rx_process_buffer(modem_rx_state_t *rx, const uint16_t *buf)
{
if (rx->phase == RX_DONE)
return 1;
/* Convert uint16_t ADC samples to float [-1, 1] */
static float fbuf[MODEM_HALF_BUF_LEN];
for (int i = 0; i < MODEM_HALF_BUF_LEN; i++)
fbuf[i] = ((float)buf[i] / 2048.0f) - 1.0f;
/* Goertzel to decide mark or space */
float mag_mark = goertzel_mag(fbuf, MODEM_HALF_BUF_LEN,
MODEM_FREQ_MARK, MODEM_SAMPLE_RATE);
float mag_space = goertzel_mag(fbuf, MODEM_HALF_BUF_LEN,
MODEM_FREQ_SPACE, MODEM_SAMPLE_RATE);
int bit = (mag_mark >= mag_space) ? 1 : 0;
/* Check if there's any meaningful signal */
float max_mag = (mag_mark > mag_space) ? mag_mark : mag_space;
rx->total_bits++;
if (max_mag < 1.0f) {
/* Very low energy — likely no signal */
rx->idle_count++;
if (rx->idle_count > 100) {
/* Timeout — no signal for too long */
if (rx->phase != RX_HUNT) {
rx->phase = RX_DONE;
return 1;
}
}
/* In HUNT mode with no signal, just skip */
if (rx->phase == RX_HUNT)
return 0;
} else {
rx->idle_count = 0;
}
switch (rx->phase) {
case RX_HUNT:
/* Look for alternating bits (preamble = 0xAA = 10101010) */
if (rx->last_bit >= 0 && bit != rx->last_bit) {
rx->preamble_count++;
} else if (rx->last_bit >= 0) {
/* Not alternating — reset if we haven't found enough preamble,
* otherwise transition to sync search since this might be
* the start of the sync word */
if (rx->preamble_count >= 8) {
/* We had good preamble, now start looking for sync */
rx->phase = RX_SYNC;
rx->shift_reg = 0;
/* Process this bit in SYNC state below */
rx->shift_reg = (rx->shift_reg << 1) | (uint16_t)bit;
rx->last_bit = bit;
return 0;
}
rx->preamble_count = 0;
}
rx->last_bit = bit;
break;
case RX_SYNC:
rx->shift_reg = (rx->shift_reg << 1) | (uint16_t)bit;
if (rx->shift_reg == 0x2D4B) {
/* Sync word found — transition to DATA */
rx->phase = RX_DATA;
rx->data_len = 0;
rx->bit_count = 0;
rx->current_byte = 0;
rx->expected_bytes = -1;
}
/* Safety: if we've been in SYNC too long without finding sync word */
if (rx->total_bits > 1000 && rx->phase == RX_SYNC) {
/* Fall back to HUNT */
rx->phase = RX_HUNT;
rx->preamble_count = 0;
rx->last_bit = -1;
}
break;
case RX_DATA:
rx->current_byte = (rx->current_byte << 1) | (uint8_t)bit;
rx->bit_count++;
if (rx->bit_count == 8) {
if (rx->data_len < RX_MAX_DATA_BYTES)
rx->data[rx->data_len] = rx->current_byte;
rx->data_len++;
rx->bit_count = 0;
rx->current_byte = 0;
/* First data byte is the length field */
if (rx->data_len == 1) {
uint8_t length = rx->data[0];
/* expected total bytes = length(1) + data(length) + crc(2) */
rx->expected_bytes = 1 + length + 2;
}
if (rx->expected_bytes > 0 &&
(int)rx->data_len >= rx->expected_bytes) {
rx->phase = RX_DONE;
return 1;
}
}
break;
case RX_DONE:
return 1;
}
return 0;
}
+68
View File
@@ -0,0 +1,68 @@
#ifndef MODEM_HW_H
#define MODEM_HW_H
#include <stddef.h>
#include <stdint.h>
/* ---- Constants ---- */
#define MODEM_SAMPLE_RATE 100000.0f
#define MODEM_BAUD_RATE 50.0f
#define MODEM_SAMPLES_PER_BIT 2000
#define MODEM_CENTER_FREQ 40000.0f
#define MODEM_SHIFT 100.0f
#define MODEM_FREQ_MARK (MODEM_CENTER_FREQ + MODEM_SHIFT / 2.0f) /* 40050 Hz */
#define MODEM_FREQ_SPACE (MODEM_CENTER_FREQ - MODEM_SHIFT / 2.0f) /* 39950 Hz */
/* DMA buffer: 4000 samples total (two halves of 2000) */
#define MODEM_DMA_BUF_LEN 4000
#define MODEM_HALF_BUF_LEN 2000
/* ---- TX state machine ---- */
typedef struct {
const uint8_t *frame;
size_t frame_len;
size_t byte_idx;
int bit_idx; /* 7 down to 0 */
double phase; /* continuous phase accumulator */
int done;
} modem_tx_state_t;
void modem_tx_init(modem_tx_state_t *tx, const uint8_t *frame, size_t frame_len);
/* Fill 2000 uint16_t DAC samples (one bit). Called from DMA ISR. */
void modem_tx_fill_buffer(modem_tx_state_t *tx, uint16_t *buf);
/* ---- RX state machine ---- */
typedef enum {
RX_HUNT, /* looking for alternating preamble bits */
RX_SYNC, /* accumulating bits looking for 0x2D4B */
RX_DATA, /* receiving data bytes per length field */
RX_DONE, /* frame received (or failure) */
} modem_rx_phase_t;
#define RX_MAX_DATA_BYTES 128
typedef struct {
modem_rx_phase_t phase;
/* Bit shift register for sync detection */
uint16_t shift_reg;
int preamble_count; /* # good alternating bits seen */
/* Data accumulation */
uint8_t data[RX_MAX_DATA_BYTES];
size_t data_len;
int bit_count; /* bits accumulated in current byte */
uint8_t current_byte;
int expected_bytes; /* -1 until length byte received */
int last_bit; /* for preamble alternation check */
/* Timeout */
int idle_count; /* consecutive bits with no energy */
int total_bits; /* total bits processed */
} modem_rx_state_t;
void modem_rx_init(modem_rx_state_t *rx);
/* Process 2000 uint16_t ADC samples (one bit period). Called from DMA ISR.
* Returns 1 when a complete frame has been received. */
int modem_rx_process_buffer(modem_rx_state_t *rx, const uint16_t *buf);
#endif /* MODEM_HW_H */
+27
View File
@@ -0,0 +1,27 @@
#include "uart_print.h"
#include "hal_init.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
void uart_printf(const char *fmt, ...)
{
char buf[256];
va_list ap;
va_start(ap, fmt);
int len = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (len > 0) {
if ((size_t)len >= sizeof(buf))
len = sizeof(buf) - 1;
HAL_UART_Transmit(&huart2, (uint8_t *)buf, (uint16_t)len, HAL_MAX_DELAY);
}
}
void uart_print_hex(const uint8_t *data, size_t len)
{
for (size_t i = 0; i < len; i++)
uart_printf("%02X ", data[i]);
uart_printf("\r\n");
}
+10
View File
@@ -0,0 +1,10 @@
#ifndef UART_PRINT_H
#define UART_PRINT_H
#include <stddef.h>
#include <stdint.h>
void uart_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
void uart_print_hex(const uint8_t *data, size_t len);
#endif /* UART_PRINT_H */