3.27_433:实现并验证RF433模块接收相应指令:新增UART路由核心模块,使程序能响应RF433/RS485指令,并向UART2输出LOG(RS485由于硬件原因未验证)
This commit is contained in:
@ -3,13 +3,16 @@
|
||||
* @file cmd_parser.h
|
||||
* @brief ASCII指令解析模块头文件
|
||||
* @author Application Layer
|
||||
* @version 1.0
|
||||
* @version 1.2
|
||||
******************************************************************************
|
||||
* @attention
|
||||
* 本模块实现ASCII文本指令的解析和处理
|
||||
* 指令格式: $CMD,param1,param2*CS\r\n
|
||||
* 支持异或校验,FF为调试特权后门
|
||||
* 包含完善的安全防护机制
|
||||
*
|
||||
* 修订历史:
|
||||
* v1.2 - 增加响应回调机制,支持多端口路由
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
@ -41,67 +44,31 @@ typedef struct {
|
||||
char param2[PARAM_MAX_LEN];
|
||||
uint8_t received_cs;
|
||||
uint8_t calculated_cs;
|
||||
uint8_t source_port;
|
||||
bool valid;
|
||||
bool skip_checksum;
|
||||
} cmd_frame_t;
|
||||
|
||||
/**
|
||||
* @brief 初始化指令解析模块
|
||||
* @note 重置解析状态机,清空缓冲区
|
||||
* @param 无
|
||||
* @retval 无
|
||||
*/
|
||||
typedef void (*cmd_response_callback_t)(uint8_t source_port, const char *response);
|
||||
|
||||
void CmdParser_Init(void);
|
||||
|
||||
/**
|
||||
* @brief 指令解析任务处理函数
|
||||
* @note 在主循环中调用,处理已接收的完整指令帧
|
||||
* @param 无
|
||||
* @retval 无
|
||||
*/
|
||||
void CmdParser_Task(void);
|
||||
|
||||
/**
|
||||
* @brief 喂入单字节数据到解析器
|
||||
* @note 通常在UART接收中断中调用
|
||||
* @param byte: 接收到的字节数据
|
||||
* @param current_tick: 当前系统tick(用于超时检测)
|
||||
* @retval 无
|
||||
*/
|
||||
void CmdParser_FeedByte(uint8_t byte, uint32_t current_tick);
|
||||
|
||||
/**
|
||||
* @brief 检查是否有完整的指令帧
|
||||
* @note 用于查询解析状态
|
||||
* @param frame: 输出参数,存储解析结果
|
||||
* @retval true: 有完整帧, false: 无
|
||||
*/
|
||||
bool CmdParser_HasCompleteFrame(cmd_frame_t *frame);
|
||||
|
||||
/**
|
||||
* @brief 确认指令已处理
|
||||
* @note 处理完指令后调用,重置解析器
|
||||
* @param 无
|
||||
* @retval 无
|
||||
*/
|
||||
void CmdParser_Acknowledge(void);
|
||||
|
||||
/**
|
||||
* @brief 获取解析错误计数
|
||||
* @note 用于调试和诊断
|
||||
* @param 无
|
||||
* @retval 错误计数
|
||||
*/
|
||||
uint32_t CmdParser_GetErrorCount(void);
|
||||
|
||||
/**
|
||||
* @brief 获取接收到的有效指令计数
|
||||
* @note 用于调试和诊断
|
||||
* @param 无
|
||||
* @retval 有效指令计数
|
||||
*/
|
||||
uint32_t CmdParser_GetValidCount(void);
|
||||
|
||||
void CmdParser_SetResponseCallback(cmd_response_callback_t callback);
|
||||
|
||||
void CmdParser_SetSourcePort(uint8_t port_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
51
Core/Inc/cmd_router.h
Normal file
51
Core/Inc/cmd_router.h
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file cmd_router.h
|
||||
* @brief 指令路由与响应分发模块头文件
|
||||
* @author Application Layer
|
||||
* @version 1.0
|
||||
******************************************************************************
|
||||
* @attention
|
||||
* 本模块实现指令路由与响应分发功能
|
||||
* 设计依据:多通信接口统一指令处理系统开发计划 第3.2节
|
||||
*
|
||||
* 核心职责:
|
||||
* - 从各UART端口读取数据并喂入解析器
|
||||
* - 根据指令来源端口路由响应
|
||||
* - 管理响应路由表
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef __CMD_ROUTER_H
|
||||
#define __CMD_ROUTER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "multi_uart_router.h"
|
||||
#include "cmd_parser.h"
|
||||
|
||||
typedef void (*cmd_response_handler_t)(port_id_t port, const char *response, uint16_t len);
|
||||
|
||||
void CmdRouter_Init(void);
|
||||
|
||||
void CmdRouter_Task(void);
|
||||
|
||||
void CmdRouter_SetResponseHandler(cmd_response_handler_t handler);
|
||||
|
||||
void CmdRouter_SendResponse(port_id_t port, const char *response, uint16_t len);
|
||||
|
||||
void CmdRouter_BroadcastResponse(const char *response, uint16_t len);
|
||||
|
||||
uint32_t CmdRouter_GetProcessedCount(void);
|
||||
|
||||
uint32_t CmdRouter_GetRoutedCount(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
61
Core/Inc/debug_log.h
Normal file
61
Core/Inc/debug_log.h
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file debug_log.h
|
||||
* @brief 增强型调试日志系统头文件
|
||||
* @author Application Layer
|
||||
* @version 1.0
|
||||
******************************************************************************
|
||||
* @attention
|
||||
* 本模块实现增强型调试日志系统
|
||||
* 设计依据:多通信接口统一指令处理系统开发计划 第3.4节
|
||||
*
|
||||
* 日志格式:[LEVEL][MODULE] message
|
||||
* 示例:[INFO][CMD] Command received: RL
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef __DEBUG_LOG_H
|
||||
#define __DEBUG_LOG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum {
|
||||
LOG_LEVEL_DEBUG = 0,
|
||||
LOG_LEVEL_INFO = 1,
|
||||
LOG_LEVEL_WARN = 2,
|
||||
LOG_LEVEL_ERROR = 3,
|
||||
LOG_LEVEL_NONE = 4
|
||||
} log_level_t;
|
||||
|
||||
void DebugLog_Init(void);
|
||||
|
||||
void DebugLog_SetLevel(log_level_t level);
|
||||
|
||||
log_level_t DebugLog_GetLevel(void);
|
||||
|
||||
void DebugLog_EnableModule(const char *module, bool enable);
|
||||
|
||||
void DebugLog_Output(log_level_t level, const char *module, const char *fmt, ...);
|
||||
|
||||
#define LOG_DEBUG(module, fmt, ...) \
|
||||
DebugLog_Output(LOG_LEVEL_DEBUG, module, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define LOG_INFO(module, fmt, ...) \
|
||||
DebugLog_Output(LOG_LEVEL_INFO, module, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define LOG_WARN(module, fmt, ...) \
|
||||
DebugLog_Output(LOG_LEVEL_WARN, module, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define LOG_ERROR(module, fmt, ...) \
|
||||
DebugLog_Output(LOG_LEVEL_ERROR, module, fmt, ##__VA_ARGS__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -3,12 +3,15 @@
|
||||
* @file io_monitor.h
|
||||
* @brief IO状态监控模块头文件
|
||||
* @author Application Layer
|
||||
* @version 1.0
|
||||
* @version 1.1
|
||||
******************************************************************************
|
||||
* @attention
|
||||
* 本模块实现四路数字输入(DI1-DI4)的状态监控
|
||||
* 采用定时扫描+软件去抖方式检测IO状态变化
|
||||
* 状态变化时通过UART2_Print上报
|
||||
* 状态变化时通过回调函数上报,支持多端口路由
|
||||
*
|
||||
* 修订历史:
|
||||
* v1.1 - 增加事件回调机制,支持多端口路由
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
@ -26,6 +29,15 @@ extern "C" {
|
||||
#define IO_SCAN_PERIOD_MS 10
|
||||
#define IO_DEBOUNCE_COUNT 3
|
||||
|
||||
/**
|
||||
* @brief IO事件回调函数类型
|
||||
* @note 当IO状态变化时调用,用于事件路由
|
||||
* @param channel: 通道号(0-3对应DI1-DI4)
|
||||
* @param state: 当前状态(0=LOW, 1=HIGH)
|
||||
* @param event_msg: 事件消息字符串
|
||||
*/
|
||||
typedef void (*io_event_callback_t)(uint8_t channel, uint8_t state, const char *event_msg);
|
||||
|
||||
/**
|
||||
* @brief 初始化IO监控模块
|
||||
* @note 初始化各通道状态,读取初始IO电平
|
||||
@ -75,6 +87,14 @@ void IO_Monitor_EnableReport(bool enable);
|
||||
*/
|
||||
uint32_t IO_Monitor_GetChangeCount(uint8_t channel);
|
||||
|
||||
/**
|
||||
* @brief 设置IO事件回调函数
|
||||
* @note 设置后,IO状态变化将通过回调函数上报
|
||||
* @param callback: 回调函数指针,NULL则使用默认UART2输出
|
||||
* @retval 无
|
||||
*/
|
||||
void IO_Monitor_SetEventCallback(io_event_callback_t callback);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
97
Core/Inc/multi_uart_router.h
Normal file
97
Core/Inc/multi_uart_router.h
Normal file
@ -0,0 +1,97 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file multi_uart_router.h
|
||||
* @brief 多UART统一路由核心模块头文件
|
||||
* @author Application Layer
|
||||
* @version 1.0
|
||||
******************************************************************************
|
||||
* @attention
|
||||
* 本模块实现多UART端口的统一管理,包括:
|
||||
* - 接收环形缓冲区
|
||||
* - 发送环形缓冲区
|
||||
* - 端口上下文管理
|
||||
* - 响应路由表
|
||||
*
|
||||
* 设计依据:多通信接口统一指令处理系统开发计划 第3.1、3.3、3.5节及附录A
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef __MULTI_UART_ROUTER_H
|
||||
#define __MULTI_UART_ROUTER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "usart.h"
|
||||
|
||||
#define UART_RX_BUFFER_SIZE 128
|
||||
#define UART_TX_BUFFER_SIZE 256
|
||||
|
||||
typedef enum {
|
||||
PORT_UART1 = 0,
|
||||
PORT_UART2 = 1,
|
||||
PORT_UART3 = 2,
|
||||
PORT_COUNT
|
||||
} port_id_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t buffer[UART_RX_BUFFER_SIZE];
|
||||
volatile uint16_t head;
|
||||
volatile uint16_t tail;
|
||||
volatile uint16_t count;
|
||||
volatile uint16_t overflow_count;
|
||||
} uart_rx_ring_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t buffer[UART_TX_BUFFER_SIZE];
|
||||
volatile uint16_t head;
|
||||
volatile uint16_t tail;
|
||||
volatile uint16_t count;
|
||||
volatile bool is_sending;
|
||||
volatile uint16_t overflow_count;
|
||||
} uart_tx_ring_t;
|
||||
|
||||
typedef struct {
|
||||
UART_HandleTypeDef *huart;
|
||||
const char *name;
|
||||
uart_rx_ring_t rx_ring;
|
||||
uart_tx_ring_t tx_ring;
|
||||
uint8_t rx_tmp;
|
||||
uint32_t rx_count;
|
||||
uint32_t tx_count;
|
||||
uint32_t error_count;
|
||||
bool initialized;
|
||||
} uart_port_context_t;
|
||||
|
||||
void MultiUART_Init(void);
|
||||
|
||||
void MultiUART_FeedByte(port_id_t port_id, uint8_t byte);
|
||||
|
||||
void MultiUART_Task(void);
|
||||
|
||||
void MultiUART_Send(port_id_t port_id, const uint8_t *data, uint16_t len);
|
||||
|
||||
void MultiUART_SendString(port_id_t port_id, const char *str);
|
||||
|
||||
void MultiUART_SendFmt(port_id_t port_id, const char *fmt, ...);
|
||||
|
||||
void MultiUART_TxCpltCallback(port_id_t port_id);
|
||||
|
||||
const char *MultiUART_GetPortName(port_id_t port_id);
|
||||
|
||||
uint16_t MultiUART_GetRxCount(port_id_t port_id);
|
||||
|
||||
uint16_t MultiUART_ReadByte(port_id_t port_id, uint8_t *byte);
|
||||
|
||||
uint16_t MultiUART_GetTxAvailable(port_id_t port_id);
|
||||
|
||||
uint32_t MultiUART_GetOverflowCount(port_id_t port_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -3,11 +3,14 @@
|
||||
* @file relay_control.h
|
||||
* @brief 继电器控制模块头文件
|
||||
* @author Application Layer
|
||||
* @version 1.0
|
||||
* @version 2.0
|
||||
******************************************************************************
|
||||
* @attention
|
||||
* 本模块提供继电器的安全控制接口
|
||||
* 当前硬件配置:PA15连接继电器控制端
|
||||
* 本模块提供单路继电器的控制接口
|
||||
* 硬件配置:PA15连接继电器控制端
|
||||
*
|
||||
* 修订历史:
|
||||
* v2.0 - 精简为单路继电器控制,移除冗余功能
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
@ -21,7 +24,6 @@ extern "C" {
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define RELAY_COUNT 1
|
||||
#define RELAY_MIN_INTERVAL 100
|
||||
|
||||
/**
|
||||
@ -35,35 +37,18 @@ void Relay_Init(void);
|
||||
/**
|
||||
* @brief 设置继电器状态
|
||||
* @note 带最小间隔保护,防止频繁切换损坏继电器
|
||||
* @param relay_id: 继电器编号(1-4),当前硬件只有1
|
||||
* @param state: true=打开, false=关闭
|
||||
* @retval 无
|
||||
*/
|
||||
void Relay_SetState(uint8_t relay_id, bool state);
|
||||
void Relay_SetState(bool state);
|
||||
|
||||
/**
|
||||
* @brief 获取继电器当前状态
|
||||
* @note 读取GPIO输出状态
|
||||
* @param relay_id: 继电器编号(1-4)
|
||||
* @note 读取软件记录的状态
|
||||
* @param 无
|
||||
* @retval true=打开, false=关闭
|
||||
*/
|
||||
bool Relay_GetState(uint8_t relay_id);
|
||||
|
||||
/**
|
||||
* @brief 翻转继电器状态
|
||||
* @note 带最小间隔保护
|
||||
* @param relay_id: 继电器编号(1-4)
|
||||
* @retval 无
|
||||
*/
|
||||
void Relay_Toggle(uint8_t relay_id);
|
||||
|
||||
/**
|
||||
* @brief 获取继电器切换次数
|
||||
* @note 用于调试和诊断
|
||||
* @param 无
|
||||
* @retval 切换次数
|
||||
*/
|
||||
uint32_t Relay_GetToggleCount(void);
|
||||
bool Relay_GetState(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -27,37 +27,148 @@
|
||||
#include <stdio.h> // snprintf
|
||||
#include <stdlib.h> // atoi
|
||||
|
||||
/*==============================================================================
|
||||
* 调试宏定义
|
||||
*============================================================================*/
|
||||
/* DEBUG_CMD_PARSER: 调试日志开关,置1时启用解析过程日志输出 */
|
||||
#define DEBUG_CMD_PARSER 1
|
||||
|
||||
#if DEBUG_CMD_PARSER
|
||||
/* 调试日志宏,带模块前缀"[CMD]"方便过滤 */
|
||||
#define DEBUG_LOG(fmt, ...) UART2_Print_Printf("[CMD] " fmt "\r\n", ##__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_LOG(fmt, ...)
|
||||
#endif
|
||||
|
||||
/*==============================================================================
|
||||
* 解析器状态机定义
|
||||
*============================================================================*/
|
||||
/**
|
||||
* @brief 指令解析器状态枚举
|
||||
* @note 状态机各状态定义,描述指令解析的完整生命周期
|
||||
*
|
||||
* 状态转换图:
|
||||
*
|
||||
* [空闲] ---'$'---> [解析命令]
|
||||
* [解析命令] ---','---> [解析参数1]
|
||||
* [解析命令] ---'*'---> [解析校验和]
|
||||
* [解析参数1] ---','---> [解析参数2]
|
||||
* [解析参数1] ---'*'---> [解析校验和]
|
||||
* [解析参数2] ---'*'---> [解析校验和]
|
||||
* [解析校验和] ---'\n'---> [完成]
|
||||
* [完成] ---'$'---> [解析命令]
|
||||
* 任何状态 ---超时---> [空闲]
|
||||
*
|
||||
* 各状态说明:
|
||||
* - PARSE_IDLE: 等待帧起始符'$',处于空闲状态
|
||||
* - PARSE_CMD: 正在解析命令字段(直到遇到','或'*')
|
||||
* - PARSE_PARAM1: 正在解析第一个参数(直到遇到','或'*')
|
||||
* - PARSE_PARAM2: 正在解析第二个参数(直到遇到'*')
|
||||
* - PARSE_CHECKSUM: 正在解析校验和(两个十六进制字符直到'\n')
|
||||
* - PARSE_COMPLETE: 完整帧已解析完成,等待处理
|
||||
*/
|
||||
typedef enum {
|
||||
PARSE_IDLE,
|
||||
PARSE_CMD,
|
||||
PARSE_PARAM1,
|
||||
PARSE_PARAM2,
|
||||
PARSE_CHECKSUM,
|
||||
PARSE_COMPLETE
|
||||
PARSE_IDLE = 0, /**< 空闲状态,等待帧起始 */
|
||||
PARSE_CMD, /**< 正在解析命令字段 */
|
||||
PARSE_PARAM1, /**< 正在解析第一个参数 */
|
||||
PARSE_PARAM2, /**< 正在解析第二个参数 */
|
||||
PARSE_CHECKSUM, /**< 正在解析校验和 */
|
||||
PARSE_COMPLETE /**< 解析完成,可处理 */
|
||||
} parse_state_t;
|
||||
|
||||
/*==============================================================================
|
||||
* 数据结构定义
|
||||
*============================================================================*/
|
||||
/**
|
||||
* @brief 解析器上下文数据结构
|
||||
* @note 保存指令解析的完整状态信息,包括当前状态、已解析字段和统计计数
|
||||
*
|
||||
* 设计目的:
|
||||
* 解析器需要记忆多个中间状态:当前解析状态、已接收的各字段内容、
|
||||
* 校验和累加值、上下文字符索引等。这些信息集中保存在此结构体中,
|
||||
* 实现无状态的逐字节处理(Stateless Byte-by-Byte Processing)模式。
|
||||
*
|
||||
* 字段说明:
|
||||
* - state: 当前解析状态机的状态
|
||||
* - frame: 已解析完成的指令帧结构(cmd_frame_t类型)
|
||||
* - field_index: 当前字段的字符写入位置索引
|
||||
* - checksum_acc: 校验和累加器,逐字节异或得到
|
||||
* - cs_buffer: 接收的校验和字符缓存(两个HEX字符)
|
||||
* - cs_index: 校验和字符缓存的当前写入位置
|
||||
* - last_rx_tick: 上次接收到数据的时间戳(用于超时检测)
|
||||
* - error_count: 解析错误累计次数
|
||||
* - valid_count: 有效指令帧累计次数
|
||||
*/
|
||||
typedef struct {
|
||||
parse_state_t state;
|
||||
cmd_frame_t frame;
|
||||
uint8_t field_index;
|
||||
uint8_t checksum_acc;
|
||||
uint8_t cs_buffer[2];
|
||||
uint8_t cs_index;
|
||||
uint32_t last_rx_tick;
|
||||
uint32_t error_count;
|
||||
uint32_t valid_count;
|
||||
parse_state_t state; /**< 解析状态机当前状态 */
|
||||
cmd_frame_t frame; /**< 已解析指令帧数据 */
|
||||
uint8_t field_index; /**< 当前字段的字符写入位置 */
|
||||
uint8_t checksum_acc; /**< 校验和累加器(异或运算) */
|
||||
uint8_t cs_buffer[2]; /**< 接收的校验和字符缓存 */
|
||||
uint8_t cs_index; /**< 校验和字符缓存写入位置 */
|
||||
uint32_t last_rx_tick; /**< 上次接收时间戳(毫秒) */
|
||||
uint32_t error_count; /**< 解析错误累计次数 */
|
||||
uint32_t valid_count; /**< 有效帧累计次数 */
|
||||
} parser_context_t;
|
||||
|
||||
/*==============================================================================
|
||||
* 全局变量定义
|
||||
*============================================================================*/
|
||||
/**
|
||||
* @brief 解析器上下文实例
|
||||
* @note static修饰确保仅本文件内可访问,保存解析过程全部状态
|
||||
*/
|
||||
static parser_context_t ctx;
|
||||
|
||||
/**
|
||||
* @brief 响应回调函数指针
|
||||
* @note 用于将响应发送到正确的源端口
|
||||
*/
|
||||
static cmd_response_callback_t g_response_callback = NULL;
|
||||
|
||||
/**
|
||||
* @brief 当前源端口ID
|
||||
* @note 记录当前正在解析的指令来自哪个端口
|
||||
*/
|
||||
static uint8_t g_current_source_port = 0;
|
||||
|
||||
/*==============================================================================
|
||||
* 内部静态函数声明
|
||||
*============================================================================*/
|
||||
static void reset_parser(void);
|
||||
static bool is_valid_cmd_char(char c);
|
||||
static bool is_valid_param_char(char c);
|
||||
static uint8_t hex_char_to_val(char c);
|
||||
static uint8_t hex_to_byte(char high, char low);
|
||||
static uint8_t calc_checksum(const char *data, uint8_t len);
|
||||
static void send_response_ok(const char *content);
|
||||
static void send_response_err(const char *err_code);
|
||||
static bool is_str_empty(const char *str);
|
||||
static bool is_str_numeric(const char *str);
|
||||
static void process_cmd_frame(const cmd_frame_t *frame);
|
||||
|
||||
/*==============================================================================
|
||||
* 内部辅助函数实现
|
||||
*============================================================================*/
|
||||
/**
|
||||
* @brief 重置解析器到初始状态
|
||||
* @note 清除所有上下文信息,准备接收新指令帧
|
||||
*
|
||||
* @param 无
|
||||
* @return 无
|
||||
*
|
||||
* 重置内容:
|
||||
* - 状态恢复为PARSE_IDLE
|
||||
* - 字段索引清零
|
||||
* - 校验和累加器清零
|
||||
* - 校验和缓存索引清零
|
||||
* - cmd_frame结构体全部清零
|
||||
*
|
||||
* 调用时机:
|
||||
* - 解析出错时
|
||||
* - 解析完成处理后
|
||||
* - 接收到完整帧后的空闲期
|
||||
*/
|
||||
static void reset_parser(void)
|
||||
{
|
||||
ctx.state = PARSE_IDLE;
|
||||
@ -67,16 +178,59 @@ static void reset_parser(void)
|
||||
memset(&ctx.frame, 0, sizeof(ctx.frame));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 验证命令字符合法性
|
||||
* @note 命令字段只能包含大写字母和数字
|
||||
*
|
||||
* @param c: 待验证的字符(输入)
|
||||
* @return bool: true=合法,false=非法
|
||||
*
|
||||
* 过滤规则:
|
||||
* - 允许: 'A'-'Z', '0'-'9'
|
||||
* - 禁止: 小写字母、标点符号、控制字符等
|
||||
*
|
||||
* 安全意义:
|
||||
* - 限制命令字符集可防止注入攻击
|
||||
* - 简化解析逻辑,避免处理边界情况
|
||||
*/
|
||||
static bool is_valid_cmd_char(char c)
|
||||
{
|
||||
return isupper((unsigned char)c) || isdigit((unsigned char)c);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 验证参数字符合法性
|
||||
* @note 参数字段可包含大部分可打印字符,但排除特殊分隔符
|
||||
*
|
||||
* @param c: 待验证的字符(输入)
|
||||
* @return bool: true=合法,false=非法
|
||||
*
|
||||
* 过滤规则:
|
||||
* - 允许: isprint()返回true的字符,但不包括'*'
|
||||
* - 禁止: '*'、'\r'、'\n'及其他不可打印字符
|
||||
*
|
||||
* 特殊说明:
|
||||
* - '*'是校验和起始分隔符,不能出现在参数中
|
||||
* - '\r'和'\n'是帧结束符
|
||||
*/
|
||||
static bool is_valid_param_char(char c)
|
||||
{
|
||||
return isprint((unsigned char)c) && c != '*' && c != '\r' && c != '\n';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 十六进制字符转换为数值
|
||||
* @note 将单个十六进制字符('0'-'9','A'-'F')转换为对应的4位数值
|
||||
*
|
||||
* @param c: 十六进制字符(输入)
|
||||
* @return uint8_t: 转换后的数值(0-15),非法字符返回0
|
||||
*
|
||||
* 转换规则:
|
||||
* '0'-'9' -> 0-9
|
||||
* 'A'-'F' -> 10-15
|
||||
* 'a'-'f' -> 10-15 (小写也支持)
|
||||
* 其他字符 -> 0 (默认处理)
|
||||
*/
|
||||
static uint8_t hex_char_to_val(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
@ -85,11 +239,37 @@ static uint8_t hex_char_to_val(char c)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将两个十六进制字符转换为一个字节
|
||||
* @note 高位字符在前,地位字符在后,组合成完整的字节值
|
||||
*
|
||||
* @param high: 高位十六进制字符(输入)
|
||||
* @param low: 低位十六进制字符(输入)
|
||||
* @return uint8_t: 组合后的字节值(0-255)
|
||||
*
|
||||
* 计算公式:result = (high_nibble << 4) | low_nibble
|
||||
*
|
||||
* 示例:
|
||||
* hex_to_byte('A', 'F') 返回 0xAF (十进制175)
|
||||
*/
|
||||
static uint8_t hex_to_byte(char high, char low)
|
||||
{
|
||||
return (hex_char_to_val(high) << 4) | hex_char_to_val(low);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算异或校验和
|
||||
* @note 对输入数据的每个字节执行异或运算,生成校验码
|
||||
*
|
||||
* @param data: 待计算校验和的数据缓冲区(输入)
|
||||
* @param len: 数据长度,以字节为单位(输入)
|
||||
* @return uint8_t: 校验和(异或结果)
|
||||
*
|
||||
* 算法原理:
|
||||
* 遍历数据缓冲区,将每个字节与累加器进行异或操作。
|
||||
* 初始累加器为0,最终结果为所有字节的异或和。
|
||||
* XOR的特性:a^a=0, a^0=a, 具有可逆性,适合简单校验
|
||||
*/
|
||||
static uint8_t calc_checksum(const char *data, uint8_t len)
|
||||
{
|
||||
uint8_t cs = 0;
|
||||
@ -99,6 +279,22 @@ static uint8_t calc_checksum(const char *data, uint8_t len)
|
||||
return cs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送成功响应消息
|
||||
* @note 构造并发送指令执行成功的响应帧
|
||||
*
|
||||
* @param content: 响应内容字符串指针(输入)
|
||||
* @return 无
|
||||
*
|
||||
* 响应帧格式:
|
||||
* $OK,<content>*<checksum>\r\n
|
||||
*
|
||||
* 校验和计算范围:
|
||||
* 从'$'之后到'*'之前的全部内容(不含$和*)
|
||||
*
|
||||
* 使用示例:
|
||||
* send_response_ok("RL,1") -> $OK,RL,1*XX\r\n
|
||||
*/
|
||||
static void send_response_ok(const char *content)
|
||||
{
|
||||
char msg[64];
|
||||
@ -108,9 +304,28 @@ static void send_response_ok(const char *content)
|
||||
cs = calc_checksum(msg + 1, len - 1);
|
||||
snprintf(msg + len, sizeof(msg) - len, "%02X\r\n", cs);
|
||||
|
||||
if (g_response_callback != NULL) {
|
||||
g_response_callback(g_current_source_port, msg);
|
||||
} else {
|
||||
UART2_Print_String(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送错误响应消息
|
||||
* @note 构造并发送指令执行失败的响应帧
|
||||
*
|
||||
* @param err_code: 错误代码字符串(输入),如"PARAM"、"CS"、"CMD"
|
||||
* @return 无
|
||||
*
|
||||
* 响应帧格式:
|
||||
* $ERR,<err_code>*<checksum>\r\n
|
||||
*
|
||||
* 错误代码含义:
|
||||
* - "PARAM": 参数格式或值非法
|
||||
* - "CS": 校验和不匹配
|
||||
* - "CMD": 命令无法识别
|
||||
*/
|
||||
static void send_response_err(const char *err_code)
|
||||
{
|
||||
char msg[32];
|
||||
@ -120,19 +335,43 @@ static void send_response_err(const char *err_code)
|
||||
cs = calc_checksum(msg + 1, len - 1);
|
||||
snprintf(msg + len, sizeof(msg) - len, "%02X\r\n", cs);
|
||||
|
||||
if (g_response_callback != NULL) {
|
||||
g_response_callback(g_current_source_port, msg);
|
||||
} else {
|
||||
UART2_Print_String(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 判断字符串是否为空
|
||||
* @note 检查指针是否为NULL或首字符是否为'\0'
|
||||
*
|
||||
* @param str: 待检查的字符串指针(输入)
|
||||
* @return bool: true=空字符串或NULL,false=非空
|
||||
*/
|
||||
static bool is_str_empty(const char *str)
|
||||
{
|
||||
return (str == NULL || str[0] == '\0');
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 判断字符串是否全为数字
|
||||
* @note 验证字符串中的所有字符是否都是十进制数字
|
||||
*
|
||||
* @param str: 待检查的字符串指针(输入)
|
||||
* @return bool: true=全为数字,false=包含非数字字符或为空
|
||||
*
|
||||
* 实现逻辑:
|
||||
* 从字符串开头遍历到'\0',检查每个字符是否满足isdigit()。
|
||||
* 一旦遇到非数字字符立即返回false。
|
||||
*/
|
||||
static bool is_str_numeric(const char *str)
|
||||
{
|
||||
/* 空字符串检查 */
|
||||
if (is_str_empty(str)) {
|
||||
return false;
|
||||
}
|
||||
/* 逐字符检查是否为数字 */
|
||||
while (*str) {
|
||||
if (!isdigit((unsigned char)*str)) {
|
||||
return false;
|
||||
@ -142,39 +381,82 @@ static bool is_str_numeric(const char *str)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 指令帧处理函数
|
||||
* @note 根据解析出的命令类型分发到相应的处理函数
|
||||
*
|
||||
* @param frame: 已解析完成的指令帧结构指针(输入)
|
||||
* @return 无
|
||||
*
|
||||
* 支持的指令列表:
|
||||
* - RL <relay_id> <state>: 控制继电器开关
|
||||
* - DI [channel]: 查询数字输入状态
|
||||
* - ECHO: 回显测试指令
|
||||
*
|
||||
* 处理流程:
|
||||
* 1. 调试日志输出指令详情
|
||||
* 2. 根据cmd字段分发到对应处理分支
|
||||
* 3. 参数合法性校验
|
||||
* 4. 调用业务层函数执行操作
|
||||
* 5. 发送成功/失败响应
|
||||
*/
|
||||
static void process_cmd_frame(const cmd_frame_t *frame)
|
||||
{
|
||||
/*----------------------------------------------------------
|
||||
* 调试日志:打印完整指令信息
|
||||
*----------------------------------------------------------*/
|
||||
DEBUG_LOG("CMD=%s P1=%s P2=%s CS=%02X/%02X %s",
|
||||
frame->cmd, frame->param1, frame->param2,
|
||||
frame->received_cs, frame->calculated_cs,
|
||||
frame->skip_checksum ? "(skip)" : "");
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 继电器控制指令: RL <state>
|
||||
* 格式: $RL,<state>*<checksum>
|
||||
* 说明: 单路继电器,state为0(关)或1(开)
|
||||
*----------------------------------------------------------*/
|
||||
if (strcmp(frame->cmd, "RL") == 0) {
|
||||
if (!is_str_numeric(frame->param1) || !is_str_numeric(frame->param2)) {
|
||||
/* 参数合法性检查:param1必须为数字 */
|
||||
if (!is_str_numeric(frame->param1)) {
|
||||
send_response_err("PARAM");
|
||||
DEBUG_LOG("Invalid RL params: not numeric");
|
||||
DEBUG_LOG("Invalid RL param: not numeric");
|
||||
return;
|
||||
}
|
||||
|
||||
int relay_id = atoi(frame->param1);
|
||||
int state = atoi(frame->param2);
|
||||
/* 将参数字符串转换为整数 */
|
||||
int state = atoi(frame->param1);
|
||||
|
||||
if (relay_id >= 1 && relay_id <= 4 && (state == 0 || state == 1)) {
|
||||
Relay_SetState(relay_id, state ? true : false);
|
||||
/* 状态值范围检查: 0或1 */
|
||||
if (state == 0 || state == 1) {
|
||||
/* 调用业务层函数设置继电器状态 */
|
||||
Relay_SetState(state ? true : false);
|
||||
|
||||
/* 构造并发送成功响应 */
|
||||
char resp[32];
|
||||
snprintf(resp, sizeof(resp), "RL,%d,%d", relay_id, state);
|
||||
snprintf(resp, sizeof(resp), "RL,%d", state);
|
||||
send_response_ok(resp);
|
||||
|
||||
DEBUG_LOG("Relay %d -> %s", relay_id, state ? "ON" : "OFF");
|
||||
DEBUG_LOG("Relay -> %s", state ? "ON" : "OFF");
|
||||
} else {
|
||||
/* 参数值超出有效范围 */
|
||||
send_response_err("PARAM");
|
||||
DEBUG_LOG("Invalid RL params: id=%d state=%d", relay_id, state);
|
||||
DEBUG_LOG("Invalid RL param: state=%d", state);
|
||||
}
|
||||
}
|
||||
/*----------------------------------------------------------
|
||||
* 数字输入查询指令: DI [channel]
|
||||
* 格式1(查询全部): $DI*<checksum> 或 $DI,0*<checksum>
|
||||
* 格式2(查询单通道): $DI,<channel>*<checksum>
|
||||
*----------------------------------------------------------*/
|
||||
else if (strcmp(frame->cmd, "DI") == 0) {
|
||||
/*----------------------------------------------------------
|
||||
* 分支1: 查询全部通道状态
|
||||
* param1为空或为"0"时触发
|
||||
*----------------------------------------------------------*/
|
||||
if (is_str_empty(frame->param1) || strcmp(frame->param1, "0") == 0) {
|
||||
/* 获取所有通道状态的组合掩码 */
|
||||
uint8_t states = IO_Monitor_GetAllStates();
|
||||
/* 构造响应消息,将四路状态按位展开为ASCII字符 */
|
||||
char resp[32];
|
||||
snprintf(resp, sizeof(resp), "DI,%d%d%d%d",
|
||||
(states >> 0) & 1, (states >> 1) & 1,
|
||||
@ -182,34 +464,71 @@ static void process_cmd_frame(const cmd_frame_t *frame)
|
||||
send_response_ok(resp);
|
||||
DEBUG_LOG("DI all states: 0x%02X", states);
|
||||
}
|
||||
/*----------------------------------------------------------
|
||||
* 分支2: 查询指定单通道状态
|
||||
* param1为数字时触发
|
||||
*----------------------------------------------------------*/
|
||||
else if (is_str_numeric(frame->param1)) {
|
||||
int channel = atoi(frame->param1);
|
||||
/* 通道编号范围检查: 1-4 */
|
||||
if (channel >= 1 && channel <= 4) {
|
||||
/* 获取该通道的当前状态(内部使用0-base索引) */
|
||||
uint8_t state = IO_Monitor_GetState(channel - 1);
|
||||
char resp[32];
|
||||
snprintf(resp, sizeof(resp), "DI,%d,%d", channel, state);
|
||||
send_response_ok(resp);
|
||||
DEBUG_LOG("DI%d = %d", channel, state);
|
||||
} else {
|
||||
/* 通道编号超出范围 */
|
||||
send_response_err("PARAM");
|
||||
DEBUG_LOG("Invalid DI channel: %d", channel);
|
||||
}
|
||||
}
|
||||
/*----------------------------------------------------------
|
||||
* 分支3: 参数格式非法
|
||||
*----------------------------------------------------------*/
|
||||
else {
|
||||
send_response_err("PARAM");
|
||||
DEBUG_LOG("Invalid DI param: not numeric");
|
||||
}
|
||||
}
|
||||
/*----------------------------------------------------------
|
||||
* 回显测试指令: ECHO
|
||||
* 格式: $ECHO*<checksum>
|
||||
* 用途: 测试通信链路是否正常
|
||||
*----------------------------------------------------------*/
|
||||
else if (strcmp(frame->cmd, "ECHO") == 0) {
|
||||
send_response_ok("ECHO");
|
||||
DEBUG_LOG("ECHO response sent");
|
||||
}
|
||||
/*----------------------------------------------------------
|
||||
* 未知命令处理
|
||||
*----------------------------------------------------------*/
|
||||
else {
|
||||
send_response_err("CMD");
|
||||
DEBUG_LOG("Unknown command: %s", frame->cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 公共函数实现
|
||||
*============================================================================*/
|
||||
/**
|
||||
* @brief 指令解析器初始化
|
||||
* @note 在系统启动时调用,初始化解析器上下文
|
||||
*
|
||||
* @param 无
|
||||
* @return 无
|
||||
*
|
||||
* 初始化内容:
|
||||
* - 使用memset将整个上下文结构清零
|
||||
* - 将状态显式设置为PARSE_IDLE
|
||||
*
|
||||
* 注意:
|
||||
* 全局变量ctx本身是static且初始化为{0},
|
||||
* 此函数调用memset是确保明确初始化,
|
||||
* 防止未来新增字段时出现未初始化问题
|
||||
*/
|
||||
void CmdParser_Init(void)
|
||||
{
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
@ -218,22 +537,83 @@ void CmdParser_Init(void)
|
||||
DEBUG_LOG("Init OK");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 向解析器输入一个字节
|
||||
* @note 核心解析函数,实现状态机逻辑,应对每个接收字节调用一次
|
||||
*
|
||||
* @param byte: 待解析的接收字节(输入)
|
||||
* @param current_tick: 当前系统时间戳,毫秒单位(输入)
|
||||
* @return 无
|
||||
*
|
||||
* 算法说明 - 状态机核心逻辑:
|
||||
* 本函数是整个解析器的核心,实现了有限状态机(FSM)。
|
||||
* 每次调用处理一个字节,根据当前状态(ctx.state)和输入字节
|
||||
* 决定状态转换和动作。
|
||||
*
|
||||
* 状态转换详细说明:
|
||||
*
|
||||
* [PARSE_IDLE]:
|
||||
* - byte='$' -> reset_parser(), state=PARSE_CMD
|
||||
* - 其他字节 -> 忽略,保持IDLE
|
||||
*
|
||||
* [PARSE_CMD]:
|
||||
* - byte=',' -> 字段结束,state=PARSE_PARAM1,field_index=0
|
||||
* - byte='*' -> 字段结束,state=PARSE_CHECKSUM,cs_index=0
|
||||
* - is_valid_cmd_char(byte) -> 写入cmd缓冲区,checksum_acc^=byte
|
||||
* - 其他情况 -> error_count++, reset_parser()
|
||||
*
|
||||
* [PARSE_PARAM1]:
|
||||
* - byte=',' -> 字段结束,state=PARSE_PARAM2,field_index=0
|
||||
* - byte='*' -> 字段结束,state=PARSE_CHECKSUM,cs_index=0
|
||||
* - is_valid_param_char(byte) -> 写入param1缓冲区,checksum_acc^=byte
|
||||
* - 其他情况 -> error_count++, reset_parser()
|
||||
*
|
||||
* [PARSE_PARAM2]:
|
||||
* - byte='*' -> 字段结束,state=PARSE_CHECKSUM,cs_index=0
|
||||
* - is_valid_param_char(byte) -> 写入param2缓冲区,checksum_acc^=byte
|
||||
* - 其他情况 -> error_count++, reset_parser()
|
||||
*
|
||||
* [PARSE_CHECKSUM]:
|
||||
* - byte='\n' -> 校验和接收完成,验证并设置state=PARSE_COMPLETE
|
||||
* - byte='\r' -> 忽略(回车符)
|
||||
* - 其他情况 -> 写入cs_buffer[cs_index++],最多2个字符
|
||||
*
|
||||
* [PARSE_COMPLETE]:
|
||||
* - reset_parser()
|
||||
* - byte='$' -> state=PARSE_CMD (允许连续帧)
|
||||
*
|
||||
* 超时处理:
|
||||
* 如果当前状态不是IDLE且距离上次接收超过PARSE_TIMEOUT_MS,
|
||||
* 则判定为接收超时,重置解析器到IDLE状态
|
||||
*/
|
||||
void CmdParser_FeedByte(uint8_t byte, uint32_t current_tick)
|
||||
{
|
||||
/*----------------------------------------------------------
|
||||
* 超时检测
|
||||
* 如果接收到新字节且距离上次接收已超时,则重置解析器
|
||||
*----------------------------------------------------------*/
|
||||
if (ctx.state != PARSE_IDLE && ctx.state != PARSE_COMPLETE) {
|
||||
if (current_tick - ctx.last_rx_tick >= PARSE_TIMEOUT_MS) {
|
||||
ctx.error_count++;
|
||||
DEBUG_LOG("Timeout, reset parser");
|
||||
reset_parser();
|
||||
/* 超时后如果收到'$',立即开始解析新帧 */
|
||||
if (byte == '$') {
|
||||
ctx.state = PARSE_CMD;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* 更新最后接收时间戳 */
|
||||
ctx.last_rx_tick = current_tick;
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 状态机主分支:根据当前状态处理输入字节
|
||||
*----------------------------------------------------------*/
|
||||
switch (ctx.state) {
|
||||
/*----------------------------------------------------------
|
||||
* PARSE_IDLE: 等待帧起始符'$'
|
||||
*----------------------------------------------------------*/
|
||||
case PARSE_IDLE:
|
||||
if (byte == '$') {
|
||||
reset_parser();
|
||||
@ -241,61 +621,82 @@ void CmdParser_FeedByte(uint8_t byte, uint32_t current_tick)
|
||||
}
|
||||
break;
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* PARSE_CMD: 解析命令字段
|
||||
*----------------------------------------------------------*/
|
||||
case PARSE_CMD:
|
||||
if (byte == ',') {
|
||||
/* 命令字段结束符,遇到逗号转入参数1解析 */
|
||||
ctx.frame.cmd[ctx.field_index] = '\0';
|
||||
ctx.state = PARSE_PARAM1;
|
||||
ctx.field_index = 0;
|
||||
} else if (byte == '*') {
|
||||
/* 无参数命令,遇到'*'直接转入校验和解析 */
|
||||
ctx.frame.cmd[ctx.field_index] = '\0';
|
||||
ctx.state = PARSE_CHECKSUM;
|
||||
ctx.field_index = 0;
|
||||
ctx.cs_index = 0;
|
||||
} else if (is_valid_cmd_char(byte)) {
|
||||
/* 有效的命令字符,写入缓冲区 */
|
||||
if (ctx.field_index < CMD_MAX_LEN - 1) {
|
||||
ctx.frame.cmd[ctx.field_index++] = byte;
|
||||
ctx.checksum_acc ^= byte;
|
||||
ctx.checksum_acc ^= byte; /* 累加到校验和 */
|
||||
} else {
|
||||
/* 缓冲区溢出,命令过长 */
|
||||
ctx.error_count++;
|
||||
reset_parser();
|
||||
}
|
||||
} else {
|
||||
/* 无效字符,命令只能包含大写字母和数字 */
|
||||
ctx.error_count++;
|
||||
reset_parser();
|
||||
}
|
||||
break;
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* PARSE_PARAM1: 解析第一个参数
|
||||
*----------------------------------------------------------*/
|
||||
case PARSE_PARAM1:
|
||||
if (byte == ',') {
|
||||
/* 参数1结束符,遇到逗号转入参数2解析 */
|
||||
ctx.frame.param1[ctx.field_index] = '\0';
|
||||
ctx.state = PARSE_PARAM2;
|
||||
ctx.field_index = 0;
|
||||
} else if (byte == '*') {
|
||||
/* 参数1结束符,遇到'*'直接转入校验和解析 */
|
||||
ctx.frame.param1[ctx.field_index] = '\0';
|
||||
ctx.state = PARSE_CHECKSUM;
|
||||
ctx.field_index = 0;
|
||||
ctx.cs_index = 0;
|
||||
} else if (is_valid_param_char(byte)) {
|
||||
/* 有效参数字符,写入缓冲区 */
|
||||
if (ctx.field_index < PARAM_MAX_LEN - 1) {
|
||||
ctx.frame.param1[ctx.field_index++] = byte;
|
||||
ctx.checksum_acc ^= byte;
|
||||
} else {
|
||||
/* 缓冲区溢出,参数过长 */
|
||||
ctx.error_count++;
|
||||
reset_parser();
|
||||
}
|
||||
} else {
|
||||
/* 无效字符 */
|
||||
ctx.error_count++;
|
||||
reset_parser();
|
||||
}
|
||||
break;
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* PARSE_PARAM2: 解析第二个参数
|
||||
*----------------------------------------------------------*/
|
||||
case PARSE_PARAM2:
|
||||
if (byte == '*') {
|
||||
/* 参数2结束符,遇到'*'转入校验和解析 */
|
||||
ctx.frame.param2[ctx.field_index] = '\0';
|
||||
ctx.state = PARSE_CHECKSUM;
|
||||
ctx.field_index = 0;
|
||||
ctx.cs_index = 0;
|
||||
} else if (is_valid_param_char(byte)) {
|
||||
/* 有效参数字符,写入缓冲区 */
|
||||
if (ctx.field_index < PARAM_MAX_LEN - 1) {
|
||||
ctx.frame.param2[ctx.field_index++] = byte;
|
||||
ctx.checksum_acc ^= byte;
|
||||
@ -309,18 +710,29 @@ void CmdParser_FeedByte(uint8_t byte, uint32_t current_tick)
|
||||
}
|
||||
break;
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* PARSE_CHECKSUM: 解析校验和
|
||||
*----------------------------------------------------------*/
|
||||
case PARSE_CHECKSUM:
|
||||
if (byte == '\n') {
|
||||
/* 帧结束符'\n'到达,校验和解析完成 */
|
||||
/* 将两个十六进制字符转换为字节 */
|
||||
ctx.frame.received_cs = hex_to_byte(ctx.cs_buffer[0], ctx.cs_buffer[1]);
|
||||
ctx.frame.calculated_cs = ctx.checksum_acc;
|
||||
/* 0xFF作为特殊值,跳过校验和验证(后门) */
|
||||
ctx.frame.skip_checksum = (ctx.frame.received_cs == 0xFF);
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 校验和验证
|
||||
*----------------------------------------------------------*/
|
||||
if (ctx.frame.skip_checksum ||
|
||||
ctx.frame.received_cs == ctx.frame.calculated_cs) {
|
||||
/* 校验通过,设置帧有效标志 */
|
||||
ctx.frame.valid = true;
|
||||
ctx.state = PARSE_COMPLETE;
|
||||
ctx.valid_count++;
|
||||
} else {
|
||||
/* 校验失败,发送错误响应 */
|
||||
ctx.error_count++;
|
||||
DEBUG_LOG("Checksum error: recv=%02X calc=%02X",
|
||||
ctx.frame.received_cs, ctx.frame.calculated_cs);
|
||||
@ -328,14 +740,20 @@ void CmdParser_FeedByte(uint8_t byte, uint32_t current_tick)
|
||||
reset_parser();
|
||||
}
|
||||
} else if (byte != '\r') {
|
||||
/* 忽略回车符'\r',接收校验和字符(最多2个HEX) */
|
||||
if (ctx.cs_index < 2) {
|
||||
ctx.cs_buffer[ctx.cs_index++] = byte;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* PARSE_COMPLETE: 解析完成状态
|
||||
*----------------------------------------------------------*/
|
||||
case PARSE_COMPLETE:
|
||||
/* 重置解析器,准备接收下一帧 */
|
||||
reset_parser();
|
||||
/* 如果立即收到'$',允许连续帧解析 */
|
||||
if (byte == '$') {
|
||||
ctx.state = PARSE_CMD;
|
||||
}
|
||||
@ -343,6 +761,27 @@ void CmdParser_FeedByte(uint8_t byte, uint32_t current_tick)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 指令解析器任务函数
|
||||
* @note 在主循环中周期性调用,处理已解析完成的指令帧
|
||||
*
|
||||
* @param 无
|
||||
* @return 无
|
||||
*
|
||||
* 工作模式:
|
||||
* CmdParser_FeedByte()负责接收字节并解析,
|
||||
* 此函数负责在帧解析完成后执行相应的业务处理。
|
||||
*
|
||||
* 处理流程:
|
||||
* 1. 检查是否处于PARSE_COMPLETE状态
|
||||
* 2. 检查帧是否valid
|
||||
* 3. 调用process_cmd_frame()执行指令
|
||||
* 4. 重置解析器到IDLE状态
|
||||
*
|
||||
* 注意:
|
||||
* 此设计将"接收解析"与"业务处理"分离,
|
||||
* 避免了中断处理函数中执行复杂业务逻辑
|
||||
*/
|
||||
void CmdParser_Task(void)
|
||||
{
|
||||
if (ctx.state == PARSE_COMPLETE && ctx.frame.valid) {
|
||||
@ -351,6 +790,21 @@ void CmdParser_Task(void)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查询是否存在已完成的指令帧
|
||||
* @note 非阻塞查询接口,供外部模块检查是否有待处理帧
|
||||
*
|
||||
* @param frame: 帧数据输出指针,如果不为NULL则复制帧数据(输出)
|
||||
* @return bool: true=存在有效帧,false=无有效帧
|
||||
*
|
||||
* 使用场景:
|
||||
* 外部模块(如主循环)可轮询此函数,
|
||||
* 当返回true时获取帧数据进行处理
|
||||
*
|
||||
* 注意:
|
||||
* 此函数只是查询,不自动清除帧数据。
|
||||
* 帧数据的清除需要调用CmdParser_Acknowledge()
|
||||
*/
|
||||
bool CmdParser_HasCompleteFrame(cmd_frame_t *frame)
|
||||
{
|
||||
if (ctx.state == PARSE_COMPLETE && ctx.frame.valid) {
|
||||
@ -362,17 +816,81 @@ bool CmdParser_HasCompleteFrame(cmd_frame_t *frame)
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 确认并清除已完成的指令帧
|
||||
* @note 在处理完一帧后调用,重置解析器到初始状态
|
||||
*
|
||||
* @param 无
|
||||
* @return 无
|
||||
*
|
||||
* 使用说明:
|
||||
* 当外部模块通过CmdParser_HasCompleteFrame获取帧数据后,
|
||||
* 处理完毕应调用此函数清除状态,防止重复处理
|
||||
*/
|
||||
void CmdParser_Acknowledge(void)
|
||||
{
|
||||
reset_parser();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取解析错误累计次数
|
||||
* @note 用于诊断通信质量或协议错误统计
|
||||
*
|
||||
* @param 无
|
||||
* @return uint32_t: 错误帧累计次数
|
||||
*
|
||||
* 错误类型包括:
|
||||
* - 字符验证失败
|
||||
* - 缓冲区溢出
|
||||
* - 校验和不匹配
|
||||
* - 接收超时
|
||||
*/
|
||||
uint32_t CmdParser_GetErrorCount(void)
|
||||
{
|
||||
return ctx.error_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取有效帧累计次数
|
||||
* @note 用于诊断通信成功率统计
|
||||
*
|
||||
* @param 无
|
||||
* @return uint32_t: 有效帧累计次数
|
||||
*
|
||||
* 计算公式:成功率 = valid_count / (valid_count + error_count)
|
||||
*/
|
||||
uint32_t CmdParser_GetValidCount(void)
|
||||
{
|
||||
return ctx.valid_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置响应回调函数
|
||||
* @note 用于将响应路由到正确的源端口
|
||||
*
|
||||
* @param callback: 回调函数指针
|
||||
* @return 无
|
||||
*
|
||||
* 回调函数原型:
|
||||
* void callback(uint8_t source_port, const char *response)
|
||||
*
|
||||
* 参数说明:
|
||||
* - source_port: 指令来源端口ID
|
||||
* - response: 待发送的响应字符串
|
||||
*/
|
||||
void CmdParser_SetResponseCallback(cmd_response_callback_t callback)
|
||||
{
|
||||
g_response_callback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置当前源端口
|
||||
* @note 在开始解析新指令前调用,记录指令来源
|
||||
*
|
||||
* @param port_id: 端口ID
|
||||
* @return 无
|
||||
*/
|
||||
void CmdParser_SetSourcePort(uint8_t port_id)
|
||||
{
|
||||
g_current_source_port = port_id;
|
||||
}
|
||||
|
||||
222
Core/Src/cmd_router.c
Normal file
222
Core/Src/cmd_router.c
Normal file
@ -0,0 +1,222 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file cmd_router.c
|
||||
* @brief 指令路由与响应分发模块实现
|
||||
* @author Application Layer
|
||||
* @version 1.0
|
||||
******************************************************************************
|
||||
* @attention
|
||||
* 本模块实现指令路由与响应分发功能
|
||||
* 设计依据:多通信接口统一指令处理系统开发计划 第3.2节
|
||||
*
|
||||
* 核心职责:
|
||||
* - 从各UART端口读取数据并喂入解析器
|
||||
* - 根据指令来源端口路由响应
|
||||
* - 管理响应路由表
|
||||
*
|
||||
* 工作流程:
|
||||
* 1. 中断中将接收字节写入对应端口的环形缓冲区
|
||||
* 2. 主循环中CmdRouter_Task轮询各端口缓冲区
|
||||
* 3. 读取字节喂入CmdParser解析器
|
||||
* 4. 解析完成后通过回调发送响应到源端口
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "cmd_router.h"
|
||||
#include "multi_uart_router.h"
|
||||
#include "cmd_parser.h"
|
||||
#include "uart2_print.h"
|
||||
#include "debug_log.h"
|
||||
#include <string.h>
|
||||
|
||||
#define DEBUG_CMD_ROUTER 1
|
||||
|
||||
#if DEBUG_CMD_ROUTER
|
||||
#define ROUTER_LOG(fmt, ...) UART2_Print_Printf("[ROUTER] " fmt "\r\n", ##__VA_ARGS__)
|
||||
#else
|
||||
#define ROUTER_LOG(fmt, ...)
|
||||
#endif
|
||||
|
||||
#define RX_LOG_BUFFER_SIZE 128
|
||||
|
||||
typedef struct {
|
||||
uint8_t source_port;
|
||||
uint32_t last_feed_tick;
|
||||
uint32_t rx_count;
|
||||
bool active;
|
||||
} port_parser_state_t;
|
||||
|
||||
static port_parser_state_t g_port_states[PORT_COUNT];
|
||||
|
||||
static cmd_response_handler_t g_response_handler = NULL;
|
||||
|
||||
static uint32_t g_processed_count = 0;
|
||||
static uint32_t g_routed_count = 0;
|
||||
|
||||
static uint8_t g_current_parsing_port = PORT_UART2;
|
||||
|
||||
static uint8_t g_rx_log_buffer[PORT_COUNT][RX_LOG_BUFFER_SIZE];
|
||||
static uint16_t g_rx_log_len[PORT_COUNT];
|
||||
static uint32_t g_rx_log_last_tick[PORT_COUNT];
|
||||
#define RX_LOG_TIMEOUT_MS 50
|
||||
|
||||
static void flush_rx_log(port_id_t port_id)
|
||||
{
|
||||
if (g_rx_log_len[port_id] == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
UART2_Print_Printf("[ROUTER] %s RX: \"", MultiUART_GetPortName(port_id));
|
||||
|
||||
for (uint16_t i = 0; i < g_rx_log_len[port_id]; i++) {
|
||||
uint8_t c = g_rx_log_buffer[port_id][i];
|
||||
if (c >= 0x20 && c < 0x7F) {
|
||||
UART2_Print_Send(&c, 1);
|
||||
} else if (c == '\r') {
|
||||
UART2_Print_Send((const uint8_t *)"\\r", 2);
|
||||
} else if (c == '\n') {
|
||||
UART2_Print_Send((const uint8_t *)"\\n", 2);
|
||||
} else {
|
||||
char hex[4];
|
||||
snprintf(hex, sizeof(hex), "\\x%02X", c);
|
||||
UART2_Print_Send((const uint8_t *)hex, 4);
|
||||
}
|
||||
}
|
||||
|
||||
UART2_Print_String("\"\r\n");
|
||||
|
||||
g_rx_log_len[port_id] = 0;
|
||||
}
|
||||
|
||||
static void append_rx_log(port_id_t port_id, uint8_t byte)
|
||||
{
|
||||
if (g_rx_log_len[port_id] < RX_LOG_BUFFER_SIZE) {
|
||||
g_rx_log_buffer[port_id][g_rx_log_len[port_id]++] = byte;
|
||||
}
|
||||
g_rx_log_last_tick[port_id] = HAL_GetTick();
|
||||
}
|
||||
|
||||
static void cmd_parser_response_callback(uint8_t source_port, const char *response)
|
||||
{
|
||||
if (g_response_handler != NULL) {
|
||||
g_response_handler((port_id_t)source_port, response, strlen(response));
|
||||
g_routed_count++;
|
||||
} else {
|
||||
MultiUART_SendString((port_id_t)source_port, response);
|
||||
g_routed_count++;
|
||||
}
|
||||
|
||||
LOG_INFO("ROUTER", "TX[%s]: %s",
|
||||
MultiUART_GetPortName((port_id_t)source_port), response);
|
||||
}
|
||||
|
||||
void CmdRouter_Init(void)
|
||||
{
|
||||
for (port_id_t i = 0; i < PORT_COUNT; i++) {
|
||||
g_port_states[i].source_port = i;
|
||||
g_port_states[i].last_feed_tick = 0;
|
||||
g_port_states[i].rx_count = 0;
|
||||
g_port_states[i].active = false;
|
||||
|
||||
g_rx_log_len[i] = 0;
|
||||
g_rx_log_last_tick[i] = 0;
|
||||
}
|
||||
|
||||
g_response_handler = NULL;
|
||||
g_processed_count = 0;
|
||||
g_routed_count = 0;
|
||||
g_current_parsing_port = PORT_UART2;
|
||||
|
||||
CmdParser_SetResponseCallback(cmd_parser_response_callback);
|
||||
|
||||
ROUTER_LOG("Init OK, %d ports registered", PORT_COUNT);
|
||||
}
|
||||
|
||||
void CmdRouter_Task(void)
|
||||
{
|
||||
uint32_t current_tick = HAL_GetTick();
|
||||
|
||||
for (port_id_t port_id = 0; port_id < PORT_COUNT; port_id++) {
|
||||
if (port_id == PORT_UART2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (g_rx_log_len[port_id] > 0 &&
|
||||
(current_tick - g_rx_log_last_tick[port_id]) >= RX_LOG_TIMEOUT_MS) {
|
||||
flush_rx_log(port_id);
|
||||
}
|
||||
}
|
||||
|
||||
for (port_id_t port_id = 0; port_id < PORT_COUNT; port_id++) {
|
||||
if (port_id == PORT_UART2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint16_t rx_count = MultiUART_GetRxCount(port_id);
|
||||
|
||||
if (rx_count == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
g_current_parsing_port = port_id;
|
||||
CmdParser_SetSourcePort(port_id);
|
||||
|
||||
uint8_t byte;
|
||||
while (MultiUART_ReadByte(port_id, &byte) > 0) {
|
||||
append_rx_log(port_id, byte);
|
||||
CmdParser_FeedByte(byte, HAL_GetTick());
|
||||
g_port_states[port_id].rx_count++;
|
||||
}
|
||||
|
||||
if (CmdParser_HasCompleteFrame(NULL)) {
|
||||
flush_rx_log(port_id);
|
||||
g_processed_count++;
|
||||
g_port_states[port_id].active = true;
|
||||
}
|
||||
}
|
||||
|
||||
CmdParser_Task();
|
||||
}
|
||||
|
||||
void CmdRouter_SetResponseHandler(cmd_response_handler_t handler)
|
||||
{
|
||||
g_response_handler = handler;
|
||||
ROUTER_LOG("Response handler %s", handler ? "set" : "cleared");
|
||||
}
|
||||
|
||||
void CmdRouter_SendResponse(port_id_t port, const char *response, uint16_t len)
|
||||
{
|
||||
if (port >= PORT_COUNT || response == NULL || len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
MultiUART_Send(port, (const uint8_t *)response, len);
|
||||
g_routed_count++;
|
||||
|
||||
LOG_INFO("ROUTER", "SendResponse[%s]: %.*s",
|
||||
MultiUART_GetPortName(port), len, response);
|
||||
}
|
||||
|
||||
void CmdRouter_BroadcastResponse(const char *response, uint16_t len)
|
||||
{
|
||||
if (response == NULL || len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (port_id_t port = 0; port < PORT_COUNT; port++) {
|
||||
MultiUART_Send(port, (const uint8_t *)response, len);
|
||||
g_routed_count++;
|
||||
}
|
||||
|
||||
LOG_INFO("ROUTER", "Broadcast: %.*s", len, response);
|
||||
}
|
||||
|
||||
uint32_t CmdRouter_GetProcessedCount(void)
|
||||
{
|
||||
return g_processed_count;
|
||||
}
|
||||
|
||||
uint32_t CmdRouter_GetRoutedCount(void)
|
||||
{
|
||||
return g_routed_count;
|
||||
}
|
||||
148
Core/Src/debug_log.c
Normal file
148
Core/Src/debug_log.c
Normal file
@ -0,0 +1,148 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file debug_log.c
|
||||
* @brief 增强型调试日志系统实现
|
||||
* @author Application Layer
|
||||
* @version 1.0
|
||||
******************************************************************************
|
||||
* @attention
|
||||
* 本模块实现增强型调试日志系统
|
||||
* 设计依据:多通信接口统一指令处理系统开发计划 第3.4节
|
||||
*
|
||||
* 日志格式:[LEVEL][MODULE] message
|
||||
* 示例:[INFO][CMD] Command received: RL
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "debug_log.h"
|
||||
#include "uart2_print.h"
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define MAX_MODULES 16
|
||||
#define MODULE_NAME_LEN 16
|
||||
|
||||
typedef struct {
|
||||
char name[MODULE_NAME_LEN];
|
||||
bool enabled;
|
||||
} module_config_t;
|
||||
|
||||
static log_level_t g_current_level = LOG_LEVEL_DEBUG;
|
||||
static module_config_t g_modules[MAX_MODULES];
|
||||
static bool g_modules_initialized = false;
|
||||
|
||||
static const char *const g_level_str[] = {
|
||||
[LOG_LEVEL_DEBUG] = "DEBUG",
|
||||
[LOG_LEVEL_INFO] = "INFO ",
|
||||
[LOG_LEVEL_WARN] = "WARN ",
|
||||
[LOG_LEVEL_ERROR] = "ERROR",
|
||||
};
|
||||
|
||||
static void init_modules(void)
|
||||
{
|
||||
if (g_modules_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_MODULES; i++) {
|
||||
g_modules[i].name[0] = '\0';
|
||||
g_modules[i].enabled = false;
|
||||
}
|
||||
|
||||
g_modules_initialized = true;
|
||||
}
|
||||
|
||||
static bool is_module_enabled(const char *module)
|
||||
{
|
||||
if (module == NULL || module[0] == '\0') {
|
||||
return true;
|
||||
}
|
||||
|
||||
init_modules();
|
||||
|
||||
for (int i = 0; i < MAX_MODULES; i++) {
|
||||
if (g_modules[i].name[0] != '\0' &&
|
||||
strncmp(g_modules[i].name, module, MODULE_NAME_LEN - 1) == 0) {
|
||||
return g_modules[i].enabled;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DebugLog_Init(void)
|
||||
{
|
||||
g_current_level = LOG_LEVEL_DEBUG;
|
||||
init_modules();
|
||||
|
||||
UART2_Print_String("[LOG] Debug log system initialized\r\n");
|
||||
}
|
||||
|
||||
void DebugLog_SetLevel(log_level_t level)
|
||||
{
|
||||
g_current_level = level;
|
||||
}
|
||||
|
||||
log_level_t DebugLog_GetLevel(void)
|
||||
{
|
||||
return g_current_level;
|
||||
}
|
||||
|
||||
void DebugLog_EnableModule(const char *module, bool enable)
|
||||
{
|
||||
if (module == NULL || module[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
init_modules();
|
||||
|
||||
for (int i = 0; i < MAX_MODULES; i++) {
|
||||
if (g_modules[i].name[0] == '\0') {
|
||||
strncpy(g_modules[i].name, module, MODULE_NAME_LEN - 1);
|
||||
g_modules[i].name[MODULE_NAME_LEN - 1] = '\0';
|
||||
g_modules[i].enabled = enable;
|
||||
return;
|
||||
}
|
||||
|
||||
if (strncmp(g_modules[i].name, module, MODULE_NAME_LEN - 1) == 0) {
|
||||
g_modules[i].enabled = enable;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugLog_Output(log_level_t level, const char *module, const char *fmt, ...)
|
||||
{
|
||||
if (level < g_current_level) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_module_enabled(module)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[256];
|
||||
int len = 0;
|
||||
|
||||
len += snprintf(buffer + len, sizeof(buffer) - len, "[%s][%s] ",
|
||||
g_level_str[level], module ? module : "MAIN");
|
||||
|
||||
if (len < (int)sizeof(buffer) - 1) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
len += vsnprintf(buffer + len, sizeof(buffer) - len, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
if (len > 0 && len < (int)sizeof(buffer)) {
|
||||
buffer[len++] = '\r';
|
||||
if (len < (int)sizeof(buffer)) {
|
||||
buffer[len++] = '\n';
|
||||
}
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
UART2_Print_Send((const uint8_t *)buffer, len);
|
||||
}
|
||||
}
|
||||
@ -3,16 +3,17 @@
|
||||
* @file io_monitor.c
|
||||
* @brief IO状态监控模块实现
|
||||
* @author Application Layer
|
||||
* @version 1.1
|
||||
* @version 1.2
|
||||
******************************************************************************
|
||||
* @attention
|
||||
* 本模块实现四路数字输入的状态监控
|
||||
* 关键特性:
|
||||
* 1. 10ms定时扫描,平衡响应速度和CPU占用
|
||||
* 2. 软件去抖,连续3次相同状态才确认变化
|
||||
* 3. 状态变化时自动上报ASCII格式消息
|
||||
* 3. 状态变化时通过回调函数上报,支持多端口路由
|
||||
*
|
||||
* 修订历史:
|
||||
* v1.2 - 增加事件回调机制,支持多端口路由
|
||||
* v1.1 - 修复审查报告中危-5:去抖计数器初始化优化
|
||||
******************************************************************************
|
||||
*/
|
||||
@ -22,23 +23,64 @@
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
|
||||
/*==============================================================================
|
||||
* 调试宏定义
|
||||
*============================================================================*/
|
||||
/* DEBUG_IO_MONITOR: 调试日志开关,置1时启用调试输出,置0时禁用 */
|
||||
#define DEBUG_IO_MONITOR 1
|
||||
|
||||
#if DEBUG_IO_MONITOR
|
||||
/* 调试日志宏,带模块前缀"[IO]"方便在串口调试终端中过滤日志 */
|
||||
#define DEBUG_LOG(fmt, ...) UART2_Print_Printf("[IO] " fmt "\r\n", ##__VA_ARGS__)
|
||||
#else
|
||||
/* 禁用调试日志时,将宏定义为空,避免生成无用代码 */
|
||||
#define DEBUG_LOG(fmt, ...)
|
||||
#endif
|
||||
|
||||
/*==============================================================================
|
||||
* 数据结构定义
|
||||
*============================================================================*/
|
||||
/**
|
||||
* @brief IO通道数据结构
|
||||
* @note 用于描述一个数字输入通道的完整状态信息
|
||||
*
|
||||
* 设计目的:
|
||||
* 该结构体封装了监控单个数字输入(DI)通道所需的全部状态信息,包括
|
||||
* 硬件连接参数(GPIO端口和引脚)、当前稳定状态、去抖计数器、原始采样状态
|
||||
* 以及状态变化统计。这使得多通道扫描逻辑能够以统一的结构处理每个通道。
|
||||
*
|
||||
* 字段说明:
|
||||
* - port: GPIO端口指针,指向硬件寄存器(如GPIOB)
|
||||
* - pin: GPIO引脚编号(如GPIO_PIN_4),用于HAL库读取函数
|
||||
* - current_state: 经过去抖处理后的稳定状态,0=低电平,1=高电平
|
||||
* - debounce_counter: 去抖计数器,累加相同采样值的次数,达到阈值才确认状态变化
|
||||
* - last_raw_state: 上一次采样的原始电平状态,用于检测电平变化
|
||||
* - change_count: 状态变化总次数统计,用于诊断和监控
|
||||
*/
|
||||
typedef struct {
|
||||
GPIO_TypeDef *port;
|
||||
uint16_t pin;
|
||||
uint8_t current_state;
|
||||
uint8_t debounce_counter;
|
||||
uint8_t last_raw_state;
|
||||
uint32_t change_count;
|
||||
GPIO_TypeDef *port; /**< GPIO端口指针,指向外设寄存器基地址 */
|
||||
uint16_t pin; /**< GPIO引脚编号,对应HAL库的引脚定义 */
|
||||
uint8_t current_state; /**< 经去抖处理后的稳定状态,0=低电平,1=高电平 */
|
||||
uint8_t debounce_counter; /**< 去抖计数器,连续采样到相同值的次数 */
|
||||
uint8_t last_raw_state; /**< 上一次采样的原始电平,用于变化检测 */
|
||||
uint32_t change_count; /**< 状态变化累计次数,用于性能监控 */
|
||||
} io_channel_t;
|
||||
|
||||
/*==============================================================================
|
||||
* 全局变量定义
|
||||
*============================================================================*/
|
||||
/**
|
||||
* @brief 四路数字输入通道配置表
|
||||
* @note 静态初始化表,定义四个监控通道的GPIO硬件连接
|
||||
*
|
||||
* 各通道映射关系:
|
||||
* - 通道0: GPIOB, GPIO_PIN_4
|
||||
* - 通道1: GPIOB, GPIO_PIN_5
|
||||
* - 通道2: GPIOB, GPIO_PIN_6
|
||||
* - 通道3: GPIOB, GPIO_PIN_7
|
||||
*
|
||||
* 初始化值说明:所有状态和计数器均初始化为0,表示上电初始状态未知
|
||||
*/
|
||||
static io_channel_t di_channels[IO_CHANNEL_COUNT] = {
|
||||
{GPIOB, GPIO_PIN_4, 0, 0, 0, 0},
|
||||
{GPIOB, GPIO_PIN_5, 0, 0, 0, 0},
|
||||
@ -46,9 +88,75 @@ static io_channel_t di_channels[IO_CHANNEL_COUNT] = {
|
||||
{GPIOB, GPIO_PIN_7, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 上一次扫描时刻的时间戳
|
||||
* @note 用于实现10ms固定间隔扫描,避免每次都执行扫描操作
|
||||
* HAL_GetTick()返回系统运行毫秒数
|
||||
*/
|
||||
static uint32_t last_scan_tick = 0;
|
||||
|
||||
/**
|
||||
* @brief 事件上报使能标志
|
||||
* @note 置true时允许状态变化自动上报,置false时禁止上报
|
||||
* 可用于批量操作时临时抑制不必要的事件通知
|
||||
*/
|
||||
static bool report_enabled = true;
|
||||
|
||||
/**
|
||||
* @brief IO事件回调函数指针
|
||||
* @note 设置后,IO状态变化将通过回调函数上报
|
||||
* 为NULL时使用默认的UART2_Print_String输出
|
||||
*/
|
||||
static io_event_callback_t g_event_callback = NULL;
|
||||
|
||||
/*==============================================================================
|
||||
* 内部静态函数声明
|
||||
*============================================================================*/
|
||||
/**
|
||||
* @brief 计算异或校验和
|
||||
* @note 对输入数据的每个字节执行异或运算,生成校验码
|
||||
* 用于DI_EVENT消息的校验和计算
|
||||
*
|
||||
* @param data: 待计算校验和的数据缓冲区(输入)
|
||||
* @param len: 数据长度,以字节为单位(输入)
|
||||
* @return 校验和: uint8_t类型的异或结果
|
||||
*
|
||||
* 算法原理:遍历数据缓冲区,将每个字节与累加器进行异或操作
|
||||
* 初始值为0,最终结果为所有字节的异或和
|
||||
*/
|
||||
static uint8_t calc_checksum(const char *data, uint8_t len);
|
||||
|
||||
/**
|
||||
* @brief 发送DI状态变化事件
|
||||
* @note 构造并发送ASCII格式的状态变化消息至UART2
|
||||
* 消息格式: $DI_EVENT,<channel>,<state>*<checksum>\r\n
|
||||
*
|
||||
* @param channel: 通道编号,从0开始计数(输入)
|
||||
* @param state: 通道状态,0=低电平,1=高电平(输入)
|
||||
* @return 无返回值
|
||||
*
|
||||
* 调用说明:
|
||||
* - 此函数在状态变化被确认后调用,用于通知上位机或日志系统
|
||||
* - 内部会调用calc_checksum计算校验和
|
||||
* - 通过UART2_Print_String发送原始字符串
|
||||
*/
|
||||
static void send_di_event(uint8_t channel, uint8_t state);
|
||||
|
||||
/*==============================================================================
|
||||
* 内部静态函数实现
|
||||
*============================================================================*/
|
||||
/**
|
||||
* @brief 计算异或校验和
|
||||
* @note 对输入数据的每个字节执行异或运算,生成校验码
|
||||
* 用于DI_EVENT消息的校验和计算
|
||||
*
|
||||
* @param data: 待计算校验和的数据缓冲区(输入)
|
||||
* @param len: 数据长度,以字节为单位(输入)
|
||||
* @return 校验和: uint8_t类型的异或结果
|
||||
*
|
||||
* 算法原理:遍历数据缓冲区,将每个字节与累加器进行异或操作
|
||||
* 初始值为0,最终结果为所有字节的异或和
|
||||
*/
|
||||
static uint8_t calc_checksum(const char *data, uint8_t len)
|
||||
{
|
||||
uint8_t cs = 0;
|
||||
@ -58,63 +166,156 @@ static uint8_t calc_checksum(const char *data, uint8_t len)
|
||||
return cs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送DI状态变化事件
|
||||
* @note 构造并发送ASCII格式的状态变化消息
|
||||
* 消息格式: $DI_EVENT,<channel>,<state>*<checksum>\r\n
|
||||
* 如果设置了回调函数,则通过回调发送;否则发送到UART2
|
||||
*
|
||||
* @param channel: 通道编号,从0开始计数(输入)
|
||||
* @param state: 通道状态,0=低电平,1=高电平(输入)
|
||||
* @return 无返回值
|
||||
*
|
||||
* 调用说明:
|
||||
* - 此函数在状态变化被确认后调用,用于通知上位机或日志系统
|
||||
* - 内部会调用calc_checksum计算校验和
|
||||
* - 如果设置了回调函数,则通过回调发送;否则通过UART2_Print_String发送
|
||||
*/
|
||||
static void send_di_event(uint8_t channel, uint8_t state)
|
||||
{
|
||||
char msg[32];
|
||||
uint8_t cs;
|
||||
|
||||
/* 构造消息主体,channel+1将0-base转换为1-base的用户可见编号 */
|
||||
int len = snprintf(msg, sizeof(msg), "$DI_EVENT,%d,%d*", channel + 1, state);
|
||||
|
||||
/* 计算异或校验和,跳过'$'符号只对正文部分计算 */
|
||||
cs = calc_checksum(msg + 1, len - 1);
|
||||
|
||||
/* 将校验和追加到消息末尾,格式为两位十六进制数 */
|
||||
snprintf(msg + len, sizeof(msg) - len, "%02X\r\n", cs);
|
||||
|
||||
/* 根据是否设置回调函数选择发送方式 */
|
||||
if (g_event_callback != NULL) {
|
||||
/* 通过回调函数发送,支持多端口路由 */
|
||||
g_event_callback(channel, state, msg);
|
||||
} else {
|
||||
/* 默认发送到UART2 */
|
||||
UART2_Print_String(msg);
|
||||
}
|
||||
|
||||
/* 输出调试日志,记录状态变化 */
|
||||
DEBUG_LOG("CH%d -> %s", channel + 1, state ? "HIGH" : "LOW");
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 公共函数实现
|
||||
*============================================================================*/
|
||||
/**
|
||||
* @brief IO监控模块初始化
|
||||
* @note 在系统启动时调用,初始化所有数字输入通道的默认状态
|
||||
*
|
||||
* @param 无
|
||||
* @return 无
|
||||
*
|
||||
* 功能说明:
|
||||
* 1. 读取各通道GPIO引脚的当前电平状态
|
||||
* 2. 初始化去抖计数器为1(已稳定采样一次)
|
||||
* 3. 重置变化计数器和扫描时间戳
|
||||
* 4. 使能事件上报功能
|
||||
*
|
||||
* 初始化策略:
|
||||
* - 去抖计数器初始化为1而非0,这样首次采样时只需再连续采样2次
|
||||
* 即可确认状态,相比初始化为0可加快首次状态确认速度
|
||||
*/
|
||||
void IO_Monitor_Init(void)
|
||||
{
|
||||
/* 遍历所有IO通道进行初始化 */
|
||||
for (int i = 0; i < IO_CHANNEL_COUNT; i++) {
|
||||
io_channel_t *ch = &di_channels[i];
|
||||
|
||||
/* 读取GPIO当前电平,HAL_GPIO_ReadPin返回GPIO_PinState类型 */
|
||||
ch->current_state = HAL_GPIO_ReadPin(ch->port, ch->pin) ? 1 : 0;
|
||||
/* 记录原始状态作为上次采样值 */
|
||||
ch->last_raw_state = ch->current_state;
|
||||
/* 去抖计数器初始化为1,已完成首次稳定采样 */
|
||||
ch->debounce_counter = 1;
|
||||
/* 变化计数清零 */
|
||||
ch->change_count = 0;
|
||||
}
|
||||
|
||||
/* 初始化扫描时间戳为0,确保首次扫描立即执行 */
|
||||
last_scan_tick = 0;
|
||||
/* 使能自动上报功能 */
|
||||
report_enabled = true;
|
||||
|
||||
/* 输出初始化完成日志,显示初始各通道状态掩码 */
|
||||
DEBUG_LOG("Init OK, initial states: 0x%02X", IO_Monitor_GetAllStates());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief IO监控定时任务
|
||||
* @note 应在主循环或定时器中断中周期性调用,执行扫描和去抖处理
|
||||
*
|
||||
* @param 无
|
||||
* @return 无
|
||||
*
|
||||
* 算法说明 - 软件去抖三段式状态机:
|
||||
* 阶段1: 采样值与上次相同 -> 计数器累加
|
||||
* 阶段2: 采样值与上次不同 -> 重置计数器并更新上次值
|
||||
* 阶段3: 计数器达到阈值(IO_DEBOUNCE_COUNT)且与当前稳定状态不同
|
||||
* -> 确认状态变化,更新current_state,触发事件上报
|
||||
*
|
||||
* 去抖阈值说明:
|
||||
* - IO_DEBOUNCE_COUNT通常定义为3,表示连续3次采样相同才确认
|
||||
* - 结合IO_SCAN_PERIOD_MS(10ms),去抖确认时间为20-30ms
|
||||
* - 可有效滤除机械开关的抖动噪声
|
||||
*/
|
||||
void IO_Monitor_Task(void)
|
||||
{
|
||||
/* 获取当前系统时间戳(毫秒) */
|
||||
uint32_t current_tick = HAL_GetTick();
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 扫描周期控制
|
||||
* 仅当距上次扫描超过IO_SCAN_PERIOD_MS时才执行新扫描
|
||||
* 这种节流机制可避免高频调用时CPU资源浪费
|
||||
*----------------------------------------------------------*/
|
||||
if (current_tick - last_scan_tick < IO_SCAN_PERIOD_MS) {
|
||||
return;
|
||||
}
|
||||
/* 更新扫描时间戳 */
|
||||
last_scan_tick = current_tick;
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 遍历所有通道执行去抖扫描
|
||||
*----------------------------------------------------------*/
|
||||
for (int i = 0; i < IO_CHANNEL_COUNT; i++) {
|
||||
io_channel_t *ch = &di_channels[i];
|
||||
|
||||
/* 读取GPIO引脚当前电平,转换为0/1表示 */
|
||||
uint8_t raw_state = HAL_GPIO_ReadPin(ch->port, ch->pin) ? 1 : 0;
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 去抖逻辑分支
|
||||
*----------------------------------------------------------*/
|
||||
if (raw_state != ch->last_raw_state) {
|
||||
/* 分支1: 采样值发生变化 -> 重置去抖计数器 */
|
||||
ch->debounce_counter = 0;
|
||||
ch->last_raw_state = raw_state;
|
||||
} else {
|
||||
/* 分支2: 采样值与上次相同 -> 累加去抖计数器 */
|
||||
if (ch->debounce_counter < IO_DEBOUNCE_COUNT) {
|
||||
ch->debounce_counter++;
|
||||
} else if (ch->current_state != raw_state) {
|
||||
}
|
||||
/* 分支3: 计数器达到阈值且与稳定状态不一致 -> 确认状态变化 */
|
||||
else if (ch->current_state != raw_state) {
|
||||
/* 更新为新确认的稳定状态 */
|
||||
ch->current_state = raw_state;
|
||||
/* 增加变化统计计数 */
|
||||
ch->change_count++;
|
||||
|
||||
/* 事件上报(已使能情况下) */
|
||||
if (report_enabled) {
|
||||
send_di_event(i, raw_state);
|
||||
}
|
||||
@ -123,18 +324,46 @@ void IO_Monitor_Task(void)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取指定通道的当前状态
|
||||
* @note 返回经过去抖处理后的稳定状态值
|
||||
*
|
||||
* @param channel: 通道编号,从0开始计数(输入)
|
||||
* @return 通道状态: 0=低电平,1=高电平,通道无效时返回0
|
||||
*
|
||||
* 使用说明:
|
||||
* - 通道编号超出有效范围(0-3)时静默返回0,不报错
|
||||
* - 返回的是已去抖的稳定状态,非原始采样值
|
||||
*/
|
||||
uint8_t IO_Monitor_GetState(uint8_t channel)
|
||||
{
|
||||
/* 参数边界检查,超出范围的通道返回0 */
|
||||
if (channel >= IO_CHANNEL_COUNT) {
|
||||
return 0;
|
||||
}
|
||||
return di_channels[channel].current_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取所有通道状态的组合掩码
|
||||
* @note 将四路通道状态打包为一个字节返回,方便批量读取和显示
|
||||
*
|
||||
* @param 无
|
||||
* @return 通道状态掩码: uint8_t类型,每位对应一个通道状态
|
||||
*
|
||||
* 位域定义:
|
||||
* - bit0: 通道0状态
|
||||
* - bit1: 通道1状态
|
||||
* - bit2: 通道2状态
|
||||
* - bit3: 通道3状态
|
||||
*
|
||||
* 示例:返回0x05(0101b)表示通道0=高、通道1=低、通道2=高、通道3=低
|
||||
*/
|
||||
uint8_t IO_Monitor_GetAllStates(void)
|
||||
{
|
||||
uint8_t states = 0;
|
||||
|
||||
/* 遍历所有通道,将各通道状态按位组合成掩码 */
|
||||
for (int i = 0; i < IO_CHANNEL_COUNT; i++) {
|
||||
if (di_channels[i].current_state) {
|
||||
states |= (1 << i);
|
||||
@ -143,16 +372,57 @@ uint8_t IO_Monitor_GetAllStates(void)
|
||||
return states;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置事件上报使能状态
|
||||
* @note 可用于在批量操作或初始化过程中临时抑制事件上报
|
||||
*
|
||||
* @param enable: true=使能上报,false=禁用上报(输入)
|
||||
* @return 无
|
||||
*
|
||||
* 使用场景:
|
||||
* - 系统初始化期间不希望产生大量事件,可先禁用再恢复
|
||||
* - 批量设置多个通道状态时,可先禁用避免中间状态触发事件
|
||||
*/
|
||||
void IO_Monitor_EnableReport(bool enable)
|
||||
{
|
||||
report_enabled = enable;
|
||||
DEBUG_LOG("Report %s", enable ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取指定通道的状态变化次数
|
||||
* @note 用于诊断和性能监控,统计各通道状态变化频率
|
||||
*
|
||||
* @param channel: 通道编号,从0开始计数(输入)
|
||||
* @return 变化次数: uint32_t类型,通道无效时返回0
|
||||
*
|
||||
* 使用说明:
|
||||
* - 变化次数从模块初始化后开始累计
|
||||
* - 可用于检测某通道是否频繁触发或存在故障(如开关抖动)
|
||||
*/
|
||||
uint32_t IO_Monitor_GetChangeCount(uint8_t channel)
|
||||
{
|
||||
/* 参数边界检查,超出范围返回0 */
|
||||
if (channel >= IO_CHANNEL_COUNT) {
|
||||
return 0;
|
||||
}
|
||||
return di_channels[channel].change_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置IO事件回调函数
|
||||
* @note 设置后,IO状态变化将通过回调函数上报
|
||||
*
|
||||
* @param callback: 回调函数指针,NULL则使用默认UART2输出(输入)
|
||||
* @return 无
|
||||
*
|
||||
* 使用说明:
|
||||
* - 设置回调后,send_di_event()将通过回调发送事件
|
||||
* - 传入NULL恢复默认UART2输出方式
|
||||
* - 回调函数原型: void callback(uint8_t channel, uint8_t state, const char *event_msg)
|
||||
*/
|
||||
void IO_Monitor_SetEventCallback(io_event_callback_t callback)
|
||||
{
|
||||
g_event_callback = callback;
|
||||
DEBUG_LOG("Event callback %s", callback ? "set" : "cleared");
|
||||
}
|
||||
|
||||
@ -34,6 +34,11 @@
|
||||
#include "cmd_parser.h"
|
||||
#include "relay_control.h"
|
||||
|
||||
/* 多通信接口统一指令处理系统模块 */
|
||||
#include "multi_uart_router.h"
|
||||
#include "cmd_router.h"
|
||||
#include "debug_log.h"
|
||||
|
||||
#if (RF433_MODE == RF433_MODE_TX) || (RF433_MODE == RF433_MODE_BOTH)
|
||||
#include "rf433_tx_app.h"
|
||||
#endif
|
||||
@ -62,6 +67,7 @@
|
||||
|
||||
/* USER CODE BEGIN PV */
|
||||
static uint8_t uart2_rx_byte = 0;
|
||||
static uint8_t uart3_rx_byte = 0;
|
||||
/* USER CODE END PV */
|
||||
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
@ -112,9 +118,17 @@ int main(void)
|
||||
CmdParser_Init();
|
||||
Relay_Init();
|
||||
|
||||
/* 初始化多通信接口统一指令处理系统 */
|
||||
MultiUART_Init();
|
||||
CmdRouter_Init();
|
||||
DebugLog_Init();
|
||||
|
||||
/* 启动UART2接收中断 */
|
||||
HAL_UART_Receive_IT(&huart2, &uart2_rx_byte, 1);
|
||||
|
||||
/* 启动UART3接收中断 - RS485接口 */
|
||||
HAL_UART_Receive_IT(&huart3, &uart3_rx_byte, 1);
|
||||
|
||||
/* 初始化RF433模块 - 使用默认配置 */
|
||||
rf433_init(NULL);
|
||||
|
||||
@ -155,6 +169,10 @@ int main(void)
|
||||
IO_Monitor_Task();
|
||||
CmdParser_Task();
|
||||
|
||||
/* 多通信接口统一指令处理系统任务 */
|
||||
MultiUART_Task();
|
||||
CmdRouter_Task();
|
||||
|
||||
#if (RF433_MODE == RF433_MODE_TX) || (RF433_MODE == RF433_MODE_BOTH)
|
||||
/* TX任务 */
|
||||
rf433_tx_app_task();
|
||||
@ -212,7 +230,7 @@ void SystemClock_Config(void)
|
||||
|
||||
/**
|
||||
* @brief UART接收完成中断回调函数
|
||||
* @note 处理UART1(RF433)和UART2(调试口)的接收数据
|
||||
* @note 处理UART1(RF433)、UART2(调试口)和UART3(RS485)的接收数据
|
||||
* @param huart: UART句柄指针
|
||||
* @retval 无
|
||||
*/
|
||||
@ -220,31 +238,56 @@ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
|
||||
{
|
||||
if (huart->Instance == USART1)
|
||||
{
|
||||
/* 调用RF433模块的UART接收回调 */
|
||||
/* 先保存接收到的字节,避免被rf433_hal回调覆盖 */
|
||||
uint8_t rx_byte = rf433_uart_rx_tmp;
|
||||
|
||||
/* 调用RF433模块的UART接收回调(内部会重启接收) */
|
||||
rf433_hal_uart_rxcplt_callback();
|
||||
|
||||
/* 喂入多UART路由器,支持RF433指令接收 */
|
||||
MultiUART_FeedByte(PORT_UART1, rx_byte);
|
||||
}
|
||||
else if (huart->Instance == USART2)
|
||||
{
|
||||
/* 喂入指令解析器 */
|
||||
/* 喂入指令解析器 - UART2保持原有处理方式 */
|
||||
CmdParser_FeedByte(uart2_rx_byte, HAL_GetTick());
|
||||
|
||||
/* 重新启动接收 */
|
||||
HAL_UART_Receive_IT(&huart2, &uart2_rx_byte, 1);
|
||||
}
|
||||
else if (huart->Instance == USART3)
|
||||
{
|
||||
/* 喂入多UART路由器 - RS485接口 */
|
||||
MultiUART_FeedByte(PORT_UART3, uart3_rx_byte);
|
||||
|
||||
/* 重新启动接收 */
|
||||
HAL_UART_Receive_IT(&huart3, &uart3_rx_byte, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UART发送完成中断回调函数
|
||||
* @note 处理UART2发送完成,触发下一次发送
|
||||
* @note 处理UART1/UART2/UART3发送完成,触发下一次发送
|
||||
* @param huart: UART句柄指针
|
||||
* @retval 无
|
||||
*/
|
||||
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
|
||||
{
|
||||
if (huart->Instance == USART2)
|
||||
if (huart->Instance == USART1)
|
||||
{
|
||||
/* 调用多UART路由器的UART1发送完成回调 */
|
||||
MultiUART_TxCpltCallback(PORT_UART1);
|
||||
}
|
||||
else if (huart->Instance == USART2)
|
||||
{
|
||||
/* 调用UART2打印模块的发送完成回调 */
|
||||
UART2_Print_TxCpltCallback();
|
||||
}
|
||||
else if (huart->Instance == USART3)
|
||||
{
|
||||
/* 调用多UART路由器的UART3发送完成回调 */
|
||||
MultiUART_TxCpltCallback(PORT_UART3);
|
||||
}
|
||||
}
|
||||
|
||||
/* USER CODE END 4 */
|
||||
|
||||
351
Core/Src/multi_uart_router.c
Normal file
351
Core/Src/multi_uart_router.c
Normal file
@ -0,0 +1,351 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file multi_uart_router.c
|
||||
* @brief 多UART统一路由核心模块实现
|
||||
* @author Application Layer
|
||||
* @version 1.0
|
||||
******************************************************************************
|
||||
* @attention
|
||||
* 本模块实现多UART端口的统一管理
|
||||
* 设计依据:多通信接口统一指令处理系统开发计划 第3.1、3.3、3.5节及附录A
|
||||
*
|
||||
* 关键特性:
|
||||
* 1. 中断+环形缓冲区接收机制
|
||||
* 2. 非阻塞发送机制
|
||||
* 3. 端口上下文表管理
|
||||
* 4. 响应路由表
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "multi_uart_router.h"
|
||||
#include "uart2_print.h"
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define DEBUG_MULTI_UART 1
|
||||
|
||||
#if DEBUG_MULTI_UART
|
||||
#define DEBUG_LOG(fmt, ...) UART2_Print_Printf("[MUART] " fmt "\r\n", ##__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_LOG(fmt, ...)
|
||||
#endif
|
||||
|
||||
static uart_port_context_t g_port_ctx[PORT_COUNT];
|
||||
|
||||
static UART_HandleTypeDef *const g_port_uart_map[PORT_COUNT] = {
|
||||
[PORT_UART1] = &huart1,
|
||||
[PORT_UART2] = &huart2,
|
||||
[PORT_UART3] = &huart3,
|
||||
};
|
||||
|
||||
static const char *const g_port_name_map[PORT_COUNT] = {
|
||||
[PORT_UART1] = "UART1",
|
||||
[PORT_UART2] = "UART2",
|
||||
[PORT_UART3] = "UART3",
|
||||
};
|
||||
|
||||
static void rx_ring_init(uart_rx_ring_t *ring)
|
||||
{
|
||||
ring->head = 0;
|
||||
ring->tail = 0;
|
||||
ring->count = 0;
|
||||
ring->overflow_count = 0;
|
||||
}
|
||||
|
||||
static void tx_ring_init(uart_tx_ring_t *ring)
|
||||
{
|
||||
ring->head = 0;
|
||||
ring->tail = 0;
|
||||
ring->count = 0;
|
||||
ring->is_sending = false;
|
||||
ring->overflow_count = 0;
|
||||
}
|
||||
|
||||
static bool rx_ring_push(uart_rx_ring_t *ring, uint8_t byte)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
__disable_irq();
|
||||
if (ring->count >= UART_RX_BUFFER_SIZE) {
|
||||
ring->overflow_count++;
|
||||
success = false;
|
||||
} else {
|
||||
ring->buffer[ring->head] = byte;
|
||||
ring->head = (ring->head + 1) % UART_RX_BUFFER_SIZE;
|
||||
ring->count++;
|
||||
}
|
||||
__enable_irq();
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static uint16_t rx_ring_pop(uart_rx_ring_t *ring, uint8_t *byte)
|
||||
{
|
||||
uint16_t result = 0;
|
||||
|
||||
__disable_irq();
|
||||
if (ring->count > 0) {
|
||||
*byte = ring->buffer[ring->tail];
|
||||
ring->tail = (ring->tail + 1) % UART_RX_BUFFER_SIZE;
|
||||
ring->count--;
|
||||
result = 1;
|
||||
}
|
||||
__enable_irq();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint16_t tx_ring_push(uart_tx_ring_t *ring, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
uint16_t written = 0;
|
||||
|
||||
__disable_irq();
|
||||
for (uint16_t i = 0; i < len; i++) {
|
||||
if (ring->count >= UART_TX_BUFFER_SIZE) {
|
||||
ring->overflow_count++;
|
||||
break;
|
||||
}
|
||||
ring->buffer[ring->head] = data[i];
|
||||
ring->head = (ring->head + 1) % UART_TX_BUFFER_SIZE;
|
||||
ring->count++;
|
||||
written++;
|
||||
}
|
||||
__enable_irq();
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
static void tx_kickoff(port_id_t port_id)
|
||||
{
|
||||
uart_port_context_t *ctx = &g_port_ctx[port_id];
|
||||
uart_tx_ring_t *ring = &ctx->tx_ring;
|
||||
uint8_t byte;
|
||||
bool has_data = false;
|
||||
|
||||
__disable_irq();
|
||||
if (!ring->is_sending && ring->count > 0) {
|
||||
byte = ring->buffer[ring->tail];
|
||||
ring->tail = (ring->tail + 1) % UART_TX_BUFFER_SIZE;
|
||||
ring->count--;
|
||||
ring->is_sending = true;
|
||||
has_data = true;
|
||||
}
|
||||
__enable_irq();
|
||||
|
||||
if (has_data) {
|
||||
HAL_UART_Transmit_IT(ctx->huart, &byte, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiUART_Init(void)
|
||||
{
|
||||
for (port_id_t i = 0; i < PORT_COUNT; i++) {
|
||||
uart_port_context_t *ctx = &g_port_ctx[i];
|
||||
|
||||
ctx->huart = g_port_uart_map[i];
|
||||
ctx->name = g_port_name_map[i];
|
||||
|
||||
rx_ring_init(&ctx->rx_ring);
|
||||
tx_ring_init(&ctx->tx_ring);
|
||||
|
||||
ctx->rx_tmp = 0;
|
||||
ctx->rx_count = 0;
|
||||
ctx->tx_count = 0;
|
||||
ctx->error_count = 0;
|
||||
ctx->initialized = true;
|
||||
}
|
||||
|
||||
DEBUG_LOG("Init OK, %d ports configured", PORT_COUNT);
|
||||
}
|
||||
|
||||
void MultiUART_FeedByte(port_id_t port_id, uint8_t byte)
|
||||
{
|
||||
// DEBUG_LOG("FeedByte: %02X", byte);
|
||||
if (port_id >= PORT_COUNT) {
|
||||
return;
|
||||
}
|
||||
|
||||
uart_port_context_t *ctx = &g_port_ctx[port_id];
|
||||
|
||||
if (!ctx->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rx_ring_push(&ctx->rx_ring, byte)) {
|
||||
ctx->error_count++;
|
||||
DEBUG_LOG("%s RX overflow", ctx->name);
|
||||
}
|
||||
|
||||
ctx->rx_count++;
|
||||
}
|
||||
|
||||
void MultiUART_Task(void)
|
||||
{
|
||||
for (port_id_t i = 0; i < PORT_COUNT; i++) {
|
||||
uart_port_context_t *ctx = &g_port_ctx[i];
|
||||
|
||||
if (!ctx->initialized) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == PORT_UART2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tx_kickoff(i);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiUART_Send(port_id_t port_id, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
if (port_id >= PORT_COUNT || data == NULL || len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (port_id == PORT_UART2) {
|
||||
UART2_Print_Send(data, len);
|
||||
return;
|
||||
}
|
||||
|
||||
uart_port_context_t *ctx = &g_port_ctx[port_id];
|
||||
|
||||
if (!ctx->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t written = tx_ring_push(&ctx->tx_ring, data, len);
|
||||
|
||||
if (written > 0) {
|
||||
ctx->tx_count += written;
|
||||
tx_kickoff(port_id);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiUART_SendString(port_id_t port_id, const char *str)
|
||||
{
|
||||
if (str == NULL) {
|
||||
return;
|
||||
}
|
||||
MultiUART_Send(port_id, (const uint8_t *)str, strlen(str));
|
||||
}
|
||||
|
||||
void MultiUART_SendFmt(port_id_t port_id, const char *fmt, ...)
|
||||
{
|
||||
if (fmt == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[128];
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
int len = vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (len > 0) {
|
||||
if (len >= (int)sizeof(buffer)) {
|
||||
len = sizeof(buffer) - 1;
|
||||
}
|
||||
MultiUART_Send(port_id, (const uint8_t *)buffer, len);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiUART_TxCpltCallback(port_id_t port_id)
|
||||
{
|
||||
if (port_id >= PORT_COUNT) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (port_id == PORT_UART2) {
|
||||
UART2_Print_TxCpltCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
uart_port_context_t *ctx = &g_port_ctx[port_id];
|
||||
uart_tx_ring_t *ring = &ctx->tx_ring;
|
||||
uint8_t byte;
|
||||
bool has_more = false;
|
||||
|
||||
__disable_irq();
|
||||
ring->is_sending = false;
|
||||
|
||||
if (ring->count > 0) {
|
||||
byte = ring->buffer[ring->tail];
|
||||
ring->tail = (ring->tail + 1) % UART_TX_BUFFER_SIZE;
|
||||
ring->count--;
|
||||
ring->is_sending = true;
|
||||
has_more = true;
|
||||
}
|
||||
__enable_irq();
|
||||
|
||||
if (has_more) {
|
||||
HAL_UART_Transmit_IT(ctx->huart, &byte, 1);
|
||||
}
|
||||
}
|
||||
|
||||
const char *MultiUART_GetPortName(port_id_t port_id)
|
||||
{
|
||||
if (port_id >= PORT_COUNT) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return g_port_name_map[port_id];
|
||||
}
|
||||
|
||||
uint16_t MultiUART_GetRxCount(port_id_t port_id)
|
||||
{
|
||||
if (port_id >= PORT_COUNT) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t count;
|
||||
__disable_irq();
|
||||
count = g_port_ctx[port_id].rx_ring.count;
|
||||
__enable_irq();
|
||||
return count;
|
||||
}
|
||||
|
||||
uint16_t MultiUART_ReadByte(port_id_t port_id, uint8_t *byte)
|
||||
{
|
||||
if (port_id >= PORT_COUNT || byte == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uart_rx_ring_t *ring = &g_port_ctx[port_id].rx_ring;
|
||||
|
||||
__disable_irq();
|
||||
if (ring->count == 0) {
|
||||
__enable_irq();
|
||||
return 0;
|
||||
}
|
||||
|
||||
*byte = ring->buffer[ring->tail];
|
||||
ring->tail = (ring->tail + 1) % UART_RX_BUFFER_SIZE;
|
||||
ring->count--;
|
||||
__enable_irq();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t MultiUART_GetTxAvailable(port_id_t port_id)
|
||||
{
|
||||
if (port_id >= PORT_COUNT) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t available;
|
||||
__disable_irq();
|
||||
available = UART_TX_BUFFER_SIZE - g_port_ctx[port_id].tx_ring.count;
|
||||
__enable_irq();
|
||||
return available;
|
||||
}
|
||||
|
||||
uint32_t MultiUART_GetOverflowCount(port_id_t port_id)
|
||||
{
|
||||
if (port_id >= PORT_COUNT) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return g_port_ctx[port_id].rx_ring.overflow_count +
|
||||
g_port_ctx[port_id].tx_ring.overflow_count;
|
||||
}
|
||||
@ -3,17 +3,17 @@
|
||||
* @file relay_control.c
|
||||
* @brief 继电器控制模块实现
|
||||
* @author Application Layer
|
||||
* @version 1.1
|
||||
* @version 2.0
|
||||
******************************************************************************
|
||||
* @attention
|
||||
* 本模块实现继电器的安全控制
|
||||
* 本模块实现单路继电器的安全控制
|
||||
* 关键特性:
|
||||
* 1. 最小切换间隔保护,防止频繁切换损坏继电器
|
||||
* 2. 状态记录,支持诊断
|
||||
* 2. 状态记录
|
||||
* 3. 调试日志输出
|
||||
*
|
||||
* 修订历史:
|
||||
* v1.1 - 修复审查报告中危-9/10:对齐RELAY_COUNT与参数校验逻辑
|
||||
* v2.0 - 精简为单路继电器控制,移除冗余功能
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
@ -29,74 +29,42 @@
|
||||
#define DEBUG_LOG(fmt, ...)
|
||||
#endif
|
||||
|
||||
#define MAX_RELAY_ID 4
|
||||
|
||||
static bool current_states[MAX_RELAY_ID] = {false, false, false, false};
|
||||
static bool current_state = false;
|
||||
static uint32_t last_toggle_tick = 0;
|
||||
static uint32_t toggle_count = 0;
|
||||
|
||||
void Relay_Init(void)
|
||||
{
|
||||
HAL_GPIO_WritePin(RL_Control_GPIO_Port, RL_Control_Pin, GPIO_PIN_RESET);
|
||||
|
||||
for (int i = 0; i < MAX_RELAY_ID; i++) {
|
||||
current_states[i] = false;
|
||||
}
|
||||
current_state = false;
|
||||
last_toggle_tick = 0;
|
||||
toggle_count = 0;
|
||||
|
||||
DEBUG_LOG("Init OK, state=OFF");
|
||||
}
|
||||
|
||||
void Relay_SetState(uint8_t relay_id, bool state)
|
||||
void Relay_SetState(bool state)
|
||||
{
|
||||
if (relay_id < 1 || relay_id > MAX_RELAY_ID) {
|
||||
DEBUG_LOG("Invalid relay ID: %d", relay_id);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t current_tick = HAL_GetTick();
|
||||
|
||||
if (current_tick - last_toggle_tick < RELAY_MIN_INTERVAL) {
|
||||
DEBUG_LOG("Toggle too fast, ignored");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t idx = relay_id - 1;
|
||||
|
||||
if (current_states[idx] == state) {
|
||||
if (current_state == state) {
|
||||
DEBUG_LOG("State unchanged: %s", state ? "ON" : "OFF");
|
||||
return;
|
||||
}
|
||||
|
||||
if (relay_id == 1) {
|
||||
HAL_GPIO_WritePin(RL_Control_GPIO_Port, RL_Control_Pin,
|
||||
state ? GPIO_PIN_SET : GPIO_PIN_RESET);
|
||||
}
|
||||
|
||||
current_states[idx] = state;
|
||||
current_state = state;
|
||||
last_toggle_tick = current_tick;
|
||||
toggle_count++;
|
||||
|
||||
DEBUG_LOG("Relay %d -> %s (count=%lu)", relay_id, state ? "ON" : "OFF", toggle_count);
|
||||
DEBUG_LOG("Relay -> %s", state ? "ON" : "OFF");
|
||||
}
|
||||
|
||||
bool Relay_GetState(uint8_t relay_id)
|
||||
bool Relay_GetState(void)
|
||||
{
|
||||
if (relay_id < 1 || relay_id > MAX_RELAY_ID) {
|
||||
return false;
|
||||
}
|
||||
return current_states[relay_id - 1];
|
||||
}
|
||||
|
||||
void Relay_Toggle(uint8_t relay_id)
|
||||
{
|
||||
if (relay_id < 1 || relay_id > MAX_RELAY_ID) {
|
||||
return;
|
||||
}
|
||||
Relay_SetState(relay_id, !current_states[relay_id - 1]);
|
||||
}
|
||||
|
||||
uint32_t Relay_GetToggleCount(void)
|
||||
{
|
||||
return toggle_count;
|
||||
return current_state;
|
||||
}
|
||||
|
||||
@ -22,38 +22,125 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/*==============================================================================
|
||||
* 调试宏定义
|
||||
*============================================================================*/
|
||||
/* DEBUG_PRINT_ENABLED: 调试日志开关,置1时启用模块自测日志输出 */
|
||||
#define DEBUG_PRINT_ENABLED 1
|
||||
|
||||
#if DEBUG_PRINT_ENABLED
|
||||
/* 调试日志宏,带模块前缀"[UART2]"方便过滤日志 */
|
||||
#define DEBUG_LOG(fmt, ...) UART2_Print_Printf("[UART2] " fmt "\r\n", ##__VA_ARGS__)
|
||||
#else
|
||||
/* 禁用时为空宏,避免无用代码生成 */
|
||||
#define DEBUG_LOG(fmt, ...)
|
||||
#endif
|
||||
|
||||
/*==============================================================================
|
||||
* 数据结构定义
|
||||
*============================================================================*/
|
||||
/**
|
||||
* @brief 环形发送缓冲区数据结构
|
||||
* @note 采用Ring Buffer(环形缓冲区)设计,实现FIFO队列管理
|
||||
*
|
||||
* 设计目的:
|
||||
* 解决UART发送与CPU执行速度不匹配的问题。当UART正在发送数据时,
|
||||
* 后续数据先存入缓冲区,待发送完成后再取出发送,实现异步非阻塞打印。
|
||||
* 环形缓冲区相比线性缓冲区的优势是无需数据搬移,head和tail指针
|
||||
* 循环递增,到达末尾后自动回绕到开头。
|
||||
*
|
||||
* 字段说明:
|
||||
* - buffer: 存放数据的字节数组,大小由UART2_TX_BUFFER_SIZE定义
|
||||
* - head: 写入位置指针,指向下一个待写入位置(生产者指针)
|
||||
* - tail: 读取位置指针,指向下一个待读取位置(消费者指针)
|
||||
* - count: 当前缓冲区中有效数据字节数
|
||||
* - is_sending: 发送忙标志,表示UART硬件是否正在发送数据
|
||||
* - overflow_count: 溢出错误计数,缓冲区满时丢弃数据的次数
|
||||
*
|
||||
* 索引计算规则:
|
||||
* head和tail均采用模运算实现回绕: new_index = (old_index + 1) % buffer_size
|
||||
* 这确保了指针在达到缓冲区末尾时自动回到开头,形成"环形"效果
|
||||
*
|
||||
* 线程安全说明:
|
||||
* 所有修改共享资源的代码段均使用__disable_irq/__enable_irq暂时禁用中断,
|
||||
* 防止在临界区内被ISR打断导致数据竞争(race condition)
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t buffer[UART2_TX_BUFFER_SIZE];
|
||||
volatile uint16_t head;
|
||||
volatile uint16_t tail;
|
||||
volatile uint16_t count;
|
||||
volatile bool is_sending;
|
||||
volatile uint16_t overflow_count;
|
||||
uint8_t buffer[UART2_TX_BUFFER_SIZE]; /**< 发送数据缓冲区,静态分配避免动态内存 */
|
||||
volatile uint16_t head; /**< 写指针,下一个数据写入位置 */
|
||||
volatile uint16_t tail; /**< 读指针,下一个数据读取位置 */
|
||||
volatile uint16_t count; /**< 缓冲区中有效数据字节计数 */
|
||||
volatile bool is_sending; /**< UART发送忙标志,防止重复启动发送 */
|
||||
volatile uint16_t overflow_count; /**< 溢出错误计数,统计因缓冲区满丢弃的数据 */
|
||||
} ring_buffer_t;
|
||||
|
||||
/*==============================================================================
|
||||
* 全局变量定义
|
||||
*============================================================================*/
|
||||
/**
|
||||
* @brief 环形发送缓冲区实例
|
||||
* @note static修饰确保仅本文件内可访问,初始化为全零
|
||||
*/
|
||||
static ring_buffer_t tx_ring = {0};
|
||||
|
||||
/*==============================================================================
|
||||
* 公共函数实现
|
||||
*============================================================================*/
|
||||
/**
|
||||
* @brief UART2打印模块初始化
|
||||
* @note 在系统启动或UART外设初始化后调用,重置环形缓冲区状态
|
||||
*
|
||||
* @param 无
|
||||
* @return 无
|
||||
*
|
||||
* 功能说明:
|
||||
* 1. 重置所有指针(head/tail/count)为初始状态
|
||||
* 2. 清除发送忙标志
|
||||
* 3. 清零溢出错误计数器
|
||||
*
|
||||
* 初始化安全:
|
||||
* 此函数应在UART硬件初始化完成之后调用,确保huart2已正确配置
|
||||
*/
|
||||
void UART2_Print_Init(void)
|
||||
{
|
||||
/* 重置环形缓冲区各状态变量 */
|
||||
tx_ring.head = 0;
|
||||
tx_ring.tail = 0;
|
||||
tx_ring.count = 0;
|
||||
tx_ring.is_sending = false;
|
||||
tx_ring.overflow_count = 0;
|
||||
|
||||
/* 输出模块初始化完成日志 */
|
||||
DEBUG_LOG("Init OK, buffer size: %d", UART2_TX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送数据到UART2(核心写入函数)
|
||||
* @note 将数据写入环形缓冲区,若UART空闲则自动启动首次发送
|
||||
* 此函数是线程安全的,可从ISR或主线程调用
|
||||
*
|
||||
* @param data: 待发送数据缓冲区的指针(输入)
|
||||
* @param len: 待发送数据字节数(输入)
|
||||
* @return 无返回值
|
||||
*
|
||||
* 算法说明 - 生产者逻辑:
|
||||
* 1. 遍历待发送数据的每个字节
|
||||
* 2. 检查缓冲区是否有空间(count < buffer_size)
|
||||
* 3. 如有空间,将数据写入buffer[head],head递增并回绕
|
||||
* 4. 如缓冲区满,放弃剩余数据并增加overflow_count
|
||||
*
|
||||
* 发送触发机制:
|
||||
* - 如果缓冲区有数据且UART当前空闲(is_sending=false),
|
||||
* 设置is_sending=true并立即触发首次发送(Kickoff)
|
||||
* - 后续发送由TxCpltCallback中断回调驱动,形成连续发送直到缓冲区清空
|
||||
*
|
||||
* 中断安全:
|
||||
* - 使用__disable_irq/__enable_irq保护临界区
|
||||
* - 防止在检查count和写入buffer之间被ISR打断
|
||||
*/
|
||||
void UART2_Print_Send(const uint8_t *data, uint16_t len)
|
||||
{
|
||||
/* 参数合法性检查,防止空指针或零长度 */
|
||||
if (len == 0 || data == NULL) {
|
||||
return;
|
||||
}
|
||||
@ -61,56 +148,117 @@ void UART2_Print_Send(const uint8_t *data, uint16_t len)
|
||||
uint16_t written = 0;
|
||||
bool needs_kickoff = false;
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 临界区:禁用中断,保护共享缓冲区的写入操作
|
||||
*----------------------------------------------------------*/
|
||||
__disable_irq();
|
||||
for (uint16_t i = 0; i < len; i++) {
|
||||
/* 检查缓冲区是否有空间 */
|
||||
if (tx_ring.count >= UART2_TX_BUFFER_SIZE) {
|
||||
/* 缓冲区已满,丢弃数据并计数 */
|
||||
tx_ring.overflow_count++;
|
||||
break;
|
||||
}
|
||||
|
||||
/* 将数据写入环形缓冲区,更新写指针(模缓冲区大小回绕) */
|
||||
tx_ring.buffer[tx_ring.head] = data[i];
|
||||
tx_ring.head = (tx_ring.head + 1) % UART2_TX_BUFFER_SIZE;
|
||||
tx_ring.count++;
|
||||
written++;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 判断是否需要触发首次发送
|
||||
* 条件:成功写入数据 AND UART当前处于空闲状态
|
||||
*----------------------------------------------------------*/
|
||||
if (written > 0 && !tx_ring.is_sending) {
|
||||
tx_ring.is_sending = true;
|
||||
needs_kickoff = true;
|
||||
}
|
||||
__enable_irq();
|
||||
__enable_irq(); /* 退出临界区 */
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 启动首次发送(Kickoff)
|
||||
* 从缓冲区取出第一个字节通过DMA/中断方式发送
|
||||
*----------------------------------------------------------*/
|
||||
if (needs_kickoff) {
|
||||
uint8_t byte;
|
||||
/* 再次禁用中断以读取尾指针(消费者操作) */
|
||||
__disable_irq();
|
||||
byte = tx_ring.buffer[tx_ring.tail];
|
||||
__enable_irq();
|
||||
/* 启动UART中断发送,发送完成后会触发TxCpltCallback */
|
||||
HAL_UART_Transmit_IT(&huart2, &byte, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送字符串到UART2
|
||||
* @note 封装UART2_Print_Send,自动计算字符串长度
|
||||
*
|
||||
* @param str: 待发送的以'\0'结尾的字符串指针(输入)
|
||||
* @return 无返回值
|
||||
*
|
||||
* 使用说明:
|
||||
* - str必须为有效指针且以'\0'结尾
|
||||
* - strlen计算长度时不包括终止符,所以实际发送的也不包括
|
||||
*/
|
||||
void UART2_Print_String(const char *str)
|
||||
{
|
||||
/* 空指针保护 */
|
||||
if (str == NULL) {
|
||||
return;
|
||||
}
|
||||
/* 使用strlen获取字符串长度并发送 */
|
||||
UART2_Print_Send((const uint8_t *)str, strlen(str));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 格式化打印到UART2
|
||||
* @note 仿printf风格,支持可变参数格式化输出
|
||||
*
|
||||
* @param fmt: 格式化字符串,与printf语法兼容(输入)
|
||||
* @param ...: 可变参数列表,对应格式化字符串中的占位符(输入)
|
||||
* @return 无返回值
|
||||
*
|
||||
* 实现说明:
|
||||
* 1. 使用va_list/va_start/va_end处理可变参数
|
||||
* 2. vsnprintf将格式化参数写入临时缓冲区(128字节)
|
||||
* 3. 将格式化后的字符串通过UART2_Print_Send发送
|
||||
*
|
||||
* 缓冲区限制:
|
||||
* - 最大格式化输出为127字节(128-1留作字符串终止符)
|
||||
* - 超出部分会被截断,不报错
|
||||
*
|
||||
* 线程安全:
|
||||
* 此函数本身是中断安全的,但内部调用UART2_Print_Send,
|
||||
* 多线程并发调用时输出可能交叉
|
||||
*/
|
||||
void UART2_Print_Printf(const char *fmt, ...)
|
||||
{
|
||||
/* 格式化字符串合法性检查 */
|
||||
if (fmt == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[128];
|
||||
va_list args;
|
||||
char buffer[128]; /* 临时格式化缓冲区,大小固定 */
|
||||
va_list args; /* 可变参数列表变量 */
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 可变参数处理:初始化va_list并执行格式化
|
||||
*----------------------------------------------------------*/
|
||||
va_start(args, fmt);
|
||||
int len = vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 发送格式化后的字符串
|
||||
* vsnprintf返回值len:
|
||||
* - >=0: 成功格式化,需要发送的字符数(不含终止符)
|
||||
* - <0: 格式化失败,不发送任何数据
|
||||
*----------------------------------------------------------*/
|
||||
if (len >= 0) {
|
||||
/* 边界检查:若len超过缓冲区实际容量则截断 */
|
||||
if (len >= (int)sizeof(buffer)) {
|
||||
len = sizeof(buffer) - 1;
|
||||
}
|
||||
@ -118,51 +266,127 @@ void UART2_Print_Printf(const char *fmt, ...)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UART2打印任务(轮询模式驱动)
|
||||
* @note 在主循环或定时器中调用,驱动环形缓冲区数据发送
|
||||
* 与TxCpltCallback配合形成轮询+中断混合发送模式
|
||||
*
|
||||
* @param 无
|
||||
* @return 无
|
||||
*
|
||||
* 工作原理:
|
||||
* 正常发送流程依赖TxCpltCallback中断回调驱动。但若某些平台
|
||||
* 或情况下中断被屏蔽,此函数作为备用发送机制,从缓冲区取出
|
||||
* 数据启动新的发送。
|
||||
*
|
||||
* 调用时机:
|
||||
* 建议在主循环中周期性调用(如每次循环或10ms定时),
|
||||
* 配合UART空闲中断或DMA完成中断实现高效发送
|
||||
*
|
||||
* 中断安全:
|
||||
* - 使用__disable_irq/__enable_irq保护临界区
|
||||
*/
|
||||
void UART2_Print_Task(void)
|
||||
{
|
||||
uint8_t byte;
|
||||
uint16_t current_tail;
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 临界区:检查是否可以发送
|
||||
*----------------------------------------------------------*/
|
||||
__disable_irq();
|
||||
/* 条件:不正在发送 且 缓冲区有数据 */
|
||||
if (tx_ring.is_sending || tx_ring.count == 0) {
|
||||
__enable_irq();
|
||||
return;
|
||||
return; /* 条件不满足,不执行发送 */
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 从缓冲区取出数据,更新读指针
|
||||
*----------------------------------------------------------*/
|
||||
current_tail = tx_ring.tail;
|
||||
tx_ring.tail = (tx_ring.tail + 1) % UART2_TX_BUFFER_SIZE;
|
||||
tx_ring.tail = (tx_ring.tail + 1) % UART2_TX_BUFFER_SIZE; /* 环形回绕 */
|
||||
tx_ring.count--;
|
||||
tx_ring.is_sending = true;
|
||||
tx_ring.is_sending = true; /* 标记为发送中,防止重复启动 */
|
||||
byte = tx_ring.buffer[current_tail];
|
||||
__enable_irq();
|
||||
__enable_irq(); /* 退出临界区 */
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 启动UART中断发送
|
||||
*----------------------------------------------------------*/
|
||||
HAL_UART_Transmit_IT(&huart2, &byte, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UART发送完成回调函数
|
||||
* @note 应在UART TX完成中断中调用,负责驱动环形缓冲区连续发送
|
||||
* 此函数是发送流程的核心驱动引擎
|
||||
*
|
||||
* @param 无
|
||||
* @return 无
|
||||
*
|
||||
* 工作原理 - 消费者逻辑:
|
||||
* 1. 清除is_sending标志,表示UART硬件已空闲
|
||||
* 2. 检查环形缓冲区是否还有待发送数据
|
||||
* 3. 如有,取出下一字节并启动新的发送
|
||||
* 4. 重复上述过程直到缓冲区清空
|
||||
*
|
||||
* 中断安全:
|
||||
* - 使用__disable_irq/__enable_irq保护共享数据访问
|
||||
* - 在中断上下文调用,必须确保操作原子性
|
||||
*
|
||||
* 调用约定:
|
||||
* 此函数需要与HAL库中断处理正确关联,通常在
|
||||
* HAL_UART_TxCpltCallback中断回调中调用
|
||||
*/
|
||||
void UART2_Print_TxCpltCallback(void)
|
||||
{
|
||||
uint8_t byte;
|
||||
uint16_t current_tail;
|
||||
bool has_more = false;
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 第一步:标记UART为空闲状态
|
||||
*----------------------------------------------------------*/
|
||||
__disable_irq();
|
||||
tx_ring.is_sending = false;
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 第二步:检查缓冲区是否有更多数据待发送
|
||||
*----------------------------------------------------------*/
|
||||
if (tx_ring.count > 0) {
|
||||
/* 取出下一字节,更新读指针(环形回绕) */
|
||||
current_tail = tx_ring.tail;
|
||||
tx_ring.tail = (tx_ring.tail + 1) % UART2_TX_BUFFER_SIZE;
|
||||
tx_ring.count--;
|
||||
tx_ring.is_sending = true;
|
||||
tx_ring.is_sending = true; /* 立即标记为发送中 */
|
||||
byte = tx_ring.buffer[current_tail];
|
||||
has_more = true;
|
||||
has_more = true; /* 标记有待发送数据 */
|
||||
}
|
||||
__enable_irq();
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* 第三步:如有待发送数据,立即启动下一次发送
|
||||
*----------------------------------------------------------*/
|
||||
if (has_more) {
|
||||
HAL_UART_Transmit_IT(&huart2, &byte, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查询发送模块忙状态
|
||||
* @note 用于判断是否有数据正在发送或缓冲区中是否有待发数据
|
||||
*
|
||||
* @param 无
|
||||
* @return bool: true=忙(正在发送或缓冲区有数据),false=空闲
|
||||
*
|
||||
* 使用场景:
|
||||
* - 在进入低功耗模式前检查是否有数据待发送
|
||||
* - 在系统关机前确认所有调试日志已发送完毕
|
||||
*
|
||||
* 中断安全:
|
||||
* - 使用临界区保护,确保检查到返回之间数据不被修改
|
||||
*/
|
||||
bool UART2_Print_IsBusy(void)
|
||||
{
|
||||
bool busy;
|
||||
@ -172,6 +396,22 @@ bool UART2_Print_IsBusy(void)
|
||||
return busy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查询发送缓冲区可用空间
|
||||
* @note 返回环形缓冲区当前剩余可写入的空间大小
|
||||
*
|
||||
* @param 无
|
||||
* @return uint16_t: 可用字节数
|
||||
*
|
||||
* 计算公式:available = buffer_size - count
|
||||
*
|
||||
* 使用场景:
|
||||
* - 在发送大数据块前检查缓冲区是否足够容纳
|
||||
* - 实现流量控制逻辑
|
||||
*
|
||||
* 中断安全:
|
||||
* - 使用临界区保护
|
||||
*/
|
||||
uint16_t UART2_Print_Available(void)
|
||||
{
|
||||
uint16_t available;
|
||||
@ -181,32 +421,64 @@ uint16_t UART2_Print_Available(void)
|
||||
return available;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取溢出错误计数
|
||||
* @note 统计因缓冲区满导致数据丢弃的次数,用于诊断
|
||||
*
|
||||
* @param 无
|
||||
* @return uint16_t: 溢出错误累计次数
|
||||
*
|
||||
* 使用说明:
|
||||
* - 此计数器仅在缓冲区满时递增
|
||||
* - 若计数持续增长,说明UART发送速度跟不上数据产生速度
|
||||
* - 可通过增大UART2_TX_BUFFER_SIZE或降低打印频率解决
|
||||
*/
|
||||
uint16_t UART2_Print_GetOverflowCount(void)
|
||||
{
|
||||
return tx_ring.overflow_count;
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 标准库printf重定向实现
|
||||
*============================================================================*/
|
||||
/**
|
||||
* @brief printf重定向函数 (Keil MDK)
|
||||
* @note 重定向标准库printf到UART2
|
||||
* @param ch: 待发送字符
|
||||
* @param f: 文件指针(未使用)
|
||||
* @retval 发送的字符
|
||||
* @brief printf重定向函数 (Keil MDK编译器)
|
||||
* @note 将标准库printf输出重定向到UART2
|
||||
*
|
||||
* @param ch: 待发送字符(输入)
|
||||
* @param f: 文件指针(未使用,Keil MDK参数要求)
|
||||
* @return int: 发送的字符
|
||||
*
|
||||
* 编译器条件编译:
|
||||
* 此函数仅在__CC_ARM(或__ARMCC_VERSION)定义时编译,
|
||||
* 即Keil MDK-ARM/ARMCC编译器环境下生效
|
||||
*
|
||||
* 实现说明:
|
||||
* 标准库printf最终会调用fputc输出每个字符,
|
||||
* 此处重定向到UART2_Print_Send实现串口打印
|
||||
*/
|
||||
#if defined(__CC_ARM) || defined(__ARMCC_VERSION)
|
||||
int fputc(int ch, FILE *f)
|
||||
{
|
||||
(void)f;
|
||||
(void)f; /* 未使用参数,避免编译器警告 */
|
||||
UART2_Print_Send((uint8_t *)&ch, 1);
|
||||
return ch;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief printf重定向函数 (GCC)
|
||||
* @note 重定向标准库printf到UART2
|
||||
* @param ch: 待发送字符
|
||||
* @retval 发送的字符
|
||||
* @brief printf重定向函数 (GCC编译器 - 单字符版本)
|
||||
* @note 将标准库printf输出重定向到UART2
|
||||
*
|
||||
* @param ch: 待发送字符(输入)
|
||||
* @return int: 发送的字符
|
||||
*
|
||||
* 编译器条件编译:
|
||||
* 此函数仅在__GNUC__定义时编译,即GCC/Clang编译器环境下生效
|
||||
*
|
||||
* 实现说明:
|
||||
* 新一代ARM GCC使用__io_putchar作为printf输出目标,
|
||||
* 此处将其重定向到UART2_Print_Send
|
||||
*/
|
||||
#if defined(__GNUC__)
|
||||
int __io_putchar(int ch)
|
||||
@ -215,9 +487,22 @@ int __io_putchar(int ch)
|
||||
return ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief write系统调用重定向 (GCC编译器)
|
||||
* @note 将文件系统write调用重定向到UART2
|
||||
*
|
||||
* @param file: 文件描述符(未使用,仅为兼容标准接口)
|
||||
* @param ptr: 数据缓冲区指针(输入)
|
||||
* @param len: 数据长度(输入)
|
||||
* @return int: 已发送的字节数
|
||||
*
|
||||
* 实现说明:
|
||||
* 有些GCC配置下printf会调用_write而非__io_putchar,
|
||||
* 此函数提供完整的write接口兼容
|
||||
*/
|
||||
int _write(int file, char *ptr, int len)
|
||||
{
|
||||
(void)file;
|
||||
(void)file; /* 忽略文件描述符,只处理标准输出 */
|
||||
UART2_Print_Send((uint8_t *)ptr, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ extern "C" {
|
||||
#define RF433_MODE_BOTH 3
|
||||
|
||||
#ifndef RF433_MODE
|
||||
#define RF433_MODE RF433_MODE_TX
|
||||
#define RF433_MODE RF433_MODE_BOTH
|
||||
#endif
|
||||
|
||||
/* ============================================================================
|
||||
|
||||
@ -363,7 +363,6 @@ rf433_error_t rf433_transmit(uint8_t *buffer, uint16_t length)
|
||||
{
|
||||
return RF433_ERROR_TIMEOUT;
|
||||
}
|
||||
printf("Sent: %s", (char*)buffer); // DEBUG_LOG未定义,使用printf替代
|
||||
return RF433_OK;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -405,6 +405,42 @@
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>2</GroupNumber>
|
||||
<FileNumber>15</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>..\Core\Src\cmd_router.c</PathWithFileName>
|
||||
<FilenameWithoutPath>cmd_router.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>2</GroupNumber>
|
||||
<FileNumber>16</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>..\Core\Src\debug_log.c</PathWithFileName>
|
||||
<FilenameWithoutPath>debug_log.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>2</GroupNumber>
|
||||
<FileNumber>17</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>..\Core\Src\multi_uart_router.c</PathWithFileName>
|
||||
<FilenameWithoutPath>multi_uart_router.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
</Group>
|
||||
|
||||
<Group>
|
||||
@ -415,7 +451,7 @@
|
||||
<RteFlg>0</RteFlg>
|
||||
<File>
|
||||
<GroupNumber>3</GroupNumber>
|
||||
<FileNumber>15</FileNumber>
|
||||
<FileNumber>18</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -427,7 +463,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>3</GroupNumber>
|
||||
<FileNumber>16</FileNumber>
|
||||
<FileNumber>19</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -439,7 +475,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>3</GroupNumber>
|
||||
<FileNumber>17</FileNumber>
|
||||
<FileNumber>20</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -451,7 +487,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>3</GroupNumber>
|
||||
<FileNumber>18</FileNumber>
|
||||
<FileNumber>21</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -463,7 +499,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>3</GroupNumber>
|
||||
<FileNumber>19</FileNumber>
|
||||
<FileNumber>22</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -475,7 +511,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>3</GroupNumber>
|
||||
<FileNumber>20</FileNumber>
|
||||
<FileNumber>23</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -487,7 +523,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>3</GroupNumber>
|
||||
<FileNumber>21</FileNumber>
|
||||
<FileNumber>24</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -499,7 +535,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>3</GroupNumber>
|
||||
<FileNumber>22</FileNumber>
|
||||
<FileNumber>25</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -511,7 +547,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>3</GroupNumber>
|
||||
<FileNumber>23</FileNumber>
|
||||
<FileNumber>26</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -523,7 +559,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>3</GroupNumber>
|
||||
<FileNumber>24</FileNumber>
|
||||
<FileNumber>27</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -535,7 +571,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>3</GroupNumber>
|
||||
<FileNumber>25</FileNumber>
|
||||
<FileNumber>28</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -547,7 +583,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>3</GroupNumber>
|
||||
<FileNumber>26</FileNumber>
|
||||
<FileNumber>29</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -559,7 +595,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>3</GroupNumber>
|
||||
<FileNumber>27</FileNumber>
|
||||
<FileNumber>30</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -571,7 +607,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>3</GroupNumber>
|
||||
<FileNumber>28</FileNumber>
|
||||
<FileNumber>31</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -583,7 +619,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>3</GroupNumber>
|
||||
<FileNumber>29</FileNumber>
|
||||
<FileNumber>32</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -603,7 +639,7 @@
|
||||
<RteFlg>0</RteFlg>
|
||||
<File>
|
||||
<GroupNumber>4</GroupNumber>
|
||||
<FileNumber>30</FileNumber>
|
||||
<FileNumber>33</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -623,7 +659,7 @@
|
||||
<RteFlg>0</RteFlg>
|
||||
<File>
|
||||
<GroupNumber>5</GroupNumber>
|
||||
<FileNumber>31</FileNumber>
|
||||
<FileNumber>34</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -635,7 +671,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>5</GroupNumber>
|
||||
<FileNumber>32</FileNumber>
|
||||
<FileNumber>35</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -647,7 +683,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>5</GroupNumber>
|
||||
<FileNumber>33</FileNumber>
|
||||
<FileNumber>36</FileNumber>
|
||||
<FileType>5</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -659,7 +695,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>5</GroupNumber>
|
||||
<FileNumber>34</FileNumber>
|
||||
<FileNumber>37</FileNumber>
|
||||
<FileType>5</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
@ -671,7 +707,7 @@
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>5</GroupNumber>
|
||||
<FileNumber>35</FileNumber>
|
||||
<FileNumber>38</FileNumber>
|
||||
<FileType>5</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
|
||||
@ -510,6 +510,21 @@
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\Core\Src\uart2_print.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>cmd_router.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\Core\Src\cmd_router.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>debug_log.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\Core\Src\debug_log.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>multi_uart_router.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\Core\Src\multi_uart_router.c</FilePath>
|
||||
</File>
|
||||
</Files>
|
||||
</Group>
|
||||
<Group>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
BIN
MDK-ARM/project/cmd_router.crf
Normal file
BIN
MDK-ARM/project/cmd_router.crf
Normal file
Binary file not shown.
39
MDK-ARM/project/cmd_router.d
Normal file
39
MDK-ARM/project/cmd_router.d
Normal file
@ -0,0 +1,39 @@
|
||||
project\cmd_router.o: ..\Core\Src\cmd_router.c
|
||||
project\cmd_router.o: ../Core/Inc/cmd_router.h
|
||||
project\cmd_router.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdint.h
|
||||
project\cmd_router.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdbool.h
|
||||
project\cmd_router.o: ../Core/Inc/multi_uart_router.h
|
||||
project\cmd_router.o: ../Core/Inc/usart.h
|
||||
project\cmd_router.o: ../Core/Inc/main.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal.h
|
||||
project\cmd_router.o: ../Core/Inc/stm32f1xx_hal_conf.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_def.h
|
||||
project\cmd_router.o: ../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f1xx.h
|
||||
project\cmd_router.o: ../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f103xb.h
|
||||
project\cmd_router.o: ../Drivers/CMSIS/Include/core_cm3.h
|
||||
project\cmd_router.o: ../Drivers/CMSIS/Include/cmsis_version.h
|
||||
project\cmd_router.o: ../Drivers/CMSIS/Include/cmsis_compiler.h
|
||||
project\cmd_router.o: ../Drivers/CMSIS/Include/cmsis_armcc.h
|
||||
project\cmd_router.o: ../Drivers/CMSIS/Device/ST/STM32F1xx/Include/system_stm32f1xx.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h
|
||||
project\cmd_router.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stddef.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc_ex.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio_ex.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_exti.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma_ex.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_cortex.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_pwr.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_spi.h
|
||||
project\cmd_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_uart.h
|
||||
project\cmd_router.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdio.h
|
||||
project\cmd_router.o: ../Core/Inc/cmd_parser.h
|
||||
project\cmd_router.o: ../Core/Inc/uart2_print.h
|
||||
project\cmd_router.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdarg.h
|
||||
project\cmd_router.o: ../Core/Inc/debug_log.h
|
||||
project\cmd_router.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\string.h
|
||||
BIN
MDK-ARM/project/cmd_router.o
Normal file
BIN
MDK-ARM/project/cmd_router.o
Normal file
Binary file not shown.
BIN
MDK-ARM/project/debug_log.crf
Normal file
BIN
MDK-ARM/project/debug_log.crf
Normal file
Binary file not shown.
8
MDK-ARM/project/debug_log.d
Normal file
8
MDK-ARM/project/debug_log.d
Normal file
@ -0,0 +1,8 @@
|
||||
project\debug_log.o: ..\Core\Src\debug_log.c
|
||||
project\debug_log.o: ../Core/Inc/debug_log.h
|
||||
project\debug_log.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdint.h
|
||||
project\debug_log.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdbool.h
|
||||
project\debug_log.o: ../Core/Inc/uart2_print.h
|
||||
project\debug_log.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdarg.h
|
||||
project\debug_log.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\string.h
|
||||
project\debug_log.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdio.h
|
||||
BIN
MDK-ARM/project/debug_log.o
Normal file
BIN
MDK-ARM/project/debug_log.o
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -40,4 +40,8 @@ project\main.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdarg.h
|
||||
project\main.o: ../Core/Inc/io_monitor.h
|
||||
project\main.o: ../Core/Inc/cmd_parser.h
|
||||
project\main.o: ../Core/Inc/relay_control.h
|
||||
project\main.o: ../Core/Inc/multi_uart_router.h
|
||||
project\main.o: ../Core/Inc/cmd_router.h
|
||||
project\main.o: ../Core/Inc/debug_log.h
|
||||
project\main.o: ../Core/Inc/rf433_tx_app.h
|
||||
project\main.o: ../Core/Inc/rf433_rx_app.h
|
||||
|
||||
Binary file not shown.
BIN
MDK-ARM/project/multi_uart_router.crf
Normal file
BIN
MDK-ARM/project/multi_uart_router.crf
Normal file
Binary file not shown.
36
MDK-ARM/project/multi_uart_router.d
Normal file
36
MDK-ARM/project/multi_uart_router.d
Normal file
@ -0,0 +1,36 @@
|
||||
project\multi_uart_router.o: ..\Core\Src\multi_uart_router.c
|
||||
project\multi_uart_router.o: ../Core/Inc/multi_uart_router.h
|
||||
project\multi_uart_router.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdint.h
|
||||
project\multi_uart_router.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdbool.h
|
||||
project\multi_uart_router.o: ../Core/Inc/usart.h
|
||||
project\multi_uart_router.o: ../Core/Inc/main.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal.h
|
||||
project\multi_uart_router.o: ../Core/Inc/stm32f1xx_hal_conf.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_def.h
|
||||
project\multi_uart_router.o: ../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f1xx.h
|
||||
project\multi_uart_router.o: ../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f103xb.h
|
||||
project\multi_uart_router.o: ../Drivers/CMSIS/Include/core_cm3.h
|
||||
project\multi_uart_router.o: ../Drivers/CMSIS/Include/cmsis_version.h
|
||||
project\multi_uart_router.o: ../Drivers/CMSIS/Include/cmsis_compiler.h
|
||||
project\multi_uart_router.o: ../Drivers/CMSIS/Include/cmsis_armcc.h
|
||||
project\multi_uart_router.o: ../Drivers/CMSIS/Device/ST/STM32F1xx/Include/system_stm32f1xx.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h
|
||||
project\multi_uart_router.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stddef.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc_ex.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio_ex.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_exti.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma_ex.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_cortex.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_pwr.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_spi.h
|
||||
project\multi_uart_router.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_uart.h
|
||||
project\multi_uart_router.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdio.h
|
||||
project\multi_uart_router.o: ../Core/Inc/uart2_print.h
|
||||
project\multi_uart_router.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdarg.h
|
||||
project\multi_uart_router.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\string.h
|
||||
BIN
MDK-ARM/project/multi_uart_router.o
Normal file
BIN
MDK-ARM/project/multi_uart_router.o
Normal file
Binary file not shown.
Binary file not shown.
@ -22,40 +22,68 @@ Dialog DLL: TCM.DLL V1.56.6.0
|
||||
|
||||
<h2>Project:</h2>
|
||||
C:\workfile\E32-433\software\E32-433TBH-SC\MDK-ARM\project.uvprojx
|
||||
Project File Date: 03/26/2026
|
||||
Project File Date: 03/27/2026
|
||||
|
||||
<h2>Output:</h2>
|
||||
*** Using Compiler 'V5.06 update 7 (build 960)', folder: 'C:\Keil_v5\ARM\ARMCC\Bin'
|
||||
Rebuild target 'project'
|
||||
assembling startup_stm32f103xb.s...
|
||||
compiling rf433_rx_app.c...
|
||||
compiling main.c...
|
||||
compiling spi.c...
|
||||
compiling gpio.c...
|
||||
compiling cmd_parser.c...
|
||||
compiling systick.c...
|
||||
compiling stm32f1xx_it.c...
|
||||
compiling usart.c...
|
||||
compiling rf433_tx_app.c...
|
||||
..\Core\Src\rf433_tx_app.c(28): warning: #188-D: enumerated type mixed with another type
|
||||
static rf433_tx_app_t g_tx_app = {0};
|
||||
..\Core\Src\rf433_tx_app.c: 1 warning, 0 errors
|
||||
compiling stm32f1xx_hal_gpio_ex.c...
|
||||
compiling relay_control.c...
|
||||
compiling stm32f1xx_hal_msp.c...
|
||||
compiling io_monitor.c...
|
||||
compiling stm32f1xx_hal.c...
|
||||
compiling rf433_rx_app.c...
|
||||
..\Core\Src\rf433_rx_app.c(23): warning: #188-D: enumerated type mixed with another type
|
||||
static rf433_rx_app_t g_rx_app = {0};
|
||||
..\Core\Src\rf433_rx_app.c: 1 warning, 0 errors
|
||||
compiling cmd_parser.c...
|
||||
compiling main.c...
|
||||
compiling spi.c...
|
||||
compiling usart.c...
|
||||
compiling systick.c...
|
||||
compiling debug_log.c...
|
||||
compiling stm32f1xx_it.c...
|
||||
compiling uart2_print.c...
|
||||
compiling stm32f1xx_hal_spi.c...
|
||||
compiling stm32f1xx_hal_pwr.c...
|
||||
compiling cmd_router.c...
|
||||
..\Core\Src\cmd_router.c(115): warning: #188-D: enumerated type mixed with another type
|
||||
for (port_id_t i = 0; i < PORT_COUNT; i++) {
|
||||
..\Core\Src\cmd_router.c(139): warning: #188-D: enumerated type mixed with another type
|
||||
for (port_id_t port_id = 0; port_id < PORT_COUNT; port_id++) {
|
||||
..\Core\Src\cmd_router.c(150): warning: #188-D: enumerated type mixed with another type
|
||||
for (port_id_t port_id = 0; port_id < PORT_COUNT; port_id++) {
|
||||
..\Core\Src\cmd_router.c(206): warning: #188-D: enumerated type mixed with another type
|
||||
for (port_id_t port = 0; port < PORT_COUNT; port++) {
|
||||
..\Core\Src\cmd_router.c(56): warning: #550-D: variable "g_current_parsing_port" was set but never used
|
||||
static uint8_t g_current_parsing_port = PORT_UART2;
|
||||
..\Core\Src\cmd_router.c: 5 warnings, 0 errors
|
||||
compiling io_monitor.c...
|
||||
compiling multi_uart_router.c...
|
||||
..\Core\Src\multi_uart_router.c(143): warning: #188-D: enumerated type mixed with another type
|
||||
for (port_id_t i = 0; i < PORT_COUNT; i++) {
|
||||
..\Core\Src\multi_uart_router.c(185): warning: #188-D: enumerated type mixed with another type
|
||||
for (port_id_t i = 0; i < PORT_COUNT; i++) {
|
||||
..\Core\Src\multi_uart_router.c(83): warning: #177-D: function "rx_ring_pop" was declared but never referenced
|
||||
static uint16_t rx_ring_pop(uart_rx_ring_t *ring, uint8_t *byte)
|
||||
..\Core\Src\multi_uart_router.c: 3 warnings, 0 errors
|
||||
compiling stm32f1xx_hal_msp.c...
|
||||
compiling relay_control.c...
|
||||
compiling stm32f1xx_hal_gpio_ex.c...
|
||||
compiling stm32f1xx_hal_cortex.c...
|
||||
compiling stm32f1xx_hal_gpio.c...
|
||||
compiling stm32f1xx_hal_rcc.c...
|
||||
compiling stm32f1xx_hal_flash.c...
|
||||
compiling stm32f1xx_hal_spi.c...
|
||||
compiling stm32f1xx_hal_rcc_ex.c...
|
||||
compiling stm32f1xx_hal_dma.c...
|
||||
compiling stm32f1xx_hal_gpio.c...
|
||||
compiling stm32f1xx_hal.c...
|
||||
compiling stm32f1xx_hal_rcc.c...
|
||||
compiling stm32f1xx_hal_pwr.c...
|
||||
compiling stm32f1xx_hal_exti.c...
|
||||
compiling stm32f1xx_hal_tim.c...
|
||||
compiling stm32f1xx_hal_tim_ex.c...
|
||||
compiling stm32f1xx_hal_flash_ex.c...
|
||||
compiling stm32f1xx_hal_flash.c...
|
||||
compiling stm32f1xx_hal_uart.c...
|
||||
compiling system_stm32f1xx.c...
|
||||
compiling rf433.c...
|
||||
..\Driver_RF433\Src\rf433.c(209): warning: #188-D: enumerated type mixed with another type
|
||||
ret = rf433_set_config(&rf433_current_config);
|
||||
@ -66,15 +94,11 @@ compiling rf433.c...
|
||||
..\Driver_RF433\Src\rf433.c(132): warning: #177-D: function "rf433_response_command_check" was declared but never referenced
|
||||
static bool rf433_response_command_check(rf433_request_cmd_t cmd, uint8_t *buffer, uint8_t length)
|
||||
..\Driver_RF433\Src\rf433.c: 4 warnings, 0 errors
|
||||
compiling stm32f1xx_hal_exti.c...
|
||||
compiling system_stm32f1xx.c...
|
||||
compiling stm32f1xx_hal_tim_ex.c...
|
||||
compiling stm32f1xx_hal_uart.c...
|
||||
compiling rf433_hal.c...
|
||||
linking...
|
||||
Program Size: Code=16622 RO-data=478 RW-data=152 ZI-data=3768
|
||||
Program Size: Code=21112 RO-data=572 RW-data=192 ZI-data=5808
|
||||
FromELF: creating hex file...
|
||||
"project\project.axf" - 0 Error(s), 5 Warning(s).
|
||||
"project\project.axf" - 0 Error(s), 14 Warning(s).
|
||||
|
||||
<h2>Software Packages used:</h2>
|
||||
|
||||
@ -98,7 +122,7 @@ Package Vendor: Keil
|
||||
|
||||
* Component: ARM::CMSIS:CORE@6.1.1
|
||||
Include file: CMSIS/Core/Include/tz_context.h
|
||||
Build Time Elapsed: 00:00:20
|
||||
Build Time Elapsed: 00:00:16
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -13,6 +13,9 @@
|
||||
"project\io_monitor.o"
|
||||
"project\relay_control.o"
|
||||
"project\uart2_print.o"
|
||||
"project\cmd_router.o"
|
||||
"project\debug_log.o"
|
||||
"project\multi_uart_router.o"
|
||||
"project\stm32f1xx_hal_gpio_ex.o"
|
||||
"project\stm32f1xx_hal_spi.o"
|
||||
"project\stm32f1xx_hal.o"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -32,9 +32,42 @@ I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_uart.h)(0x69AAA0CD)
|
||||
I (../Drivers/CMSIS/Include/core_cm3.h)(0x69AAA0BA)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdint.h)(0x5F63877C)
|
||||
I (../Drivers/CMSIS/Include/cmsis_version.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Include/cmsis_compiler.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Include/cmsis_compiler.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Include/cmsis_armcc.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Device/ST/STM32F1xx/Include/system_stm32f1xx.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h)(0x69AAA0CD)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stddef.h)(0x5F63877C)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_exti.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_cortex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_pwr.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_spi.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_uart.h)(0x69AAA0CD)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdbool.h)(0x5F63877C)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdio.h)(0x5F63877C)
|
||||
F (..\Core\Src\rf433_rx_app.c)(0x69C25185)(--c99 -c --cpu Cortex-M3 -D__MICROLIB -g -O0 --apcs=interwork --split_sections -I ../Core/Inc -I ../Drivers/STM32F1xx_HAL_Driver/Inc -I ../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy -I ../Drivers/CMSIS/Device/ST/STM32F1xx/Include -I ../Drivers/CMSIS/Include -I ../Middlewares/u8g2Lib/inc -I ../Middlewares/MultMenu/application -I ../Middlewares/MultMenu/disp -I ../Middlewares/MultMenu/menu -I ../Driver_RF433 -I ../Driver_RF433/Inc -I ../Driver_RF433/Src
|
||||
|
||||
-I.\RTE\_project
|
||||
|
||||
-IC:\Users\xtell\AppData\Local\Arm\Packs\ARM\CMSIS\6.2.0\CMSIS\Core\Include
|
||||
|
||||
-IC:\Users\xtell\AppData\Local\Arm\Packs\Keil\STM32F1xx_DFP\2.4.1\Device\Include
|
||||
|
||||
-D__UVISION_VERSION="543" -DSTM32F10X_MD -D_RTE_ -DUSE_HAL_DRIVER -DSTM32F103xB
|
||||
|
||||
-o project\rf433_rx_app.o --omf_browse project\rf433_rx_app.crf --depend project\rf433_rx_app.d)
|
||||
I (../Driver_RF433/Inc/rf433_config.h)(0x69C62F40)
|
||||
I (../Core/Inc/rf433_rx_app.h)(0x69C22B79)
|
||||
I (../Driver_RF433/Inc/rf433.h)(0x69C222AA)
|
||||
I (../Driver_RF433/Inc/rf433_hal.h)(0x69C2716B)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdint.h)(0x5F63877C)
|
||||
I (../Drivers/CMSIS/Device/ST/STM32F1xx/Include/system_stm32f1xx.h)(0x69AAA0CD)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdbool.h)(0x5F63877C)
|
||||
I (../Core/Inc/main.h)(0x69C35425)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal.h)(0x69AAA0CD)
|
||||
I (../Core/Inc/stm32f1xx_hal_conf.h)(0x69C35424)
|
||||
@ -68,9 +101,9 @@ I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_spi.h)(0x69AAA0CD)
|
||||
-I.\RTE\_project
|
||||
|
||||
-IC:\Users\xtell\AppData\Local\Arm\Packs\ARM\CMSIS\6.2.0\CMSIS\Core\Include
|
||||
|
||||
|
||||
-IC:\Users\xtell\AppData\Local\Arm\Packs\Keil\STM32F1xx_DFP\2.4.1\Device\Include
|
||||
|
||||
|
||||
-D__UVISION_VERSION="543" -DSTM32F10X_MD -D_RTE_ -DUSE_HAL_DRIVER -DSTM32F103xB
|
||||
|
||||
-o project\rf433_tx_app.o --omf_browse project\rf433_tx_app.crf --depend project\rf433_tx_app.d)
|
||||
@ -104,14 +137,18 @@ I (../Core/Inc/spi.h)(0x69C35422)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_pwr.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_spi.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_spi.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_uart.h)(0x69AAA0CD)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdio.h)(0x5F63877C)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdio.h)(0x5F63877C)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\string.h)(0x5F63878A)
|
||||
I (../Core/Inc/uart2_print.h)(0x69C50FF9)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdarg.h)(0x5F63877C)
|
||||
F (../Core/Src/main.c)(0x69C50991)(--c99 -c --cpu Cortex-M3 -D__MICROLIB -g -O0 --apcs=interwork --split_sections -I ../Core/Inc -I ../Drivers/STM32F1xx_HAL_Driver/Inc -I ../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy -I ../Drivers/CMSIS/Device/ST/STM32F1xx/Include -I ../Drivers/CMSIS/Include -I ../Middlewares/u8g2Lib/inc -I ../Middlewares/MultMenu/application -I ../Middlewares/MultMenu/disp -I ../Middlewares/MultMenu/menu -I ../Driver_RF433 -I ../Driver_RF433/Inc -I ../Driver_RF433/Src
|
||||
I (../Core/Inc/uart2_print.h)(0x69C636F0)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdarg.h)(0x5F63877C)
|
||||
F (../Core/Src/main.c)(0x69C62F4C)(--c99 -c --cpu Cortex-M3 -D__MICROLIB -g -O0 --apcs=interwork --split_sections -I ../Core/Inc -I ../Drivers/STM32F1xx_HAL_Driver/Inc -I ../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy -I ../Drivers/CMSIS/Device/ST/STM32F1xx/Include -I ../Drivers/CMSIS/Include -I ../Middlewares/u8g2Lib/inc -I ../Middlewares/MultMenu/application -I ../Middlewares/MultMenu/disp -I ../Middlewares/MultMenu/menu -I ../Driver_RF433 -I ../Driver_RF433/Inc -I ../Driver_RF433/Src
|
||||
|
||||
-I.\RTE\_project
|
||||
|
||||
-IC:\Users\xtell\AppData\Local\Arm\Packs\ARM\CMSIS\6.2.0\CMSIS\Core\Include
|
||||
|
||||
-IC:\Users\xtell\AppData\Local\Arm\Packs\Keil\STM32F1xx_DFP\2.4.1\Device\Include
|
||||
|
||||
-D__UVISION_VERSION="543" -DSTM32F10X_MD -D_RTE_ -DUSE_HAL_DRIVER -DSTM32F103xB
|
||||
@ -174,7 +211,7 @@ I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_spi.h)(0x69AAA0CD)
|
||||
|
||||
-o project\gpio.o --omf_browse project\gpio.crf --depend project\gpio.d)
|
||||
I (../Core/Inc/gpio.h)(0x665D64E1)
|
||||
I (../Core/Inc/main.h)(0x69C35425)
|
||||
I (../Core/Inc/main.h)(0x69C35425)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal.h)(0x69AAA0CD)
|
||||
I (../Core/Inc/stm32f1xx_hal_conf.h)(0x69C35424)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc.h)(0x69AAA0CD)
|
||||
@ -267,23 +304,23 @@ I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_spi.h)(0x69AAA0CD)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdint.h)(0x5F63877C)
|
||||
I (../Drivers/CMSIS/Include/cmsis_version.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Include/cmsis_compiler.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Include/cmsis_armcc.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Device/ST/STM32F1xx/Include/system_stm32f1xx.h)(0x69AAA0CD)
|
||||
I (../Drivers/CMSIS/Include/cmsis_armcc.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Device/ST/STM32F1xx/Include/system_stm32f1xx.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h)(0x69AAA0CD)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stddef.h)(0x5F63877C)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_exti.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_exti.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_cortex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_pwr.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_pwr.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_spi.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_uart.h)(0x69AAA0CD)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdbool.h)(0x5F63877C)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdbool.h)(0x5F63877C)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdio.h)(0x5F63877C)
|
||||
I (../Driver_RF433/Inc/rf433_hal.h)(0x69C2716B)
|
||||
F (../Core/Src/stm32f1xx_it.c)(0x69C35423)(--c99 -c --cpu Cortex-M3 -D__MICROLIB -g -O0 --apcs=interwork --split_sections -I ../Core/Inc -I ../Drivers/STM32F1xx_HAL_Driver/Inc -I ../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy -I ../Drivers/CMSIS/Device/ST/STM32F1xx/Include -I ../Drivers/CMSIS/Include -I ../Middlewares/u8g2Lib/inc -I ../Middlewares/MultMenu/application -I ../Middlewares/MultMenu/disp -I ../Middlewares/MultMenu/menu -I ../Driver_RF433 -I ../Driver_RF433/Inc -I ../Driver_RF433/Src
|
||||
@ -313,11 +350,11 @@ I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_spi.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h)(0x69AAA0CD)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stddef.h)(0x5F63877C)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_exti.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_cortex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h)(0x69AAA0CD)
|
||||
@ -346,8 +383,8 @@ I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_pwr.h)(0x69AAA0CD)
|
||||
I (../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f1xx.h)(0x69AAA0CD)
|
||||
I (../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f103xb.h)(0x69AAA0CD)
|
||||
I (../Drivers/CMSIS/Include/core_cm3.h)(0x69AAA0BA)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdint.h)(0x5F63877C)
|
||||
I (../Drivers/CMSIS/Include/cmsis_version.h)(0x69AAA0BA)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdint.h)(0x5F63877C)
|
||||
I (../Drivers/CMSIS/Include/cmsis_version.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Include/cmsis_compiler.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Include/cmsis_armcc.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Device/ST/STM32F1xx/Include/system_stm32f1xx.h)(0x69AAA0CD)
|
||||
@ -380,6 +417,87 @@ I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_spi.h)(0x69AAA0CD)
|
||||
-o project\cmd_parser.o --omf_browse project\cmd_parser.crf --depend project\cmd_parser.d)
|
||||
I (../Core/Inc/cmd_parser.h)(0x69C636F0)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdint.h)(0x5F63877C)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdbool.h)(0x5F63877C)
|
||||
I (../Core/Inc/uart2_print.h)(0x69C636F0)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdarg.h)(0x5F63877C)
|
||||
I (../Core/Inc/io_monitor.h)(0x69C60329)
|
||||
I (../Core/Inc/relay_control.h)(0x69C636F0)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\string.h)(0x5F63878A)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\ctype.h)(0x5F63877C)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdio.h)(0x5F63877C)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdlib.h)(0x5F63877C)
|
||||
F (..\Core\Src\io_monitor.c)(0x69C60379)(--c99 -c --cpu Cortex-M3 -D__MICROLIB -g -O0 --apcs=interwork --split_sections -I ../Core/Inc -I ../Drivers/STM32F1xx_HAL_Driver/Inc -I ../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy -I ../Drivers/CMSIS/Device/ST/STM32F1xx/Include -I ../Drivers/CMSIS/Include -I ../Middlewares/u8g2Lib/inc -I ../Middlewares/MultMenu/application -I ../Middlewares/MultMenu/disp -I ../Middlewares/MultMenu/menu -I ../Driver_RF433 -I ../Driver_RF433/Inc -I ../Driver_RF433/Src
|
||||
|
||||
-I.\RTE\_project
|
||||
|
||||
-IC:\Users\xtell\AppData\Local\Arm\Packs\ARM\CMSIS\6.2.0\CMSIS\Core\Include
|
||||
|
||||
-IC:\Users\xtell\AppData\Local\Arm\Packs\Keil\STM32F1xx_DFP\2.4.1\Device\Include
|
||||
|
||||
-D__UVISION_VERSION="543" -DSTM32F10X_MD -D_RTE_ -DUSE_HAL_DRIVER -DSTM32F103xB
|
||||
|
||||
-o project\io_monitor.o --omf_browse project\io_monitor.crf --depend project\io_monitor.d)
|
||||
I (../Core/Inc/io_monitor.h)(0x69C60329)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdint.h)(0x5F63877C)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdbool.h)(0x5F63877C)
|
||||
I (../Core/Inc/uart2_print.h)(0x69C636F0)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdarg.h)(0x5F63877C)
|
||||
I (../Core/Inc/main.h)(0x69C35425)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal.h)(0x69AAA0CD)
|
||||
I (../Core/Inc/stm32f1xx_hal_conf.h)(0x69C35424)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_def.h)(0x69AAA0CD)
|
||||
I (../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f1xx.h)(0x69AAA0CD)
|
||||
I (../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f103xb.h)(0x69AAA0CD)
|
||||
I (../Drivers/CMSIS/Include/core_cm3.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Include/cmsis_version.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Include/cmsis_compiler.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Include/cmsis_armcc.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Device/ST/STM32F1xx/Include/system_stm32f1xx.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h)(0x69AAA0CD)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stddef.h)(0x5F63877C)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_exti.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_cortex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_pwr.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_spi.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_uart.h)(0x69AAA0CD)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdio.h)(0x5F63877C)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\string.h)(0x5F63878A)
|
||||
F (..\Core\Src\relay_control.c)(0x69C636F0)(--c99 -c --cpu Cortex-M3 -D__MICROLIB -g -O0 --apcs=interwork --split_sections -I ../Core/Inc -I ../Drivers/STM32F1xx_HAL_Driver/Inc -I ../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy -I ../Drivers/CMSIS/Device/ST/STM32F1xx/Include -I ../Drivers/CMSIS/Include -I ../Middlewares/u8g2Lib/inc -I ../Middlewares/MultMenu/application -I ../Middlewares/MultMenu/disp -I ../Middlewares/MultMenu/menu -I ../Driver_RF433 -I ../Driver_RF433/Inc -I ../Driver_RF433/Src
|
||||
|
||||
-I.\RTE\_project
|
||||
|
||||
-IC:\Users\xtell\AppData\Local\Arm\Packs\ARM\CMSIS\6.2.0\CMSIS\Core\Include
|
||||
|
||||
-IC:\Users\xtell\AppData\Local\Arm\Packs\Keil\STM32F1xx_DFP\2.4.1\Device\Include
|
||||
|
||||
-D__UVISION_VERSION="543" -DSTM32F10X_MD -D_RTE_ -DUSE_HAL_DRIVER -DSTM32F103xB
|
||||
|
||||
-o project\relay_control.o --omf_browse project\relay_control.crf --depend project\relay_control.d)
|
||||
I (../Core/Inc/relay_control.h)(0x69C636F0)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdint.h)(0x5F63877C)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdbool.h)(0x5F63877C)
|
||||
I (../Core/Inc/uart2_print.h)(0x69C636F0)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stdarg.h)(0x5F63877C)
|
||||
I (../Core/Inc/main.h)(0x69C35425)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal.h)(0x69AAA0CD)
|
||||
I (../Core/Inc/stm32f1xx_hal_conf.h)(0x69C35424)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_def.h)(0x69AAA0CD)
|
||||
I (../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f1xx.h)(0x69AAA0CD)
|
||||
I (../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f103xb.h)(0x69AAA0CD)
|
||||
I (../Drivers/CMSIS/Include/core_cm3.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Include/cmsis_version.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Include/cmsis_compiler.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Include/cmsis_armcc.h)(0x69AAA0BA)
|
||||
I (../Drivers/CMSIS/Device/ST/STM32F1xx/Include/system_stm32f1xx.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h)(0x69AAA0CD)
|
||||
I (C:\Keil_v5\ARM\ARMCC\include\stddef.h)(0x5F63877C)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc_ex.h)(0x69AAA0CD)
|
||||
@ -812,9 +930,9 @@ I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h)(0x69AAA0CD)
|
||||
F (../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_cortex.c)(0x69AAA0CD)(--c99 -c --cpu Cortex-M3 -D__MICROLIB -g -O0 --apcs=interwork --split_sections -I ../Core/Inc -I ../Drivers/STM32F1xx_HAL_Driver/Inc -I ../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy -I ../Drivers/CMSIS/Device/ST/STM32F1xx/Include -I ../Drivers/CMSIS/Include -I ../Middlewares/u8g2Lib/inc -I ../Middlewares/MultMenu/application -I ../Middlewares/MultMenu/disp -I ../Middlewares/MultMenu/menu -I ../Driver_RF433 -I ../Driver_RF433/Inc -I ../Driver_RF433/Src
|
||||
|
||||
-I.\RTE\_project
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h)(0x69AAA0CD)
|
||||
|
||||
-IC:\Users\xtell\AppData\Local\Arm\Packs\ARM\CMSIS\6.2.0\CMSIS\Core\Include
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_spi.h)(0x69AAA0CD)
|
||||
|
||||
-IC:\Users\xtell\AppData\Local\Arm\Packs\Keil\STM32F1xx_DFP\2.4.1\Device\Include
|
||||
|
||||
-D__UVISION_VERSION="543" -DSTM32F10X_MD -D_RTE_ -DUSE_HAL_DRIVER -DSTM32F103xB
|
||||
@ -879,5 +997,5 @@ I (C:\Keil_v5\ARM\ARMCC\include\stdio.h)(0x5F63877C)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_cortex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h)(0x69AAA0CD)
|
||||
I (../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_pwr.h)(0x69AAA0CD)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,2 +1,36 @@
|
||||
project\rf433_rx_app.o: ..\Core\Src\rf433_rx_app.c
|
||||
project\rf433_rx_app.o: ../Driver_RF433/Inc/rf433_config.h
|
||||
project\rf433_rx_app.o: ../Core/Inc/rf433_rx_app.h
|
||||
project\rf433_rx_app.o: ../Driver_RF433/Inc/rf433.h
|
||||
project\rf433_rx_app.o: ../Driver_RF433/Inc/rf433_hal.h
|
||||
project\rf433_rx_app.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdint.h
|
||||
project\rf433_rx_app.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdbool.h
|
||||
project\rf433_rx_app.o: ../Core/Inc/main.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal.h
|
||||
project\rf433_rx_app.o: ../Core/Inc/stm32f1xx_hal_conf.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_def.h
|
||||
project\rf433_rx_app.o: ../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f1xx.h
|
||||
project\rf433_rx_app.o: ../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f103xb.h
|
||||
project\rf433_rx_app.o: ../Drivers/CMSIS/Include/core_cm3.h
|
||||
project\rf433_rx_app.o: ../Drivers/CMSIS/Include/cmsis_version.h
|
||||
project\rf433_rx_app.o: ../Drivers/CMSIS/Include/cmsis_compiler.h
|
||||
project\rf433_rx_app.o: ../Drivers/CMSIS/Include/cmsis_armcc.h
|
||||
project\rf433_rx_app.o: ../Drivers/CMSIS/Device/ST/STM32F1xx/Include/system_stm32f1xx.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h
|
||||
project\rf433_rx_app.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stddef.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc_ex.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio_ex.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_exti.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma_ex.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_cortex.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_pwr.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_spi.h
|
||||
project\rf433_rx_app.o: ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_uart.h
|
||||
project\rf433_rx_app.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\stdio.h
|
||||
project\rf433_rx_app.o: C:\Keil_v5\ARM\ARMCC\Bin\..\include\string.h
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
943
docs/多通信接口统一指令处理系统开发计划.md
Normal file
943
docs/多通信接口统一指令处理系统开发计划.md
Normal file
@ -0,0 +1,943 @@
|
||||
# 多通信接口统一指令处理系统开发计划
|
||||
|
||||
**项目名称**:E32-433TBH-SC 多接口统一指令处理扩展
|
||||
**版本**:V1.0
|
||||
**制定日期**:2026-03-27
|
||||
**适用范围**:STM32F103嵌入式系统
|
||||
|
||||
***
|
||||
|
||||
## 1. 项目概述
|
||||
|
||||
### 1.1 背景与目标
|
||||
|
||||
**现有系统状况**:
|
||||
|
||||
- UART2作为调试专用接口,已实现ASCII指令(`$CMD,param*CS`格式)解析
|
||||
- 指令覆盖继电器控制(RL)、数字输入查询(DI)、回显测试(ECHO)
|
||||
- 四路DI状态变化通过UART2自动上报(`$DI_EVENT`格式)
|
||||
- RF433模块通过UART1通信,485模块预留给UART3
|
||||
|
||||
**目标**:在不破坏现有UART2调试接口功能的前提下,将UART1(RF433)和UART3(485)纳入统一指令处理体系,使这三个接口能够:
|
||||
|
||||
1. **接收相同格式指令**并执行相同的业务操作
|
||||
2. **向指令来源接口返回响应**(而非统一从UART2返回)
|
||||
3. **所有收发数据在UART2上打印详细调试日志**
|
||||
|
||||
### 1.2 范围与边界
|
||||
|
||||
| 纳入范围 | 排除范围 |
|
||||
| --------------------------- | ------------------- |
|
||||
| UART1(RF433)、UART3(485)指令接收 | 现有RF433 TX/RX应用逻辑修改 |
|
||||
| 多路响应路由机制 | 物理层驱动修改 |
|
||||
| 全链路调试日志系统 | 协议层(protocol.h)重构 |
|
||||
| 共享资源并发保护 | <br /> |
|
||||
|
||||
### 1.3 预期交付物
|
||||
|
||||
| 交付物 | 说明 |
|
||||
| ------------------------- | ------------------- |
|
||||
| `multi_uart_router.[c/h]` | 多UART统一路由核心模块 |
|
||||
| `cmd_router.[c/h]` | 指令路由与响应分发模块 |
|
||||
| `debug_log.[c/h]` | 增强型调试日志系统 |
|
||||
| 修改后的 `cmd_parser.c` | 支持多实例解析器(可选)或响应路由接口 |
|
||||
| 修改后的 `main.c` | 中断回调和任务调度整合 |
|
||||
| 测试用例与验证方案 | <br /> |
|
||||
|
||||
***
|
||||
|
||||
## 2. 系统架构设计
|
||||
|
||||
### 2.1 当前架构分析
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ main.c │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ UART2_Rx │ │ UART1_Rx │ │ UART3_Rx │ │
|
||||
│ │ Interrupt │ │ (RF433) │ │ (RS485) │ │
|
||||
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────┐ │
|
||||
│ │ CmdParser (仅UART2专用) │ │
|
||||
│ │ CmdParser_FeedByte() + Task() │ │
|
||||
│ └─────────────────────────┬───────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────────┼──────────────────┐ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │Relay_Ctrl │ │ IO_Monitor │ │ (其他) │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────┐ │
|
||||
│ │ UART2_Print │ ◄── 所有响应固定从UART2输出 │
|
||||
│ │ (Ring Buffer) │ │
|
||||
│ └─────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**关键问题**:
|
||||
|
||||
1. `cmd_parser.c`中`send_response_ok()`和`send_response_err()`**硬编码**使用`UART2_Print_String()`
|
||||
2. `IO_Monitor`中`send_di_event()`同样**硬编码**使用`UART2_Print_String()`
|
||||
3. UART1和UART3的接收中断**未接入指令解析体系**
|
||||
4. 缺乏统一的**响应路由**机制
|
||||
|
||||
### 2.2 目标架构设计
|
||||
|
||||
#### 2.2.1 整体数据流图
|
||||
|
||||
```
|
||||
UART2_Print (调试日志)
|
||||
▲
|
||||
│ DEBUG_LOG()
|
||||
│
|
||||
┌─────────────────────────────────────┴─────────────────────────────────────┐
|
||||
│ [UART2 - 调试专用通道] │
|
||||
│ 调试命令输入 ──► Rx Interrupt ──► CmdParser ──► 执行 ──► 响应UART2 │
|
||||
└───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌───────────────────────────────────────────────────────────────────────────┐
|
||||
│ [UART1 - RF433无线模块] │
|
||||
│ RF433数据 ──► Rx Interrupt ──► ┬── FeedByte() ──► 解析 ──► 执行 ──► 响应UART1
|
||||
│ │ ▲ │
|
||||
│ │ │ │
|
||||
│ ┌──────────────────────────────┴────────────────────┴────────────────┐ │
|
||||
│ │ Multi-UART Command Router (新增核心) │ │
|
||||
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
|
||||
│ │ │UART1_Recv │ │UART2_Recv │ │UART3_Recv │ │ │
|
||||
│ │ │RingBuffer │ │RingBuffer │ │RingBuffer │ │ │
|
||||
│ │ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ │ │
|
||||
│ │ │ │ │ │ │
|
||||
│ │ └───────────────┼───────────────┘ │ │
|
||||
│ │ ▼ │ │
|
||||
│ │ ┌─────────────────┐ │ │
|
||||
│ │ │ Unified Parser │ ◄── 共享解析状态机 │ │
|
||||
│ │ │ (CmdParser) │ │ │
|
||||
│ │ └────────┬────────┘ │ │
|
||||
│ │ │ │ │
|
||||
│ │ ┌──────────────┼──────────────┐ │ │
|
||||
│ │ ▼ ▼ ▼ │ │
|
||||
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │
|
||||
│ │ │Relay_Ctrl │ │IO_Monitor│ │ (Future) │ │ │
|
||||
│ │ └─────┬─────┘ └─────┬─────┘ └───────────┘ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ └──────────────┼─────────────────────────────────────┐ │ │
|
||||
│ │ │ Response Router │ │ │
|
||||
│ │ ▼ │ │ │
|
||||
│ │ ┌─────────────────────────────────────────────────────────┐ │ │ │
|
||||
│ │ │ port_table[] = { │ │ │ │
|
||||
│ │ │ {UART1, &huart1, "RF433"}, │ │ │ │
|
||||
│ │ │ {UART2, &huart2, "DEBUG"}, // 保持原样 │ │ │ │
|
||||
│ │ │ {UART3, &huart3, "RS485"} │ │ │ │
|
||||
│ │ │ } │ │ │ │
|
||||
│ │ └─────────────────────────────────────────────────────────┘ │ │ │
|
||||
└────┴──────────────────────────────────────────────────────────────┴──────┘
|
||||
|
||||
┌───────────────────────────────────────────────────────────────────────────┐
|
||||
│ [UART3 - RS485有线模块] │
|
||||
│ 485数据 ──► Rx Interrupt ──► ──► FeedByte() ──► 解析 ──► 执行 ──► 响应UART3
|
||||
└───────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 2.2.2 模块划分与职责
|
||||
|
||||
| 模块名 | 职责 | 文件位置 |
|
||||
| --------------------- | --------------------------- | ----------------------------- |
|
||||
| **Multi-UART Router** | 三个UART接收通道管理、环形缓冲区、响应路由表 | `multi_uart_router.[c/h]`(新增) |
|
||||
| **Cmd Router** | 指令解析器封装、响应目标指定、响应构造与发送 | `cmd_router.[c/h]`(新增) |
|
||||
| **Debug Log** | 统一日志接口,支持来源标签、十六进制 dump、时间戳 | `debug_log.[c/h]`(新增) |
|
||||
| **CmdParser** | 状态机解析(修改:移除硬编码响应,改为回调方式) | 修改现有 |
|
||||
| **UART2\_Print** | 底层环形缓冲区发送(复用) | 现有 |
|
||||
|
||||
### 2.3 关键设计决策
|
||||
|
||||
#### 2.3.1 指令格式一致性策略
|
||||
|
||||
**决策**:保持现有`$CMD,param1,param2*CS`格式不变,三个UART共用同一解析器。
|
||||
|
||||
**理由**:
|
||||
|
||||
- 现有`cmd_parser.c`已实现完整的状态机解析,去抖、超时、校验和验证
|
||||
- 复用解析器避免代码膨胀(STM32F103资源有限:64KB Flash,20KB RAM)
|
||||
- 统一格式降低后续维护复杂度
|
||||
|
||||
#### 2.3.2 响应路由机制
|
||||
|
||||
**方案**:引入**端口上下文表(port\_context\_table)**
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
UART_HandleTypeDef *huart; // 指向具体UART句柄
|
||||
const char *port_name; // 端口名称标签,用于日志
|
||||
ring_buffer_t *rx_ring; // 接收环形缓冲区
|
||||
uint8_t rx_tmp; // 单字节接收暂存
|
||||
} uart_port_context_t;
|
||||
```
|
||||
|
||||
**路由原理**:
|
||||
|
||||
- 指令解析完成后,业务函数返回**响应数据**和**来源端口句柄**
|
||||
- `Response_Send()`根据句柄查找对应UART发送
|
||||
- UART2保持原有直接调用方式,**不经过路由层**(调试专用)
|
||||
|
||||
#### 2.3.3 日志记录策略
|
||||
|
||||
**分层日志级别**:
|
||||
|
||||
| 级别 | 宏定义 | 触发条件 | 示例输出 |
|
||||
| ----- | ------------- | -------------- | -------------------------------------------------- |
|
||||
| ERROR | `LOG_ERROR()` | 校验失败、超时、参数错误 | `[UART1] ERR: CS mismatch recv=0xA1 calc=0x3C` |
|
||||
| WARN | `LOG_WARN()` | 缓冲区边缘、异常状态 | `[UART3] WARN: RX buffer 90% full` |
|
||||
| INFO | `LOG_INFO()` | 指令接收、响应发送 | `[UART1] RX: $RL,1*2F` → `[UART1] TX: $OK,RL,1*00` |
|
||||
| DEBUG | `LOG_DEBUG()` | 十六进制 dump、详细状态 | `[UART1] DUMP: [A1 B2 C3 D4]` |
|
||||
|
||||
**日志格式模板**:
|
||||
|
||||
```
|
||||
[TIMESTAMP][PORT] <LEVEL>: <message>
|
||||
示例:
|
||||
[012345][UART1] INFO: CMD=RL P1=1 P2=空
|
||||
[012346][UART1] INFO: Relay -> ON
|
||||
[012347][UART1] TX: $OK,RL,1*00
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## 3. 详细设计
|
||||
|
||||
### 3.1 多UART数据接收层
|
||||
|
||||
#### 3.1.1 UART1/UART3中断+DMA/环形缓冲区设计
|
||||
|
||||
**推荐方案:中断+环形缓冲区**(对比DMA方案)
|
||||
|
||||
| 方案 | 优点 | 缺点 | 适用场景 |
|
||||
| ------------ | ---------- | ------------ | --------- |
|
||||
| **中断+环形缓冲区** | 实现简单,代码量小 | 中断频繁(约每字节一次) | 数据量小、指令简短 |
|
||||
| DMA+环形缓冲区 | 中断少,CPU效率高 | 实现复杂,内存占用大 | 高速大数据流 |
|
||||
|
||||
**选定**:中断+环形缓冲区(现有UART2已验证可行)
|
||||
|
||||
**环形缓冲区设计**:
|
||||
|
||||
```c
|
||||
#define UART_RX_BUFFER_SIZE 128 // 每个端口接收缓冲区大小
|
||||
|
||||
typedef struct {
|
||||
uint8_t buffer[UART_RX_BUFFER_SIZE];
|
||||
volatile uint16_t head; // 写入位置(中断写入)
|
||||
volatile uint16_t tail; // 读取位置(主循环读取)
|
||||
volatile uint16_t count; // 有效数据计数
|
||||
} uart_rx_ring_t;
|
||||
```
|
||||
|
||||
**关键约束**:
|
||||
|
||||
- `head`和`tail`使用`volatile`防止编译器优化
|
||||
- 缓冲区大小需为2的幂次,便于模运算优化(本项目128=2^7)
|
||||
- 中断中**只负责**将数据压入缓冲区,**禁止**复杂逻辑
|
||||
|
||||
#### 3.1.2 数据接收状态机
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ UART ISR (每字节) │
|
||||
└──────────────────┬──────────────────────┘
|
||||
│
|
||||
┌──────────────────▼──────────────────────┐
|
||||
│ 写入 rx_ring.buffer[head] │
|
||||
│ head = (head + 1) % RX_BUFFER_SIZE │
|
||||
│ count++ │
|
||||
└──────────────────┬──────────────────────┘
|
||||
│
|
||||
┌──────────────────▼──────────────────────┐
|
||||
│ count >= RX_BUFFER_SIZE ? │
|
||||
│ overflow_count++ (丢弃) │
|
||||
│ head 回绕覆盖旧数据 │
|
||||
└──────────────────┬──────────────────────┘
|
||||
│
|
||||
┌──────────────────▼──────────────────────┐
|
||||
│ 启动下次接收: HAL_UART_Receive_IT() │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**防溢出策略**:
|
||||
|
||||
- 新数据覆盖最旧数据(Lossy Ring Buffer)
|
||||
- 溢出时记录`overflow_count`,供诊断使用
|
||||
|
||||
### 3.2 统一指令路由模块
|
||||
|
||||
#### 3.2.1 模块接口设计(关键函数原型)
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief 指令路由模块初始化
|
||||
* @note 初始化所有UART端口的接收缓冲区和解析器
|
||||
* @param 无
|
||||
* @retval 无
|
||||
*/
|
||||
void CmdRouter_Init(void);
|
||||
|
||||
/**
|
||||
* @brief 向指定端口的解析器喂入数据
|
||||
* @note 由UART中断回调调用,线程安全
|
||||
* @param port_id: 端口ID (PORT_UART1/PORT_UART2/PORT_UART3)
|
||||
* @param byte: 接收到的字节
|
||||
* @param current_tick: 系统时间戳
|
||||
* @retval 无
|
||||
*/
|
||||
void CmdRouter_FeedByte(port_id_t port_id, uint8_t byte, uint32_t current_tick);
|
||||
|
||||
/**
|
||||
* @brief 指令路由任务(主循环调用)
|
||||
* @note 轮询所有端口的解析器状态,执行已就绪的指令
|
||||
* @param 无
|
||||
* @retval 无
|
||||
*/
|
||||
void CmdRouter_Task(void);
|
||||
|
||||
/**
|
||||
* @brief 发送响应到指定端口
|
||||
* @note 根据port_id查找对应UART句柄,发送响应数据
|
||||
* @param port_id: 目标端口ID
|
||||
* @param data: 响应数据缓冲区
|
||||
* @param len: 数据长度
|
||||
* @retval 无
|
||||
*/
|
||||
void CmdRouter_SendResponse(port_id_t port_id, const uint8_t *data, uint16_t len);
|
||||
|
||||
/**
|
||||
* @brief 发送格式化响应
|
||||
* @note 支持printf风格格式化,自动计算校验和
|
||||
* @param port_id: 目标端口ID
|
||||
* @param fmt: 格式化字符串
|
||||
* @retval 无
|
||||
*/
|
||||
void CmdRouter_SendResponseFmt(port_id_t port_id, const char *fmt, ...);
|
||||
```
|
||||
|
||||
#### 3.2.2 指令包结构定义
|
||||
|
||||
```c
|
||||
/** 端口ID枚举 */
|
||||
typedef enum {
|
||||
PORT_UART1 = 0, /**< RF433模块 */
|
||||
PORT_UART2 = 1, /**< 调试串口 */
|
||||
PORT_UART3 = 2, /**< RS485模块 */
|
||||
PORT_COUNT
|
||||
} port_id_t;
|
||||
|
||||
/**
|
||||
* @brief 带源端口信息的指令帧结构
|
||||
* @note 解析完成后携带来源端口信息,用于响应路由
|
||||
*/
|
||||
typedef struct {
|
||||
cmd_frame_t frame; /**< 原始指令帧数据 */
|
||||
port_id_t source_port; /**< 指令来源端口 */
|
||||
uint32_t recv_tick; /**< 接收完成时间戳 */
|
||||
} routed_cmd_frame_t;
|
||||
|
||||
/**
|
||||
* @brief 端口上下文结构
|
||||
* @note 管理每个UART端口的接收缓冲区和解析状态
|
||||
*/
|
||||
typedef struct {
|
||||
UART_HandleTypeDef *huart; /**< UART句柄指针 */
|
||||
const char *name; /**< 端口名称(用于日志) */
|
||||
uart_rx_ring_t rx_ring; /**< 接收环形缓冲区 */
|
||||
parser_context_t parser; /**< 解析器上下文 */
|
||||
uint8_t rx_tmp; /**< 中断接收暂存 */
|
||||
uint32_t rx_count; /**< 累计接收字节数 */
|
||||
uint32_t error_count; /**< 解析错误计数 */
|
||||
} uart_port_context_t;
|
||||
```
|
||||
|
||||
#### 3.2.3 与现有`CmdParser`的集成方式
|
||||
|
||||
**方案:扩展而非修改**
|
||||
|
||||
1. **新增`CmdRouter`层**:封装现有`CmdParser`,添加端口追踪能力
|
||||
2. **保持`CmdParser`独立**:不修改现有`cmd_parser.c`,通过回调钩子实现集成
|
||||
|
||||
**集成接口设计**:
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief 注册指令处理回调
|
||||
* @note 当指令解析完成时调用,传入来源端口信息
|
||||
* @param callback: 回调函数指针
|
||||
* @retval 无
|
||||
*/
|
||||
void CmdParser_RegisterCallback(void (*callback)(const routed_cmd_frame_t *frame));
|
||||
```
|
||||
|
||||
**替代方案(可选)**:
|
||||
|
||||
如果项目允许适度修改`cmd_parser.c`,推荐直接将`send_response_ok/err()`改为通过回调输出:
|
||||
|
||||
```c
|
||||
// cmd_parser.h 新增
|
||||
typedef void (*response_callback_t)(port_id_t port_id, const char *response, uint16_t len);
|
||||
|
||||
void CmdParser_SetResponseCallback(response_callback_t callback);
|
||||
```
|
||||
|
||||
### 3.3 多路响应处理模块
|
||||
|
||||
#### 3.3.1 响应路由表设计
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief 响应路由表
|
||||
* @note 静态表,根据port_id索引查找对应UART句柄
|
||||
*/
|
||||
static UART_HandleTypeDef* const g_port_uart_map[PORT_COUNT] = {
|
||||
[PORT_UART1] = &huart1, // RF433
|
||||
[PORT_UART2] = &huart2, // DEBUG
|
||||
[PORT_UART3] = &huart3, // RS485
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 端口名称表(用于日志输出)
|
||||
*/
|
||||
static const char* const g_port_name_map[PORT_COUNT] = {
|
||||
[PORT_UART1] = "UART1",
|
||||
[PORT_UART2] = "UART2",
|
||||
[PORT_UART3] = "UART3",
|
||||
};
|
||||
```
|
||||
|
||||
#### 3.3.2 响应发送队列管理
|
||||
|
||||
**设计原则**:
|
||||
|
||||
- 复用现有`UART2_Print`的环形缓冲区机制
|
||||
- 为UART1和UART3**各创建一个发送环形缓冲区**
|
||||
- 发送流程:业务层构造响应 → 写入目标端口缓冲区 → UART Tx ISR驱动发送
|
||||
|
||||
```c
|
||||
/** 发送环形缓冲区(与uart2_print结构兼容) */
|
||||
typedef struct {
|
||||
uint8_t buffer[UART_TX_BUFFER_SIZE];
|
||||
volatile uint16_t head;
|
||||
volatile uint16_t tail;
|
||||
volatile uint16_t count;
|
||||
volatile bool is_sending;
|
||||
} uart_tx_ring_t;
|
||||
|
||||
/** 端口发送上下文 */
|
||||
typedef struct {
|
||||
uart_tx_ring_t tx_ring;
|
||||
UART_HandleTypeDef *huart;
|
||||
volatile uint16_t overflow_count;
|
||||
} uart_tx_context_t;
|
||||
```
|
||||
|
||||
**关键API**:
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief 初始化指定端口的发送缓冲区
|
||||
* @param port_id: 端口ID
|
||||
* @retval 无
|
||||
*/
|
||||
void MultiUART_TxInit(port_id_t port_id);
|
||||
|
||||
/**
|
||||
* @brief 发送数据到指定端口(非阻塞)
|
||||
* @param port_id: 端口ID
|
||||
* @param data: 数据缓冲区
|
||||
* @param len: 数据长度
|
||||
* @retval 无
|
||||
*/
|
||||
void MultiUART_Send(port_id_t port_id, const uint8_t *data, uint16_t len);
|
||||
|
||||
/**
|
||||
* @brief 指定端口发送完成回调(供HAL_UART_TxCpltCallback调用)
|
||||
* @param port_id: 端口ID
|
||||
* @retval 无
|
||||
*/
|
||||
void MultiUART_TxCpltCallback(port_id_t port_id);
|
||||
```
|
||||
|
||||
### 3.4 增强型调试日志系统
|
||||
|
||||
#### 3.4.1 日志格式标准(具体示例)
|
||||
|
||||
**基础格式**:
|
||||
|
||||
```
|
||||
[HHHHHH][PORT] LEVEL: message\r\n
|
||||
│ │ │ │
|
||||
│ │ │ └── 具体日志内容
|
||||
│ │ └───────── 日志级别 (ERROR/WARN/INFO/DEBUG)
|
||||
│ └─────────────── 来源端口 (UART1/UART2/UART3/ALL)
|
||||
└─────────────────────── 系统运行时间(十六进制,毫秒)
|
||||
```
|
||||
|
||||
**示例输出**:
|
||||
|
||||
```
|
||||
[0001F4][UART1] INFO: RX len=12 "$RL,1*2F\r\n"
|
||||
[0001F5][UART1] INFO: CMD=RL P1=1 valid=true
|
||||
[0001F6][UART1] INFO: Relay -> ON
|
||||
[0001F7][UART1] TX: "$OK,RL,1*00\r\n"
|
||||
[0001F8][UART3] INFO: RX len=15 "$DI,2*5A\r\n"
|
||||
[0001F9][UART3] INFO: CMD=DI P1=2 valid=true
|
||||
[0001FA][UART3] INFO: DI2 = HIGH
|
||||
[0001FB][UART3] TX: "$OK,DI,2,1*2B\r\n"
|
||||
[00020A][UART1] ERROR: CS mismatch recv=0xA1 calc=0x3C
|
||||
[00020B][UART1] TX: "$ERR,CS*XX\r\n"
|
||||
```
|
||||
|
||||
**十六进制Dump格式(DEBUG级别)**:
|
||||
|
||||
```
|
||||
[0001FC][UART1] DEBUG: HEX dump (16 bytes)
|
||||
[0001FC] 24 52 4C 2C 31 2A 32 46 0D 0A FF FF FF FF FF FF FF
|
||||
$ R L , 1 * 2 F CR LF
|
||||
```
|
||||
|
||||
#### 3.4.2 日志分级与过滤机制
|
||||
|
||||
**编译期分级**(通过`DEBUG_LOG_LEVEL`宏):
|
||||
|
||||
```c
|
||||
#define DEBUG_LEVEL_NONE 0 // 全部禁用
|
||||
#define DEBUG_LEVEL_ERROR 1 // 仅错误
|
||||
#define DEBUG_LEVEL_WARN 2 // 错误+警告
|
||||
#define DEBUG_LEVEL_INFO 3 // 错误+警告+信息
|
||||
#define DEBUG_LEVEL_DEBUG 4 // 全部
|
||||
|
||||
#ifndef DEBUG_LOG_LEVEL
|
||||
#define DEBUG_LOG_LEVEL DEBUG_LEVEL_INFO
|
||||
#endif
|
||||
|
||||
// 日志宏定义
|
||||
#if DEBUG_LOG_LEVEL >= DEBUG_LEVEL_ERROR
|
||||
#define LOG_ERROR(fmt, ...) UART2_Print_Printf("[%06X][%s] ERROR: " fmt "\r\n", \
|
||||
(unsigned int)HAL_GetTick(), port_name, ##__VA_ARGS__)
|
||||
#else
|
||||
#define LOG_ERROR(fmt, ...)
|
||||
#endif
|
||||
|
||||
#if DEBUG_LOG_LEVEL >= DEBUG_LEVEL_WARN
|
||||
#define LOG_WARN(fmt, ...) UART2_Print_Printf("[%06X][%s] WARN: " fmt "\r\n", \
|
||||
(unsigned int)HAL_GetTick(), port_name, ##__VA_ARGS__)
|
||||
#else
|
||||
#define LOG_WARN(fmt, ...)
|
||||
#endif
|
||||
|
||||
#if DEBUG_LOG_LEVEL >= DEBUG_LEVEL_INFO
|
||||
#define LOG_INFO(fmt, ...) UART2_Print_Printf("[%06X][%s] INFO: " fmt "\r\n", \
|
||||
(unsigned int)HAL_GetTick(), port_name, ##__VA_ARGS__)
|
||||
#else
|
||||
#define LOG_INFO(fmt, ...)
|
||||
#endif
|
||||
|
||||
#if DEBUG_LOG_LEVEL >= DEBUG_LEVEL_DEBUG
|
||||
#define LOG_DEBUG(fmt, ...) UART2_Print_Printf("[%06X][%s] DEBUG: " fmt "\r\n", \
|
||||
(unsigned int)HAL_GetTick(), port_name, ##__VA_ARGS__)
|
||||
#define LOG_HEXDUMP(data, len) Print_HexDump(port_name, data, len)
|
||||
#else
|
||||
#define LOG_DEBUG(fmt, ...)
|
||||
#define LOG_HEXDUMP(data, len)
|
||||
#endif
|
||||
```
|
||||
|
||||
#### 3.4.3 性能影响评估
|
||||
|
||||
| 指标 | 估算值 | 说明 |
|
||||
| --------- | ------------ | ------------------- |
|
||||
| Flash占用增量 | +4KB \~ +6KB | 路由层+日志系统 |
|
||||
| RAM占用增量 | +1KB \~ +2KB | 环形缓冲区(RX+TX各两个UART) |
|
||||
| CPU开销 | <5% | 日志仅在指令收发时产生,中断驱动 |
|
||||
| UART2带宽占用 | 峰值约30% | 假设每帧响应产生约50字节日志 |
|
||||
|
||||
**优化措施**:
|
||||
|
||||
- 使用`__attribute__((section(".flash.text")))`将日志函数放入Flash
|
||||
- 环形缓冲区大小根据实际数据量调整,避免过大
|
||||
- 日志输出异步化,不阻塞主流程
|
||||
|
||||
### 3.5 资源保护与并发控制
|
||||
|
||||
#### 3.5.1 共享资源访问冲突解决方案
|
||||
|
||||
**共享资源识别**:
|
||||
|
||||
| 共享资源 | 访问方 | 潜在冲突 |
|
||||
| --------------------------- | -------- | ------- |
|
||||
| `Relay_SetState()` | 多指令入口 | 同时控制继电器 |
|
||||
| `IO_Monitor_GetState()` | 多指令入口 | 读操作,可并发 |
|
||||
| `IO_Monitor_GetAllStates()` | 多指令入口 | 读操作,可并发 |
|
||||
| 环形缓冲区(TX/RX) | 中断 + 主循环 | 需要临界区保护 |
|
||||
|
||||
**解决方案:优先级继承 + 临界区**
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief 继电器控制(线程安全版本)
|
||||
* @note 内部使用临界区保护,防止并发访问
|
||||
* @param port_id: 来源端口(用于日志)
|
||||
* @param state: 目标状态
|
||||
* @retval 无
|
||||
*/
|
||||
void Relay_SetState_Safe(port_id_t port_id, bool state)
|
||||
{
|
||||
uint32_t irq_flags;
|
||||
__disable_irq(); // 进入临界区
|
||||
irq_flags = __get_PRIMASK(); // 保存中断状态
|
||||
|
||||
// 继电器操作
|
||||
Relay_SetState(state);
|
||||
|
||||
// 日志输出(允许中断嵌套,日志本身有保护)
|
||||
LOG_INFO("Relay -> %s (from %s)", state ? "ON" : "OFF",
|
||||
g_port_name_map[port_id]);
|
||||
|
||||
__set_PRIMASK(irq_flags); // 恢复中断状态
|
||||
__enable_irq(); // 退出临界区
|
||||
}
|
||||
```
|
||||
|
||||
**中断优先级配置建议**:
|
||||
|
||||
```c
|
||||
// 在SystemClock_Config()之后调用
|
||||
void Configure_UART_Priorities(void)
|
||||
{
|
||||
// STM32F103中断优先级(数字越小优先级越高)
|
||||
// 建议配置:
|
||||
// - UART1 (RF433): 优先级=5,子优先级=0(较高优先级)
|
||||
// - UART2 (DEBUG): 优先级=6,子优先级=0(中优先级)
|
||||
// - UART3 (RS485): 优先级=5,子优先级=1(与UART1同级别)
|
||||
// - Systick: 优先级=15(最低,保证系统心跳)
|
||||
|
||||
HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
|
||||
HAL_NVIC_SetPriority(USART2_IRQn, 6, 0);
|
||||
HAL_NVIC_SetPriority(USART3_IRQn, 5, 1);
|
||||
}
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## 4. 实施路线图
|
||||
|
||||
### 阶段1:架构验证与框架搭建(第1-2周)
|
||||
|
||||
**目标**:验证环形缓冲区机制和路由架构的可行性
|
||||
|
||||
| 任务 | 交付物 | 验收标准 |
|
||||
| --------------------------------- | ---- | ------------- |
|
||||
| 1.1 创建`multi_uart_router.c/h`基础框架 | 源码文件 | 编译通过,无语法错误 |
|
||||
| 1.2 实现UART1/UART3接收环形缓冲区 | 单元测试 | 收发数据一致,无丢失 |
|
||||
| 1.3 实现端口上下文表和初始化函数 | 函数实现 | 三个端口均可正确初始化 |
|
||||
| 1.4 实现`CmdRouter_FeedByte()`接口 | 函数实现 | 字节可正确路由到对应解析器 |
|
||||
|
||||
**关键里程碑代码片段**:
|
||||
|
||||
```c
|
||||
// multi_uart_router.h
|
||||
#ifndef __MULTI_UART_ROUTER_H
|
||||
#define __MULTI_UART_ROUTER_H
|
||||
|
||||
#include "usart.h"
|
||||
#include "cmd_parser.h"
|
||||
|
||||
typedef enum {
|
||||
PORT_UART1 = 0,
|
||||
PORT_UART2 = 1,
|
||||
PORT_UART3 = 2,
|
||||
PORT_COUNT
|
||||
} port_id_t;
|
||||
|
||||
void MultiUART_Init(void);
|
||||
void MultiUART_FeedByte(port_id_t port_id, uint8_t byte, uint32_t tick);
|
||||
void MultiUART_Task(void);
|
||||
void MultiUART_Send(port_id_t port_id, const uint8_t *data, uint16_t len);
|
||||
void MultiUART_SendString(port_id_t port_id, const char *str);
|
||||
|
||||
#endif
|
||||
```
|
||||
|
||||
### 阶段2:UART1/RF433接口实现(第3-4周)
|
||||
|
||||
**目标**:UART1能够接收指令并返回响应
|
||||
|
||||
| 任务 | 交付物 | 验收标准 |
|
||||
| ------------------------ | ------------------ | ---------------------------- |
|
||||
| 2.1 修改`main.c`中UART1中断回调 | 修改后main.c | 中断正确调用`MultiUART_FeedByte()` |
|
||||
| 2.2 实现UART1 TX环形缓冲区发送 | `MultiUART_Send()` | 响应正确发送到UART1 |
|
||||
| 2.3 实现响应路由功能 | 路由表 | UART1指令响应返回UART1 |
|
||||
| 2.4 全链路日志打印 | 日志输出 | UART2显示完整收发流程 |
|
||||
|
||||
**关键修改**:
|
||||
|
||||
```c
|
||||
// main.c 修改 - UART1中断回调
|
||||
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
|
||||
{
|
||||
if (huart->Instance == USART1) {
|
||||
MultiUART_FeedByte(PORT_UART1, rf433_uart_rx_tmp, HAL_GetTick());
|
||||
HAL_UART_Receive_IT(&huart1, &rf433_uart_rx_tmp, 1);
|
||||
}
|
||||
// ... 其他端口保持原样 ...
|
||||
}
|
||||
```
|
||||
|
||||
### 阶段3:UART3/485接口实现(第5-6周)
|
||||
|
||||
**目标**:UART3能够接收指令并返回响应
|
||||
|
||||
| 任务 | 交付物 | 验收标准 |
|
||||
| ------------------- | ------ | ------------ |
|
||||
| 3.1 实现UART3接收中断处理 | 中断处理 | 指令正确喂入解析器 |
|
||||
| 3.2 实现UART3 TX发送功能 | 发送函数 | 响应正确发送到UART3 |
|
||||
| 3.3 RS485方向控制(如果需要) | 方向控制逻辑 | 半双工切换正确 |
|
||||
| 3.4 多端口并发测试 | 测试报告 | 两端口同时收发正常 |
|
||||
|
||||
### 阶段4:集成测试与优化(第7-8周)
|
||||
|
||||
**目标**:系统稳定性和性能优化
|
||||
|
||||
| 任务 | 交付物 | 验收标准 |
|
||||
| ------------- | ------ | --------------- |
|
||||
| 4.1 多接口并发指令测试 | 测试报告 | 无竞争条件、无数据丢失 |
|
||||
| 4.2 大数据量压力测试 | 压力测试报告 | 1000帧连续收发无错误 |
|
||||
| 4.3 异常情况处理测试 | 异常测试报告 | 校验失败、超时等处理正确 |
|
||||
| 4.4 性能优化与内存调整 | 优化报告 | RAM<70%,CPU<80% |
|
||||
|
||||
### 阶段5:文档完善与交付(第9周)
|
||||
|
||||
| 任务 | 交付物 |
|
||||
| ----------- | --------------------- |
|
||||
| 5.1 API接口文档 | cmd\_router\_api.md |
|
||||
| 5.2 修改说明文档 | migration\_guide.md |
|
||||
| 5.3 测试验证报告 | validation\_report.md |
|
||||
| 5.4 最终代码归档 | 完整源码包 |
|
||||
|
||||
***
|
||||
|
||||
## 5. 测试验证计划
|
||||
|
||||
### 5.1 单元测试策略
|
||||
|
||||
**测试框架**:使用`unity`或自定义最小测试框架(嵌入式友好)
|
||||
|
||||
| 模块 | 测试用例 | 验证点 |
|
||||
| ----- | ------------------------ | --------------- |
|
||||
| 环形缓冲区 | `test_ring_push_pop()` | 数据先进先出,无损坏 |
|
||||
| 环形缓冲区 | `test_ring_overflow()` | 溢出时旧数据被覆盖 |
|
||||
| 环形缓冲区 | `test_ring_concurrent()` | 中断+主循环并发安全 |
|
||||
| 路由表 | `test_port_lookup()` | 端口ID正确映射到UART句柄 |
|
||||
| 响应构造 | `test_response_format()` | 校验和计算正确 |
|
||||
|
||||
### 5.2 集成测试用例
|
||||
|
||||
#### 5.2.1 多接口并发指令测试
|
||||
|
||||
**测试场景**:同时从UART1和UART3发送继电器控制指令
|
||||
|
||||
**测试步骤**:
|
||||
|
||||
1. UART1发送:`$RL,1*2F`(期望Relay开启)
|
||||
2. UART3发送:`$RL,0*2E`(期望Relay关闭)
|
||||
3. 间隔50ms交替发送10轮
|
||||
|
||||
**验收标准**:
|
||||
|
||||
- 每次操作后继电器状态与最后一条指令一致
|
||||
- 两个端口各自收到正确的响应帧
|
||||
- UART2日志完整记录所有收发过程
|
||||
|
||||
#### 5.2.2 大数据量压力测试
|
||||
|
||||
**测试场景**:连续快速发送1000帧指令
|
||||
|
||||
**测试步骤**:
|
||||
|
||||
1. UART1以10ms间隔连续发送DI查询指令
|
||||
2. 记录丢包率、错误率
|
||||
3. 监测RAM占用峰值
|
||||
|
||||
**验收标准**:
|
||||
|
||||
- 丢包率 < 0.1%
|
||||
- 无内存溢出
|
||||
- 平均响应时间 < 20ms
|
||||
|
||||
#### 5.2.3 异常情况处理测试
|
||||
|
||||
| 测试场景 | 输入 | 期望行为 |
|
||||
| ---------- | --------------------- | ------------------- |
|
||||
| 校验和错误 | `$RL,1*A1`(实际应为2F) | 返回`$ERR,CS*XX`,日志记录 |
|
||||
| 未知命令 | `$ABC*00` | 返回`$ERR,CMD*XX` |
|
||||
| 参数越界 | `$DI,5*XX`(通道1-4) | 返回`$ERR,PARAM*XX` |
|
||||
| 帧超时 | 发送`$RL`后等待2秒 | 解析器重置,日志输出timeout |
|
||||
| 端口同时发送相同指令 | UART1和UART3同时发`$ECHO` | 各自收到独立响应 |
|
||||
|
||||
### 5.3 验收标准
|
||||
|
||||
| 指标 | 目标值 | 测量方法 |
|
||||
| --------- | ---------- | ----------- |
|
||||
| 指令处理成功率 | ≥99.5% | 1000帧测试统计 |
|
||||
| 响应延迟 | ≤50ms(P99) | 示波器或时间戳统计 |
|
||||
| Flash占用增量 | ≤8KB | 编译后.map文件分析 |
|
||||
| RAM占用增量 | ≤2KB | 运行时内存分析 |
|
||||
| CPU空闲率 | ≥70% | Systick空闲计数 |
|
||||
|
||||
***
|
||||
|
||||
## 6. 风险评估与应对
|
||||
|
||||
### 6.1 技术风险
|
||||
|
||||
| 风险 | 概率 | 影响 | 应对措施 |
|
||||
| --------------------- | -- | -- | ----------------------------------------- |
|
||||
| **数据丢失**:高频率指令导致缓冲区溢出 | 中 | 高 | 1. 动态调整缓冲区大小2. 实现流量控制(XOFF/XON)3. 丢帧时日志记录 |
|
||||
| **响应延迟**:复杂指令阻塞发送 | 中 | 中 | 1. 指令处理异步化2. 预计算校验和3. DMA加速发送 |
|
||||
| **竞争条件**:多端口同时访问继电器 | 低 | 高 | 1. 临界区保护2. 命令队列化3. 最小切换间隔保护(已有) |
|
||||
| **内存碎片**:频繁分配/释放 | 低 | 中 | 1. 全部使用静态缓冲区2. 避免动态malloc |
|
||||
|
||||
### 6.2 资源风险
|
||||
|
||||
| 风险 | 概率 | 影响 | 应对措施 |
|
||||
| ---------------------- | -- | -- | --------------------------------------------------------------------------- |
|
||||
| **Flash不足**:代码量超过64KB | 低 | 高 | 1. 启用-O2优化2. 精简日志字符串3. 评估裁剪非必要功能 |
|
||||
| **RAM不足**:缓冲区+解析器上下文超支 | 低 | 高 | 1. 缓冲区实际需求计算: RX: 3×128=384B TX: 2×256=512B 解析器: 3×128B=384B 总计约1.3KB(余量充足) |
|
||||
| **中断风暴**:高频率字节接收导致系统假死 | 低 | 高 | 1. 配置合适的中断优先级2. 使用DMA分担CPU负载 |
|
||||
|
||||
### 6.3 应对措施优先级
|
||||
|
||||
```
|
||||
P0(立即处理):
|
||||
├─ 临界区保护实现(防止竞争条件)
|
||||
└─ 环形缓冲区溢出处理(日志+计数)
|
||||
|
||||
P1(本阶段完成):
|
||||
├─ 中断优先级配置
|
||||
└─ 内存使用量测量验证
|
||||
|
||||
P2(集成测试阶段):
|
||||
├─ 压力测试暴露潜在问题
|
||||
└─ 性能优化调参
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## 7. 附录
|
||||
|
||||
### 附录A:关键数据结构定义
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief 接收环形缓冲区
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t buffer[UART_RX_BUFFER_SIZE];
|
||||
volatile uint16_t head; /**< 写入索引 */
|
||||
volatile uint16_t tail; /**< 读取索引 */
|
||||
volatile uint16_t count; /**< 有效数据计数 */
|
||||
} uart_rx_ring_t;
|
||||
|
||||
/**
|
||||
* @brief 发送环形缓冲区
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t buffer[UART_TX_BUFFER_SIZE];
|
||||
volatile uint16_t head;
|
||||
volatile uint16_t tail;
|
||||
volatile uint16_t count;
|
||||
volatile bool is_sending;
|
||||
volatile uint16_t overflow_count;
|
||||
} uart_tx_ring_t;
|
||||
|
||||
/**
|
||||
* @brief 端口上下文
|
||||
*/
|
||||
typedef struct {
|
||||
UART_HandleTypeDef *huart;
|
||||
const char *name;
|
||||
uart_rx_ring_t rx_ring;
|
||||
uart_tx_ring_t tx_ring;
|
||||
parser_context_t parser;
|
||||
uint8_t rx_tmp;
|
||||
uint32_t rx_count;
|
||||
uint32_t error_count;
|
||||
} uart_port_context_t;
|
||||
|
||||
/**
|
||||
* @brief 带路由信息的指令帧
|
||||
*/
|
||||
typedef struct {
|
||||
cmd_frame_t frame;
|
||||
port_id_t source_port;
|
||||
uint32_t recv_tick;
|
||||
} routed_cmd_frame_t;
|
||||
```
|
||||
|
||||
### 附录B:接口API文档
|
||||
|
||||
#### B.1 CmdRouter模块
|
||||
|
||||
| 函数 | 原型 | 说明 |
|
||||
| --------------------------- | -------------------------------------------------------------------------------- | ------------ |
|
||||
| `CmdRouter_Init` | `void CmdRouter_Init(void)` | 初始化所有端口上下文 |
|
||||
| `CmdRouter_FeedByte` | `void CmdRouter_FeedByte(port_id_t port, uint8_t byte, uint32_t tick)` | 向指定端口喂入数据 |
|
||||
| `CmdRouter_Task` | `void CmdRouter_Task(void)` | 主循环调用,处理就绪指令 |
|
||||
| `CmdRouter_SendResponse` | `void CmdRouter_SendResponse(port_id_t port, const uint8_t *data, uint16_t len)` | 发送原始响应 |
|
||||
| `CmdRouter_SendResponseFmt` | `void CmdRouter_SendResponseFmt(port_id_t port, const char *fmt, ...)` | 发送格式化响应 |
|
||||
|
||||
#### B.2 DebugLog模块
|
||||
|
||||
| 宏 | 说明 |
|
||||
| ------------------------ | ----------- |
|
||||
| `LOG_ERROR(fmt, ...)` | 错误级别日志 |
|
||||
| `LOG_WARN(fmt, ...)` | 警告级别日志 |
|
||||
| `LOG_INFO(fmt, ...)` | 信息级别日志 |
|
||||
| `LOG_DEBUG(fmt, ...)` | 调试级别日志 |
|
||||
| `LOG_HEXDUMP(data, len)` | 十六进制内存 dump |
|
||||
|
||||
#### B.3 回调接口
|
||||
|
||||
```c
|
||||
/**
|
||||
* @brief 指令执行完成回调(由CmdRouter内部调用)
|
||||
* @param frame: 带路由信息的指令帧
|
||||
* @retval 无
|
||||
*/
|
||||
void App_OnCommandReceived(const routed_cmd_frame_t *frame);
|
||||
```
|
||||
|
||||
### 附录C:响应格式规范
|
||||
|
||||
**成功响应**:`$OK[,content]*CS\r\n`
|
||||
|
||||
- 示例:`$OK,RL,1*00\r\n`(继电器控制成功)
|
||||
- 示例:`$OK,DI,1101*2B\r\n`(四路DI状态)
|
||||
|
||||
**错误响应**:`$ERR,err_code*CS\r\n`
|
||||
|
||||
- err\_code: PARAM(参数错误)、CS(校验和错误)、CMD(未知命令)、TIMEOUT(超时)
|
||||
- 示例:`$ERR,CS*XX\r\n`
|
||||
|
||||
**事件上报**:`$DI_EVENT,channel,state*CS\r\n`
|
||||
|
||||
- 示例:`$DI_EVENT,1,1*A5\r\n`(通道1变为高电平)
|
||||
|
||||
***
|
||||
|
||||
## 文档版本历史
|
||||
|
||||
| 版本 | 日期 | 作者 | 变更说明 |
|
||||
| --- | ---------- | --- | ------ |
|
||||
| 1.0 | 2026-03-27 | 架构师 | 初始版本创建 |
|
||||
|
||||
***
|
||||
|
||||
**文档结束**
|
||||
Reference in New Issue
Block a user