修复多连接的问题

This commit is contained in:
2026-07-19 15:27:49 +08:00
parent 9ed4c22022
commit e06d849007
38 changed files with 2475 additions and 1953 deletions

View File

@@ -1,12 +1,9 @@
/*
* 模块名称TPAFE5160 16位8通道同步采样ADC驱动
* 模块功能:提TPAFE5160 并行接口模式下的初始化、过采样设置、转换启动、数据读取接
* 适用平台STM32F407ZGT6并行16位数据总线接 GPIOG[15:0]
* 作者:王建锋
* 创建日期:2026-07-17
* 修改记录:
* 2026-07-17 王建锋 创建初始版本,参考 AD7606 并行驱动及 TPAFE5160 手册
* 2026-07-17 王建锋 增加 EXTI 中断读取模式
* 模块名称TPAFE5160 16<EFBFBD>?通道同步采样ADC驱动
* 模块功能:提<EFBFBD>?TPAFE5160 并行接口模式下的初始化、过采样设置、转换启动、数据读取接<EFBFBD>? * 适用平台STM32F407ZGT6<EFBC8C>?6位数据总线<E680BB>?GPIOG[15:0]
* 作者王建<EFBFBD>? * 创建日期<E697A5>?026-07-17
* 修改记录<EFBFBD>? * 2026-07-17 王建<E78E8B>? 创建初始版本<EFBC8C>?AD7606 并行驱动<E9A9B1>?TPAFE5160 手册
* 2026-07-17 王建<E78E8B>? 增加 EXTI 中断读取模式
*/
/* 头文件包含区 */
@@ -19,35 +16,31 @@
#define DBG_TAG "[ADC]"
#include "dbg_log.h"
/* ======================== 私有宏定======================== */
/* ======================== 私有宏定<EFBFBD>?======================== */
/*
* 并行读取时序延时 (168MHz 主频1 NOP 5.95ns)
* 并行读取时序延时 (168MHz 主频<EFBFBD>? NOP <EFBFBD>?5.95ns)
*
* TPAFE5160 并行时序要求 (VDRIVE > 2.7V)
* t10 (RD 低脉宽) ≥ 22ns
* t11 (RD 高脉宽) ≥ 10ns
* t14 (数据访问时间) ≤ 21ns (RD 下降沿算起)
* t15 (数据保持时间) ≥ 6ns (从 RD 下降沿算起)
* TPAFE5160 并行时序要求 (VDRIVE > 2.7V)<EFBFBD>? * t10 (RD 低脉<E4BD8E>? <20>?22ns
* t11 (RD 高脉<EFBFBD>? <20>?10ns
* t14 (数据访问时间) <20>?21ns (<28>?RD 下降沿算<E6B2BF>?
* t15 (数据保持时间) <EFBFBD>?6ns (<EFBFBD>?RD 下降沿算<EFBFBD>?
*
* GPIO 写操作本身约 1 AHB 周期 (~6ns)
* 加上端口延迟 (~12-18ns),实际 RD 引脚翻转滞后约 18-24ns。
* 因此 NOP 延时只需覆盖数据建立时间即可。
*/
#define TP_NOP_5() __NOP(); __NOP(); __NOP(); __NOP(); __NOP()
* GPIO 写操作本身约 1 <EFBFBD>?AHB 周期 (~6ns)<EFBFBD>? * 加上端口延迟 (~12-18ns)<EFBC8C>?RD 引脚翻转滞后<E6BB9E>?18-24ns<6E>? * 因此 NOP 延时只需覆盖数据建立时间即可<E58DB3>? */
#define TPAFE5160_NOP_5() __NOP(); __NOP(); __NOP(); __NOP(); __NOP()
/* RD 低脉宽延时GPIO写(~6ns) + 5NOP(~30ns) > 22ns */
#define TP_RD_LOW_DLY() do { TPAFE5160_RD_LOW(); TP_NOP_5(); } while (0)
/* RD 低脉宽延时GPIO<EFBFBD>?~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 TP_RD_HIGH_DLY() do { TPAFE5160_RD_HIGH(); __NOP(); __NOP(); __NOP(); } while (0)
/* RD 高脉宽延时GPIO<EFBFBD>?~6ns) + 3NOP(~18ns) > 10ns */
#define TPAFE5160_RD_HIGH_DLY() do { TPAFE5160_RD_HIGH(); __NOP(); __NOP(); __NOP(); } while (0)
/* CONVST 脉冲延时上升沿前需保证低电平5NOP 覆盖 t5 20ns */
#define TP_CONVST_PULSE() do { \
/* CONVST 脉冲延时上升沿前需保证低电平5NOP 覆盖 t5 <EFBFBD>?20ns */
#define TPAFE5160_CONVST_PULSE() do { \
TPAFE5160_CONVST_LOW(); \
TP_NOP_5(); \
TPAFE5160_NOP_5(); \
TPAFE5160_CONVST_HIGH(); \
TP_NOP_5(); \
TPAFE5160_NOP_5(); \
} while (0)
/* ======================== 私有函数声明 ======================== */
@@ -59,22 +52,20 @@ static void tpafe5160_read_channels(uint8_t count, int16_t *buf);
/*
* 函数功能:初始化 TPAFE5160设置过采样、等待就绪
* 入口参数:无
* 返回值0 - 成功-2 - BUSY 超时
* 限定条件CubeMX 已完GPIO 初始
* 函数说明1. 设置过采样为无过采样 (000)
* 返回值0 - 成功<EFBFBD>?2 - BUSY 超时
* 限定条件CubeMX 已完<EFBFBD>?GPIO 初始<EFBFBD>? * 函数说明<E8AFB4>?. 设置过采样为无过采样 (000)
* 2. 确保 RD 为高、CONVST 为低
* 3. 等待 BUSY 释放(转换空闲)
*/
int tpafe5160_init(void)
{
int tpafe5160_init(void) {
/* 设置默认过采样:无过采样 (OS[2:0] = 000) */
tpafe5160_set_os(TP_OS_NONE);
tpafe5160_set_os(TPAFE5160_OS_NONE);
/* 确保控制引脚处于空闲状*/
/* 确保控制引脚处于空闲状<EFBFBD>?*/
TPAFE5160_RD_HIGH();
TPAFE5160_CONVST_LOW();
/* 等待 BUSY 释放,确保上电后无残留转*/
/* 等待 BUSY 释放,确保上电后无残留转<EFBFBD>?*/
int ret = tpafe5160_wait_busy(TPAFE5160_CONV_TIMEOUT_MS);
if (ret != TPAFE5160_OK) {
DBG_ERROR("Init wait BUSY timeout");
@@ -85,14 +76,10 @@ int tpafe5160_init(void)
}
/*
* 函数功能:设置过采样
* 入口参数os - 过采样率枚举值 tpafe5160_os_t
* 返回值:无
* 限定条件GPIO 已初始化
* 函数说明:通过 OS[2:0] 引脚设置过采样率,在下一次 BUSY 下降沿锁存生效
*/
void tpafe5160_set_os(tpafe5160_os_t os)
{
* 函数功能:设置过采样<EFBFBD>? * 入口参数os - 过采样率枚举<E69E9A>? tpafe5160_os_t
* 返回值<EFBFBD>? * 限定条件GPIO 已初始化
* 函数说明:通过 OS[2:0] 引脚设置过采样率在下一<E4B88B>?BUSY 下降沿锁存生<E5AD98>? */
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 */
@@ -101,27 +88,22 @@ void tpafe5160_set_os(tpafe5160_os_t os)
/*
* 函数功能启动一次转换CONVST 上升沿触发)
* 入口参数:无
* 返回值:
* 限定条件GPIO 已初始化
* 函数说明:产生 CONVST 脉冲上升沿启动全部8通道同步采样与转换
*/
void tpafe5160_start_conv(void)
{
TP_CONVST_PULSE();
* 返回值:<EFBFBD>? * 限定条件GPIO 已初始化
* 函数说明<EFBFBD>?CONVST 脉冲上升沿启动全部8通道同步采样与转<E4B88E>? */
void tpafe5160_start_conv(void) {
TPAFE5160_CONVST_PULSE();
}
/*
* 函数功能:等待转换完成
* 入口参数timeout_ms - 超时时间 uint32_t > 0
* 返回值0 - 转换完成-2 - 超时
* 函数功能等待转换完<E68DA2>? * 入口参数timeout_ms - 超时时间 uint32_t > 0
* 返回值0 - 转换完成<EFBFBD>?2 - 超时
* 限定条件:已调用 tpafe5160_start_conv()
* 函数说明:轮BUSY 引脚等待下降沿
*/
int tpafe5160_wait_busy(uint32_t timeout_ms)
{
* 函数说明:轮<EFBFBD>?BUSY 引脚等待下降<EFBFBD>? */
int tpafe5160_wait_busy(uint32_t timeout_ms) {
uint32_t tick_start = HAL_GetTick();
/* 等待 BUSY 释放(低电平表示空闲 */
/* 等待 BUSY 释放(低电平表示空闲<EFBFBD>?*/
while (TPAFE5160_BUSY_READ() == GPIO_PIN_SET) {
if ((HAL_GetTick() - tick_start) >= timeout_ms) {
return TPAFE5160_BUSY_TIMEOUT;
@@ -132,29 +114,26 @@ int tpafe5160_wait_busy(uint32_t timeout_ms)
}
/*
* 函数功能:查询当前是否正在转
* 入口参数:无
* 返回值1 - 正在转换0 - 空闲
* 函数功能:查询当前是否正在转<EFBFBD>? * 入口参数:无
* 返回值1 - 正在转换<E8BDAC>? - 空闲
* 限定条件GPIO 已初始化
* 函数说明:读BUSY 引脚电平
* 函数说明:读<EFBFBD>?BUSY 引脚电平
*/
uint8_t tpafe5160_is_busy(void)
{
uint8_t tpafe5160_is_busy(void) {
return (TPAFE5160_BUSY_READ() == GPIO_PIN_SET) ? 1 : 0;
}
/*
* 函数功能:读取全部8通道转换结果
* 函数功能:读取全<EFBFBD>?通道转换结果
* 入口参数buf - 8个int16_t的输出缓冲区 int16_t* 不为 NULL
* 返回值0 - 成功-2 - BUSY 超时
* 返回值0 - 成功<EFBFBD>?2 - BUSY 超时
* 限定条件GPIO 已初始化
* 函数说明1. 启动转换并等BUSY 释放
* 函数说明<EFBFBD>?. 启动转换并等<EFBFBD>?BUSY 释放
* 2. 连续8次拉低RD读取各通道数据
* 3. 通过 FRSTDATA 验证第一通道
*/
int tpafe5160_read_all(int16_t *buf)
{
int ret;
int tpafe5160_read_all(int16_t *buf) {
int ret = 0;
/* 启动转换 */
tpafe5160_start_conv();
@@ -173,17 +152,15 @@ int tpafe5160_read_all(int16_t *buf)
}
/*
* 函数功能:读取指定通道的转换结
* 入口参数channel - 通道号 uint8_t 0 - 7
* 函数功能:读取指定通道的转换结<EFBFBD>? * 入口参数channel - 通道<E9809A>? uint8_t 0 - 7
* value - 输出指针 int16_t* 不为 NULL
* 返回值0 - 成功-1 - 通道号无效,-2 - BUSY 超时
* 返回值0 - 成功<EFBFBD>?1 - 通道号无效,-2 - BUSY 超时
* 限定条件GPIO 已初始化
* 函数说明:启动转换并等待完成后,连续读取至指定通道
*/
int tpafe5160_read_channel(uint8_t channel, int16_t *value)
{
int ret;
int16_t buf[TPAFE5160_CH_NUM];
int tpafe5160_read_channel(uint8_t channel, int16_t *value) {
int ret = 0;
int16_t buf[TPAFE5160_CH_NUM] = {0};
if (channel >= TPAFE5160_CH_NUM || value == NULL) {
return TPAFE5160_ERROR;
@@ -199,29 +176,23 @@ int tpafe5160_read_channel(uint8_t channel, int16_t *value)
}
/*
* 函数功能原始ADC值转电压
* 入口参数raw - ADC原始值 int16_t 有符号补码
* 返回值:电压值 float 单位 V
* 函数功能原始ADC值转电压<EFBFBD>? * 入口参数raw - ADC原始<E58E9F>? int16_t 有符号补<E58FB7>? * 返回值电压<E794B5>? float 单位 V
* 限定条件:无
* 函数说明:±5V量程LSB = 10V / 65536 152.59μV
* ±10V量程LSB = 20V / 65536 305.18μV
* 硬件 RANGE 引脚GND±5V 量程
* 函数说明:<EFBFBD>?V量程<EFBFBD>?LSB = 10V / 65536 <EFBFBD>?152.59μV
* ±10V量程<EFBFBD>?LSB = 20V / 65536 <EFBFBD>?305.18μV
* 硬件 RANGE 引脚<EFBFBD>?GND<EFBFBD>?±5V 量程
*/
float tpafe5160_to_voltage(int16_t raw)
{
/* RANGE 接 GND → ±5V 量程,满量程 10V */
float tpafe5160_to_voltage(int16_t raw) {
/* RANGE <20>?GND <20>?±5V 量程,满量程 10V */
return (float)raw * (10.0f / 65536.0f);
}
/*
* 函数功能:直接读取并行数据总线(不启动转换
* 入口参数:无
* 返回值16位原始数据 uint16_t
* 限定条件RD 为低或 CS 与 RD 已拉低
* 函数说明:读取 GPIOG->IDR 低16位对应 DB[15:0]
* 函数功能:直接读取并行数据总线(不启动转换<EFBFBD>? * 入口参数:无
* 返回值16位原始数<EFBFBD>? uint16_t
* 限定条件RD 为低<E4B8BA>?CS <20>?RD 已拉<E5B7B2>? * 函数说明<EFBC9A>?GPIOG->IDR <20>?6位对应 DB[15:0]
*/
uint16_t tpafe5160_read_bus(void)
{
uint16_t tpafe5160_read_bus(void) {
return TPAFE5160_READ_BUS();
}
@@ -229,100 +200,82 @@ uint16_t tpafe5160_read_bus(void)
/*
* 函数功能:通过 RD 脉冲连续读取多个通道数据
* 入口参数count - 要读取的通道 uint8_t 1 - 8
* buf - 输出缓冲 int16_t* 不为 NULL
* 返回值:
* 限定条件转换已完成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) 覆盖建立时间
* 入口参数count - 要读取的通道<EFBFBD>? uint8_t 1 - 8
* buf - 输出缓冲<EFBFBD>? int16_t* 不为 NULL
* 返回值:<EFBFBD>? * 限定条件转换已完成BUSY 为低RD 初始为高
* 函数说明<EFBFBD>?RD 下降沿输出一个通道数据按通道1~8顺序输出
* DB[15:0] 直接<E79BB4>?GPIOG[15:0],通过 IDR 寄存器一次读<E6ACA1>? *
* 时序关键<EFBFBD>?(VDRIVE > 2.7V, 168MHz 主频)<29>? * GPIO 写操<E58699>?~6ns + 端口延迟 ~18ns <20>?RD 引脚实际翻转<E7BFBB>?24ns <20>? * t14 数据建立 <20>?21ns <20>?数据<E695B0>?RD 下降沿后 21ns 内有<E58685>? * 5 <20>?NOP (~30ns) 覆盖建立时间
*/
static void tpafe5160_read_channels(uint8_t count, int16_t *buf)
{
static void tpafe5160_read_channels(uint8_t count, int16_t *buf) {
uint8_t i;
for (i = 0; i < count; i++) {
/* RD 下降沿ADC 输出当前通道数据DB[15:0] */
TP_RD_LOW_DLY();
/* RD 下降沿ADC 输出当前通道数据<EFBFBD>?DB[15:0] */
TPAFE5160_RD_LOW_DLY();
/* 读取16位并行数*/
/* 读取16位并行数<EFBFBD>?*/
buf[i] = (int16_t)TPAFE5160_READ_BUS();
/* RD 上升沿:准备下一通道 */
TP_RD_HIGH_DLY();
TPAFE5160_RD_HIGH_DLY();
}
}
/* ======================== 中断模式实现 ======================== */
/* 双缓冲区ISR s_buf_b主循环s_buf_a */
/* 双缓冲区ISR <EFBFBD>?s_buf_b主循环<EFBFBD>?s_buf_a */
static int16_t s_buf_a[TPAFE5160_CH_NUM];
static int16_t s_buf_b[TPAFE5160_CH_NUM];
static volatile uint8_t s_ready = 0;
static volatile uint8_t s_buf_sel = 0; /* 0 = B / A, 1 = A / B */
static volatile uint8_t s_buf_sel = 0; /* 0 = <EFBFBD>?B / <EFBFBD>?A, 1 = <EFBFBD>?A / <EFBFBD>?B */
/*
* 函数功能:使BUSY EXTI 中断(运行时重使能用
* 入口参数:无
* 返回值:无
* 限定条件CubeMX 已完成 GPIO 和 NVIC 配置
* 函数说明:正常启动流程无需调用,仅在 irq_disable() 后需要重新使能时使用
* 函数功能:使<EFBFBD>?BUSY EXTI 中断(运行时重使能用<EFBFBD>? * 入口参数:无
* 返回值<EFBFBD>? * 限定条件CubeMX 已完<E5B7B2>?GPIO <20>?NVIC 配置
* 函数说明正常启动流程无需调用<EFBFBD>?irq_disable() 后需要重新使能时使用
*/
void tpafe5160_irq_enable(void)
{
void tpafe5160_irq_enable(void) {
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}
/*
* 函数功能:关BUSY EXTI 中断
* 函数功能:关<EFBFBD>?BUSY EXTI 中断
* 入口参数:无
* 返回值:
* 限定条件:已调用 tpafe5160_irq_enable()
* 返回值:<EFBFBD>? * 限定条件:已调用 tpafe5160_irq_enable()
* 函数说明:仅禁用 NVIC 中断GPIO 配置保持 CubeMX 设定
*/
void tpafe5160_irq_disable(void)
{
void tpafe5160_irq_disable(void) {
HAL_NVIC_DisableIRQ(EXTI9_5_IRQn);
}
/*
* 函数功能:启动转换(中断模式
* 入口参数:无
* 返回值:无
* 限定条件:已调用 tpafe5160_irq_enable()
* 函数说明:产生 CONVST 脉冲,转换完成后由 EXTI 中断自动读取 8 通道数据
* 函数功能:启动转换(中断模式<EFBFBD>? * 入口参数:无
* 返回值<EFBFBD>? * 限定条件:已调用 tpafe5160_irq_enable()
* 函数说明<EFBFBD>?CONVST 脉冲转换完成后<E68890>?EXTI 中断自动读取 8 通道数据
*/
void tpafe5160_start_conv_irq(void)
{
void tpafe5160_start_conv_irq(void) {
s_ready = 0;
TP_CONVST_PULSE();
TPAFE5160_CONVST_PULSE();
}
/*
* 函数功能:检查是否有新的转换数据
* 入口参数:无
* 返回值1 - 数据就绪0 - 无新数据
* 返回值1 - 数据就绪<EFBFBD>? - 无新数据
* 限定条件:中断模式已启用
* 函数说明:在 EXTI 回调中置位,主循环读取后需调用 tpafe5160_clear_ready() 清除
*/
uint8_t tpafe5160_data_ready(void)
{
uint8_t tpafe5160_data_ready(void) {
return s_ready;
}
/*
* 函数功能:清除数据就绪标
* 入口参数:无
* 返回值:无
* 限定条件:中断模式已启用
* 函数功能:清除数据就绪标<EFBFBD>? * 入口参数:无
* 返回值<EFBFBD>? * 限定条件:中断模式已启用
* 函数说明:主循环处理完数据后调用
*/
void tpafe5160_clear_ready(void)
{
void tpafe5160_clear_ready(void) {
s_ready = 0;
}
@@ -330,23 +283,16 @@ void tpafe5160_clear_ready(void)
* 函数功能:获取数据缓冲区指针
* 入口参数:无
* 返回值int16_t[8] 数据缓冲区的 const 指针
* 限定条件tpafe5160_data_ready() 返回 1 时调
* 函数说明双缓冲切换ISR 写另一个缓冲区,主循环安全读取当前缓冲区
*/
const int16_t* tpafe5160_get_buf(void)
{
* 限定条件tpafe5160_data_ready() 返回 1 时调<EFBFBD>? * 函数说明双缓冲切换ISR 写另一个缓冲区主循环安全读取当前缓冲<E7BC93>? */
const int16_t* tpafe5160_get_buf(void) {
return (s_buf_sel == 0) ? s_buf_a : s_buf_b;
}
/*
* 函数功能BUSY 下降沿 EXTI 处理(由 HAL_GPIO_EXTI_Callback 调用
* 入口参数GPIO_Pin - 触发中断的引脚号
* 返回值:无
* 限定条件BUSY EXTI 已使能
* 函数说明:转换完成后自动读取 8 通道数据到缓冲区,耗时约 0.5µs (168MHz)
* 函数功能BUSY 下降<EFBFBD>?EXTI 处理(由 HAL_GPIO_EXTI_Callback 调用<EFBFBD>? * 入口参数GPIO_Pin - 触发中断的引脚号
* 返回值<EFBFBD>? * 限定条件BUSY EXTI 已使<E5B7B2>? * 函数说明:转换完成后自动读取 8 通道数据到缓冲区耗时<E88097>?0.5µs (168MHz)
*/
void tpafe5160_exti_handler(uint16_t GPIO_Pin)
{
void tpafe5160_exti_handler(uint16_t GPIO_Pin) {
if (GPIO_Pin != TP_BUSY_Pin) {
return;
}
@@ -357,9 +303,9 @@ void tpafe5160_exti_handler(uint16_t GPIO_Pin)
/* 连续读取8个通道约 0.5µs @ 168MHz */
uint8_t i;
for (i = 0; i < TPAFE5160_CH_NUM; i++) {
TP_RD_LOW_DLY();
TPAFE5160_RD_LOW_DLY();
p_wr[i] = (int16_t)TPAFE5160_READ_BUS();
TP_RD_HIGH_DLY();
TPAFE5160_RD_HIGH_DLY();
}
/* 切换缓冲区并标记就绪 */