目录
- 一、基于单通道轮询读取的Bug
- 二、原因分析
- 三、多通道同时初始化的程序
- 1. 芯片初始化
- 2. 主程序架构
- 资料链接
一、基于单通道轮询读取的Bug
本人曾在2022年6月12日发表过一篇名为《STM32的硬件SPI驱动AD7124的方法》的博客,链接如下:
STM32的硬件SPI驱动AD7124的方法
但在去年的12月,本人发现在此模式运行下出现了个令人费解的bug,这款程序的架构是对AD7124的每个通道进行分时读取,也就是switch-case开关语句。具体来讲就是在读取某个通道前,需要将所要读取的对应通道先初始化,其他通道全部关闭,然后再执行对应的读取指令,上述步骤结束后ad_counter+1,对下一个需要采样的通道重复步骤,直到采集完最后一个通道再给ad_counter清零。以下是代码段:
while(1)
{
switch(ad_counter)
{
case 0:
AD7124_Reset();
delay_us(5); //复位后必须要延时
AD7124_CS_L;
AD7124_Ch0_INIT(Samprate_1kHz); //多通道初始化函数
AD7124_Set_Gain(1); //1倍增益
AD7124_CS_H;
AD7124_CS_L;
AD7124_SPI_ReadWrite(0x42); //读操作
Data_0 = AD7124_Read_Data(3); //Data采集结果
AD7124_CS_H;
data_temp_0 = Data_0;
data_last0 = (float)Vref/AD_Gain * ((float)data_temp_0/0X800000-1); //双极性模式电压转换公式,单位mV
ad_counter++; //采集通道计数器+1,采集下一个通道
break;
case 1:
AD7124_Reset();
delay_us(5); //复位后必须要延时
AD7124_CS_L;
AD7124_Ch1_INIT(Samprate_1kHz); //多通道初始化函数
AD7124_Set_Gain(1); //1倍增益
AD7124_CS_H;
AD7124_CS_L;
AD7124_SPI_ReadWrite(0x42); //读操作
Data_1 = AD7124_Read_Data(3); //Data采集结果
AD7124_CS_H;
data_temp_1 = Data_1;
data_last1 = (float)Vref/AD_Gain * ((float)data_temp_1/0X800000-1); //双极性模式电压转换公式,单位mV
ad_counter++; //采集通道计数器+1,采集下一个通道
break;
case 2:
AD7124_Reset();
delay_us(5); //复位后必须要延时
AD7124_CS_L;
AD7124_Ch2_INIT(Samprate_1kHz); //多通道初始化函数
AD7124_Set_Gain(1); //1倍增益
AD7124_CS_H;
AD7124_CS_L;
AD7124_SPI_ReadWrite(0x42); //读操作
Data_2 = AD7124_Read_Data(3); //Data采集结果
AD7124_CS_H;
data_temp_2 = Data_2;
data_last2 = (float)Vref/AD_Gain * ((float)data_temp_2/0X800000-1); //双极性模式电压转换公式,单位mV
ad_counter++; //采集通道计数器+1,采集下一个通道
break;
case 3:
AD7124_Reset();
delay_us(5); //复位后必须要延时
AD7124_CS_L;
AD7124_Ch3_INIT(Samprate_1kHz); //多通道初始化函数
AD7124_Set_Gain(1); //1倍增益
AD7124_CS_H;
AD7124_CS_L;
AD7124_SPI_ReadWrite(0x42); //读操作
Data_3 = AD7124_Read_Data(3); //Data采集结果
AD7124_CS_H;
data_temp_3 = Data_3;
data_last3 = (float)Vref/AD_Gain * ((float)data_temp_3/0X800000-1); //双极性模式电压转换公式,单位mV
ad_counter++; //采集通道计数器+1,采集下一个通道
break;
case 4:
AD7124_Reset();
delay_us(5); //复位后必须要延时
AD7124_CS_L;
AD7124_Ch4_INIT(Samprate_1kHz); //多通道初始化函数
AD7124_Set_Gain(1); //1倍增益
AD7124_CS_H;
AD7124_CS_L;
AD7124_SPI_ReadWrite(0x42); //读操作
Data_4 = AD7124_Read_Data(3); //Data采集结果
AD7124_CS_H;
data_temp_4 = Data_4;
data_last4 = (float)Vref/AD_Gain * ((float)data_temp_4/0X800000-1); //双极性模式电压转换公式,单位mV
ad_counter++; //采集通道计数器+1,采集下一个通道
break;
case 5:
AD7124_Reset();
delay_us(5); //复位后必须要延时
AD7124_CS_L;
AD7124_Ch5_INIT(Samprate_1kHz); //多通道初始化函数
AD7124_Set_Gain(1); //1倍增益
AD7124_CS_H;
AD7124_CS_L;
AD7124_SPI_ReadWrite(0x42); //读操作
Data_5 = AD7124_Read_Data(3); //Data采集结果
AD7124_CS_H;
data_temp_5 = Data_5;
data_last5 = (float)Vref/AD_Gain * ((float)data_temp_5/0X800000-1);
ad_counter=0;
break;
}
但是这种架构有个最大的问题:在增益PGA=1的情况下,采集±2V以上的模拟量会出现较为严重的数据失真,给与Ch0~3通道的实际模拟直流量分别为2.4V、2.3V、2.2V与2.1V,具体表现见下图GIF。
如图所示,采集到的数据比实际值要小得多,而且会发生不规律的跳动。
二、原因分析
通过查阅AD7124手册,发现并没有详细记载此案例的资料。但有一点可以肯定,AD7124从采集模拟量到建立数据是需要一定的时间的;在老的代码架构中,超级循环内频繁地对芯片初始化,势必会影响采集数据的建立时间,再加上PGA越大的情况下所需要的数据建立时间也就越长,因此会出现上Gif图的情况。
但是在PGA=1以外的情况,以上架构中暂时看不出问题。
三、多通道同时初始化的程序
为了解决PGA=1的情况采集2V以上电压的数据失真问题,本人不得不探索多通道同时初始化&读取的方案,这也是很多人私信要求的,今天来解决大家的诉求!
1. 芯片初始化
新的AD7124初始化代码如下:
/*************多通道初始化函数*************/
void AD7124_MUL_INIT(uint8_t SamHz)
{
AD7124_Reset();
delay_us(100);
//写AD7124 控制寄存器
AD7124_Write_Reg(AD7124_ADC_CTRL_REG,2,AD7124_ADC_CTRL_REG_DATA_STATUS|\ //通道指示
AD7124_ADC_CTRL_REG_REF_EN|\ //内部参考打开
AD7124_ADC_CTRL_REG_POWER_MODE(2)|\ //POWER_MODE(x) x=0 低功耗 x=1 中功率 x=2、3全功率
AD7124_ADC_CTRL_REG_MODE(0) | AD7124_ADC_CTRL_REG_CLK_SEL(0) |\ //CTRL_REG_MODE(x) x=0 连续转换模式 x=1 单次转换模式 其他模式请看手册 | CLK_SEL(x) x=0 内部 x=2 外部
AD7124_ADC_CTRL_REG_CS_EN);
#ifdef AD7124_4
// //写AD7124 IO控制寄存器1
// AD7124_Write_Reg(AD7124_IO_CTRL1_REG,3,AD7124_IO_CTRL1_REG_IOUT0(0) | AD7124_IO_CTRL1_REG_IOUT_CH0(15));
// //IOUT0(x) 0-7电流分别是关闭 50 100 250 500 750 1000 1000uA | CH0(x)x=0 AIN0 x=1 AIN1 x=4 AIN2 x=5 AIN3 x=10 AIN4 x=11 AIN5 x=14 AIN6 x=15 AIN7
// AD_Delay(50000);//修改IO控制寄存器恒流源 需要延迟久一些
// //写AD7124 IO控制寄存器2
// AD7124_Write_Reg(AD7124_IO_CTRL2_REG,2,AD7124_IO_CTRL2_REG_GPIO_VBIAS1);
// //内部偏置电压使能 VBIAS0-7 对应通道0-7
//写AD7124 通道0寄存器
AD7124_Write_Reg(AD7124_CH0_MAP_REG,2,AD7124_CH_MAP_REG_CH_ENABLE | AD7124_CH_MAP_REG_SETUP(0) | AD7124_CH_MAP_REG_AINP(0) | AD7124_CH_MAP_REG_AINM(1));
//使能通道0 | 用配置0 | AINP(x) ADC+选择 0-7对应通道0-7 | AINM(x) ADC-选择 0-7对应通道0-7 其他通道请查看手册
//写AD7124 通道1寄存器
AD7124_Write_Reg(AD7124_CH1_MAP_REG,2,AD7124_CH_MAP_REG_CH_ENABLE | AD7124_CH_MAP_REG_SETUP(0) | AD7124_CH_MAP_REG_AINP(2) | AD7124_CH_MAP_REG_AINM(3));
//使能通道1 | 用配置0 | AINP(x) ADC+选择 0-7对应通道0-7 | AINM(x) ADC-选择 0-7对应通道0-7 其他通道请查看手册
//写AD7124 通道2寄存器
AD7124_Write_Reg(AD7124_CH2_MAP_REG,2,AD7124_CH_MAP_REG_CH_ENABLE | AD7124_CH_MAP_REG_SETUP(0) | AD7124_CH_MAP_REG_AINP(4) | AD7124_CH_MAP_REG_AINM(5));
//使能通道2 | 用配置0 | AINP(x) ADC+选择 0-7对应通道0-7 | AINM(x) ADC-选择 0-7对应通道0-7 其他通道请查看手册
//写AD7124 通道3寄存器
AD7124_Write_Reg(AD7124_CH3_MAP_REG,2,AD7124_CH_MAP_REG_CH_ENABLE | AD7124_CH_MAP_REG_SETUP(0) | AD7124_CH_MAP_REG_AINP(6) | AD7124_CH_MAP_REG_AINM(7));
//使能通道3 | 用配置0 | AINP(x) ADC+选择 0-7对应通道0-7 | AINM(x) ADC-选择 0-7对应通道0-7 其他通道请查看手册
#endif
#ifdef AD7124_8
// AD7124_Write_Reg(AD7124_IO_CTRL1_REG,3,AD7124_IO_CTRL1_REG_IOUT0(0) | AD7124_IO_CTRL1_REG_IOUT_CH0(15));
// //IOUT0(x) 0-7电流分别是关闭 50 100 250 500 750 1000 1000uA | CH0(x) 0-15分别是0-15通道
// AD_Delay(50000);//修改IO控制寄存器恒流源 需要延迟久一些
// //写AD7124 IO控制寄存器2
// AD7124_Write_Reg(AD7124_IO_CTRL2_REG,2,AD7124_IO_CTRL2_REG_GPIO_VBIAS1);
// //内部偏置电压使能 VBIAS0-15 对应通道0-15
//写AD7124 通道0寄存器
AD7124_Write_Reg(AD7124_CH0_MAP_REG,2,AD7124_CH_MAP_REG_CH_ENABLE | AD7124_CH_MAP_REG_SETUP(0) | AD7124_CH_MAP_REG_AINP(0) | AD7124_CH_MAP_REG_AINM(1));
//使能通道0 | 用配置0 | AINP(x) ADC+选择 0-15对应通道0-15 | AINM(x) ADC-选择 0-15对应通道0-15 其他通道请查看手册
//写AD7124 通道1寄存器
AD7124_Write_Reg(AD7124_CH1_MAP_REG,2,AD7124_CH_MAP_REG_CH_ENABLE | AD7124_CH_MAP_REG_SETUP(0) | AD7124_CH_MAP_REG_AINP(2) | AD7124_CH_MAP_REG_AINM(3));
//使能通道1 | 用配置0 | AINP(x) ADC+选择 0-15对应通道0-15 | AINM(x) ADC-选择 0-15对应通道0-15 其他通道请查看手册
//写AD7124 通道2寄存器
AD7124_Write_Reg(AD7124_CH2_MAP_REG,2,AD7124_CH_MAP_REG_CH_ENABLE | AD7124_CH_MAP_REG_SETUP(0) | AD7124_CH_MAP_REG_AINP(4) | AD7124_CH_MAP_REG_AINM(5));
//使能通道2 | 用配置0 | AINP(x) ADC+选择 0-15对应通道0-15 | AINM(x) ADC-选择 0-15对应通道0-15 其他通道请查看手册
//写AD7124 通道3寄存器
AD7124_Write_Reg(AD7124_CH3_MAP_REG,2,AD7124_CH_MAP_REG_CH_ENABLE | AD7124_CH_MAP_REG_SETUP(0) | AD7124_CH_MAP_REG_AINP(6) | AD7124_CH_MAP_REG_AINM(7));
//使能通道3 | 用配置0 | AINP(x) ADC+选择 0-15对应通道0-15 | AINM(x) ADC-选择 0-15对应通道0-15 其他通道请查看手册
AD7124_Write_Reg(AD7124_CH4_MAP_REG,2,AD7124_CH_MAP_REG_CH_DISABLE | AD7124_CH_MAP_REG_SETUP(0) | AD7124_CH_MAP_REG_AINP(8) | AD7124_CH_MAP_REG_AINM(9));
AD7124_Write_Reg(AD7124_CH5_MAP_REG,2,AD7124_CH_MAP_REG_CH_DISABLE | AD7124_CH_MAP_REG_SETUP(0) | AD7124_CH_MAP_REG_AINP(10) | AD7124_CH_MAP_REG_AINM(11));
AD7124_Write_Reg(AD7124_CH7_MAP_REG,2,AD7124_CH_MAP_REG_CH_DISABLE | AD7124_CH_MAP_REG_SETUP(0) | AD7124_CH_MAP_REG_AINP(12) | AD7124_CH_MAP_REG_AINM(13));
AD7124_Write_Reg(AD7124_CH8_MAP_REG,2,AD7124_CH_MAP_REG_CH_DISABLE | AD7124_CH_MAP_REG_SETUP(0) | AD7124_CH_MAP_REG_AINP(14) | AD7124_CH_MAP_REG_AINM(15));
#endif
//写AD7124 配置0寄存器
AD7124_Write_Reg(AD7124_CFG0_REG,2,AD7124_CFG_REG_BIPOLAR | AD7124_CFG_REG_AIN_BUFP | AD7124_CFG_REG_AINN_BUFM | AD7124_CFG_REG_REF_SEL(2) |\
AD7124_CFG_REG_PGA(0));
//双极性 | 打开ADC+缓冲 | 打开ADC-缓冲 | REF_SEL(x) x=0 REFIN1(+)/REFIN1(-) x=1 REFIN2(+)/REFIN2(-) x=2 内部基准电压源 x=3 AVDD
//REG_PGA(x) 0-7对应增益1(±2.5V) 2(±1.25V) 4(± 625mV ) 8(±312.5 mV ) 16(±156.25 mV) 32(±78.125 mV) 64(±39.06mV) 128(±19.53mV)
Samprate=SamHz;
switch(SamHz)
{
case Samprate_2Hz:
AD7124_Write_Reg(AD7124_FILT0_REG,3,AD7124_FILT_REG_FILTER(7) | AD7124_FILT_REG_REJ60 | \
AD7124_FILT_REG_POST_FILTER(6) | AD7124_FILT_REG_FS(0));
break;
case Samprate_5Hz:
AD7124_Write_Reg(AD7124_FILT0_REG,3,AD7124_FILT_REG_FILTER(7) | AD7124_FILT_REG_REJ60 | \
AD7124_FILT_REG_POST_FILTER(3) | AD7124_FILT_REG_FS(0));
break;
case Samprate_10Hz:
AD7124_Write_Reg(AD7124_FILT0_REG,3,AD7124_FILT_REG_FILTER(5) | AD7124_FILT_REG_REJ60 | \
AD7124_FILT_REG_POST_FILTER(0) | AD7124_FILT_REG_FS(24));
break;
case Samprate_20Hz:
AD7124_Write_Reg(AD7124_FILT0_REG,3,AD7124_FILT_REG_FILTER(0) | AD7124_FILT_REG_REJ60 | \
AD7124_FILT_REG_POST_FILTER(0) | AD7124_FILT_REG_FS(30));
break;
case Samprate_50Hz:
AD7124_Write_Reg(AD7124_FILT0_REG,3,AD7124_FILT_REG_FILTER(0) | AD7124_FILT_REG_REJ60 | \
AD7124_FILT_REG_POST_FILTER(0) | AD7124_FILT_REG_FS(22));
break;
case Samprate_100Hz:
AD7124_Write_Reg(AD7124_FILT0_REG,3,AD7124_FILT_REG_FILTER(0) | AD7124_FILT_REG_REJ60 | \
AD7124_FILT_REG_POST_FILTER(0) | AD7124_FILT_REG_FS(11));
break;
case Samprate_200Hz:
AD7124_Write_Reg(AD7124_FILT0_REG,3,AD7124_FILT_REG_FILTER(0) | AD7124_FILT_REG_REJ60 | \
AD7124_FILT_REG_POST_FILTER(0) | AD7124_FILT_REG_FS(5));
break;
case Samprate_500Hz:
AD7124_Write_Reg(AD7124_FILT0_REG,3,AD7124_FILT_REG_FILTER(2) | AD7124_FILT_REG_REJ60 | \
AD7124_FILT_REG_POST_FILTER(0) | AD7124_FILT_REG_FS(2));
break;
case Samprate_1kHz:
AD7124_Write_Reg(AD7124_FILT0_REG,3,AD7124_FILT_REG_FILTER(2) | AD7124_FILT_REG_REJ60 | \
AD7124_FILT_REG_POST_FILTER(0) | AD7124_FILT_REG_FS(1));
break;
}
//FILTER(x)x=0 sinc4 x=2 sinc3 x=7 后置滤波器 其他请查看手册 | 打开60 Hz陷波 | POST_FILTER(x) 后置滤波器设置查看手册 | FS(x)具体滤波配置查看网址
//http://beta-tools.analog.com/virtualeval/#tool_pid=AD7124-4&tab=fbd
//此网页可以用于详细配置 滤波频率 因为不是整数值
}
由上述代码可见,需要用到的通道全部都在一个初始化程序内,且只在超级循环外初始化1次就够了;并且,照顾到用户所使用芯片种类的不同,可以在头文件ad7124.h使用不同的宏定义来使能特定的代码段。
本人这里用到的是AD7124-8,一次能使用8组通道,但是本次实验只用了前4组。
#ifndef _AD7124_H
#define _AD7124_H
#include <stdint.h>
#include "sys.h"
#define AD7124_CS_H GPIO_SetBits(GPIOB,GPIO_Pin_12)
#define AD7124_CS_L GPIO_ResetBits(GPIOB,GPIO_Pin_12)
//#define AD7124_4 0x00
#define AD7124_8 0x01
#define Multi_Channel
//#define Single_Channel
#define Vref 2500.0 //参考电压±2.5V
#define AD_Tim_Interval 1000 //单位us
2. 主程序架构
最新优化的代码架构如下:
uint8_t AD_ID_REG; //AD7124芯片ID
uint32_t Data; //原始数据
uint32_t AD7124_UART_DATA[8], AD7124_DATA_Temp; //数据缓存
float Value[8]; //存储没给通道数据的数组
extern uint8_t AD_Gain; //ADC增益数值
int main(void)
{
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
// uart_init(128000);
AD7124_SPI_Config(); //AD7124硬件初始化
/*AD7124校准,在芯片初始化的时候设置*/
// AD7124_Write_Reg(AD7124_IO_CTRL2_REG,2,0x4410);
AD7124_Reset(); //AD7124复位
delay_us(5); //复位后必须要延时
AD7124_MUL_INIT(Samprate_1kHz); //多通道初始化函数
AD_ID_REG = Get_AD7124_ID(); //读取1号AD7124-8 ID = 8:0x14/4:0x04
AD7124_Set_Gain(1); //1倍增益
while(1)
{
AD7124_CS_L; //读操作前需要把片选拉低
AD7124_SPI_ReadWrite(0x42); //读操作
Data = AD7124_Read_Data(3); //Data采集结果
AD_statusReg = AD7124_SPI_ReadWrite(0XFF) & 0x0F; //获取AD7124的通道号,轮询获取
AD7124_CS_H;
AD7124_UART_DATA[AD_statusReg] = Data;
AD7124_DATA_Temp = Data;
// Value[AD_statusReg] = (float)Vref/AD_Gain * (float)AD7124_DATA_Temp/(2*8388608.0); //单极性模式电压转换公式,单位mV
Value[AD_statusReg] = (double)Vref/AD_Gain * ((double)(AD7124_DATA_Temp/8388608.0)-1); //双极性模式电压转换公式,单位mV
}
}
我们可以看到相比老架构更加精简,且在所有的PGA增益下都不会发生采样的失真。下图是用上述代码采集到的数值;由此可见,debug中观测到的数值和实际输入的模拟量仅有10~20mV的差别。
资料链接
代码可以在此处下载: