修复 P2 全部 10 项:错误传播/竞态/门控/文档失真
待办池 docs/待处理内容.md §3.2 的 10 个 P2 全部闭环;逐项落代码 + 注释说明。 P2-1 cfg_file: 受管键行超 CFG_LINE_TEST_MAX 拒绝保存(防重复键行) P2-2 ch395f: DMA 失败传播状态 + 锁存通道故障 + 读不拷不可信数据 P2-7 net_select: LISTENING 不再恒判可读(消除 select 忙轮询地雷) P2-8 time_sync: 核实 L3 pending 改写已闭环(net_recv_to 分片 + 总预算) P2-9 sys_log: 挂载改幂等 + 卷锁,defaultTask 2s 重试建 /log P2-10 sys_log: s_read_raw 纳入卷锁 try(读者/写者 unlink 互斥) P2-11 net_task: RECONFIG 改 osEventFlagsWait 原子取走 + 清除 P2-12 app_cfg: APP_*_ENABLE 如实标注为未接线占位,删“开关”说法 P2-13 freertos: 产品构建 vTaskDelete(testTask) 回收 8KB CCM 栈 P2-15 download.bat: 先删旧日志 + 增“未产出日志”判定 P2-13 用 USER CODE 区回收而非生成区 #if 门控:生成区改动会被 CubeMX 重生成覆盖,本方案重生成不丢。P2-12 因产品定义 B0 未定、APP_NET_ENABLE 无单一接线点,按修复建议备选(删“开关”说法)处理。 验证:@build 0 错 0 警;主机单测 watchdog 39/39 + sys_clock 100/100 + cfg_file 149/149(新增 TC-CFG-044);pre-commit --all-files 17/17。
This commit is contained in:
+42
-22
@@ -10,6 +10,9 @@
|
||||
* 2026-07-18 王建锋 改进write_send_buf/read_recv_buf使用SPI2 DMA批量传输
|
||||
* 2026-10-02 王建锋 E-34:3 处字节级 SPI 调用由 HAL_MAX_DELAY 改有限超时(100ms)
|
||||
* + 返回值检查 + 故障锁存(沿程只记一条日志);原 SPI 异常时调用方永久挂死
|
||||
* 2026-10-04 王建锋 P2-2:ch395f_spi_dma_transfer 由 void 改为返回状态(0 成功/-1 失败),
|
||||
* DMA 初始化失败/超时中止均锁存通道故障;read_recv_buf 在 DMA 失败时不再把
|
||||
* rx_buf 内容拷入调用方缓冲(改零填充),消除"失败当成功"的静默数据损坏
|
||||
*/
|
||||
|
||||
/* 头文件包含区 - 先包含系统头文件,再包含自定义头文件 */
|
||||
@@ -40,7 +43,7 @@
|
||||
* 静态变量定义区 - DMA传输相关
|
||||
*/
|
||||
static volatile uint8_t s_spi2_dma_done = 0U; /* DMA传输完成标志 */
|
||||
static uint8_t s_spi_byte_fault = 0U; /* E-34:字节级SPI故障锁存(超时/出错置位,MCU复位清除) */
|
||||
static uint8_t s_spi_fault = 0U; /* E-34/P2-2:SPI 通道故障锁存(字节级或 DMA 级超时/出错置位,MCU复位清除) */
|
||||
|
||||
__attribute__((aligned(4)))
|
||||
static uint8_t s_spi2_dma_tx_buf[CH395F_DMA_BUF_SIZE]; /* DMA发送缓冲区 */
|
||||
@@ -52,8 +55,8 @@ static uint8_t s_spi2_dma_rx_buf[CH395F_DMA_BUF_SIZE]; /* DMA接收缓冲区 */
|
||||
*/
|
||||
static void ch395f_spi_begin(void);
|
||||
static void ch395f_spi_end(void);
|
||||
static void ch395f_spi_dma_transfer(uint16_t len);
|
||||
static void ch395f_spi_byte_fault_set(void);
|
||||
static int ch395f_spi_dma_transfer(uint16_t len);
|
||||
static void ch395f_spi_fault_set(const char *kind);
|
||||
|
||||
/*
|
||||
* 私有函数定义区
|
||||
@@ -82,31 +85,34 @@ static void ch395f_spi_end(void) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:锁存一次字节级SPI故障(E-34:仅首条故障记日志)
|
||||
* 入口参数:无
|
||||
* 函数功能:锁存一次 SPI 通道故障(E-34/P2-2:仅首条故障记日志)
|
||||
* 入口参数:kind - 故障类别标签("byte"/"dma-hal"/"dma-timeout") const char*
|
||||
* 返回值:无
|
||||
* 限定条件:无
|
||||
* 函数说明:任一字节级SPI调用超时/失败时调用;置位 s_spi_byte_fault 并记一条日志,
|
||||
* 函数说明:任一字节级或 DMA 级 SPI 调用超时/失败时调用;置位 s_spi_fault 并记一条日志,
|
||||
* 后续故障静默(防串口刷屏);上层经 ch395f_spi_byte_fault() 查询
|
||||
*/
|
||||
static void ch395f_spi_byte_fault_set(void) {
|
||||
if (s_spi_byte_fault == 0U) {
|
||||
s_spi_byte_fault = 1U;
|
||||
DBG_ERROR("SPI byte op fail, channel latched");
|
||||
static void ch395f_spi_fault_set(const char *kind) {
|
||||
if (s_spi_fault == 0U) {
|
||||
s_spi_fault = 1U;
|
||||
DBG_ERROR("SPI channel fault latched: %s", kind);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:DMA全双工传输并等待完成
|
||||
* 入口参数:len - 传输长度(字节) uint16_t 1 - CH395F_DMA_BUF_SIZE
|
||||
* 返回值:0 - 传输完成且无错误 -1 - 传输失败(HAL 启动失败 / 超时中止)
|
||||
* 限定条件:1. s_spi2_dma_tx_buf中已填充待发送数据
|
||||
* 2. 需确保DMA传输缓冲区已对齐到4字节边界
|
||||
* 函数说明:1. 发送s_spi2_dma_tx_buf内容,同时接收数据存入s_spi2_dma_rx_buf
|
||||
* 2. 阻塞等待DMA完成回调置标志位
|
||||
* 3. 超时(1秒)时强制中止SPI DMA并打印错误信息
|
||||
* 4. 传输完成后检查SPI错误码并清除溢出标志
|
||||
* 5. P2-2(2026-10-04):失败路径锁存通道故障并返回 -1,调用方据此
|
||||
* 判定 rx_buf 内容不可信,不再"失败当成功"继续消费
|
||||
*/
|
||||
static void ch395f_spi_dma_transfer(uint16_t len) {
|
||||
static int ch395f_spi_dma_transfer(uint16_t len) {
|
||||
HAL_StatusTypeDef status;
|
||||
uint32_t tickstart;
|
||||
volatile uint32_t loop_cnt = 0U;
|
||||
@@ -119,7 +125,8 @@ static void ch395f_spi_dma_transfer(uint16_t len) {
|
||||
(int)hspi2.State,
|
||||
hspi2.ErrorCode,
|
||||
(int)hspi2.hdmarx->State);
|
||||
return;
|
||||
ch395f_spi_fault_set("dma-hal");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tickstart = HAL_GetTick();
|
||||
@@ -142,7 +149,8 @@ static void ch395f_spi_dma_transfer(uint16_t len) {
|
||||
SPI2->CR1, SPI2->CR2, SPI2->SR);
|
||||
/* 强制中止SPI2 DMA状态,避免死锁 */
|
||||
HAL_SPI_Abort(&hspi2);
|
||||
return;
|
||||
ch395f_spi_fault_set("dma-timeout");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +160,8 @@ static void ch395f_spi_dma_transfer(uint16_t len) {
|
||||
hspi2.ErrorCode, (int)hspi2.State);
|
||||
__HAL_SPI_CLEAR_OVRFLAG(&hspi2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -200,7 +210,7 @@ uint8_t ch395f_write_cmd(uint8_t cmd) {
|
||||
/* 通过SPI发送命令并接收回显(E-34:有限超时+返回值检查;失败返回0xFF占位并锁存故障) */
|
||||
if (HAL_SPI_TransmitReceive(&hspi2, &cmd, &rx_data, 1,
|
||||
CH395F_SPI_BYTE_TIMEOUT_MS) != HAL_OK) {
|
||||
ch395f_spi_byte_fault_set();
|
||||
ch395f_spi_fault_set("byte");
|
||||
}
|
||||
return rx_data;
|
||||
}
|
||||
@@ -217,7 +227,7 @@ uint8_t ch395f_write_data(uint8_t data) {
|
||||
/* 通过SPI发送数据并接收回显(E-34:有限超时+返回值检查) */
|
||||
if (HAL_SPI_TransmitReceive(&hspi2, &data, &rx_data, 1,
|
||||
CH395F_SPI_BYTE_TIMEOUT_MS) != HAL_OK) {
|
||||
ch395f_spi_byte_fault_set();
|
||||
ch395f_spi_fault_set("byte");
|
||||
}
|
||||
return rx_data;
|
||||
}
|
||||
@@ -235,20 +245,20 @@ uint8_t ch395f_read_data(void) {
|
||||
/* 发送0xFF作为时钟源,读取芯片返回数据(E-34:有限超时+返回值检查) */
|
||||
if (HAL_SPI_TransmitReceive(&hspi2, &tx_dummy, &rx_data, 1,
|
||||
CH395F_SPI_BYTE_TIMEOUT_MS) != HAL_OK) {
|
||||
ch395f_spi_byte_fault_set();
|
||||
ch395f_spi_fault_set("byte");
|
||||
}
|
||||
return rx_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:查询字节级SPI故障锁存(E-34)
|
||||
* 返回值:0 - 正常 1 - 曾发生字节级SPI超时/错误
|
||||
* 函数功能:查询 SPI 通道故障锁存(E-34 / P2-2)
|
||||
* 返回值:0 - 正常 1 - 曾发生 SPI 超时/错误(字节级或 DMA 级)
|
||||
* 限定条件:无
|
||||
* 函数说明:锁存式(不自动清除,MCU 复位前一直有效);上层据此判定
|
||||
* "SPI 通道异常"(区别于"无芯片"/"PHY 未链接");仅首条故障记日志
|
||||
*/
|
||||
uint8_t ch395f_spi_byte_fault(void) {
|
||||
return s_spi_byte_fault;
|
||||
return s_spi_fault;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1173,9 +1183,10 @@ void ch395f_write_send_buf(uint8_t sock, uint8_t *p_data, uint16_t len) {
|
||||
/* 数据同步屏障:确保memcpy写入对DMA可见 */
|
||||
__DSB();
|
||||
|
||||
/* CS低 → DMA批量传输 → CS高 */
|
||||
/* CS低 → DMA批量传输 → CS高(P2-2:失败已由 transfer 锁存通道故障并记日志,
|
||||
* 调用方经 ch395f_spi_byte_fault() 查询;此处无后续处理,失败即放弃本次发送) */
|
||||
ch395f_spi_begin();
|
||||
ch395f_spi_dma_transfer(total);
|
||||
(void)ch395f_spi_dma_transfer(total);
|
||||
ch395f_spi_end();
|
||||
}
|
||||
|
||||
@@ -1193,6 +1204,7 @@ void ch395f_write_send_buf(uint8_t sock, uint8_t *p_data, uint16_t len) {
|
||||
*/
|
||||
void ch395f_read_recv_buf(uint8_t sock, uint8_t *p_data, uint16_t len) {
|
||||
uint16_t total;
|
||||
int rc;
|
||||
|
||||
/* 检查输入参数合法性 */
|
||||
if (p_data == NULL) {
|
||||
@@ -1221,9 +1233,17 @@ void ch395f_read_recv_buf(uint8_t sock, uint8_t *p_data, uint16_t len) {
|
||||
|
||||
/* CS低 → DMA全双工传输(发送命令头 + 接收数据) → CS高 */
|
||||
ch395f_spi_begin();
|
||||
ch395f_spi_dma_transfer(total);
|
||||
rc = ch395f_spi_dma_transfer(total);
|
||||
ch395f_spi_end();
|
||||
|
||||
/* P2-2(2026-10-04):DMA 失败时 rx_buf 内容不可信,禁止拷入调用方缓冲
|
||||
* (否则把总线残留/半截数据当有效载荷 → 静默数据损坏);改零填充并返回,
|
||||
* 调用方据 ch395f_spi_byte_fault() 判定通道异常。 */
|
||||
if (rc != 0) {
|
||||
memset(p_data, 0, len);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 从DMA接收缓冲区偏移4处拷贝有效数据到用户缓冲区 */
|
||||
memcpy(p_data, &s_spi2_dma_rx_buf[4], len);
|
||||
}
|
||||
|
||||
@@ -302,8 +302,8 @@ uint8_t ch395f_write_data(uint8_t data);
|
||||
uint8_t ch395f_read_data(void);
|
||||
|
||||
/*
|
||||
* 函数功能:查询字节级SPI故障锁存(E-34)
|
||||
* 返回值:0 - 正常 1 - 曾发生字节级SPI超时/错误
|
||||
* 函数功能:查询 SPI 通道故障锁存(E-34 / P2-2)
|
||||
* 返回值:0 - 正常 1 - 曾发生 SPI 超时/错误(字节级或 DMA 级)
|
||||
* 限定条件:无
|
||||
* 函数说明:锁存式(MCU 复位前一直有效);上层据此判定"SPI 通道异常"
|
||||
* (区别于"无芯片"/"PHY 未链接");仅首条故障记日志
|
||||
|
||||
@@ -241,10 +241,16 @@ int net_poll_events(net_pollfd_t *fds, int nfds, int timeout) {
|
||||
* 函数功能:检查指定 Socket 当前是否可读
|
||||
* 入口参数:sockfd - 待检查的 Socket 描述符 int 0 - NET_MAX_SOCKETS-1
|
||||
* 出口参数:无
|
||||
* 返回值:1 - 可读(监听态有新连接,或已连接态接收缓冲非空);0 - 不可读或描述符无效 int
|
||||
* 返回值:1 - 可读(接收缓冲非空);0 - 不可读或描述符无效 int
|
||||
* 限定条件:net_init() 已调用
|
||||
* 函数说明:1. 监听态 Socket 视为可读(存在可 accept 的新连接)
|
||||
* 2. 已建立连接且接收缓冲区长度大于 0 时视为可读
|
||||
* 函数说明:1. P2-7(2026-10-04):LISTENING 态**不再**恒判可读。本网络层无
|
||||
* "待 accept 的挂起连接队列"——新连接到达时 CONNECT 事件把该 socket
|
||||
* 自身直接转为 ESTABLISHED(handle_connect_event:"监听自身转数据通道"),
|
||||
* 故监听态恒无数据可读。原实现对 LISTENING 恒返回 1,会使
|
||||
* select()/poll() 在监听 socket 上永久立即就绪(忙轮询地雷;当前无
|
||||
* 调用方,属潜在地雷)。连接到达经 NET_EVENT_CONNECTED 回调通知。
|
||||
* 2. 其余态:readable 由 net_poll 在 RECV_OK 时置位、net_recv_locked
|
||||
* 读空后清零。避免应用任务直接访问 CH395F SPI(见 Trap 16)
|
||||
*/
|
||||
static int check_socket_readable(int sockfd) {
|
||||
const net_sock_t *p_sock;
|
||||
@@ -254,13 +260,12 @@ static int check_socket_readable(int sockfd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 监听 Socket 有新连接时可读 */
|
||||
/* 监听态恒不可读(见上方 P2-7 说明) */
|
||||
if (NET_SOCK_STATE_LISTENING == p_sock->state) {
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 已建立连接或 UDP 等有数据可读:readable 由 net_poll 在 RECV_OK 时置位、
|
||||
* net_recv_locked 读空后清零。避免应用任务直接访问 CH395F SPI(见 Trap 16) */
|
||||
/* 有数据可读 */
|
||||
if (p_sock->readable) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user