/** * anemometer_driver.c — RS485 风速仪 OOPC 驱动实现(不透明指针模式) * * 内部可见的完整结构体定义。外部调用者通过 .h 中的前向声明 * 只能拿到 AnemometerDriver_S* 指针,无法访问任何内部字段。 * * 设计参考: * - FreeModbus RTU 接收状态机(STATE_RX_IDLE / RCV / ERROR / IDLE) * - 3.5 字符超时机制 + 256 字节 PDU 溢出保护 * - 静态内存池分配(无 malloc) * * 寄存器映射(来自 modbus_param_framework.md): * - 风速值 0x0010, uint16, R, 单位 0.1m/s * - 从站地址 0x02, 轮询周期 500ms, 超时 100ms, 重试 3 次 */ #include "anemometer_driver.h" /* ---------- 编译期常量 ---------- */ #define RS485_MAX_MSG_BYTES 256U /* 最大 Modbus PDU 字节数(FreeModbus 标准上限) */ #define RS485_FRAME_MIN_BYTES 7U /* 最短有效帧:地址+功能码+2字节数据+2字节CRC = 7 */ #define POLL_INTERVAL_MS_DEFAULT 500U /* 默认轮询周期 ms */ #define TIMEOUT_MS_DEFAULT 100U /* 默认超时 ms(9600bps 约 10ms/字节) */ #define RETRY_MAX_DEFAULT 3U /* 最大重试次数 */ #define SLAVE_ADDR_DEFAULT 0x02U /* 风速仪默认从站地址 */ #define REG_WIND_SPEED 0x0010U /* 风速寄存器地址(uint16, 0.1m/s) */ #define REG_WIND_COUNT 0x02U /* 读取的寄存器个数 */ #define FUNC_READ_HOLDING 0x03U /* 03H 功能码 */ /* CRC16 Modbus 多项式 */ #define CRC_POLY 0xA001U /* * 3.5 字符超时基准(tick 计数): * 9600bps → 1 字节 ≈ 1.04ms → 3.5 字节 ≈ 3.64ms * 定时器 1ms tick → 向上取整 5 个 tick */ #define T35_TICK_COUNT 5U /* ---------- 接收状态机(来自 FreeModbus RTU 设计)---------- */ typedef enum { RX_STATE_INIT, /* 初始化:等待总线空闲 */ RX_STATE_IDLE, /* 空闲:等待第一个字节 */ RX_STATE_RCV, /* 接收:正在收帧 */ RX_STATE_ERROR /* 错误:缓冲区满,等待超时丢弃 */ } RxState_E; /* ---------- 完整结构体定义(仅此 .c 文件内部可见)---------- */ struct AnemometerDriver_S { USART_TypeDef* h_uart; /* 绑定的串口 */ uint8_t slave_addr; /* Modbus 从站地址 */ uint16_t poll_interval_ms; /* 轮询间隔 */ uint16_t timeout_ms; /* 超时阈值 */ uint8_t retry_max; /* 最大重试次数 */ volatile uint16_t wind_speed_raw; /* 最新风速 ×10(0xFFFF=无效) */ /* ----- 轮询管理 ----- */ volatile uint32_t tick_counter; /* 毫秒计数器(由 TimerISR 递增) */ volatile uint16_t next_poll_tick; /* 下次轮询的 tick 时间点 */ volatile uint8_t poll_in_flight; /* 1=正在等待回复 */ volatile uint8_t retry_counter; /* 当前重试次数 */ /* ----- 接收缓冲区与状态机 ----- */ volatile uint8_t rx_buf[RS485_MAX_MSG_BYTES]; volatile uint16_t rx_buf_pos; /* 当前写指针 */ volatile RxState_E rx_state; /* 接收状态 */ volatile uint16_t t35_ticks; /* 3.5 字符计时器 */ volatile uint8_t t35_enabled; /* 1=3.5 字符定时器已开启 */ volatile uint8_t frame_ready; /* 1=已收到完整帧待解析 */ /* ----- 发送缓冲区 ----- */ uint8_t tx_buf[8]; /* 03H 读请求帧(8 字节足矣) */ }; /* ---------- 静态内存池(无 malloc)---------- */ #define MAX_ANEMOMETER_INSTANCES 2U static AnemometerDriver_S g_anemometer_pool[MAX_ANEMOMETER_INSTANCES]; static uint8_t g_anemometer_used[MAX_ANEMOMETER_INSTANCES]; /* ---------- 内部私有函数前向声明 ---------- */ static uint16_t CRC16_Modbus(const uint8_t* buf, uint16_t len); static void SendReadRequest(AnemometerDriver_S* me); static void ParseResponse(AnemometerDriver_S* me); /* ======================================================================== * 公开 API 实现 * ======================================================================== */ AnemometerDriver_S* AnemometerDriver_Create(USART_TypeDef* h_uart, uint8_t slave_addr) { uint8_t i; AnemometerDriver_S* inst = NULL; /* 找一个未使用的实例槽位 */ for (i = 0; i < MAX_ANEMOMETER_INSTANCES; i++) { if (g_anemometer_used[i] == 0) { inst = &g_anemometer_pool[i]; g_anemometer_used[i] = 1; break; } } if (inst == NULL) { return NULL; /* 无可用槽位 */ } /* 初始化所有字段 */ inst->h_uart = h_uart; inst->slave_addr = slave_addr; inst->poll_interval_ms = POLL_INTERVAL_MS_DEFAULT; inst->timeout_ms = TIMEOUT_MS_DEFAULT; inst->retry_max = RETRY_MAX_DEFAULT; inst->wind_speed_raw = 0xFFFFU; /* 初始无效 */ inst->tick_counter = 0; inst->next_poll_tick = 0; inst->poll_in_flight = 0; inst->retry_counter = 0; inst->rx_buf_pos = 0; inst->rx_state = RX_STATE_INIT; inst->t35_ticks = 0; inst->t35_enabled = 0; inst->frame_ready = 0; return inst; } void AnemometerDriver_SetPollInterval(AnemometerDriver_S* me, uint16_t ms) { if (me != NULL && ms > 0) { me->poll_interval_ms = ms; } } uint16_t AnemometerDriver_GetWindSpeed(const AnemometerDriver_S* me) { if (me == NULL) { return 0xFFFFU; } return me->wind_speed_raw; } /* ======================================================================== * 轮询管理(主循环入口) * ======================================================================== */ void AnemometerDriver_Poll(AnemometerDriver_S* me) { if (me == NULL) return; uint32_t now = me->tick_counter; /* ----- 超时检测 ----- */ if (me->poll_in_flight) { uint32_t elapsed = now >= me->next_poll_tick ? (now - me->next_poll_tick) : (0xFFFFFFFFU - me->next_poll_tick + now + 1U); if (elapsed >= me->timeout_ms) { /* 超时 */ me->retry_counter++; if (me->retry_counter > me->retry_max) { /* 超过重试上限,标记无效 */ me->wind_speed_raw = 0xFFFFU; me->poll_in_flight = 0; me->retry_counter = 0; me->next_poll_tick = (uint16_t)(now + me->poll_interval_ms); } else { /* 重试发送 */ SendReadRequest(me); me->next_poll_tick = (uint16_t)(now + me->timeout_ms); } } } /* ----- 启动新轮询 ----- */ else { uint32_t diff = now >= me->next_poll_tick ? (now - me->next_poll_tick) : (0xFFFFFFFFU - me->next_poll_tick + now + 1U); if (diff >= me->poll_interval_ms) { SendReadRequest(me); me->poll_in_flight = 1; me->retry_counter = 0; me->next_poll_tick = (uint16_t)(now + me->timeout_ms); } } } /* ======================================================================== * 中断服务入口 * ======================================================================== */ /** * USART RXNE 中断处理——每收到一个字节调用一次。 * 内部维护 FreeModbus 风格的接收状态机。 */ void AnemometerDriver_ReceiveISR(AnemometerDriver_S* me) { if (me == NULL) return; uint8_t byte; /* 读数据寄存器并清除 RXNE 标志 */ byte = (uint8_t)(me->h_uart->DR & 0xFF); switch (me->rx_state) { case RX_STATE_INIT: /* 启动后激活 3.5 字符计时器,等待总线空闲后跳 IDLE */ me->t35_ticks = 0; me->t35_enabled = 1; break; case RX_STATE_ERROR: /* 收到噪声字节,静默丢弃。等 3.5 字符超时后自动复位到 IDLE。 */ break; case RX_STATE_IDLE: /* 第一个字节:开始收帧 */ me->rx_buf_pos = 0; me->rx_buf[me->rx_buf_pos++] = byte; me->rx_state = RX_STATE_RCV; me->t35_ticks = 0; me->t35_enabled = 1; break; case RX_STATE_RCV: if (me->rx_buf_pos < RS485_MAX_MSG_BYTES) { me->rx_buf[me->rx_buf_pos++] = byte; } else { /* 溢出:切换 ERROR 态,等超时丢掉 */ me->rx_state = RX_STATE_ERROR; } me->t35_ticks = 0; /* 每收到一个字节重置 t3.5 */ me->t35_enabled = 1; break; } } /** * 1ms 定时器中断入口——驱动 tick 计数器 + 3.5 字符超时判定。 */ void AnemometerDriver_TimerISR(AnemometerDriver_S* me) { if (me == NULL) return; /* 全局毫秒计数器(溢出后回绕,Poll 中用差值防溢出) */ me->tick_counter++; /* ----- 3.5 字符超时处理 ----- */ if (me->t35_enabled) { me->t35_ticks++; if (me->t35_ticks >= T35_TICK_COUNT) { me->t35_enabled = 0; switch (me->rx_state) { case RX_STATE_INIT: /* 总线空闲,进入接收就绪 */ me->rx_state = RX_STATE_IDLE; break; case RX_STATE_RCV: /* 收完一帧:检查最短长度 */ if (me->rx_buf_pos >= RS485_FRAME_MIN_BYTES) { me->frame_ready = 1; } /* 回到空闲,准备收下一帧 */ me->rx_state = RX_STATE_IDLE; break; case RX_STATE_ERROR: /* 溢出帧静默丢弃,回 IDLE */ me->rx_state = RX_STATE_IDLE; break; case RX_STATE_IDLE: /* 超时但不在接收态,不处理 */ break; } } } /* ----- 接收完成 → 在 ISR 外解析(frame_ready 标志由 Poll 消费)----- */ if (me->frame_ready && me->poll_in_flight) { ParseResponse(me); me->frame_ready = 0; me->poll_in_flight = 0; /* 安排下次轮询 */ me->next_poll_tick = (uint16_t)(me->tick_counter + me->poll_interval_ms); } } /* ======================================================================== * 内部私有函数 * ======================================================================== */ /** * 构造并发送 03H 读风速寄存器的请求帧 * 帧格式:[从站地址][03H][起始地址高][起始地址低][寄存器数高][寄存器数低][CRC低][CRC高] */ static void SendReadRequest(AnemometerDriver_S* me) { uint16_t crc; me->tx_buf[0] = me->slave_addr; me->tx_buf[1] = FUNC_READ_HOLDING; me->tx_buf[2] = (uint8_t)(REG_WIND_SPEED >> 8); /* 起始地址高字节 */ me->tx_buf[3] = (uint8_t)(REG_WIND_SPEED & 0xFF); /* 起始地址低字节 */ me->tx_buf[4] = 0x00; /* 寄存器数高 */ me->tx_buf[5] = REG_WIND_COUNT; /* 寄存器数低 */ crc = CRC16_Modbus(me->tx_buf, 6); me->tx_buf[6] = (uint8_t)(crc & 0xFF); /* CRC 低字节 */ me->tx_buf[7] = (uint8_t)(crc >> 8); /* CRC 高字节 */ /* 发送 8 字节帧 */ for (uint8_t i = 0; i < 8; i++) { USART_SendData(me->h_uart, me->tx_buf[i]); while (USART_GetFlagStatus(me->h_uart, USART_FLAG_TXE) == RESET) { /* 等待发送完成 */ } } } /** * 解析 03H 回复帧:校验从站地址 + 功能码 + CRC,提取风速值。 * 回复帧格式:[从站地址][03H][字节数][数据高][数据低][CRC低][CRC高] */ static void ParseResponse(AnemometerDriver_S* me) { uint8_t len = me->rx_buf_pos; uint16_t crc_received; uint16_t crc_calc; /* 最短有效回复:地址+功能码+字节数+2数据+2CRC = 7 字节 */ if (len < 7) { return; /* 帧太短,丢弃 */ } /* 校验从站地址 */ if (me->rx_buf[0] != me->slave_addr) { return; /* 不是发给我们的回复 */ } /* 校验功能码 */ if (me->rx_buf[1] != FUNC_READ_HOLDING) { return; /* 不是 03H 回复 */ } /* 校验 CRC(全帧 CRC,含头部) */ crc_received = (uint16_t)me->rx_buf[len - 1] << 8; crc_received |= (uint16_t)me->rx_buf[len - 2]; crc_calc = CRC16_Modbus(me->rx_buf, len - 2); if (crc_received != crc_calc) { return; /* CRC 不匹配 */ } /* 提取风速值(字节 3:4 是大端序 uint16) */ me->wind_speed_raw = (uint16_t)me->rx_buf[3] << 8; me->wind_speed_raw |= (uint16_t)me->rx_buf[4]; } /** * CRC16 Modbus 计算(LUT 查表法,0xA001 多项式) */ static uint16_t CRC16_Modbus(const uint8_t* buf, uint16_t len) { uint16_t crc = 0xFFFFU; for (uint16_t i = 0; i < len; i++) { crc ^= buf[i]; for (uint8_t j = 0; j < 8; j++) { if (crc & 0x0001U) { crc = (crc >> 1) ^ CRC_POLY; } else { crc >>= 1; } } } return crc; }