STM32F407单片机移植ADS1115驱动程序

2023-05-16

    最近一个工程项目需要使用ADS1115采集电压,网上研究了一下,测试成功,期间走了很多弯路,为避免后来的研究者重走我的老路,特分享给大家。

注:网上很多ADS1115驱动程序移植到STM32F407单片机上虽然可以采集到电压,但是采集的电压有问题,大部分程序只能采集到高8位,采集不到低8位(低8位始终为0),用万用表添加电压信号,只有当电压变化幅度在0.2V---0.3V以上时,采集的码值才会发生变化。

        本程序经过测试,电压只要变化幅度在0.01V,采集的码值都会发生变化,以下数据是我的实测值(码值)。

//0V=5  1V=731  2V=1457  3V=2184  4V=2910  5V=3636

一、ADS1115概况

ADS1115是德州仪器推出的具有IIC接口的16位ADC转换器,超小型X2QFN或VSSOP 封装,低功耗(20uA),宽电压输入2.0V-5.5V,可编程数据转换速率8SPS-860SPS,四个单端输入或两个差分输入。可应用于,电池电压电流检测,低速便携式仪表以及温度测量系统中。

                                                                                 

                                                                                            VSSOP封装的ADS1115顶部视图         

                                                                      在这里插入图片描述

二、引脚功能

引脚名称类型描述
ADDR数字量输入I2C 丛机地址选择
AIN0模拟量输入模拟量输入0通道
AIN1模拟量输入模拟量输入1通道
AIN2模拟量输入模拟量输入2通道
AIN3模拟量输入模拟量输入3通道
ALERT/RDY数字量输出比较器输出或转换就绪
GND模拟量接地
SCL数字量输入IIC时钟
SDA数字量输入/输出IIC数据线
VDD模拟量VCC(2.0V-5.5V)

 三、功能介绍

1、量程与分辨率

                                                             

                                                                                                   不同量程下对应的最小分辨率

2、采样速率与转换时间

采样速率可以由中的DR[2:0]位来控制,AD转换可以在一个周期内完成,因此转换时间为1/DR。

3、数字比较器(本次实验不使用)

 ADS1115具有可编程的数字比较器,可以在ALERT/RD引脚上发出警报。Config寄存器中的COMP_MODE位将比较器配置为传统比较器或窗口比较器。 在传统的比较器模式下,当转换数据超过高阈值寄存器(Hi_thresh)中设置的限制时,ALERT / RDY引脚将置为有效(默认为低电平有效)。 然后,仅当转换数据降至低阈值寄存器(Lo_thresh)中设置的限制以下时,比较器才会置为无效。 在窗口比较器模式下,当转换数据超过Hi_thresh寄存器或低于Lo_thresh寄存器值时,ALERT / RDY引脚将置为有效。

       在窗口模式或传统比较器模式下,均可将比较器配置为在被配置寄存器中的COMP_LAT位置位后锁存。即使输入信号未超出阈值寄存器的范围,此设置也将保留断言。只能通过发出SMBus警报响应或读取转换寄存器来清除此锁存的断言。可以通过Config寄存器中的COMP_POL位将ALERT / RDY引脚配置为高电平有效或低电平有效。
       两种比较器模式只有在一定数量的连续读数超过阈值寄存器中设置的阈值(Hi_thresh和Lo_thresh)之后,比较器也可以配置为激活ALERT / RDY引脚。 Config寄存器中的COMP_QUE [1:0]位将比较器配置为在激活ALERT / RDY引脚之前等待超过阈值的一,二或四个读数。 COMP_QUE [1:0]位还可以禁用比较器功能,并将ALERT / RDY引脚置于高电平状态。

4、操作模式

ADS1115由两种模式,连续转换和单次转换,由其中的MODE位选择相应的工作模式。

(1)、单次转换

当其中的MODE 位设置为1时,ADS1115进入掉电状态,并以单次转换模式工作,首次上电时,此状态时ADS的默认状态。尽管进入了掉电模式,但设备仍会响应命令。 ADS1115保持此掉电状态,直到将1写入中的操作状态(OS)位。当OS位有效时,器件将在大约25μs的时间内上电,将OS位复位为0,并开始单次转换。当转换的数据准备好后,设备会再次掉电。正在进行的转换时将1写入OS位无效。要切换到连续转换模式,请将0写入MODE中的MODE位。

(2)、连续转换

在连续转换模式(MODE位设置为0)下,ADS1115连续执行转换。转换完成后,ADS1115将结果放入转换寄存器,并立即开始另一次转换。配置新的设置时,当前正在进行的转换将使用先前的设置完成转换。此后,将开始使用新的设置进行连续转换。要切换到单次转换模式,请向配置寄存器的MODE位写入1或复位设备。

四、IIC地址的选择

        ADS1115具有一个地址引脚ADDR,用于设置器件的I2C地址。 该引脚可以是连接到GND,VDD,SDA或SCL,因此可以通过一对IIC引脚选择四个不同的地址。
 一般我们将地址位接GND,1001000,最后一位是确定IIC的写/读状态,写的时候是1,读的时候是0.所以slave address读写地址是0x90/0x91(10010000/10010001)

                                                             

上表是ADDR引脚连接和对应的从机地址 ,可以看到当ADDR连接到GND时,从器件的写地址=0x90,从器件的读地址=0x91。

五、ADS1115读写时序

        要从ADS1115访问特定的寄存器,主机必须首先写一个适当的值到地址指针寄存器中的地址指针位P [1:0]。在从机地址字节,低R / W位和成功的从机确认之后,直接写入地址指针寄存器。写入地址指针寄存器后,从机应答,而主机发出STOP或重复的START条件。从ADS1115读取时,写入位P [1:0]的前一个值确定要读取的寄存器。要更改读取哪个寄存器,必须将新值写入P [1:0]。要将新值写入P [1:0],主机发出R / W位为低的从机地址字节,后跟地址指针寄存器字节。主机不必发送其他数据,并且可以发出STOP条件。如果需要重复读取同一寄存器,则无需连续发送地址指针寄存器,因为ADS1115会存储P [1:0]的值,直到被写操作修改为止。

                                         

                                                                                                       ADS1115读取时序图 

读时序操作步骤:
   1.发送写地址给ADS1115(0x90);
   2.向地址指针寄存器写数据,后两位有效,只能写0x00,0x01,0x02,0x03;
   3.发送读地址给ADS1115(0x91);
   4.读取ADS1115的数据(两个字节,MSB先行);

                                    

                                                                                                           写ADS1115的时序图 

写时序操作步骤:
   1.发送写地址给ADS1115(0x90);
   2.向地址指针寄存器写数据,后两位有效,只能写0x00,0x01,0x02,0x03;
   3.发送数据给ADS1115(高位在前)

六、数据格式

        ADS1115以二进制补码格式提供16位数据。 正满量程(+ FS)输入时,输出的AD值代码为7FFFh,负满量程(-FS)输入时,输出的AD值代码为8000h。这些代码的输出为了提示超量程的提示。

                                                   

七、地址指针寄存器(pointer register

        pointer register寄存器只有最后两位(BIT1和BIT0)有效,最后两位(BIT1和BIT0)指向了ADS1115的4个功能寄存器地址的地址。说白了,pointer register寄存器类似于C语言的指针,它指向了另外4个寄存器的地址。

                                                           

        pointer register寄存器的指向地址参见下表

                                                                这里写图片描述

(1)、当pointer register的后两位(BIT1和BIT0)= 0 0 ,代表conversion register(转换寄存器)。

(2)、当pointer register的后两位(BIT1和BIT0)= 0 1 ,代表config register(配置寄存器),即:表示往芯片中写配置字节(例如:转换通道、单次转换还是连续转换,转换电压范围、采样速度等等)。

(3)、当pointer register的后两位(BIT1和BIT0)= 1 0 ,代表Lo_thresh register(阀值比较器高字节寄存器)

(4)、当pointer register的后两位(BIT1和BIT0)= 1 1 ,代表Hi_thresh register(阀值比较器高字节寄存器)

八、转换寄存器(conversion register)

        当地址寄存器的BIT1,BIT0=00时,为转换寄存器(只读)。

        16位转换寄存器包含二进制二进制补码格式的最后一次转换结果。 上电后,转换寄存器清除为0,并保持0直到第一次转换完成。

        16位转换寄存器

九、配置寄存器

        当地址寄存器的BIT1,BIT0=01时,为配置寄存器。16位配置寄存器用于控制工作模式,输入选择,数据速率,满量程范围和比较器模式。配置寄存器详细描述参见下表:

bit字段类型复位后值描述
[15]OS读/写1H运行状态或单次转换开始
该位确定设备的运行状态。
仅在掉电状态下才能编写OS,而在进行转换时则无效。
写时:
0:无效
1:开始单次转换(处于掉电状态)
读时::
0:设备正在进行转换
1:转换空闲
[14:12]MUX[2:0]读/写0H输入多路复用器配置
这些位配置输入多路复用器
000:AINP = AIN0和AINN = AIN1(默认)
001 : AINP = AIN0 and AINN = AIN3
010 : AINP = AIN1 and AINN = AIN3
011 : AINP = AIN2 and AINN = AIN3
100 : AINP = AIN0 and AINN = GND
101 : AINP = AIN1 and AINN = GND
110 : AINP = AIN2 and AINN = GND
111 : AINP = AIN3 and AINN = GND
[11:9]PGA[2:0]读/写2H可编程的增益放大器(量程的选择 )
这些位设置可编程增益放大器的FSR。
000 : FSR = ±6.144 V(1)
001 : FSR = ±4.096 V(1)
010 : FSR = ±2.048 V (default)
011 : FSR = ±1.024 V
100 : FSR = ±0.512 V
101 : FSR = ±0.256 V
110 : FSR = ±0.256 V
111 : FSR = ±0.256 V
[8]MODE读/写1H设备运行方式
该位控制操作模式。
0:连续转换模式
1:单次模式或掉电状态(默认)
[7:5]DR[2:0]读/写4H转换速率
这些位控制转换速率
000:8 SPS
001:16 SPS
010:32 SPS
011:64 SPS
100:128 SPS(默认)
101:250 SPS
110:475 SPS
111:860 SPS
[4]COMP_MODE读/写0H比较器模式
该位控制比较器工作
0:传统比较器(默认)
1:窗口比较器
[3]COMP_POL读/写0H比较器极性
该位控制ALERT / RDY引脚的极性
0:低电平有效(默认)
1:高电平有效
[2]COMP_LAT读/写0H锁存比较器
该位控制ALERT / RDY引脚在被置为有效后锁存,还是在转换后处于上限和下限阈值范围之内清零。
0:非锁存比较器。 置位后ALERT / RDY引脚不锁存(默认)
1:锁存比较器。 置为有效的ALERT / RDY引脚保持锁存状态,直到转换数据由主服务器或适当的SMBus警报响应读取由主机发送。 设备以其地址响应,它是最低的当前声明ALERT / RDY总线的地址。
[2:0]COMP_QUE[1:0]读/写3H比较器置位和禁用
这些位执行两个功能。 设置为11时,比较器被禁用,ALERT / RDY引脚被设置为高阻抗状态。 当设置为任何其他值时,将启用ALERT / RDY引脚和比较器功能,并且该设置值确定连续的转换次数超过在声明ALERT / RDY引脚之前所需的上限或下限阈值
00:一次转换后断言
01:两次转换后置位
10:四次转换后置位
11:禁用比较器并将ALERT / RDY引脚设置为高阻抗(默认)

十、Lo_thresh和Hi_thresh寄存器

       比较器使用的上下阈值以二进制补码格式存储在两个16位寄存器。 比较器为数字比较器。 因此,只要更改PGA设置,就必须更新这两个寄存器中的值(使能比较器前提下)。 通过将Hi_thresh寄存器MSB设置为1并将Lo_thresh寄存器MSB设置为0,要使用ALERT / RDY引脚的比较器功能,Hi_thresh寄存器值必须始终大于 Lo_thresh寄存器值。

                                                                                                  Lo_thresh寄存器

                                                                                                      Hi_thresh寄存器

                                                                                                        两个阈值寄存器描述

十一、编程流程

ADS1115编程分为三步(注:Lo_thresh和Hi_thresh寄存器可以不用设置,使用默认值即可)。

第一步:设置配置寄存器

               1)写入0x90(从机地址)

               2)写入0x01(配置寄存器地址)

               3)写入高字节(配置寄存器的高8位)

               4)写入低字节(配置寄存器的低8位)

第二步:设置转换寄存器

               1)写入0x90(从机地址)

               2)写入0x00(转换寄存器地址)

第三步:读取转换结果

               1)写入0x91(从机地址)

               2)读转换结果高字节

               3)读转换结果低字节

十二、源程序

1、IIC.H

///
//从器件地址

#define  ADS1115_WR_DEVICE_ADDR	 	0x90       //  ADS1115写从器件地址=0x90
#define  ADS1115_RE_DEVICE_ADDR 	0x91       //  ADS1115读从器件地址=0x91


///
//寄存器地址
///
#define  CONVERSION_REG_ADDR 0x00	//  转换寄存器地址
#define  CONFIG_REG_ADDR  0x01   	//  配置寄存器地址
#define  LOTH_REG  0x02				//  最低阀值寄存器地址
#define  HITH_REG  0x03				//  最高阀值寄存器地址

2、IIC.C



#define  RCC_ADS1115_I2C_PORT 			RCC_AHB1Periph_GPIOC
#define GPIO_ADS1115_I2C_PORT			GPIOE
#define GPIO_ADS1115_I2C_SCL_Pin		GPIO_Pin_2
#define GPIO_ADS1115_I2C_SDA_Pin		GPIO_Pin_3



#define ADS1115_I2C_SCL_1()  	GPIO_SetBits(			GPIO_ADS1115_I2C_PORT, GPIO_ADS1115_I2C_SCL_Pin)	// SCL = 1
#define ADS1115_I2C_SCL_0()  	GPIO_ResetBits(			GPIO_ADS1115_I2C_PORT, GPIO_ADS1115_I2C_SCL_Pin)	// SCL = 0
#define ADS1115_I2C_SDA_1()  	GPIO_SetBits(			GPIO_ADS1115_I2C_PORT, GPIO_ADS1115_I2C_SDA_Pin)	// SDA = 1
#define ADS1115_I2C_SDA_0()  	GPIO_ResetBits(			GPIO_ADS1115_I2C_PORT, GPIO_ADS1115_I2C_SDA_Pin)	// SDA = 0
#define ADS1115_I2C_SDA_READ()  GPIO_ReadInputDataBit(	GPIO_ADS1115_I2C_PORT, GPIO_ADS1115_I2C_SDA_Pin)	// 读SDA口线状态
#define ADS1115_I2C_SCL_READ()  GPIO_ReadInputDataBit(	GPIO_ADS1115_I2C_PORT, GPIO_ADS1115_I2C_SCL_Pin)	// 读SCL口线状态



/*
*********************************************************************************************************
*	函 数 名: bsp_InitI2C
*	功能说明: 配置I2C总线的GPIO,采用模拟IO的方式实现
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void ADS1115_bsp_InitI2C(void)
{	
	GPIO_InitTypeDef GPIO_InitStructure;

	
	RCC_AHB1PeriphClockCmd(RCC_ADS1115_I2C_PORT, ENABLE);	//打开GPIO时钟
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;  			//输出模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;			//开漏输出,必须配置为开漏输出(GPIO_OType = GPIO_OType_OD)。如果配置为推挽,读取时钟时肯定会失败。
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//GPIO_PuPd_NOPULL;	
	GPIO_InitStructure.GPIO_Pin =  GPIO_ADS1115_I2C_SCL_Pin | GPIO_ADS1115_I2C_SDA_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIO_ADS1115_I2C_PORT, &GPIO_InitStructure);
	// 给一个停止信号, 复位I2C总线上的所有设备到待机模式
	ADS1115_i2c_Stop();
}



void ADS1115_SDA_INPUT(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;  		//输入模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;		//开漏输出,必须配置为开漏输出(GPIO_OType = GPIO_OType_OD)。如果配置为推挽,读取时钟时肯定会失败。
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//GPIO_PuPd_NOPULL;
	GPIO_InitStructure.GPIO_Pin =  GPIO_ADS1115_I2C_SDA_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIO_ADS1115_I2C_PORT , &GPIO_InitStructure);
}



void ADS1115_SDA_OUTPUT(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;;  	//输出模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;		//开漏输出,必须配置为开漏输出(GPIO_OType = GPIO_OType_OD)。如果配置为推挽,读取时钟时肯定会失败。
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//GPIO_PuPd_NOPULL;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	
	GPIO_InitStructure.GPIO_Pin =  GPIO_ADS1115_I2C_SDA_Pin;
	GPIO_Init(GPIO_ADS1115_I2C_PORT , &GPIO_InitStructure);
}



/*
*********************************************************************************************************
*	函 数 名: i2c_Delay
*	功能说明: I2C总线位延迟,最快400KHz
*	形    参:  无
*	返 回 值: 无
* 
	CPU主频168MHz时,在内部Flash运行, MDK工程不优化。用台式示波器观测波形。
	循环次数为5时,SCL频率 = 1.78MHz (读耗时: 92ms, 读写正常,但是用示波器探头碰上就读写失败。时序接近临界)
	循环次数为10时,SCL频率 = 1.1MHz (读耗时: 138ms, 读速度: 118724B/s)
	循环次数为30时,SCL频率 = 440KHz, SCL高电平时间1.0us,SCL低电平时间1.2us
	上拉电阻选择2.2K欧时,SCL上升沿时间约0.5us,如果选4.7K欧,则上升沿约1us
	实际应用选择400KHz左右的速率即可
*/
void ADS1115_i2c_Delay(void)
{
	uint8_t i;

	for (i = 0; i < 30; i++);
}



void ADS1115_delay_us(uint8_t time)
{
	uint8_t i;
	
	
	for (i = 0 ; i < time ; i++)
	{
		__NOP();
		__NOP();
		__NOP();
		__NOP();
		__NOP();
		__NOP();
		__NOP();
		__NOP();
		__NOP();
		__NOP();
		__NOP();
		__NOP();
		__NOP();
		__NOP();
		__NOP();
	}
}



/*
*********************************************************************************************************
*	函 数 名: i2c_Start
*	功能说明: CPU发起I2C总线启动信号
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void ADS1115_i2c_Start(void)
{
	/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
	ADS1115_SDA_OUTPUT();
	ADS1115_I2C_SDA_1();	
	ADS1115_delay_us(20);	
	ADS1115_I2C_SCL_1();
	ADS1115_delay_us(50);	
	ADS1115_I2C_SDA_0();	//START:when CLK is high,DATA change from high to low 
	ADS1115_delay_us(50);	
	ADS1115_I2C_SCL_0();	//钳住I2C总线,准备发送或接收数据 
	ADS1115_delay_us(10);
}



/*
*********************************************************************************************************
*	函 数 名: i2c_Stop
*	功能说明: CPU发起I2C总线停止信号
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void ADS1115_i2c_Stop(void)
{
	/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */	
	ADS1115_SDA_OUTPUT();
	ADS1115_I2C_SDA_0();	//STOP:when CLK is high DATA change form low to high
	ADS1115_delay_us(20);	
	ADS1115_I2C_SCL_1();
	ADS1115_delay_us(50);
	ADS1115_I2C_SDA_1();
	ADS1115_delay_us(30);
}



/*
*********************************************************************************************************
*	函 数 名: i2c_Ack
*	功能说明: CPU产生一个ACK信号
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
SCL在高电平期间,SDA被从设备拉为低电平表示应答
void ADS1115_i2c_Ack(void)
{
	ADS1115_SDA_OUTPUT();
	ADS1115_I2C_SDA_0();	/* CPU驱动SDA = 0 */
	ADS1115_delay_us(20);

	ADS1115_I2C_SCL_1();	/* CPU产生1个时钟 */
	ADS1115_delay_us(50);
	ADS1115_I2C_SCL_0();	//清时钟线,钳住I2C总线以便继续接收
	ADS1115_delay_us(10);	
}



/*
*********************************************************************************************************
*	函 数 名: i2c_NoAck
*	功能说明: CPU产生1个NACK信号
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void ADS1115_i2c_NoAck(void)
{
	ADS1115_SDA_OUTPUT();
	ADS1115_I2C_SDA_1();	/* CPU驱动SDA = 1 */
	ADS1115_delay_us(20);
	
	ADS1115_I2C_SCL_1();	/* CPU产生1个时钟 */
	ADS1115_delay_us(50);
	ADS1115_I2C_SCL_0();	//清时钟线,钳住I2C总线以便继续接收
	ADS1115_delay_us(10);
}



/*
*********************************************************************************************************
*	函 数 名: i2c_WaitAck
*	功能说明: CPU产生一个时钟,并读取器件的ACK应答信号
*	形    参:  无
*	返 回 值: 返回0表示正确应答,1表示无器件响应
*********************************************************************************************************
*/
/*    功    能: 提供I2C总线的时钟信号, 并返回在时钟电平为高期间SDA 信号线上状*/
/*              态。本函数用于数据发送时的确认检查。        				 */

//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
BOOL ADS1115_i2c_WaitAck(void)
{
	uint8_t ucErrTime = 0x00;
	
	
	ADS1115_SDA_INPUT();	
	ADS1115_I2C_SDA_1();
	ADS1115_delay_us(30);
	
	ADS1115_I2C_SCL_1();	/* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
	ADS1115_delay_us(30);	
	
	while (ADS1115_I2C_SDA_READ())
	{
		ucErrTime++;
		if (ucErrTime > 250)
		{
			ADS1115_I2C_SCL_0();
			ADS1115_delay_us(30);
			ADS1115_i2c_Stop();
			return (FALSE);
		}
	}
	ADS1115_I2C_SCL_0();
	ADS1115_delay_us(30);		
	return (TRUE);
}



/*
*********************************************************************************************************
*	函 数 名: i2c_SendByte
*	功能说明: CPU向I2C总线设备发送8bit数据
*	形    参:  _ucByte : 等待发送的字节
*	返 回 值: 无
*********************************************************************************************************
*/
void ADS1115_i2c_SendByte(uint8_t _ucByte)
{
	uint8_t i;

	
	ADS1115_SDA_OUTPUT();	
	/* 先发送字节的高位bit7 */
	for (i = 0; i < 8; i++)
	{
		
		if (_ucByte & 0x80)
			ADS1115_I2C_SDA_1();
		else
			ADS1115_I2C_SDA_0();

		_ucByte <<= 1;	/* 左移一个bit */
		
		ADS1115_delay_us(10);		
		ADS1115_I2C_SCL_1();
		ADS1115_delay_us(30);
		ADS1115_I2C_SCL_0();
		ADS1115_delay_us(10);
	}
}



/*
*********************************************************************************************************
*	函 数 名: i2c_ReadByte
*	功能说明: CPU从I2C总线设备读取8bit数据
*	形    参:  无
*	返 回 值: 读到的数据
*********************************************************************************************************
*/
uint8_t ADS1115_i2c_ReadByte(void)
{
	uint8_t i;
	uint8_t value = 0x00;

	
	/* 读到第1个bit为数据的bit7 */	
	for (i = 0; i < 8; i++)
	{
		ADS1115_SDA_OUTPUT();
		ADS1115_I2C_SDA_1();
		ADS1115_delay_us(20);
		
		ADS1115_I2C_SCL_1();
		ADS1115_delay_us(50);
		ADS1115_SDA_INPUT();
		ADS1115_delay_us(10);
		value <<= 1;
		
		if (ADS1115_I2C_SDA_READ())
			value++;
		ADS1115_delay_us(10);
		ADS1115_I2C_SCL_0();
		ADS1115_delay_us(10);
	}
	return (value);
}

3、ADS1115.C

uint8_t BYTE_BUF[2]; 


//
//ADS1115的操作分3步骤。
//步骤1、向配置寄存器0x01写入配置,先写高8位,再写低8位
//H_CMD :命令字高8位(通道,量程,转换模式)
//L_CMD : 命令字低8位(采样率设置 比较模式 有效电平 信号输出锁存)
//
BOOL ADS1115_Confight(uint8_t w_device_addr , uint8_t register_addr , uint8_t H_CMD , uint8_t L_CMD)
{
	ADS1115_i2c_Start();
	ADS1115_i2c_SendByte(w_device_addr);
	if (!ADS1115_i2c_WaitAck())
	{
		ADS1115_i2c_Stop();
		return (FALSE);
	}
	ADS1115_i2c_SendByte(register_addr) ;
	ADS1115_i2c_WaitAck();
	ADS1115_delay_us(100);
	
	ADS1115_i2c_SendByte(H_CMD);	//c-an0 d-an1 e-an2 f-an3  0->6.144 2->4.096,高字节配置
	ADS1115_i2c_WaitAck();
	ADS1115_i2c_SendByte(L_CMD);	//低字节 最高转换速度
	ADS1115_i2c_WaitAck();
	ADS1115_i2c_Stop();
	return (TRUE);
}



//
//步骤2、写入指针寄存器0x00,准备读取电压
//
BOOL ADS1115_PointRegister(uint8_t w_device_addr , uint8_t register_addr)
{
	ADS1115_i2c_Start();
	ADS1115_i2c_SendByte(w_device_addr);
	if (!ADS1115_i2c_WaitAck())
	{
		ADS1115_i2c_Stop();
		return (FALSE);
	}
	ADS1115_i2c_SendByte(register_addr);
	ADS1115_i2c_WaitAck();
	ADS1115_i2c_Stop();
	return (TRUE);
}



//
//步骤3、读取电压数据
//
BOOL ADS1115_Read_AD(uint8_t r_device_addr)
{  	
  	ADS1115_i2c_Start();
  	ADS1115_i2c_SendByte(r_device_addr);
  	if (!ADS1115_i2c_WaitAck())
	{
		ADS1115_i2c_Stop();
		return (TRUE);
	}
	
	BYTE_BUF[0] = ADS1115_i2c_ReadByte();
	ADS1115_i2c_Ack();
	BYTE_BUF[1] = ADS1115_i2c_ReadByte();
	ADS1115_i2c_NoAck();
	ADS1115_i2c_Stop();
	return (TRUE);
}



//
//AD转换中单通道单次转换模式,是只进行一次AD转换吗?
//它和单通道多次转换有什么区别? 单次转换是指进行一次转换前需要你给一次指令。
//连续转换的话开启后自动连续进行转换,转换的数据连续进行更新。
//如果你采用单次转换,就需要不断发送转换的命令,读取结果,再发转换命令,再读,也就是循环!
//如果连续转换开启的话,就在程序中循环读就可以了。
///
//警告:通道转换之间必须至少要延时5毫秒;否则采集不准确。
//
int16_t ADS1115_Read_ADC(uint8_t channel)
{
	uint8_t H_CMD = 0x00;
	uint8_t L_CMD = 0x82;
	int16_t tempData;
	

	switch (channel)
	{
		case 0:
			H_CMD = 0xC2;	//bit[15]=OS=1:开始单次转换
							//bit[14:12]=MUX[2:0]=100 : AINP = AIN0 and AINN = GND
							//bit[11:9]=PGA[2:0]=001 : FSR = ±4.096 V(1)
							//bit[8]=MODE=1:单次模式或掉电状态
			break;

		case 1:
			H_CMD = 0xD2;	//bit[15]=OS=1:开始单次转换
							//bit[14:12]=MUX[2:0]=101 : AINP = AIN1 and AINN = GND
							//bit[11:9]=PGA[2:0]=001 : FSR = ±4.096 V(1)
							//bit[8]=MODE=1:单次模式或掉电状态
			break;
		
		case 2:
			H_CMD = 0xE2;	//bit[15]=OS=1:开始单次转换
							//bit[14:12]=MUX[2:0]=110 : AINP = AIN2 and AINN = GND
							//bit[11:9]=PGA[2:0]=001 : FSR = ±4.096 V(1)
							//bit[8]=MODE=1:单次模式或掉电状态
			break;

		case 3:
			H_CMD = 0xF2;	//bit[15]=OS=1:开始单次转换
							//bit[14:12]=MUX[2:0]=111 : AINP = AIN3 and AINN = GND
							//bit[11:9]=PGA[2:0]=001 : FSR = ±4.096 V(1)
							//bit[8]=MODE=1:单次模式或掉电状态
			break;		
	}//switch (channel)
	
	L_CMD = 0x82;	//bit[7:5]=DR[2:0]=100:128 SPS(默认)
					//bit[4]=COMP_MODE=0:传统比较器(默认)
					//bit[3]=COMP_POL=0:低电平有效(默认)
					//bit[2]=COMP_LAT=0:非锁存比较器。 置位后ALERT / RDY引脚不锁存(默认)
					//bit[2:0]=COMP_QUE[1:0]=10:四次转换后置位
	
	ADS1115_Confight(ADS1115_WR_DEVICE_ADDR , CONFIG_REG_ADDR , H_CMD , L_CMD);
	ADS1115_delay_us(200);	// 延时一定时间,防止通道切换互相影响 	
	ADS1115_PointRegister(ADS1115_WR_DEVICE_ADDR , CONVERSION_REG_ADDR);
	ADS1115_delay_us(200);	
	ADS1115_Read_AD(ADS1115_RE_DEVICE_ADDR);
	tempData = (int16_t)(BYTE_BUF[0] << 8) + (int16_t)BYTE_BUF[1];
	return (tempData);
}

4.main.c

uint16_t ads1115_buffer[4];

int main(void)
{	
   if (SysTick_Config(SystemCoreClock / 1000))
   { 
      while (1); 
   }	
   ADS1115_bsp_InitI2C();
   __set_PRIMASK(0);

  while (1)
	{
         for (int i=0;i<4;i++)
           ads1115_buffer[i] = ADS1115_Read_ADC(i);  
         Delay(100);//   延时100毫秒  
    }
}

采样结果:

              0V=5  1V=731  2V=1457  3V=2184  4V=2910  5V=3636

 

注意事项

  • 对于多通道采样,每次通道切换时,应当等待几毫秒的时间后再进行采样,否则采样的数据可能不稳定或者发生通道间干扰。

 

 

感谢:

作者:特维斯的电磁炉
链接:https://www.jianshu.com/p/e0b448995316
来源:简书

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32F407单片机移植ADS1115驱动程序 的相关文章

  • Unity和ROS通讯的机械臂

    Unity和ROS之间的通讯包 目录 Unity和ROS之间的通讯包参考及引用1 Github源码地址 Unity和ROS之间的通讯 一个ROS包 xff08 用于接收从Unity场景发送的ROS消息 xff09 两个导入到Unity的包
  • 虚拟机Ubuntu磁盘扩容个人记录

    虚拟机Ubuntu磁盘扩容个人记录 一 修改扩展大小 修改成功以后 xff0c 打开虚拟机Ubuntu 二 进入Ubuntu修改 打开终端 xff0c 输入 sudo gparted 右键点击后更改大小 然后确定修改 最后 xff0c 终端
  • 下载源码包后在Ubuntu编译

    下载源码包后在Ubuntu编译 学习使用Ubuntu过程中 xff0c 不免会尝试跑一遍别人的代码 xff0c 这时候要知道如何将源码下载后进行编译及运行 本文与以下两篇博客相关 xff1a ROS工作空间和功能包 ROS实现代码及已编译代
  • 利用科大讯飞语音合成模块SDK实现ROS语音交互

    利用科大讯飞语音合成模块SDK实现ROS语音交互 本文内容与CSDN博主 AI Chen 的原创文章相同 xff0c 可以直接参考原文 xff1a https blog csdn net qq 39400324 article detail
  • tts_subscribe.cpp代码

    tts subscribe cpp span class token comment 语音合成 xff08 Text To Speech xff0c TTS xff09 技术能够自动将任意文字实时转换为连续的 自然语音 xff0c 是一种能
  • FreeRTOS线程安全、中断安全的printf实现方式

    文章目录 前言一 问题描述二 解决方案1 线程安全 中断安全的printf实现方式 总结参考 前言 利用串口对基于FreeRTOS的多任务程序调试过程中 xff0c 时常出现数据交叉打印 xff0c 甚至出现乱码现象 这是因为通常情况下 x
  • 结构体空间对齐和结构体数组的使用

    今天在学习linux的platform总线时 xff0c 设备对资源的描述用到了结构体数组 xff0c 以前从没见过 如下 很明显用结构体数组主要是用来给一系列相同类型的结构体赋值 xff0c 主要是来看内存分配问题 首先是结构体的内存分配
  • ubuntu:出现bash: /opt/ros/melodic/setup.bash: 没有那个文件或目录 解决方法

    如图 xff0c 解决方法如下 xff1a 打开虚拟机终端 xff0c 输入 gedit bashrc 弹出一个文件 xff0c 找到最后一行 xff0c 如图所示 然后我roscore了一下 xff0c 看了一下我的ros版本 xff1a
  • STC单片机仿三菱FX2n方案 hex烧录进去就可以当plc用

    STC单片机仿三菱FX2n方案 hex烧录进去就可以当plc用 xff0c 成本低方便快捷id 61 643520282241 amp
  • 西门子S7-1200系列PLC定时器指令

    定时器指令是在PLC程序设计中非常常见的一种指令 xff0c S7 1200系列PLC的定时器的指令格式及使用方式都不同于S7 200系列PLC S7 1200系列PLC的采用的是IEC标准的定时器指令 xff0c 用户程序中可以使用的定时
  • 0基础学springboot之对数据库的增删改查

    1 xff1a 使用的是mysql数据库 xff0c 数据库中的表为student 2 xff1a 创建springboot项目 xff0c 在pom文件里加载mysql依赖 lt dependency gt lt groupId gt m
  • 平衡车Car_Balance(一)——电机

    目录 一 电机简要概述 二 常见电机种类 1 直流有刷电机 xff08 BDC xff0c 最最最常见的电机 xff09 2 直流无刷电机 xff08 BLDC xff09 3 舵机 4 步进电机 三 平衡小车选用电机及需要关注的参数 1
  • 用matlab进行xlsx表格的提取和操作

    用matlab进行xlsx表格的提取和操作 一 数据的提取二 垃圾数据的清除三 数据的分类四 图像的绘制 在学习数学建模时 xff0c 我们会碰到大数据的问题 xff0c 这类问题通常要进行数据预处理 xff0c 这是我用matlab进行预
  • Android 开发艺术探索笔记(5,我的头条面试经历分享

    public Messenger Handler target mTarget 61 target getIMessenger public Messenger IBinder target mTarget 61 IMessenger St
  • 穿越机F4飞控F405代码pcb文件,原理图

    穿越机F4飞控F405代码pcb文件 原理图 xff0c 源代码 xff0c 穿越机无人机 xff0c F4V3飞控PCB资料 xff0c AD格式原理图 PCB图 Bo可直接打板id 61 659104114509 amp
  • 【AD20学习笔记】网表导入及模块化布局设计(1)

    7 20 今天写完 本章快捷键总结 xff08 绿色是自己设的 xff09 Q可以切换单位 xff08 但是要右击按着欸 xff0c 有一定失败概率 xff09 JC可以查元件位号 F6可以进行矩形的元件摆放 按shift 是可以多选 TM
  • 【五一创作】微服务学习笔记

    微服务学习 写一个服务的步骤一般可以有 1 xff0c 在pom xml文件中导入相关的依赖 2 xff0c 编写相关的配置类 xff08 application yml xff09 配置类编写的三步 1 服务的端口 2 服务的名字 3 服
  • Win10启动或关闭windows功能,一直显示“请稍后”该怎么办?(win10请稍后)

    解决方法一 xff1a 1 按下 Win 43 R 组合键呼出运行 xff0c 输入 xff1a services msc 点击确定打开服务 xff1b 2 在服务界面中双击打开 Windows Modules Installer xff0
  • ROS2 CmakeList如何正确引入第三方.so文件

    在使用ros2开发的时候难免会需要引入第三方的厂家提供的 so文件库 注意这里说的并不是自己写的cpp文件转成 so然后再自己的工程项目中使用 例如经常使用的ICANCmd h头文件和libCanCmd so库还有onnx推理的时候onnx
  • 实战篇 | 基于freeRTOS的多任务事件传输

    之前分享了很多关于freeRTOS的知识 xff0c 那么我们怎么在实战中去写代码呢 xff1f 本篇文章重在对基于freeRTOS的架构代码的解析 整个功能如下图 xff1a 为什么要用freeRTOS 在实际项目中 xff0c 如果程序

随机推荐

  • 浅谈安卓UI设计,android串口工具apk

    lt xml version 61 34 1 0 34 encoding 61 34 utf 8 34 gt lt android support constraint ConstraintLayout xmlns android 61 h
  • 四轴飞行器-基于STM32微控制器(电子入门必看)

    nbsp nbsp nbsp nbsp nbsp nbsp 从开始做微型多旋翼飞行器以来 我经常和一大批有着同样爱好的朋友们互相交流 他们走着我曾经走过的路 也经历着我所经历过的坎坷 无论我在何坛何群 总有朋友追问着很多重复的问题 未能全部
  • 基于stm32cubeMX的平衡小车HAL库+蓝牙遥控+直立环+速度环+转向环 STM32F103C8T6

    基于stm32cubeMX的平衡小车HAL库 蓝牙遥控 直立环 速度环 转向环 一 代码工程链接 我的平衡车代码 HAL库 cubeMX配置 主控stm32f103 程序代码容易移植 建议先看完正文 工程 平衡车工程 PCB 代码程序 蓝牙
  • Java日常学习:运用链栈实现进制转换

    一 简单介绍 今天我就给大家带来java中的链栈实现进制转换的方法 xff0c 该方法也是在学习栈的时候发现的 xff0c 我们都知道在进制转换的时候有很多种方法 xff0c 其中最常用也最容易理解的就是除基倒取余法 xff0c 那么我们先
  • CLion解决中文输出乱码(2022年最新教程)

    CLion是个很好用的IDE xff0c 但是在用CLion写C C 43 43 代码时 xff0c 中文输出会是乱码 xff0c 此前博主也找了很多资料 xff0c 但是网上的教程大多都是脱裤子放屁 xff0c 治标不治本 xff0c 在
  • 【Linux网络编程】TCP并发服务器的实现(IO多路复用select)

    文章目录 一 服务器模型1 1 服务器概念1 2 TCP并发服务器的意义1 3 实现TCP并发服务器的方式 二 使用IO多路复用实现TCP并发服务器优势三 select函数四 TCP并发服务器的构建4 1 创建套接字4 2 填写服务器网络信
  • 树莓派解决cannot currently show the desktop最优法

    最近在搞树莓派前面的步骤都轻轻松松的解决了这个问题卡了我很久 这个是解决之后的效果 xff0c 我使用改分辨率用了很多办法这个办法解决的效率最快 直接在下载好文件的解决 1 将有sd卡的读卡器插入 2 在config txt文件中加入四句话
  • 十、C++中的类 class与struct的区别

    面向对象程序设计 xff0c 需要诸如类和对象这样的概念 C 43 43 支持面向过程 基于对象 面向对象 泛型编程四种 C语言不支持面向对象编程 类是一种将数据和函数组织在一起的方式 一个函数参数过多 xff0c 代码不好维护 xff0c
  • 飞控开发--气压计MS5611

    ms5611简介 xff1a 官方给出的最大分辨率 xff1a 10cm 工作电压 xff1a 1 8v 3 6v 气压 AD 精度 xff1a 24位 工作环境 xff1a xff0d 40 43 85 C xff0c 10 1200mb
  • 如何使用JavaScript将Set转换为Array?

    如何使用JavaScript将Set转换为Array xff1f 下面本篇文章家里给大家介绍一下在JavaScript中将Set转换为Array的方法 xff0c 希望对大家有所帮助 在JavaScript中 xff0c 想要将Set xf
  • 如何在Markdown中插入图片并顺利共享

    使用PicGo 43 GitHub搭建图床 实现便捷的Markdown图片管理 昨天在呕心沥血写 xff08 搬 xff09 完我的第一篇技术分享博文m1 使用 VMware 安装 CentOS7 并部署 k8s 高可用集群之后 xff0c
  • 海康威视网络摄像头sdk的开发(Demo的使用)指南

    如果您是想实现海康sdk包的Demo实例中MFC的分功能 xff0c 那么请慢慢看 xff0c 这篇文章百分之九十九可以帮你实现 提醒 xff1a 内容来自网络和自己实际操作 xff0c 如有问题请联系hww168 64 yeah net
  • Proxy反向代理解决跨域问题

    一 问题的产生 在项目开发过程中遇到了一个问题 xff0c 访问图片与访问其他的数据的服务器不是同一个 xff0c 虽然后台已经将两个服务器合并到一个 xff0c 但是前台配置好后访问就会产生了一个问题 xff0c 具体报错如下 xff1a
  • 什么是枚举【详解】

    本期介绍 x1f356 主要介绍 xff1a 什么是枚举 xff0c 枚举是如何定义 初始化的 xff0c 以及枚举到底如何使用 xff0c 还有枚举这种语法存在的优点有那些 x1f440 文章目录 一 什么是枚举 x1f356 二 枚举类
  • vscode将项目导入远程仓库 git

    1 打开软件 点击第三个图标 远程仓库 点击代码管理右上角三个小点 远程 远程仓库 添加远程存储库 跳出输入框 2 复制git的地址 3 将上一步复制的仓库链接 粘贴进去 回车 4 命名 5 打开命令行 输入下列命令 用户名 邮箱 PS D
  • 深度学习二

    BT神经元为按照误 差逆向传播算法训练的多层前馈神经网络 BT神经网络分为输入层 隐藏层 输出层 输入层一般有数据种类多个神经元 xff0c 接受数据 隐藏层的神经元为根号下隐藏层 输出层加b个 xff0c 输入的每个数据加权和返回数之和为
  • ROS学习(五)

    学习古月 ROS机器人开发实践 一书时 xff0c 在第六章的6 3遇到的问题的总结 在执行将xacro文件转化成urdf的文件格式时 xff0c 执行语句 rosrun xacro xacro py mrobot urdf xacro g
  • 新建Mavlink消息

    1 下载Mavlink生成器 1 1 Git clone 需要在翻墙的网络环境下下载 span class token function git span clone https github com mavlink mavlink git
  • Python从入门到精通11天(lambda匿名函数和map函数的使用)

    lambda匿名函数和map函数的使用 lambda匿名函数map函数lambda与map的联用 lambda匿名函数 匿名函数在计算机编程中是指一类无需定义标识符 xff08 函数名 xff09 的函数或子程序 xff0c lambda函
  • STM32F407单片机移植ADS1115驱动程序

    最近一个工程项目需要使用ADS1115采集电压 xff0c 网上研究了一下 xff0c 测试成功 xff0c 期间走了很多弯路 xff0c 为避免后来的研究者重走我的老路 xff0c 特分享给大家 注 xff1a 网上很多ADS1115驱动