moc3020如何测量好坏(MM32F103空气质量检测仪)

还是我的风格,开篇先啰嗦:

闲来无事,太难的不会,就想玩玩手里的吃灰板子。

去年在灵动的活动中获得一块MM32L073为主控的开发板,型号为eMiniBoard MB-023。当时测评就写了一个开箱和串口测试,现在重新捡起来,玩点小应用,因为手头的传感器有限,只能做一个空气质量检测仪,主要包含三个功能:空气温度检测,空气湿度检测和PM2.5浓度检测。

但是在调试温湿度检测的时候翻车了,使用的传感器是DHT11模块,该模块在使用时需要微秒级别的延时,但是我在MM32L073的库中找不到us的延时函数,当然排除空函数的粗延时,使用SysTick只能做到ms级别的延时,自己写一个us的延时函数,发现根本不起作用,具体如下:

复制

void delay_init() { RCC_ClocksTypeDef RCC_Clocks; if (SysTick_Config(SystemCoreClock / 1000)) { /* Capture error */ while (1); } /* Configure the SysTick handler priority */ NVIC_SetPriority(SysTick_IRQn, 0x0);//SysTick中断优先级设置 }

以上是官方库中的延时函数初始化,SysTick_Config函数中给定系统时钟(48M)除以1000,使SysTick以1毫秒进入一次中断,如果我将SystemCoreClock 除以1000000应该是1us进入一次中断,但是这个会导致延时函数卡死,在debug后发现程序卡死在SysTick_Config()函数,继续追踪:

复制

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) { if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ SysTick->LOAD = ticks - 1; /* set reload register */ NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt */ SysTick->VAL = 0; /* Load the SysTick Counter Value */ SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ return (0); /* Function successful */ }

程序卡死在SysTick->LOAD = ticks - 1;也就是SysTick的重装载值寄存器写入47卡死,忘了在哪篇帖子看到有人说该值不能小于255,否则会自动将255写入。经过测试确实如此,此处我没有深入探究,改用定时器。ps:该问题在stm32F030和GD32E230均不存在,不知这其中有何缘由,希望了解的大佬给个提示。

经过测试,定时器的中断是无法达到1us进入一次中断的,无论如何设置,定时器最短只能在3us左右进一次中断。该方案作废。

路程已经过半,总不能半途而废,于是在某宝买了一个MM32F103CBT6最小系统,花了七十多大洋,涨价真离谱啊!

啰里啰嗦一大堆,终于可以进入正文了

本文一共三个模块:

oled模块,使用模拟IIC驱动。DHT11温湿度模块,IO口的读写操作。夏普GP2Y10粉尘传感器,UART操作。

OLED驱动

oled模块手里有两块7针0.96寸的屏幕,但是在测试的时候都不好用,无论如何也点亮不了,于是在买最小系统板的同时也顺带买了一个4针的oled 0.96的屏,今天测试同样无法使用,换了几个方法,又拿stm32的板子,用例程测试,都是不行,最后发现是杜邦线断了,在内部断了,外表看不出来。我真的是一言难尽

moc3020如何测量好坏(MM32F103空气质量检测仪)(1)

关于该屏幕的介绍这里就不啰嗦了,网上到处都是,这里分享一下我的驱动。oled.c

复制

#include "oled.h" #include "stdlib.h" #include "oledfont.h" #include "delay.h" //OLED的显存 //存放格式如下. //[0]0 1 2 3 ... 127 //[1]0 1 2 3 ... 127 //[2]0 1 2 3 ... 127 //[3]0 1 2 3 ... 127 //[4]0 1 2 3 ... 127 //[5]0 1 2 3 ... 127 //[6]0 1 2 3 ... 127 //[7]0 1 2 3 ... 127 /********************************************** //IIC Start **********************************************/ /********************************************** //IIC Start **********************************************/ void IIC_Start() { OLED_SCLK_Set() ; OLED_SDIN_Set(); OLED_SDIN_Clr(); OLED_SCLK_Clr(); } /********************************************** //IIC Stop **********************************************/ void IIC_Stop() { OLED_SCLK_Set() ; // OLED_SCLK_Clr(); OLED_SDIN_Clr(); OLED_SDIN_Set(); } void IIC_Wait_Ack() { //GPIOB->CRH &= 0XFFF0FFFF; //设置PB12为上拉输入模式 //GPIOB->CRH |= 0x00080000; // OLED_SDA = 1; // delay_us(1); //OLED_SCL = 1; //delay_us(50000); /* while(1) { if(!OLED_SDA) //判断是否接收到OLED 应答信号 { //GPIOB->CRH &= 0XFFF0FFFF; //设置PB12为通用推免输出模式 //GPIOB->CRH |= 0x00030000; return; } } */ OLED_SCLK_Set() ; OLED_SCLK_Clr(); } /********************************************** // IIC Write byte **********************************************/ void Write_IIC_Byte(unsigned char IIC_Byte) { unsigned char i; unsigned char m,da; da=IIC_Byte; OLED_SCLK_Clr(); for(i=0;i<8;i ) { m=da; // OLED_SCLK_Clr(); m=m&0x80; if(m==0x80) {OLED_SDIN_Set();} else OLED_SDIN_Clr(); da=da<<1; OLED_SCLK_Set(); OLED_SCLK_Clr(); } } /********************************************** // IIC Write Command **********************************************/ void Write_IIC_Command(unsigned char IIC_Command) { IIC_Start(); Write_IIC_Byte(0x78); //Slave address,SA0=0 IIC_Wait_Ack(); Write_IIC_Byte(0x00); //write command IIC_Wait_Ack(); Write_IIC_Byte(IIC_Command); IIC_Wait_Ack(); IIC_Stop(); } /********************************************** // IIC Write Data **********************************************/ void Write_IIC_Data(unsigned char IIC_Data) { IIC_Start(); Write_IIC_Byte(0x78); //D/C#=0; R/W#=0 IIC_Wait_Ack(); Write_IIC_Byte(0x40); //write data IIC_Wait_Ack(); Write_IIC_Byte(IIC_Data); IIC_Wait_Ack(); IIC_Stop(); } void OLED_WR_Byte(unsigned dat,unsigned CMD) { if(cmd) { Write_IIC_Data(dat); } else { Write_IIC_Command(dat); } } /******************************************** // fill_Picture ********************************************/ void fill_picture(unsigned char fill_Data) { unsigned char m,n; for(m=0;m<8;m ) { OLED_WR_Byte(0xb0 m,0); //page0-page1 OLED_WR_Byte(0x00,0); //low column start address OLED_WR_Byte(0x10,0); //high column start address for(n=0;n<128;n ) { OLED_WR_Byte(fill_Data,1); } } } /***********************Delay****************************************/ void Delay_50ms(unsigned int Del_50ms) { unsigned int m; for(;Del_50ms>0;Del_50ms--) for(m=6245;m>0;m--); } void Delay_1ms(unsigned int Del_1ms) { unsigned char j; while(Del_1ms--) { for(j=0;j<123;j ); } } //坐标设置 void OLED_Set_Pos(unsigned char x, unsigned char y) { OLED_WR_Byte(0xb0 y,OLED_CMD); OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD); OLED_WR_Byte((x&0x0f),OLED_CMD); } //开启OLED显示 void OLED_Display_On(void) { OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令 OLED_WR_Byte(0X14,OLED_CMD); //DCDC ON OLED_WR_Byte(0XAF,OLED_CMD); //DISPLAY ON } //关闭OLED显示 void OLED_Display_Off(void) { OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令 OLED_WR_Byte(0X10,OLED_CMD); //DCDC OFF OLED_WR_Byte(0XAE,OLED_CMD); //DISPLAY OFF } //清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!! void OLED_Clear(void) { u8 i,n; for(i=0;i<8;i ) { OLED_WR_Byte (0xb0 i,OLED_CMD); //设置页地址(0~7) OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址 OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址 for(n=0;n<128;n )OLED_WR_Byte(0,OLED_DATA); } //更新显示 } void OLED_On(void) { u8 i,n; for(i=0;i<8;i ) { OLED_WR_Byte (0xb0 i,OLED_CMD); //设置页地址(0~7) OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址 OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址 for(n=0;n<128;n )OLED_WR_Byte(1,OLED_DATA); } //更新显示 } //在指定位置显示一个字符,包括部分字符 //x:0~127 //y:0~63 //mode:0,反白显示;1,正常显示 //size:选择字体 16/12 void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size) { unsigned char c=0,i=0; c=chr-' ';//得到偏移后的值 if(x>Max_Column-1){x=0;y=y 2;} if(Char_Size ==16) { OLED_Set_Pos(x,y); for(i=0;i<8;i ) OLED_WR_Byte(F8X16[c*16 i],OLED_DATA); OLED_Set_Pos(x,y 1); for(i=0;i<8;i ) OLED_WR_Byte(F8X16[c*16 i 8],OLED_DATA); } else { OLED_Set_Pos(x,y); for(i=0;i<6;i ) OLED_WR_Byte(F6x8[c][i],OLED_DATA); } } //m^n函数 u32 oled_pow(u8 m,u8 n) { u32 result=1; while(n--)result*=m; return result; } //显示2个数字 //x,y :起点坐标 //len :数字的位数 //size:字体大小 //mode:模式 0,填充模式;1,叠加模式 //num:数值(0~4294967295); void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2) { u8 t,temp; u8 enshow=0; for(t=0;t<len;t ) { temp=(num/oled_pow(10,len-t-1)); if(enshow==0&&t<(len-1)) { if(temp==0) { OLED_ShowChar(x (size2/2)*t,y,' ',size2); continue; }else enshow=1; } OLED_ShowChar(x (size2/2)*t,y,temp '0',size2); } } //显示一个字符号串 void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 Char_Size) { unsigned char j=0; while (chr[j]!='\0') { OLED_ShowChar(x,y,chr[j],Char_Size); x =8; if(x>120){x=0;y =2;} j ; } } //显示汉字 void OLED_ShowCHinese(u8 x,u8 y,u8 no) { u8 t,adder=0; OLED_Set_Pos(x,y); for(t=0;t<16;t ) { OLED_WR_Byte(Hzk[2*no][t],OLED_DATA); adder =1; } OLED_Set_Pos(x,y 1); for(t=0;t<16;t ) { OLED_WR_Byte(Hzk[2*no 1][t],OLED_DATA); adder =1; } } /***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/ void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[]) { unsigned int j=0; unsigned char x,y; if(y1%8==0) y=y1/8; else y=y1/8 1; for(y=y0;y<y1;y ) { OLED_Set_Pos(x0,y); for(x=x0;x<x1;x ) { OLED_WR_Byte(BMP[j ],OLED_DATA); } } } //初始化SSD1306 void OLED_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能A端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOD3,6 GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_7); delay_ms(800); OLED_WR_Byte(0xAE,OLED_CMD);//--display off OLED_WR_Byte(0x00,OLED_CMD);//---set low column address OLED_WR_Byte(0x10,OLED_CMD);//---set high column address OLED_WR_Byte(0x40,OLED_CMD);//--set start line address OLED_WR_Byte(0xB0,OLED_CMD);//--set page address OLED_WR_Byte(0x81,OLED_CMD); // contract control OLED_WR_Byte(0xFF,OLED_CMD);//--128 OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverse OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64) OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 duty OLED_WR_Byte(0xC8,OLED_CMD);//Com scan direction OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset OLED_WR_Byte(0x00,OLED_CMD);// OLED_WR_Byte(0xD5,OLED_CMD);//set osc division OLED_WR_Byte(0x80,OLED_CMD);// OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode off OLED_WR_Byte(0x05,OLED_CMD);// OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge Period OLED_WR_Byte(0xF1,OLED_CMD);// OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartion OLED_WR_Byte(0x12,OLED_CMD);// OLED_WR_Byte(0xDB,OLED_CMD);//set Vcomh OLED_WR_Byte(0x30,OLED_CMD);// OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enable OLED_WR_Byte(0x14,OLED_CMD);// OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel }

oled.h

复制

#ifndef __OLED_H #define __OLED_H #include "sys.h" #include "stdlib.h" #define OLED_MODE 0 #define SIZE 8 #define XLevelL 0x00 #define XLevelH 0x10 #define Max_Column 128 #define Max_Row 64 #define Brightness 0xFF #define X_WIDTH 128 #define Y_WIDTH 64 //-----------------OLED IIC端口定义---------------- #define OLED_SCLK_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_5)//SCL #define OLED_SCLK_Set() GPIO_SetBits(GPIOA,GPIO_Pin_5) #define OLED_SDIN_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_7)//SDA #define OLED_SDIN_Set() GPIO_SetBits(GPIOA,GPIO_Pin_7) #define OLED_CMD 0 //写命令 #define OLED_DATA 1 //写数据 //OLED控制用函数 void OLED_WR_Byte(unsigned dat,unsigned cmd); void OLED_Display_On(void); void OLED_Display_Off(void); void OLED_Init(void); void OLED_Clear(void); void OLED_DrawPoint(u8 x,u8 y,u8 t); void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot); void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size); void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size); void OLED_ShowString(u8 x,u8 y, u8 *p,u8 Char_Size); void OLED_Set_Pos(unsigned char x, unsigned char y); void OLED_ShowCHinese(u8 x,u8 y,u8 no); void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[]); void Delay_50ms(unsigned int Del_50ms); void Delay_1ms(unsigned int Del_1ms); void fill_picture(unsigned char fill_Data); void Picture(); void IIC_Start(); void IIC_Stop(); void Write_IIC_Command(unsigned char IIC_Command); void Write_IIC_Data(unsigned char IIC_Data); void Write_IIC_Byte(unsigned char IIC_Byte); void IIC_Wait_Ack(); #endif

这里只有一点需要说明,就是Write_IIC_Byte(0x78); 写IIC地址,改地址一般默认0x78,该地址是可以通过屏幕背面的电阻修改的。

DHT11温湿度模块DHT11模块为单总线通信,一根数据线即可完成数据的交互,MCU发送数据请求后,等待模块回传数据即可,一次通讯的时间是4ms左右,速度较慢,所以只适合一般的引用场景,一次完整的数据是40bit,数据格式如下:

数据格式:8bit湿度整数数据 8bit湿度小数数据

8bi温度整数数据 8bit温度小数数据 8bit校验和数据传送正确时校验和数据等于“8bit湿度整数数据 8bit湿度小数数据 8bi温度整数数据 8bit温度小数数据”所得结果的末8位。

用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集, 用户可选择读取部分数据.从模式下,DHT11接收到开始信号触发一次温湿度采集, 如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集.采集数据后转换到低速模式。

总线空闲状态为高电平,主机把总线拉低等待DHT11响应,主机把总线拉低必须大于18毫秒,保证DHT11能检测到起始信号。 DHT11接收到主机的开始信号后,等待主机开始信号结束,然后发送80us低电平响应信号.主机发送开始信号结束后,延时等待20-40us后, 读取DHT11的响应信号,主机发送开始信号后,可以切换到输入模式,或者输出高电平均可, 总线由上拉电阻拉高。根据以上信息,我们便可编写DTH11的代码:该模块在正点原子的例程中也有,但是正点原子使用的是GPIO的位带操作,在使用MM32L073时候无法使用,M0内核好像没有位带操作,只能使用伪位带操作或者直接用函数来修改GPIO的输入输出。因为我之前在MM32L073玩了两天,所以这里也不采用位带操作,直接函数控制。DHT11.C

复制

#include "dht11.h" #include "delay.h" void DHT11_IO_IN(void)//温湿度模块输入函数 { GPIO_InitTypeDef GPIO_InitStructure; //RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB,ENABLE); GPIO_InitStructure.GPIO_Pin=IO_DHT11; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; GPIO_Init(GPIO_DHT11,&GPIO_InitStructure); } void DHT11_IO_OUT(void)//温湿度模块输出函数 { GPIO_InitTypeDef GPIO_InitStructure; // RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB,ENABLE); GPIO_InitStructure.GPIO_Pin=IO_DHT11; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; GPIO_Init(GPIO_DHT11,&GPIO_InitStructure); } //复位DHT11 void DHT11_Rst(void) { DHT11_IO_OUT(); //SET OUTPUT DHT11_DQ_Low; //DQ=0 delay_ms(20); //拉低至少18ms DHT11_DQ_High; //DQ=1 delay_us(30); //主机拉高20~40us } //等待DHT11的回应 //返回1:未检测到DHT11的存在 //返回0:存在 u8 DHT11_Check(void) { u8 retry=0;//定义临时变量 DHT11_IO_IN();//SET INPUT while ((GPIO_ReadInputDataBit(GPIO_DHT11,IO_DHT11)==1)&&retry<100)//DHT11会拉低40~80us { retry ; delay_us(1); }; if(retry>=100)return 1; else retry=0; while ((GPIO_ReadInputDataBit(GPIO_DHT11,IO_DHT11)==0)&&retry<100)//DHT11拉低后会再次拉高40~80us { retry ; delay_us(1); }; if(retry>=100)return 1; return 0; } //从DHT11读取一个位 //返回值:1/0 u8 DHT11_Read_Bit(void) { u8 retry=0; while((GPIO_ReadInputDataBit(GPIO_DHT11,IO_DHT11)==1)&&retry<100)//等待变为低电平 { retry ; delay_us(1); } retry=0; while((GPIO_ReadInputDataBit(GPIO_DHT11,IO_DHT11)==0)&&retry<100)//等待变高电平 { retry ; delay_us(1); } delay_us(40);//等待40us if(GPIO_ReadInputDataBit(GPIO_DHT11,IO_DHT11)==1) return 1; else return 0; } //从DHT11读取一个字节 //返回值:读到的数据 u8 dat; u8 DHT11_Read_Byte(void) { u8 i; dat=0; for (i=0;i<8;i ) { dat<<=1; dat|=DHT11_Read_Bit(); } return dat; } //从DHT11读取一次数据 //temp:温度值(范围:0~50°) //humi:湿度值(范围:20%~90%) //返回值:0,正常;1,读取失败 u8 buf[5]; u8 DHT11_Read_Data(u8 *temp,u8 *humi) { u8 i; DHT11_Rst(); if(DHT11_Check()==0) { for(i=0;i<5;i )//读取40位数据 { buf[i]=DHT11_Read_Byte(); } if((buf[0] buf[1] buf[2] buf[3])==buf[4]) { *humi=buf[0]; *temp=buf[2]; } }else return 1; return 0; } //初始化DHT11的IO口 DQ 同时检测DHT11的存在 //返回1:不存在 //返回0:存在 u8 DHT11_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitStructure.GPIO_Pin=IO_DHT11; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; GPIO_Init(GPIO_DHT11,&GPIO_InitStructure); GPIO_SetBits(GPIO_DHT11,IO_DHT11); DHT11_Rst(); //复位DHT11 return DHT11_Check();//等待DHT11的回应 } /*****************************************************************/

dht11.h

复制

#ifndef __DHT11_H__ #define __DHT11_H__ #include "sys.h" #define IO_DHT11 GPIO_Pin_12 //引入中间变量,方便移植 #define GPIO_DHT11 GPIOB //引入中间变量,方便移植 #define DHT11_DQ_High GPIO_SetBits(GPIO_DHT11,IO_DHT11) #define DHT11_DQ_Low GPIO_ResetBits(GPIO_DHT11,IO_DHT11) void DHT11_IO_OUT(void);//温湿度模块输出函数 void DHT11_IO_IN(void); //温湿度模块输入函数 u8 DHT11_Init(void); //初始化DHT11 u8 DHT11_Read_Data(u8 *temp,u8 *humi);//读取温湿度 u8 DHT11_Read_Byte(void); //读出一个字节 u8 DHT11_Read_Bit(void); //读出一个位 u8 DHT11_Check(void); //检测是否存在DHT11 void DHT11_Rst(void); //复位DHT11 #endif

SHARP GP2Y1051AU0F粉尘传感器

粉尘传感器中有LED光源和光敏检测,当空气中含有粉尘时会使光发生散射,光敏元件会检测到这些散射的光而输出不同的电压,粉尘浓度的不同输出的电压也不同,该模块直接由串口输出,直接读取串口的数据,提取电压值,计算后便可获得粉尘浓度值。

串口输出参数:

波特率:2400 bit/s

数据发送格式:

起始位

Vout(H)

Vout(L)

Vref(H)

Vref(L)

校验位

结束位

0xAA

如:0x01

如:0x3A

如:0x00

如:0x7A

如:0xD0

0xFF

数据处理:

Vout = (Vout(H)*256 Vout(L))/1024*5

粉尘浓度计算:Ud= A*Vout,A为比例系数,一般用800。

注意:模块数据不是按照数据包输出,也不需要MCU发送指令,因此只需接模块的TX,RX悬空便可,仅仅是10ms输出一个字节,一共7个字节,结束位输出完成后,下一个10ms到来,继续输出下一个起始位,所以在接收数据时不能按照整包接收的方式,要逐个接收并判断起始位。

moc3020如何测量好坏(MM32F103空气质量检测仪)(2)

我采用开启串口接收中断,每一次触发中断接收数据后都做数据的判断处理:

复制

void UART1_IRQHandler(void) //串口1中断服务程序 { u8 Res; if(UART_GetITStatus(UART1, UART_IT_RXIEN) != RESET) { //接收中断(接收到的数据必须是0x0d 0x0a结尾) UART_ClearITPendingBit(UART1, UART_IT_RXIEN); Res = UART_ReceiveData(UART1); //读取接收到的数据 PM2_5_DATA_COUNT(Res); } }

接收到一位数据后进入数据处理函数PM2_5_DATA_COUNT();

复制

float vout; void PM2_5_DATA_COUNT(u8 dat) { u16 sum = 0;//用于计算校验和 if(dat==170)//判断起始位 ,起始位固定位0xAA=170, { j = 0; DST_Buffer[j] = dat; } else{ j=j 1; DST_Buffer[j] = dat; if(j==6) { sum = DST_Buffer[1] DST_Buffer[2] DST_Buffer[3] DST_Buffer[4]; if(sum==DST_Buffer[5]&&DST_Buffer[6]==0xFF) { vout = (float)((DST_Buffer[1]*256 DST_Buffer[2])); vout = vout/(1024*5); PM2_5 = 1000*vout; } } } }

其中vout变量可以定义为局部变量,这里我为了调试拿了出来,但是肯定要定义成浮点型,因为计算的电压是小数,此函数是为了找到起始位,并将数据按照数据格式排列在数组中,这样方便最后取数据去计算。我在一开始使用的是DMA的接收方式,数据会错乱摆放,导致寻找数据很麻烦,后面才换成串口中断的方式。

如果想要提高精度可将多次测量进行平均,我因为太懒,不搞了。

moc3020如何测量好坏(MM32F103空气质量检测仪)(3)

主函数:

复制

int main(void) { u8 wd=0; u8 sd=0; delay_init(); DHT11_Init(); uart_initwBaudRate(2400); OLED_Init(); //初始化OLED OLED_Clear(); while(1) { OLED_ShowString(0,0,"PM2.5:",16); OLED_ShowString(0,3,"temp:",16); OLED_ShowString(0,6,"humi:",16); OLED_ShowString(85,0,"ug/m3",16); OLED_ShowString(85,3,"C",16); OLED_ShowString(85,6,"%RH",16); OLED_ShowNum(45,0,PM2_5,3,16); DHT11_Read_Data(&wd,&sd);//读取温湿度值 OLED_ShowNum(50,3,wd,3,16); OLED_ShowNum(50,6,sd,3,16); } }

因为PM2.5是中断处理的,主函数中添加一个温湿度的读取,所有的数据打印在OLED便可。最后看一下我的实物图。

moc3020如何测量好坏(MM32F103空气质量检测仪)(4)

动态图展示了空气质量变化的动态效果,因为室内不能用烟雾,测试用的电子烟,实际上不属于粉尘,所以数值变化不明显。

此文到此便结束了,因为只是瞎倒腾,所以使用的模块都是比较粗糙的,外壳也没有,我的3D打印机还是没钱买,凑活过吧。

简单效果,很多人都会,不喜勿喷。

原标题:【瞎折腾系列】MM32F103空气质量检测仪

原作者:呐咯密密

本文为21ic有奖征文作品,详情请见21ic论坛活动专区:第二届万元红包——蓝V达人有奖征文活动,如果您也有兴趣参与征文,欢迎进入论坛参与活动~

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页