fsk_tx.c

fsk_tx.c

/** @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]=0xA5=10100101:bit_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 first:bit0 取字节的 bit7;题目若规定 LSB first,改为 >> bit_in_byte。 */
return (uint8_t)((device->data[byte_index] >> (7U - bit_in_byte)) & 0x01U);
}

/**
* 真正把一个数据 bit 变成 PWM 载波:0选 bit0_frequency_hz,1选 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;
}