【STM32】綜合練習——智能風扇系統

目錄

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();}}}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/900792.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/900792.shtml
英文地址,請注明出處:http://en.pswp.cn/news/900792.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

任務擴展-輸入商品原價,折扣并計算促銷后的價格

1.在HbuilderX軟件中創建項目&#xff0c;把項目的路徑放在xampp中的htdocs 2.創建php文件&#xff1a;price.php,price_from.php 3.在瀏覽器中&#xff0c;運行項目效果&#xff0c;通過xampp中admin進行運行瀏覽&#xff0c;在后添加文件名稱即可&#xff0c;注意&#xff…

3D Gaussian Splatting as MCMC 與gsplat中的應用實現

3D高斯潑濺(3D Gaussian splatting)自2023年提出以后,相關研究paper井噴式增長,盡管出現了許多改進版本,但依舊面臨著諸多挑戰,例如實現照片級真實感、應對高存儲需求,而 “懸浮的高斯核” 問題就是其中之一。浮動高斯核通常由輸入圖像中的曝光或顏色不一致引發,也可能…

【軟件測試】Postman中如何搭建Mock服務

在 Postman 中&#xff0c;Mock 服務是一項非常有用的功能&#xff0c;允許你在沒有實際后端服務器的情況下模擬 API 響應。通過創建 Mock 服務&#xff0c;你可以在開發階段或測試中模擬 API 的行為&#xff0c;幫助團隊成員進行前端開發、API 測試和集成測試等工作。 Mock 服…

Spring-MVC

Spring-MVC 1.SpringMVC簡介 - SpringMVC概述 SpringMVC是一個基于Spring開發的MVC輕量級框架&#xff0c;Spring3.0后發布的組件&#xff0c;SpringMVC和Spring可以無縫整合&#xff0c;使用DispatcherServlet作為前端控制器&#xff0c;且內部提供了處理器映射器、處理器適…

關于Spring MVC中@RequestParam注解的詳細說明,用于在前后端參數名稱不一致時實現參數映射。包含代碼示例和總結表格

以下是關于Spring MVC中RequestParam注解的詳細說明&#xff0c;用于在前后端參數名稱不一致時實現參數映射。包含代碼示例和總結表格&#xff1a; 1. 核心作用 RequestParam用于顯式綁定HTTP請求參數到方法參數&#xff0c;支持以下場景&#xff1a; 參數名不一致&#xff1…

MySQL主從復制技術詳解:原理、實現與最佳實踐

目錄 引言&#xff1a;MySQL主從復制的技術基礎 MySQL主從復制的實現機制 復制架構與線程模型 復制連接建立過程 數據變更與傳輸流程 MySQL不同復制方式的特點與適用場景 異步復制&#xff08;Asynchronous Replication&#xff09; 全同步復制&#xff08;Fully Synch…

ROS Master多設備連接

Bash Shell Shell是位于用戶與操作系統內核之間的橋梁&#xff0c;當用戶在終端敲入命令后&#xff0c;這些輸入首先會進入內核中的tty子系統&#xff0c;TTY子系統負責捕獲并處理終端的輸入輸出流&#xff0c;確保數據正確無誤的在終端和系統內核之中。Shell在此過程不僅僅是…

Trae + LangGPT 生成結構化 Prompt

Trae LangGPT 生成結構化 Prompt 0. 引言1. 安裝 Trae2. 克隆 LangGPT3. Trae 和 LangGPT 聯動4. 集成到 Dify 中 0. 引言 Github 上 LangGPT 這個項目&#xff0c;主要向我們介紹了寫結構化Prompt的一些方法和示例&#xff0c;我們怎么直接使用這個項目&#xff0c;輔助我們…

《安富萊嵌入式周報》第352期:手持開源終端,基于參數陣列的定向揚聲器,炫酷ASCII播放器,PCB電阻箱,支持1Ω到500KΩ,Pebble智能手表代碼重構

周報匯總地址&#xff1a;嵌入式周報 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬漢嵌入式論壇 - Powered by Discuz! 視頻版 https://www.bilibili.com/video/BV1DEf3YiEqE/ 《安富萊嵌入式周報》第352期&#xff1a;手持開源終端&#x…

python 淺拷貝copy與深拷貝deepcopy 理解

一 淺拷貝與深拷貝 1. 淺拷貝 淺拷貝只復制了對象本身&#xff08;即c中的引用&#xff09;。 2. 深拷貝 深拷貝創建一個新的對象&#xff0c;同時也會創建所有子對象的副本&#xff0c;因此新對象與原對象之間完全獨立。 二 代碼理解 1. 案例一 a 10 b a b 20 print…

day22 學習筆記

文章目錄 前言一、遍歷1.行遍歷2.列遍歷3.直接遍歷 二、排序三、去重四、分組 前言 通過今天的學習&#xff0c;我掌握了對Pandas的數據類型進行基本操作&#xff0c;包括遍歷&#xff0c;去重&#xff0c;排序&#xff0c;分組 一、遍歷 1.行遍歷 intertuples方法用于遍歷D…

SpringMVC的請求-文件上傳

文件上傳客戶端三要素 1. 表單項type“file” 2. 表單的提交方式是post 3. 表單的enctype屬性是多部分表單形式&#xff0c;及enctype“multipart/form-data” <% page contentType"text/html;charsetUTF-8" language"java" %> <html> <he…

在Ubuntu系統如何讓MySQL服務器支持遠程連接

目錄 問題描述 解決方案 步驟一&#xff1a;檢查MySQL配置文件 ?編輯 步驟二&#xff1a;修改bind-address參數 ?編輯 步驟三&#xff1a;重啟MySQL服務 步驟四&#xff1a;驗證更改 步驟五&#xff1a;檢查防火墻設置 步驟六&#xff1a;測試遠程連接 注意事項 …

JSON工具-JSONUtil

對象轉JSON JSONUtil.toJsonStr可以將任意對象&#xff08;Bean、Map、集合等&#xff09;直接轉換為JSON字符串。 如果對象是有序的Map等對象&#xff0c;則轉換后的JSON字符串也是有序的。 //region 處理POST請求&#xff0c;將TreeMap轉換為JSON字符串返回/*** 處理POST請求…

死鎖 手撕死鎖檢測工具

目錄 引言 一.理論聯立 1.死鎖的概念和原因 2.死鎖檢測的基本思路 3.有向圖在死鎖檢測中的應用 二.代碼實現案例&#xff08;我們會介紹部分重要接口解釋&#xff09; 1.我們定義一個線性表來存線程ID和鎖ID 2.表中數據的查詢接口 3.表中數據的刪除接口 4.表中數據的添…

Java 中 SQL 注入問題剖析?

一、引言? 在當今數字化時代&#xff0c;數據是企業和組織的核心資產之一。許多應用程序都依賴于數據庫來存儲和管理數據&#xff0c;而 Java 作為一種廣泛使用的編程語言&#xff0c;常被用于開發與數據庫交互的應用程序。然而&#xff0c;SQL 注入這一安全漏洞卻如同隱藏在…

安全理念和安全產品發展史

從安全理念的發展歷史來看,技術與產品的演進始終圍繞 “威脅對抗” 與 “業務適配” 兩大核心展開。以下從七個關鍵階段解析安全技術與產品的發展脈絡,并結合最新實踐與未來趨勢提供深度洞察: 一、密碼學奠基階段(1970s 前) 安全理念:以 “信息保密” 為核心,防御手段…

【Ansible自動化運維】二、Playbook 深入探究:構建復雜自動化流程

? 在 Ansible 自動化運維體系中&#xff0c;Playbook 是極為關鍵的部分。它允許我們以一種結構化、可重復的方式定義和執行一系列復雜的任務&#xff0c;從而構建高效的自動化流程。本篇文章將深入探究 Ansible Playbook 的各個方面&#xff0c;助您掌握構建復雜自動化…

springboot項目中常用的工具類和api

在Spring Boot項目中&#xff0c;開發者通常會依賴一些工具類和API來簡化開發、提高效率。以下是一些常用的工具類及其典型應用場景&#xff0c;涵蓋 Spring 原生工具、第三方庫&#xff08;如Hutool、Guava&#xff09; 和 Java 自帶工具。 1. Spring Framework 自帶工具類 (…

23種設計模式-行為型模式-模板方法

文章目錄 簡介場景解決代碼關鍵優化點 總結 簡介 模板方法是一種行為設計模式&#xff0c;它在超類中定義了一個算法的框架&#xff0c;允許子類在不修改結構的情況下重寫算法的特定步驟。 場景 假如你正在開發一款分析文檔的數據挖掘程序。用戶需要向程序輸入各種格式&…