• 【毕业设计】基于Stm32的家庭气象仪 天气监控系统 - 物联网 单片机 嵌入式



    0 前言

    🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。

    为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是

    🚩 基于PID控制的智能平衡车设计与实现

    🥇学长这里给一个题目综合评分(每项满分5分)

    • 难度系数:4分
    • 工作量:4分
    • 创新点:3分

    🧿 选题指导, 项目分享:

    https://gitee.com/dancheng-senior/project-sharing-1/blob/master/%E6%AF%95%E8%AE%BE%E6%8C%87%E5%AF%BC/README.md


    1 简介

    该智能气象站系统是基于STM32F103C8T6的物联网应用系统。可检测环境中的温度、湿度、空气质量、雨量、PM2. 5等数据上传到0neNET平台,并通过TFT屏显示实时数据。

    2 主要器件

    • STM32F103C8T6核心板
    • 联网模块
    • 温湿度传感器(DHT11)
    • 空气质量传感器(MQ135)
    • 雨滴传感器
    • PM2. 5传感器

    3 实现效果

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    4 设计原理

    4.1 DHT11温湿度传感器

    简介
    在这里插入图片描述
    DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。因此该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点。每个DHT11传感器都在极为精确的湿度校验室中进行校准。校准系数以程序的形式储存在OTP内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。单线制串行接口,使系统集成变得简易快捷。超小的体积、极低的功耗,信号传输距离可达20米以上,使其成为各类应用甚至最为苛刻的应用场合的最佳选则。产品为 4 针单排引脚封装。连接方便,特殊封装形式可根据用户需求而提供。
    接线
    在这里插入图片描述
    DHT11编码步骤

    1. 单片机上点后1s内不读取(不重要)
    2. 主机(单片机)发送起始信号:
      • 主机先拉高data
      • 拉低data延迟18ms
      • 拉高data(通过此操作将单片机引脚设置为输入)。
    3. 从机(DHT11)收到起始信号后进行应答:
      • 从机拉低data,主机读取到data线被拉低持续80us后从机拉高data线, 持续80us,直到高电平结束,意味着主机可以开始接受数据。
    4. 主机开始接收数据:
      • 主机先把data线拉高(io设置为输入)
      • 从机把data线拉低,主机读取data线电平,直到低电平结束(大约50us)
      • 从机拉高data线后,延迟40us左右(28~70us之间)主机再次读取data线电平,如果为低电平,则为“0”,如果为高电平,则为“1”。
      • 继续重复上述1,2步骤累计40次。

    4.2 MQ135空气质量传感器

    简介
    MQ135是一款对氨气、硫化物、苯系蒸汽等均有着高灵敏度的半导体气敏元件。其凭借着成本低、电路结构简单、功耗低等优点被广泛应用于日常生活生产中。
    MQ135气体传感器所使用的气敏材料是在清洁空气中电导率较低的二氧化锡(SnO2)。当传感器所处环境中存在污染气体时,传感器的电导率随空气中污染气体浓度的增加而增大。使用简单的电路即可将电导率的变化转换为与该气体浓度相对应的输出信号。
    在这里插入图片描述
    特点

    • 在较宽的浓度范围内对有害气体有良好的灵敏度;
    • 对氨气、硫化物、苯系等气氛灵敏度高;
    • 长寿命、低成本;
    • 简单的驱动电路即可;

    该传感器有两个输出口

    • DO:输出信号数字,也就是对应单片机的电平高低;
    • AO:输出模拟信号,也就是ADC信号;

    接线
    在这里插入图片描述

    • MQ135模块使用5V进行驱动,DO输出数字信号,AO输出模拟信号。
    • DO输出:就相当于一个开关电源,到了设定的值进行跳转,基本没啥用处。如果需要做一个气体上限报警装置,此时可以使用DO引脚。
    • AO输出:进行模拟量的输入和输出。

    原理图
    在这里插入图片描述
    AOUT作为模拟信号输出引脚,直接将AOUT脚接STM32的AD转换的输入脚,ADC将采集到的模拟信号转换为数字信号。在正常环境中,即:没有被测气体的环境,设定传感器输出电压值为参考电压,这时,AOUT端的电压在1V左右,当传感器检测到被测气体时,电压每升高0.1v,实际被测气体的浓度增加20ppm(简单的说:1ppm=1mg/kg=1mg/l=1×10-6 常用来表示气体浓度,或者溶液浓度。),根据这个参数就可以在单片机里面将测得的模拟量电压值转换为浓度值。
    注: 传感器通电后,需要预热20s左右,测量的数据才稳定,传感器发热属于正常现象,因为内部有电热丝,如果烫手就不正常了。输出浓度和电压关系的比值并非线性,而是趋于线性,所以测量值存在误差。

    4.3

    在这里插入图片描述
    雨滴传感器基本上是一块板,上面以线形形式涂覆镍。它基于抵抗原理。雨水传感器模块允许通过模拟输出引脚测量湿度,当湿度阈值超过时,它可以提供数字输出。

    传感器模块包括一个电位计,LM393比较器,LED,电容器和电阻器。雨板模块由铜轨组成,铜轨用作可变电阻器。它的阻力随雨板上的湿度而变化。

    该模块基于LM393运算放大器。它包括电子模块和“收集”雨滴的印刷电路板。当雨滴积聚在电路板上时,它们会形成并联电阻路径,该路径可通过运算放大器进行测量。

    传感器是一个电阻偶极子,在潮湿时显示较小的电阻,而在干燥时显示较大的电阻。当船上没有雨滴时,它会增加电阻,因此我们根据V = IR获得高电压。

    当出现雨滴时,它会降低电阻,因为水是电的导体,并且水的存在使镍线并联连接,因此降低了电阻并降低了其两端的电压降。

    电路图
    在这里插入图片描述

    5 部分核心代码

    DHT11温湿度传感器部分

    #include "reg52.h"
    #include "LCD1602.h"
    #include "intrins.h"
    
    //typedef unsigned char uchar;
    //typedef unsigned int uint;
    	
    //定义变量
    sbit Data=P3^6;
    uchar rec_dat[13];//用于保存接收到的数据组
    
    void DHT11_delay_us(uchar n)
    {
        while(--n);
    }
    
    void DHT11_delay_ms(uint z)
    {
       uint i,j;
       for(i=z;i>0;i--)
          for(j=110;j>0;j--);
    }
    
    void DHT11_start()
    {
       Data=1;
       DHT11_delay_us(2);
       Data=0;
       DHT11_delay_ms(20);   //延时18ms以上
       Data=1;
       DHT11_delay_us(30);
    }
    
    uchar DHT11_rec_byte()      //接收一个字节
    {
       uchar i,dat=0;
      for(i=0;i<8;i++)    //从高到低依次接收8位数据
       {          
          while(!Data);   //等待50us低电平过去
          DHT11_delay_us(8);     //延时60us,如果还为高则数据为1,否则为0 
          dat<<=1;           //移位使正确接收8位数据,数据为0时直接移位
          if(Data==1)    //数据为1时,使dat加1来接收数据1
             dat+=1;
          while(Data);  //等待数据线拉低    
        }  
        return dat;
    }
    
    void DHT11_receive()      //接收40位的数据
    {
        uchar R_H,R_L,T_H,T_L,RH,RL,TH,TL,revise; 
        DHT11_start();
        if(Data==0)
        {
            while(Data==0);   //等待拉高     
            DHT11_delay_us(40);  //拉高后延时80us
            R_H=DHT11_rec_byte();    //接收湿度高八位  
            R_L=DHT11_rec_byte();    //接收湿度低八位  
            T_H=DHT11_rec_byte();    //接收温度高八位  
            T_L=DHT11_rec_byte();    //接收温度低八位
            revise=DHT11_rec_byte(); //接收校正位
    
            DHT11_delay_us(25);    //结束
    
            if((R_H+R_L+T_H+T_L)==revise)      //校正
            {
                RH=R_H;
                RL=R_L;
                TH=T_H;
                TL=T_L;
            } 
    		
    	
            /*数据处理,方便显示*/
            rec_dat[0]=RH/10+'0';
            rec_dat[1]=(RH%10)+'0';
    		rec_dat[2]='%';
            rec_dat[3]='R';
            rec_dat[4]='H';
            rec_dat[5]=' ';
    		rec_dat[6]=' ';
            rec_dat[7]=(TH/10)+'0';
            rec_dat[8]=(TH%10)+'0';
    		rec_dat[9]='^';
            rec_dat[10]='C';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    MQ135部分

    #include "adc.h"
     #include "delay.h"
    	   
    //初始化ADC
    //这里我们仅以规则通道为例
    //我们默认将开启通道0~3																	   
    void  Adc_Init(void)
    { 	
    	ADC_InitTypeDef ADC_InitStructure; 
    	GPIO_InitTypeDef GPIO_InitStructure;
    
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1	, ENABLE );	  //使能ADC1通道时钟
     
    
    	RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
    
    	//PA1 作为模拟通道输入引脚                         
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;		//模拟输入引脚
    	GPIO_Init(GPIOA, &GPIO_InitStructure);	
    
    	ADC_DeInit(ADC1);  //复位ADC1 
    
    	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	//ADC工作模式:ADC1和ADC2工作在独立模式
    	ADC_InitStructure.ADC_ScanConvMode = DISABLE;	//模数转换工作在单通道模式
    	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;	//模数转换工作在单次转换模式
    	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//转换由软件而不是外部触发启动
    	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//ADC数据右对齐
    	ADC_InitStructure.ADC_NbrOfChannel = 1;	//顺序进行规则转换的ADC通道的数目
    	ADC_Init(ADC1, &ADC_InitStructure);	//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器   
    
      
    	ADC_Cmd(ADC1, ENABLE);	//使能指定的ADC1
    	
    	ADC_ResetCalibration(ADC1);	//使能复位校准  
    	 
    	while(ADC_GetResetCalibrationStatus(ADC1));	//等待复位校准结束
    	
    	ADC_StartCalibration(ADC1);	 //开启AD校准
     
    	while(ADC_GetCalibrationStatus(ADC1));	 //等待校准结束
     
    //	ADC_SoftwareStartConvCmd(ADC1, ENABLE);		//使能指定的ADC1的软件转换启动功能
    
    }				  
    //获得ADC值
    //ch:通道值 0~3
    u16 Get_Adc(u8 ch)   
    {
      	//设置指定ADC的规则组通道,一个序列,采样时间
    	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );	//ADC1,ADC通道,采样时间为239.5周期	  			    
      
    	ADC_SoftwareStartConvCmd(ADC1, ENABLE);		//使能指定的ADC1的软件转换启动功能	
    	 
    	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
    
    	return ADC_GetConversionValue(ADC1);	//返回最近一次ADC1规则组的转换结果
    }
    
    u16 Get_Adc_Average(u8 ch,u8 times)
    {
    	u32 temp_val=0;
    	u8 t;
    	for(t=0;t<times;t++)
    	{
    		temp_val+=Get_Adc(ch);
    		delay_ms(5);
    	}
    	return temp_val/times;
    } 	 
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    雨滴传感器

    //对雨滴传感器的端口进行初始化
    void rain_INIT(void)
    {
    	GPIO_InitTypeDef GPIO_InitStructure;//定义结构体变量
    	
    	RCC_APB2PeriphClockCmd(rain_RCC,ENABLE);	
    	GPIO_InitStructure.GPIO_Pin=rain_PIN;  //选择你要设置的IO口
    	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;	 //设置浮空输入
    	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;	  //设置传输速率
    	GPIO_Init(rain_PORT,&GPIO_InitStructure); 	   /* 初始化GPIO */
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    6 最后

  • 相关阅读:
    SQL必需掌握的100个重要知识点:分组数据
    【MySQL】数据类型和表的约束
    spring事务
    真实场景sql优化持续更新(老司机必备)
    数据库治理利器:动态读写分离
    [bmim][Tf2N]离子液体(IL)负载UiO-66-PEI
    专业制造一体化ERP系统,专注于制造工厂生产管理信息化,可定制-亿发
    FlutterFair低代码开发实践
    Java代码审计-Java的反射机制
    什么?console.log打印出的数据不对?
  • 原文地址:https://blog.csdn.net/m0_71572576/article/details/126554294