78248a1cdf
前一笔立契约与门禁(`Drivers/BSP/bsp_err.h` + `bsp-err` 钩子 + RS485 试点);这一笔按用户
指定顺序 C3 → C1 → C2 把三个模块真正收编,并让存储失败第一次变得可读、可分类。
**C3 · SD2506 整模块(11 个公共 API + 6 个内部 helper)**:三枚私有宏 `SD2506_OK`/
`SD2506_ERROR`/`SD2506_I2C_ERROR` **删除而不是留别名**——留别名等于新代码还能用旧形状,
把迁移洗掉;编译器替我们找全所有调用点。新增 `sd2506_err_from_hal()`,**把 I2C 超时从
"和没人应答共用一个 −2"里拆出来归成 `BSP_ERR_TIMEOUT`(可重试)**——SD2506 是启动链第一站、
I2C1 上电时序敏感,这是本波唯一真正的语义改善,不是改名。`sd2506_init` 的 charge 寄存器
**回读校验不符**原返参数类错误,改归 `BSP_ERR_IO`(那不是调用方的错)。
**C1 · 同形两义**:`GD5F_BUSY_TIMEOUT` → `GD5F_WAIT_EXPIRED`(**值 −2 不变**,纯消"名字叫
TIMEOUT 其实是返回码")。**GD5F 私有码集整体收编刻意没做**:169 处引用里 48 处在板级套件、
还要动 Trap 记录用词,属另一波带真机+套件回归的工作,不夹带。TPAFE5160 收编 23 处:
`BUSY_TIMEOUT → BSP_ERR_TIMEOUT`、`ERROR → BSP_ERR_PARAM`(**同值 −1,行为零变化**)、
`FRSTDATA_ERR → BSP_ERR_NO_DEVICE`(E-44 的本义就是"芯片没驱动 DB 总线",归到器件无响应比
私有 −3 有用,且避开与 `BSP_ERR_TIMEOUT` 撞值)。实测 `tpafe5160.o`/`gd5f2gq5ue.o` 的 Code
增量为 **0**——改名不产生一条指令。
⚠ **`tpafe5160_snapshot()` 刻意不套成败码**:它是 1=有新帧 / 0=无新帧 / −1=参数错 的三态
**状态**返回,0 不是错误而是"这一拍没有新数据",强行收编会把慢消费者的正常路径变成失败。
顺带订正我上一版在 `bsp_err.h` 的断言——"本层只有 `net_*` 把数量与错误挤同一返回值"是错的,
`snapshot`/`adc_input_poll` 就是第二族;两族现都已写明并各注保留理由。
**C2 · FTL 坍缩点分类**:`ftl_flush_cache`/`ftl_read_page` 不再把 dhara 失败压成
`GD5F_ERROR`,
按**处置差异**分三类,并在最早同时知道 LPN 与原始码的位置就地打印:`ECC`/`TOO_BAD` →
`BSP_ERR_CRC`(页级,重试或换页,不代表卷坏);`JOURNAL_FULL`/`MAP_FULL` →
`BSP_ERR_NO_SPACE`(介质健康但块池/日志见底,此时重格式化只是把问题推回去);
`BAD_BLOCK`/`RECOVER`/`CORRUPT_MAP`/越界 → `BSP_ERR_IO`(完整性故障,保卷 + 上报,不许自愈
重格式化)。这与 **E-31** 是同一条纪律的两端。可读名用自建短表:**本仓 dhara 没有编
`error.c`,用 `dhara_strerror()` 会直接链接失败**。`disk_read`/`disk_write` 仍回
`RES_ERROR`
(FatFs 契约);要让 defaultTask 卷健康探针按类分支还差跨调用存活的快照 → C4 待还。
**两次靠核实拦住的自我错误**:① 我一度认定 `DBG_ERROR("... err=%d", err)` 是把结构体塞进
varargs 的格式 bug——核 `Lib/dhara/error.h` 后确认 `dhara_error_t` 是 `enum`(标量),那行日志
本来合法,改了反而是引入错误;② `bsp-err` 一次 `replace_all` 越界把 8 处 **GD5F 码空间**的
判据改成 `!= BSP_OK`(数值同为 0、无行为变化),但那正是"把两个码空间混起来",已逐一还原。
`cppcheck` 亦抓到 `cls` 声明作用域过宽,已收进失败分支。
**验证**:`@build` 0 错 0 警告(`Code` 213,648、RO 18,860、`ER_IROM1` 232,508 B /
22.2%、
`ROM 合计` 233,696;**RAM 三项零增量**);`@test` PASS(692 ms,烧入戳 `Oct 23 01:35:32`)
且逐条读到被改模块的锚点:`SD2506 init: OK (ret=0)` 与 ID/时间/温度/电池四条读回正常、
`ADC init: OK`、`NAND init: OK`、`Map resumed: 14094/97504`、`adc=` 帧计数持续递增(证明
snapshot 三态未被改坏)、`[ERR]`/Fault/FAIL 0 条。主机单测:系统时钟 100/100、dhara NAND
移植层 149/149(两者都因本次改动需补 `Drivers/BSP` include 路径,补后在钩子内实跑 Passed)。
`pre-commit run` 全量 RC=0(20 个实跑钩子 Passed);`bsp-err` 基线 **19 → 17**(还掉两个
`*_BUSY_TIMEOUT = -2`)、`--prune` 干净;`doc-links --all --sections-strict` 147 篇
0 问题;
`id-check --strict`、`layer-deps`、`mem-numbers`、`mem-budget`、`ui-golden` 全过;33
个文件
字节卫生 0 异常(无 CR / 无 U+FFFD / 无行尾空格 / 均有尾换行)。
**口径与数字联动**:新增 `docs/架构设计/STM32F4-Base存储架构说明.md` §10.4(三类码与处置
理由);`pre-commit使用说明.md` §2.27 与 `AGENTS.md` 基线计数 19 → 17;TPAFE README、
GD5F 测试指南与 Trap 记录均**保留旧名留痕**以便对旧日志;配图桶按 .map 对象级增量重算
(`dhara/FTL` 10,931 → 11,222,+291 全部落在 `nand_ftl.o`;应用代码/UI +36 落在
`app_main.o`;其余散量入"其他"7,557;桶和 = Code+RO 断言通过、文本溢出 0);
`ram_baseline --update/--write`、`ram_report_20261023.md` 快照、报告 §1.3/§5.1/§5.4
与
部署文档 §3.3 数字全部同步;报告 §0.4/§9 的 S-5 状态改为 🟡 主体已落地。
132 lines
5.4 KiB
C
132 lines
5.4 KiB
C
/*
|
||
* 模块名称:RS-485 半双工通信驱动
|
||
* 模块功能:RS-485 半双工通信驱动,封装任意 UART 外设实现 485 方向控制,
|
||
* 提供阻塞发送、中断接收(IDLE 空闲帧检测)、DMA 发送等功能。
|
||
* 参考 ST AN3070 应用笔记《IO-Link master: USART communication》
|
||
* 及 controllerstech.com RS485 教程
|
||
* 适用平台:STM32F4 系列
|
||
* 作者:王建锋
|
||
* 创建日期:2026-07-18
|
||
* 修改记录:
|
||
* 2026-07-18 王建锋 创建初始版本
|
||
*/
|
||
|
||
#ifndef RS485_H
|
||
#define RS485_H
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
#include "main.h"
|
||
#include "bsp_err.h" /* bsp_err_t / BSP_WAIT_*:对外只报统一错误码(2026-10-22 试点) */
|
||
|
||
/*
|
||
* 常量定义
|
||
*/
|
||
#define RS485_TIMEOUT_DEFAULT 1000U /* 默认发送超时(ms) */
|
||
#define RS485_RX_BUF_SIZE 256U /* 接收缓冲区大小 */
|
||
#define RS485_TX_MAX_LEN 256U /* 单帧发送长度上限(防御脏长度进 UART,2026-09-24 加固) */
|
||
|
||
/*
|
||
* RS-485 句柄结构体
|
||
* 将任意 UART + 任意 GPIO 组合绑定为一个 RS-485 半双工通信实例
|
||
*/
|
||
typedef struct {
|
||
UART_HandleTypeDef *huart; /* UART 外设句柄 */
|
||
GPIO_TypeDef *dir_port; /* 方向控制 GPIO 端口 */
|
||
uint16_t dir_pin; /* 方向控制 GPIO 引脚号 */
|
||
volatile uint16_t rx_size; /* 最近一次接收的字节数 */
|
||
uint8_t rx_buf[RS485_RX_BUF_SIZE]; /* 接收缓冲区 */
|
||
} rs485_handle_t;
|
||
|
||
/*
|
||
* 公共函数声明
|
||
*/
|
||
|
||
/*
|
||
* 函数功能:初始化 RS-485 句柄,绑定 UART 和方向控制引脚
|
||
* 入口参数:handle - RS-485 句柄指针
|
||
* huart - UART 外设句柄指针(已由 CubeMX 初始化)
|
||
* dir_port - 方向控制 GPIO 端口(如 GPIOD)
|
||
* dir_pin - 方向控制 GPIO 引脚号(如 GPIO_PIN_0)
|
||
* 返回值:无
|
||
* 限定条件:UART 和 GPIO 须先由 CubeMX 完成初始化
|
||
* 函数说明:初始化后默认处于接收状态(DE 引脚拉低),并自动启动 IDLE
|
||
* 中断接收,无需再手动调用 rs485_receive_start。
|
||
*/
|
||
void rs485_init(rs485_handle_t *p_handle,
|
||
UART_HandleTypeDef *p_huart,
|
||
GPIO_TypeDef *p_dir_port,
|
||
uint16_t dir_pin);
|
||
|
||
/*
|
||
* 函数功能:阻塞方式发送数据
|
||
* 入口参数:handle - RS-485 句柄指针
|
||
* data - 待发送数据缓冲区
|
||
* len - 待发送字节数
|
||
* timeout - 发送超时(ms),传 0 使用 RS485_TIMEOUT_DEFAULT
|
||
* 返回值:BSP_OK - 成功;BSP_ERR_PARAM - 参数非法;BSP_ERR_TIMEOUT - 发送超时(可重试,
|
||
* 见 BSP_ERR_CAN_RETRY);BSP_ERR_IO - 总线错误
|
||
* 限定条件:在主循环或任务中调用,不可在 UART 中断中调用;timeout 单位是毫秒,
|
||
* 传 0 用 RS485_TIMEOUT_DEFAULT(不许拿 0xFFFFFFFF 当"无限",用 BSP_WAIT_FOREVER)
|
||
* 函数说明:发送前自动拉高 DE,发送完成后等待 TC 标志再拉低 DE,
|
||
* 确保最后一字节完全移出后再切换到接收状态
|
||
*/
|
||
bsp_err_t rs485_transmit(rs485_handle_t *p_handle,
|
||
const uint8_t *p_data,
|
||
uint16_t len,
|
||
uint32_t timeout);
|
||
|
||
/*
|
||
* 函数功能:启动中断方式接收(IDLE 空闲帧检测)
|
||
* 入口参数:handle - RS-485 句柄指针
|
||
* 返回值:BSP_OK - 已启动;BSP_ERR_PARAM - 句柄空;BSP_ERR_BUSY - 接收通道正被占用
|
||
* BSP_ERR_STATE - 接收通道不处于可启动状态
|
||
* 限定条件:UART 须已开启全局中断,NVIC 中须使能对应 UART 中断
|
||
* 函数说明:使用 HAL_UARTEx_ReceiveToIdle_IT 实现变长帧接收,
|
||
* 接收缓冲固定使用句柄内部的 rx_buf(RS485_RX_BUF_SIZE)。
|
||
* 当 UART 总线空闲超过 1 个字符时间后硬件触发 IDLE 中断,
|
||
* HAL 自动调用 HAL_UARTEx_RxEventCallback 并返回已接收字节数。
|
||
* 用户须在回调中重新调用本函数重新开启接收。
|
||
*/
|
||
bsp_err_t rs485_receive_start(rs485_handle_t *p_handle);
|
||
|
||
/*
|
||
* 函数功能:将 RS-485 句柄与 HAL 回调关联
|
||
* 入口参数:handle - RS-485 句柄指针
|
||
* 返回值:无
|
||
* 限定条件:须在 HAL_UART_RxCpltCallback / HAL_UARTEx_RxEventCallback 中调用
|
||
* 函数说明:将 HAL 回调中接收到的数据大小回写到句柄的 rx_size 字段
|
||
*/
|
||
void rs485_rx_set_size(rs485_handle_t *p_handle, uint16_t size);
|
||
|
||
/*
|
||
* 函数功能:获取最近一次接收的字节数
|
||
* 入口参数:handle - RS-485 句柄指针
|
||
* 返回值:最近一次接收的字节数
|
||
* 限定条件:须在 HAL_UARTEx_RxEventCallback 触发后调用
|
||
*/
|
||
uint16_t rs485_rx_get_size(const rs485_handle_t *p_handle);
|
||
|
||
/*
|
||
* 全局 RS-485 实例(由 rs485.c 定义,应用层通过 extern 引用)。
|
||
* 接收缓冲已内聚到 rs485_handle_t 的 rx_buf 字段,随实例统一管理。
|
||
*/
|
||
extern rs485_handle_t g_rs485;
|
||
|
||
/*
|
||
* 应用层接收钩子:RS-485 收到完整帧(IDLE 空闲帧)后由驱动回调调用,
|
||
* 传入本次接收缓冲区与有效字节数。默认弱实现为空,应用可重定义此函数
|
||
* 处理业务数据。
|
||
* 注意:此函数在 UART 中断上下文执行,应尽快拷贝数据后返回,不要在内部
|
||
* 调用阻塞式 API(如 rs485_transmit 会切换 DE 并等待 TC)。
|
||
*/
|
||
void rs485_on_rx(uint8_t *buf, uint16_t size);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* RS485_H */
|