STM32F103C8T6二氧化碳传感器JW01-CO2的使用方法和代码驱动
UART 初始化:配置 STM32 的 USART1(PA9=TX,PA10=RX),波特率 9600、8 位数据位、1 位停止位、无校验(匹配 JW01 默认参数),使能接收中断(避免轮询占用 CPU 资源);浓度计算:CO₂浓度值由 D1(高 8 位)和 D2(低 8 位)组成,公式为CO₂浓度(ppm) = (D1 << 8) | D2(例如 D1=0x03、D2=0xE8 时,浓度 = 1
1. 传感器用途
检测原理
采用非分散红外(NDIR)技术,利用 CO₂分子对特定波长(4.26 微米)红外光的选择性吸收特性实现测量。传感器内部红外光源发射光束,经过气室时部分光被 CO₂吸收,探测器通过检测光强变化计算 CO₂浓度。
参数与性能
测量范围:350~2000ppm(适用于室内空气质量监测场景);
输出方式:UART 串口通信,波特率 9600,数据格式为 6 字节(基础版)或 9 字节(三合一版本,可同步检测甲醛、TVOC);
工作电压:5V,预热时间约 60 秒,响应速度达 1 次 / 秒;
精度与稳定性:通过校验和算法(前 5 字节之和等于第 6 字节)确保数据准确性,支持温度补偿功能,可消除环境干扰。
典型应用场景
智能家居与环境监测:实时监测家庭、办公室、教室等室内 CO₂浓度,联动新风系统自动换气(如浓度超过 1000ppm 时触发通风),改善空气质量;
农业与温室控制:在大棚种植中,通过监测 CO₂浓度优化植物光合作用效率(如浓度低于 800ppm 时自动释放 CO₂气肥,提升作物产量);
工业过程监控:用于食品包装(控制气调保鲜环境)、酿酒发酵(监测 CO₂生成量)等场景,保障生产过程稳定性;
物联网与嵌入式系统:作为物联网节点的传感器单元,将数据上传至云端(如阿里云、OneNET),实现远程监控与数据分析(例如在智能宠物托运装置中集成 JW01,实时推送宠物舱内 CO₂浓度)。
2. 传感器介绍
JW01-CO₂-V2.2 是由佰培电子推出的模块化传感器,专为低成本、高精度室内 / 场景化 CO₂监测设计,核心优势在于 “NDIR 技术小型化 + 串口直读”,无需复杂外围电路即可实现 CO₂浓度采集,广泛适配单片机、物联网模块等嵌入式系统。
核心特性
小型化设计:尺寸约 25mm×30mm×10mm,兼容面包板 / PCB 焊接,适合空间受限设备(如智能手环、小型空气监测仪);
即插即用:仅需 5V 电源 + 2 根 UART 通信线即可工作,无需额外校准电路(出厂预校准,支持后期指令校准);
多版本可选:
基础版:仅监测 CO₂(350~2000ppm);
三合一版:额外集成甲醛(0~1.5mg/m³)、TVOC(0~6mg/m³)检测,数据格式扩展为 9 字节,满足多参数空气质量监测需求;
抗干扰能力:内置温度补偿电路,可抵消 - 10℃~60℃环境温度对检测精度的影响(误差≤±5% FS);气室采用防尘滤网,减少灰尘对红外探测的干扰。
3.单片机连接硬件图

实物图



4. 驱动思路
JW01 的驱动核心为 “UART 数据接收 + 校验和解析”,需按 “初始化→数据接收→校验→解析→应用” 流程设计,具体如下:
步骤 1:硬件初始化
UART 初始化:配置 STM32 的 USART1(PA9=TX,PA10=RX),波特率 9600、8 位数据位、1 位停止位、无校验(匹配 JW01 默认参数),使能接收中断(避免轮询占用 CPU 资源);
GPIO 初始化:配置 OLED 的 I2C 引脚(PB6=SCL,PB7=SDA)为推挽输出;
步骤 2:数据接收与缓存
开启 UART 接收中断,将接收到的字节存入环形缓冲区;
步骤 3:数据校验与解析
校验和验证:基础版 6 字节数据格式为[0x2C, D1, D2, D3, D4, SUM],其中SUM = (0x2C + D1 + D2 + D3 + D4) % 256;若计算的 SUM 与接收的第 6 字节不一致,则丢弃该帧数据;
浓度计算:CO₂浓度值由 D1(高 8 位)和 D2(低 8 位)组成,公式为CO₂浓度(ppm) = (D1 << 8) | D2(例如 D1=0x03、D2=0xE8 时,浓度 = 1000ppm)。
步骤 4:数据应用与联动
显示输出:将解析后的 CO₂浓度值通过OLED显示,格式为 “CO2 Value: 1050 ppm”;
串口一打印以下数据:
接收数据(Hex): 2C 01 66 03 FF 95
模块地址: 0x2C
计算校验和: 0x95
接收校验和: 0x95
CO2 数值: 358 ppm
5. 单片机程序代码
main.c
#include "stm32f10x.h"
#include "string.h"
#include "stdio.h"
#include "delay.h"
#include "bsp_usart.h"
#include "oled.h"
uint8_t parseData(uint8_t data[], uint8_t length)
{
// 检查数据长度是否符合要求
if(length < 6) {
printf("数据长度不足!\n");
return 0;
}
printf("接收数据(Hex): ");
for(int i = 0; i < length; i++) {
printf("%02X ", data[i]);
}
printf("\n");
// 模块地址
uint8_t moduleAddress = data[0];
// CO2浓度高字节
uint8_t co2High = data[1];
// CO2浓度低字节
uint8_t co2Low = data[2];
// 满量程高字节
uint8_t fullScaleHigh = data[3];
// 满量程低字节
uint8_t fullScaleLow = data[4];
// 校验和
uint8_t checksum = data[5];
// 计算校验和
uint8_t calculatedChecksum = 0;
for(int i = 0; i < 5; i++) {
calculatedChecksum += data[i];
}
// 输出模块地址
printf("模块地址: 0x%02X\n", moduleAddress);
// 输出计算得到的校验和
printf("计算校验和: 0x%02X\n", calculatedChecksum);
// 输出接收到的校验和
printf("接收校验和: 0x%02X\n", checksum);
// 校验和验证
if (calculatedChecksum == checksum) {
// 计算并输出CO2浓度值
uint16_t co2Value = (uint16_t)(co2High * 256 + co2Low);
printf("CO2 数值: %d ppm\n", co2Value);
// 在OLED上显示CO2浓度
OLED_Clear();
OLED_ShowString(0, 0, "CO2 Sensor");
OLED_ShowString(0, 2, "CO2 Value:");
char co2Str[16];
sprintf(co2Str, "%d ppm", co2Value);
OLED_ShowString(0, 4, co2Str);
return 1; // 返回成功标志
} else {
printf("校验和不匹配,数据可能有误!\n");
OLED_Clear();
OLED_ShowString(0, 0, "CO2 Sensor");
OLED_ShowString(0, 2, "Checksum Error!");
return 0; // 返回失败标志
}
}
int main(void)
{
NVIC_PriorityGroupConfig (NVIC_PriorityGroup_2);
SysTick_Init(72); //系统时钟初始化
usart1_init(115200);//串口1初始化
printf("USART1 OK!\r\n");
usart2_init(9600);//串口1初始化
usart3_init(115200);//串口3初始化
OLED_Init();
while(1)
{
if(buf_uart2.rx_flag==1)
{
delay_ms(10);
parseData((uint8_t *)buf_uart2.buf, buf_uart2.index);
Clear_Buffer_UART2();
}
}
}
Uart.c
#include "stm32f10x.h"
#include "bsp_usart.h"
#include "stdio.h"
#include "string.h"
#include "stm32f10x_tim.h"
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
int _sys_exit(int x)
{
x = x;
return 0;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
#if EN_USART1
UART_BUF buf_uart1; //CH340
//初始化IO 串口1
//bound:波特率
void usart1_init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
USART_DeInit(USART1); //复位串口1
//USART1_TX PA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9
//USART1_RX PA.10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA10
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//一般设置为115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口
USART_Cmd(USART1, ENABLE); //使能串口
#if EN_USART1_RX
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启相关中断
USART_ClearFlag(USART1, USART_FLAG_TC);
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
#endif
}
/*********************************串口1的服务函数*************************************************/
void USART1_Send_byte(char data)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, data);
}
/*-------------------------------------------------*/
/*函数名:串口1 发送数组 */
/*参 数:bound:波特率 */
/*返回值:无 */
/*-------------------------------------------------*/
void USART1_Send(char *Data,uint16_t Len)
{
uint16_t i;
for(i=0; i<Len; i++)
{
USART1_Send_byte(Data[i]);
}
}
void USART1_SendStr(char*SendBuf)//串口1打印数据
{
while(*SendBuf)
{
while((USART1->SR&0X40)==0);//等待发送完成
USART1->DR = (u8) *SendBuf;
SendBuf++;
}
}
/*****************************************************
清空电脑反馈的缓冲数据 串口1
*****************************************************/
void Clear_Buffer_UART1(void)//清空缓存
{
buf_uart1.index=0;
buf_uart1.rx_flag=0;
memset(buf_uart1.buf,0,BUFLEN);
}
void UART1_receive_process_event(char ch ) //串口2给4g用
{
if(buf_uart1.index >= BUFLEN)
{
buf_uart1.index = 0 ;
}
else
{
buf_uart1.buf[buf_uart1.index++] = ch;
}
}
//串口1的接收中断程序
void USART1_IRQHandler(void) //串口1中断服务程序
{
uint8_t Res;
Res=Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断,可以扩展来控制
{
Res=USART_ReceiveData(USART1);//接收模块的数据;
UART1_receive_process_event(Res);//接收模块的数据
}
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //模块空闲
{
Res=USART_ReceiveData(USART1);//接收模块的数据;
buf_uart1.rx_flag=1;
}
}
#endif
#if EN_USART2
UART_BUF buf_uart2; //EC200T
//初始化IO 串口2
//pclk1:PCLK1时钟频率(Mhz)
//bound:波特率
void usart2_init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能,GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//USART2
USART_DeInit(USART2); //复位串口2
//USART2_TX PA.2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.2
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA2
//USART2_RX PA.3
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA3
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//115200
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART2, &USART_InitStructure); //初始化串口
USART_Cmd(USART2, ENABLE); //使能串口
#if EN_USART2_RX
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启相关中断
USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//开启相关中断
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;//串口1中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =1; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
#endif
}
void Clear_Buffer_UART2(void)//清空缓存
{
buf_uart2.index=0;
buf_uart2.rx_flag=0;
memset(buf_uart2.buf,0,BUFLEN);
}
/*********************************串口2的服务函数*************************************************/
void USART2_Send_byte(char data)
{
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, data);
}
/*-------------------------------------------------*/
/*函数名:串口2 发送数组 */
/*参 数:bound:波特率 */
/*返回值:无 */
/*-------------------------------------------------*/
void USART2_Send(char *Data,uint16_t Len)
{
uint16_t i;
for(i=0; i<Len; i++)
{
USART2_Send_byte(Data[i]);
}
}
void USART2_SendStr(char*SendBuf)//串口1打印数据
{
while(*SendBuf)
{
while((USART2->SR&0X40)==0);//等待发送完成
USART2->DR = (u8) *SendBuf;
SendBuf++;
}
}
void usart2_receive_process_event(unsigned char ch ) //串口2给4g用
{
if(buf_uart2.index >= BUFLEN)
{
buf_uart2.index = 0 ;
}
else
{
buf_uart2.buf[buf_uart2.index++] = ch;
}
}
void USART2_IRQHandler(void) //串口2接收函数
{
char Res;
Res=Res;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断,可以扩展来控制
{
Res=USART_ReceiveData(USART2);//接收模块的数据;
usart2_receive_process_event(Res);//接收模块的数据
}
if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) //模块空闲
{
Res=USART_ReceiveData(USART2);//接收模块的数据;
buf_uart2.rx_flag=1;
}
}
#endif
#if EN_USART3
UART_BUF buf_uart3; //TTL
void usart3_init(u32 bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能,GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);//USART3
USART_DeInit(USART3); //复位串口3
//USART3_TX PB10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB10
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PA2
//USART3_RX PB11
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB11
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//115200
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART3, &USART_InitStructure); //初始化串口
USART_Cmd(USART3, ENABLE); //使能串口
#if EN_USART3_RX
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启相关中断
USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);//开启相关中断
USART_ClearFlag(USART3, USART_FLAG_TC);
//Usart3 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;//串口3中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
#endif
}
void USART3_Send_byte(char data)
{
while(USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET);
USART_SendData(USART3, data);
}
/*-------------------------------------------------*/
/*函数名:串口2 发送数组 */
/*参 数:bound:波特率 */
/*返回值:无 */
/*-------------------------------------------------*/
void USART3_Send(char *Data,uint16_t Len)
{
uint16_t i;
for(i=0; i<Len; i++)
{
USART3_Send_byte(Data[i]);
}
}
void USART3_SendStr(char*SendBuf)//串口3打印数据
{
while(*SendBuf)
{
while((USART3->SR&0X40)==0);//等待发送完成
USART3->DR = (u8) *SendBuf;
SendBuf++;
}
}
/*****************************************************
清空电脑反馈的缓冲数据 串口1
*****************************************************/
void Clear_Buffer_UART3(void)//清空缓存
{
buf_uart3.index=0;
buf_uart3.rx_flag=0;
memset(buf_uart3.buf,0,BUFLEN);
}
void USART3_receive_process_event(char ch ) //串口2给4g用
{
if(buf_uart3.index >= BUFLEN)
{
buf_uart3.index = 0 ;
}
else
{
buf_uart3.buf[buf_uart3.index++] = ch;
}
}
void USART3_IRQHandler(void) //串口3中断服务程序
{
char Res;
Res=Res;
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) //接收中断,可以扩展来控制
{
Res=USART_ReceiveData(USART3);//接收模块的数据;
USART3_receive_process_event(Res);
}
if(USART_GetITStatus(USART3, USART_IT_IDLE) != RESET) //模块空闲
{
Res=USART_ReceiveData(USART3);//接收模块的数据;
buf_uart3.rx_flag=1;
}
}
#endif
6.实现效果图片



代码下载链接
更多推荐



所有评论(0)