基于stm32的仿真实现温湿度检测,蜂鸣器,跑马灯及其速度的控制

这几天,学校组织了为期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;i<x;i++)
		for(j=0;j<SIGN;j++);
}


//延时函数--毫秒级别
//VAL计数器是24bit的寄存器,所以最大值是16777215
//所以nms的最大值是 199
void delay_ms(int nms){
	uint32_t temp;
	
	//延时nms毫秒需要的计数数值,把它交给重载计数器
	SysTick->LOAD = 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);
		
	
	}
	
}

需要源码文件的小伙伴,请移步到我的个人博客(传送门),并在文章下留言,即可获取源码文件。

  • 22
    点赞
  • 132
    收藏
    觉得还不错? 一键收藏
  • 106
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 106
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值