项目代码下载:
代码下载 效果如视频中演示
各种模块的混合使用
(1)温度、湿度的测量; DHT11
(2)温度、湿度等参数的显示; LCD屏幕
(3)报警数据的设定(按键); EXIT中断服务函数
(4)温度、湿度控制; 风扇,舵机,加热片模块
(5)数据传输(无线通信传输); ESP8266
(6)声光报警提醒。 LED,蜂鸣器
注:本文在正点原子的ESP8266的代码上进行了一些额外操作来完成一些功能,以便自己更加了解这些模块的套用
模块1:DHT11
DHT11 是一款湿温度一体化的数字传感器。该传感器包括一个电阻式测湿元件和一个 NTC 测温元件,并与一个高性能 8
位单片机相连接。通过单片机等微处理器简单的电路连接就能够 实时的采集本地湿度和温度。DHT11
与单片机之间能采用简单的单总线进行通信,仅仅需要一 个 I/O 口。传感器内部湿度和温度数据 40Bit
的数据一次性传给单片机,数据采用校验和方式 进行校验,有效的保证数据传输的准确性。DHT11 功耗很低,5V 电源电压下,工作平均最大 电流
0.5mA。
也就是如果在低电平持续12-14us后,高电平被拉高26-28us,代表这一位是0。
如果在低电平持续12-14us后,高电平被拉高116-118us,代表这一位是1。
以上便是DHT11的简单介绍,网络上不缺乏DHT11的代码,就不贴出。(正点原子永远的神)
模块2:LCD显示温湿度
TFTLCD的配置,正点原子已经为我们配置好了(ESP8266代码自带屏幕),而要做到显示温度,我们就必须先得到温度,得到温度后通过函数显示在屏幕上。具体操作如下
sprintf((char*)p,"t%s%02dh%s%02dtr%s%02d%02dhr%s%02d%02d\r\n",CHARACTER[0],temperature_t,CHARACTER[0],humidity_t,CHARACTER[0],setval_l,setval_h,CHARACTER[0],setvall_l,setvall_h);//测试数据 CHARACTER[0]是 = 号
Show_Str(30+54,100,400,12,p,12,0);
模块3:报警数值的设定(按键控制)
我们知道既然要对一个数值进行报警设定,那么它可能就是超过了某个数值范围,因此我们需要定义上下限值,且上下限的值可以调节,这就需要用到板子上的四个按键。那么如何通过检测按键来改变范围呢,这就需要用到按键的中断服务函数。
简单介绍,STM32的一部分外设对应着一些中断线,当你配置了这些中断线所对应的IO口,那么当这些外设被检测到触发后,便会触发中断服务函数,并进入其中执行相应的代码,代码结束后便会跳出服务函数继续执行下面的代码。正是这一点使得继续进入了中断服务函数也不会干扰程序的正常运行。代码如下:
#include "exti.h"
#include "delay.h"
#include "led.h"
#include "key.h"
//////////////////////////////////////////////////////////////////////////////////
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F407开发板
//外部中断 驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2014/5/4
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//////////////////////////////////////////////////////////////////////////////////
//外部中断0服务程序
extern u8 setval_h;
extern u8 setval_l;
extern u8 setvall_h;
extern u8 setvall_l;
void EXTI0_IRQHandler(void)
{
delay_ms(10); //消抖
if(WK_UP==1)
{
setvall_h+=2;
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位
}
//外部中断2服务程序
void EXTI2_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY2==0)
{
setvall_l-=2;
}
EXTI_ClearITPendingBit(EXTI_Line2);//清除LINE2上的中断标志位
}
//外部中断3服务程序
void EXTI3_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY1==0)
{
}
EXTI_ClearITPendingBit(EXTI_Line3); //清除LINE3上的中断标志位
}
//外部中断4服务程序
void EXTI4_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY0==0)
{
setval_l+=2;
}
EXTI_ClearITPendingBit(EXTI_Line4);//清除LINE4上的中断标志位
}
//外部中断初始化程序
//初始化PE2~4,PA0为中断输入.
void EXTIX_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
KEY_Init(); //按键对应的IO口初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource2);//PE2 连接到中断线2
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource3);//PE3 连接到中断线3
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource4);//PE4 连接到中断线4
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);//PA0 连接到中断线0
/* 配置EXTI_Line0 */
EXTI_InitStructure.EXTI_Line = EXTI_Line0;//LINE0
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能LINE0
EXTI_Init(&EXTI_InitStructure);//配置
/* 配置EXTI_Line2,3,4 */
EXTI_InitStructure.EXTI_Line = EXTI_Line2 | EXTI_Line3 | EXTI_Line4;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//中断线使能
EXTI_Init(&EXTI_InitStructure);//配置
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//外部中断0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;//抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);//配置
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;//外部中断2
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);//配置
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;//外部中断3
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);//配置
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;//外部中断4
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;//抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);//配置
}
通过检测按键来执行温度上下限的改变。
模块4:温湿度控制
使用风扇降温,使用加热片升温,使用舵机挤压喷壶喷水加湿。
风扇和加热片都非常简单,给个电平便开始工作,主要的是舵机的配置与使用。
舵机接收的是PWM信号,能使舵机内部电路产生一个偏置电压,触发电机通过减速齿轮带动电位器移动,当电压差为零时,电机停转,从而达到伺服的效果。
即,给舵机提供一个特定的PWM信号,舵机就可以旋转到指定的位置。
舵机接收的PWM信号频率为50HZ,即周期为20ms。当高电平的脉宽在0.5ms-2.5ms之间时舵机就可以对应旋转到不同的角度。


为了更好地理解其信号,和编写代码,把PWM关键点转换如下:
PWM信号周期: 20000us
0度时,高电平时长: 500us
180度时, 高电平时长:2500us
每增加1 °,需增加高电平时长:(2500-500)÷180 = 11.1us
某角度值A,需要的总高电平时长:(A x11.1 +500)us
特别地说明: 把所有ms值, 转换为us值, 是为了方便代码的编写和理解.
————————————————
原文链接:https://blog.youkuaiyun.com/zhouml_msn/article/details/116142124
#include "PWM.h"
#include "led.h"
void TIM1_PWM_Init(u32 arr,u32 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE); //TIM1????
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //??PORT??
GPIO_PinAFConfig(GPIOA,GPIO_PinSource8,GPIO_AF_TIM1); //GPIOA8??????1
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_11; //GPIOF9
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //????
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //??100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //??????
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //??
GPIO_Init(GPIOA,&GPIO_InitStructure); //???PA
TIM_TimeBaseStructure.TIM_Period=arr; //??????
TIM_TimeBaseStructure.TIM_Prescaler=psc; //?????
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //??????
TIM_TimeBaseStructure.TIM_ClockDivision=0; //TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseStructure);//??????1
//???TIM14 Channel1 PWM??
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //???????:TIM????????2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //??????
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //????:TIM???????
TIM_OC1Init(TIM1, &TIM_OCInitStructure);//??T??????????TIM1 4OC1
TIM_OC4Init(TIM1, &TIM_OCInitStructure);
TIM_CtrlPWMOutputs(TIM1,ENABLE);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //??TIM1?CCR1????????
TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM1,ENABLE);//ARPE??
TIM_Cmd(TIM1, ENABLE); //??TIM1
int main()
{
TIM1_PWM_Init(200-1,8400-1); //84M/84=1Mhz的计数频率,重装载值500,所以PWM频率为
1M/2000=50hz.
}
TIM_SetCompare1(TIM1,190); //??0?
delay_ms(500);
TIM_SetCompare1(TIM1,185); //????45? (???)
delay_ms(500);
TIM_SetCompare1(TIM1,180); //????90?
delay_ms(500);
TIM_SetCompare1(TIM1,175); //????135?
delay_ms(500);
TIM_SetCompare1(TIM1,170); //????180?
delay_ms(500);
}
PSC:TIM时钟的分频系数:内部时钟经PSC值分频后, 传给CNT计数器使用;
CNT:计数器,CNT每计数一次的脉冲时长为:1÷(CLK÷PSC)
ARR:自动重装载值:CNT计数器经过多少次脉冲就重新开始计数。用这个值可控制需要的PWM信号周期
CCR:用于控制周期内高电平时长, 当CNT<CCR时, 为有效电平. 而有效电平的高低, 则是通过CCER寄存器设置的, 默认的有效电平为高电平.通过TIM_SetCompare设置.
知道了以上这些,假设我们设置CCRx为190,低电平输出,那么低电平时间为190/200*20ms=19ms,高电平为1s。这便是舵机的基础配置。
模块5:ESP8266 WIFI模块
我选择的是使用STA WIFI模式,因此我们进入相应的代码部分


通过再这段代码下面的代码进行修改,便可以做到通过再上位机发送指令来控制相应的外设。
while(1)
{
if(timex==0) //TCP Server
{
DHT11_Read_Data(&temperature_t,&humidity_t); //读取温湿度值
if(setval_l<temperature_t<setval_h||setvall_l<humidity_t<setvall_h) // 20
{
GPIO_ResetBits(GPIOF,GPIO_Pin_9|GPIO_Pin_10);
//GPIO_SetBits(GPIOF,GPIO_Pin_8);//蜂鸣器叫
//delay_ms(2000);
//GPIO_SetBits(GPIOF,GPIO_Pin_8);//亮灯
GPIO_SetBits(GPIOF,GPIO_Pin_8);// 控制蜂鸣器
}
// BEEP_Init();
//GPIO_ResetBits(GPIOF,GPIO_Pin_8); //蜂鸣器对应引脚 GPIOF8 拉低,
//GPIO_ResetBits(GPIOF,GPIO_Pin_9|GPIO_Pin_10);
sprintf((char*)p,"t%s%02dh%s%02dtr%s%02d%02dhr%s%02d%02d\r\n",CHARACTER[0],temperature_t,CHARACTER[0],humidity_t,CHARACTER[0],setval_l,setval_h,CHARACTER[0],setvall_l,setvall_h);//测试数据
Show_Str(30+54,100,400,12,p,12,0);
atk_8266_send_cmd("AT+CIPSEND=0,25","OK",200); //发送指定长度的数据
delay_ms(200);
atk_8266_send_data(p,"OK",100); //发送指定长度的数据
timex=1000;
time=0;
}
//else
// time=time+1;
if(timex)timex--;
if(timex==1)LCD_Fill(30+54,100,239,112,WHITE);
t++;
delay_ms(10);
if(USART3_RX_STA&0X8000) //接收到一次数据了
{
rlen=USART3_RX_STA&0X7FFF; //得到本次接收到的数据长度
USART3_RX_BUF[rlen]=0; //添加结束符
printf("%s",USART3_RX_BUF); //发送到串口
sprintf((char*)p,"收到%d字节,内容如下",rlen);//接收到的字节数
LCD_Fill(30+54,115,239,130,WHITE);
POINT_COLOR=BRED;
Show_Str(30+54,115,156,12,p,12,0); //显示接收到的数据长度
POINT_COLOR=BLUE;
LCD_Fill(30,130,239,319,WHITE);
Show_Str(30,130,180,190,USART3_RX_BUF,12,0);//显示接收到的数据
//if(strcmp((const char*)(USART3_RX_BUF+11),"0")==0)LED1=0; //打开LED1
//if(strcmp((const char*)(USART3_RX_BUF+11),"1")==0)LED1=1; //关闭LED1
//if(strcmp((const char*)(USART3_RX_BUF+11),"2")==0)LED1=0; //打开LED1
// if(strcmp((const char*)(USART3_RX_BUF+11),"1")==0)LED1=1; //关闭LED1
if(USART3_RX_BUF[11]==0) //0
{
LED0=0; //亮灯
GPIO_SetBits(GPIOF,GPIO_Pin_1);//GPIOF9,F10设置高,灯灭 风扇开启
}
else if(USART3_RX_BUF[11]==1) //1
{
LED0=1; //灭灯
GPIO_ResetBits(GPIOF,GPIO_Pin_1);//GPIOF9,F10设置高,灯灭
}
else if(USART3_RX_BUF[11]==2)
{
LED1=0;
GPIO_ResetBits(GPIOF,GPIO_Pin_3);//GPIOF9,F10设置高,灯灭 开启加热片
}
else if(USART3_RX_BUF[11]==3)
{
LED1=1;
GPIO_SetBits(GPIOF,GPIO_Pin_3);//GPIOF9,F10设置高,灯灭
}
else if(USART3_RX_BUF[11]==4) //等于4喷水
{ TIM_SetCompare1(TIM1,190); //??0?
delay_ms(500);
TIM_SetCompare1(TIM1,185); //????45? (???)
delay_ms(500);
TIM_SetCompare1(TIM1,180); //????90?
delay_ms(500);
TIM_SetCompare1(TIM1,175); //????135?
delay_ms(500);
TIM_SetCompare1(TIM1,170); //????180?
delay_ms(500);
TIM_SetCompare1(TIM1,175); //????45?,??135????
delay_ms(500);
TIM_SetCompare1(TIM1,180); //?????90????
delay_ms(500);
TIM_SetCompare1(TIM1,185); //??45?
delay_ms(500);
TIM_SetCompare1(TIM1,190); //??0?
delay_ms(500);
}
else;
USART3_RX_STA=0;
if(constate!='+')t=1000; //状态为还未连接,立即更新连接状态
else t=0; //状态为已经连接了,10秒后再检查
}
if(t==1000)//连续10秒钟没有收到任何数据,检查连接是不是还存在.
{
// // LCD_Fill(30+54,125,239,130,WHITE);
// LCD_Fill(60,80,120,92,WHITE);
constate=atk_8266_consta_check();//得到连接状态
if(constate=='+')Show_Str(30+30,80,200,12,"连接成功",12,0); //连接状态
else Show_Str(30+30,80,200,12,"连接失败",12,0);
t=0;
}
//if((t%20)==0)LED0=!LED0;
atk_8266_at_response(1);
}
模块6:声光报警提醒(蜂鸣器,LED)
蜂鸣器和LED灯的配置过于简单,给个电平即可。
总结:
以上便是全部内容,本篇文章只是大概提供一些模块嵌套使用思路,内容简单,希望能对大家有所帮助。附上视频链接
使用ESP8266wifi模块控制开发板上的各种外设

本文介绍了一个基于ESP8266的智能温湿度控制系统,利用DHT11传感器监测环境温湿度,通过LCD显示数据,并利用按键设置报警阈值。系统还能通过无线通信实现远程控制。




169





