目錄
0 前言
1 硬件準備
2 功能介紹
3 前置配置
3.1 時鐘配置
3.2?文件配置
4 功能實現?
4.1 按鍵功能
4.2 屏幕功能
4.3 調速功能
4.4 倒計時功能
4.5 搖頭功能
4.6 測距待機功能
0 前言
由于時間關系,暫停詳細更新,本文章中,只會記錄重要代碼,關于cubmx的配置以及引腳配置,請自行下載文件配置
【免費】基于STM32的多功能風扇資源-CSDN文庫
1 硬件準備
STM32F103C8T6 * 1
面包板 * 1
OLED顯示屏(SSD131590) * 1
RGB三色全彩LED模塊 * 1
SG90舵機 * 1
HC-SR04超聲波模塊 * 1
130直流電機 * 1
風扇頭 * 1
L298N電機驅動模塊 * 1
直插2腳微動按鍵 * 2
USB轉TTL模塊-CH340模塊 * 1
ST-LINK V2 * 1
杜邦線 (公對公、公對母、母對母)
跳線
2 功能介紹
(1)完成手動調節(按鍵)調節風扇三檔轉速(檔位轉速分別為30%、50%、80%)
(2)實現按鍵定時功能(短按一次時間加5秒,長按后倒計時開始,倒計時結束后風扇停止轉動)
(3)實現風速不同檔位顯示,一檔風速亮白燈,二檔風速亮藍燈,三檔風速亮紅燈,并通過OLED屏幕顯示當前燈的顏色和定時時間(用英文和數字)
(4)實現通過串口顯示當前風速
(5)以超聲波為總開關控制系統運作(在20厘米內系統才可工作)
(6)利用舵機實現風扇搖頭功能,并通過按鍵進行控制
3 前置配置
3.1 時鐘配置
?設置高速外部時鐘:晶振
設置時鐘頻率為72Mhz?
3.2?文件配置
修改文件名稱以及選擇IDE:MDK-ARM?
為每一個外設單獨生成一對.c/.h文件(模塊化,方便管理)
4 功能實現?
4.1 按鍵功能
在interrupt.c中使用回調函數判斷定時器2,進行按鍵掃描(按鍵具體的功能不在此文件中)?
// interrupt.cif(htim == &htim2) // button{keys[0].clickState = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6); // left BUTTONkeys[1].clickState = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5); // right BUTTONfor(int i=0; i<=1; i++){switch(keys[i].stage){case 0:{//judge clickif(keys[i].clickState == 0) keys[i].stage = 1; //go next stage}break;case 1:{if(keys[i].clickState == 0){// is true click// clear timekeys[i].pressTime = 0;// go next stagekeys[i].stage = 2;}}break;case 2:{if(keys[i].clickState == 1) // release click{//judge click which button and its typeif(keys[i].pressTime < 30){// short clickchooseButton(i, click);} else {// long clickchooseButton(i, longClick);}// reset stage keys[i].stage = 0;} else { // still click// time ++keys[i].pressTime++;}}break;}}}
按鍵具體的功能被拆分在了buttonFunciton.c中
從上到下依次為:
按鍵1的單擊(調速)、按鍵1的長按(搖頭)
按鍵2的單擊(計時器加5)、按鍵2的長按(倒計時開始)
#include "buttonFunction.h"// to judge which button is press down
void chooseButton(uint8_t key, uint8_t type){switch(key){case 0: // button 1{if(type == 0){// click —— Toggle Speed Mode and Oled Update//WindSpeedspeedLevel++;speedLevel %= 4;SetWindSpeed();UsartSpeed();HomePage();} else if (type == 1){// long click —— Toggle Shaking ModeisShake = !isShake;if(isShake){HAL_TIM_Base_Start_IT(&htim1);shakeIsBegin = 1;} else {HAL_TIM_Base_Stop_IT(&htim1);shakeIsBegin = 0;}}}break;case 1: // button 2{if(type == 0){// click —— Count Down Number Add and Oled UpdatetimeCount += 5;HomePage();} else if (type == 1){// long click —— Count Down Startif(timeCount > 0){HAL_TIM_Base_Start_IT(&htim4);isBegin = 1;}}}break;}
}
4.2 屏幕功能
OLED使用Keysking的模塊文件,這里就不展示了,使用方法見【STM32入門教程-2024】第14集 如何在OLED屏幕上揮毫_嗶哩嗶哩_bilibili
// page.c
#include "page.h"// ——————————————Init—————————————————
void PageInit(void){HAL_Delay(20); // 單片機啟動比OLED上電快,需要延遲等待一下OLED_Init(); // 初始化OLED
}// ——————————————View——————————————————
void CopyrightPage(void) {OLED_NewFrame();OLED_DrawImage(5,1, &logoImg, OLED_COLOR_NORMAL);OLED_PrintString(85, 4, "創客", &font16x16, OLED_COLOR_NORMAL);OLED_PrintString(85, 24, "中心", &font16x16, OLED_COLOR_NORMAL);OLED_PrintString(85, 44, "出品", &font16x16, OLED_COLOR_NORMAL);OLED_ShowFrame();
}void HomePage(void){OLED_NewFrame();HomePageTitle();HomePageSpeedLight();if(timeCount > 0){HomePageCountDown();}OLED_ShowFrame();
}void SleepPage(void){OLED_NewFrame();OLED_PrintString(32, 10, "待機中...", &font16x16, OLED_COLOR_NORMAL);OLED_PrintString(4, 35, "~(p≧ w≦ q)~", &font12x12, OLED_COLOR_NORMAL);OLED_ShowFrame();
}// ————————————Component————————————————// Home Title
void HomePageTitle(void){OLED_PrintString(32, 10, "智能の扇", &font16x16, OLED_COLOR_NORMAL);
}// Home Body
void HomePageSpeedLight(void){StopAllLight();switch(speedLevel){case 0:{OLED_PrintString(50, 30, "STOP", &font16x16, OLED_COLOR_NORMAL);}break;case 1:{OLED_PrintString(46, 30, "WHITE", &font16x16, OLED_COLOR_NORMAL);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);}break;case 2:{OLED_PrintString(50, 30, "BLUE", &font16x16, OLED_COLOR_NORMAL);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);}break;case 3:{OLED_PrintString(54, 30, "RED", &font16x16, OLED_COLOR_NORMAL);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);}break;}
}// Home Bottom
void HomePageCountDown(void){char arr[40];sprintf(arr, "%d", timeCount);if(timeCount >= 10000){OLED_PrintString(46, 50, arr, &font16x16, OLED_COLOR_NORMAL);} else if(timeCount >= 1000){OLED_PrintString(50, 50, arr, &font16x16, OLED_COLOR_NORMAL);} else if(timeCount >= 100){OLED_PrintString(54, 50, arr, &font16x16, OLED_COLOR_NORMAL);} else if(timeCount >= 10){OLED_PrintString(58, 50, arr, &font16x16, OLED_COLOR_NORMAL);} else {OLED_PrintString(62, 50, arr, &font16x16, OLED_COLOR_NORMAL);}
}// Stop All Light
void StopAllLight(void){HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
}
// page.h
#ifndef __PAGE_H__
#define __PAGE_H__#include "main.h"
#include "string.h"
#include "oled.h"
#include "stdio.h"
#include "stdbool.h"// ——————————————Internal Api————————————————
// view
void PageInit(void);
void CopyrightPage(void);
void HomePage(void);
void SleepPage(void);// Component
void HomePageTitle(void);
void HomePageSpeedLight(void);
void HomePageCountDown(void);
void StopAllLight(void);
// ——————————————External Api————————————————
// windSpeed
extern uint8_t speedLevel;
extern uint8_t tempSpeedLevel;
// countDown
extern uint16_t timeCount;
// ——————————————————————————————————————————
#endif
4.3 調速功能
// windSpeed.c
#include "windSpeed.h"void SetWindSpeed(void){switch(speedLevel){case 0:{__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, StopSpeed);}break;case 1:{__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, LowSpeed);}break;case 2:{__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, MidSpeed);}break;case 3:{__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, HighSpeed);}break;}
}void UsartSpeed(void){sprintf(arr, "當前檔位為:%d", speedLevel);HAL_UART_Transmit(&huart1, (uint8_t *)arr, sizeof(arr), HAL_MAX_DELAY);}
// windSpeed.h
#ifndef __WINDSPEED_H__
#define __WINDSPEED_H__#include "main.h"
#include "tim.h"
#include "usart.h"
#include <stdio.h>// ————————————Internal Api——————————————————#define StopSpeed 0
#define LowSpeed 300
#define MidSpeed 500
#define HighSpeed 800
uint8_t speedLevel = 0;
uint8_t tempSpeedLevel = 10;
void SetWindSpeed(void);
void UsartSpeed(void);
char arr[99];// ————————————External Api——————————————————// 0// ——————————————————————————————————————————#endif
4.4 倒計時功能
// interrupt.cif(htim == &htim4){ // Time Count Downif(timeCount > 1){timeCount--;} else {timeCount--;speedLevel = 0;SetWindSpeed();HAL_TIM_Base_Stop_IT(&htim4);isBegin = 0;HAL_TIM_Base_Stop_IT(&htim1);shakeIsBegin = 0;UsartSpeed();}HomePage();}
?倒計時功能主要在interrupt中實現,此處只聲明了個全局變量?
// countDown.c
#include "countDown.h"
// countDown.h
#ifndef __COUNTDOWN_H__
#define __COUNTDOWN_H__#include "main.h"
#include "stdbool.h"
// ————————————Internal Api——————————————————uint16_t timeCount;
bool isBegin = 0;// ————————————External Api——————————————————// 0// ——————————————————————————————————————————#endif
4.5 搖頭功能
舵機教程:【STM32】動畫講解輸入捕獲 并實現超聲波測距_嗶哩嗶哩_bilibili?
// shake.c
#include "shake.h"int shakeNumber = 1500; //500——2500 duty
bool shakeMode = 0;void shakeApi(void){if(shakeMode == 0){// forwardif(shakeNumber < 2500){shakeNumber += shakeSpeed;} else {shakeMode = 1;}__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, shakeNumber);} else {// reverseif(shakeNumber > 500){shakeNumber -= shakeSpeed;} else {shakeMode = 0;}__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, shakeNumber);}}
// shake.h
#ifndef __SHAKE_H__
#define __SHAKE_H__#include "main.h"
#include "tim.h"
#include "stdbool.h"// ————————————Internal Api——————————————————#define shakeSpeed 10 // 5(slow) 10(normal) 20(fast) 100(very fast)
void shakeApi(void);
bool shakeIsBegin = 0;// ————————————External Api——————————————————// 0// ——————————————————————————————————————————#endif
啟用中斷:
// interrupt.c
if(htim == &htim1){ // ShakeshakeApi();
}
4.6 測距待機功能
為了使系統正常工作時(20cm以內)?,能繼續進行上一次的數據(如倒計時從上一次離開開始,繼續倒計時),這里并沒有直接修改存儲該數據的變量,而是直接將所有功能暫停(具體實現見StopAll函數),恢復時再讀取存儲數據的變量
// ultrasound.c
#include "ultrasound.h"void TriggerUltrasound(void){// Send trigger signalHAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET);
// HAL_Delay(1);for(uint32_t i=0; i<11; i++);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);// Reset counter__HAL_TIM_SET_COUNTER(&htim3, 0);// WaitHAL_Delay(50);
}void MeasurementDistance(void){timeCountBefore = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_1);timeCountAfter = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_2);distance = (timeCountAfter - timeCountBefore) * 0.034 / 2;
}void StopAll(void){// stop all lightStopAllLight();// stop wideSpeed__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 0);// stop count downHAL_TIM_Base_Stop_IT(&htim4);// stop shakeHAL_TIM_Base_Stop_IT(&htim1);// sleep pageSleepPage();
}void StartAll(void){// start count downif(timeCount > 0 && isBegin){HAL_TIM_Base_Start_IT(&htim4);}// start shakeif(shakeIsBegin){HAL_TIM_Base_Start_IT(&htim1);}// start wideSpeedSetWindSpeed();// home page - start all lightHomePage();
}
// ultrasound.h
#ifndef __ULTRASOUND_H__
#define __ULTRASOUND_H__#include "main.h"
#include "tim.h"
#include "stdbool.h"// ————————————Internal Api——————————————————uint16_t timeCountBefore;
uint16_t timeCountAfter;
float distance;
void TriggerUltrasound(void);
void MeasurementDistance(void);
void StopAll(void);
void StartAll(void);// ————————————External Api——————————————————// page - light
extern void HomePage(void);
extern void SleepPage(void);
extern void StopAllLight(void);
// windSpeed
extern uint8_t speedLevel;
extern void SetWindSpeed(void);
// count down
extern uint16_t timeCount;
extern bool isBegin;
// shake
extern bool shakeIsBegin;
// ——————————————————————————————————————————#endif
輸入捕獲中斷:
測距思想:一個通道讀上升沿,一組的另一個通道讀下降沿,并且在讀到下降沿的時候進行中斷
教程:【STM32】動畫講解輸入捕獲 并實現超聲波測距_嗶哩嗶哩_bilibili?
// interrupt.c
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){if(htim == &htim3 && htim -> Channel == HAL_TIM_ACTIVE_CHANNEL_2){ // ultrasound// Measurement Ultrasound DistanceMeasurementDistance();
// sprintf(aeee, "%f", distance);
// HAL_UART_Transmit(&huart1, (uint8_t *)aeee, sizeof(aeee), HAL_MAX_DELAY);if(distance > 22){// sleepStopAll();} else if(distance >= 0 && distance <= 22){// normalStartAll();}}}