目錄
一、設計背景和意義
1.1設計背景:
1.2設計意義:
二、實物展示
三、硬件功能介紹
2.1 硬件清單:
2.2 功能介紹:
四、軟件設計流程圖
五、硬件PCB展示
六、軟件主函序展示
七、單片機實物資料
資料獲取?查看主頁介紹:兆龍電子單片機設計
一、設計背景和意義
1.1設計背景:
在物聯網與智慧家居快速發展的當下,傳統風扇控制系統存在諸多局限。多數風扇設備仍依賴手動調節,缺乏環境適應性與智能化控制能力,既無法根據室內環境和用戶需求自動調節,也難以滿足精細化吹風需求。隨著人們對使用體驗、能耗控制要求提升,單一控制模式已不適用。
現有智能風扇方案常存在功能割裂問題,遠程調控、傳感器聯動、使用場景適配等功能未能有效整合,且成本較高難以普及。基于此,本設計以 STM32F103C8T6 為核心,融合多傳感器與語音控制技術,構建低成本、高集成度的智能語音風扇控制系統,以解決傳統風扇控制的智能化不足問題。
1.2設計意義:
本設計通過整合多種監測與語音調控技術,具有多重實用價值。從使用體驗看,提供遠程、按鍵、APP、語音指令等多元控制方式,滿足不同場景下的吹風需求,尤其使用場景適配與自動調節功能提升了使用便捷性。
從節能角度,借助環境監測與用戶使用習慣實現按需調速,避免無效能耗,符合綠色家居理念。從技術層面,探索了 STM32 微控制器與語音識別模塊協同工作的實現方式,為低成本智能風扇系統開發提供了可參考的集成方案,推動智能風扇技術的普及應用,具有較好的實踐與推廣價值。
二、實物展示
下方為實物演示視頻
【開源】基于STM32單片機智能語音風扇系統
下方為實物展示圖片
三、硬件功能介紹
2.1 硬件清單:
- STM32F103C8T6最小系統
- OLED顯示
- DS18B20溫度傳感器
- HC-SR501人體紅外傳感器
- TB6612電機驅動模塊
- JDY-31-SPP藍牙模塊
- 語音模塊
- 聲光報警
2.2 功能介紹:
(1)STM32F103C8T6單片機作為主控單元
(2)溫度采集:DS18B20傳感器采集環境溫度
(3)人體檢測:人體紅外傳感器判斷是否有人
(4)屏幕顯示:OLED顯示溫度、有無人、檔位、模式等信息
(5)風扇搖頭:步進電機模擬風扇搖頭
(6)手動模式:按鍵調節風扇風速
(7)自動模式:判斷是否有人以及根據溫度控制風扇開關和風速
(8)語音模式:通過語音控制風速、模式以及搖頭
(9)定時功能:定時關閉
(10)APP控制:通過藍牙遠程查看數據以及下發控制
四、軟件設計流程圖
五、硬件PCB展示
六、軟件主函序展示
#include "sys.h"
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "math.h"
#include "delay.h"
#include "gpio.h"
#include "key.h"
#include "oled.h"
#include "usart.h"
#include "ds18b20.h"
#include "motor_bujin.h"
#include "timer.h"
#include "motor_dianji.h"/**********************************
變量定義
**********************************/
uint8_t key_num = 0; //按鍵掃描標志位
uint8_t flag_display = 0; //顯示界面標志位
uint32_t time_num = 0; //10ms計時
char display_buf[32]; //顯示數組_Bool flag_mode = 0; //模式標志位(自動|手動)
_Bool flag_direction = 0; //搖頭標志位
int flag_dir = 0;short temp_value = 0; //溫度值
u16 temp_max = 30; //溫度最大值
u16 temp_min = 20; //溫度最低值s16 motor_pwm = 0; //電機速度int countdown_hour = 0; //倒計時時
int countdown_minute = 0; //倒計時分
int countdown_second = 0; //倒計時秒
_Bool flag_countdown_begin = 0; //倒計時開始標志位
_Bool flag_countdown_end = 0; //倒計時結束標志位
extern _Bool flag_1s; //1秒到達標志位extern _Bool flag_bujin_foreward; //步進電機打開標志位
extern _Bool flag_bujin_reversal; //步進電機關閉標志位
extern _Bool flag_bujin_state; //步進電機狀態標志位extern uint8_t usart1_buf[256]; //串口1接收數組
extern uint8_t usart2_buf[256]; //串口2接收數組
unsigned char SendString[50];
uint8_t danwei = 0,send_renflag=0,send_modeflag=0,send_YTflag=0;
/**********************************
函數聲明
**********************************/
void Key_function(void); //按鍵函數
void Monitor_function(void); //監測函數
void Display_function(void); //顯示函數
void Manage_function(void); //處理函數/****
******* 主函數
*****/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置中斷分組為2號
Delay_Init(); //延時初始化
Gpio_Init(); //IO初始化
Key_Init(); //按鍵初始化
Oled_Init(); //OLED初始化
Oled_Clear_All(); //清屏
while(DS18B20_Init()); //DS18B20初始化
Usart1_Init(9600); //串口1初始化
Usart2_Init(9600); //串口2初始化
Step_Motor_Init(); //步進電機初始化
TIM2_Init(1000-1,7200-1); //初始化0.1s的定時器
Motor_Dianji_Init(); //直流電機初始化
TIM_SetCompare1(MOTOR_DIANJI_TIM,motor_pwm);Delay_ms(1000);
Delay_ms(1000);while(1){
Key_function(); //按鍵函數
Monitor_function(); //監測函數
Display_function(); //顯示函數
Manage_function(); //處理函數time_num++; //計時變量+1
Delay_ms(10);
if(time_num %5 == 0) //最小系統LED閃爍LED_SYS = ~LED_SYS;
if(time_num >= 5000){time_num = 0;}}
}/****
*******按鍵函數
*****/
void Key_function(void)
{key_num = Chiclet_Keyboard_Scan(0); //按鍵掃描
if(key_num != 0) //有按鍵按下{
switch(key_num){
case 1: //按鍵1:切換界面flag_display++;
if(flag_display >= 6)flag_display = 0;Oled_Clear_All();
break;case 2: //按鍵2:加鍵
switch(flag_display){
case 0: //界面0:風速切換
if(flag_mode==1){
//flag_mode = 1;motor_pwm+=300;
if(motor_pwm <= 900){
TIM_SetCompare1(MOTOR_DIANJI_TIM,855+motor_pwm/20); }
else{motor_pwm = 0;
TIM_SetCompare1(MOTOR_DIANJI_TIM,motor_pwm);}}
break;case 1: //界面1:溫度最大值+1
if(temp_max < 99)temp_max++;
break;case 2: //界面2:溫度最小值+1
if(temp_min < temp_max-1)temp_min++;
break;case 3: //界面3:倒計時時+1
if(countdown_hour < 100)countdown_hour++;
break;case 4: //界面4:倒計時分+1countdown_minute++;
if(countdown_minute >= 60)countdown_minute = 0;
break;case 5: //界面5:倒計時秒+1countdown_second++;
if(countdown_second >= 60)countdown_second = 0;
break;default:
break;}
break;case 3: //按鍵3:減鍵
switch(flag_display){
case 0: //界面0:搖頭模式切換
if(flag_direction == 0)flag_direction = 1;
elseflag_direction = 0;
break;case 1: //界面1:溫度最大值-1
if(temp_max > temp_min+1)temp_max--;
break;case 2: //界面2:溫度最小值-1
if(temp_min > 0)temp_min--;
break;case 3: //界面3:倒計時時-1
if(countdown_hour > 0)countdown_hour--;
break;case 4: //界面4:倒計時分-1countdown_minute--;
if(countdown_minute < 0)countdown_minute = 59;
break;case 5: //界面5:倒計時秒-1countdown_second--;
if(countdown_second < 0)countdown_second = 59;
break;default:
break;}
break;case 4: //按鍵4:切換模式
if(flag_display == 0){
if(flag_mode == 0){flag_mode = 1;}
else{flag_mode = 0;}}
break;default:
break;}}
}/****
*******監測函數
*****/
void Monitor_function(void)
{
if(flag_display == 0) //測量界面{
if(time_num % 10 == 0) //約2s檢測一次{temp_value = DS18B20_Get_Temp(); //獲取溫度值}if(USART2_WaitRecive() == 0) //接收語音指令{
if(usart2_buf[0] == 0x01) //0x01:切換手動模式{flag_mode = 1;}
else if(usart2_buf[0] == 0x02) //0x02:切換自動模式{flag_mode = 0;}
else if(usart2_buf[0] == 0x03) //0x03:關搖頭{flag_direction = 0;}
else if(usart2_buf[0] == 0x04) //0x04:開搖頭{flag_direction = 1;}
else if(usart2_buf[0] == 0x05) //0x05:減風速{flag_mode = 1;motor_pwm-=300;
if(motor_pwm > 0){
TIM_SetCompare1(MOTOR_DIANJI_TIM,855+motor_pwm/20);}
else{motor_pwm = 0;
TIM_SetCompare1(MOTOR_DIANJI_TIM,motor_pwm);}}
else if(usart2_buf[0] == 0x06) //0x06:加風速{flag_mode = 1;motor_pwm+=300;
if(motor_pwm <= 900){
TIM_SetCompare1(MOTOR_DIANJI_TIM,855+motor_pwm/20);}
else{motor_pwm = 900;
TIM_SetCompare1(MOTOR_DIANJI_TIM,motor_pwm);}}
USART2_Clear();}}
}/****
*******顯示函數
*****/
void Display_function(void)
{
switch(flag_display) //根據不同的顯示模式標志位,顯示不同的界面{
case 0: //界面0:測量界面,顯示溫度、人體、檔位、搖頭、倒計時時間
Oled_ShowCHinese(1, 0, "溫度:");
sprintf(display_buf,"%d.%dC ",temp_value/10,temp_value%10);
Oled_ShowString(1, 6, display_buf);Oled_ShowCHinese(2, 0, "人體:");
if(HUMAN == 1)
Oled_ShowCHinese(2, 3, "有人");
else
Oled_ShowCHinese(2, 3, "沒有");if(motor_pwm == 0)
Oled_ShowCHinese(3, 0, "關閉");
else if(motor_pwm == 300)
Oled_ShowCHinese(3, 0, "一檔");
else if(motor_pwm == 600)
Oled_ShowCHinese(3, 0, "二檔");
else if(motor_pwm == 900)
Oled_ShowCHinese(3, 0, "三檔");if(flag_direction == 1)
Oled_ShowCHinese(3, 5, "搖頭開");
else
Oled_ShowCHinese(3, 5, "搖頭關");sprintf(display_buf,"%02d:%02d:%02d",countdown_hour,countdown_minute,countdown_second);
Oled_ShowString(4,0,display_buf);if(flag_mode == 0)
Oled_ShowCHinese(4, 5, "自動");
else
Oled_ShowCHinese(4, 5, "手動");
break;case 1: //界面1:顯示設置溫度最大值
Oled_ShowCHinese(1,0,"設置溫度最大值");
if(time_num % 5 == 0){
sprintf(display_buf,"%d ",temp_max);
Oled_ShowString(2, 6, display_buf);}
if(time_num % 10 == 0){
Oled_ShowString(2, 6, " ");}
break;case 2: //界面2:顯示設置溫度最小值
Oled_ShowCHinese(1,0,"設置溫度最小值");
if(time_num % 5 == 0){
sprintf(display_buf,"%d ",temp_min);
Oled_ShowString(2, 6, display_buf);}
if(time_num % 10 == 0){
Oled_ShowString(2, 6, " ");}
break;case 3: //界面3:顯示設置倒計時時
Oled_ShowCHinese(1,1,"設置倒計時時");
if(time_num % 5 == 0){
sprintf(display_buf,"%02d:%02d:%02d",countdown_hour,countdown_minute,countdown_second);
Oled_ShowString(2,4,display_buf);}
if(time_num % 10 == 0){
Oled_ShowString(2,4," ");}
break;case 4: //界面4:顯示設置倒計時分
Oled_ShowCHinese(1,1,"設置倒計時分");
if(time_num % 5 == 0){
sprintf(display_buf,"%02d:%02d:%02d",countdown_hour,countdown_minute,countdown_second);
Oled_ShowString(2,4,display_buf);}
if(time_num % 10 == 0){
Oled_ShowString(2,7," ");}
break;case 5: //界面5:顯示設置倒計時秒
Oled_ShowCHinese(1,1,"設置倒計時秒");
if(time_num % 5 == 0){
sprintf(display_buf,"%02d:%02d:%02d",countdown_hour,countdown_minute,countdown_second);
Oled_ShowString(2,4,display_buf);}
if(time_num % 10 == 0){
Oled_ShowString(2,10," ");}
break;default:
break;}
}/****
*******處理函數
*****/
void Manage_function(void)
{
if(flag_display == 0) //測量界面{
if(flag_mode == 0) //自動模式下,(有人時,溫度大于最大值三檔、大于中值二檔、大于最小值一檔,否則關閉){
if((temp_value > temp_max*10) && HUMAN == 1){motor_pwm = 900;
TIM_SetCompare1(MOTOR_DIANJI_TIM,855+motor_pwm/20);}
else if((temp_value > ((temp_max+temp_min)/2)*10) && HUMAN == 1){motor_pwm = 600;
TIM_SetCompare1(MOTOR_DIANJI_TIM,855+motor_pwm/20);}
else if((temp_value > temp_min*10) && HUMAN == 1){motor_pwm = 300;
TIM_SetCompare1(MOTOR_DIANJI_TIM,855+motor_pwm/20);}
else{motor_pwm = 0;
TIM_SetCompare1(MOTOR_DIANJI_TIM,motor_pwm);}}if(countdown_hour != 0 || countdown_minute != 0 || countdown_second != 0) //通風倒計時不為0flag_countdown_begin = 1; //開始倒計時,進入定時器中斷if(flag_1s == 1) //1s到達{flag_1s = 0;if(countdown_second > 0) //倒計時秒>0countdown_second--; //倒計時秒-1else//倒計時秒=0{if(countdown_minute > 0) //如果倒計時分>0{countdown_minute--; //倒計時分-1countdown_second = 59; //倒計時秒=59}else//如果倒計時分=0{if(countdown_hour > 0) //如果倒計時時>0{countdown_hour--; //倒計時時-1countdown_minute = 59; //倒計時分=59countdown_second = 59; //倒計時秒=59}else//如果倒計時時=0,倒計時結束{flag_countdown_begin = 0;flag_countdown_end = 1;}}}}if(flag_countdown_end == 1) //倒計時結束,關閉風扇進入手動模式{flag_countdown_end = 0;motor_pwm = 0;TIM_SetCompare1(MOTOR_DIANJI_TIM,motor_pwm);Beep=1;
Delay_ms(1000);Beep=0;flag_mode = 1;}
if(motor_pwm == 0)flag_direction = 0;
if(flag_direction == 1) //如果在搖頭模式,步進電機搖頭{
if(flag_bujin_state == 0){flag_bujin_foreward = 1;flag_bujin_reversal = 0;}
else{flag_bujin_foreward = 0;flag_bujin_reversal = 1;}}
else{flag_bujin_foreward = 0;flag_bujin_reversal = 0;}}
else//設置界面,關閉風扇{flag_countdown_begin = 0;flag_direction = 0;flag_bujin_foreward = 0;flag_bujin_reversal = 0;motor_pwm = 0;
TIM_SetCompare1(MOTOR_DIANJI_TIM,motor_pwm);}
}