AD7124罕见的Bug与多通道读取的研究

目录

  • 一、基于单通道轮询读取的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的差别。

正常数据

资料链接

代码可以在此处下载:

链接: 驱动程序:硬件SPI控制AD7124(多通道读取版)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hex囧雪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值