这几天,学校组织了为期5天的关于基于stm32的仿真的课程设计,最后一天,我们要进行实训项目的测验,要独立完成一个项目,具体有以下要求:
原理图绘制:
按键 6个
LED灯 4盏+1盏
蜂鸣器 1个
温湿度传感器 1个
要求:
(1)按下按键key1 , 开启LED跑马灯(4盏)
(2)按下按键key2 , 关闭LED跑马灯(4盏)
(3)按下按键key3 , 控制独立的LED 每500ms 闪烁一次-----使用定时器 TIM_cmd(TIM2,ENABLE)
(4)按下按键key4 , 关闭独立的LED 闪烁-----使用定时器 TIM_cmd(TIM2,DISENABLE)
(5)按下按键key5 , 开启蜂鸣器
(6)按下按键key6 , 关闭蜂鸣器
(7)温湿度检测报警,当温度超过30度或者湿度超过80%时,蜂鸣器报警
加分项1:
添加2个按键
按下按键7,跑马灯加速,按下按键8,跑马灯减速
满分项:
添加显像管(seg),把温湿度显示在显像管中。
因为博主并不是主攻嵌入式开发的,所以5天时间对于嵌入式开发的研究还远远不够,因此,对于此次作业项目,并没有完成最后一个满分项,但我还是想将此次完成的项目进行记录,以作纪念。
一、Protues仿真部分
二、keil工程
#include "stm32f4xx.h"
#include "sys.h"
int SIGN = 10000;
void delay(int x)
{
int i,j;
for(i=0;iLOAD = 84000*nms;
//将VAL计数器的数值赋值为0
SysTick->VAL = 0;
//将控制器的0bit位赋值为1,开启计数
SysTick->CTRL |= 0x1;
//循环检测控制器得到16bit,当它为1的时候,表示计数器由1减到0了
do{
temp = SysTick->CTRL;
}while( !(temp>>16 & 1) && (temp&0x1));
//temp>>16 & 1 结果为真的时候,表示16bit数值已经为1了,要跳出循环
//结果为假的时候,表示16bit数值还是0,要继续循环
//但是while的判断语句是 逻辑真--循环 逻辑假---结束
//所以在最外面加上 ! 取反。
//temp&0x1为真是要求控制器的0bit位数值为1,即计数器一直在计数
//将控制器的0bit位赋值为0,停止计数
SysTick->CTRL &= ~0x1;
//将计数器数值设置为0
SysTick->VAL = 0;
}
//1微秒是84次脉冲
void delay_us(int nus){
uint32_t temp;
//延时nms毫秒需要的计数数值,把它交给重载计数器---会忽略小数
SysTick->LOAD = 84*nus;
SysTick->VAL = 0;
SysTick->CTRL |= 0x1;
do{
temp = SysTick->CTRL;
}while( !(temp>>16 & 1) && (temp&0x1));
SysTick->CTRL &= ~0x1;
SysTick->VAL = 0;
}
//配置LED引脚
void led(){
//定义GPIO结构体
GPIO_InitTypeDef a;
//使能LED灯GPIO分组的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
//GPIO结构体赋值---也就是选择引脚的配置参数
a.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_9;//选中引脚
a.GPIO_Mode = GPIO_Mode_OUT;//配置输出模式----是主动设置引脚电平高低
a.GPIO_Speed = GPIO_Speed_100MHz;//选择高速
a.GPIO_OType = GPIO_OType_PP;//推挽输出
a.GPIO_PuPd = GPIO_PuPd_NOPULL;//无上下拉电阻
//配置GPIO的引脚函数---将选好的配置放入到GPIOA组里面
GPIO_Init(GPIOA,&a);
}
//初始化蜂鸣器
void beep_init()
{
//定义GPIO结构体
GPIO_InitTypeDef a;
//使能LED灯的GPIO分组
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
//对GPIO结构体进行赋值
a.GPIO_Pin = GPIO_Pin_8;
a.GPIO_Mode = GPIO_Mode_OUT;//配置输出模式----是主动设置引脚电平高低
a.GPIO_Speed = GPIO_Speed_100MHz;//选择高速
a.GPIO_OType = GPIO_OType_PP;//推挽输出
a.GPIO_PuPd = GPIO_PuPd_NOPULL;//无上下拉电阻
//配置GPIO的引脚函数---将选好的配置放入到GPIOA组里面
GPIO_Init(GPIOB,&a);
//让蜂鸣器停下
PEout(8)=0;
}
//蜂鸣器响
void beep_up(){
int i = 0;
for(i=0;i<2;i++)
delay_ms(100);
PBout(8)=1;
for(i=0;i<2;i++)
delay_ms(100);
PBout(8)=0;
}
//配置开关引脚
void btns(){
GPIO_InitTypeDef k;//定义GPIO结构体
EXTI_InitTypeDef b;//定义外部中断控制器结构体
NVIC_InitTypeDef c;//定义中断配置结构体
//使能KEY按键GPIO分组的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
//使能EXTI的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
//GPIO结构体赋值---也就是选择引脚的配置参数
k.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
k.GPIO_Mode =GPIO_Mode_IN;//配置输入模式---是检测引脚电平高低
k.GPIO_Speed=GPIO_Speed_100MHz;//选择高速
k.GPIO_PuPd = GPIO_PuPd_NOPULL;//无上下拉电阻
GPIO_Init(GPIOE,&k);
//发生外部中断时的引脚
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource0);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource1);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource2);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource5);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource6);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource7);
//对EXTI结构体进行赋值
b.EXTI_Line = EXTI_Line0 | EXTI_Line1 | EXTI_Line2 | EXTI_Line3 | EXTI_Line4 | EXTI_Line5 | EXTI_Line6 | EXTI_Line7;//选择输入线0,1,2,3,4,5,6,7
b.EXTI_Mode = EXTI_Mode_Interrupt;//选择中断
b.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
b.EXTI_LineCmd = ENABLE;//使能
EXTI_Init(&b);//根据配置好的EXTI参数初始化外部中断控制器
//开启跑马灯
c.NVIC_IRQChannel = EXTI0_IRQn;//通道为EXTI0_IRQHandler
c.NVIC_IRQChannelCmd = ENABLE;//使能
c.NVIC_IRQChannelPreemptionPriority = 3;//抢占优先级
c.NVIC_IRQChannelSubPriority = 0;//响应优先级
//根据配置好的NVIC参数初始化中断服务
NVIC_Init(&c);
//关闭跑马灯
c.NVIC_IRQChannel = EXTI1_IRQn;//通道为EXTI1_IRQHandler
c.NVIC_IRQChannelCmd = ENABLE;//使能
c.NVIC_IRQChannelPreemptionPriority = 0;
c.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&c);
//开启独立灯
c.NVIC_IRQChannel = EXTI2_IRQn;//通道为EXTI2_IRQHandler
c.NVIC_IRQChannelCmd = ENABLE;//使能
c.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级
c.NVIC_IRQChannelSubPriority = 0;//响应优先级
//根据配置好的NVIC参数初始化中断服务
NVIC_Init(&c);
//关闭独立灯
c.NVIC_IRQChannel = EXTI3_IRQn;//通道为EXTI3_IRQHandler
c.NVIC_IRQChannelCmd = ENABLE;//使能
c.NVIC_IRQChannelPreemptionPriority = 0;
c.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&c);
//开启蜂鸣器
c.NVIC_IRQChannel = EXTI4_IRQn;//通道为EXTI4_IRQHandler
c.NVIC_IRQChannelCmd = ENABLE;//使能
c.NVIC_IRQChannelPreemptionPriority = 2;
c.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&c);
//关闭蜂鸣器等
c.NVIC_IRQChannel = EXTI9_5_IRQn;
c.NVIC_IRQChannelCmd = ENABLE;//使能
c.NVIC_IRQChannelPreemptionPriority = 1;
c.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&c);
}
//目的:初始化TIM4,让定时器每1秒发生五次中断
//当前输入频率 84MHz , 可以设置预分频器 8399 重装载值 9999
void tim4_init(){
TIM_TimeBaseInitTypeDef a;//定义定时器的结构体
NVIC_InitTypeDef b;//定义中断结构体
//开启TIM4的时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
//设置时基单元
a.TIM_Prescaler = 8399;//预分频器数值
a.TIM_Period = 4999;//重装载值
a.TIM_CounterMode = TIM_CounterMode_Up;//计数器递增模式
//a.TIM_CounterMode = 0;//定时的时候用不到滤波
//a.TIM_RepetitionCounter;//重复计数器仅限高级定时器使用
TIM_TimeBaseInit(TIM4, &a);
//初始化中断配置
b.NVIC_IRQChannel = TIM4_IRQn;//中断服务通道
b.NVIC_IRQChannelCmd = ENABLE;//中断使能
b.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级
b.NVIC_IRQChannelSubPriority = 0;//响应优先级
NVIC_Init(&b);
}
void TIM4_IRQHandler()
{
//判断中断是否真的发生----检查中断标志位是否为1
if( TIM_GetITStatus(TIM4,TIM_IT_Update) == SET )
{
//LED灯闪烁
PAout(9) ^=1;
//清除中断标志位
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
}
}
//中断服务函数
void EXTI0_IRQHandler()
{
SIGN=10000;
//判断中断是否真的发生
while(EXTI_GetITStatus(EXTI_Line0)==SET)//确实发生了中断
{
PAout(0)=1;
delay(200);
PAout(0)=0;
PAout(1)=1;
delay(200);
PAout(1)=0;
PAout(2)=1;
delay(200);
PAout(2)=0;
PAout(3)=1;
delay(200);
PAout(3)=0;
}
}
void EXTI1_IRQHandler()
{
//判断中断是否真的发生
if(EXTI_GetITStatus(EXTI_Line1)==SET)//确实发生了中断
{
//清除中断标志位,防止CPU认为中断在不停的发生
EXTI_ClearITPendingBit(EXTI_Line0);
//清除中断标志位,防止CPU认为中断在不停的发生
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
void EXTI2_IRQHandler()
{
//判断中断是否真的发生
if(EXTI_GetITStatus(EXTI_Line2)==SET)//确实发生了中断
{
//开启定时器中断
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);
//开启定时器
TIM_Cmd(TIM4,ENABLE);
//清除中断标志位,防止CPU认为中断在不停的发生
EXTI_ClearITPendingBit(EXTI_Line2);
}
}
void EXTI3_IRQHandler()
{
//判断中断是否真的发生
if(EXTI_GetITStatus(EXTI_Line3)==SET)//确实发生了中断
{
//关闭定时器中断
TIM_ITConfig(TIM4,TIM_IT_Update,DISABLE);
//关闭定时器
TIM_Cmd(TIM4,DISABLE);
PAout(9)=0;
//清除中断标志位,防止CPU认为中断在不停的发生
EXTI_ClearITPendingBit(EXTI_Line3);
}
}
void EXTI4_IRQHandler()
{
//判断中断是否真的发生
while(EXTI_GetITStatus(EXTI_Line4)==SET)//确实发生了中断
{
//开启蜂鸣器
beep_up();
}
PBout(8)=0;
}
void EXTI9_5_IRQHandler()
{
//判断中断是否真的发生
if(EXTI_GetITStatus(EXTI_Line5)==SET)//确实发生了中断
{
//清除中断标志位,防止CPU认为中断在不停的发生
EXTI_ClearITPendingBit(EXTI_Line4);
//清除中断标志位,防止CPU认为中断在不停的发生
EXTI_ClearITPendingBit(EXTI_Line5);
}
//判断中断是否真的发生
if(EXTI_GetITStatus(EXTI_Line0)==SET && EXTI_GetITStatus(EXTI_Line6)==SET)//确实发生了中断
{
SIGN = 2000;
//清除中断标志位,防止CPU认为中断在不停的发生
EXTI_ClearITPendingBit(EXTI_Line6);
}
//判断中断是否真的发生
if(EXTI_GetITStatus(EXTI_Line0)==SET && EXTI_GetITStatus(EXTI_Line7)==SET)//确实发生了中断
{
SIGN=20000;
//清除中断标志位,防止CPU认为中断在不停的发生
EXTI_ClearITPendingBit(EXTI_Line7);
}
}
//空闲状态
void idle(){
//定义GPIO结构体
GPIO_InitTypeDef a;
//使能按键GPIO分组的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
//GPIO结构体赋值---也就是选择引脚的配置参数
a.GPIO_Pin = GPIO_Pin_0;//选中0引脚
a.GPIO_Mode = GPIO_Mode_OUT;//配置输出模式----是主动设置引脚电平高低
a.GPIO_Speed = GPIO_Speed_100MHz;//选择高速
a.GPIO_OType = GPIO_OType_PP;//推挽输出
a.GPIO_PuPd = GPIO_PuPd_NOPULL;//无上下拉电阻
//配置GPIO的引脚函数---将选好的配置放入到GPIOA组里面
GPIO_Init(GPIOD,&a);
PDout(0)=1;
}
void work(){
//定义GPIO结构体
GPIO_InitTypeDef k;
//使能KEY按键GPIO分组的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
//GPIO结构体赋值---也就是选择引脚的配置参数
k.GPIO_Pin = GPIO_Pin_0;
k.GPIO_Mode =GPIO_Mode_IN;//配置输入模式---是检测引脚电平高低
k.GPIO_Speed=GPIO_Speed_100MHz;//选择高速
k.GPIO_PuPd = GPIO_PuPd_NOPULL;//无上下拉电阻
//k.GPIO_OType 不需要设置,因为电流没经过输出控制器
GPIO_Init(GPIOD,&k);
}
//开始启动
void start(){
idle();
PDout(0)=0;
delay_ms(18);
PDout(0)=1;
delay_us(30);
work();
}
//检测DHT11是否启动
int detect(){
int i=0,j=0;
while(PDin(0)==0){
delay_us(10);
i++;
if(i>10){
return -1;
}
}
while(PDin(0)==1){
delay_us(10);
j++;
if(j>10){
return -1;
}
}
return 0;
}
//接收输入数据
int readdata(){
int a=0,i=0;
for(i=0;i<8;i++){
while(PDin(0)==0);
delay_us(50);
if(PDin(0)==1)
a |=1<<(7-i);
while(PDin(0)==1);
}
return a;
}
//判断温湿度
void judge(int T,int H){
if(T>30 || H>80){
//开启蜂鸣器
beep_up();
}
}
int main()
{
int i,data[5];
int T=0,H=0;
//设置滴答定时器的输入频率
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
//设置中断优先级分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
led();
tim4_init();
btns();
beep_init();
while(1)
{
for(i=0;i<5;i++)
delay_ms(100);
start();
if(detect() != 0)
continue;
for(i=0;i<5;i++)
data[i]=readdata();
T=data[2];
H=data[0];
judge(T,H);
}
}