STM32第七课:KQM6600空气质量传感器

news2024/10/5 14:09:17

文章目录

  • 需求
  • 一、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,USART1
	  RCC_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停止位,波特率115200
		USART_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~15
	NVIC_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 PC11RX
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	 RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4,ENABLE);
	//配置io
	    GPIO_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,停止位1
		USART_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~15
	  NVIC_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再读DR
		data = 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 PC11RX
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	 RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4,ENABLE);
	//配置io
	    GPIO_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,停止位1
		USART_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~15
	  NVIC_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再读DR
		data = 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.学会了使用空闲中断,以及数据的获取与处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1875362.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GPU算力是什么,哪些行业需要用到GPU算力?

近两年&#xff0c;计算能力已成为推动各行各业发展的关键因素。而GPU&#xff08;图形处理器&#xff09;算力&#xff0c;作为现代计算技术的重要分支&#xff0c;正逐渐在多个领域展现出其强大的潜力和价值。尚云将简要介绍GPU算力的定义和基本原理&#xff0c;并探讨其在哪…

使用Apache POI库在Java中导出Excel文件的详细步骤

使用Apache POI库在Java中导出Excel文件的详细步骤 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把手教你开发炫酷的vbs脚本制作(完善中……&#xff09; 4、牛逼哄哄的 IDEA编程利器技…

企业中对RAG的优化方案

企业中对RAG的优化方案 RAG优化&#xff1a;检索、语义和生成方面的提升RAG流程一、数据处理优化数据清洗实际案例 二、检索方面优化向量库检索倒排索引数据库检索 三、生成方面优化调整Prompt 四、架构优化RAGAgent架构Self-RAG架构Agentic RAG架构 总结 RAG优化&#xff1a;检…

【Django】网上蛋糕项目商城-热销和新品

概念 本文将完成实现项目的热销和新品两个分类的商品列表进行分页展示。 热销和新品功能实现步骤 在head.html头部页面中点击这两个超链接向服务器发送请求。 在urls.py文件中定义该请求地址 path(goodsrecommend_list/,views.goodsrecommend_list) 在views.py文件中定义g…

mac配置hdc

首先需要找到 .zshrc 文件&#xff1a; 访达进入到user文件夹中&#xff0c;shiftcommand.键显示隐藏文件&#xff1a; 双击打开进行编辑&#xff0c;在最后添加 //HDC_HOME 指的是hdc的绝对路径&#xff0c;要替换成自己的路径 export HDC_HOME/Users/你的名字/Library/Huaw…

系统架构设计师 - 计算机网络(1)

计算机网络 计算机网络TCP/IP 协议簇TCP与UDP ★★★DHCP与DNS ★★★DNS 协议应用DHCP 协议应用 网络规划与设计逻辑设计与物理设计 ★★★★逻辑网络设计物理网路设计 层次化网络设计网络冗余设计 网络存储 ★★网络存储方式磁盘阵列 - Raid 大家好呀&#xff01;我是小笙&am…

二刷算法训练营Day45 | 动态规划(7/17)

目录 详细布置&#xff1a; 1. 139. 单词拆分 2. 多重背包理论基础 3. 背包总结 3.1 背包递推公式 3.2 遍历顺序 01背包 完全背包 详细布置&#xff1a; 1. 139. 单词拆分 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单…

北大西洋横断面(ANT)项目计划

North Atlantic Transect (ANT) program 简介 - **&#x1f5fa;️ 北大西洋横断面计划 (ANT) 概述** 北大西洋横断面计划 (ANT) 是一个由美国资助的 GEOTRACES 项目&#xff0c;旨在测量非洲海岸沿线的海洋化学参数。该计划收集了有关海水中的溶解氧、营养盐、碳酸盐离子、微…

STM32第八课:Su-03t语音识别模块

文章目录 需求一、SU03T语音识别模块二、模块配置流程1.固件烧录2.配置串口和传输引脚3.中断函数4.double类型转换5 数据发送6.接收处理 三、该模块完整代码总结 需求 基于上次完成空气质量传感器&#xff0c;利用SU03T语音识别模块&#xff0c;实现空气质量的语音问答播报。 …

240622_昇思学习打卡-Day4-ResNet50迁移学习

240622_昇思学习打卡-Day4-ResNet50迁移学习 我们对事物的认知都是一点一点积累出来的&#xff0c;往往借助已经认识过的东西&#xff0c;可以更好地理解和认识新的有关联的东西。比如一个人会骑自行车&#xff0c;我们让他去骑摩托车他也很快就能学会&#xff0c;比如已经学会…

STM32开发方式的演变与未来展望

一、STM32开发方式的演变 自2007年STM32微控制器首次亮相以来&#xff0c;其开发方式经历了从寄存器到标准库&#xff0c;再到HAL&#xff08;硬件抽象层&#xff09;的演变。 1.寄存器开发&#xff08;2007年-2010年代初&#xff09; 最初&#xff0c;由于初期缺乏足够的软…

Cyuyanzhong的内存函数

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、memcpy函数的使用与模拟实现二、memmove函数的使用和模拟实现三、memset函数与memcmp函数的使用&#xff08;一&#xff09;、memset函数&#xff08;内存块…

Qt creator实现一个简单计算器

目录 1 界面设计 2 思路简介 3 代码 目录 1 界面设计 ​2 思路简介 3 代码 3.1 widget.h 3.2 widget.c 4 完整代码 在这里主要记载了如何使用Qt creator完成一个计算器的功能。该计算器可以实现正常的加减乘除以及括号操作&#xff0c;能实现简单的计算器功能。 1 界…

.NET使用CsvHelper快速读取和写入CSV文件

前言 在日常开发中使用CSV文件进行数据导入和导出、数据交换是非常常见的需求&#xff0c;今天我们来讲讲在.NET中如何使用CsvHelper这个开源库快速实现CSV文件读取和写入。 CsvHelper类库介绍 CsvHelper是一个.NET开源、快速、灵活、高度可配置、易于使用的用于读取和写入C…

Spring Boot集成vavr快速入门demo

1.什么是vavr&#xff1f; 初闻vavr&#xff0c;感觉很奇怪&#xff0c;咋这个名字&#xff0c;后面看到它的官网我沉默了&#xff0c;怀疑初创团队付费资讯了UC震惊部如何取名字&#xff0c;好家伙&#xff0c;vavr就是java这四个字倒过来&#xff0c;真的是’颠覆’了java……

如何成为-10x工程师:反向教学大数据开发实际工作中应如何做

10x 工程师可能是神话&#xff0c;但 -10x 工程师确实存在。要成为 -10x 工程师&#xff0c;只需每周浪费 400 小时的工程时间。结合以下策略&#xff1a; 目录 如何使 10 名工程师的输出无效化改变需求大数据开发示例 创建 400 小时的繁忙工作任务示例大数据开发示例 创建 400…

心理辅导平台系统

摘 要 中文本论文基于Java Web技术设计与实现了一个心理辅导平台。通过对国内外心理辅导平台发展现状的调研&#xff0c;本文分析了心理辅导平台的背景与意义&#xff0c;并提出了论文研究内容与创新点。在相关技术介绍部分&#xff0c;对Java Web、SpringBoot、B/S架构、MVC模…

Unable to get expected results using BM25 or any search functions in Weaviate

题意&#xff1a;使用 Weaviate 中的 BM25 或任何搜索函数都无法获得预期结果 问题背景&#xff1a; I have created a collection in Weaviate, and ingested some documents into the Weaviate database using LlamaIndex. When I used the default search, I found that it…

高精度除法的实现

高精度除法与高精度加法的定义、前置过程都是大致相同的&#xff0c;如果想了解具体内容&#xff0c;可以移步至我的这篇博客&#xff1a;高精度加法计算的实现 在这里就不再详细讲解&#xff0c;只讲解主体过程qwq 主体过程 高精度除法的原理和小学学习的竖式除法是一样的。 …

Chrome导出cookie的实战教程

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…