
/** file fsk_tx.c brief 独立 FSK 发送器实现。 */#include fsk_tx.h/* 以下三个 static 函数仅供本文件使用main.c 不需要、也不能直接调用它们。 */static uint8_t FSK_Transmitter_GetBit(const FSK_Transmitter_t *device, uint32_t bit_index);static uint8_t FSK_Transmitter_ApplyBit(FSK_Transmitter_t *device, uint8_t bit);static uint8_t FSK_Transmitter_CalculateChecksum(const uint8_t *data, uint16_t length,FSK_TX_ChecksumType_t type);/*** 初始化发送对象只保存“哪个 PWM 输出载波、哪个 TIM 定时推进 bit”等信息* 不会立即输出波形也不会启动定时器。必须先完成 PWM_Driver_Init()。*/uint8_t FSK_Transmitter_Init(FSK_Transmitter_t *device, PWM_Driver_t *pwm,TIM_HandleTypeDef *symbol_timer, uint32_t bit0_frequency_hz,uint32_t bit1_frequency_hz, float duty){if ((device NULL) || (pwm NULL) || (symbol_timer NULL) ||(bit0_frequency_hz 0U) || (bit1_frequency_hz 0U) ||!((duty 0.0f) (duty 1.0f))){return 0U;}device-pwm pwm; /* 保存 PWM 对象地址后续用它把频率切到 bit0/bit1。 */device-symbol_timer symbol_timer;device-bit0_frequency_hz bit0_frequency_hz;device-bit1_frequency_hz bit1_frequency_hz;device-duty duty;device-data NULL;device-bit_index 0U;device-total_bits 0U;device-busy 0U;device-initialized 1U;return 1U;}/*** 开始发送原始字节。数据不复制故 data 指向的数组在发送完成前不能被改写或释放。* 例如长度2时共发送16个码元每个码元的时长由 symbol_timer 的 PSC/ARR 决定。*/uint8_t FSK_Transmitter_SendBytes(FSK_Transmitter_t *device, const uint8_t *data,uint16_t length){if ((device NULL) || (device-initialized 0U) || (data NULL) ||(length 0U) || (device-busy ! 0U)){return 0U;}device-data data; /* 保存数组首地址定时器中断将持续从此处取下一 bit。 */device-bit_index 0U;device-total_bits (uint32_t)length * 8U; /* 字节数换成待发送的总 bit 数。 */device-busy 1U;/* 先输出第 0 位第一个完整码元结束后TIM2 中断才推进到下一位。 */if (FSK_Transmitter_ApplyBit(device, FSK_Transmitter_GetBit(device, 0U)) 0U){FSK_Transmitter_Stop(device);return 0U;}__HAL_TIM_SET_COUNTER(device-symbol_timer, 0U); /* 首个 bit 从完整码元时间开始计时。 */__HAL_TIM_CLEAR_FLAG(device-symbol_timer, TIM_FLAG_UPDATE);if (HAL_TIM_Base_Start_IT(device-symbol_timer) ! HAL_OK){FSK_Transmitter_Stop(device); /* 定时器未启动就不能留下持续载波。 */return 0U;}return 1U;}/*** 无校验组帧。此函数把三段内容复制进 device-frame_buffer因此 payload 即使是* 局部数组也安全随后仍由 SendBytes() 和定时器中断逐 bit 发送。*/uint8_t FSK_Transmitter_SendFrame(FSK_Transmitter_t *device,const uint8_t *head, uint8_t head_length,const uint8_t *payload, uint16_t payload_length,const uint8_t *tail, uint8_t tail_length){uint16_t index 0U;uint16_t i;uint32_t total_length (uint32_t)head_length (uint32_t)payload_length (uint32_t)tail_length;/* 长度为0时对应指针可以是NULL长度非0时数组地址必须有效。 */if ((device NULL) || (device-initialized 0U) || (device-busy ! 0U) ||((head_length ! 0U) (head NULL)) ||((payload_length ! 0U) (payload NULL)) ||((tail_length ! 0U) (tail NULL)) ||(total_length 0U) || (total_length FSK_TX_FRAME_BUFFER_SIZE)){return 0U;}/* 依次复制最终缓存内容严格为head - payload - tail。 */for (i 0U; i head_length; i) { device-frame_buffer[index] head[i]; }for (i 0U; i payload_length; i) { device-frame_buffer[index] payload[i]; }for (i 0U; i tail_length; i) { device-frame_buffer[index] tail[i]; }return FSK_Transmitter_SendBytes(device, device-frame_buffer, index);}/*** 带校验组帧。最终顺序固定为 head - payload - checksum(可选) - tail。* 校验字节放在帧尾之前是多数“帧尾结束符”协议的常用写法题目不同则改此处。*/uint8_t FSK_Transmitter_SendFrameChecked(FSK_Transmitter_t *device,const uint8_t *head, uint8_t head_length,const uint8_t *payload, uint16_t payload_length,FSK_TX_ChecksumType_t checksum_type,const uint8_t *tail, uint8_t tail_length){uint16_t index 0U;uint16_t i;uint8_t has_checksum (checksum_type ! FSK_TX_CHECKSUM_NONE) ? 1U : 0U;uint32_t total_length (uint32_t)head_length (uint32_t)payload_length (uint32_t)has_checksum (uint32_t)tail_length;if ((device NULL) || (device-initialized 0U) || (device-busy ! 0U) ||((head_length ! 0U) (head NULL)) ||((payload_length ! 0U) (payload NULL)) ||((tail_length ! 0U) (tail NULL)) ||(checksum_type FSK_TX_CHECKSUM_CRC8_ATM) || (total_length 0U) ||(total_length FSK_TX_FRAME_BUFFER_SIZE)){return 0U;}for (i 0U; i head_length; i) { device-frame_buffer[index] head[i]; }for (i 0U; i payload_length; i) { device-frame_buffer[index] payload[i]; }/* 此刻 index 正好是“帧头数据”的长度因此校验范围不含帧尾。 */if (has_checksum ! 0U){device-frame_buffer[index] FSK_Transmitter_CalculateChecksum(device-frame_buffer, index, checksum_type);index;}for (i 0U; i tail_length; i) { device-frame_buffer[index] tail[i]; }return FSK_Transmitter_SendBytes(device, device-frame_buffer, index);}/*** 码元定时器溢出时调用的入口。* 调用链TIM2_IRQHandler - HAL_TIM_IRQHandler - HAL_TIM_PeriodElapsedCallback* - 你在 tim.c 中写的 FSK_Transmitter_OnTimerElapsed(fsk_tx, htim)。* 此函数在中断环境运行只推进 bit 并切换 PWM禁止 HAL_Delay、printf、UART 发送。*/void FSK_Transmitter_OnTimerElapsed(FSK_Transmitter_t *device, TIM_HandleTypeDef *htim){if ((device NULL) || (device-busy 0U) || (htim ! device-symbol_timer)){return;}device-bit_index; /* 当前 bit 已保持一个完整码元现在移动到下一 bit。 */if (device-bit_index device-total_bits){FSK_Transmitter_Stop(device);return;}(void)FSK_Transmitter_ApplyBit(device, FSK_Transmitter_GetBit(device, device-bit_index));}/*** 停止当前帧先关闭码元定时器中断避免停止过程中又进入一次发送回调再关闭 PWM。* 可在主循环紧急停止也会在最后一个 bit 结束时由 OnTimerElapsed() 自动调用。*/void FSK_Transmitter_Stop(FSK_Transmitter_t *device){if ((device NULL) || (device-initialized 0U)) return;(void)HAL_TIM_Base_Stop_IT(device-symbol_timer);(void)PWM_Driver_Stop(device-pwm);device-busy 0U;device-data NULL;device-bit_index 0U;device-total_bits 0U;}/** 主循环查询接口1U 说明本帧尚未发完此时不要再次调用 SendBytes/SendFrame。 */uint8_t FSK_Transmitter_IsBusy(const FSK_Transmitter_t *device){return ((device ! NULL) (device-initialized ! 0U)) ? device-busy : 0U;}/*** 将“全局 bit 序号”换成 0 或 1。* 例如 data[0]0xA510100101bit_index 0、1、2 分别返回1、0、1。*/static uint8_t FSK_Transmitter_GetBit(const FSK_Transmitter_t *device, uint32_t bit_index){uint32_t byte_index bit_index / 8U;uint32_t bit_in_byte bit_index % 8U;/* MSB firstbit0 取字节的 bit7题目若规定 LSB first改为 bit_in_byte。 */return (uint8_t)((device-data[byte_index] (7U - bit_in_byte)) 0x01U);}/*** 真正把一个数据 bit 变成 PWM 载波0选 bit0_frequency_hz1选 bit1_frequency_hz。* 由于 PWM_Driver_SetFrequency() 不允许在运行中改 PSC/ARR顺序必须是* 停 PWM - 改频率 - 恢复占空比 - 启 PWM。示波器看到的就是此处产生的20/24kHz。*/static uint8_t FSK_Transmitter_ApplyBit(FSK_Transmitter_t *device, uint8_t bit){uint32_t frequency (bit 0U) ? device-bit0_frequency_hz : device-bit1_frequency_hz;/* PWM 驱动规定运行中不得改 ARR/PSC先停、改频率、设占空比、再启动。 */(void)PWM_Driver_Stop(device-pwm);if ((PWM_Driver_SetFrequency(device-pwm, frequency) ! PWM_STATUS_OK) ||(PWM_Driver_SetDuty(device-pwm, device-duty) ! PWM_STATUS_OK) ||(PWM_Driver_Start(device-pwm) ! PWM_STATUS_OK)){return 0U;}return 1U;}/*** 计算一个8位校验值SUM8是累加低8位XOR8是逐字节异或CRC8/ATM使用多项式0x07。* 本函数不发送任何数据只返回一个将被 SendFrameChecked() 放入帧尾前的字节。*/static uint8_t FSK_Transmitter_CalculateChecksum(const uint8_t *data, uint16_t length,FSK_TX_ChecksumType_t type){uint8_t value 0U;uint16_t i;uint8_t bit;for (i 0U; i length; i){if (type FSK_TX_CHECKSUM_SUM8){value (uint8_t)(value data[i]); /* uint8_t 自动只保留低8位。 */}else if (type FSK_TX_CHECKSUM_XOR8){value ^ data[i];}else if (type FSK_TX_CHECKSUM_CRC8_ATM){value ^ data[i];for (bit 0U; bit 8U; bit){value ((value 0x80U) ! 0U) ? (uint8_t)((value 1U) ^ 0x07U): (uint8_t)(value 1U);}}}return value;}