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,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.学会了使用空闲中断,以及数据的获取与处理。

    作者:小白橘颂

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32第七课:KQM6600空气质量传感器

    发表回复