基于STM32103C8T6标准库的多功能风扇

        这个多功能风扇是我在学完江科大的STM32入门教程后自己编写的第一个小项目,难度不大,但是在完成这个项目的过程中,我对涉及到的外设有了更加深刻的理解,尤其是TIM定时器,并且在此次项目的过程中我学会了逻辑分析仪的使用。在这个项目完成后,我的第一感觉是自己终于学到了一点实实在在的东西,并且能运用于实际了。天助自助者,望诸君共勉!

        回归正题,接下来开始详细讲解这个项目。

        多功能风扇功能概述:1.通过舵机实现摇头功能;        2.风扇6档调速;        3.实时温湿度监测;        4.高温自动打开风扇并告警,在温度降低后自动关闭

当然,最初的设想是可以用手机通过进行蓝牙控制,但是当时还完全没有接触过蓝牙模块,以为这个HC-05会很难呢,就暂时没有加上,后来着实没有时间且自己也觉得没有太大的必要再加,如果有兴趣的小伙伴可以试着将这个功能加上去。

实物图如下

        模块部分难度不大,就不再赘述了,直接放代码。

按键模块

void Key_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//PB1-控制电机
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//PB11—控制舵机
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
}

电机模块


void Motor_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GOIOA时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//开启TIM3时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启GOIOB时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO时钟
	
	GPIO_InitTypeDef GPIO_InitStruct;	//初始化PB0-提供PWM波
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//PB1-按下引发中断
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP ;//初始化PA4,PA5为电机提供电压差
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_5;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	
//	TIM_InternalClockConfig(TIM3);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;//初始化时基单元
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period=100-1;
	TIM_TimeBaseInitStruct.TIM_Prescaler=36-1;
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
	
	TIM_OCInitTypeDef TIM_OCInitStruct;//配置输出比较寄存器
	TIM_OCStructInit(&TIM_OCInitStruct);
	TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
	TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
	TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
	TIM_OCInitStruct.TIM_Pulse=0;//CCR
	TIM_OC3Init(TIM3,&TIM_OCInitStruct);
	
	TIM_Cmd(TIM3,ENABLE);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);//选择PB1管脚用作AFIO外设中断线路
	
	EXTI_InitTypeDef EXTI_InitStruct;//初始化EXTI
	EXTI_InitStruct.EXTI_Line=EXTI_Line1;
	EXTI_InitStruct.EXTI_LineCmd=ENABLE;
	EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;
	EXTI_Init(&EXTI_InitStruct);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	
	NVIC_InitTypeDef NVIC_InitStruct;//初始化NVIC
	NVIC_InitStruct.NVIC_IRQChannel=EXTI1_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;
	NVIC_Init(&NVIC_InitStruct);
}

void Motor_SetSpeed(int8_t Speed)
{
		GPIO_SetBits(GPIOA,GPIO_Pin_4);
		GPIO_ResetBits(GPIOA,GPIO_Pin_5);
		TIM_SetCompare3(TIM3,Speed);
}

void EXTI1_IRQHandler(void)//中断函数
	{
		if(EXTI_GetITStatus(EXTI_Line1)==SET)
		{	
			Servo_OFF();
			KeyNum_Motor=1;
			EXTI_ClearITPendingBit(EXTI_Line1);
		}
	}

void Motor_SpeedAdd(void)
{
	Speed+=20;
		if(Speed>100)
			{
				Speed=0;
			}
	Motor_SetSpeed(Speed);
}

void Motor_ON(void)
{
		GPIO_SetBits(GPIOA,GPIO_Pin_4);
		GPIO_ResetBits(GPIOA,GPIO_Pin_5);
		TIM_SetCompare3(TIM3,100);
}

void Motor_OFF(void)
{
		GPIO_ResetBits(GPIOA,GPIO_Pin_4);
		GPIO_ResetBits(GPIOA,GPIO_Pin_5);
		TIM_SetCompare3(TIM3,0);
}

舵机模块



void Servo_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GOIOA时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//开启TIM2时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO时钟
	
	GPIO_InitTypeDef GPIO_InitStruct;//初始化PA1-驱动舵机
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP ;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
//	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;//初始化时基单元
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period=20000-1;
	TIM_TimeBaseInitStruct.TIM_Prescaler=72-1;
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
	
	TIM_OCInitTypeDef TIM_OCInitStruct;//配置输出比较寄存器
	TIM_OCStructInit(&TIM_OCInitStruct);
	TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
	TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
	TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
	TIM_OCInitStruct.TIM_Pulse=0;//CCR
	TIM_OC2Init(TIM2,&TIM_OCInitStruct);
	
	TIM_Cmd(TIM2,ENABLE);	
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource11);//选择PB11管脚用作AFIO外设中断线路
	
	EXTI_InitTypeDef EXTI_InitStruct;//初始化EXTI
	EXTI_InitStruct.EXTI_Line=EXTI_Line11;
	EXTI_InitStruct.EXTI_LineCmd=ENABLE;
	EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;
	EXTI_Init(&EXTI_InitStruct);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	
	NVIC_InitTypeDef NVIC_InitStruct;//初始化NVIC
	NVIC_InitStruct.NVIC_IRQChannel=EXTI15_10_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;
	NVIC_Init(&NVIC_InitStruct);
}

void PWM_SetCompare2(uint16_t Compare)//舵机
{
	TIM_SetCompare2(TIM2,Compare);
}

void Servo_SetAngle(float Angle)
{
	PWM_SetCompare2(Angle / 180 * 2000 + 500);
}

void Servo_ON(void)
{
	dirction=1;
	while(dirction==1)
		{
			Angle+=2;
			Delay_ms(20);
			Servo_SetAngle(Angle);
				if(Angle>135)
				{
					dirction=-1;
					Delay_ms(1000);
				}
			}
		while(dirction==-1)
		{
			Angle-=2;
			Delay_ms(20);
			Servo_SetAngle(Angle);
				if(Angle<1)
				{
					dirction=1;
					Delay_ms(1000);
				}
		}
}

void Servo_OFF(void)
{
	dirction=0;
}

void EXTI15_10_IRQHandler(void)//中断函数
	{
		if(EXTI_GetITStatus(EXTI_Line11)==SET)
		{		
			Servo_OFF();
			KeyNum_Servo++;
			EXTI_ClearITPendingBit(EXTI_Line11);
		}
	}

DHT11模块

void DHT11_GPIO_OUT(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);    //开启挂载GPIO的总线APB2
		
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出,输入也是有效
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);		

}


//初始化为输入
 
 
void DHT11_GPIO_IN(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
		
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;	
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入,不可以输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);					
}



uint8_t DHT11_ReadByte(void)
{
	uint8_t temp;
	uint8_t ReadDat=0;
	
	uint8_t retry = 0;	
	uint8_t i;
	//二进数除以十即为数据
	
	
	
	for(i=0;i<8;i++)
	{
		//消耗准备信号低电平50us
		while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0&&retry<100)
		{
			Delay_us(1);	
			retry++;		
		}
		retry=0;
		
		
		//虽然0信号持续时间为28us,retry循环非常巧妙,
		//避免问题,相当于如果一直是0的话可以避免延时带来的多出时间
		Delay_us(30);//如果是0,那temp=0起作用,非0,仍然在高电平时间内
		
		//因为如果数据是0的话持续时间短,故先预置0,简化代码。
		
		temp=0;//数字信号0,temp=0
		//数字0信号高电平持续28us,数字1信号高电平70us,延时30us以确认数字0或1
		if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==1) 
		{
			temp=1;
		}			
			//数字1信号高电平剩余40us
		while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==1&&retry<100)
		{		
			Delay_us(1);
			retry++;
		}
		retry=0;
		//<<=左移赋值符号,|=先或再赋值,得到1位数据,即8bit
		ReadDat<<=1;
		ReadDat|=temp;
	}	
	return ReadDat;


}

void DHT11_ReadData(uint8_t *temp, uint8_t *humi)
{
    uint8_t i,data[5];
	uint8_t retry=0;
    
	
	
	//stm32 PB14设置为输出,发送开始信号低电平18ms,高电平40us
	DHT11_GPIO_OUT();
	GPIO_ResetBits(GPIOB,GPIO_Pin_14);//拉低引脚输出电平
	Delay_ms(18);//保证dht11能收到起始信号
	
	
	
	GPIO_SetBits(GPIOB,GPIO_Pin_14);//拉高引脚输出电平
	Delay_us(40);//延时40us,等待dht11响应信号
	
	
	//stm32 PB14设置为输入,检查并接收响应信号低电平80us,高电平80us
	DHT11_GPIO_IN();//可以不切换,直接输出高电平,输入也是开启的
	Delay_us(20);//延时20us,dht11响应低电平80us,还剩60us,
	
	
	//检查是否是低电平以确定是否有响应信号	
	if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0)
	{	
		//第一个循环消耗响应低电平60us
		while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0&&retry<100)//接收响应信号低电平剩余60us
		{
			Delay_us(1);
			retry++;		
		}
		
		
		retry=0;//超过100us自动向下运行,以免卡死
		//消耗dht11拉高80us
		while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==1&&retry<100)//接收响应信号高电平80us
		{
			Delay_us(1);
			retry++;			
		}
		retry=0;
		
		//接收8字节数据,每传送1bit之前先进行拉低50us
		//接收了5位数据,40bit
		for(i=0;i<5;i++)
		{
			data[i]=DHT11_ReadByte();
		}
		Delay_us(50);//DHT11拉低总线50us作为一字节结束信号,或者使用以下语句
        if ((data[0] + data[1] + data[2] + data[3]) == data[4])
        {
            *humi = data[0];
            *temp = data[2];
        }	
	}

}

有源蜂鸣器

void Beep_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_2;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	GPIO_SetBits(GPIOA,GPIO_Pin_2);//默认不叫
}

void Beep_OFF(void)
{
	GPIO_SetBits(GPIOA,GPIO_Pin_2);
}

void Beep_ON(void)
{
	GPIO_ResetBits(GPIOA,GPIO_Pin_2);
}

主函数


int Stage=0;
int Flag=0;

int main(void)
{
	OLED_Init();
	Servo_Init();
	Motor_Init();
	Key_Init();
	Beep_Init();
	
	
	
	while(1)
	{
//温湿度
		DHT11_ReadData(&temp, &humi);
		Delay_ms(1000);
		DHT11_ReadData(&temp, &humi);
		
		if(temp<30)
			{	
				OLED_ShowString(1, 1, "Snap:");
				OLED_ShowString(2, 1, "Stage:");
				OLED_ShowString(3, 1, "temp:");
				OLED_ShowString(4, 1, "humi:");
				
				OLED_ShowNum(3, 6,temp,2);
				OLED_ShowNum(4, 6,humi,2);
				
				if(Flag == 1)
				{
					Motor_OFF();
					Beep_OFF();
					Flag = 0;
				}			
//舵机
			if(KeyNum_Servo%2==0)
				{
					OLED_ShowString(1, 6, "OK");
					Servo_ON();	
				}
			else{
					Servo_OFF();
					OLED_ShowString(1, 6, "NO");
				}
		
//电机
			if(KeyNum_Motor==1)
				{
					Stage++;
					if(Stage>5)
					{
						Stage=0;
					}
					OLED_ShowNum(2, 7,Stage,1);
					Motor_SpeedAdd();
					KeyNum_Motor=0;
				if(KeyNum_Servo%2==0)
					{
						Servo_ON();	
					}
				}
			}	


		 if(temp>30)
			{
				OLED_ShowString(1, 1, "Snap:");
				OLED_ShowString(2, 1, "Stage:");
				OLED_ShowString(3, 1, "temp:");
				OLED_ShowString(4, 1, "humi:");
				
				OLED_ShowNum(3, 6,temp,2);
				OLED_ShowNum(4, 6,humi,2);
				OLED_ShowNum(2, 7,5,1);
				
				Motor_ON();
				Beep_ON();
				Flag = 1;
			}
	}
}

        到这里,这篇文章就结束了,感谢各位的观看,如果觉得文章对你有帮助,可以点赞,如果有什么问题,大家也可以留言一起探讨,但请营造一个和谐友好的交流环境。当然,如果发现了什么问题也欢迎各位指正,多谢!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值