基於STM32採集CO2(MH-Z19C)感測器資料
阿新 • • 發佈:2020-12-23
技術標籤:STM32CO2串列埠通訊感測器stm32嵌入式串列埠通訊微控制器
本文主要記錄利用STM32的USART2串列埠採集CO2感測器資料,並通過USART1串列埠利用串列埠除錯助手顯示CO2數值。
一、實驗器材
正點原子STM32MINI開發板,煒盛科技CO2(MH-Z19C)感測器。
二、硬體連線
STM32的USART1(PA9-TX,PA10-RX)通過CH340晶片與電腦連線,USART2的接收端PA3-RX連線MH-Z19C的傳送端TX,USART2的傳送端PA2-TX連線MH-Z19C的接收端RX。尤其注意你所使用的串列埠與其他功能不衝突,若衝突會導致串列埠接收資料錯誤。
首先通過STM32主控晶片向Sensor傳送讀取氣體濃度指令:FF 01 86 00 00 00 00 00 79,
感測器接收到指令後返回值為:FF 86 HIGH LOW — — — — 校驗和。
USART2中斷函式主要負責感測器返回資料的儲存與處理。
四、USART2.C程式
#include "sys.h"
#include "usart.h"
//
//如果使用ucos,則包括下面的標頭檔案即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h" //ucos 使用
#endif
//加入以下程式碼,支援printf函式,而不需要選擇use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//標準庫需要的支援函式
struct __FILE
{
int handle;
};
FILE __stdout;
//定義_sys_exit()以避免使用半主機模式
void _sys_exit(int x)
{
x = x;
}
//重定義fputc函式
int fputc(int ch, FILE *f)
{
while(((USART1->SR&0X40)==0)|((USART2->SR&0X40)==0));//迴圈傳送,直到傳送完畢
USART1->DR = (u8) ch;
USART2->DR = (u8) ch;
return ch;
}
#endif
#if EN_USART1_RX| EN_USART2_RX|EN_UART5_RX //如果使能了接收
//串列埠1中斷服務程式
//注意,讀取USARTx->SR能避免莫名其妙的錯誤
u8 USART_RX_BUF[USART_REC_LEN]; //接收緩衝,最大USART_REC_LEN個位元組.
//接收狀態
//bit15, 接收完成標誌
//bit14, 接收到0x0d
//bit13~0, 接收到的有效位元組數目
u16 USART_RX_STA=0; //接收狀態標記
u16 PM25;
u16 CO2;
void uart_init(u32 bound) //初始化串列埠1和串列埠2,函式的引數是波特率
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//串列埠1設定
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA時鐘
//USART1_TX GPIOA.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);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶佔優先順序3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優先順序3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根據指定的引數初始化VIC暫存器
//USART 初始化設定
USART_InitStructure.USART_BaudRate = bound;//串列埠波特率
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); //初始化串列埠1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟串列埠接受中斷
USART_Cmd(USART1, ENABLE); //使能串列埠1
串列埠2設定--用於USART2採集煒盛PM2.5(ZH03B)感測器
//RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //使能USART2,
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA時鐘
// //USART2_TX GPIOA.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);//初始化GPIOA.2
// //USART2_RX GPIOA.3初始化
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA.3
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
// GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.3
// //Usart2 NVIC 配置
// NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
// NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶佔優先順序3
// NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優先順序3
// NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
// NVIC_Init(&NVIC_InitStructure); //根據指定的引數初始化VIC暫存器
// //USART 初始化設定
// USART_InitStructure.USART_BaudRate = bound;//串列埠波特率
// 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); //初始化串列埠2
// USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//開啟串列埠接受中斷
// USART_Cmd(USART2, ENABLE); //使能串列埠2
//串列埠2設定--用於USART2採集煒盛MH-Z19C二氧化碳感測器(我用的正點原子STM32MINI開發板,其他串列埠被佔用)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //使能USART2,
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA時鐘
//USART2_TX GPIOA.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);//初始化GPIOA.2
//USART2_RX GPIOA.3初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA.3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.3
//Usart2 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶佔優先順序3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優先順序3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根據指定的引數初始化VIC暫存器
//USART 初始化設定
USART_InitStructure.USART_BaudRate = bound;//串列埠波特率
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); //初始化串列埠2
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//開啟串列埠接受中斷
USART_Cmd(USART2, ENABLE); //使能串列埠2
}
void USART1_IRQHandler(void) //串列埠1中斷服務程式
{
u8 Res;
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS為真,則需要支援OS.
OSIntEnter();
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中斷(接收到的資料必須是0x0d 0x0a結尾)
{
Res =USART_ReceiveData(USART1); //讀取接收到的資料
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始
else USART_RX_STA|=0x8000; //接收完成了
}
else //還沒收到0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收資料錯誤,重新開始接收
}
}
}
}
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS為真,則需要支援OS.
OSIntExit();
#endif
}
//該段程式碼作用是採集並處理煒盛PM2.5(ZH03B)感測器資料
//void USART2_IRQHandler(void) //串列埠2中斷服務程式
// {
// u8 Res;
//
//#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS為真,則需要支援OS.
// OSIntEnter();
//#endif
// static char start=0;
// if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中斷
// {
// Res =USART_ReceiveData(USART2); //讀取接收到的資料
// //USART_SendData(USART1,Res);
// if(Res == 0x42) //如果接收的第一位資料是0X42(這個是檢視感測器的手冊得知的)
// {
// USART_RX_STA = 0; //讓陣列索引值從0開始
// start = 1; //這個變數是來確定第二位是否接收到了0X4D(這個也是檢視感測器的手冊得知的)
// }
// if(start == 1)
// {
// USART_RX_BUF[USART_RX_STA] = Res ; //把接收到的資料存到數組裡面
// USART_RX_STA++;
// if(USART_RX_STA >= 24 && (USART_RX_BUF[1]==0x4d))
// {
// //USART_SendData(USART1,USART_RX_BUF[12]*256+USART_RX_BUF[13]);
// PM25=USART_RX_BUF[12]*256+USART_RX_BUF[13];
// printf("PM2.5:%d\n",PM25);
// start = 0;
// USART_RX_STA=0;//重新開始接收
// USART_RX_BUF[0] = 0;
// }
// }
// }
//
//#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS為真,則需要支援OS.
// OSIntExit();
//#endif
//
// }
//該段程式碼作用是採集並處理煒盛MH-Z19C二氧化碳感測器資料
void USART2_IRQHandler(void) //串列埠2中斷服務程式
{
u8 Res;
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS為真,則需要支援OS.
OSIntEnter();
#endif
static char start=0;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中斷
{
Res =USART_ReceiveData(USART2); //讀取接收到的資料
//USART_SendData(USART1,Res);
if(Res == 0xFF) //如果接收的第一位資料是0XFF(這個是檢視感測器的手冊得知的)
{
USART_RX_STA = 0; //讓陣列索引值從0開始
start = 1; //這個變數是來確定第二位是否接收到了0X86(這個也是檢視感測器的手冊得知的)
}
if(start == 1)
{
USART_RX_BUF[USART_RX_STA] = Res ; //把接收到的資料存到數組裡面
USART_RX_STA++;
if(USART_RX_STA >= 9 && (USART_RX_BUF[1]==0x86))
{
//USART_SendData(USART1,USART_RX_BUF[12]*256+USART_RX_BUF[13]);
CO2=USART_RX_BUF[2]*256+USART_RX_BUF[3];
printf("CO2:%dppm\n",CO2);
start = 0;
USART_RX_STA=0;//重新開始接收
USART_RX_BUF[0] = 0;
}
}
}
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS為真,則需要支援OS.
OSIntExit();
#endif
}
#endif
五 USART2.h程式
#ifndef __USART_H
#define __USART_H
#include "stdio.h"
#include "sys.h"
#define USART_REC_LEN 200 //定義最大接收位元組數 200
#define EN_USART1_RX 1 //使能(1)/禁止(0)串列埠1接收
#define EN_USART2_RX 1 //使能(1)/禁止(0)串列埠2接收
extern u8 USART_RX_BUF[USART_REC_LEN]; //接收緩衝,最大USART_REC_LEN個位元組.末位元組為換行符
extern u16 USART_RX_STA; //接收狀態標記
//如果想串列埠中斷接收,請不要註釋以下巨集定義
void uart_init(u32 bound);
#endif
六 CO2.c程式
```c
#include "CO2.h"
//傳送讀取指令
//const u16 CO2_SPAN_SET[9]={0xFF,0x01,0x99,0x00,0x00,0x00,0x13,0x88,0x8F};//此處為設定CO2量程,參考MH-Z19B資料手冊,而MH-Z19C手冊裡並沒有設定量程的指令,該處程式碼慎用。
const u16 CO2_WORK_CMD[9]={0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
//void CO2_SPAN(void)
//{
// int i;
// for(i = 0; i < 9; i++)
// {
// USART_ClearFlag(USART2,USART_FLAG_TC);
// USART_SendData(USART2,CO2_SPAN_SET[i]);
// while(USART_GetFlagStatus(USART2, USART_FLAG_TC)==RESET);
// }
//
//}
void CO2_WORK(void)
{
int i;
for(i = 0; i < 9; i++)
{
USART_ClearFlag(USART2,USART_FLAG_TC);
USART_SendData(USART2,CO2_WORK_CMD[i]);
while(USART_GetFlagStatus(USART2, USART_FLAG_TC)==RESET);
}
}
七、CO2.h程式、
```c
#ifndef __CO2_H
#define __CO2_H
#include "stdio.h"
#include "sys.h"
#include "usart.h"
//extern void CO2_SPAN(void);
extern void CO2_WORK(void);
#endif
八、main.c程式(功能:當檢測到CO2>1500ppm時,LED1點亮,否則LED1熄滅,LED0只是個閃爍燈而已)
```c
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "stdio.h"
#include "CO2.h"
extern u16 CO2;
int main(void)
{
delay_init(); //延時函式初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 設定中斷優先順序分組2
uart_init(9600); //串列埠初始化為9600
LED_Init(); //初始化與LED連線的硬體介面
while(1)
{
// CO2_SPAN();
CO2_WORK();
if(CO2>1500)
{
LED1=0;
}
else
{
LED1=1;
}
LED0=!LED0;
delay_ms(250);
}
}
九、實驗結果
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201222235531335.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzUzNjM3NTg2,size_16,color_FFFFFF,t_70#pic_center)
十、樓主是一名微控制器愛好者,歡迎大家留言交流。
走鋼絲的猴書於2020.12.22晚23:42