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 模块来源
采购链接: ADS1115 超小型 16 位 模数转换器 ADC 4 通道
2.9.2 规格参数
工作电压: 2.0-5.5V
工作电流: 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 5 | 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 5 | 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 为例。
/******************************************************************
* 函 数 名 称: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);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/******************************************************************
* 函 数 名 称: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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2.9.3.2 引脚选择
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;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
在文件 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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2.9.4 移植验证
在自己工程中的 main 主函数中,编写如下。
/********************************************************************************
* 文 件 名: main.c
* 版 本 号: 初版
* 修改作者: LC
* 修改日期: 2023年04月06日
* 功能介绍:
******************************************************************************
* 注意事项:
*********************************************************************************/
#include "gd32f4xx.h"
#include "systick.h"
#include "bsp_usart.h"
#include "bsp_ads1115.h"
#include "stdio.h"
int main(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); // 优先级分组
systick_config(); //滴答定时器初始化 1ms
usart_gpio_config(115200);
ADS1115_GPIO_Init();
printf("demo start\r\n");
while(1)
{
//当前设置最大量程为4.096V
printf("A0 = %.4f\r\n", ReadADS1115(0x00) );//读取A0的值
delay_1ms(1000);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
移植现象:将 A0 接入 GND、3.3V 和 5V。
移植成功示例,见文件 2.9.4-1 。
文件 2.9.4-1 移植成功示例