资讯详情

【瞎折腾系列】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最小系统,花了70多大洋,涨价真的离谱! 很多啰嗦,终于可以进入正文了。 本文共有三个模块: OLED模块,使用模拟IIC驱动。 DHT11温湿度模块,IO口读写操作。 夏普GP2Y10粉尘传感器,UART操作。 OLED驱动 oled模块手里有两块7针0.96英寸的屏幕,但在测试中不容易使用,无论如何也不能点亮,所以在购买最小系统板的同时也买了4针oled 0.96屏,今天的测试也不能用,换了几种方法,拿了stm32板,用例程测试,都不行,最后发现杜邦线断了,内部断了,外观看不见。我真是一言难尽 这里介绍这个屏幕就不啰嗦了,网上到处都是,这里分享一下我的驱动。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))%10;

                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到来,继续输出下一个起始位,所以在接收数据时不能按照整包接收的方式,要逐个接收并判断起始位。 我采用开启串口接收中断,每一次触发中断接收数据后都做数据的判断处理: 复制 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的接收方式,数据会错乱摆放,导致寻找数据很麻烦,后面才换成串口中断的方式。 如果想要提高精度可将多次测量进行平均,我因为太懒,不搞了。 主函数: 复制 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便可。最后看一下我的实物图。

动态图展示了空气质量变化的动态效果,因为室内不能用烟雾,测试用的电子烟,实际上不属于粉尘,所以数值变化不明显。 此文到此便结束了,因为只是瞎倒腾,所以使用的模块都是比较粗糙的,外壳也没有,我的3D打印机还是没钱买,凑活过吧。 简单效果,很多人都会,不喜勿喷。 --------------------- 作者:呐咯密密 链接:https://bbs.21ic.com/icview-3150216-1-1.html 来源:21ic.com 此文章来自于21ic网站,著作权归21ic所有,未经允许禁止转载。

标签: f6光敏传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台