ADS1115学习代码

刚开始写分享,只写常使用功能的步骤和细节。(注意代码注释写的很详细,可多看注释)

功能ADC测量
通信I2C通信
电压2.0V ~ 5.0V
特点封装简单、16位、精度高、多引脚、可差分测量、操作简单

一、学习环境:我使用的是STM32F103C8T6最小系统板、1.3寸OLEDI2C显示屏、ADS1115模块。(东西不多,实现就行)

软件I2C.c文件的配置代码(可直接用):

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

#define GPIO         GPIOB

//写SCL
void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIO,GPIO_Pin_12,(BitAction)BitValue);
	Delay_us(10);
}

//写SDA
void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIO,GPIO_Pin_13,(BitAction)BitValue);
	Delay_us(10);
}

//读SDA
uint8_t MyI2C_R_SDA(void)
{
	uint8_t Data = GPIO_ReadInputDataBit(GPIO,GPIO_Pin_13);
	Delay_us(10);
	return Data;
}

//初始化I2C
void MyI2C_Init(void)
{
    //开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
    //初始化GPIOB的PB12、PB13引脚(PB1引脚可当报警功能的ALRT引脚)
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;                      //配置开漏输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_1; //注意观察芯片引脚的使用功能(软件I2C就无所谓引脚了)
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIO,&GPIO_InitStructure);
	
	GPIO_SetBits(GPIO,GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_1);   //初始化引脚(将引脚拉高)
}

//I2C起始标志位
void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}

//I2C停止标志位
void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);
}

//I2C发送八位数据
void MyI2C_Send8Bit_Data(uint8_t Byte)
{
	uint8_t i;
	for(i = 0;i < 8;i++)
	{
		MyI2C_W_SDA(Byte & (0x80 >> i));   //高位数据先发送
		MyI2C_W_SCL(1);
		MyI2C_W_SCL(0);
	}
}

//I2C接收八位数据
uint8_t MyI2C_Recrive8Bit_Data(void)
{
	uint8_t i,Data = 0x00;
	MyI2C_W_SDA(1);
	for(i = 0;i < 8;i++)
	{
		MyI2C_W_SCL(1);
		if(MyI2C_R_SDA() == 1)
		{
			Data |= (0x80 >> i);       //高位数据先接受,存到Data中
		}
		MyI2C_W_SCL(0);
	}
	return Data;                       //将读到的数据返回
}

//主机发送应答
void MyI2C_SendAck(uint8_t AckData)
{
	MyI2C_W_SDA(AckData);              //参数为0或1
	MyI2C_W_SCL(1);
	MyI2C_W_SCL(0);
}

//主机接受应答
uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t Ack_Data;
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	Ack_Data = MyI2C_R_SDA();
	MyI2C_W_SCL(0);
	return Ack_Data;                    //返回ACK应答
}

读取ALRT报警引脚的信息
//uint8_t Get_ALRT(void)
//{
//	  return GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_1);
//}

I2C.h文件:

#ifndef __I2C_H
#define __I2C_H

void MyI2C_Init(void);                     //初始化I2C函数
void MyI2C_Start(void);                    //I2C起始标志                   
void MyI2C_Stop(void);                     //I2C停止标志
void MyI2C_Send8Bit_Data(uint8_t Byte);    //发送8位数据函数
uint8_t MyI2C_Recrive8Bit_Data(void);      //接收8位数据函数
void MyI2C_SendAck(uint8_t AckData);       //发送应答数据
uint8_t MyI2C_ReceiveAck(void);            //接收应答数据

#endif

二、接下来配置ADS1115模块的文件,首先搞懂ADS1115的使用资料,主要由以下重点内容:

1、ADS1115模块的引脚使用:

VDD引脚接正极(当前代码接最小系统板的3.3V)
GND引脚接最小系统板的GND
SCL引脚为I2C时钟线(当前代码接最小系统板的PB12引脚,若是硬件I2C当前芯片接PB10等硬件支持的引脚)
SDA引脚为I2C数据线(当前代码接最小系统板的PB13引脚,若是硬件I2C当前芯片接PB11等硬件支持的引脚)
ADDR引脚这是从机地址选择引脚(当前代码接GND,地址为0x90。可根据需要自行选择,若接VDD则地址为0x91、接SDA则地址为0x92、接SCL则地址为0x93)
ALRT引脚可接GPIO,作为报警功能使用(当前代码未使用)
A0引脚看下文
A1引脚看下文
A2引脚看下文
A3引脚看下文

2、ADS1115模块有四个寄存器,分别名称、功能和使用:

转换寄存器

Conversion_ADDRESS

简单说就是读取电压的(但读到的数据为16位,并且需要处理)
配置寄存器
Config_ADDRESS
引来配置引脚、功能、量程、模式、速率、比较器等功能

低阈值寄存器

Lo_thresh

如果配置成测量两个引脚之间的电压时可用比较器进行比较,此时该寄存器可设置低阈值

高阈值寄存器

Hi_thresh

如果配置成测量两个引脚之间的电压时可用比较器进行比较,此时该寄存器可设置高阈值

3、一下是配置寄存器的各种选择配置信息(可以根据自己需要进行配置,也可以按照我的配置)

#define ADS1115_ADDRESS			0x90  		    //从机地址(根据ADDR引脚的接线进行判断)
#define Conversion_ADDRESS      0x00			//转换寄存器地址
#define Config_ADDRESS			0x01			//配置寄存器地址
#define Lo_thresh				0x02			//低阈值寄存器地址
#define Hi_thresh				0x03			//高阈值寄存器地址

#define OS_DISABLE				0x00			//关闭(读时:当前正在转换)
#define OS_EABLE				0x01			//开始单次转换配置(读时:当前没有转换)

#define MUX_A0A1				0x00			//A0脚A1差分(默认)
#define MUX_A0A3				0x01			//A0脚A3差分
#define MUX_A1A3				0x02			//A1脚A3差分
#define MUX_A2A3				0x03			//A2脚A3差分
#define MUX_A0  				0x04			//A0脚
#define MUX_A1  				0x05			//A1脚
#define MUX_A2  				0x06			//A2脚
#define MUX_A3  				0x07			//A3脚

#define PGA_0256             	0x05            //量程0.256V
#define PGA_0512            	0x04			//量程0.512V
#define PGA_1024            	0x03			//量程1.024V
#define PGA_2048           	 	0x02			//量程2.048V(默认)
#define PGA_4096            	0x01			//量程4.096V
#define PGA_6144            	0x00			//量程6.144V

#define MODE_Continuous			0x00			//模式配置为连续转换模式
#define MODE_Single	    		0x01			//模式配置为单次或停电状态模式

#define DR_8					0x00			//速率为每秒采点8个(SPS)
#define DR_16					0x01			//速率为每秒采点16个(SPS)
#define DR_32					0x02			//速率为每秒采点32个(SPS)
#define DR_64					0x03			//速率为每秒采点64个(SPS)
#define DR_128					0x04			//速率为每秒采点128个(SPS)(默认)
#define DR_250					0x05			//速率为每秒采点250个(SPS)
#define DR_475					0x06			//速率为每秒采点475个(SPS)
#define DR_860					0x07			//速率为每秒采点860个(SPS)

#define COMP_MODE_Tradition		0x01			//比较器模式默认为传统比较器
#define COMP_MODE_Window		0x01			//比较器模式默认为窗口比较器

#define COMP_POL_LOW			0x00			//比较器极性默认为低电平有效
#define COMP_POL_HIGH			0x01			//比较器极性默认为低电平有效

#define COMP_LAT_DISABLE		0x00			//默认为非锁存比较器
#define COMP_LAT_ENABLE		  	0x01			//锁存比较器

#define COMP_QUE_ONE			0x00  		    //一次转换后断言
#define COMP_QUE_TWO			0x01  		    //两次转换后断言
#define COMP_QUE_THREE			0x02  		    //四次转换后断言
#define COMP_QUE_DISABLE		0x03  		    //差分失能

#define Lo_thresh_LowByte		0x74		    //低阈值寄存器低八位的数据
#define Lo_thresh_HighByte		0x40		    //低阈值寄存器高八位的数据 16,500  3.09375V
#define Hi_thresh_LowByte		0x5C		    //高阈值寄存器低八位的数据
#define Hi_thresh_HighByte		0x44		    //高阈值寄存器高八位的数据 17,500  3.28125V

//这是我已经配置好的数据,可直接写入寄存器(注意高八位低八位数据)
#define Config_High_Byte	(OS_EABLE << 7)|(MUX_A0A1 << 4)|(PGA_6144 << 1)|MODE_Continuous      //高八位配置数据
#define Config_Low_Byte		(DR_128 << 5)|(COMP_MODE_Window << 4)|(COMP_POL_LOW << 3)|(COMP_LAT_ENABLE << 2)|COMP_QUE_TWO      //低八位配置数据

这里简单说明一下(解决A0、A1、A2、A3引脚的问题):

1、如果想要实现单通道测量,将QUE配置为 COMP_QUE_DISABLE 失能比较器就可以了,此时通道引脚就只选择MUX_A0、MUX_A1、MUX_A2、MUX_A3四个当中的任意一个即可。

2、此时接线就将你选择的那个引脚接需要测量的电路,其他的不接就可以了。

3、如果想实现差分测量(可显示正负电压)就可以先跟着我的配置试一遍。

4、注意设置比较器时,高阈值寄存器一定要比低阈值寄存器的值要大。

#define Lo_thresh_LowByte        0x74            //低阈值寄存器低八位的数据
#define Lo_thresh_HighByte       0x40            //低阈值寄存器高八位的数据 16,500  3.09375V
#define Hi_thresh_LowByte        0x5C            //高阈值寄存器低八位的数据
#define Hi_thresh_HighByte       0x44            //高阈值寄存器高八位的数据 17,500  3.28125V

(这是我设置的数值)

三、把上面的宏定义放到下面的ADS1115.c文件中就可以了

以下是ADS1115.c文件:

#include "stm32f10x.h"                  // Device header
#include "I2C.h"
#include "OLED.h"

uint8_t Ack_Flag_Success = 0;						//ACK应答状态(0:成功;1:从机地址错误;2:寄存器地址错误;3:高八位数据通信错误;4:低八位数据错误)

//------------------------------------------写数据------------------------------------------
void ADS1115_WriteReg(uint8_t RegAddress,uint8_t High_Byte,uint8_t Low_Byte)
{
	MyI2C_Start();
	MyI2C_Send8Bit_Data(ADS1115_ADDRESS);  		//找到从机地址
	while(MyI2C_ReceiveAck())                   //每个while都是检查从机是否收到数据应答(存在可能卡死的BUG,一般没事,你可以写成超时退出的逻辑,也可以写成其他的检查方式)
	{
		Ack_Flag_Success = 11;
	}
	
	MyI2C_Send8Bit_Data(RegAddress);  			//告诉从机向哪个地址下的寄存器写数据(写为0x90地址)
	while(MyI2C_ReceiveAck())
	{
		Ack_Flag_Success = 12;
	}
	
	MyI2C_Send8Bit_Data(High_Byte);  			//发送高八位数据
	while(MyI2C_ReceiveAck())
	{
		Ack_Flag_Success = 13;
	}
	
	MyI2C_Send8Bit_Data(Low_Byte);  			//发送低八位数据
    while(MyI2C_ReceiveAck())
	{
		Ack_Flag_Success = 14;
	}
	MyI2C_Stop();
}

//------------------------------------------读数据------------------------------------------
double ADS1115_ReadReg(uint8_t RegAddress)
{
	uint16_t Data;
	double ret;
	
	MyI2C_Start();
	MyI2C_Send8Bit_Data(ADS1115_ADDRESS);  		//找到从机地址
  while(MyI2C_ReceiveAck())
	{
		Ack_Flag_Success = 1;
	}
	
	MyI2C_Send8Bit_Data(RegAddress);			//告诉从机向哪个地址下的寄存器读数据
  while(MyI2C_ReceiveAck())
	{
		Ack_Flag_Success = 2;
	}
	MyI2C_Stop();
	
	MyI2C_Start();
	MyI2C_Send8Bit_Data(ADS1115_ADDRESS | 0x01);//找到从机地址,告诉从机是要读数据(读为0x91地址)
	while(MyI2C_ReceiveAck())
	{
		Ack_Flag_Success = 3;
	}
	
	Data = (MyI2C_Recrive8Bit_Data() << 8) & 0xff00;//开始读数据
	MyI2C_SendAck(0);
	Data += MyI2C_Recrive8Bit_Data();
	
	OLED_ShowNum(3,2,Data,8);    				//调试读到的数据
	
	MyI2C_SendAck(0);
	MyI2C_Stop();
	
	//数值计算取决于PGA配置  PGA:FSR = ±6.144 V(1)
	if(Data > 0x8000)  //如果获取的Data为负值
	{
		ret = ((float)(0xffff - Data) / 32768.0) * 6.144;
	}
	else               //如果获取的Data为正值
	{
		ret = ((float)Data / 32768.0) * 6.144;
	}
	
	//展示数据
	if(Data < 0x8000)                //正电压
	{
		OLED_ShowNum(1,2,ret,6);
		OLED_ShowDouble(4,2,ret,6);  //这是自己写的OLED屏幕显示小数的函数,需要的可以看下后文
	}
	else							 //负电压
	{
		OLED_ShowChar(1,2,'-');
		OLED_ShowChar(4,2,'-');
		OLED_ShowNum(1,3,ret,6);
		OLED_ShowDouble(4,3,ret,6);
	}
	return ret;
}

//-------------------------------------初始化I2C通信-----------------------------------------
void ADS1115_Init(void)
{
	MyI2C_Init();
}

//------------------------------------初始化配置寄存器----------------------------------------
void ADS115_Config(void)
{
	ADS1115_WriteReg(Config_ADDRESS,Config_High_Byte,Config_Low_Byte);   //写入我宏定义中配置好的数据
	ADS1115_WriteReg(Lo_thresh,Lo_thresh_HighByte,Lo_thresh_LowByte);    //设定下限阈值
	ADS1115_WriteReg(Hi_thresh,Hi_thresh_HighByte,Hi_thresh_LowByte);    //设定上限阈值
}

(为什么比0x8000大的就是负值电压呢?因为ADS1115为16位寄存器,负值最高位为符号位也就是1,换算成16进制就是0x8000,所以只要大于0x8000就相当于最高位为1,也就是为负值电压)

(读到数据为什么这样处理?在只读的转换寄存器介绍中是这样写的:“16位转换寄存器包含二进制二进制补码格式的最后一次转换结果。 上电后,转换寄存器清除为0,并保持0直到第一次转换完成。”此时读到的数据想要转换成电压值,就必须知道分辨率是多少:你选择的量程(当前是6.144)/ 0x8000 = 分辨率(0.0001875),只需要读到的数据 * 分辨率(0.0001875)= 电压值,所以上述代码需要这样处理。)

其他的OLED库函数网上有很多,显示小数的不多,可以直接修改以下函数实现(当然也可以直接进行串口调试):
 

/**
  * @brief  OLED显示小数(十进制,正数)
	* @param  Line行数
	* @param  Column起始位置
  * @param  double类型小数
	* @param  小数点后n位
  * @retval 无
  */
void OLED_ShowDouble(uint8_t Line, uint8_t Column, double Data,int n)
{
    int a = (int)Data;  					//整数部分
    int Long = 1;  								//整数部分的位数
	int num = a;  								//中间变量
	while(num >= 10)  							//判断整数部分有几位
	{
		num /= 10;
		Long++;
	}
	
	OLED_ShowNum(Line,Column,a,Long);  //显示整数部分(可替换成你文件里的显示整数的库函数)
	OLED_ShowChar(Line,(Column + Long),'.');  //显示字符部分(可替换成你文件里的显示字符的库函数)
	
	double x = Data - a;
	int y = 1;
	int b = 1;
	while(n > 0)  //循环显示小数部分(显示小数点三位或三位以上会存在误差,如5.678可能会显示为5.677,多打印后几位为5.677999)
    {
		x = Data - a;
		x = x * 10 * b;
		OLED_ShowNum(Line,Column + Long + (y++),(int)x,1);  //显示整数部分(可替换成你文件里的显示整数的库函数)
		n--;
		b *= 10;
    }
}

接下来是ADS1115.h文件的内容:

#ifndef __ADS1115_H
#define __ADS1115_H

#define ADS1115_ADDRESS			0x90  		    //从机地址(根据ADDR引脚的接线进行判断)
#define Conversion_ADDRESS      0x00			//转换寄存器地址
#define Config_ADDRESS			0x01			//配置寄存器地址
#define Lo_thresh				0x02			//低阈值寄存器地址
#define Hi_thresh				0x03			//高阈值寄存器地址

#define OS_DISABLE				0x00			//关闭(读时:当前正在转换)
#define OS_EABLE				0x01			//开始单次转换配置(读时:当前没有转换)

#define MUX_A0A1				0x00			//A0脚A1差分(默认)
#define MUX_A0A3				0x01			//A0脚A3差分
#define MUX_A1A3				0x02			//A1脚A3差分
#define MUX_A2A3				0x03			//A2脚A3差分
#define MUX_A0  				0x04			//A0脚
#define MUX_A1  				0x05			//A1脚
#define MUX_A2  				0x06			//A2脚
#define MUX_A3  				0x07			//A3脚

#define PGA_0256             	0x05            //量程0.256V
#define PGA_0512            	0x04			//量程0.512V
#define PGA_1024            	0x03			//量程1.024V
#define PGA_2048           	 	0x02			//量程2.048V(默认)
#define PGA_4096            	0x01			//量程4.096V
#define PGA_6144            	0x00			//量程6.144V

#define MODE_Continuous			0x00			//模式配置为连续转换模式
#define MODE_Single	    		0x01			//模式配置为单次或停电状态模式

#define DR_8					0x00			//速率为每秒采点8个(SPS)
#define DR_16					0x01			//速率为每秒采点16个(SPS)
#define DR_32					0x02			//速率为每秒采点32个(SPS)
#define DR_64					0x03			//速率为每秒采点64个(SPS)
#define DR_128					0x04			//速率为每秒采点128个(SPS)(默认)
#define DR_250					0x05			//速率为每秒采点250个(SPS)
#define DR_475					0x06			//速率为每秒采点475个(SPS)
#define DR_860					0x07			//速率为每秒采点860个(SPS)

#define COMP_MODE_Tradition		0x01			//比较器模式默认为传统比较器
#define COMP_MODE_Window		0x01			//比较器模式默认为窗口比较器

#define COMP_POL_LOW			0x00			//比较器极性默认为低电平有效
#define COMP_POL_HIGH			0x01			//比较器极性默认为低电平有效

#define COMP_LAT_DISABLE		0x00			//默认为非锁存比较器
#define COMP_LAT_ENABLE		  	0x01			//锁存比较器

#define COMP_QUE_ONE			0x00  		    //一次转换后断言
#define COMP_QUE_TWO			0x01  		    //两次转换后断言
#define COMP_QUE_THREE			0x02  		    //四次转换后断言
#define COMP_QUE_DISABLE		0x03  		    //差分失能

#define Lo_thresh_LowByte		0x74		    //低阈值寄存器低八位的数据
#define Lo_thresh_HighByte		0x40		    //低阈值寄存器高八位的数据 16,500  3.09375V
#define Hi_thresh_LowByte		0x5C		    //高阈值寄存器低八位的数据
#define Hi_thresh_HighByte		0x44		    //高阈值寄存器高八位的数据 17,500  3.28125V

//这是我已经配置好的数据,可直接写入寄存器(注意高八位低八位数据)
#define Config_High_Byte	(OS_EABLE << 7)|(MUX_A0A1 << 4)|(PGA_6144 << 1)|MODE_Continuous      //高八位配置数据
#define Config_Low_Byte		(DR_128 << 5)|(COMP_MODE_Window << 4)|(COMP_POL_LOW << 3)|(COMP_LAT_ENABLE << 2)|COMP_QUE_TWO      //低八位配置数据

extern uint8_t Ack_Flag_Success;  //用于检查从机是否收到主机的数据(就是检查一下ACK应答)

void ADS1115_WriteReg(uint8_t RegAddress,uint8_t High_Byte,uint8_t Low_Byte);
double ADS1115_ReadReg(uint8_t RegAddress);
void ADS1115_Init(void);
void ADS115_Config(void);

#endif

四、万里长征最后一步,只需要在主函数里面调用一下就可以了(main.c的内容):

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "ADS1115.h"
#include "I2C.h"
#include "Delay.h"
#include "USART.h"
#include <string.h>
#include <stdio.h>

//这个函数是为了看一下从机是否接收到数据从而返回应答,在OLED屏幕的第二行展示一下成功或错误就可以了
//不用也可以
void ACK(void)
{
	if(Ack_Flag_Success == 0)
	{
		OLED_ShowString(2,2,"ACK:Success");
	}
	else if(Ack_Flag_Success == 1)
	{
		OLED_ShowString(2,2,"ACK:R ADD ERROR");
	}
	else if(Ack_Flag_Success == 2)
	{
		OLED_ShowString(2,2,"ACK:R RegAdd ERROR");
	}
	else if(Ack_Flag_Success == 3)
	{
		OLED_ShowString(2,2,"ACK:R Data ERROR");
	}
	else if(Ack_Flag_Success == 11)
	{
		OLED_ShowString(2,2,"ACK:W ADD ERROR");
	}
	else if(Ack_Flag_Success == 12)
	{
		OLED_ShowString(2,2,"ACK:W RegAdd ERROR");
	}
	else if(Ack_Flag_Success == 13)
	{
		OLED_ShowString(2,2,"ACK:WH Data ERROR");
	}
	else if(Ack_Flag_Success == 14)
	{
		OLED_ShowString(2,2,"ACK:WL Data ERROR");
	}
}

int main(void)
{
	OLED_Init();		//OLED初始化
	ADS1115_Init();     //初始化ADSM模块
	ADS115_Config();    //向ADS1115的配置寄存器中写配置数据
	OLED_ShowChar(4,11,'V');  //显示电压单位
	
	while (1)
	{
		ADS1115_ReadReg(Conversion_ADDRESS);  //对转换寄存器中进行读数据
		Delay_ms(1);
		ACK();                                //调用检查应答的函数
		Delay_ms(1000);                       //每间隔1S测量一下
	}
}

最后接线:我配置的是 MUX_A0A1 ,也就是A0和A1之间的电压,只需要将A0接到正极,A1接到负极就可以会显示正电压,把A0和A1反转接线就会显示负电压。

(注意若要在程序中切换通道,中间必须延时5毫秒,否则采集值不准)

有需要的可以简单学习,好多细节内容也没讲到,但是跟着把代码敲下来就差不多能弄懂,如果有难度可以直接建好文件复制代码就能实现,然后试着改动一下配置寄存器的参数观察发现的变化,这样也能学到东西。非常用心的编写,希望大家点赞多支持初学者。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值