2.9 ADS1115多路模数转换器
ADS1115 器件是兼容 IIC 的 16 位高精度低功耗模数转换器 (ADC),采用超小型无引线 X2QFN-10 封装和 VSSOP-10 封装。ADS111x 器件采用了低漂移电压基准和振荡器。ADS1114 和 ADS1115 还采用可编程增益放大器(PGA)和数字比较器。这些特性加以较宽的工作电源电压范围使得 ADS1115 非常适合功率与空间受限的传感器测量。
ADS111x 可在数据速率高达每秒 860 个样本 (SPS) 的情况下执行转换。PGA 可提供从 ±256mV 到±6.144V 的输入范围,从而实现精准的大小信号测量。ADS1115 具有 一个输入多路复用器 (MUX),可实现两次差动输入测量或四次单端输入测量。在ADS1115 中可使用数字比较器进行欠压和过压检测。 ADS1115既可在连续转换模式下工作,也可在单冲模式下工作。在单冲模式下,这些器件可在一次转换后自动断电;因此显著降低了空闲期间的功耗。
2.9.1 模块来源
资料下载:
【https://pan.quark.cn/s/47d05f5fc820】
2.9.2 规格参数
工作电压:2.0-5.5V
工作电流:150uA
采集精度:16位
采集通道:4通道
控制方式:IIC
管脚数量:10 Pin(2.54mm间距排针)
工作电流:150uA
采集精度:16位
采集通道:4通道
控制方式:IIC
管脚数量:10 Pin(2.54mm间距排针)
2.9.3 移植过程
我们的目标是在梁山派GD32F470上能够实现4路ADC采集电压功能。首先要获取资料,查看数据手册应如何实现,再移植至我们的工程。
2.9.3.1 查看资料
ADS1115是采用的IIC通信,所以首先要了解IIC的地址与时序,再确定根据寄存器的设置。
(1)器件地址
器件地址的设置见下表。
说明:当模块上的ADDR引脚接入GND时,其器件地址为1001000,最后一位数据是读写位。
BIT 7 | BIT 6 | BIT 6 | BIT 4 | BIT 3 | BIT 2 | BIT 1 | BIT 0 |
---|---|---|---|---|---|---|---|
1 | 0 | 0 | 1 | 0 | 0 | 0 | R/W |
当模块上的ADDR引脚接入VDD时,其器件地址为1001001,最后一位数据是读写位。
BIT 7 | BIT 6 | BIT 6 | BIT 4 | BIT 3 | BIT 2 | BIT 1 | BIT 0 |
---|---|---|---|---|---|---|---|
1 | 0 | 0 | 1 | 0 | 0 | 1 | R/W |
本文以接入GND为地址。即后续的地址为0X90。
(2)时序
下图是读时序,步骤是:
IIC起始信号-> 发送器件地址+0(写) -> 等待模块应答 -> 应答后发送寄存器地址 -> 等待模块应答 -> 重新发送起始信号 -> 发送器件地址+1(读) -> 等待模块应答 -> 应答后读取高8位数据 -> 读取完毕主机发送应答信号 -> 读取低8位数据 -> 读取完毕主机发送应答信号 -> 发送IIC停止信号。
下图是写时序,步骤是:
IIC起始信号-> 发送器件地址+0(写) -> 等待模块应答 -> 应答后发送寄存器地址 -> 等待模块应答 -> 应答后写入高8位数据 -> 等待模块应答 -> 写入低8位数据 -> 等待模块应答 -> 发送IIC停止信号。
(3)寄存器说明
ADS1115有四个寄存器,可通过IIC接口使用地址指针进入。
-
地址0X00为转换寄存器,它包含最后一次转换的结果。
-
地址0X01为配置寄存器,用于更改ADS1115的工作模式和查询设备状态。
-
另外两个寄存器,Lo_thresh和Hi_thresh,设置用于比较器函数的阈值,我们用不到。
配置寄存器说明
配置寄存器有16位,用于控制工作模式、输入选择、数据速率、满量程范围和比较器模式。
第15位:OS,读操作时可以知道当前设备的工作状态;写操作时可以设置单次转换。本文配置为1(必须为断电模式下,当对OS写1时,设备会进入上电模式并完成一次数据转换,然后会自动将OS置0)
第14-12位:MUX为输入多路复用器,对输入模式进行选择,如下图有八种输入模式,分别是四种差分与四种单端输入,本文配置为A0单端输入(0x04)。(单端输入就是测量的数据有两个引脚,一个输出一个地。将测量的输出接入A0引脚,测量的地与ADS1115共地)
第11-9位:PGA为可编程增益放大器,设置FSR(满刻度的范围),本文配置为±4.096V(0x01)后面电压计算公式与这个有关。
第8位:MODE选择持续转换模式与单次转换模式(单次转换模式需要OS位触发),本文配置为连续转换模式(0x00)
第7-5位:DR配置data rate数据传输速率,本文配置为128SPS(0x04)
第4-2位:对比较器的配置,我们不使用,默认为0即可(0x00)
第1-0位:本位配置为关闭比较器并将ALERT/RDY引脚设置为高阻抗模式(0x03)
最终得到的配置结果为1100_0010_1000_0011(0xC283)。
当前配置的是A0的引脚,我们后续获取数据也是从A0引脚读取。
转换寄存器说明
16位转换寄存器以二进制的补码格式保存最后一次转换的结果。需要注意的是,在上电之后,转换寄存器被清除为0,并保持为0,直到第一次转换完成。
(4)实现代码说明
读取到的ADC值如何换算为电压?
以PGA设置为4.96V为例。
电压 = 采集到的ADC值 * 分辨率
分辨率 = 测量电压范围 / (2^AD位数-1) = 4.096 / 2的15次方 = 0.000125V
分辨率也可以在数据手册中查看,见右图。其中125uV = 0.125mV = 0.000125V。
/******************************************************************
* 函 数 名 称:WriteADS1115
* 函 数 说 明:向ADS1115的add地址写入dat数据
* 函 数 形 参: add写入寄存器地址
* dat_H写入的高8位数据
* dat_L写入的低8位数据
* 函 数 返 回:0写入成功
* 1写入器件地址无应答
* 2写入寄存器地址无应答
* 作 者:LC
* 备 注:器件地址=0X90
******************************************************************/
uint8_t WriteADS1115(uint8_t add,uint8_t dat_H,
uint8_t dat_L)
{
IIC_Start();//起始信号
IIC_Write(0x90);//器件地址
if( IIC_Wait_Ack() == 1 )
return 1;
IIC_Write(add);//寄存器地址
if( IIC_Wait_Ack() == 1 )
return 2;
IIC_Write(dat_H);//写入高8位
IIC_Wait_Ack();//等待应答
IIC_Write(dat_L);//写入低8位
IIC_Wait_Ack();//等待应答
IIC_Stop();//停止信号
return (0);
}
/******************************************************************
* 函 数 名 称:ReadADS1115
* 函 数 说 明:读取ADS1115的数据
* 函 数 形 参:add读取的寄存器地址
* 函 数 返 回:-1-读取失败 其他-读取成功
* 作 者:LC
* 备 注:无
******************************************************************/
float ReadADS1115(unsigned char add)
{
int i =0;
unsigned char dat[2]={0};
unsigned int num = 0;
float ret=0;
IIC_Start();//起始信号
IIC_Write(0x90);//器件地址+写
if( IIC_Wait_Ack() == 1 )
return -1;
IIC_Write(add);//寄存器地址
if( IIC_Wait_Ack() == 1 )
return -1;
do{
//超时判断
i++;
if( i > 20 ) return -1;
delay_1ms(1);
IIC_Start();//重新发送起始信号
IIC_Write(0x91);//器件地址+读
}while(IIC_Wait_Ack() == 1);
dat[0]=IIC_Read();//读高8位数据
IIC_Send_Ack(0);//应答
dat[1]=IIC_Read();//读低8位数据
IIC_Send_Ack(1);//非应答
IIC_Stop();//发送停止信号
//数据整合
num = ((dat[0]<<8) | (dat[1]));
//分辨率计算:测量电压范围/(2^AD位数-1)
// 分辨率= 4.096/2^15=0.000125
// 电压= 采集到的ADC值 * 分辨率
if(num>32768)
ret=(65535-num)*0.000125;
else
ret=num*0.000125;
return ret;
}
2.9.3.2 引脚选择
ADS1115 | 立创·梁山派 |
---|---|
VDD | 5V |
GND | GND |
SCL | PB8 |
SDA | PB9 |
A0 | 接入要测量的电压 |
2.9.3.3 移植至工程
移植步骤中的导入.c和.h文件与之前相同,只是将.c和.h文件更改为bsp_ads1115.c与bsp_ads1115.h。见2.2.3.3 移植至工程。这里不再过多讲述。移植完成后面修改相关代码。
在文件bsp_ads1115.c中,编写如下代码。
/********************************************************************************
* 文 件 名: bsp_ads1115.c
* 版 本 号: 初版
* 修改作者: LC
* 修改日期: 2023年04月06日
* 功能介绍:
******************************************************************************
* 注意事项:
*********************************************************************************/
#include "bsp_ads1115.h"
#include "systick.h"
#include "stdio.h"
/******************************************************************
* 函 数 名 称:ADS1115_GPIO_Init
* 函 数 说 明:对IIC引脚初始化
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:1100_0010_1000_0011 WriteADS1115(0x01,0xc2,0x83);
******************************************************************/
void ADS1115_GPIO_Init(void)
{
//打开SDA与SCL的引脚时钟
rcu_periph_clock_enable(RCU_SCL);
rcu_periph_clock_enable(RCU_SDA);
//设置SCL引脚模式为上拉输出
gpio_mode_set(PORT_SCL, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_SCL);
//设置引脚为开漏模式,翻转速度2MHz
gpio_output_options_set(PORT_SCL, GPIO_OTYPE_OD, GPIO_OSPEED_2MHZ, GPIO_SCL);
//设置引脚输出高电平SCL等待信号
gpio_bit_write(PORT_SCL, GPIO_SCL, SET);
//设置SDA引脚
gpio_mode_set(PORT_SDA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_SDA);
gpio_output_options_set(PORT_SDA, GPIO_OTYPE_OD, GPIO_OSPEED_2MHZ, GPIO_SDA);
gpio_bit_write(PORT_SDA, GPIO_SDA, SET);
//写入配置参数
WriteADS1115(0x01,0xC2,0x83);
}
/******************************************************************
* 函 数 名 称:IIC_Start
* 函 数 说 明:IIC起始信号
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void IIC_Start(void)
{
SDA_OUT();
SDA(1);
delay_1us(5);
SCL(1);
delay_1us(5);
SDA(0);
delay_1us(5);
SCL(0);
delay_1us(5);
}
/******************************************************************
* 函 数 名 称:IIC_Stop
* 函 数 说 明:IIC停止信号
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void IIC_Stop(void)
{
SDA_OUT();
SCL(0);
SDA(0);
SCL(1);
delay_1us(5);
SDA(1);
delay_1us(5);
}
/******************************************************************
* 函 数 名 称:IIC_Send_Ack
* 函 数 说 明:主机发送应答
* 函 数 形 参:0应答 1非应答
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void IIC_Send_Ack(unsigned char ack)
{
SDA_OUT();
SCL(0);
SDA(0);
delay_1us(5);
if(!ack) SDA(0);
else SDA(1);
SCL(1);
delay_1us(5);
SCL(0);
SDA(1);
}
/******************************************************************
* 函 数 名 称:IIC_Wait_Ack
* 函 数 说 明:等待从机应答
* 函 数 形 参:无
* 函 数 返 回:1=无应答 0=有应答
* 作 者:LC
* 备 注:无
******************************************************************/
unsigned char IIC_Wait_Ack(void)
{
char ack = 0;
unsigned char ack_flag = 10;
SDA_IN();
SDA(1);
delay_1us(5);
SCL(1);
delay_1us(5);
while( (GETSDA()==1) && ( ack_flag ) )
{
ack_flag--;
delay_1us(5);
}
if( ack_flag <= 0 )
{
IIC_Stop();
return 1;
}
else
{
SCL(0);
SDA_OUT();
}
return ack;
}
/******************************************************************
* 函 数 名 称:IIC_Write
* 函 数 说 明:IIC写一个字节
* 函 数 形 参:dat写入的数据
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void IIC_Write(unsigned char dat)
{
int i = 0;
SDA_OUT();
SCL(0);//拉低时钟开始数据传输
for( i = 0; i < 8; i++ )
{
SDA( (dat & 0x80) >> 7 );
__nop();
__nop();
__nop();
dat<<=1;
delay_1us(6);
SCL(1);
delay_1us(4);
SCL(0);
delay_1us(4);
}
}
/******************************************************************
* 函 数 名 称:IIC_Read
* 函 数 说 明:IIC读1个字节
* 函 数 形 参:无
* 函 数 返 回:读出的1个字节数据
* 作 者:LC
* 备 注:无
******************************************************************/
unsigned char IIC_Read(void)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
SCL(0);
delay_1us(5);
SCL(1);
delay_1us(5);
receive<<=1;
if( GETSDA() )
{
receive|=1;
}
delay_1us(5);
}
return receive;
}
/******************************************************************
* 函 数 名 称:WriteADS1115
* 函 数 说 明:向ADS1115的add地址写入dat数据
* 函 数 形 参:add写入寄存器地址 dat_H写入的高8位数据 dat_L写入的低8位数据
* 函 数 返 回:0写入成功 1写入器件地址无应答 2写入寄存器地址无应答
* 3写入高8位数据无应答 4写入低8位数据无应答
* 作 者:LC
* 备 注:器件地址=0X90
******************************************************************/
uint8_t WriteADS1115(uint8_t add,uint8_t dat_H,uint8_t dat_L)
{
IIC_Start();
IIC_Write(0x90);
if( IIC_Wait_Ack() == 1 )
{
printf("error 1\r\n");
return 1;
}
IIC_Write(add);
if( IIC_Wait_Ack() == 1 )
{
printf("error 2\r\n");
return 2;
}
IIC_Write(dat_H);
IIC_Wait_Ack();
IIC_Write(dat_L);
IIC_Wait_Ack();
IIC_Stop();
return (0);
}
/******************************************************************
* 函 数 名 称:ReadADS1115
* 函 数 说 明:读取ADS1115的数据
* 函 数 形 参:add读取的寄存器地址
* 函 数 返 回:-1-读取失败 其他-读取成功
* 作 者:LC
* 备 注:无
******************************************************************/
float ReadADS1115(unsigned char add)
{
int i =0;
unsigned char dat[2]={0};
unsigned int num = 0;
float ret=0;
IIC_Start();//起始信号
IIC_Write(0x90);//器件地址+写
if( IIC_Wait_Ack() == 1 )
return -1;
IIC_Write(add);//寄存器地址
if( IIC_Wait_Ack() == 1 )
return -1;
do{
//超时判断
i++;
if( i > 20 ) return -1;
delay_1ms(1);
IIC_Start();//重新发送起始信号
IIC_Write(0x91);//器件地址+读
}while(IIC_Wait_Ack() == 1);
dat[0]=IIC_Read();//读高8位数据
IIC_Send_Ack(0);//应答
dat[1]=IIC_Read();//读低8位数据
IIC_Send_Ack(1);//非应答
IIC_Stop();//发送停止信号
//数据整合
num = ((dat[0]<<8) | (dat[1]));
//数值计算取决于PGA配置
//2的15次方=32768
//设置的最大量程4.096
// if(num>32768)
// ret=((float)(65535-num)/32768.0)*4.096;
// else
// ret=((float)num/32768.0)*4.096;
//分辨率计算:测量电压范围/(2^AD位数-1)
// 分辨率= 4.096/2^15=0.000125
// 电压= 采集到的ADC值 * 分辨率
if(num>32768)
ret=(65535-num)*0.000125;
else
ret=num*0.000125;
return ret;
}
在文件bsp_ads1115.h中,编写如下代码。
#ifndef _BSP_ADS1115_H_
#define _BSP_ADS1115_H_
#include "gd32f4xx.h"
#define RCU_SCL RCU_GPIOB
#define PORT_SCL GPIOB
#define GPIO_SCL GPIO_PIN_8
#define RCU_SDA RCU_GPIOB
#define PORT_SDA GPIOB
#define GPIO_SDA GPIO_PIN_9
#define SDA_IN() {gpio_mode_set(PORT_SDA, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_SDA);} //SDA输入模式
#define SDA_OUT() {gpio_mode_set(PORT_SDA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_SDA);} //SDA输出模式
#define SCL(BIT) gpio_bit_write( PORT_SCL, GPIO_SCL, BIT?SET:RESET)
#define SDA(BIT) gpio_bit_write( PORT_SDA, GPIO_SDA, BIT?SET:RESET)
#define GETSDA() gpio_input_bit_get( PORT_SDA, GPIO_SDA)
void ADS1115_GPIO_Init(void);
unsigned char WriteADS1115(unsigned char add,unsigned char dat_H,unsigned char dat_L);
float ReadADS1115(unsigned char add);
#endif
2.9.4 移植验证
在自己工程中的main主函数中,编写如下。
【完整代码资料见:https://pan.quark.cn/s/47d05f5fc820】
移植现象:将A0接入GND、3.3V和5V。
移植成功示例:
【完整代码资料见:https://pan.quark.cn/s/47d05f5fc820】