SMT32 智能环境监测系统 嵌入式初学者课堂小组作业
一、应用知识
1,开发语言:C语言
2,开发工具:Keil uVision5、Setup_JLinkARM_V415e
3,开发平台:XCOM V2.0
4,开发知识:温湿度传感DHT11、STM32F4xx中文参考手册
5,文件操作:GEC-M4原理图2016-07-29、程序烧录硬件连接、中断控制线
6,任务管理:stm32f4xx_dsp_stdperiph_lib_um
二、软件设计
1、使用的工具软件是:ubuntu,gcc,secureCRT
三、智能环境监测系统实现功能描述:
(1)登录系统:通过串口助手,输入密码登录,正确则蜂鸣器滴一下示意,错误则长鸣一会儿(定义一个数组存放初始密码);
(2)入系统后,通过串口助手输入"temp humi#"就可以定期监测当下环境的温湿度(每隔2秒);(要求温湿度数据能够在串口助手显示),若温湿度超过设定的值范围,蜂鸣器嘀急促报警;
(3)要求按键能够改变温湿度设定的阈值,按下按键1,温度阈值就+1;按下按键2,比如温度阈值就-1:按下按键湿度阈值就+1;按下按键4,湿度阈值就-1,(设定阈值也能实时在串口助手显示)按键控制是由外部中断去控制。
(4)要求开发板的4个led,每隔1s依次闪烁,呈见流水灯效果表示系统稳定运行。
附加实验图片(效果图)
图示1输入错误密码无法进入温湿度检测系统
图2 输入正确密码进入温湿度检测系统并正确读取
图3 密码正确进入正常状态led显示
图4 按键1和按键3分别控制温度和湿度阈值增值
图5 按键2和按键4分别控制温度和湿度减值
温度阈值的标识符:temp_cmp,湿度阈值的标识符:temp_shidu,
四、实训总结
本实验设计温湿度检测系统,根据实验的要求正确实现本次系统功能。更加深入的理解SMT32的应用,也有更加深刻的嵌入式应用开发经历。
实验过程中,遇到过四个方面问题。首先,在每隔两秒显示温湿度的状态值,最开始使用的是定时器中断控制时间的状态,但是在写时定时器与系统的时钟始终无法匹配。之后改用延时函数的方式设置时间显示状态。其次,在当前温度状态值与阈值的比较时,最开始是粗暴的使用result的值进行比较,随后发现效果十分不理想,因为读取温湿度值使用的是数组获取的,因此要提前改数组值进行比较才最为合理,经实验证明效果也十分的理想。
再其次,使用外部中断时,实时的显示了状态的数值以及阈值的数值,但是出现了按键抖动的问题,为了解决这一问题,我们定义了另一种延时函数,在外部中断函数中使用,显示的状态则不会发生一次按下出现多种数值的状态。最后,在阈值警报中,实现了中断的效果,但是改变的阈值数无法与当前变化的温湿度状态进行比较,检查发现,temp_cmp和temp_shidu的值应放入到进入第二个while循环中,也就是进入系统后就进行实时更新。
本次实验解决问题的过程中,感谢我的老师、队友和其他同学们的帮助解惑,没有他们我无法那么顺利的完成,这也让我深刻的理解了团队合作的重要性,更好的指引我未来的工作状态和工作方向。
程序附录:
main
#include "usart.h"
#include <stdio.h>
#include <string.h>
#include "dht11.h"
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "exti.h"
EXTI_InitTypeDef EXTI_InitStructure;
int32_t temp_cmp = 25;
int32_t temp_shidu = 50;
void delay()
{int i =0x1000000;while(i--);
}
int main(void)
{ char password_buf[20] = "654321#\r\n"; //输入密码值char buf[5]; //存放温湿度数据的数组 int32_t result = 0; usart1_config(115200); //串口1的初始化 dht11_config(); //dht11的初始化 led_config(); //led配置key_config(); //key配置 exti0_config(); //外部中断0配置 exti2_config(); //外部中断2配置 exti3_config(); //外部中断3配置 exti4_config(); //外部中断4配置 while (1) { if (usart1_event == 1) //断数据是否接收完毕的标志改变 { usart1_event = 0; printf("[%s]\r\n", usart1_recvbuf); if (strcmp((char *)usart1_recvbuf, password_buf) == 0) { printf("密码正确:\r\n"); PFout(8) = 1; delay_ms(1000); PFout(8) = 0; memset((char *)usart1_recvbuf, 0, usart1_cnt); //清空接收数据的数组 usart1_cnt = 0; //清空数据接收记录的个数while (1) { if (strcmp((char *)usart1_recvbuf, "temp_humi?#\r\n") == 0) { result = dht11_read(buf); //读取温湿度 if (result == 0) { float A = (float)buf[2] + (float)buf[3] / 100.0; // buf[3]是小数部分,表示百分之一 printf("读取温湿度成功!\r\n"); printf("temp: %d.%d humi: %d.%d temp_cmp:%d temp_shidu:%d\r\n",buf[2], buf[3], buf[0], buf[1],temp_cmp,temp_shidu); led_ctrl(1,0,0,0); //三盏灯熄灭delay_ms(1000);led_ctrl(0,1,0,0); //三盏灯熄灭delay_ms(1000);led_ctrl(0,0,1,0); //三盏灯熄灭delay_ms(1000);led_ctrl(0,0,0,1);; //三盏灯熄灭delay_ms(1000);if (A > temp_cmp) //比较温度值{ PFout(8) = 1; delay_ms(200); PFout(8) = 0; delay_ms(200); PFout(8) = 1; delay_ms(200); PFout(8) = 0; }if (B> temp_shidu) //比较湿度值{ PFout(8) = 1; delay_ms(200); PFout(8) = 0; delay_ms(200); PFout(8) = 1; delay_ms(200); PFout(8) = 0; } } }}} else { printf("密码错误,请重新输入:\r\n"); PFout(8) = 1; delay_ms(2000); PFout(8) = 0; led_ctrl(1,1,1,1); //四盏灯熄灭} memset((char *)usart1_recvbuf, 0, usart1_cnt); //清空接收数据的数组 usart1_cnt = 0; //清空数据接收记录的个数 } }
}
void EXTI0_IRQHandler(void)
{if(EXTI_GetITStatus(EXTI_Line0) != RESET ){temp_cmp++; delay();EXTI_ClearITPendingBit(EXTI_Line0);}
}
void EXTI2_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line2) != RESET){temp_cmp--;delay();EXTI_ClearITPendingBit(EXTI_Line2);}
}
void EXTI3_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line3) != RESET){temp_shidu++;delay();EXTI_ClearITPendingBit(EXTI_Line3);}
}
void EXTI4_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line4) != RESET){temp_shidu--;delay();EXTI_ClearITPendingBit(EXTI_Line4);}
}
"exti.h"
#ifndef _EXTI_H_
#define _EXTI_H_#include "stm32f4xx.h"
#include "sys.h"extern void exti0_config(void);extern void exti2_config(void);extern void exti3_config(void);extern void exti4_config(void);#endif
"exti.c"
#include "exti.h"
#include <stdio.h>
#include "stm32f4xx.h"
#include "sys.h"//中断0
void exti0_config(void)
{EXTI_InitTypeDef EXTI_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;/* Enable GPIOA clock A端口时钟使能*/RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);/* Enable SYSCFG clock SYSCFG系统配置时钟使能*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);/* Configure PA0 pin as input floating 配置PA0引脚为浮空输入模式*/GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_Init(GPIOA, &GPIO_InitStructure);/* Connect EXTI Line0 to PA0 pin 将PA0引脚连接到外部中断线0*/SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);/* Configure EXTI Line0 配置外部中断线0*/EXTI_InitStructure.EXTI_Line = EXTI_Line0; //选择外部中断线0EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //设置中断模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //上升沿触发,按键1松开EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能EXTI_Init(&EXTI_InitStructure); //应用该配置参数/* Enable and set EXTI Line0 Interrupt to the lowest priority 使能外部中断线0并且设置为最低优先级*/NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //选择中断通道0NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //响应优先级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断NVIC_Init(&NVIC_InitStructure); //应用该配置参数}//中断2
void exti2_config(void)
{EXTI_InitTypeDef EXTI_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;/* Enable GPIOA clock E端口时钟使能*/RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);/* Enable SYSCFG clock SYSCFG系统配置时钟使能*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);/* Configure PA0 pin as input floating 配置PE2引脚为浮空输入模式*/GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;GPIO_Init(GPIOE, &GPIO_InitStructure);/* Connect EXTI Line0 to PE2 pin 将PE2引脚连接到外部中断线2*/SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource2);/* Configure EXTI Line2 配置外部中断线2*/EXTI_InitStructure.EXTI_Line = EXTI_Line2; //选择外部中断线2EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //设置中断模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //上升沿触发,按键1松开EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能EXTI_Init(&EXTI_InitStructure); //应用该配置参数/* Enable and set EXTI Line2 Interrupt to the lowest priority 使能外部中断线2并且设置为最低优先级*/NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //选择中断通道2NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //响应优先级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断NVIC_Init(&NVIC_InitStructure); //应用该配置参数}//中断3
void exti3_config(void)
{EXTI_InitTypeDef EXTI_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;/* Enable GPIOA clock E端口时钟使能*/RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);/* Enable SYSCFG clock SYSCFG系统配置时钟使能*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);/* Configure PE3 pin as input floating 配置PE3引脚为浮空输入模式*/GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;GPIO_Init(GPIOE, &GPIO_InitStructure);/* Connect EXTI Line3 to PE3 pin 将PE3引脚连接到外部中断线3*/SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource3);/* Configure EXTI Line3 配置外部中断线3*/EXTI_InitStructure.EXTI_Line = EXTI_Line3; //选择外部中断线3EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //设置中断模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //上升沿触发,按键1松开EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能EXTI_Init(&EXTI_InitStructure); //应用该配置参数/* Enable and set EXTI Line3 Interrupt to the lowest priority 使能外部中断线3并且设置为最低优先级*/NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn; //选择中断通道3NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //响应优先级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断NVIC_Init(&NVIC_InitStructure); //应用该配置参数}//中断4
void exti4_config(void)
{EXTI_InitTypeDef EXTI_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;/* Enable GPIOE clock E端口时钟使能*/RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);/* Enable SYSCFG clock SYSCFG系统配置时钟使能*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);/* Configure PE4 pin as input floating 配置PE4引脚为浮空输入模式*/GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_Init(GPIOE, &GPIO_InitStructure);/* Connect EXTI Line4 to PE4 pin 将P4引脚连接到外部中断线4*/SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource4);/* Configure EXTI Line4 配置外部中断线4*/EXTI_InitStructure.EXTI_Line = EXTI_Line4; //选择外部中断线4EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //设置中断模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //上升沿触发,按键1松开EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能EXTI_Init(&EXTI_InitStructure); //应用该配置参数/* Enable and set EXTI Line4 Interrupt to the lowest priority 使能外部中断线4并且设置为最低优先级*/NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; //选择中断通道4NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //响应优先级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断NVIC_Init(&NVIC_InitStructure); //应用该配置参数}
"key.h"
#include "key.h"void key_config(void)
{GPIO_InitTypeDef GPIO_InitStructure;/* GPIOA Peripheral clock enable 给A端口外设时钟使能*/RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //设置0号引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //设置输入模式GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //设置引脚速度为高速GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //设置无上下拉电阻GPIO_Init(GPIOA, &GPIO_InitStructure); //应用到配置/* GPIOE Peripheral clock enable 给E端口外设时钟使能*/RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //设置2号引脚GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //设置3号引脚GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //设置4号引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //设置输入模式GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //设置引脚速度为高速GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //设置无上下拉电阻GPIO_Init(GPIOE, &GPIO_InitStructure); //应用到配置
}
"key.c"
#ifndef _KEY_H_
#define _KEY_H_#include "stm32f4xx.h"
#include "sys.h"extern void key_config(void);#endif
"dht11.c"
#include "dht11.h"static GPIO_InitTypeDef GPIO_InitStructure; //static修饰的全局变量仅在本文件有效#define DHT11_PIN_OUT PGout(9)
#define DHT11_PIN_IN PGin(9)void dht11_config(void)
{RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //设置9号引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //设置输出模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //设置推挽模式,说白了就是增强他的驱动电流GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //设置引脚速度为高速GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //设置无上下拉电阻GPIO_Init(GPIOG, &GPIO_InitStructure); //应用到配置//dht11初始化,主机发送高电平信号DHT11_PIN_OUT = 1;
}//模式切换
void dht11_mode(GPIOMode_TypeDef mode)
{GPIO_InitStructure.GPIO_Mode = mode; //GPIO_Mode_OUT GPIO_Mode_INGPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //设置推挽模式,说白了就是增强他的驱动电流GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //设置引脚速度为高速GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //设置无上下拉电阻GPIO_Init(GPIOG, &GPIO_InitStructure); //应用到配置
}//通信的开始
int32_t dht11_start(void)
{int32_t t = 0;//主机发送低电平信号持续18msDHT11_PIN_OUT = 0;delay_ms(20);//主机发送高电平信号持续30usDHT11_PIN_OUT = 1;delay_us(30);//模式切换dht11_mode(GPIO_Mode_IN);//等待dht11响应低电平信号的到来while(DHT11_PIN_IN == 1){delay_us(1);t++;if(t > 4000) //等待响应信号超时return -1;}t = 0;//低电平到来,持续80uswhile(DHT11_PIN_IN == 0){delay_us(1);t++;if(t > 100) //等待低电平信号持续时间超时return -2;}t = 0;//高电平到来,持续80uswhile(DHT11_PIN_IN == 1){delay_us(1);t++;if(t > 100) //等待高电平信号持续时间超时return -3;}t = 0;return 0; //通信开始成功
}//接收1个字节的数据
int32_t dht11_read_byte(void)
{int8_t data = 0; // 0000 0000int32_t i = 0;int32_t t = 0;for(i=7; i>=0; i--) //高位先出{//等待dht11响应低电平信号的到来while(DHT11_PIN_IN == 1){delay_us(1);t++;if(t > 4000) //等待响应信号超时return -4;}t = 0;//低电平到来,持续50uswhile(DHT11_PIN_IN == 0){delay_us(1);t++;if(t > 100) //等待低电平信号持续时间超时return -5;}t = 0;//判断接收到的数据是0还是1 取决于高电平持续的时间 delay_us(40);if(DHT11_PIN_IN == 1) //接收到的数据为1{data |= 1 << i; }}return data;
}//通信结束
int32_t dht11_stop(void)
{int32_t t = 0;//等待dht11响应低电平信号的到来while(DHT11_PIN_IN == 1){delay_us(1);t++;if(t > 4000) //等待响应信号超时return -6;}t = 0;//低电平到来,持续50uswhile(DHT11_PIN_IN == 0){delay_us(1);t++;if(t > 100) //等待低电平信号持续时间超时return -7;}t = 0;//模式转换dht11_mode(GPIO_Mode_OUT);DHT11_PIN_OUT == 1;return 0; //通信结束成功
}//读取温湿度
int32_t dht11_read(char *buf)
{int32_t sum = 0;int32_t i = 0;if(dht11_start() != 0) //通信开始失败{return -8;}for(i=0; i<5; i++){buf[i] = dht11_read_byte();}if(dht11_stop() != 0) //通信结束失败{return -9;}sum = buf[0]+buf[1]+buf[2]+buf[3];sum &= 0xff; //1111 1111if(sum != buf[4]) //校验和错误{return -10;}return 0; //读取温湿度成功
}
"led.c"
#include "led.h"void led_config(void)
{GPIO_InitTypeDef GPIO_InitStructure;/* GPIOF Peripheral clock enable 给F端口外设时钟使能*/RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);/* GPIOE Peripheral clock enable 给E端口外设时钟使能*/RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);/* Configure PF9 in output pushpull mode */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_8; //设置9号,10号引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //设置输出模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //设置推挽模式,说白了就是增强他的驱动电流GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //设置引脚速度为高速GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //设置无上下拉电阻GPIO_Init(GPIOF, &GPIO_InitStructure); //应用到配置GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14; //设置9号,10号引脚GPIO_Init(GPIOE, &GPIO_InitStructure);PFout(8) = 0;PFout(9) = 1;PFout(10) = 1;PEout(13) = 1;PEout(14) = 1;
}void led_ctrl(int sw1,int sw2,int sw3,int sw4)
{if(sw1){PFout(9) = 0; //led1亮}else{PFout(9) = 1; //led1灭}if(sw2){PFout(10) = 0; //led1亮}else{PFout(10) = 1; //led1灭}if(sw3){PEout(13) = 0; //led1亮}else{PEout(13) = 1; //led1灭}if(sw4){PEout(14) = 0; //led1亮}else{PEout(14) = 1; //led1灭}
}
这个基础的功能,最开始的设计是有蓝牙功能,以及led的延时是定时器中断的方式,但是由于课时被放假冲了,作者也不想努力了就没学,还有写的定时器中断函数不知道哪里由问题,死活就是不行,就算了。主程序还可用其他方式写,我看了一下其他的小组有的是用类似于FPGA的状态机的思路写的,还有其他乱七八糟的写法,本人太菜了,更多功能等大家写吧。