init commit
This commit is contained in:
@@ -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 */
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user