項(xiàng)目概述
本教程將指導(dǎo)你如何使用STM32F407VET6零知增強(qiáng)板實(shí)現(xiàn)一個(gè)功能強(qiáng)大的四路獨(dú)立計(jì)時(shí)器。每個(gè)計(jì)時(shí)器可以獨(dú)立控制,支持開(kāi)始、暫停和重置功能,并具備定時(shí)報(bào)警功能(4小時(shí)或每小時(shí)觸發(fā))。項(xiàng)目結(jié)合了TFT顯示屏、蜂鳴器和按鈕控制,提供了一個(gè)直觀的用戶界面。
目錄
一、硬件準(zhǔn)備
二、軟件環(huán)境配置
三、核心代碼解析
四、項(xiàng)目演示效果
五、常見(jiàn)問(wèn)題解答
六、完整源碼獲取
核心功能
>四路獨(dú)立計(jì)時(shí)器:每個(gè)計(jì)時(shí)器獨(dú)立運(yùn)行,互不影響
>多種控制模式: 開(kāi)始、暫停、重置功能
>智能報(bào)警系統(tǒng): 4小時(shí)及以上每小時(shí)報(bào)警提示
>直觀的用戶界面:TFT顯示屏顯示計(jì)時(shí)器狀態(tài)
>聲音提示: 蜂鳴器提供報(bào)警音效
>長(zhǎng)/短按操作:按鈕支持不同時(shí)長(zhǎng)的操作
一、硬件準(zhǔn)備
1.1 硬件清單
主控板:STM32F407VET6零知增強(qiáng)板<
顯示屏:1.54英寸TFT顯示屏(ST7789驅(qū)動(dòng))<
蜂鳴器:有源蜂鳴器模塊<
LED: LED燈珠<
按鈕: 4個(gè)輕觸開(kāi)關(guān)<
連接線:杜邦線若干<
電源: 5V電源適配器或USB供電<
1.2硬件連接
模塊 | 零知增強(qiáng)板引腳 |
---|---|
TFT_CS | 53 |
TFT_DC | 2 |
TFT_MOSI | 51 |
TFT_SCLK | 52 |
TFT_RST | 4 |
蜂鳴器&LED | 3 |
按鈕1 | 14 |
按鈕2 | 15 |
按鈕3 | 16 |
按鈕4 | 17 |
1.3 連接硬件圖
主控零知增強(qiáng)板和ST7789顯示屏:
蜂鳴器和按鍵電路:
1.4 連接實(shí)物圖
二、軟件環(huán)境配置
1.零知開(kāi)源開(kāi)發(fā)工具(Lingzhi IDE)
2.安裝必要的庫(kù):
Adafruit_GFX
Adafruit_ST7789
3.配置開(kāi)發(fā)板類型:STM32F407VET6
三、核心代碼解析
1. 引腳定義與初始化
#include #include #include // 屏幕引腳配置 #define TFT_CS 53 #define TFT_RST 4 #define TFT_DC 2 #define TFT_MOSI 51 #define TFT_SCLK 52 // 使用硬件SPI Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST); // 蜂鳴器引腳 #define BUZZER_PIN 3 // 按鈕引腳 - 高電平觸發(fā) #define BUTTON_PIN1 14 #define BUTTON_PIN2 15 #define BUTTON_PIN3 16 #define BUTTON_PIN4 17 // 計(jì)時(shí)器結(jié)構(gòu) typedef struct { unsigned long totalSeconds; bool isRunning; bool isReset; unsigned long lastUpdateTime; // 每個(gè)計(jì)時(shí)器獨(dú)立的更新時(shí)間戳 unsigned long lastHourAlarm; // 上次小時(shí)報(bào)警時(shí)間戳 bool alarmTriggered; // 計(jì)時(shí)器報(bào)警狀態(tài) } Timer; Timer timers[4]; // 四個(gè)計(jì)時(shí)器
2. 按鈕狀態(tài)檢測(cè)
// 按鈕狀態(tài) enum ButtonState { BUTTON_RELEASED, BUTTON_PRESSED }; // 按鈕結(jié)構(gòu) typedef struct { uint8_t pin; ButtonState state; ButtonState lastState; unsigned long pressStartTime; } Button; Button buttons[4]; // 當(dāng)前選中的計(jì)時(shí)器 int selectedTimer = 0; bool alarmActive = false; // 報(bào)警激活狀態(tài) bool alarmSilenced = false; // 報(bào)警被靜音 unsigned long lastBeepTime = 0; // 上次蜂鳴器響的時(shí)間 unsigned long lastDebounceTime = 0; const unsigned long debounceDelay = 50; // 消抖時(shí)間(毫秒) // 報(bào)警參數(shù) const unsigned long ALARM_INTERVAL = 800; // 蜂鳴器報(bào)警間隔(ms) const unsigned long HOUR_SECONDS = 3600; // 1小時(shí)的秒數(shù) const unsigned long ALARM_HOURS = 4; // 報(bào)警小時(shí)數(shù) // PWM參數(shù) const int TONE_FREQUENCY = 2500; // 蜂鳴器頻率 (Hz) const int TONE_DURATION = 300; // 蜂鳴器單次響聲持續(xù)時(shí)間 (ms)
3. 初始化設(shè)置
void setup() { Serial.begin(9600); // 初始化蜂鳴器 pinMode(BUZZER_PIN, OUTPUT); digitalWrite(BUZZER_PIN, LOW); // 初始化按鈕 buttons[0] = {BUTTON_PIN1, BUTTON_RELEASED, BUTTON_RELEASED, 0}; buttons[1] = {BUTTON_PIN2, BUTTON_RELEASED, BUTTON_RELEASED, 0}; buttons[2] = {BUTTON_PIN3, BUTTON_RELEASED, BUTTON_RELEASED, 0}; buttons[3] = {BUTTON_PIN4, BUTTON_RELEASED, BUTTON_RELEASED, 0}; for (int i = 0; i < 4; i++) { pinMode(buttons[i].pin, INPUT); } // 初始化屏幕 tft.init(240, 320); tft.setRotation(1); tft.fillScreen(ST77XX_BLACK); // 初始化計(jì)時(shí)器 for (int i = 0; i < 4; i++) { timers[i].totalSeconds = 0; timers[i].isRunning = false; timers[i].isReset = true; timers[i].lastUpdateTime = 0; timers[i].lastHourAlarm = 0; timers[i].alarmTriggered = false; } // 繪制初始界面 drawTimers(); }
4. 主循環(huán)控制
void loop() { unsigned long currentMillis = millis(); // 更新所有正在運(yùn)行的計(jì)時(shí)器 for (int i = 0; i < 4; i++) { if (timers[i].isRunning) { // 每個(gè)計(jì)時(shí)器獨(dú)立更新 if (currentMillis - timers[i].lastUpdateTime >= 1000) { timers[i].totalSeconds++; timers[i].lastUpdateTime = currentMillis; // 檢查報(bào)警條件 checkAlarmConditions(i); // 只更新這個(gè)計(jì)時(shí)器的顯示 drawTimer(i); } } } // 處理按鈕事件 pollButtons(); handleButtonEvents(); // 處理報(bào)警聲音 updateAlarmSound(); delay(10); }
5. 報(bào)警系統(tǒng)實(shí)現(xiàn)
// 檢查報(bào)警條件 void checkAlarmConditions(int index) { // 檢查是否達(dá)到4小時(shí)或每小時(shí) if (timers[index].totalSeconds >= ALARM_HOURS * HOUR_SECONDS) { // 檢查是否達(dá)到新的小時(shí) if (timers[index].totalSeconds % HOUR_SECONDS == 0) { // 避免連續(xù)觸發(fā) if (timers[index].totalSeconds != timers[index].lastHourAlarm) { timers[index].alarmTriggered = true; alarmActive = true; alarmSilenced = false; timers[index].lastHourAlarm = timers[index].totalSeconds; } } } } // 更新報(bào)警聲音 void updateAlarmSound() { if (alarmActive && !alarmSilenced) { unsigned long currentMillis = millis(); // 每秒響一次(300ms開(kāi),800ms關(guān)) if (currentMillis - lastBeepTime >= ALARM_INTERVAL) { lastBeepTime = currentMillis; // 播放悅耳音調(diào) tone(BUZZER_PIN, TONE_FREQUENCY, TONE_DURATION); } } else { noTone(BUZZER_PIN); } } // 輪詢按鈕狀態(tài)(帶消抖) void pollButtons() { unsigned long currentMillis = millis(); for (int i = 0; i < 4; i++) { // 讀取按鈕狀態(tài)(高電平表示按下) ButtonState reading = (digitalRead(buttons[i].pin) == HIGH) ? BUTTON_PRESSED : BUTTON_RELEASED; // 如果狀態(tài)改變,重置消抖計(jì)時(shí)器 if (reading != buttons[i].lastState) { lastDebounceTime = currentMillis; } // 如果狀態(tài)穩(wěn)定時(shí)間超過(guò)消抖延遲 if ((currentMillis - lastDebounceTime) > debounceDelay) { // 更新按鈕狀態(tài) if (reading != buttons[i].state) { buttons[i].state = reading; // 記錄按下開(kāi)始時(shí)間 if (buttons[i].state == BUTTON_PRESSED) { buttons[i].pressStartTime = currentMillis; } } } // 保存當(dāng)前狀態(tài)用于下次比較 buttons[i].lastState = reading; } } // 靜音報(bào)警并清除報(bào)警狀態(tài) void silenceAlarm() { alarmActive = false; alarmSilenced = true; noTone(BUZZER_PIN); // 清除所有計(jì)時(shí)器的報(bào)警狀態(tài) for (int i = 0; i < 4; i++) { timers[i].alarmTriggered = false; // 重繪計(jì)時(shí)器以清除"ALARM"顯示 drawTimer(i); } }
6. 按鈕事件處理
void handleButtonEvents() { unsigned long currentMillis = millis(); bool buttonEventOccurred = false; int previousSelectedTimer = selectedTimer; //跟蹤之前的選擇 for (int i = 0; i < 4; i++) { if (buttons[i].state == BUTTON_PRESSED) { // 長(zhǎng)按檢測(cè)(超過(guò)1秒) if (currentMillis - buttons[i].pressStartTime > 1000) { // 長(zhǎng)按 - 只復(fù)位當(dāng)前選中的計(jì)時(shí)器 if (i == selectedTimer) { timers[i].totalSeconds = 0; timers[i].isRunning = false; timers[i].isReset = true; timers[i].lastUpdateTime = 0; timers[i].lastHourAlarm = 0; timers[i].alarmTriggered = false; drawTimer(i); silenceAlarm(); } buttonEventOccurred = true; } } else if (buttons[i].state == BUTTON_RELEASED) { // 按鈕釋放時(shí)檢測(cè)短按 if (buttons[i].pressStartTime > 0 && currentMillis - buttons[i].pressStartTime > debounceDelay && currentMillis - buttons[i].pressStartTime <= 1000) { buttonEventOccurred = true; // 短按 - 開(kāi)始/暫停計(jì)時(shí)器或切換計(jì)時(shí)器 if (i == selectedTimer) { timers[i].isRunning = !timers[i].isRunning; timers[i].isReset = false; timers[i].lastUpdateTime = millis(); } else { previousSelectedTimer = selectedTimer; selectedTimer = i; // 重繪所有計(jì)時(shí)器以更新選中框 drawTimer(previousSelectedTimer); drawTimer(selectedTimer); } // 只更新當(dāng)前計(jì)時(shí)器的顯示 drawTimer(i); } // 重置按下開(kāi)始時(shí)間 buttons[i].pressStartTime = 0; } } // 如果有按鈕事件,靜音報(bào)警并清除報(bào)警狀態(tài) if (buttonEventOccurred) { silenceAlarm(); } }
7. 用戶界面設(shè)計(jì)
void drawTimers() { tft.fillScreen(ST77XX_BLACK); // 繪制四個(gè)計(jì)時(shí)器區(qū)域 tft.drawRect(0, 0, 160, 120, ST77XX_WHITE); // 左上 tft.drawRect(160, 0, 160, 120, ST77XX_WHITE); // 右上 tft.drawRect(0, 120, 160, 120, ST77XX_WHITE); // 左下 tft.drawRect(160, 120, 160, 120, ST77XX_WHITE); // 右下 // 繪制所有計(jì)時(shí)器 for (int i = 0; i < 4; i++) { drawTimer(i); } } void drawTimer(int index) { int x, y; // 確定位置 switch (index) { case 0: x = 30; y = 50; break; // 左上 case 1: x = 190; y = 50; break; // 右上 case 2: x = 30; y = 170; break; // 左下 case 3: x = 190; y = 170; break; // 右下 default: return; } // 清除時(shí)間顯示區(qū)域(避免殘留字符) tft.fillRect(x, y, 100, 20, ST77XX_BLACK); // 設(shè)置文本顏色和大小 tft.setTextSize(2); tft.setTextColor(index == selectedTimer ? ST77XX_YELLOW : ST77XX_WHITE); // 計(jì)算時(shí)間 int hours = timers[index].totalSeconds / 3600; int minutes = (timers[index].totalSeconds % 3600) / 60; int seconds = timers[index].totalSeconds % 60; // 格式化時(shí)間 char timeStr[12]; sprintf(timeStr, "%02d:%02d:%02d", hours, minutes, seconds); // 顯示時(shí)間 tft.setCursor(x, y); tft.print(timeStr); // 清除狀態(tài)顯示區(qū)域 tft.fillRect(x, y + 30, 60, 10, ST77XX_BLACK); // 顯示狀態(tài) tft.setTextSize(1); tft.setCursor(x, y + 30); if (timers[index].isReset) { tft.print("Reset"); } else if (timers[index].isRunning) { tft.setTextColor(ST77XX_GREEN); tft.print("Running"); } else { tft.setTextColor(ST77XX_ORANGE); tft.print("Paused"); } // 顯示報(bào)警狀態(tài)(僅在報(bào)警觸發(fā)且未靜音時(shí)顯示) tft.fillRect(x + 60, y + 30, 40, 10, ST77XX_BLACK); if (timers[index].alarmTriggered && !alarmSilenced) { tft.setCursor(x + 60, y + 30); tft.setTextColor(ST77XX_MAGENTA); tft.print("ALARM"); } //優(yōu)化選擇高亮繪圖 static int lastSelected = -1; //顯示選中框 if (index == selectedTimer || index == lastSelected) { int rectX, rectY; switch (index) { case 0: rectX = 2; rectY = 2; break; case 1: rectX = 162; rectY = 2; break; case 2: rectX = 2; rectY = 122; break; case 3: rectX = 162; rectY = 122; break; } //通過(guò)繪制黑色清除先前的選擇 tft.drawRect(rectX, rectY, 156, 116, ST77XX_BLACK); tft.drawRect(rectX+1, rectY+1, 154, 114, ST77XX_BLACK); //如果這是選定的計(jì)時(shí)器,則繪制新選區(qū) if (index == selectedTimer) { tft.drawRect(rectX, rectY, 156, 116, ST77XX_YELLOW); tft.drawRect(rectX+1, rectY+1, 154, 114, ST77XX_YELLOW); } } lastSelected = selectedTimer; }
使用說(shuō)明
>選擇計(jì)時(shí)器:短按對(duì)應(yīng)按鈕選擇要操作的計(jì)時(shí)器(黃色邊框表示選中)
>開(kāi)始/暫停:短按當(dāng)前選中計(jì)時(shí)器的按鈕
>重置計(jì)時(shí)器:長(zhǎng)按(>1秒)當(dāng)前選中計(jì)時(shí)器的按鈕
>報(bào)警靜音:任意按鈕操作可暫時(shí)靜音報(bào)警
>報(bào)警條件:
計(jì)時(shí)達(dá)到4小時(shí)及以上時(shí),每小時(shí)觸發(fā)一次報(bào)警
顯示屏顯示"ALARM"狀態(tài)
蜂鳴器發(fā)出提示音
四、項(xiàng)目演示效果
1. 四小時(shí)報(bào)警功能演示
當(dāng)任意一個(gè)計(jì)時(shí)器達(dá)到4小時(shí)或以上時(shí),系統(tǒng)會(huì)觸發(fā)報(bào)警功能:
計(jì)時(shí)器區(qū)域顯示"ALARM"文字、報(bào)警狀態(tài)會(huì)持續(xù)顯示直到用戶操作、蜂鳴器發(fā)出悅耳的2500Hz提示音
2. 報(bào)警靜音操作演示
報(bào)警觸發(fā)后,用戶可以通過(guò)以下方式關(guān)閉報(bào)警:
按下任意一個(gè)計(jì)時(shí)器按鈕(短按)、蜂鳴器立即停止發(fā)聲、屏幕上"ALARM"提示消失、系統(tǒng)進(jìn)入靜音狀態(tài)
長(zhǎng)按當(dāng)前選中計(jì)時(shí)器的按鈕(>1秒)、除了停止報(bào)警,還會(huì)重置該計(jì)時(shí)器、計(jì)時(shí)器歸零并顯示"Reset"狀態(tài)
3. 多計(jì)時(shí)器獨(dú)立運(yùn)行演示
四個(gè)計(jì)時(shí)器可完全獨(dú)立操作:
四個(gè)計(jì)時(shí)器可同時(shí)開(kāi)始計(jì)時(shí)、每個(gè)計(jì)時(shí)器獨(dú)立記錄時(shí)間、顯示屏分區(qū)顯示各自狀態(tài)
>選擇計(jì)時(shí)器1:短按按鈕1
>開(kāi)始/暫停:再次短按
>按鈕1重置:長(zhǎng)按按鈕1(>1秒)
其他計(jì)時(shí)器操作類似
4. 項(xiàng)目視頻演示
https://blog.csdn.net/lingzhilab/article/details/148974172?spm=1001.2014.3001.5502#t18
達(dá)到四小時(shí)計(jì)數(shù)后持續(xù)報(bào)警,按下任意鍵清除報(bào)警聲,在四個(gè)小時(shí)基礎(chǔ)上每過(guò)一個(gè)小時(shí)報(bào)警一次。
五、常見(jiàn)問(wèn)題解答
Q1: 報(bào)警聲音可以調(diào)整嗎?
A: 可以,在代碼中修改以下參數(shù):
const int TONE_FREQUENCY = 2500; // 頻率(Hz),范圍0-5000 const int TONE_DURATION = 300; // 單次響聲持續(xù)時(shí)間(ms) const unsigned long ALARM_INTERVAL = 800; // 報(bào)警間隔(ms)
Q2: 為什么我的報(bào)警沒(méi)有觸發(fā)?
A: 請(qǐng)檢查:
計(jì)時(shí)器是否達(dá)到4小時(shí)(顯示04:00:00)
ALARM_HOURS參數(shù)設(shè)置是否正確(默認(rèn)為4)
蜂鳴器接線是否正確(正負(fù)極)
Q3: 如何改變報(bào)警的小時(shí)閾值?
A: 修改代碼中的常量定義:
const unsigned long ALARM_HOURS = 2; // 2小時(shí)觸發(fā)報(bào)警
Q4: 按鈕按下后沒(méi)有響應(yīng)怎么辦?
A: 檢查:
按鈕是否正常連接(用萬(wàn)用表測(cè)試通斷)
按鈕引腳配置是否正確
消抖參數(shù)是否合適(可調(diào)整debounceDelay)
六、完整源碼獲取
百度網(wǎng)盤(pán)獲取鏈接,通過(guò)網(wǎng)盤(pán)分享的文件:STM32-Multi-Timer.zip
https://pan.baidu.com/s/1v9NuKp690DUWvqAC73VV8Q?pwd=3wv2
壓縮包內(nèi)容:
/STM32-Multi-Timer
├── TIM_NVIC.ino // 主程序
├── Adafruit-ST7735-Library-master/ // 所需庫(kù)文件
├── SPI/ // 電路圖
^_^本教程詳細(xì)展示了四路獨(dú)立計(jì)時(shí)器的報(bào)警功能和操作演示,并提供了完整的源碼獲取方式。這個(gè)項(xiàng)目不僅具有實(shí)際應(yīng)用價(jià)值,還涵蓋了嵌入式開(kāi)發(fā)的多個(gè)關(guān)鍵技術(shù)點(diǎn):
>多任務(wù)處理(四個(gè)獨(dú)立計(jì)時(shí)器)>用戶界面設(shè)計(jì)(TFT顯示)
>中斷處理(按鈕響應(yīng)) >報(bào)警系統(tǒng)設(shè)計(jì)(聲光提示)
>狀態(tài)機(jī)實(shí)現(xiàn)(計(jì)時(shí)器狀態(tài)管理)
?零知開(kāi)源是一個(gè)真正屬于國(guó)人自己的開(kāi)源軟硬件平臺(tái),在開(kāi)發(fā)效率上超越了Arduino平臺(tái)并且更加容易上手,大大降低了開(kāi)發(fā)難度。
?零知開(kāi)源在軟件方面提供了完整的學(xué)習(xí)教程和豐富示例代碼,讓不懂程序的工程師也能非常輕而易舉的搭建電路來(lái)創(chuàng)作產(chǎn)品,測(cè)試產(chǎn)品??靵?lái)動(dòng)手試試吧!
?訪問(wèn)零知開(kāi)源平臺(tái),獲取更多實(shí)戰(zhàn)項(xiàng)目和教程資源吧!
www.lingzhilab.com
審核編輯 黃宇
-
計(jì)時(shí)器
+關(guān)注
關(guān)注
1文章
432瀏覽量
33648 -
STM32F407VET6
+關(guān)注
關(guān)注
2文章
6瀏覽量
3221
發(fā)布評(píng)論請(qǐng)先 登錄
STM32F407VET6和STM32F407IET6有什么區(qū)別?
零知開(kāi)源——STM32F4驅(qū)動(dòng)MAX31865實(shí)現(xiàn)PT100高精度測(cè)溫
零知開(kāi)源——基于STM32F407VET6零知增強(qiáng)板的四路獨(dú)立計(jì)時(shí)器

基于STM32F407VET6零知增強(qiáng)板的四路獨(dú)立計(jì)時(shí)器
STM32F407VET6的片上資源描述
STM32F103VET6/STM32F407VET6原理圖相關(guān)資料分享
如何對(duì)STM32F407VET6的串口進(jìn)行回環(huán)測(cè)試呢
可以使用ST Link對(duì)STM32F407VET6黑板進(jìn)行編程嗎?
STM32F407VET6核心板的電路原理圖免費(fèi)下載

基于STM32F407VET6的CS1237驅(qū)動(dòng)程序
stm32f407vet6原理介紹

評(píng)論