目录
一、介绍
二、传感器原理
1.原理图
2.引脚描述
3.工作原理介绍
三、程序设计
main.c文件
ccs811.h文件
ccs811.c文件
四、实验效果
五、资料获取
项目分享
一、介绍
CCS811模块是一种气体传感器,可以测量环境中TVOC(总挥发性有机物质)浓度和eCO2(二氧化碳)浓度,作为衡量空气质量(IAQ)的指标。它内部还集成了MCU,使得这款传感器具有了板载处理能力,在无需主机干预的情况下,即可提供等效二氧化碳等级或总挥发性有机化合物(TVOc)指标。
以下是二氧化碳传感器的参数:
型号 | CCS811 |
工作电压 | 1.8~3.6V(推荐3.3V) |
工作电流 | 20mA |
工作温度 | -5~50℃ |
存储温度 | -40~125℃ |
检测范围 | 400~5000ppm |
通信接口 | IIC |
哔哩哔哩视频:
CCS811二氧化碳传感器详解(STM32)
(资料分享见文末)
二、传感器原理
1.原理图
这里需要说明一下,SDA和SCL大家都比较熟悉,但是INT和WAK就少见了,在这款模块中,INT相当于复位引脚,CCS811芯片内部集成的单片机程序跑飞之后可以将该引脚接地复位,同时需要WAK为低电平的时候SDA、SCL才能正常通信
2.引脚描述
引脚名称 | 描述 |
VCC | 供给电压DC 3.3V |
GND | 地线 |
SCL | IIC时钟线 |
SDA | IIC数据线 |
WAK | 低电平使能 |
INT | 中断 |
RST | 复位 |
ADD | 地址选择位 |
3.工作原理介绍
CCS811-811是一种低功耗的数字气体传感器,集成了CCS801传感器和8位MCU(带模数转换器(ADC)),用来检测室内的空气质量,包括二氧化碳(Co2)和广泛的挥发性有机化合物气体(VOCs),产品的低功耗特性可用在环境监测设备上,灵敏度高,智能算法计算TVOC/eCO2数值并输出IIC信号可直接与单片机通信。模块的主要特点:检测室内空气质量的金属氧化物(MOX)传感器,集成了8位MCU用于运算第一级算法,集成了12位ADC用于传感器读数和数字化转换,IIC从属接口可直接接入主控系统复位/中断控制。
三、程序设计
1.使用STM32F103C8T6读取CCS811二氧化碳传感器采集的数据,通过串口发送至电脑
2.将读取得到信息数据同时在OLED上显示
CCS811_SCL | PB6 |
CCS811_SDA | PB7 |
CCS811_WAK | PB5 |
OLED_SCL | PB11 |
OLED_SDA | PB10 |
串口 | 串口1 |
main.c文件
#include "stm32f10x.h"
#include "led.h"
#include "usart.h"
#include "delay.h"
#include "oled.h"
#include "ccs811.h"/*****************辰哥单片机设计******************STM32* 项目 : CCS811二氧化碳传感器实验 * 版本 : V1.0* 日期 : 2024.8.28* MCU : STM32F103C8T6* 接口 : 参看ccs811.h * BILIBILI : 辰哥单片机设计* CSDN : 辰哥单片机设计* 作者 : 辰哥 **********************BEGIN***********************/float co2;
u8 buff[30];//参数显示缓存数组int main(void)
{ SystemInit();//配置系统时钟为72M delay_init(72);LED_Init();LED_On();CCS811_Init();USART1_Config();//串口初始化OLED_Init();printf("Start \n");delay_ms(1000);OLED_Clear();//显示“二氧化碳:”OLED_ShowChinese(0,0,0,16,1);OLED_ShowChinese(16,0,1,16,1);OLED_ShowChinese(32,0,2,16,1);OLED_ShowChinese(48,0,3,16,1);OLED_ShowChar(64,0,':',16,1);while (1){LED_Toggle();co2 = CCS811_GetData();OLED_ShowNum(40,20,co2,4,16,1);OLED_ShowString(80,20,"ppm",16,1);delay_ms(50); //延时50ms}
}
ccs811.h文件
#ifndef __CCS811_H
#define __CCS811_H
#include "stm32f10x.h"
#include "delay.h"
#include "sys.h"/*****************辰哥单片机设计******************STM32* 文件 : CCS811二氧化碳传感器h文件 * 版本 : V1.0* 日期 : 2024.8.28* MCU : STM32F103C8T6* 接口 : 见代码 * BILIBILI : 辰哥单片机设计* CSDN : 辰哥单片机设计* 作者 : 辰哥**********************BEGIN***********************//***************根据自己需求更改****************/
// CCS811 GPIO宏定义#define CCS811_IIC_CLK RCC_APB2Periph_GPIOB
#define CCS811_IIC_PORT GPIOB
#define CCS811_IIC_SDA_PIN GPIO_Pin_7
#define CCS811_IIC_SCL_PIN GPIO_Pin_6
#define CCS811_WAK_PORT GPIOB
#define CCS811_WAK_PIN GPIO_Pin_5//IO操作函数
#define CCS811_IIC_SCL PBout(6) //SCL
#define CCS811_IIC_SDA PBout(7) //SDA
#define CCS811_READ_SDA PBin(7) //输入SDA /*********************END**********************///CCS811
#define CCS811_Add 0x5A<<1
#define STATUS_REG 0x00 //状态寄存器
#define MEAS_MODE_REG 0x01 //测量模式和条件寄存器
#define ALG_RESULT_DATA 0x02 //算法结果。最高有效 2 个字节包含等效 CO2 (eCO2) 水平的 ppm 估计值,最低有效 2 个字节包含总 VOC 水平的 ppb 估计值
#define ENV_DATA 0x05
#define NTC_REG 0x06
#define THRESHOLDS 0x10
#define BASELINE 0x11
#define HW_ID_REG 0x20 //硬件 ID 值为 0x81
#define ERROR_ID_REG 0xE0 //错误 ID。当状态寄存器报告错误时,它的源位于此寄存器中
#define APP_START_REG 0xF4
#define SW_RESET 0xFF
#define CCS_811_ADDRESS 0x5A
#define GPIO_WAKE 0x5
#define DRIVE_MODE_IDLE 0x0
#define DRIVE_MODE_1SEC 0x10
#define DRIVE_MODE_10SEC 0x20
#define DRIVE_MODE_60SEC 0x30
#define INTERRUPT_DRIVEN 0x8
#define THRESHOLDS_ENABLED 0x4void CCS811_Init(void);
void ON_CCS811(void);
void CCS811_EN(void);
void OFF_CCS811(void);u16 CCS811_GetData(void);u8 CCS811_Single_WriteI2C_byte(u8 Slave_Address,u8 REG_Address,u8 data);
u8 CCS811_Single_MWriteI2C_byte(u8 Slave_Address,u8 REG_Address,u8 const *data,u8 length);
u8 CCS811_Single_ReadI2C(u8 Slave_Address,u8 REG_Address,u8 *REG_data,u8 length);#endif
ccs811.c文件
#include "ccs811.h"/*****************辰哥单片机设计******************STM32* 文件 : CCS811二氧化碳传感器c文件 * 版本 : V1.0* 日期 : 2024.8.28* MCU : STM32F103C8T6* 接口 : 见代码 * BILIBILI : 辰哥单片机设计* CSDN : 辰哥单片机设计* 作者 : 辰哥**********************BEGIN***********************/
u8 MeasureMode,Status,Error_ID;
u8 Information[10];
u8 BUF[12];typedef struct {
u16 eco2;
u16 tvoc;
u8 status;
u8 error_id;
u16 raw_data;
} ccs811_measurement_t;
ccs811_measurement_t CCS;u16 car_num;void CCS811_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(CCS811_IIC_CLK,ENABLE);//先使能外设IO PORTB时钟 GPIO_InitStructure.GPIO_Pin = CCS811_IIC_SDA_PIN|CCS811_IIC_SCL_PIN; // 端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHzGPIO_Init(CCS811_IIC_PORT, &GPIO_InitStructure); //根据设定参数初始化GPIO CCS811_IIC_SCL = 1;CCS811_IIC_SDA = 1;CCS811_EN();CCS811_Single_ReadI2C(CCS811_Add,0x00,&Status,1);CCS811_Single_ReadI2C(CCS811_Add,0xE0,&Error_ID,1);CCS811_Single_ReadI2C(CCS811_Add,0x02,BUF,8);CCS811_Single_ReadI2C(CCS811_Add,0x20,Information,1); //Read CCS's information ,ID
}//CCS811引脚输出模式控制
void CCS811_IIC_SDA_OUT(void)//SDA输出方向配置
{GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin=CCS811_IIC_SDA_PIN;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//SDA推挽输出GPIO_Init(CCS811_IIC_PORT,&GPIO_InitStructure); }void CCS811_IIC_SDA_IN(void)//SDA输入方向配置
{GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin=CCS811_IIC_SDA_PIN;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//SCL上拉输入GPIO_Init(CCS811_IIC_PORT,&GPIO_InitStructure);}u16 CCS811_GetData(void)
{int car;//CCS811 CO2数据采集ON_CCS811(); //nWAKE pin is asserted at least 50μs before the transaction and kept asserted throughout,nWAKE pin is active low CCS811_Single_ReadI2C(CCS811_Add,0x00,&Status,1);CCS811_Single_ReadI2C(CCS811_Add,0xE0,&Error_ID,1);CCS811_Single_ReadI2C(CCS811_Add,0x02,BUF,8);CCS811_Single_ReadI2C(CCS811_Add,0x20,Information,1); //Read CCS's information ,IDOFF_CCS811(); CCS.eco2= (u16)BUF[0]*256+BUF[1];CCS.tvoc= (u16)BUF[2]*256+BUF[3];Information[0]=0;car=(float)CCS.eco2; //二氧化碳return car;
}IIC起始函数//
/*
IIC起始:当SCL处于高电平期间,SDA由高电平变成低电平出现一个下降沿,然后SCL拉低
*/
u8 CCS811_IIC_Start(void)
{CCS811_IIC_SDA_OUT();CCS811_IIC_SDA = 1; delay_us(5); //延时保证时钟频率低于40K,以便从机识别CCS811_IIC_SCL = 1;delay_us(5);//延时保证时钟频率低于40K,以便从机识别//if(!CCS811_READ_SDA) return 0;//SDA线为低电平则总线忙,退出CCS811_IIC_SDA = 0; //SCL处于高电平的时候,SDA拉低delay_us(5);//if(CCS811_READ_SDA) return 0;//SDA线为高电平则总线出错,退出CCS811_IIC_SCL = 0;delay_us(5);return 1;
}
//**************************************
//IIC停止信号
/*
IIC停止:当SCL处于高电平期间,SDA由低电平变成高电平出现一个上升沿
*/
//**************************************
void CCS811_IIC_Stop(void)
{CCS811_IIC_SDA_OUT();CCS811_IIC_SDA = 0;CCS811_IIC_SCL = 0;delay_us(5);CCS811_IIC_SCL = 1;delay_us(5);CCS811_IIC_SDA = 1;//当SCL处于高电平期间,SDA由低电平变成高电平 //延时
}
//**************************************
//IIC发送应答信号
//入口参数:ack (0:ACK 1:NAK)
/*
应答:当从机接收到数据后,向主机发送一个低电平信号
先准备好SDA电平状态,在SCL高电平时,主机采样SDA
*/
//**************************************
void CCS811_IIC_SendACK(u8 i)
{CCS811_IIC_SDA_OUT();if(1==i)CCS811_IIC_SDA = 1; //准备好SDA电平状态,不应答else CCS811_IIC_SDA = 0; //准备好SDA电平状态,应答 CCS811_IIC_SCL = 1; //拉高时钟线delay_us(5); //延时CCS811_IIC_SCL = 0 ; //拉低时钟线delay_us(5);
}
///等待从机应答
/*
当本机(主机)发送了一个数据后,等待从机应答
先释放SDA,让从机使用,然后采集SDA状态
*/
/
u8 CCS811_IIC_WaitAck(void) //返回为:=1有ACK,=0无ACK
{ uint16_t i=0;CCS811_IIC_SDA_IN();CCS811_IIC_SDA = 1;delay_us(1); //释放SDACCS811_IIC_SCL = 1;delay_us(1); //SCL拉高进行采样while(CCS811_READ_SDA)//等待SDA拉低{i++; //等待计数if(i==500)//超时跳出循环break;}if(CCS811_READ_SDA)//再次判断SDA是否拉低{CCS811_IIC_SCL = 0; return RESET;//从机应答失败,返回0}delay_us(5);//延时保证时钟频率低于40K,CCS811_IIC_SCL = 0;delay_us(5); //延时保证时钟频率低于40K,CCS811_IIC_SDA_OUT();return SET;//从机应答成功,返回1
}
//**************************************
//向IIC总线发送一个字节数据
/*
一个字节8bit,当SCL低电平时,准备好SDA,SCL高电平时,从机采样SDA
*/
//**************************************
void CCS811_IIC_SendByte(u8 dat)
{ u8 i;CCS811_IIC_SDA_OUT();CCS811_IIC_SCL = 0;//SCL拉低,给SDA准备for (i=0; i<8; i++) //8位计数器{if(dat&0x80)//SDA准备CCS811_IIC_SDA = 1; else CCS811_IIC_SDA = 0;CCS811_IIC_SCL = 1; //拉高时钟,给从机采样delay_us(5); //延时保持IIC时钟频率,也是给从机采样有充足时间CCS811_IIC_SCL = 0; //拉低时钟,给SDA准备delay_us(5); //延时保持IIC时钟频率dat <<= 1; //移出数据的最高位 }delay_us(10);
}
//**************************************
//从IIC总线接收一个字节数据
//**************************************
u8 CCS811_IIC_RecvByte()
{u8 i;u8 dat = 0;CCS811_IIC_SDA_IN();CCS811_IIC_SDA = 1;//释放SDA,给从机使用delay_us(5); //延时给从机准备SDA时间 for (i=0; i<8; i++) //8位计数器{ dat <<= 1;CCS811_IIC_SCL = 1; //拉高时钟线,采样从机SDAif(CCS811_READ_SDA) //读数据 dat |=0x01; delay_us(5); //延时保持IIC时钟频率 CCS811_IIC_SCL = 0; //拉低时钟线,处理接收到的数据delay_us(5); //延时给从机准备SDA时间} return dat;
}
//**************************************
//向IIC设备写入一个字节数据
//**************************************
u8 CCS811_Single_WriteI2C_byte(u8 Slave_Address,u8 REG_Address,u8 data)
{if(CCS811_IIC_Start()==0) //起始信号{CCS811_IIC_Stop(); return RESET;} CCS811_IIC_SendByte(Slave_Address); //发送设备地址+写信号if(!CCS811_IIC_WaitAck()){CCS811_IIC_Stop(); return RESET;}CCS811_IIC_SendByte(REG_Address); //内部寄存器地址,if(!CCS811_IIC_WaitAck()){CCS811_IIC_Stop(); return RESET;}CCS811_IIC_SendByte(data); //内部寄存器数据,if(!CCS811_IIC_WaitAck()){CCS811_IIC_Stop(); return RESET;}CCS811_IIC_Stop(); //发送停止信号return SET;
}u8 CCS811_Single_MWriteI2C_byte(u8 Slave_Address,u8 REG_Address,u8 const *data,u8 length)
{if(CCS811_IIC_Start()==0) //起始信号{CCS811_IIC_Stop();return RESET;} CCS811_IIC_SendByte(Slave_Address); //发送设备地址+写信号if(!CCS811_IIC_WaitAck()){CCS811_IIC_Stop();return RESET;}CCS811_IIC_SendByte(REG_Address); //内部寄存器地址,if(!CCS811_IIC_WaitAck()){CCS811_IIC_Stop();return RESET;}while(length){CCS811_IIC_SendByte(*data++); //内部寄存器数据,if(!CCS811_IIC_WaitAck()){CCS811_IIC_Stop(); return RESET;} //应答length--;}// CCS811_IIC_SendByte(*data); //内部寄存器数据,// if(!CCS811_IIC_WaitAck()){CCS811_IIC_Stop(); return RESET;}CCS811_IIC_Stop(); //发送停止信号 return SET;
}//**************************************
//从IIC设备读取一个字节数据
//**************************************
u8 CCS811_Single_ReadI2C(u8 Slave_Address,u8 REG_Address,u8 *REG_data,u8 length)
{if(CCS811_IIC_Start()==0) //起始信号{CCS811_IIC_Stop();return RESET;} CCS811_IIC_SendByte(Slave_Address); //发送设备地址+写信号if(!CCS811_IIC_WaitAck()){CCS811_IIC_Stop() ;return RESET;} CCS811_IIC_SendByte(REG_Address); //发送存储单元地址if(!CCS811_IIC_WaitAck()){CCS811_IIC_Stop();return RESET;} if(CCS811_IIC_Start()==0) //起始信号{CCS811_IIC_Stop(); return RESET;} CCS811_IIC_SendByte(Slave_Address+1); //发送设备地址+读信号if(!CCS811_IIC_WaitAck()){CCS811_IIC_Stop(); return RESET;}while(length-1){*REG_data++=CCS811_IIC_RecvByte(); //读出寄存器数据CCS811_IIC_SendACK(0); //应答length--;}*REG_data=CCS811_IIC_RecvByte(); CCS811_IIC_SendACK(1); //发送停止传输信号CCS811_IIC_Stop(); //停止信号return SET;
}void CCS811_EN()
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//先使能外设IO PORTB时钟 GPIO_InitStructure.GPIO_Pin = CCS811_WAK_PIN; // 端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHzGPIO_Init(CCS811_WAK_PORT, &GPIO_InitStructure); //根据设定参数初始化GPIO GPIO_ResetBits(CCS811_WAK_PORT,CCS811_WAK_PIN);
}void ON_CCS811()
{GPIO_ResetBits(CCS811_WAK_PORT,CCS811_WAK_PIN);
}
void OFF_CCS811()
{GPIO_SetBits(CCS811_WAK_PORT,CCS811_WAK_PIN);
}