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 状态改为 🟡 主体已落地。
434 lines
16 KiB
C
434 lines
16 KiB
C
/*
|
||
* 模块名称:TPAFE5160 16位8通道同步采样ADC驱动
|
||
* 模块功能:提供 TPAFE5160 并行接口模式下的初始化、过采样设置、转换启动、数据读取接口
|
||
* 适用平台:STM32F407ZGT6,并行16位数据总线接 GPIOG[15:0]
|
||
* 作者:王建锋
|
||
* 创建日期:2026-07-17
|
||
* 修改记录:
|
||
* 2026-07-17 王建锋 创建初始版本,参考 AD7606 并行驱动及 TPAFE5160 手册
|
||
* 2026-07-17 王建锋 增加 EXTI 中断读取模式
|
||
* 2026-09-20 王建锋 V0.1 引脚适配(引脚值随 main.h 宏,驱动逻辑不变)
|
||
* 2026-10-10 王建锋 消费者接入前置修复(E-44 / E-45 / E-58),详见 tpafe5160.h
|
||
*/
|
||
|
||
/* 头文件包含区 */
|
||
#include <string.h> /* memcpy:原子快照 */
|
||
|
||
#include "tpafe5160.h"
|
||
#include "main.h"
|
||
|
||
/* taskENTER_CRITICAL:把"选缓冲 + 拷数据"收进一个不可分割动作(E-58)。
|
||
* 与 App/log/console_log.c、Drivers/BSP/NET/net_socket.c 同一写法,仅任务上下文可用 */
|
||
#include "FreeRTOS.h"
|
||
#include "task.h"
|
||
|
||
/*
|
||
* 调试输出配置
|
||
*/
|
||
#define DBG_TAG "[ADC]"
|
||
#include "dbg_log.h"
|
||
|
||
/* ======================== 私有宏定义 ======================== */
|
||
|
||
/*
|
||
* 并行读取时序延时 (168MHz 主频,1 NOP ≈ 5.95ns)
|
||
*
|
||
* TPAFE5160 并行时序要求 (VDRIVE > 2.7V):
|
||
* t10 (RD 低脉宽) ≥ 22ns
|
||
* t11 (RD 高脉宽) ≥ 10ns
|
||
* t14 (数据访问时间) ≤ 21ns (从 RD 下降沿算起)
|
||
* t15 (数据保持时间) ≥ 6ns (从 RD 下降沿算起)
|
||
*
|
||
* GPIO 写操作本身约 1 个 AHB 周期 (~6ns),
|
||
* 加上端口延迟 (~12-18ns),实际 RD 引脚翻转滞后约 18-24ns。
|
||
* 因此 NOP 延时只需覆盖数据建立时间即可。
|
||
*/
|
||
#define TPAFE5160_NOP_5() __NOP(); __NOP(); __NOP(); __NOP(); __NOP()
|
||
|
||
/* RD 低脉宽延时:GPIO写(~6ns) + 5NOP(~30ns) > 22ns */
|
||
#define TPAFE5160_RD_LOW_DLY() do { TPAFE5160_RD_LOW(); TPAFE5160_NOP_5(); } while (0)
|
||
|
||
/* RD 高脉宽延时:GPIO写(~6ns) + 3NOP(~18ns) > 10ns */
|
||
#define TPAFE5160_RD_HIGH_DLY() do { TPAFE5160_RD_HIGH(); __NOP(); __NOP(); __NOP(); } while (0)
|
||
|
||
/* CONVST 脉冲延时:上升沿前需保证低电平,5NOP 覆盖 t5 约20ns */
|
||
#define TPAFE5160_CONVST_PULSE() do { \
|
||
TPAFE5160_CONVST_LOW(); \
|
||
TPAFE5160_NOP_5(); \
|
||
TPAFE5160_CONVST_HIGH(); \
|
||
TPAFE5160_NOP_5(); \
|
||
} while (0)
|
||
|
||
/* ======================== 私有函数声明 ======================== */
|
||
|
||
static bsp_err_t tpafe5160_read_channels(uint8_t count, int16_t *buf);
|
||
|
||
/* ======================== 公共函数定义 ======================== */
|
||
|
||
/*
|
||
* 函数功能:初始化 TPAFE5160(设置过采样、等待就绪、首帧有效性自检)
|
||
* 入口参数:无
|
||
* 返回值:0 - 成功,-2 - BUSY 超时,-3 - FRSTDATA 无跳变(芯片未驱动数据总线)
|
||
* 限定条件:CubeMX 已完成 GPIO 初始化
|
||
* 函数说明:1. 设置过采样为无过采样 (000)
|
||
* 2. 确保 RD 为高、CONVST 为低
|
||
* 3. 等待 BUSY 释放(转换空闲)
|
||
* 4. 采一帧并校验 FRSTDATA 有跳变(E-44:旧实现只看 BUSY,芯片死亡
|
||
* 时照样报 Init OK,此后所有读数都是上拉/总线残值——静默死亡)
|
||
*/
|
||
bsp_err_t tpafe5160_init(void) {
|
||
/* 设置默认过采样:无过采样 (OS[2:0] = 000) */
|
||
tpafe5160_set_os(TPAFE5160_OS_NONE);
|
||
|
||
/* 确保控制引脚处于空闲状态 */
|
||
TPAFE5160_RD_HIGH();
|
||
TPAFE5160_CONVST_LOW();
|
||
|
||
/* 等待 BUSY 释放,确保上电后无残留转换 */
|
||
int ret = tpafe5160_wait_busy(TPAFE5160_CONV_TIMEOUT_MS);
|
||
if (ret != BSP_OK) {
|
||
DBG_ERROR("Init wait BUSY timeout");
|
||
return ret;
|
||
}
|
||
|
||
/* 首帧有效性自检(E-44):一次完整转换 + 读取,确认芯片真的在响应 RD */
|
||
int16_t probe[TPAFE5160_CH_NUM];
|
||
tpafe5160_start_conv();
|
||
ret = tpafe5160_wait_busy(TPAFE5160_CONV_TIMEOUT_MS);
|
||
if (ret != BSP_OK) {
|
||
DBG_ERROR("Init probe BUSY timeout");
|
||
return ret;
|
||
}
|
||
if (tpafe5160_read_channels(TPAFE5160_CH_NUM, probe) != BSP_OK) {
|
||
ret = BSP_ERR_NO_DEVICE;
|
||
DBG_ERROR("Init FRSTDATA no-toggle (chip not driving DB bus)");
|
||
return ret;
|
||
}
|
||
|
||
DBG_INFO("Init OK");
|
||
return ret;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设置过采样率
|
||
* 入口参数:os - 过采样率枚举值 tpafe5160_os_t
|
||
* 返回值:无
|
||
* 限定条件:GPIO 已初始化
|
||
* 函数说明:通过 OS[2:0] 引脚设置过采样率,在下一次 BUSY 下降沿锁存生效
|
||
*/
|
||
void tpafe5160_set_os(tpafe5160_os_t os) {
|
||
TPAFE5160_OS0(os & 0x01); /* OS0 = bit0 */
|
||
TPAFE5160_OS1((os >> 1) & 0x01); /* OS1 = bit1 */
|
||
TPAFE5160_OS2((os >> 2) & 0x01); /* OS2 = bit2 */
|
||
}
|
||
|
||
/*
|
||
* 函数功能:启动一次转换(CONVST 上升沿触发)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:GPIO 已初始化
|
||
* 函数说明:产生 CONVST 脉冲上升沿,启动全部8通道同步采样与转换
|
||
*/
|
||
void tpafe5160_start_conv(void) {
|
||
TPAFE5160_CONVST_PULSE();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:等待转换完成
|
||
* 入口参数:timeout_ms - 超时时间 uint32_t > 0
|
||
* 返回值:0 - 转换完成,-2 - 超时
|
||
* 限定条件:已调用 tpafe5160_start_conv()
|
||
* 函数说明:轮询 BUSY 引脚等待下降沿
|
||
*/
|
||
bsp_err_t tpafe5160_wait_busy(uint32_t timeout_ms) {
|
||
uint32_t tick_start = HAL_GetTick();
|
||
|
||
/* 等待 BUSY 释放(低电平表示空闲) */
|
||
while (TPAFE5160_BUSY_READ() == GPIO_PIN_SET) {
|
||
if ((HAL_GetTick() - tick_start) >= timeout_ms) {
|
||
return BSP_ERR_TIMEOUT;
|
||
}
|
||
}
|
||
|
||
return BSP_OK;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:查询当前是否正在转换
|
||
* 入口参数:无
|
||
* 返回值:1 - 正在转换,0 - 空闲
|
||
* 限定条件:GPIO 已初始化
|
||
* 函数说明:读取 BUSY 引脚电平
|
||
*/
|
||
uint8_t tpafe5160_is_busy(void) {
|
||
return (TPAFE5160_BUSY_READ() == GPIO_PIN_SET) ? 1 : 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读取全部8通道转换结果
|
||
* 入口参数:buf - 8个int16_t的输出缓冲区 int16_t* 不为 NULL
|
||
* 返回值:0 - 成功,-2 - BUSY 超时,-3 - FRSTDATA 无跳变(读回值不可信)
|
||
* 限定条件:GPIO 已初始化
|
||
* 函数说明:1. 启动转换并等待 BUSY 释放
|
||
* 2. 连续8次拉低RD读取各通道数据
|
||
* 3. 校验 FRSTDATA 在这一帧内发生跳变(E-44)
|
||
*/
|
||
bsp_err_t tpafe5160_read_all(int16_t *buf) {
|
||
bsp_err_t ret = BSP_OK;
|
||
|
||
if (buf == NULL) {
|
||
return BSP_ERR_PARAM;
|
||
}
|
||
|
||
/* 启动转换 */
|
||
tpafe5160_start_conv();
|
||
|
||
/* 等待转换完成 */
|
||
ret = tpafe5160_wait_busy(TPAFE5160_CONV_TIMEOUT_MS);
|
||
if (ret != BSP_OK) {
|
||
DBG_ERROR("read_all BUSY timeout");
|
||
return ret;
|
||
}
|
||
|
||
/* 连续读取8个通道(内含 FRSTDATA 有效性校验) */
|
||
ret = tpafe5160_read_channels(TPAFE5160_CH_NUM, buf);
|
||
if (ret != BSP_OK) {
|
||
DBG_ERROR("read_all FRSTDATA no-toggle (data not trustworthy)");
|
||
return BSP_ERR_NO_DEVICE;
|
||
}
|
||
|
||
return BSP_OK;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读取指定通道的转换结果
|
||
* 入口参数:channel - 通道号 uint8_t 0 - 7
|
||
* value - 输出指针 int16_t* 不为 NULL
|
||
* 返回值:0 - 成功,-1 - 通道号无效,-2 - BUSY 超时,-3 - FRSTDATA 无跳变
|
||
* 限定条件:GPIO 已初始化
|
||
* 函数说明:启动转换并等待完成后,连续读取至指定通道
|
||
*/
|
||
bsp_err_t tpafe5160_read_channel(uint8_t channel, int16_t *value) {
|
||
bsp_err_t ret = BSP_OK;
|
||
int16_t buf[TPAFE5160_CH_NUM] = {0};
|
||
|
||
if (channel >= TPAFE5160_CH_NUM || value == NULL) {
|
||
return BSP_ERR_PARAM;
|
||
}
|
||
|
||
ret = tpafe5160_read_all(buf);
|
||
if (ret != BSP_OK) {
|
||
return ret;
|
||
}
|
||
|
||
*value = buf[channel];
|
||
return BSP_OK;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:原始ADC值转电压值
|
||
* 入口参数:raw - ADC原始值 int16_t 有符号补码
|
||
* 返回值:电压值 float 单位 V
|
||
* 限定条件:无
|
||
* 函数说明:±5V量程时 LSB = 10V / 65536 ≈ 152.59μV
|
||
* ±10V量程时 LSB = 20V / 65536 ≈ 305.18μV
|
||
* 硬件 RANGE 引脚接 GND,默认 ±5V 量程
|
||
*/
|
||
float tpafe5160_to_voltage(int16_t raw) {
|
||
/* RANGE 接 GND → ±5V 量程,满量程 10V */
|
||
return (float)raw * (10.0f / 65536.0f);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:直接读取并行数据总线(不启动转换)
|
||
* 入口参数:无
|
||
* 返回值:16位原始数据 uint16_t
|
||
* 限定条件:RD 为低或 CS 与 RD 已拉低
|
||
* 函数说明:读取 GPIOG->IDR 低16位,对应 DB[15:0]
|
||
*/
|
||
uint16_t tpafe5160_read_bus(void) {
|
||
return TPAFE5160_READ_BUS();
|
||
}
|
||
|
||
/* ======================== 私有函数定义 ======================== */
|
||
|
||
/*
|
||
* 函数功能:通过 RD 脉冲连续读取多个通道数据(含 FRSTDATA 有效性校验)
|
||
* 入口参数:count - 要读取的通道数 uint8_t 1 - 8
|
||
* buf - 输出缓冲区 int16_t* 不为 NULL
|
||
* 返回值:0 - 成功,-3 - 整帧读完 FRSTDATA 无跳变(读回值不可信)
|
||
* 限定条件:转换已完成(BUSY 为低),RD 初始为高
|
||
* 函数说明:每次 RD 下降沿输出一个通道数据,按通道1~8顺序输出
|
||
* DB[15:0] 直接接 GPIOG[15:0],通过 IDR 寄存器一次读取
|
||
*
|
||
* 时序关键点 (VDRIVE > 2.7V, 168MHz 主频):
|
||
* GPIO 写操作 ~6ns + 端口延迟 ~18ns → RD 引脚实际翻转约 24ns 后
|
||
* t14 数据建立 ≤ 21ns → 数据在 RD 下降沿后 21ns 内有效
|
||
* 5 个 NOP (~30ns) 覆盖建立时间
|
||
*
|
||
* E-44(数据有效性):FRSTDATA 由芯片主动驱动,板上 R24 4.7k 上拉(V0.1 网表)。
|
||
* 手册 Parallel Data Read:FRSTDATA 随首个 RD 下降沿变高标记通道 1 可读,t26/t27
|
||
* 给出"RD 下降沿 → FRSTDATA 高/低"两个跳变。判据刻意**不锁死具体是哪个 RD 边沿
|
||
* 拉高/拉低**(本板未实测确认边沿次序),只要求"一帧读下来必须发生跳变":
|
||
* 芯片死亡 / 不响应 RD 时该脚被上拉钉死为高 → 全程无跳变 → 判失败。
|
||
* 旧实现完全不看这根脚(宏 TPAFE5160_FRSTDATA_READ 有定义、代码零引用),
|
||
* 却在注释里承诺"通过 FRSTDATA 验证第一通道",芯片死亡时返回 OK + 常量垃圾。
|
||
*/
|
||
static bsp_err_t tpafe5160_read_channels(uint8_t count, int16_t *buf) {
|
||
uint8_t i;
|
||
uint8_t lvl0 = (TPAFE5160_FRSTDATA_READ() == GPIO_PIN_SET) ? 1u : 0u;
|
||
uint8_t toggled = 0;
|
||
|
||
for (i = 0; i < count; i++) {
|
||
/* RD 下降沿:ADC 输出当前通道数据到 DB[15:0] */
|
||
TPAFE5160_RD_LOW_DLY();
|
||
|
||
/* 读取16位并行数据 */
|
||
buf[i] = (int16_t)TPAFE5160_READ_BUS();
|
||
|
||
/* RD 上升沿:准备下一通道 */
|
||
TPAFE5160_RD_HIGH_DLY();
|
||
|
||
/* FRSTDATA 跳变观测(每次 RD 之后采一次) */
|
||
if (((TPAFE5160_FRSTDATA_READ() == GPIO_PIN_SET) ? 1u : 0u) != lvl0) {
|
||
toggled = 1;
|
||
}
|
||
}
|
||
|
||
return toggled ? BSP_OK : BSP_ERR_NO_DEVICE;
|
||
}
|
||
|
||
/* ======================== 中断模式实现 ======================== */
|
||
|
||
/* 双缓冲区:ISR 写非活动缓冲,消费者在临界区内拷活动缓冲 */
|
||
static int16_t s_buf_a[TPAFE5160_CH_NUM];
|
||
static int16_t s_buf_b[TPAFE5160_CH_NUM];
|
||
static volatile uint8_t s_buf_sel = 0; /* 0 = 写 B / 读 A, 1 = 写 A / 读 B */
|
||
/* E-45:帧序号取代"就绪电平标志"。旧 s_ready 有三个写者,其中 start_conv_irq
|
||
* 在触发前清 0 —— 重触发这个动作本身就会抹掉一帧还没被消费的数据(高速采样
|
||
* 系统性丢样,且无从发现)。序号只增不清,消费者比较序号即可 */
|
||
static volatile uint32_t s_frame_seq = 0;
|
||
/* E-44:FRSTDATA 无跳变帧计数。中断里只加计数(不打日志),由消费者/自检侧上报 */
|
||
static volatile uint16_t s_frstdata_err = 0;
|
||
/* 消费者游标:只在任务上下文读写(snapshot 内部),中断不碰 */
|
||
static uint32_t s_consumer_seq = 0;
|
||
|
||
/*
|
||
* 函数功能:使能 BUSY EXTI 中断(运行时重使能用)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:CubeMX 已完成 GPIO 和 NVIC 配置
|
||
* 函数说明:正常启动流程无需调用,仅在 irq_disable() 后需要重新使能时使用
|
||
*/
|
||
void tpafe5160_irq_enable(void) {
|
||
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:关闭 BUSY EXTI 中断
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:已调用 tpafe5160_irq_enable()
|
||
* 函数说明:仅禁用 NVIC 中断,GPIO 配置保持 CubeMX 设定
|
||
*/
|
||
void tpafe5160_irq_disable(void) {
|
||
HAL_NVIC_DisableIRQ(EXTI9_5_IRQn);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:启动转换(中断模式)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:已调用 tpafe5160_irq_enable()
|
||
* 函数说明:产生 CONVST 脉冲,转换完成后由 EXTI 中断自动读取 8 通道数据。
|
||
* E-45:这里**不再清就绪标志**——旧实现"触发前置 0"等于用重触发去丢帧
|
||
*/
|
||
void tpafe5160_start_conv_irq(void) {
|
||
TPAFE5160_CONVST_PULSE();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:查询已完成的帧序号
|
||
* 入口参数:无
|
||
* 返回值:uint32_t 帧序号(EXTI 每读完一帧 +1)
|
||
* 限定条件:中断模式已启用
|
||
* 函数说明:见 tpafe5160.h 同名声明的契约说明(E-45)
|
||
*/
|
||
uint32_t tpafe5160_frame_seq(void) {
|
||
return s_frame_seq;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:FRSTDATA 无跳变帧的累计次数
|
||
* 入口参数:无
|
||
* 返回值:uint16_t 计数
|
||
* 限定条件:中断模式已启用
|
||
* 函数说明:非 0 = 至少一帧的 8 个值不可信(E-44),消费者据此上报自检
|
||
*/
|
||
uint16_t tpafe5160_frstdata_err(void) {
|
||
return s_frstdata_err;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:原子快照最新一帧(消费者取数的唯一正确姿势)
|
||
* 入口参数:dst - 输出缓冲区 int16_t* 不为 NULL
|
||
* count - 拷贝通道数 uint8_t 1 - 8
|
||
* 返回值:1 - 取到新帧,0 - 无新帧,-1 - 参数无效
|
||
* 限定条件:**任务上下文**(内部 taskENTER_CRITICAL,中断里不可调用)
|
||
* 函数说明:E-58——把"选缓冲 + 拷数据 + 记游标"收进一个临界区。旧 get_buf() 只给
|
||
* 指针、任务再逐通道拷,两步之间被 BUSY EXTI 抢占并跨过一帧周期就拷回
|
||
* 两帧混合的数据(8 通道失去同一时刻性)且无任何错误指示
|
||
*/
|
||
int tpafe5160_snapshot(int16_t *dst, uint8_t count) {
|
||
uint32_t seq;
|
||
|
||
if (dst == NULL || count == 0u || count > TPAFE5160_CH_NUM) {
|
||
return BSP_ERR_PARAM;
|
||
}
|
||
|
||
seq = s_frame_seq;
|
||
if (seq == s_consumer_seq) {
|
||
return 0; /* 无新帧 */
|
||
}
|
||
|
||
taskENTER_CRITICAL();
|
||
(void)memcpy(dst, (s_buf_sel == 0u) ? s_buf_a : s_buf_b,
|
||
(size_t)count * sizeof(int16_t));
|
||
s_consumer_seq = seq;
|
||
taskEXIT_CRITICAL();
|
||
|
||
return 1;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:BUSY 下降沿 EXTI 处理(由 HAL_GPIO_EXTI_Callback 调用)
|
||
* 入口参数:GPIO_Pin - 触发中断的引脚号
|
||
* 返回值:无
|
||
* 限定条件:BUSY EXTI 已使能
|
||
* 函数说明:转换完成后自动读取 8 通道数据到缓冲区,耗时约 0.5µs (168MHz)
|
||
*/
|
||
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
|
||
tpafe5160_exti_handler(GPIO_Pin);
|
||
}
|
||
|
||
void tpafe5160_exti_handler(uint16_t GPIO_Pin) {
|
||
if (GPIO_Pin != TP_BUSY_Pin) {
|
||
return;
|
||
}
|
||
|
||
/* 双缓冲切换:ISR 写与消费者读不同的缓冲区 */
|
||
int16_t *p_wr = (s_buf_sel == 0u) ? s_buf_b : s_buf_a;
|
||
|
||
/* 连续读取8个通道(内含 FRSTDATA 有效性校验),约 0.5µs @ 168MHz */
|
||
int ret = tpafe5160_read_channels(TPAFE5160_CH_NUM, p_wr);
|
||
|
||
/* 切换缓冲区并推进帧序号(E-45:序号只增不清,重触发不会抹掉未消费帧) */
|
||
s_buf_sel ^= 1u;
|
||
s_frame_seq++;
|
||
|
||
/* E-44:中断里不打日志,只累计;消费者据 tpafe5160_frstdata_err() 上报自检 */
|
||
if (ret != BSP_OK && s_frstdata_err < 0xFFFFu) {
|
||
s_frstdata_err++;
|
||
}
|
||
}
|