简介
KQM6600TAUs型空气质量检测模 块,使用MEMS VOC传感器件作为检测空气中有机化合物气体(VOC)的模块。UART通信数据输出,根据VOC数据计算和等效甲醛,CO2输出。其具有体积小,功耗低,灵敏度高,响应速度快等居多优点,广泛应用在空气质量检测及控制领域。
型号与引脚描述
KQM6600TAUs 空气质量模块实现了三种与用户主控 MCU 的数据传输方
式,为 UART 通信输出方式,UART(波特率 9600bps)。
分别对应各个型号引脚描述:
使用电路连接示意图
各型号对应应用连接示意图分别如下图所示:(F 引脚休眠和校准功能如果不用,可以空置)
数据输出格式
起始位:1 位
⚫ 数据位:8 位数据
⚫ 奇/偶校验:无
⚫ 停止位:1 位
⚫ 波特率:9600bps
BYTE1 地址码:0x5F
数据 1 高位\数据 1 低位:VOC 输出数据(16 进制),单位 0.1PPM
数据 2 高位\数据 2 低位:甲醛输出数据(16 进制),单位 0.01mg/m3
数据 3 高位\数据 3 低位:CO2 输出数据(16 进制),单位 1PPM
校验值: 前三字节的校验和的低位字节 byte1+byte2+byte3+....+byte7
代码
main.c
KQM6600.C
#include "kqm6600.h"
#include "stdio.h"
#include "string.h"
KQM Kqm; //定义KQM6600相关的变量
SENSOR sensor; //传感器参数
/*
串口2
PA2 TX 复用推挽输出
PA3 RX
*/
void KQM6600_Config(void)
{
//1.开GPIOA时钟
//void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState); stm3210x_rcc.h 693行
//void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//2.定义结构体
//void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct); stm32f10x_gpio.h 351行
GPIO_InitTypeDef GPIO_InitStruct={0};
//3.给结构体赋值 TX -- 串口2和KQM6600通信只需要接收数据,不需要发送 TX管脚可以不配置
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//工作模式
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2; //引脚
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //工作速度
//4.调用XXX_init函数,将参数写入到寄存器中
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//工作模式
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3; //引脚
GPIO_Init(GPIOA,&GPIO_InitStruct);
//5.开串口2的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
//6.定义结构体
//void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct); stm32f10x_usart.h 366行
USART_InitTypeDef USART_InitStruct={0};
//7.给结构体赋值 关于波特率 数据位 校验方式 停止位 要和KQM6600保持一致
USART_InitStruct.USART_BaudRate = 9600;//波特率 9600 115200
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控制
USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx; //发送接收使能
USART_InitStruct.USART_Parity = USART_Parity_No; //无校验
USART_InitStruct.USART_StopBits = USART_StopBits_1; //1个停止位
USART_InitStruct.USART_WordLength = USART_WordLength_8b; //8个数据位
//8.调用xxx_init函数将参数写入到寄存器中
USART_Init(USART2,&USART_InitStruct);
//9.使能串口功能 xxx_cmd
// void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState); stm32f10x_usart.h 370行
USART_Cmd(USART2,ENABLE);
//根据需求配置是否配置中断
//10.定义结构体
NVIC_InitTypeDef NVIC_InitStruct={0};
//11.给结构体赋值
NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn; //中断通道
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //使能对应的中断
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级 void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; //次级优先级 两个优先级的参数要和分组函数NVIC_PriorityGroupConfig保持一致
//12.调用XXX_Init函数将参数写入到寄存器中
NVIC_Init(&NVIC_InitStruct);
//13.开启对应的中断
//void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState); stm32f10x_usart.h 371行
USART_ITConfig(USART2,USART_IT_RXNE,ENABLE); //开启接收中断
USART_ITConfig(USART2,USART_IT_IDLE,ENABLE); //开启空闲中断
}
/*
中断服务函数的编写要求:
1.无参数 无返回值
2.中断服务函数的名字,必须从启动文件startup_stm32f10x_hd.s中复制
3.中断服务函数中,不要有大量延时操作,及时退出
4.置标志位
5.中断服务函数不需要声明
注意:
1.如果仿真,中断服务程序不能放断点,函数名字有错误
2.停止仿真,代码卡在启动文件 B .位置,没有编写中断服务程序,或者名字有误
*/
/*
接收中断:每接收1个字节,触发1次接收中断
空闲中断:如果检测到总线空闲了(连续10位总线都是高)
对方发过来的数据 0x01 0x02 0x03 0x04
总共触发5次中断:触发4次接收中断,触发1次空闲中断
接收中断中:放断点,会导致后面的数据无法接收
一般在空闲中断中放断点
*/
void USART2_IRQHandler(void)
{
uint8_t date=0;
//ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT); stm32f10x_usart.h 392行
//void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
//1.判断中断是否发生
if(USART_GetITStatus(USART2,USART_IT_RXNE)==SET)
{ //触发接收中断
//2.编写中断服务函数执行内容
date=USART_ReceiveData(USART2);
Kqm.R_Buff[Kqm.R_Length++]=date;
if(Kqm.R_Length >= KQM_R_Buff_Length) //避免数组越界
Kqm.R_Length=0;
//3.清除中断标志位
USART_ClearITPendingBit(USART2,USART_IT_RXNE);
}
if(USART_GetITStatus(USART2,USART_IT_IDLE)==SET)
{
//uint16_t USART_ReceiveData(USART_TypeDef* USARTx); stm32f10x_usart.h 379行
date=USART_ReceiveData(USART2);
//参考手册 25.6.4的位4的介绍 软件序列清除 先读USART_SR(USART_GetITStatus),然后读USART_DR(USART_ReceiveData)
Kqm.R_Idle=1; //接收完成标志位置1
}
}
//数据接收完成,调用处理函数
void KQM_Handle(void)
{
uint8_t temp_date = 0; //存放校验和 局部变量要给初值
if(Kqm.R_Idle == 1)
{
Kqm.R_Idle = 0;
if(Kqm.R_Buff[0] == 0x5F)
{//帧头 -- 地址码正确
temp_date = Kqm.R_Buff[0] + Kqm.R_Buff[1] + Kqm.R_Buff[2] + Kqm.R_Buff[3] + Kqm.R_Buff[4] + Kqm.R_Buff[5] + Kqm.R_Buff[6];
if(temp_date == Kqm.R_Buff[7])
{//校验通过
sensor.voc = ((Kqm.R_Buff[1]<<8) + Kqm.R_Buff[2]) * 0.1;
sensor.ch2o = ((Kqm.R_Buff[3]<<8) + Kqm.R_Buff[4]) * 0.1;
sensor.co2 = ((Kqm.R_Buff[5]<<8) + Kqm.R_Buff[6]);
printf("voc = %.1f,ch2o = %.2f,co2 = %d\r\n",sensor.voc,sensor.ch2o,sensor.co2);
}
}
memset(Kqm.R_Buff,0,sizeof(Kqm.R_Buff));
Kqm.R_Length = 0;
}
}