
/** file fsk_rx.c brief FSK 解调、码元记录及原始 fsk_tx 帧解析实现。 */#include fsk_rx.h#include simple_fft.h/* 当前 Fs64kHz、FFT 点数 N64因此频率格宽为 1kHz20kHz 对应 bin2024kHz 对应 bin24。 */#define FSK_RX_BIT0_BIN 20U#define FSK_RX_BIT1_BIN 24U#define FSK_RX_PREAMBLE_TRANSITIONS 6U#define FSK_RX_FIRST_SAMPLE_US 35000U#define FSK_RX_SYMBOL_US 20000U#define FSK_RX_RAW_FIRST_SAMPLE_US 15000U /* 本机同步启动时首码元后段取样留出 ADCFFT 结果更新时间。 */typedef enum{FSK_RX_SEARCH_SYNC 0, /* 逐 bit 搜索帧头的最后一个同步字节例如 D5。 */FSK_RX_READ_PAYLOAD, /* 已同步正在接收固定长度 DATA。 */FSK_RX_READ_CHECKSUM, /* DATA 完成等待发送端附加的校验字节。 */FSK_RX_READ_TAIL, /* 校验正确继续比较每一个帧尾字节。 */FSK_RX_FRAME_READY /* 完整帧已验证等待主循环 TakeFrame() 取走。 */} FSK_RX_ParseState_t;static TIM_HandleTypeDef *g_symbol_timer;static volatile uint16_t g_dma[FSK_RX_DMA_LENGTH];static volatile uint8_t g_first_half_ready;static volatile uint8_t g_second_half_ready;static float g_fft_input[FSK_RX_BLOCK_LENGTH];static SimpleFFT_Complex_t g_fft_output[FSK_RX_BLOCK_LENGTH];static uint8_t g_decision_bits[FSK_RX_DECISION_BUFFER_LENGTH]; /* 每块 FFT 的判频结果约每1ms一个。 */static uint16_t g_decision_count;static uint8_t g_symbol_bits[FSK_RX_SYMBOL_BUFFER_LENGTH]; /* TIM4 每20ms取得的真实通信码元。 */static uint16_t g_symbol_count;static uint8_t g_payload_bits[FSK_RX_MAX_PAYLOAD * 8U]; /* 只保存 DATA 区供逐 bit 回环比较。 */static uint16_t g_payload_bit_count;static volatile uint8_t g_latest_bit;static volatile uint8_t g_latest_bit_ready;static uint8_t g_head[FSK_RX_MAX_HEAD_LENGTH];static uint8_t g_head_length;static uint8_t g_tail[FSK_RX_MAX_TAIL_LENGTH];static uint8_t g_tail_length;static uint8_t g_payload_length;static FSK_RX_ChecksumType_t g_checksum_type;static uint8_t g_format_ready;static uint8_t g_raw_mode; /* 1不解析帧只按已知字节数直接收裸数据。 */static uint8_t g_last_decision;static uint8_t g_decision_valid;static uint8_t g_preamble_transitions;static uint8_t g_symbol_active;static uint8_t g_first_symbol_sample;static FSK_RX_ParseState_t g_parse_state;static uint8_t g_sync_shift;static uint8_t g_sync_bit_count;static uint8_t g_current_byte;static uint8_t g_current_bit_count;static uint8_t g_received_length;static uint8_t g_tail_index;static uint8_t g_checksum;static uint8_t g_frame[FSK_RX_MAX_PAYLOAD];static volatile uint8_t g_frame_ready;static uint8_t FSK_RX_DecideBit(const volatile uint16_t *samples);static uint8_t FSK_RX_UpdateChecksum(uint8_t value, uint8_t byte);static void FSK_RX_ResetParser(void);static void FSK_RX_ObserveDecision(uint8_t bit);static void FSK_RX_PushSymbol(uint8_t bit);static void FSK_RX_PushByte(uint8_t byte);static void FSK_RX_RecordDecision(uint8_t bit);static void FSK_RX_RecordSymbol(uint8_t bit);void FSK_RX_Init(TIM_HandleTypeDef *symbol_timer){uint16_t i;g_symbol_timer symbol_timer; /* 保存码元定时器传 NULL 时只做基础 FFT 判频。 */for (i 0U; i FSK_RX_DMA_LENGTH; i) g_dma[i] 0U;g_first_half_ready 0U;g_second_half_ready 0U;g_latest_bit 0U;g_latest_bit_ready 0U;g_format_ready 0U;FSK_RX_ClearRecords();FSK_RX_ResetParser();}uint8_t FSK_RX_StartRawReceive(uint8_t byte_length){if ((g_symbol_timer NULL) || (byte_length 0U) ||(byte_length FSK_RX_MAX_PAYLOAD)){return 0U;}/* 不需要前导码。main 在启动发送器前调用本函数使 TIM4 与第一个码元对齐。 */FSK_RX_ResetParser();g_raw_mode 1U;g_payload_length byte_length;g_symbol_active 1U;g_first_symbol_sample 1U;/** TIM4 当前计数频率为 1MHz。首样本取在第一个 20ms 码元开始后的 15ms* 不取 10ms 中点是为了确保 ADC DMA 至少完成若干个 1ms 数据块* g_latest_bit 已经从上一次的旧值更新为当前第一个码元的判频结果。* 后续仍每 20ms 取一次落在每个后续码元的同一相位。*/__HAL_TIM_SET_AUTORELOAD(g_symbol_timer, FSK_RX_RAW_FIRST_SAMPLE_US - 1U);__HAL_TIM_SET_COUNTER(g_symbol_timer, 0U);__HAL_TIM_CLEAR_FLAG(g_symbol_timer, TIM_FLAG_UPDATE);return (HAL_TIM_Base_Start_IT(g_symbol_timer) HAL_OK) ? 1U : 0U;}uint8_t FSK_RX_StartBasicTest(uint8_t byte_length){/** 55 的二进制为 01010101连续两个 55 可产生足够多的频率跳变* 使 FSK_RX_ObserveDecision() 锁定 TIM4 的 20ms 取样边界。* D5 是锁定完成后、用户数据开始前的固定分界字节。* 这些字节只用于本地测试同步TakeFrame() 不会把它们返回给 main。*/static const uint8_t test_sync[] {0x55U, 0x55U, 0xD5U};return FSK_RX_SetFrameFormat(test_sync, sizeof(test_sync), byte_length,FSK_RX_CHECKSUM_NONE, NULL, 0U);}uint8_t FSK_RX_SetFrameFormat(const uint8_t *head, uint8_t head_length,uint8_t payload_length, FSK_RX_ChecksumType_t checksum_type,const uint8_t *tail, uint8_t tail_length){uint8_t i;/* 先检查所有长度和指针再复制避免非法配置破坏正在工作的接收状态机。 */if ((head NULL) || (head_length 0U) || (head_length FSK_RX_MAX_HEAD_LENGTH) ||(payload_length 0U) || (payload_length FSK_RX_MAX_PAYLOAD) ||(checksum_type FSK_RX_CHECKSUM_CRC8_ATM) ||(tail_length FSK_RX_MAX_TAIL_LENGTH) || ((tail_length ! 0U) (tail NULL))){return 0U;}/* 将帧头/帧尾复制到模块内部后续主函数修改原数组也不会影响接收。 */for (i 0U; i head_length; i) g_head[i] head[i];for (i 0U; i tail_length; i) g_tail[i] tail[i];g_head_length head_length;g_tail_length tail_length;g_payload_length payload_length;g_checksum_type checksum_type;g_format_ready 1U;FSK_RX_ResetParser();return 1U;}volatile uint16_t *FSK_RX_GetDMABuffer(void) { return g_dma; }/* 这两个函数由 ADC3 DMA 中断回调调用只置标志FFT 必须留在主循环执行。 */void FSK_RX_OnADCHalfComplete(void) { g_first_half_ready 1U; }void FSK_RX_OnADCComplete(void) { g_second_half_ready 1U; }void FSK_RX_Process(void){uint8_t bit;if (g_first_half_ready ! 0U){g_first_half_ready 0U;bit FSK_RX_DecideBit(g_dma[0]); /* 第一半 DMA 的 64 个新采样点。 */g_latest_bit bit;g_latest_bit_ready 1U;FSK_RX_RecordDecision(bit);FSK_RX_ObserveDecision(bit);}if (g_second_half_ready ! 0U){g_second_half_ready 0U;bit FSK_RX_DecideBit(g_dma[FSK_RX_BLOCK_LENGTH]); /* 第二半 DMA 的 256 个新采样点。 */g_latest_bit bit;g_latest_bit_ready 1U;FSK_RX_RecordDecision(bit);FSK_RX_ObserveDecision(bit);}}uint8_t FSK_RX_TakeLatestBit(uint8_t *bit){if ((bit NULL) || (g_latest_bit_ready 0U)) return 0U;*bit g_latest_bit;g_latest_bit_ready 0U;return 1U;}const uint8_t *FSK_RX_GetDecisionBits(uint16_t *count){if (count ! NULL) *count g_decision_count;return g_decision_bits;}const uint8_t *FSK_RX_GetSymbolBits(uint16_t *count){if (count ! NULL) *count g_symbol_count;return g_symbol_bits;}uint16_t FSK_RX_CopySymbolBits(uint8_t *destination, uint16_t capacity){uint16_t index;uint16_t copy_count;/* 不允许向空地址复制容量为0也没有任何可复制元素。 */if ((destination NULL) || (capacity 0U)) return 0U;/* 若用户数组比内部记录数组小只复制前 capacity 个绝不越界。 */copy_count (g_symbol_count capacity) ? g_symbol_count : capacity;for (index 0U; index copy_count; index){destination[index] g_symbol_bits[index];}return copy_count;}const uint8_t *FSK_RX_GetPayloadBits(uint16_t *count){if (count ! NULL) *count g_payload_bit_count;return g_payload_bits;}void FSK_RX_ClearRecords(void){uint16_t i;for (i 0U; i FSK_RX_DECISION_BUFFER_LENGTH; i) g_decision_bits[i] 0U;for (i 0U; i FSK_RX_SYMBOL_BUFFER_LENGTH; i) g_symbol_bits[i] 0U;for (i 0U; i (FSK_RX_MAX_PAYLOAD * 8U); i) g_payload_bits[i] 0U;g_decision_count 0U;g_symbol_count 0U;g_payload_bit_count 0U;}void FSK_RX_OnSymbolTimerElapsed(TIM_HandleTypeDef *htim){uint8_t symbol;if ((htim ! g_symbol_timer) || (g_symbol_active 0U)) return;if (g_first_symbol_sample ! 0U){g_first_symbol_sample 0U;__HAL_TIM_SET_AUTORELOAD(g_symbol_timer, FSK_RX_SYMBOL_US - 1U);g_symbol_timer-Instance-EGR TIM_EGR_UG;__HAL_TIM_CLEAR_FLAG(g_symbol_timer, TIM_FLAG_UPDATE);}symbol g_latest_bit; /* 取最近一次 FFT 判频视为当前20ms码元的0或1。 */FSK_RX_RecordSymbol(symbol);FSK_RX_PushSymbol(symbol);}uint8_t FSK_RX_TakeFrame(uint8_t *data, uint8_t capacity, uint8_t *length){uint8_t i;if ((data NULL) || (length NULL) || (g_frame_ready 0U) ||(capacity g_received_length)) return 0U;for (i 0U; i g_received_length; i) data[i] g_frame[i];*length g_received_length;FSK_RX_ResetParser();return 1U;}uint8_t FSK_RX_TakeFullFrame(uint8_t *data, uint8_t capacity, uint8_t *length){uint8_t index 0U;uint8_t i;uint16_t full_length;/* 完整帧 帧头 DATA 可选校验 帧尾。 */full_length (uint16_t)g_head_length (uint16_t)g_received_length (uint16_t)g_tail_length ((g_checksum_type FSK_RX_CHECKSUM_NONE) ? 0U : 1U);if ((data NULL) || (length NULL) || (g_frame_ready 0U) ||(full_length capacity)){return 0U;}for (i 0U; i g_head_length; i) data[index] g_head[i];for (i 0U; i g_received_length; i) data[index] g_frame[i];if (g_checksum_type ! FSK_RX_CHECKSUM_NONE) data[index] g_checksum;for (i 0U; i g_tail_length; i) data[index] g_tail[i];*length index;FSK_RX_ResetParser(); /* 取走后重新等待下一帧前导码。 */return 1U;}static uint8_t FSK_RX_DecideBit(const volatile uint16_t *samples){float dc 0.0f;float energy0;float energy1;uint16_t i;/* 先计算并减去直流偏置避免 ADC 中点电压在频谱中形成很大的直流分量。 */for (i 0U; i FSK_RX_BLOCK_LENGTH; i) dc (float)samples[i];dc / (float)FSK_RX_BLOCK_LENGTH;for (i 0U; i FSK_RX_BLOCK_LENGTH; i) g_fft_input[i] (float)samples[i] - dc;if (SimpleFFT_ForwardReal(g_fft_input, g_fft_output, FSK_RX_BLOCK_LENGTH) 0U) return 0U;/* 分别计算两种候选载波所在频点的能量能量大的频率代表当前 bit。 */energy0 SimpleFFT_GetBinEnergy(g_fft_output, FSK_RX_BIT0_BIN);energy1 SimpleFFT_GetBinEnergy(g_fft_output, FSK_RX_BIT1_BIN);return (energy1 energy0) ? 1U : 0U;}static uint8_t FSK_RX_UpdateChecksum(uint8_t value, uint8_t byte){uint8_t bit;/* 三种规则与 fsk_tx.c 的 CalculateChecksum() 保持逐字节一致。 */if (g_checksum_type FSK_RX_CHECKSUM_SUM8) return (uint8_t)(value byte);if (g_checksum_type FSK_RX_CHECKSUM_XOR8) return (uint8_t)(value ^ byte);if (g_checksum_type FSK_RX_CHECKSUM_CRC8_ATM){value ^ byte;for (bit 0U; bit 8U; bit){value ((value 0x80U) ! 0U) ? (uint8_t)((value 1U) ^ 0x07U) :(uint8_t)(value 1U);}}return value;}static void FSK_RX_ResetParser(void){/* 复位协议状态时先停 TIM4ADC DMA/TIM8 不停止基础判频仍会继续运行。 */if (g_symbol_timer ! NULL) (void)HAL_TIM_Base_Stop_IT(g_symbol_timer);g_last_decision 0U;g_decision_valid 0U;g_preamble_transitions 0U;g_symbol_active 0U;g_first_symbol_sample 0U;g_parse_state FSK_RX_SEARCH_SYNC;g_sync_shift 0U;g_sync_bit_count 0U;g_current_byte 0U;g_current_bit_count 0U;g_received_length 0U;g_tail_index 0U;g_checksum 0U;g_frame_ready 0U;g_raw_mode 0U;}static void FSK_RX_ObserveDecision(uint8_t bit){if ((g_format_ready 0U) || (g_symbol_active ! 0U)) return;if (g_decision_valid 0U){g_last_decision bit;g_decision_valid 1U;return;}if (bit g_last_decision) return; /* 没有发生频率切换不属于交替前导码的跳变。 */g_last_decision bit;if (g_preamble_transitions FSK_RX_PREAMBLE_TRANSITIONS) g_preamble_transitions;if ((g_preamble_transitions FSK_RX_PREAMBLE_TRANSITIONS) (g_symbol_timer ! NULL)){g_symbol_active 1U;g_first_symbol_sample 1U;__HAL_TIM_SET_AUTORELOAD(g_symbol_timer, FSK_RX_FIRST_SAMPLE_US - 1U);__HAL_TIM_SET_COUNTER(g_symbol_timer, 0U);__HAL_TIM_CLEAR_FLAG(g_symbol_timer, TIM_FLAG_UPDATE);(void)HAL_TIM_Base_Start_IT(g_symbol_timer); /* 从这里起每20ms进入一次 TIM4 回调。 */}}static void FSK_RX_PushSymbol(uint8_t bit){bit (bit ! 0U) ? 1U : 0U;/** 裸字节测试不寻找同步字TIM4 的每次采样直接按 MSB first 组为字节。* 只要收到调用 StartRawReceive() 时指定的字节数便宣布数据可取。*/if (g_raw_mode ! 0U){if (g_payload_bit_count (FSK_RX_MAX_PAYLOAD * 8U)){g_payload_bits[g_payload_bit_count] bit;}g_current_byte (uint8_t)((g_current_byte 1U) | bit);g_current_bit_count;if (g_current_bit_count 8U){g_frame[g_received_length] g_current_byte;g_current_byte 0U;g_current_bit_count 0U;if (g_received_length g_payload_length){g_frame_ready 1U;g_parse_state FSK_RX_FRAME_READY;g_symbol_active 0U;(void)HAL_TIM_Base_Stop_IT(g_symbol_timer);}}return;}if (g_parse_state FSK_RX_SEARCH_SYNC){g_sync_shift (uint8_t)((g_sync_shift 1U) | bit);if (g_sync_bit_count 8U) g_sync_bit_count;/* 前导码阶段结束后只比较帧头最后一个字节通常 D5作为同步字。 */if ((g_sync_bit_count 8U) (g_sync_shift g_head[g_head_length - 1U])){uint8_t i;g_checksum 0U;for (i 0U; i g_head_length; i) g_checksum FSK_RX_UpdateChecksum(g_checksum, g_head[i]);g_parse_state FSK_RX_READ_PAYLOAD;g_current_byte 0U;g_current_bit_count 0U;}return;}if (g_parse_state FSK_RX_FRAME_READY) return;/* 同步字之后的 DATA bit 在组装为字节前立即保存主函数可逐 bit 对比发送端。 */if ((g_parse_state FSK_RX_READ_PAYLOAD) (g_payload_bit_count (FSK_RX_MAX_PAYLOAD * 8U))){g_payload_bits[g_payload_bit_count] bit;}g_current_byte (uint8_t)((g_current_byte 1U) | bit);g_current_bit_count;if (g_current_bit_count 8U){FSK_RX_PushByte(g_current_byte);g_current_byte 0U;g_current_bit_count 0U;}}static void FSK_RX_PushByte(uint8_t byte){if (g_parse_state FSK_RX_READ_PAYLOAD){g_frame[g_received_length] byte; /* DATA 字节按接收顺序写入帧缓存。 */g_checksum FSK_RX_UpdateChecksum(g_checksum, byte);if (g_received_length g_payload_length){g_parse_state (g_checksum_type FSK_RX_CHECKSUM_NONE) ?FSK_RX_READ_TAIL : FSK_RX_READ_CHECKSUM;if ((g_parse_state FSK_RX_READ_TAIL) (g_tail_length 0U)){g_frame_ready 1U;g_parse_state FSK_RX_FRAME_READY;}}return;}if (g_parse_state FSK_RX_READ_CHECKSUM){if (byte ! g_checksum) { FSK_RX_ResetParser(); return; } /* 校验错则丢弃整帧并重新找前导码。 */g_parse_state FSK_RX_READ_TAIL;if (g_tail_length 0U){g_frame_ready 1U;g_parse_state FSK_RX_FRAME_READY;}return;}if (g_parse_state FSK_RX_READ_TAIL){if (byte ! g_tail[g_tail_index]) { FSK_RX_ResetParser(); return; } /* 帧尾任一字节不一致也丢帧。 */g_tail_index;if (g_tail_index g_tail_length){g_frame_ready 1U;g_parse_state FSK_RX_FRAME_READY;g_symbol_active 0U;if (g_symbol_timer ! NULL) (void)HAL_TIM_Base_Stop_IT(g_symbol_timer);}}}static void FSK_RX_RecordDecision(uint8_t bit){if (g_decision_count FSK_RX_DECISION_BUFFER_LENGTH)g_decision_bits[g_decision_count] (bit ! 0U) ? 1U : 0U;}static void FSK_RX_RecordSymbol(uint8_t bit){if (g_symbol_count FSK_RX_SYMBOL_BUFFER_LENGTH)g_symbol_bits[g_symbol_count] (bit ! 0U) ? 1U : 0U;}