qzc0927 发表于 2023-12-20 16:30

【DFRobot 云雀气象仪】学习笔记06(使用SGP30传感器获取CO2浓度)

## 10:使用IIC接口来实现CO2检测

SGP30是一款二氧化碳(CO2)和有机化合物(TVOC)气体传感器模块,它可以通过检测环境中的CO2和TVOC浓度来实现空气质量监测和控制。

### 10.1:硬件设计

模块使用IIC接口,硬件电路设计如下:

!(https://boreyun.oss-cn-shanghai.aliyuncs.com/image-20231220161215349.png)

硬件电路接口如下:

模块的SDA引脚连接到微控制器的PB11引脚。

\- 模块的SCL引脚连接到微控制器的PB10引脚。

\- 模块的VIN引脚连接到微控制器的5V电源引脚。

\- 模块的GND引脚连接到微控制器的地线引脚。

硬件实物图:

!(https://boreyun.oss-cn-shanghai.aliyuncs.com/image-20231220161353736.png)

### 10.2:软件设计

首先配置引脚定义,引脚配置需要和硬件接口一致。

```c
#ifndef __SGP30_H
#define __SGP30_H
#include "SYSTEM/system.h"

#defineSGP30_SCL   PBout(10)
#defineSGP30_SDA   PBout(11)

// USART GPIO 引脚宏定义
#defineSGP30_SCL_GPIO_CLK      RCC_APB2Periph_GPIOB
#defineSGP30_SCL_GPIO_PORT       GPIOB
#defineSGP30_SCL_GPIO_PIN      GPIO_Pin_10

#defineSGP30_SDA_GPIO_SDA      RCC_APB2Periph_GPIOB
#defineSGP30_SDA_GPIO_PORT       GPIOB
#defineSGP30_SDA_GPIO_PIN      GPIO_Pin_11

#defineSGP30_SDA_READ()         GPIO_ReadInputDataBit(SGP30_SDA_GPIO_PORT, SGP30_SDA_GPIO_PIN)

#define SGP30_read0xb1//SGP30的读地址
#define SGP30_write 0xb0//SGP30的写地址


void SGP30_IIC_Start(void);                                //发送IIC开始信号
void SGP30_IIC_Stop(void);                                  //发送IIC停止信号
void SGP30_IIC_Send_Byte(u8 txd);                        //IIC发送一个字节
u16 SGP30_IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 SGP30_IIC_Wait_Ack(void);                                 //IIC等待ACK信号
void SGP30_IIC_Ack(void);                                        //IIC发送ACK信号
void SGP30_IIC_NAck(void);                                //IIC不发送ACK信号
void SGP30_IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 SGP30_IIC_Read_One_Byte(u8 daddr,u8 addr);       
void SGP30_Init(void);                                  
void SGP30_Write(u8 a, u8 b);
u32 SGP30_Read(void);


#endif
```

驱动采用模拟IIC接口,具体实现代码如下:

```c
#include "SGP30/sgp30.h"
#include "SYSTICK/SysTick.h"



void SGP30_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(SGP30_SCL_GPIO_CLK | SGP30_SDA_GPIO_SDA, ENABLE);

GPIO_InitStructure.GPIO_Pin = SGP30_SCL_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SGP30_SCL_GPIO_PORT, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = SGP30_SDA_GPIO_PIN;
GPIO_Init(SGP30_SDA_GPIO_PORT, &GPIO_InitStructure);
}


void SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = SGP30_SDA_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SGP30_SDA_GPIO_PORT, &GPIO_InitStructure);
}

void SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = SGP30_SDA_GPIO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(SGP30_SDA_GPIO_PORT, &GPIO_InitStructure);
}

//产生IIC起始信号
void SGP30_IIC_Start(void)
{
SDA_OUT();
SGP30_SDA = 1;
SGP30_SCL = 1;
delay_us(20);

SGP30_SDA = 0;        //START:when CLK is high,DATA change form high to low
delay_us(20);
SGP30_SCL = 0;         //钳住I2C总线,准备发送或接收数据
}

//产生IIC停止信号
void SGP30_IIC_Stop(void)
{
SDA_OUT();
SGP30_SCL = 0;
SGP30_SDA = 0;        //STOP:when CLK is high DATA change form low to high
delay_us(20);
SGP30_SCL = 1;
SGP30_SDA = 1;        //发送I2C总线结束信号
delay_us(20);
}

//等待应答信号到来
//返回值:1,接收应答失败
//      0,接收应答成功
u8 SGP30_IIC_Wait_Ack(void)
{
u8 ucErrTime = 0;
SDA_IN();
SGP30_SDA = 1;
delay_us(10);
SGP30_SCL = 1;
delay_us(10);
while(SGP30_SDA_READ())
{
    ucErrTime++;
    if(ucErrTime > 250)
    {
      SGP30_IIC_Stop();
      return 1;
    }
}
SGP30_SCL = 0;                //时钟输出0
return 0;
}

//产生ACK应答
void SGP30_IIC_Ack(void)
{
SGP30_SCL = 0;
SDA_OUT();
SGP30_SDA = 0;
delay_us(20);
SGP30_SCL = 1;
delay_us(20);
SGP30_SCL = 0;
}

//不产生ACK应答
void SGP30_IIC_NAck(void)
{
SGP30_SCL = 0;
SDA_OUT();
SGP30_SDA = 1;
delay_us(20);
SGP30_SCL = 1;
delay_us(20);
SGP30_SCL = 0;
}

//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void SGP30_IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
SGP30_SCL = 0;                   //拉低时钟开始数据传输
for(t = 0; t < 8; t++)
{
    if((txd & 0x80) >> 7)
      SGP30_SDA = 1;
    else
      SGP30_SDA = 0;
    txd <<= 1;
    delay_us(20);
    SGP30_SCL = 1;
    delay_us(20);
    SGP30_SCL = 0;
    delay_us(20);
}
delay_us(20);

}

//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u16 SGP30_IIC_Read_Byte(u8 ack)
{
u8 i;
u16 receive = 0;
SDA_IN();
for(i = 0; i < 8; i++ )
{
    SGP30_SCL = 0;
    delay_us(20);
    SGP30_SCL = 1;
    receive <<= 1;
    if(SGP30_SDA_READ())
      receive++;
    delay_us(20);
}
if (!ack)
    SGP30_IIC_NAck();//发送nACK
else
    SGP30_IIC_Ack(); //发送ACK
return receive;
}


//初始化IIC接口
void SGP30_Init(void)
{
SGP30_GPIO_Init();
SGP30_Write(0x20, 0x03);
//        SGP30_ad_write(0x20,0x61);
//        SGP30_ad_write(0x01,0x00);
}


void SGP30_Write(u8 a, u8 b)
{
SGP30_IIC_Start();
SGP30_IIC_Send_Byte(SGP30_write); //发送器件地址+写指令
SGP30_IIC_Wait_Ack();
SGP30_IIC_Send_Byte(a);                //发送控制字节
SGP30_IIC_Wait_Ack();
SGP30_IIC_Send_Byte(b);
SGP30_IIC_Wait_Ack();
SGP30_IIC_Stop();
delay_ms(100);
}

u32 SGP30_Read(void)
{
u32 dat;
u8 crc;
SGP30_IIC_Start();
SGP30_IIC_Send_Byte(SGP30_read); //发送器件地址+读指令
SGP30_IIC_Wait_Ack();
dat = SGP30_IIC_Read_Byte(1);
dat <<= 8;
dat += SGP30_IIC_Read_Byte(1);
crc = SGP30_IIC_Read_Byte(1); //crc数据,舍去
crc = crc;//为了不让出现编译警告
dat <<= 8;
dat += SGP30_IIC_Read_Byte(1);
dat <<= 8;
dat += SGP30_IIC_Read_Byte(0);
SGP30_IIC_Stop();
return(dat);
}

```

模块工作的时候,需要先进行自检,确认精度,初始化代码如下:

```C
        //SGP30模块开机需要一定时间初始化,在初始化阶段读取的CO2浓度为400ppm,TVOC为0ppd且恒定不变,因此上电后每隔一段时间读取一次
        //SGP30模块的值,如果CO2浓度为400ppm,TVOC为0ppd,发送“正在检测中...”,直到SGP30模块初始化完成。
        //初始化完成后刚开始读出数据会波动比较大,属于正常现象,一段时间后会逐渐趋于稳定。
        //气体类传感器比较容易受环境影响,测量数据出现波动是正常的,可自行添加滤波函数。
        while(CO2Data == 400 && TVOCData == 0)
        {
                SGP30_Write(0x20,0x08);
                sgp30_dat = SGP30_Read();//读取SGP30的值
                CO2Data = (sgp30_dat & 0xffff0000) >> 16;//取出CO2浓度值
                TVOCData = sgp30_dat & 0x0000ffff;                       //取出TVOC值
                printf("正在检测中...\r\n");
                delay_ms(500);
        }
```

### 10.3:实验结果

最后,将结果显示到LCD屏幕上,结果如下:

!(https://boreyun.oss-cn-shanghai.aliyuncs.com/image-20231220162409650.png)

chejm 发表于 2023-12-20 17:11

<p>好的技术贴需要大力支持,感谢楼主的无私分享,收藏起来,慢慢学习</p>

qzc0927 发表于 2023-12-20 18:04

chejm 发表于 2023-12-20 17:11
好的技术贴需要大力支持,感谢楼主的无私分享,收藏起来,慢慢学习

<p>相互学习,共同进步<img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/congra.gif" width="48" /></p>

eew_TKwwQ7 发表于 2023-12-21 08:45

<p>Sensirion的传感器确实不错</p><br/>
页: [1]
查看完整版本: 【DFRobot 云雀气象仪】学习笔记06(使用SGP30传感器获取CO2浓度)