2.57 AGS10 有害气体传感器
AGS10 是一款采用数字信号输出的 MEMS TVOC 传感器。配置了专用的数字模块采集技术和气体感应传感技术,确保了产品具有极高的可靠性与卓越的长期稳定性,同时具有低功耗、高灵敏度、快速响应、成本低、驱动电路简单等特点。
2.57.1 模块来源
2.57.2 规格参数
工作电压 : 3.0-6.0 V 典型功率 : 75mW 采样周期 : ≥2s 接口速率 :I2C 从机模式(≤15kHz) 预热时间 : ≥120s 工作温度 : 0~50℃ 工作湿度 : 0~95%RH 寿命 : >5 年(25℃,清洁空气中) 输出单位 : ppb 测量范围 : 0~99999 ppb 典型精度( 25℃/50%RH): 25% 读数 标准测试气体 : 乙醇 模块尺寸:15*10.6mm 数据手册
2.57.3 工作原理
2.57.4 通信步骤
AGS10 传感器采用标准 I 2C 通信协议,适应多种设备。IIC 的物理接口包含串行数据信号(SDA)与串行时钟信号(SCL)两个接口。两个接口需通过 1kΩ~10kΩ 电阻上拉至 VDD。SDA 用于读、写传感器数据。SCL 上电必须保持高电平直到进行 IIC 通信开始,否则会引起 I 2C 通讯不良。当 I 2C 通信时 SCL 用于主机与传感器之间的通讯同步。多个 I2C 设备可以共享总线,但是只能允许一个主机设备出现在总线上。既然采用的是 IIC,那么我们需要知道它的 IIC 通信地址。
传感器 IIC 器件地址为 0x1A(7-bit),写指令为 0x34,读指令为 0x35。
读取 TVOC 数据的步骤:
具体实现读取数据代码如下:
uint8_t ags10_read(void)
{
uint8_t timeout = 0;
uint8_t data[5] = {0};
//发送起始信号S
AGS10_IIC_Start();
//发送器件地址+写
AGS10_IIC_Send_Byte(0X34);
//等待应答ACK,如不应答则结束该函数
if( AGS10_I2C_WaitAck() == 1 ) return 1;
//发送寄存器地址
AGS10_IIC_Send_Byte(0X00);
//等待应答ACK,如不应答则结束该函数
if( AGS10_I2C_WaitAck() == 1 ) return 2;
//发送停止信号P
AGS10_IIC_Stop();
do{
delay_1ms(1);
timeout++;
//重新发送起始信号
AGS10_IIC_Start();
//发送器件地址+读
AGS10_IIC_Send_Byte(0X35);
}while( (AGS10_I2C_WaitAck() == 1) && (timeout >= 50) );
//如果超时
if( timeout >= 50 ) return 3;
//读取5个数据
data[0] = AGS10_IIC_Read_Byte();
AGS10_IIC_Send_Ack();
data[1] = AGS10_IIC_Read_Byte();
AGS10_IIC_Send_Ack();
data[2] = AGS10_IIC_Read_Byte();
AGS10_IIC_Send_Ack();
data[3] = AGS10_IIC_Read_Byte();
AGS10_IIC_Send_Ack();
data[4] = AGS10_IIC_Read_Byte();
AGS10_IIC_Send_Nack();
AGS10_IIC_Stop();
//CRC校验
if( Calc_CRC8(data,4) != data[4] )
{
// printf("Check failed\r\n");
return 4;
}
//数据整合
TVOC_data = (data[1]<<16) | (data[2]<<8) | data[3] ;
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2.57.5 移植工程
AGS10 通讯方式为 I2C 通讯,我们只需要用梁山派实现 I2C 通讯,并按照数据手册对 AGS10 进行控制,就可以获取实时的有害气体浓度数据。这里我给出的代码是基于软件 I2C,便于理解 I2C 通讯协议。
2.57.5.1 引脚选择
I2C 通讯引脚选择 PF7,PF9。
AGS10 | ######### 立创·梁山派 | ######### 接线图 |
---|
2.57.5.2 移植步骤
下面给出 AGS10 的驱动代码,包括软件 IIC 的实现,以及 AGS10 的采集。
bsp_ags10.c
#include "bsp_ags10.h"
#include "bsp_usart.h"
#include "stdio.h"
/******************************************************************
* 函 数 名 称:ags10_gpio_init
* 函 数 说 明:对AGS10的IIC引脚初始化
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:
******************************************************************/
void ags10_gpio_init(void)
{
//打开SDA与SCL的引脚时钟
rcu_periph_clock_enable(RCU_AGS10_SCL);
rcu_periph_clock_enable(RCU_AGS10_SDA);
//设置SCL引脚模式为上拉输出
gpio_mode_set(PORT_AGS10_SCL, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_AGS10_SCL);
//设置引脚为开漏模式,翻转速度2MHz
gpio_output_options_set(PORT_AGS10_SCL, GPIO_OTYPE_OD, GPIO_OSPEED_2MHZ, GPIO_AGS10_SCL);
//设置引脚输出高电平SCL等待信号
gpio_bit_write(PORT_AGS10_SCL, GPIO_AGS10_SCL, SET);
//设置SDA引脚
gpio_mode_set(PORT_AGS10_SDA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_AGS10_SDA);
gpio_output_options_set(PORT_AGS10_SDA, GPIO_OTYPE_OD, GPIO_OSPEED_2MHZ, GPIO_AGS10_SDA);
gpio_bit_write(PORT_AGS10_SDA, GPIO_AGS10_SDA, SET);
}
//起始信号
void AGS10_IIC_Start(void)
{
AGS10_SDA_OUT();
AGS10_SDA(1);
AGS10_SCL(1);
delay_1us(5);
AGS10_SDA(0);
delay_1us(5);
AGS10_SCL(0);
delay_1us(5);
}
//停止信号
void AGS10_IIC_Stop(void)
{
AGS10_SDA_OUT();
AGS10_SCL(0);
AGS10_SDA(0);
AGS10_SCL(1);
delay_1us(5);
AGS10_SDA(1);
delay_1us(5);
}
//发送非应答
void AGS10_IIC_Send_Nack(void)
{
AGS10_SDA_OUT();
AGS10_SCL(0);
AGS10_SDA(0);
AGS10_SDA(1);
AGS10_SCL(1);
delay_1us(5);
AGS10_SCL(0);
AGS10_SDA(0);
}
//发送应答
void AGS10_IIC_Send_Ack(void)
{
AGS10_SDA_OUT();
AGS10_SCL(0);
AGS10_SDA(1);
AGS10_SDA(0);
AGS10_SCL(1);
delay_1us(5);
AGS10_SCL(0);
AGS10_SDA(1);
}
/**********************************************************
* 函 数 名 称:I2C_WaitAck
* 函 数 功 能:等待从机应答
* 传 入 参 数:无
* 函 数 返 回:1=非应答 0=应答
* 作 者:LC
* 备 注:无
**********************************************************/
unsigned char AGS10_I2C_WaitAck(void)
{
char ack = 0;
unsigned char ack_flag = 10;
AGS10_SCL(0);
AGS10_SDA(1);
AGS10_SDA_IN();
delay_1us(5);
AGS10_SCL(1);
delay_1us(5);
while( (AGS10_GETSDA()==1) && ( ack_flag ) )
{
ack_flag--;
delay_1us(5);
}
//非应答
if( ack_flag <= 0 )
{
AGS10_IIC_Stop();
return 1;
}
else//应答
{
AGS10_SCL(0);
AGS10_SDA_OUT();
}
return ack;
}
//发送一个字节
void AGS10_IIC_Send_Byte(uint8_t dat)
{
int i = 0;
AGS10_SDA_OUT();
AGS10_SCL(0);
for( i = 0; i < 8; i++ )
{
AGS10_SDA( (dat & 0x80) >> 7 );
delay_1us(1);
AGS10_SCL(1);
delay_1us(5);
AGS10_SCL(0);
delay_1us(5);
dat<<=1;
}
}
//接收一个字节
unsigned char AGS10_IIC_Read_Byte(void)
{
unsigned char i,receive=0;
AGS10_SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
AGS10_SCL(0);
delay_1us(5);
AGS10_SCL(1);
delay_1us(5);
receive<<=1;
if( AGS10_GETSDA() )
{
receive |= 1;
}
}
AGS10_SCL(0);
return receive;
}
//********************************************************************
//函数名称:Calc_CRC8
//功能 :CRC8 计算,初值:0xFF,多项式:0x31(x8 + x5 + x4 +1)
//参数 :u8* dat:需要校验数据的首地址;u8 Num:CRC 校验数据长度
//返回 :crc:计算出的校验值
//********************************************************************
uint8_t Calc_CRC8(uint8_t *dat, uint8_t Num)
{
uint8_t i, byte, crc=0xFF;
for(byte=0; byte<Num; byte++)
{
crc ^= (dat[byte]);
for( i = 0; i < 8; i++ )
{
if(crc & 0x80) crc = ( crc << 1 ) ^ 0x31;
else crc = ( crc << 1 );
}
}
return crc;
}
/**********************************************************
* 函 数 名 称:ags10_read
* 函 数 功 能:读取AGS10的TVOC浓度数据
* 传 入 参 数:无
* 函 数 返 回:1: 通信失败
* 2:发送失败
* 3:等待超时
* 4: 校验失败
* 作 者:LCKFB
* 备 注:
**********************************************************/
uint32_t ags10_read(void)
{
uint8_t timeout = 0;
uint8_t data[5] = {0};
uint32_t TVOC_data = 0;
AGS10_IIC_Start();
AGS10_IIC_Send_Byte(0X34);
if( AGS10_I2C_WaitAck() == 1 ) return 1;
AGS10_IIC_Send_Byte(0X00);
if( AGS10_I2C_WaitAck() == 1 ) return 2;
AGS10_IIC_Stop();
do{
delay_1ms(1);
timeout++;
AGS10_IIC_Start();
AGS10_IIC_Send_Byte(0X35);
}while( (AGS10_I2C_WaitAck() == 1) && (timeout >= 50) );
//如果超时
if( timeout >= 50 ) return 3;
data[0] = AGS10_IIC_Read_Byte();
AGS10_IIC_Send_Ack();
data[1] = AGS10_IIC_Read_Byte();
AGS10_IIC_Send_Ack();
data[2] = AGS10_IIC_Read_Byte();
AGS10_IIC_Send_Ack();
data[3] = AGS10_IIC_Read_Byte();
AGS10_IIC_Send_Ack();
data[4] = AGS10_IIC_Read_Byte();
AGS10_IIC_Send_Nack();
AGS10_IIC_Stop();
if( Calc_CRC8(data,4) != data[4] )
{
// printf("Check failed\r\n");
return 4;
}
TVOC_data = (data[1]<<16) | (data[2]<<8) | data[3] ;
return TVOC_data;
}
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
bsp_ags10.h
#ifndef _BSP_AGS10_H_
#define _BSP_AGS10_H_
#include "gd32f4xx.h"
#define RCU_AGS10_SCL RCU_GPIOF
#define PORT_AGS10_SCL GPIOF
#define GPIO_AGS10_SCL GPIO_PIN_7
#define RCU_AGS10_SDA RCU_GPIOF
#define PORT_AGS10_SDA GPIOF
#define GPIO_AGS10_SDA GPIO_PIN_9
#define AGS10_SDA_IN() {gpio_mode_set(PORT_AGS10_SDA, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_AGS10_SDA);} //SDA输入模式
#define AGS10_SDA_OUT() {gpio_mode_set(PORT_AGS10_SDA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_AGS10_SDA);} //SDA输出模式
#define AGS10_SCL(BIT) gpio_bit_write( PORT_AGS10_SCL, GPIO_AGS10_SCL, BIT?SET:RESET)
#define AGS10_SDA(BIT) gpio_bit_write( PORT_AGS10_SDA, GPIO_AGS10_SDA, BIT?SET:RESET)
#define AGS10_GETSDA() gpio_input_bit_get( PORT_AGS10_SDA, GPIO_AGS10_SDA)
void ags10_gpio_init(void);
uint32_t ags10_read(void);
#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
2.57.6 移植验证
在 main.c 中编写以下代码。
/********************************************************************************
* 测试硬件:立创·梁山派开发板GD32F470ZGT6 使用主频200Mhz 晶振25Mhz
* 版 本 号: V1.0
* 修改作者: LC
* 修改日期: 2023年06月12日
* 功能介绍:
******************************************************************************
* 梁山派软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:club.szlcsc.com
* 其余模块移植手册:【立创·梁山派开发板】模块移植手册
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*********************************************************************************/
#include "gd32f4xx.h"
#include "systick.h"
#include "bsp_led.h"
#include "bsp_usart.h"
#include "stdio.h"
#include "string.h"
#include "bsp_ags10.h"
/******************************************************************
* 函 数 名 称:main
* 函 数 说 明:主函数
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者: LC
* 备 注:无
******************************************************************/
int main(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); // 优先级分组
//滴答定时器初始化 1us
systick_config();
//AGS10有害气体传感器初始化
ags10_gpio_init();
//串口0初始化(调试)
usart_gpio_config( 115200U );
printf("start\r\n");
while(1)
{
//显示TVOC浓度
printf("TVOC = %d ppb\r\n",ags10_read() );
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
代码工程文件