TI公司16位ADC芯片 --- ADS1158 单端模式使用心得

33 篇文章 5 订阅

【本文发布于https://blog.csdn.net/Stack_/article/details/116331897,未经许可不得转载,转载须注明出处】




以下为个人理解,如有不对的地方欢迎指正。



一、电路

在这里插入图片描述

单端模式采集16路模拟量电路

外部运放电路为电压跟随器,在ADS1158的手册内也有此电路。如果16路模拟信号直连AD转换器,转换结果和实际电压偏差大,尝试将ADS1158寄存器位[BYPAS]设为1(附件工程内搜索DATA_CONFIG0更改配置),AN0-AN15以及AINCOM信号将经过外部电压跟随器后再回到ADS1158内部进行AD转换,可有效改善。



二、SPI

在这里插入图片描述

使用外接16M晶振时,SPI最高波特率为8M



根据时序配置SPI

@ CSDN Tyrion.Mon
/**
  * @brief  初始化SPI0
  * @note   SPI0_NSS -- PA4
  *         SPI0_SCK -- PA5
  *         SPI0_MI  -- PA6
  *         SPI0_MO  -- PA7
  * @param  None
  * @retval None
  * @author PWH
  * @date   2021/3
  */
void SPI0_Init(void)
{
	RCU->RCU_APB2EN.Bits.SPI0EN = 1;			//
	
	/* SCK复用输出 */
	Gd32f30x_Gpio_Init(GPIO_PA5, GPIO_MODE_OUT_50MHZ, GPIO_CTL_AF_PP);
	/* MI上拉输入 */
	Gd32f30x_Gpio_Init(GPIO_PA6, GPIO_MODE_IN, GPIO_CTL_IPU);
	/* MO复用输出 */
	Gd32f30x_Gpio_Init(GPIO_PA7, GPIO_MODE_OUT_50MHZ, GPIO_CTL_AF_PP);
	/* SPI0 */
	//1:主机模式
	SPI0->SPI_CTL0.Bits.MSTMOD = 1;
	//0: 2 线单向传输模式
	SPI0->SPI_CTL0.Bits.BDEN = 0;
	//0:全双工模式(当 BDEN 清零时)
	SPI0->SPI_CTL0.Bits.RO = 0;
	//0: 8 位数据帧格式
	SPI0->SPI_CTL0.Bits.FF16 = 0;
	//1: NSS 软件模式, NSS 电平取决于 SWNSS 位
	SPI0->SPI_CTL0.Bits.SWNSSEN = 1;
	SPI0->SPI_CTL0.Bits.SWNSS = 1;
	//SPI0时钟来源:APB2   psc=011 : 16分频  
	//120M/16=7.5<(ADS1158 16M外部晶振时,SPI最高速度8M)
	SPI0->SPI_CTL0.Bits.PSC = 3;
	//0:先发送最高有效位
	SPI0->SPI_CTL0.Bits.LF = 0;
	//0: SPI 为空闲状态时, CLK 引脚拉低
	SPI0->SPI_CTL0.Bits.CKPL = 0;
	//0:在第1个时钟跳变沿采集第一个数据
	SPI0->SPI_CTL0.Bits.CKPH = 0;
	//1: RBNE 中断使能。当 RBNE 置位时,产生中断。
//	SPI0->SPI_CTL1.Bits.RBNEIE = 1;
	//1: SPI 设备使能
	SPI0->SPI_CTL0.Bits.SPIEN = 1;
}



三、命令字节

在这里插入图片描述

@ CSDN Tyrion.Mon
/* 命令字节的bits[7:5] 表示命令类型 */
#define COMMAND_CH_DATA_DIRECT					0x00	//直接读取通道数据
#define COMMAND_CH_DATA_COMMAND					0x20	//命令读取通道数据
#define COMMAND_READ_REGISTER					0x40	//读寄存器
#define COMMAND_WRITE_REGISTER					0x60	//写寄存器
#define COMMAND_PULSE_CONVERT					0x80	//开始转换,此命令bit[4:0]无效
#define COMMAND_RESEVED							0xA0	//保留
#define COMMAND_RESET							0xC0	//reset,此命令bit[4:0]无效
//#define COMMAND_CH_DATA_DIRECT					0xE0	//和0x00↑一样

/* 命令字节的bits[4] */
#define ENABLE_MULTIPLE_REG_ACCESS				0x10	//启用多寄存器访问
#define DISABLE_MULTIPLE_REG_ACCESS				0x00	//禁用多寄存器访问

/* 命令字节的bits[3:0] 表示访问的寄存器地址 */
#define REG_ADDR_CONFIG0						0x00	//配置寄存器0
#define REG_ADDR_CONFIG1						0x01	//配置寄存器1
#define REG_ADDR_MUXSCH							0x02	//多路复用固定通道寄存器
#define REG_ADDR_MUXDIF							0x03	//多路复用器差分输入选择寄存器
#define REG_ADDR_MUXSG0							0x04	//多路复用单端输入选择寄存器0
#define REG_ADDR_MUXSG1							0x05	//多路复用单端输入选择寄存器1
#define REG_ADDR_SYSRED							0x06	//系统读取选择寄存器
#define REG_ADDR_GPIO_CONFIG					0x07	//GPIO配置寄存器(输入输出配置)
#define REG_ADDR_GPIO_DATA						0x08	//GPIO数据寄存器(置位、清零、读状态)
#define REG_ADDR_ID								0x09	//出厂ID

/* 软/命令复位 bit[4:0]无效 */
#define COMMAND_SOFE_RESET						COMMAND_RESET
/* 软 脉冲触发单次转换 bit[4:0]无效 */
#define COMMAND_SOFE_PULSE_CONVERT				COMMAND_PULSE_CONVERT
/* 命令读通道数据 bit[3:0]无效 */
#define COMMAND_CH_DATA_READ					(COMMAND_CH_DATA_COMMAND | ENABLE_MULTIPLE_REG_ACCESS)

读单个寄存器时,发送COMMAND_READ_REGISTER | DISABLE_MULTIPLE_REG_ACCESS | 寄存器地址,再发送任意一个字节,即可获得该寄存器值;

写单个寄存器时,发送COMMAND_WRITE_REGISTER | DISABLE_MULTIPLE_REG_ACCESS | 寄存器地址,再发送配置值,即可更改该寄存器的值。



四、寄存器

在这里插入图片描述

CONFIG0@ CSDN Tyrion.Mon
BIT50:自动扫描模式;1:固定通道模式。在这里配为自动扫描
BIT40:AD转换器直接从16个通道采样;1:如下图,16个通道模拟量从MUXOUTP和MUXOUTN输出,经外部电路(如果设计有)处理过后,输入到ADCINP和ADCINN再到AD转换器。适用于需要放大输入信号或者对信号作其它处理的情况,可以节省硬件电路。
BIT30:使用外部晶振;1:晶振引脚输出时钟
BIT2未明白,可维持默认值
BIT10:转换完成后读取AD值时伴随表示数据状态的一个字节
CONFIG1@ CSDN Tyrion.Mon
BIT7如果对耗电没有要求,选择待机模式,理论上从待机模式下启动转换会快些
BIT[6:4]不是很明白,保持默认即可
BIT[3:2]不是很明白,保持默认即可
BIT[1:0]对转换速度没有要求就选最大值,转换速度选择越慢的越精确
MUXSCH@ CSDN Tyrion.Mon
BIT[7:0]在固定通道模式下使用,测量有正有负的电压,参考电源须由5V - 0V改为+2.5V - -2.5V。AINPx指定电压对的正压通道,AINNx指定电压对的负压通道。(懵逼,不知道怎么用)
MUXDIF
BIT[7:0]AIN0置位时,AIN0和AIN1组成一对差分信号对,电压为两者差值; AIN1置位时,AIN2和AIN3组成一对差分信号对;…在自动扫描模式下有效,未选中的在一轮扫描中会被跳过。
MUXSG0@ CSDN Tyrion.Mon
BIT[7:0]AIN0置位时,通道AIN0被选中为单端模式,电压为AIN0和AINCOM的差值;AIN1置位时,通道AIN1被选中为单端模式,电压为AIN1和AINCOM的差值。…在自动扫描模式下有效,,未选中的在一轮扫描中会被跳过。
MUXSG1@ CSDN Tyrion.Mon
BIT[7:0]AIN0置位时,通道AIN8被选中为单端模式,电压为AIN8和AINCOM的差值。…在自动扫描模式下有效,未选中的在一轮扫描中会被跳过。
SYSRED@ CSDN Tyrion.Mon
BIT[7:0]未明

在这里插入图片描述

看懂这个图就最好



五、开启转换

在上电不进行配置时,ADS1158会自动扫描所有通道,用示波器能看到DRDY引脚有波形输出。

在这里插入图片描述

1.持续转换: 配置好ADS1158后,把START脚一直拉高,会自动扫描选中的所有通道。扫描完一个通道并准备好从SPI输出数据时,会拉低DRDY引脚一段时间,然后拉高继续转换下一通道,转换完成再次拉低DRDY。如果没能及时将数据读出来,将会导致数据被覆盖;
2. 脉冲转换:配置好ADS1158后,把START拉高立刻又拉低,ADS1158将只转换当前通道,转换完成后拉低DRDY,并指向下一通道。而后进入待机或睡眠模式。



六、数据读取

将转换模式配置为脉冲转换:START脚触发转换,DRDY引脚变低后读取三字节通道数据。不断循环此操作

在这里插入图片描述

以第一个字节的bit7判断数据是否有效:为1时为新/已更新数据。
以第一个字节的bit[4:0]判断当前数据为哪个通道的数据。

在这里插入图片描述



七、数据解析

第二、第三个字节的数据需要根据公式进行转换为电压。
.. ..

在这里插入图片描述






@ CSDN Tyrion.Mon
/**
  * @brief  向一个寄存器写一个字节
  * @note   
  * @param  None
  * @retval None
  * @author PWH
  * @date   2021/3
  */
static uint16_t Ads1158_WriteOneReg(uint8_t Reg, uint8_t Byte)
{	
	Ads1158_Select(CS_ENABLE);
	
	SPIx_SendData(SPI0, COMMAND_WRITE_REGISTER | DISABLE_MULTIPLE_REG_ACCESS | Reg);
	SPIx_SendData(SPI0, Byte);
	
	Ads1158_Select(CS_DISABLE);
	
	return 0;
}
/**
  * @brief  ADS 读寄存器
  * @note   
  * @param  None
  * @retval 
  * @author PWH
  * @date   2021/3
  */
static uint16_t Ads1158_ReadOneReg(uint8_t Reg)
{
	uint16_t rec = 0;
	
	Ads1158_Select(CS_ENABLE);
	
	rec = SPIx_SendData(SPI0, COMMAND_READ_REGISTER | DISABLE_MULTIPLE_REG_ACCESS | Reg);
	rec = SPIx_SendData(SPI0, 0x00);
	
	Ads1158_Select(CS_DISABLE);
//	printf("Reg = %#x\r\n", rec);
	return rec;
}
/**
  * @brief  ADS软件复位
  * @note   
  * @param  None
  * @retval None
  * @author PWH
  * @date   2021/3
  */
static void Ads1158_Reset(void)
{
	Ads1158_Select(CS_ENABLE);
	
	SPIx_SendData(SPI0, COMMAND_SOFE_RESET);
	
	Ads1158_Select(CS_DISABLE);
}

/**
  * @brief  ADS 读ID寄存器进行通讯测试
  * @note   
  * @param  None
  * @retval 
  * @author PWH
  * @date   2021/3
  */
static int8_t Ads1158_CommTest(void)
{
	if ((uint8_t)(Ads1158_ReadOneReg(REG_ADDR_ID)) == DEVICE_ID)
		return 1;
	return -1;
}

/**
  * @brief  初始化ADS1158
  * @note       SPI  -- SPI0
  *             CS   -- PA4
  *             START-- PB2
  *             DRDY -- PF11
  *             RST  -- PF12
  * @param  None
  * @retval None
  * @author PWH
  * @date   2021/3
  */
void Ads1158_Init(void)
{
	/* CS推挽输出 */
	Gd32f30x_Gpio_Init(ADS1158_CS_PIN, GPIO_MODE_OUT_50MHZ, GPIO_CTL_OUT_PP);
	Gd32f30x_Gpio_Write(ADS1158_CS_PIN, GPIO_SET);
	/* START 推挽输出 */
	Gd32f30x_Gpio_Init(ADS1158_START_PIN, GPIO_MODE_OUT_50MHZ, GPIO_CTL_OUT_PP);
	Gd32f30x_Gpio_Write(ADS1158_START_PIN, GPIO_RESET);
	/* DRDY(ADS1158数据准备好输出)上拉输入 */
	Gd32f30x_Gpio_Init(ADS1158_DRDY_PIN, GPIO_MODE_IN, GPIO_CTL_IPU);
	/* RST 推挽输出 */
	Gd32f30x_Gpio_Init(ADS1158_RST_PIN, GPIO_MODE_OUT_50MHZ, GPIO_CTL_OUT_PP);
	Gd32f30x_Gpio_Write(ADS1158_RST_PIN, GPIO_RESET);
	
	SPI0_Init();

	Gd32f30x_Gpio_Write(ADS1158_RST_PIN, GPIO_SET);

	Ads1158_Reset();
	
	while (Ads1158_CommTest() != 1);
	
	//CONFIG0 
	Ads1158_WriteOneReg(REG_ADDR_CONFIG0, DATA_CONFIG0);
	//CONFIG1
	Ads1158_WriteOneReg(REG_ADDR_CONFIG1, DATA_CONFIG1);
	//MUXSG0
	Ads1158_WriteOneReg(REG_ADDR_MUXSG0, DATA_MUXSG0);
	//MUXSG1
	Ads1158_WriteOneReg(REG_ADDR_MUXSG1, DATA_MUXSG1);
	//GPIO
	Ads1158_WriteOneReg(REG_ADDR_GPIO_CONFIG, DATA_GPIO_CONFIG);
	
	if (Ads1158_ReadOneReg(REG_ADDR_CONFIG0) != DATA_CONFIG0)
	{
		while (1);
	}
	if (Ads1158_ReadOneReg(REG_ADDR_CONFIG1) != DATA_CONFIG1)
	{
		while (1);
	}
	if (Ads1158_ReadOneReg(REG_ADDR_MUXSG0) != DATA_MUXSG0)
	{
		while (1);
	}
	if (Ads1158_ReadOneReg(REG_ADDR_MUXSG1) != DATA_MUXSG1)
	{
		while (1);
	}
	if (Ads1158_ReadOneReg(REG_ADDR_GPIO_CONFIG) != DATA_GPIO_CONFIG)
	{
		while (1);
	}
}
/**
  * @brief  检测到DRDY引脚下降沿后读取通道数据
  * @note   
  * @param  None
  * @retval None
  * @author PWH
  * @date   2021/3
  */
void Ads1158_ReadChannelDataWithoutCmd(uint8_t *pBuff, uint8_t len)
{
	uint8_t i = 0;
	
	Ads1158_Select(CS_ENABLE);
	for (i = 0; i < len; i++)
	{
		*pBuff++ = SPIx_SendData(SPI0, 0x00);
	}
	Ads1158_Select(CS_DISABLE);
}
/**
  * @brief  ADS开始转换
  * @note   
  * @param  None
  * @retval None
  * @author PWH
  * @date   2021/3
  */
void Ads1158_StartStopConvert(uint8_t StartStop)
{
	if (StartStop == CONVERT_START)
	{
		Gd32f30x_Gpio_Write(ADS1158_START_PIN, GPIO_SET);
	}
	else
	{
		Gd32f30x_Gpio_Write(ADS1158_START_PIN, GPIO_RESET);
	}
}

/**
  * @brief  ADS数据是否准备输出
  * @note   
  * @param  None
  * @retval 0 -- ready
  * @author PWH
  * @date   2021/3
  */
int8_t Ads1158_DataReady(void)
{
	if (Gd32f30x_Gpio_Read(ADS1158_DRDY_PIN) == DATA_READY)
		return DATA_READY;
	return -1;
}

@ CSDN Tyrion.Mon
static uint8_t Ads1158_ReadComplete = true;		// 1 表示已读取通道
uint16_t Ads1158_ChConvertData[16] = {0x00};	// 通道转换的原始数据
uint32_t Ads1158_ChRealVoltage[16] = {0x00};	// 通道电压(根据原始数据转换)
uint32_t Ads1158_AD[16] = {0x00};				// 通道AD(根据电压数据转换)

/**
  * @brief  
  * @note   
  * @param  None
  * @retval None
  * @author PWH
  * @date   2021/4
  */
void Ads1158_Proc(void)
{
	uint8_t i;
	static uint32_t ads_time = 0;
	uint8_t array[3];
	
	/* 启动转换 */
	if ((Ads1158_ReadComplete == true) && (Ads1158_DataReady() != DATA_READY))
	{
		Ads1158_ReadComplete = false;
		Ads1158_StartStopConvert(CONVERT_START);	// 脉冲触发转换,只转换一个通道
		Ads1158_StartStopConvert(CONVERT_STOP);		// 
	}

	if (UserTimer_Read(&ads_time) > 5000)	//超时
	{
		printf("                            ADS ERROR RESET\r\n ");
		UserTimer_Reset(&ads_time);
		Ads1158_ReadComplete = true;
	}
	
	/* 读取数据 */
	if ((Ads1158_ReadComplete == false) && (Ads1158_DataReady() == DATA_READY))
	{
		/* 读取转换数据 */
		Ads1158_ReadChannelDataWithoutCmd(array, 3);
		if (array[0] & 0x80)	//数据已刷新
		{
			array[0] &= 0x1f;	//bit4-bit0表示当前读回的数据所属通道
			if ( (array[0] >= DATA_AIN0) && (array[0] <= DATA_AIN15) )
			{
				Ads1158_ChConvertData[array[0] - DATA_AIN0] = ((uint16_t)array[1]) << 8 | array[2];
				Ads1158_ChRealVoltage[array[0] - DATA_AIN0] = (uint32_t)(Ads1158_ChConvertData[array[0] - DATA_AIN0] * REFERENCE_VOLTAGE / 30720.0);
				Ads1158_AD[array[0] - DATA_AIN0] = Ads1158_ChRealVoltage[array[0] - DATA_AIN0] * 65536.0 / REFERENCE_VOLTAGE;
			}
			if (array[0] == DATA_AIN15)
			{
				UserTimer_Reset(&ads_time);
			}
			Ads1158_ReadComplete = true;	//表示已读取通道
		}
	}
}




PCB及代码



【PCB及代码】

AD7606 数据采集模块,16ADC,8通道同时200KHz频率采集,每秒8*200K样本。SPI接口或8080 16并口,可自行选择。 AD7606 数据采集模块特性: 使用AD7606 高精度16ADC芯片 8路模拟输入。阻抗1M欧姆。【无需负电源,无需前端模拟运放电路,可直接接传感器输出】 输入范围正负5V,正负10V。可通过IO控制量程。 分辨率 16。 最大采样频率 200Ksps。 支持8档过采样设置(可以有效降低抖动) 内置基准 单5V供电 SPI接口或16总线接口。接口IO电平可以是5V或3.3V AD7606 数据采集模块实物截图: 2种接口方式: 并口模式跳线:R1 悬空(不贴),R2贴10K电阻 SPI接口模式跳线:R1 贴10K电阻,R2 悬空(不贴) 附件内容例程主要包括AD7606_SPI例程、ADS7606_SPI_51单片机例程等 见截图; 【软件定时采集的实现方案1】 --- 我们提供的SPI例子采用这种方案,见bsp_spi_ad7606.c文件 在定时器中断服务程序中实现: 定时器中断ISR: { 中断入口; 读取8个通道的采样结果保存到RAM; ----> 读取的是上次的采集结果,对于连续采集来说,是没有关系的 启动下次ADC采集;(翻转CVA和CVB) 中断返回; } 定时器的频率就是ADC采样频率。这种模式可以不连接BUSY口线。 【软件定时采集的实现方案2】 --- 我们提供的8080接口例子采用这种方案,见bsp_ad7606.c文件 配置CVA、CVB引脚为PWM输出模式,周期设置为需要的采样频率; ----> 之后MCU将产生周期非常稳定的AD转换信号 将BUSY口线设置为中断下降沿触发模式; 外部中断ISR: { 中断入口; 读取8个通道的采样结果保存到RAM; } 【软件定时采集的实现方案1和方案2的差异】 (1)方案1 可以少用 BUSY口线,但是其他中断服务程序或者主程序临时关闭全局中断时,可能导致ADC转换周期存在轻微抖动。 (2)方案2 可以确保采集时钟的稳定性,因为它是MCU硬件产生的。但是需要多接一根BUSY口线。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值