嵌入式室内环境参数监控系统设计

  • Post author:
  • Post category:其他



一、设计要求

本次课设要求学生们掌握四个方面,第一是文献检索及实验研究方法,第二是室内环境监控系统一般组成; 第三是掌握技术方案比选方法;最后是掌握STM32 ADC使用,同时本次课设需要做到以下几个方面:

  1. 了解国内室内环境参数监控系统组成及技术架构;
  2. 设计室内环境参数监控系统,要求至少采集温度及光照,完成环境参数传感器选型;
  3. 基于开发板或其他32位微控制器及所选用的传感器设计一个室内环境参数采集终端,实现至少一路温度、光照强度数据采集;
  4. 设置温度和光照门限,当温度高于门限值时蜂鸣器报警,当光照强度高于及低于门限值时控制LED灯亮度以实现光照调节(也可自行设计亮度调节方式)。
  5. 将采集到的温度和光照强度数据显示到LCD屏上;




二、设计原理



  1. 硬件设计
  1. 采用STM32F103ZET6单片机作为主控芯片,具有较强的处理能力,且具有低功耗、低成本等特点,可以实现室内环境参数监控的多种复杂操作;
  2. 采用DHT11温湿度传感器,可以实时检测室内温度和湿度;
  3. 采用光强传感器,可以检测室内光照强度参数;
  4. 采用LCD显示屏,可以显示室内环境参数的实时数据;
  5. 采用LED灯,可以模拟补光操作;
  6. 采用蜂鸣器,可以实现报警功能。
  1. 软件设计
  1. 采用C语言编写程序,实现对传感器参数的采集、处理以及参数的显示;
  2. 采用报警功能,当室内温度参数超出设定范围时,可以发出警报信号;
  3. 采用室内光照强度实时调节,根据室内光照强度参数实时调节以达到适合工作、学习的平衡状态。




三、设计方案



1硬件设计

1.1硬件系统框图

1.2主控芯片模块(STM32F103ZET6)

STM32F103ZET6芯片的主要特性如下:

  1. 集成了32位的ARM Cortex-M3内核,最高工作频率可达72MHZ,计算能力    为1.25DMIPS,具有单周期乘法指令和硬件除法器;
  2. 具有512KB片内Flash存储器和64KB片内片内SARM存储器;
  3. 内部集成了8MHZ晶体振荡电路器,可外接416MHZ的时钟源;
  4. 2.0-3.0V单一供电源,具有上电复位功能;
  5. 具有睡眠、停止、待机三种低功耗工作模式;
  6. 144个引脚LQFP封装;
  7. 内部集成了11个定时器:4个16位通用定时器,2个16位定时器可产生的PWM波控制电机的定时器,2个16位的可驱动DAC的定时器,2个加窗口的看门狗定时器和1 个24位的系统节拍定时器;
  8. 2个12位的DAC和3个12位的ADC;
  9. 集成了内部温度传感和实时时钟RTC;
  10. 具有112根高速通用输入输出口,可从其中任选16根作为外部中断输入口,几乎全部GPIO口可承受5V输入(PA0~PA7,PB0~PB1,PC0~PC5,PC13~PC15和PF6~PF10除外);
  11. 集成了13个外部通信接口:2个12C、3个SPI、1个CAN、5个UART、5个UART、1个USB2.0设备和1个并行SDIO;
  12. 具有12通道的DMA控制器,支持定时器、ADC、DAC、SDIO、12S、SPI、12C和UART外设;
  13. 具有96位的全球唯一编号;
  14. 工作温度-40~85℃;

1.3电源电路模块

1.4复位电路模块

可实现上电复位和按键复位,开发板接通电源时,复位持续时间由R2电阻值C10容值乘积决定,一般情况下电阻值取10k电容值取10uf可以满足复位要求,按钮RESET可以实现按键复位,当需要复位时按下RESET按钮,RESET引脚直接接地,CPU进入复位状态。

1.5时钟电路模块


从上图可以看出三种不同的时钟源可被用来驱动系统时钟(SYSCLK):


(1)HSI振荡器时钟


(2)HSE振荡器时钟


(3)PLL时钟


这些设备有以下2种二级时钟源:


(1)40kHz低速内部RC,可以用于驱动独立看门狗和通过程序选择驱动RTC。RTC用于从停机/ 待机模式下自动唤醒系统。


(2)32.768kHz低速外部晶体也可用来通过程序选择驱动RTC(RTCCLK)。


当不被使用时,任一个时钟源都可被独立地启动或关闭,由此优化系统功耗。


1.6

DHT11温湿度传感器模块

DHT11是一款温湿度一体化的数字传感器。该传感器包括一个电容式测湿元件和一个 NTC测温元件,并与一个高性能 8 位单片机相连接。通过单片机等微处理器简单的电路连接就能够实时的采集本地湿度和温度。DHT11与单片机之间能采用简单的单总线进行通信,仅仅需要一个I/O口。传感器内部湿度和温度数据40Bit的数据一次性传给单片机,数据采用校验和方式进行校验,有效的保证数据传输的准确性。DHT11功耗很低,5V 源电压下,工作平均最大电流0.5mA。

1.7光强传感器模块

图中,LSI为光敏二极管,外观看起来与贴片LED类似(位于OLED插座旁边,LSI)R34为其提供反向电压,当环境光线发生变化时,LSI两端的电压也会随之改变,通过ADC3_IN6通道获取LIGHT_SENSOR(PF8)上面的电压,即可得到环境光线的强弱,光线越强,电压越低,光线越弱,电压越高。

1.8TFT_LCD显示模块

FTLCD 模块采用 2*17 的 2.54 公排针与外部连接,即图中 TFT_LCD 部分。从图 1.8.1可以看出,正点原子 TFTLCD 模块采用 16 位的并口方式与外部连接。该模块与显示功能的相关信号线如表 1.8.2:

1.9LED照明补光模块

1.10蜂鸣器报警模块

开发板配置了一个无源蜂鸣器BEEP作为系统报警使用,由PNP三极管Q1控制其导通或关闭,加两个电阻实现当管脚输出3.3V电平时才能发生,且防止由于外部干扰导致的误发声。

  1. 软件设计

    1. DHT11传感器温湿度采集

      1. 程序设计

DHT11实验中使用的是单总线协议,用到的是库函数中 GPIO 相关函数。

DHT11配置步骤 :

(1)使能DHT11数据线对应的GPIO时钟。 本实验中DHT11的数据线引脚是PG11,因此需要先使能 GPIOG 的时钟;

(2)设置对应 GPIO 工作模式(开漏输出),本实验 GPIO 使用开漏输出模式;

(3)参考单总线协议,编写信号代码:

  1. DHT11开始发送数据流程

主机发送开始信号后,延时等待 20us-40us 后读取 DH11T 的回应信号,读取总线为低电平,说明 DHT11 发送响应信号,DHT11 发送响应信号后,再把总线拉高,准备发送数据,每一 bit 数据都以低电平开始,格式见下面图示。如果读取响应信号为高电平,则 DHT11 没有响应,请检查线路是否连接正常。

主机复位信号和DHT11响应信号

(4)编写 DHT11的读函数 ,基于读1bit数据的基础上,编写 DHT11 读1字节函数。

(5)编写DHT11获取温度函数, 参考 DHT11典型温湿度读取过程,编写获取温湿度函数。

2.1.2程序流程图

本实验开机的时候先检测是否有 DHT1 1存在,如果没有,则提示错误。只有在检测到 DHT11之后才开始读取温湿度值并显示在 LCD 上,如果发现了 DHT1 1,则程序每隔 100ms 左右读取一次数据,并把温湿度显示在 LCD 上。 LED0 闪烁用于提示程序正在运行。

2.1.3程序代码

//复位DHT11
void DHT11_Rst(void)	   
{                 
	DHT11_IO_OUT(); 	//SET OUTPUT
    DHT11_DQ_OUT=0; 	//拉低DQ
    delay_ms(20);    	//拉低至少18ms
    DHT11_DQ_OUT=1; 	//DQ=1 
	delay_us(30);     	//主机拉高20~40us
}
//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
u8 DHT11_Check(void) 	   
{   
	u8 retry=0;
	DHT11_IO_IN();//SET INPUT	 
    while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us
	{
		retry++;
		delay_us(1);
	};	 
	if(retry>=100)return 1;
	else retry=0;
    while (!DHT11_DQ_IN&&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(DHT11_DQ_IN&&retry<100)//等待变为低电平
	{
		retry++;
		delay_us(1);
	}
	retry=0;
	while(!DHT11_DQ_IN&&retry<100)//等待变高电平
	{
		retry++;
		delay_us(1);
	}
	delay_us(40);//等待40us
	if(DHT11_DQ_IN)return 1;
	else return 0;		   
}
//从DHT11读取一个字节
//返回值:读到的数据
u8 DHT11_Read_Byte(void)    
{        
    u8 i,dat;
    dat=0;
	for (i=0;i<8;i++) 
	{
   		dat<<=1; 
	    dat|=DHT11_Read_Bit();
    }						    
    return dat;
}
//从DHT11读取一次数据
//temp:温度值(范围:0~50°),humi:湿度值(范围:20%~90%)
u8 DHT11_Read_Data(u8 *temp,u8 *humi)    
{        
 	u8 buf[5];
	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的存在  	 
u8 DHT11_Init(void)
{	 
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);	 //使能PG端口时钟
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;				 //PG11端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOG, &GPIO_InitStructure);				 //初始化IO口
 	GPIO_SetBits(GPIOG,GPIO_Pin_11);						 //PG11 输出高	    
	DHT11_Rst();  //复位DHT11
	return DHT11_Check();//等待DHT11的回应
}

  1. 2光照强度采集

    1. 1程序设计

通过 ADC3_CH6通道读取 LIGHT_SENSOR(PF8)上面的电压,即可得到环境光线的强弱。光线越强,电压越低,光线越暗,电压越高。

  1. 1.2

    1. 程序流程图

    通过 ADC3 的通道 6(PF8)读取光敏传感器(LS1)的电压值,并转换为 0~100 的光线强度值,显示在 LCD 模块上面。光线越亮,值越大;光线越暗,值越小。大家可以用手指遮挡 LS1和用手电筒照射 LS1,来查看光强变化。 LED0 闪烁用于提示程序正在运行。

  2. .2.3程序代码

    //初始化光敏传感器
    void Lsens_Init(void)
    {
      GPIO_InitTypeDef GPIO_InitStructure;
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);//使能PORTF时钟	
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;//PF8 anolog输入
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;		//模拟输入引脚
    GPIO_Init(GPIOF, &GPIO_InitStructure);	
    	Adc3_Init();
    }
    //读取Light Sens的值
    //0~100:0,最暗;100,最亮 
    u8 Lsens_Get_Val(void)
    {
    	u32 temp_val=0;
    	u8 t;//读取次数
    	for(t=0;t<LSENS_READ_TIMES;t++)
    	{
    		temp_val+=Get_Adc3(LSENS_ADC_CHX);	//读取ADC值
    		delay_ms(5);
    	}
    	temp_val/=LSENS_READ_TIMES;//得到平均值 
    	if(temp_val>4000)temp_val=4000;
    	return (u8)(100-(temp_val/40));
    }

  3. ADC模数转换

    1. 程序设计
    2. ADC模块采集的是光敏电阻两端的电压值,电压属于模拟信息需转化为数字信息,只有这样才能的处理数据从而完成光照强度的监控。
  4. 代码

    //初始化ADC3
    //这里我们仅以规则通道为例
    //我们默认仅开启通道6																
    void  Adc3_Init(void)
    {      
    	ADC_InitTypeDef ADC_InitStructure; 
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3	, ENABLE );	  //使能ADC3通道时钟
        RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3,ENABLE);//ADC复位
    RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3,DISABLE);//复位结束	
        
    	ADC_DeInit(ADC3);  //复位ADC3,将外设 ADC3的全部寄存器重设为缺省值
    	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	//ADC工作模式: 独立模式
    	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(ADC3, &ADC_InitStructure);	//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器  
    	ADC_Cmd(ADC3, ENABLE);	//使能指定的ADC3
    	ADC_ResetCalibration(ADC3);	//使能复位校准  
    	while(ADC_GetResetCalibrationStatus(ADC3));	//等待复位校准结束
    	ADC_StartCalibration(ADC3);	 //开启AD校准
    	while(ADC_GetCalibrationStatus(ADC3));	 //等待校准结束
    }		 
    //获得ADC3_ch6通道的值
    u16 Get_Adc3(u8 ch)   
    {
      //设置指定ADC的规则组通道,一个序列,采样时间
    	ADC_RegularChannelConfig(ADC3, ch, 1, ADC_SampleTime_239Cycles5 );	//ADC3,ADC通道,采样时间为239.5周期	  			    
    	ADC_SoftwareStartConvCmd(ADC3, ENABLE);		//使能指定的ADC3的软件转换启动功能	
    	while(!ADC_GetFlagStatus(ADC3, ADC_FLAG_EOC ));//等待转换结束
    	return ADC_GetConversionValue(ADC3);	//返回最近一次ADC3规则组的转换结果
    }

    3.TFT_LCD显示代码

    //初始化lcd
    void LCD_Init(void)
    {
        GPIO_InitTypeDef GPIO_InitStructure;
        FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;
        FSMC_NORSRAMTimingInitTypeDef  readWriteTiming; 
        FSMC_NORSRAMTimingInitTypeDef  writeTiming;
    
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC,ENABLE);   //使能FSMC时钟
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOG,ENABLE); //使能PORTB,D,E,G以及AFIO复用功能时钟
    
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;           //PB0 推挽输出 背光
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    //推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOB, &GPIO_InitStructure);
    
        //PORTD复用推挽输出
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_14|GPIO_Pin_15;    //PORTD复用推挽输出
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;     //复用推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOD, &GPIO_InitStructure); 
         
        //PORTE复用推挽输出
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; //PORTE复用推挽输出
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;     //复用推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOE, &GPIO_InitStructure);
    
        //PORTG12复用推挽输出 P0RTG0-->RS
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_12;   //PORTD复用推挽输出
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;         //复用推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOG, &GPIO_InitStructure);
    //LCD开启显示
    void LCD_DisplayOn(void)
    {
        if (lcddev.id == 0X5510)    //5510开启显示指令
        {
            LCD_WR_REG(0X2900);     //开启显示
        }
        else                        //9341/5310/1963/7789 等发送开启显示指令
        {
            LCD_WR_REG(0X29);       //开启显示
        }
    }
    
    //LCD关闭显示
    void LCD_DisplayOff(void)
    {
        if (lcddev.id == 0X5510)    //5510关闭显示指令
        {
            LCD_WR_REG(0X2800);     //关闭显示
        }
        else                        //9341/5310/1963/7789 等发送关闭显示指令
        {
            LCD_WR_REG(0X28);       //关闭显示
        }
    }
    
    //清屏函数
    //color:要清屏的填充色
    void LCD_Clear(u16 color)
    {
        u32 index = 0;
        u32 totalpoint = lcddev.width;
        totalpoint *= lcddev.height;    //得到总点数
        LCD_SetCursor(0x00, 0x0000);    //设置光标位置
        LCD_WriteRAM_Prepare();         //开始写入GRAM
        for (index = 0; index < totalpoint; index++)
        {
            LCD->LCD_RAM=color;
        }
    }
    //在指定区域内填充指定颜色
    //区域大小:(xend-xsta+1)*(yend-ysta+1)
    //xsta
    //color:要填充的颜色
    void LCD_Fill(u16 sx, u16 sy, u16 ex, u16 ey, u16 color)
    {
        u16 i, j;
        u16 xlen = 0;
        xlen = ex - sx + 1;
        for (i = sy; i <= ey; i++)
        {
            LCD_SetCursor(sx, i);       //设置光标位置
            LCD_WriteRAM_Prepare();     //开始写入GRAM
            for (j = 0; j < xlen; j++)
            {
                LCD->LCD_RAM=color;     //设置光标位置
            }
        }
    }
    
    //在指定区域内填充指定颜色块
    //(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
    //color:要填充的颜色
    void LCD_Color_Fill(u16 sx, u16 sy, u16 ex, u16 ey, u16 *color)
    {
        u16 height, width;
        u16 i, j;
        width = ex - sx + 1;            //得到填充的宽度
        height = ey - sy + 1;           //高度
        for (i = 0; i < height; i++)
        {
            LCD_SetCursor(sx, sy + i);  //设置光标位置
            LCD_WriteRAM_Prepare();     //开始写入GRAM
            for (j = 0; j < width; j++)
            {
                LCD->LCD_RAM=color[i * width + j];  //写入数据
            }
        }
    }
    
    //在指定位置显示一个字符
    //x,y:起始坐标
    //num:要显示的字符:" "--->"~"
    //size:字体大小 12/16/24
    //mode:叠加方式(1)还是非叠加方式(0)
    void LCD_ShowChar(u16 x, u16 y, u8 num, u8 size, u8 mode)
    {
        u8 temp, t1, t;
        u16 y0 = y;
        u8 csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size / 2);  //得到字体一个字符对应点阵集所占的字节数
        num = num - ' ';    //得到偏移后的值(ASCII字库是从空格开始取模,所以-' '就是对应字符的字库)
        for (t = 0; t < csize; t++)
        {
            if (size == 12)temp = asc2_1206[num][t];        //调用1206字体
            else if (size == 16)temp = asc2_1608[num][t];   //调用1608字体
            else if (size == 24)temp = asc2_2412[num][t];   //调用2412字体
            else return;                                    //没有的字库
            for (t1 = 0; t1 < 8; t1++)
            {
                if (temp & 0x80)LCD_Fast_DrawPoint(x, y, POINT_COLOR);
                else if (mode == 0)LCD_Fast_DrawPoint(x, y, BACK_COLOR);
                temp <<= 1;
                y++;
                if (y >= lcddev.height)return;      //超区域了
                if ((y - y0) == size)
                {
                    y = y0;
                    x++;
                    if (x >= lcddev.width)return;   //超区域了
    
                    break;
                }
            }
        }
    }
    
    //m^n函数
    //返回值:m^n次方.
    u32 LCD_Pow(u8 m, u8 n)
    {
        u32 result = 1;
        while (n--)result *= m;
        return result;
    }
    //显示数字,高位为0,则不显示
    //x,y :起点坐标
    //len :数字的位数
    //size:字体大小
    //color:颜色
    //num:数值(0~4294967295);
    void LCD_ShowNum(u16 x, u16 y, u32 num, u8 len, u8 size)
    {
        u8 t, temp;
        u8 enshow = 0;
        for (t = 0; t < len; t++)
        {
            temp = (num / LCD_Pow(10, len - t - 1)) % 10;
            if (enshow == 0 && t < (len - 1))
            {
                if (temp == 0)
                {
                    LCD_ShowChar(x + (size / 2)*t, y, ' ', size, 0);
                    continue;
                }
                else enshow = 1;
            }
            LCD_ShowChar(x + (size / 2)*t, y, temp + '0', size, 0);
        }
    }
    
    //显示数字,高位为0,还是显示
    //x,y:起点坐标
    //num:数值(0~999999999);
    //len:长度(即要显示的位数)
    //size:字体大小
    //mode:
    //[7]:0,不填充;1,填充0.
    //[6:1]:保留
    //[0]:0,非叠加显示;1,叠加显示.
    void LCD_ShowxNum(u16 x, u16 y, u32 num, u8 len, u8 size, u8 mode)
    {
        u8 t, temp;
        u8 enshow = 0;
        for (t = 0; t < len; t++)
        {
            temp = (num / LCD_Pow(10, len - t - 1)) % 10;
            if (enshow == 0 && t < (len - 1))
            {
                if (temp == 0)
                {
                    if (mode & 0X80)LCD_ShowChar(x + (size / 2)*t, y, '0', size, mode & 0X01);
                    else LCD_ShowChar(x + (size / 2)*t, y, ' ', size, mode & 0X01);
                    continue;
                }
                else enshow = 1;
            }
            LCD_ShowChar(x + (size / 2)*t, y, temp + '0', size, mode & 0X01);
        }
    }
    
    //显示字符串
    //x,y:起点坐标
    //width,height:区域大小
    //size:字体大小
    //*p:字符串起始地址
    void LCD_ShowString(u16 x, u16 y, u16 width, u16 height, u8 size, u8 *p)
    {
        u8 x0 = x;
        width += x;
        height += y;
        while ((*p <= '~') && (*p >= ' '))   //判断是不是非法字符!
        {
            if (x >= width)
            {
                x = x0;
                y += size;
            }
            if (y >= height)break; //退出
            LCD_ShowChar(x, y, *p, size, 0);
            x += size / 2;
            p++;
        }
    }
    
    

    2.5蜂鸣器报警代码

    void BEEP_Init(void)//BEEP初始化
    {
    
      GPIO_InitTypeDef  GPIO_InitStructure;
    
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能PB端口时钟
    	
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;//beep->PB8端口配置
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//端口模式配置
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//端口速率配置
      GPIO_Init(GPIOB,&GPIO_InitStructure);//根据参数初始化GPIOB_Pin8
    
      GPIO_ResetBits(GPIOB,GPIO_Pin_8);//初始时端口配置为低电平
    }
    
    if(temperature>=34)
    {
    BEEP=1;
    }	

    2.6LED照明补光程序代码

    //TIM3 PWM部分初始化 
    //PWM输出初始化
    //arr:自动重装值
    //psc:时钟预分频数
    void TIM3_PWM_Init(u16 arr,u16 psc)
    {  
    	GPIO_InitTypeDef GPIO_InitStructure;
    	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    	TIM_OCInitTypeDef  TIM_OCInitStructure;
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	//使能定时器3时钟
     	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIOE外设和AFIO复用功能模块时钟
       //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形	GPIOA.7
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //TIM_CH2
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
       //初始化TIM3
    	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
    	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 
    	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    	
    	//初始化TIM3 Channel2 PWM模式	 
    	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
     	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
    	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
    	TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC2
    
    	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的预装载寄存器
    	TIM_Cmd(TIM3, ENABLE);  //使能TIM3
    }

    三、主程序

    int main(void)
     {
     	u8 adcx;//光照强度
    	u8 t=0;	
    	u8 temperature;//温度
    	u8 humidity;   //湿度
    	u16 led1pwmval; //PWN输出的占空比
    	delay_init();	    	 //延时函数初始化	  
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
    	uart_init(115200);	 	//串口初始化为115200
    	LED_Init();		  		 //初始化与LED连接的硬件接口
    	BEEP_Init();        //初始化BEEP
      LCD_Init();				 //初始化LCD
    	Lsens_Init(); 		//初始化光敏传感器
    	TIM3_PWM_Init(899,0);	 //不分频。PWM频率=72000000/900=80Khz
    
    	POINT_COLOR=RED;//设置字体为红色
      	
    	//显示提示信息											      
    	LCD_ShowString(30,50,200,16,16,"Based On STM32");	
    	LCD_ShowString(30,70,200,16,16,"DHT11_LSENS MONITORING");	
    	LCD_ShowString(30,90,200,16,16,"Copyright GROUP1");
    	LCD_ShowString(30,110,200,16,16,"2022/12/25");
    
    	while(DHT11_Init())	//DHT11初始化	
    	{
    		LCD_ShowString(30,130,200,16,16,"DHT11 Error");
    
    		delay_ms(200);
    		LCD_Fill(30,130,239,130+16,WHITE);
     		delay_ms(200);
    	}
    	LCD_ShowString(30,130,200,16,16,"DHT11 OK");
    	POINT_COLOR=BLUE;//设置字体为蓝色
    	LCD_ShowString(30,170,200,16,16,"LSENS_VAL:");
      LCD_ShowString(30,190,200,16,16,"Temp:  C");	 
     	LCD_ShowString(30,210,200,16,16,"Humi:  %");		
    	while(1)
    	{
    		adcx=Lsens_Get_Val();
    		LCD_ShowxNum(30+10*8,170,adcx,3,16,0);//显示ADC的值 
    		if(t%10==0)			//每100ms读取一次
    		{									  
    			DHT11_Read_Data(&temperature,&humidity);	//读取温湿度值					    
    			LCD_ShowNum(30+40,190,temperature,2,16);	//显示温度	   		   
    			LCD_ShowNum(30+40,210,humidity,2,16);		//显示湿度	 	   
    		}				   
    	 	delay_ms(10);
    		t++;
    		if(temperature>=34) 
    		{
    			BEEP=1;
    		}	
    		led1pwmval=(100-adcx)/10;//根据光照强度实时调整LED1的亮度
    		TIM_SetCompare2(TIM3,led1pwmval);}
    }

    四、测试

  5. 项目文件编译并下载到单片机后上电,LCD屏上可以清晰显示当前测试环境的温度、湿度和光照强度。


  6. 通过上面两图的对比,未外加灯光时光照强度为7,LED1很亮;当外加灯光时,光照强度为93,LED1微亮或者熄灭。

  7. 测试时室温为14摄氏度,当设置温度报警上限为10摄氏度时,可以听到蜂鸣器发出声音报警。



版权声明:本文为m0_60000199原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。