本次重构完成了 433MHz 全链路协议统一:实现了 AA TYPE LEN ID PAYLOAD SUM 统一帧格式;引入了基于 AUX 的 LBT 避让与 TX 优先调度,确保高频发送稳定;完成了端口语义化重命名;并成功集成了 IO 监控、RS485 及 W5500 以太网 的标准化打包转发,实现了多源数据的高效、稳定透传。

This commit is contained in:
edisondeng
2026-05-08 15:25:19 +08:00
parent de67b86952
commit 878379f101
11 changed files with 793 additions and 121 deletions

View File

@ -85,11 +85,89 @@ static uint8_t u1_rx_buffer[256];
static volatile uint16_t u1_rx_len = 0;
static volatile uint32_t u1_last_rx_time = 0;
/* === 485 设备的接收缓存 (UART3) 增加这三行!=== */
/* === 485 设备的接收缓存 (UART3) === */
static uint8_t u3_rx_buffer[512];
static volatile uint16_t u3_rx_len = 0;
static volatile uint32_t u3_last_rx_time = 0;
static volatile uint32_t u3_ignore_until = 0;
/* === 协议处理全局变量 === */
static uint16_t g_hb_seq = 0; /* 心跳序列号 */
static uint32_t g_last_hb_tick = 0; /* 上次心跳时间 */
static uint8_t g_last_io_state = 0xFF; /* 上次记录的 IO 状态,用于变化检测 */
/* === W5500 外部变量声明 === */
#if USE_W5500
extern uint16_t local_port;
extern uint8_t ethernet_buf[];
#endif
/**
* @brief 判断单片机与 433 模块连接的串口是否正忙于传输
* @return bool: true=串口引擎忙(需避让), false=串口空闲
*/
bool RF433_UART_Is_Busy(void)
{
// 仅查询 UART1 的引擎状态,不关心模块 AUX
return MultiUART_IsBusy(PORT_433);
}
/**
* @brief 计算并发送 RF433 协议数据包
* @param type: 协议类型 (0x10, 0x55, 0x48, 0xAA)
* @param payload: 载荷数据指针
* @param len: 载荷长度
*/
void RF433_SendPacket(uint8_t type, const uint8_t *payload, uint8_t len)
{
uint8_t frame[260];
uint16_t frame_idx = 0;
uint8_t checksum = 0;
frame[frame_idx++] = PROTO_START_BYTE; // AA
frame[frame_idx++] = type; // TYPE
frame[frame_idx++] = (uint8_t)(len + 1); // LEN (ID + Payload)
frame[frame_idx++] = MY_DEVICE_ID; // ID
if (len > 0 && payload != NULL) {
memcpy(&frame[frame_idx], payload, len);
frame_idx += len;
}
/* 计算校验和 (Sum8) */
for (uint16_t i = 0; i < frame_idx; i++) {
checksum += frame[i];
}
frame[frame_idx++] = checksum;
/* 通过 MultiUART 发送 */
MultiUART_Send(PORT_433, frame, frame_idx);
}
/**
* @brief 检查 433 无线信道是否忙碌 (发送保护区)
* @return bool: true=忙碌(需避让), false=空闲
*/
bool RF433_Is_Air_Busy(void)
{
return (HAL_GPIO_ReadPin(AUX_GPIO_Port, AUX_Pin) == GPIO_PIN_RESET);
}
/**
* @brief 读取当前 4 路 DI 的合并状态
* @return uint8_t: Bit0-3 对应 DI1-4
*/
uint8_t IO_Get_Current_State(void)
{
uint8_t state = 0;
if (HAL_GPIO_ReadPin(MCU_DI1_GPIO_Port, MCU_DI1_Pin) == GPIO_PIN_SET) state |= (1 << 0);
if (HAL_GPIO_ReadPin(MCU_DI2_GPIO_Port, MCU_DI2_Pin) == GPIO_PIN_SET) state |= (1 << 1);
if (HAL_GPIO_ReadPin(MCU_DI3_GPIO_Port, MCU_DI3_Pin) == GPIO_PIN_SET) state |= (1 << 2);
if (HAL_GPIO_ReadPin(MCU_DI4_GPIO_Port, MCU_DI4_Pin) == GPIO_PIN_SET) state |= (1 << 3);
return state;
}
/* W5500 variables */
#if USE_W5500
#define SOCKET_ID 0
@ -240,74 +318,36 @@ int main(void)
/* USER CODE BEGIN WHILE */
while (1)
{
/* === 1. 恢复系统的核心驱动引擎 === */
/* === 1. 核心通信驱动引擎 (最高优先级) === */
UART2_Print_Task();
MultiUART_Task();
IO_Monitor_Task();
/* === 2. 网络轮询任务 === */
#if USE_W5500
loopback_udps(SOCKET_ID, ethernet_buf, local_port);
#endif
/* === 3. 极速无乱码透传 === */
/* === 方案 A从 433 收到无线数据 -> 给上位机解析 === */
/* === 2. 无线接收透传 (433 -> 485/Debug) === */
#if (RF433_MODE == RF433_MODE_RX) || (RF433_MODE == RF433_MODE_BOTH)
// 如果 433 收到无线数据,直接透传输出,不进行解码
if (u1_rx_len > 0 && (HAL_GetTick() - u1_last_rx_time > 20))
{
static uint8_t temp_buf1[256];
__disable_irq();
__disable_irq();
uint16_t len = u1_rx_len;
memcpy(temp_buf1, (uint8_t*)u1_rx_buffer, len);
u1_rx_len = 0;
u1_rx_len = 0;
__enable_irq();
MultiUART_Send(PORT_RS485, (uint8_t*)u1_rx_buffer, len);
MultiUART_Send(PORT_DEBUG, (uint8_t*)u1_rx_buffer, len);
}
#endif
/* 🚀 终极上位机协议:[0xAA帧头] + [设备ID] + [来源接口] + [数据长度] + [纯净数据] */
if (len >= 2 && temp_buf1[0] == 0xAA)
{
uint8_t sender_id = temp_buf1[1];
uint16_t payload_len = len - 2;
uint8_t* payload_data = &temp_buf1[2];
/* 默认 0x03 为 RS485 透传数据 */
uint8_t source_type = 0x03;
/* 判断是不是 $DI 标签 */
if (payload_len >= 3 && payload_data[0] == '$' && payload_data[1] == 'D' && payload_data[2] == 'I')
{
source_type = 0x01; /* 也是 DI 口数据,但不砍标签了,直接全发过去 */
}
/* 判断是不是网口 [NET] 标签 */
else if (payload_len >= 5 && payload_data[0] == '[' && payload_data[1] == 'N' && payload_data[2] == 'E' && payload_data[3] == 'T' && payload_data[4] == ']')
{
source_type = 0x02; /* 0x02 代表是 网络口 收到的数据 */
payload_data += 5; /* 砍掉 "[NET]" 标签 */
payload_len -= 5;
}
/* ========================================================== */
/* === 3. 核心发送保护区 (TX 优先调度) === */
// 只有当我们单片机正在往 433 模块灌数时,才进行避让
// 只要单片机串口引擎一空闲,主循环就立刻开始处理后续采集任务
if (RF433_UART_Is_Busy()) {
continue;
}
if (payload_len > 0) {
/* 重新组装终极协议帧 */
uint8_t upper_buf[260];
upper_buf[0] = 0xAA; // Byte 0: 魔法帧头
upper_buf[1] = sender_id; // Byte 1: 发送设备 ID
upper_buf[2] = source_type; // Byte 2: 数据来源 (1=DI, 2=NET, 3=485)
upper_buf[3] = (uint8_t)payload_len; // Byte 3: 真实数据长度
memcpy(&upper_buf[4], payload_data, payload_len); // Byte 4~末尾: 绝对纯净的数据
/* 发送给 485 和电脑上位机 */
MultiUART_Send(PORT_UART3, upper_buf, payload_len + 4);
MultiUART_Send(PORT_UART2, upper_buf, payload_len + 4);
}
}
else
{
/* 普通无帧头干扰数据,按原样透传 */
MultiUART_Send(PORT_UART3, temp_buf1, len);
MultiUART_Send(PORT_UART2, temp_buf1, len);
}
/* === 方案 B从 485 收到设备数据 -> 穿上包装(附加ID) -> 通过 433 无线发射 === */
/* === 4. 实时数据采集与上报 (仅在非发射状态运行) === */
// (A) 485 来源数据处理 (Type 0x48)
#if USE_RS485
if (u3_rx_len > 0 && (HAL_GetTick() - u3_last_rx_time > 20))
{
static uint8_t temp_buf3[512];
@ -317,20 +357,45 @@ int main(void)
u3_rx_len = 0;
__enable_irq();
/* 🚀 制作“快递包”:在最前面加上 [0xAA] 和 [本机ID] */
static uint8_t rf_tx_buf[515];
rf_tx_buf[0] = 0xAA; // 贴上魔法帧头
rf_tx_buf[1] = MY_DEVICE_ID; // 贴上本机的身份证号
memcpy(&rf_tx_buf[2], temp_buf3, len); // 把 485 收到的真实数据塞进后面
/* 把带有身份证的完整包裹,通过 433 发射到空气中 */
MultiUART_Send(PORT_UART1, rf_tx_buf, len + 2);
RF433_SendPacket(PROTO_TYPE_485, temp_buf3, len);
}
#endif
// (B) I/O 状态监控与变化上报 (Type 0x10)
#if USE_IO_MONITOR
IO_Monitor_Task(); // 执行去抖扫描
uint8_t current_io = IO_Monitor_GetAllStates();
if (current_io != g_last_io_state) {
if (g_last_io_state == 0xFF) {
g_last_io_state = current_io;
} else {
g_last_io_state = current_io;
RF433_SendPacket(PROTO_TYPE_IO, &current_io, 1);
}
}
#endif
// (C) 30秒系统心跳包 (Type 0xAA)
#if USE_IO_MONITOR
if (HAL_GetTick() - g_last_hb_tick >= 30000) {
g_last_hb_tick = HAL_GetTick();
uint8_t hb_payload[3];
hb_payload[0] = IO_Monitor_GetAllStates();
hb_payload[1] = (uint8_t)(g_hb_seq >> 8);
hb_payload[2] = (uint8_t)(g_hb_seq & 0xFF);
g_hb_seq++;
RF433_SendPacket(PROTO_TYPE_HB, hb_payload, 3);
}
#endif
// (D) W5500 以太网轮询处理 (Type 0x55)
#if USE_W5500
loopback_udps(SOCKET_ID, ethernet_buf, local_port);
#endif
/* USER CODE END WHILE */
}
}
}
/* USER CODE END 3 */
/**
* @brief System Clock Configuration
@ -433,7 +498,7 @@ void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
if (huart->Instance == USART1)
{
/* 调用多UART路由器的UART1发送完成回调 */
MultiUART_TxCpltCallback(PORT_UART1);
MultiUART_TxCpltCallback(PORT_433);
}
else if (huart->Instance == USART2)
{
@ -444,7 +509,7 @@ void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
/* 调用多UART路由器的UART3发送完成回调 */
MultiUART_TxCpltCallback(PORT_UART3);
MultiUART_TxCpltCallback(PORT_RS485);
}
}