STM32第七课:KQM6600空气质量传感器
文章目录
- 需求
- 一、KQM6600模块及接线方法
- 二、模块配置流程
- 1.环境
- 2.配置时钟和IO
- 3.配置串口初始化,使能以及中断
- 4.中断函数
- 三、数据处理
- 四、关键代码
- 总结
需求
能够在串口实时显示当前的VOC(挥发性有机化合物),甲醛和Co2浓度。

一、KQM6600模块及接线方法
KQM6600TAUs型空气质量检测模块,使用MEMS VOC传感器件作为检测空气中有机化合物气体(VOC)的模块。UART通信数据输出,根据VOC数据计算和等效甲醛,CO2输出。其具有体积小,功耗低,灵敏度高,响应速度快等居多优点,广泛应用在空气质量检
测及控制领域。

要注意该模块的电压和波特率。

由官方的说明书可知,只需在将该模块的(V)供电,(A)TX和(G)GND三个引脚接到板子上即可。

本次例程由于没有使用SD卡模块,所以选择将KQM6600模块的数据传输到UART4上,只需将该模块的A(TX)接到PC11即可。
二、模块配置流程
1.环境
1.首先要保证串口1能够将接收到的实时数据发送到串口上。
2.还要对printf进行重定向,让其能够打印到串口上。(详情见STM32第三课:串口调试)
代码如下:
usart.c
#include "usart.h"
#include "stdio.h"void Usart1_Config()
{//开时钟:GPIOA,USART1RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE);//配置对应的IO口 PA9(tx):复用推挽 PA10(RX):浮空输入GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;GPIO_Init(GPIOA,&GPIO_InitStruct);//配置串口1 8数据位,0校验位,1停止位,波特率115200USART_InitTypeDef USART_InitStruct = {0};//可以通过结构体类型跳转USART_InitStruct.USART_BaudRate = 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;USART_InitStruct.USART_WordLength = USART_WordLength_8b;USART_Init(USART1,&USART_InitStruct);USART_Cmd(USART1,ENABLE);//配置串口1的中断//在串口1产生接收的时候,会产生中断,我们直接去中断函数里面处理就可以了//选择串口1的中断原因USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//USART1->CR1 |= 0x1<<5;//使能串口1的接收非空中断NVIC_SetPriority(USART1_IRQn,7);//设置优先级0~15NVIC_EnableIRQ(USART1_IRQn);//使能中断通道
}void SendData(uint8_t data)
{while((USART1->SR&0x01<<6)==0){}//等待上次发送完成USART1->DR = data;//发送数据
}int fputc(int ch, FILE *f)
{//printf函数最终会跳转到这里来运行while((USART1->SR&0x1<<6)==0);//发送数据USART1->DR = (uint8_t)ch;return ch;
}void USART1_IRQHandler(void)
{uint8_t data=0;if((USART1->SR&0x1<<5)!=0){//执行该中断函数的原因有很多,所以判断一下是不是接收导致的//接收数据data = USART_ReceiveData(USART1);//读操作,同时也是清空中断标志位USART_SendData(USART1, data); }
}
usart.h
#ifndef _USART_H_
#define _USART_H_
#include "stm32f10x.h"
#include "stdio.h"void Usart1_Config();
void SendData(uint8_t data);
int fputc(int ch, FILE *f);#endif
2.配置时钟和IO
首先创建一个kqm.c.h文件用来专门配置该模块
和串口1一样,只不过要注意此时是UART4,引脚为PC11。
//开时钟 U4 PC11RXRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4,ENABLE);//配置ioGPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC,&GPIO_InitStruct);
3.配置串口初始化,使能以及中断

初始化要将波特率改为9600,然后只用打开接收就行,其他与串口1一样。
使能就不说了,改个参数就行。
中断也一样,只用改改参数。
//配置串口 波特率9600 数据位8,校验位0,停止位1USART_InitTypeDef USART_InitStruct = {0};//可以通过结构体类型跳转USART_InitStruct.USART_BaudRate = 9600;//波特率USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件控制流不开USART_InitStruct.USART_Mode = USART_Mode_Rx;//打开接收USART_InitStruct.USART_Parity = USART_Parity_No;USART_InitStruct.USART_StopBits = USART_StopBits_1;USART_InitStruct.USART_WordLength = USART_WordLength_8b;USART_Init(UART4,&USART_InitStruct);//使能串口USART_Cmd(UART4,ENABLE);//配置串口4的中断(采用中断接收)USART_ITConfig(UART4,USART_IT_RXNE,ENABLE);//使能串口4 的接收非空中断USART_ITConfig(UART4,USART_IT_IDLE,ENABLE);//总线空闲中断NVIC_SetPriority(UART4_IRQn,7);//设置优先级0~15NVIC_EnableIRQ(UART4_IRQn);//使能中断通道
4.中断函数
依旧是先去启动文件中找到该串口的中断函数复制过来。
然后使用USART_GetITStatus函数进行标志位判断,若为1则代表传输完成。
由于数据位为8位所以此时需要定义一个数组来存放这8位数组。
还需要定义一个参数来计数,每当存放够8位时,清0,再从头开始覆盖存入数据。此刻就完成了8位数据获取。
uint8_t data=0;//判断接收中断是否发生if(USART_GetITStatus(UART4,USART_IT_RXNE)==SET){data = UART4->DR;u4recive[u4count]=data;u4count++;u4count%=8;//USART1->DR = data;//回显}
为了判断该模块是否传输完数据,此时我们要使用一个新的知识:中断空闲
USART_GetITStatus(UART4,USART_IT_IDLE)
当结尾时IDLE时就代表此时判断的是中断空闲。
当中断空闲被置为1时,就代表示总线空闲,8位数据传完了,接收完毕。此时想要清理中断空闲的话,需要先读SR再读DR。
然后在设置一个标志位u4flag,将其置为1代表接收完毕。
//触发空闲中断,表示总线空闲,接收完毕if(USART_GetITStatus(UART4,USART_IT_IDLE)==SET){data = UART4->SR;//清理空闲中断,先读SR再读DRdata = UART4->DR;u4flag=1;}
三、数据处理
先判断标志位u4flag是否为0,若为0这直接退出,不进入数据处理函数。

由手册可知,该模块有预热操作,为了屏蔽掉预热操作的数据,此时需要判断传输过来的8位数组,检查是否每位都为0xff,只要有一位不是就继续进行,否则向串口打印‘数据预热’。
if(u4flag==0){return 0;}u4flag=0;for(i=1;i<7;i++){if(u4recive[i]!=0xff){break;}}if(i==7){u4count=0;printf("数据预热\r\n");memset(u4recive,0,102);return 0;}
以上都没有问题之后,此时获取到的数组才是真正有用的数据。


由手册可知该数据的转换模式,照着做就行。记得用完将数据位和标志位都清零。
voc=((u4recive[1]<<8)+u4recive[2])*0.11;ch2o = ((u4recive[3]<<8)+u4recive[4])*0.01;co2 = ((u4recive[5]<<8)+u4recive[6]);printf("VOC = %.1f PPM\r\n甲醛 = %.2f MG/M3\r\nCo2 = %.0fPPM\r\n",voc,ch2o,co2);u4count=0;memset(u4recive,0,102);return 0;
最后记得将处理函数添加到主函数的while(1)死循环中。
由于串口4用到了串口1输出,所以在main函数的配置应在串口1的后面。
四、关键代码
main.c
#include "stm32f10x.h"
#include "usart.h"
#include "stdio.h"
#include "delay.h"
#include "kqm.h"
#include "string.h"int main()
{NVIC_SetPriorityGrouping(5);//两位抢占两位次级Usart1_Config(); Kqm_U4Config();while(1){ KQM_DealData();}
}
kqm.c
#include "stm32f10x.h"
#include "stdio.h"
#include "string.h"float voc,ch2o,co2;
void Kqm_U4Config()
{//开时钟 U4 PC11RXRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4,ENABLE);//配置ioGPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC,&GPIO_InitStruct);//配置串口 波特率9600 数据位8,校验位0,停止位1USART_InitTypeDef USART_InitStruct = {0};//可以通过结构体类型跳转USART_InitStruct.USART_BaudRate = 9600;//波特率USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件控制流不开USART_InitStruct.USART_Mode = USART_Mode_Rx;//打开接收USART_InitStruct.USART_Parity = USART_Parity_No;USART_InitStruct.USART_StopBits = USART_StopBits_1;USART_InitStruct.USART_WordLength = USART_WordLength_8b;USART_Init(UART4,&USART_InitStruct);//使能串口USART_Cmd(UART4,ENABLE);//配置串口4的中断(采用中断接收)USART_ITConfig(UART4,USART_IT_RXNE,ENABLE);//使能串口4 的接收非空中断USART_ITConfig(UART4,USART_IT_IDLE,ENABLE);//总线空闲中断NVIC_SetPriority(UART4_IRQn,7);//设置优先级0~15NVIC_EnableIRQ(UART4_IRQn);//使能中断通道
}uint8_t u4recive[102]={0};
uint8_t u4count=0;
uint8_t u4flag=0;
uint8_t i=0;void UART4_IRQHandler(void)
{uint8_t data=0;//判断接收中断是否发生if(USART_GetITStatus(UART4,USART_IT_RXNE)==SET){data = UART4->DR;u4recive[u4count]=data;u4count++;u4count%=8;//USART1->DR = data;//回显}//触发空闲中断,表示总线空闲,接收完毕if(USART_GetITStatus(UART4,USART_IT_IDLE)==SET){data = UART4->SR;//清理空闲中断,先读SR再读DRdata = UART4->DR;u4flag=1;}}uint8_t KQM_DealData()
{if(u4flag==0){return 0;}u4flag=0;for(i=1;i<7;i++){if(u4recive[i]!=0xff){break;}}if(i==7){u4count=0;printf("数据预热\r\n");memset(u4recive,0,102);return 0;}voc=((u4recive[1]<<8)+u4recive[2])*0.11;ch2o = ((u4recive[3]<<8)+u4recive[4])*0.01;co2 = ((u4recive[5]<<8)+u4recive[6]);printf("VOC = %.1f PPM\r\n甲醛 = %.2f MG/M3\r\nCo2 = %.0fPPM\r\n",voc,ch2o,co2);u4count=0;memset(u4recive,0,102);return 0;
}
kqm.h
#ifndef _KQM_H_
#define _KQM_H_
#include "stm32f10x.h"
void Kqm_U4Config();
uint8_t KQM_DealData();#endif
其他代码遇上几节课一样。
总结
1.学会了KQM6600空气质量传感器模块的接线和配置。
2.学会了使用空闲中断,以及数据的获取与处理。
相关文章:
STM32第七课:KQM6600空气质量传感器
文章目录 需求一、KQM6600模块及接线方法二、模块配置流程1.环境2.配置时钟和IO3.配置串口初始化,使能以及中断4.中断函数 三、数据处理四、关键代码总结 需求 能够在串口实时显示当前的VOC(挥发性有机化合物),甲醛和Co2浓度。 …...
任务4.8.4 利用Spark SQL实现分组排行榜
文章目录 1. 任务说明2. 解决思路3. 准备成绩文件4. 采用交互式实现5. 采用Spark项目实战概述:使用Spark SQL实现分组排行榜任务背景任务目标技术选型实现步骤1. 准备数据2. 数据上传至HDFS3. 启动Spark Shell或创建Spark项目4. 读取数据5. 数据转换6. 创建临时视图…...
五线谱与简谱有什么区别 五线谱简谱混排怎么打 吉他谱软件哪个好
五线谱与简谱作为音乐记谱领域的两大主流系统,各自承载着深厚的历史渊源与独特的表现力,并在全球范围内被不同程度地接受和应用。尽管两者都是为了记录音乐作品中的音高和节奏信息,但其内在机制、适用范围以及学习曲线存在显著差别。下面我们…...
[C#][opencvsharp]C#使用opencvsharp进行年龄和性别预测支持视频图片检测
使用 OpenCVSharp 来调用 age_net.caffemodel 和 gender_net.caffemodel 来进行性别和年龄预测涉及几个步骤。以下是一个简化的流程和示例文案: 1. 准备工作 确保你已经安装了 OpenCVSharp 和相关的依赖项。确保你有 age_net.prototxt、age_net.caffemodel、gende…...
pdf拆分,pdf拆分在线使用,pdf拆分多个pdf
在数字化的时代,pdf文件已经成为我们日常办公、学习不可或缺的文档格式。然而,有时候我们可能需要对一个大的pdf文件进行拆分,以方便管理和分享。那么,如何将一个pdf文件拆分成多个pdf呢?本文将为你推荐一种好用的拆分…...
VScode Python debug:hydra.run.dir 写入launch.json
记录一个debug时的经验: VS code extension名称版本Pythonv2024.8.1Python Debuggerv2024.6.0 我配置的project运行 train.py 时需要在 terminal 输入参数 hydra.run.dirxxx 我想用 vscode debug 查看内部代码,按以往的经验需要将args写入launch.json&…...
ExVideo: 提升5倍性能-用于视频合成模型的新型后调谐方法
标题:ExVideo: Extending Video Diffusion Models via Parameter-Efficient Post-Tuning作者: Zhongjie Duan; Wenmeng Zhou; Cen Chen; Yaliang Li; Weining QianDOI: 10.48550/arXiv.2406.14130摘要: Recently, advancements in video synthesis have attracted s…...
laravel Dcat Admin 入门应用(三)Grid 之 Column
Dcat Admin 是一个基于 Laravel-admin 二次开发而成的后台构建工具,只需很少的代码即可构建出一个功能完善的高颜值后台系统。支持页面一键生成 CURD 代码,内置丰富的后台常用组件,开箱即用,让开发者告别冗杂的 HTML 代码。 larav…...
掌握Llama 2分词器:填充、提示格式及更多
目录 简介Llama 2分词器基础为分词器设置填充添加特殊标记使用BOS和EOS标记进行分词定义填充标记训练中使用填充标记高级功能:掩码标记Llama的提示格式结论 简介 在语言模型领域,时间变化迅速。自Llama 2发布已经有几个月了,但关于其分词器…...
pdf合并,pdf合并成一个pdf,pdf合并在线网页版
在处理pdf文件的过程中,有时我们需要将多个pdf文件合并成一个pdf文件。作为一名有着丰富计算机应用经验的技术博主,我将为您详细介绍如何将多个pdf文件合并成一个pdf文件。 pdf合并方法:使用, “轻云处理pdf官网” 打开 “轻云处…...
算法基础--------【图论】
图论(待完善) DFS:和回溯差不多 BFS:进while进行层序遍历 定义: 图论(Graph Theory)是研究图及其相关问题的数学理论。图由节点(顶点)和连接这些节点的边组成。图论的研究范围广泛,涉及路径、…...
x86和x64架构的区别及应用
x86和x64架构的区别及应用 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 在计算机硬件和软件领域,x86和x64是两种常见的处理器架构。它们在计算能…...
2024年度总结:不可错过的隧道IP网站评估推荐
随着网络技术的飞速发展,隧道IP服务成为了许多企业和个人在进行网络活动时的得力助手。作为专业的测评团队,我们经过一整年的深入研究和测试,为大家带来了三款备受瞩目的隧道IP网站推荐——品易HTTP、极光HTTP和一G代理。接下来,我…...
Linux下VSCode的安装和基本使用
应用场景:嵌入式开发。 基本只需要良好的编辑环境,能支持文件搜索和跳转,就挺OK的。 之所以要在Linux下安装,是因为在WIN11上安装后,搜索功能基本废了,咋弄都弄不好,又不方便重装win系统&#x…...
C# 实现websocket双向通信
🎈个人主页:靓仔很忙i 💻B 站主页:👉B站👈 🎉欢迎 👍点赞✍评论⭐收藏 🤗收录专栏:C# 🤝希望本文对您有所裨益,如有不足之处ÿ…...
Spring Boot结合FFmpeg实现视频会议系统视频流处理与优化
在构建高效稳定的视频会议系统时,实时视频流的处理和优化是开发者面临的核心挑战之一。这不仅仅是简单的视频数据传输,更涉及到一系列复杂的技术问题,需要我们深入分析和有效解决。 高并发与实时性要求: 视频会议系统通常需要支持多人同时进行视频通话,这就意味着系统需要…...
扫扫地,搞搞卫生 ≠ 车间5S管理
在制造业的日常运营中,车间管理是一项至关重要的工作,它直接关系到生产效率、产品质量以及员工的工作环境。然而,许多人常常将简单的“扫扫地,搞搞卫生”等同于车间5S管理,这种误解不仅可能导致管理效果不佳࿰…...
ES(笔记)
es就是json请求体代替字符串查询 dsl查询和过滤,一个模糊查询,一个非模糊查询 must,should 做模糊查询的,里面都是match,根据查询内容进行匹配,filter过滤,term词元查询,就是等值查…...
开箱即用的fastposter海报生成器
什么是 fastposter ? fastposter 海报生成器是一款快速开发海报的工具。只需上传一张背景图,在对应的位置放上组件(文字、图片、二维码、头像)即可生成海报。 点击代码直接生成各种语言 SDK 的调用代码,方便快速开发。 软件特性&…...
力扣每日一题 6/28 动态规划/数组
博客主页:誓则盟约系列专栏:IT竞赛 专栏关注博主,后期持续更新系列文章如果有错误感谢请大家批评指出,及时修改感谢大家点赞👍收藏⭐评论✍ 2742.给墙壁刷油漆【困难】 题目: 给你两个长度为 n 下标从 0…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
