1. 程式人生 > 其它 >基於STM32採集CO2(MH-Z19C)感測器資料

基於STM32採集CO2(MH-Z19C)感測器資料

技術標籤: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。尤其注意你所使用的串列埠與其他功能不衝突,若衝突會導致串列埠接收資料錯誤。

三、CO2感測器控制原理
首先通過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.2223:42