需求:無鍵盤啟動自動走時;鍵盤輸入 A/E 進入設置模式,分別設置時/分/秒,含合法性校驗(時<24,分/秒<60);設置完成按 E 啟動走時。
struct Time { uint8_t h, m, s; }。RUNNING:正常走時,每秒更新顯示。SET_HOUR、SET_MIN、SET_SEC:設置狀態,接收數字鍵輸入,顯示閃爍對應位。kbhit() + getch())或中斷驅動。printf("\r%02d:%02d:%02d", h, m, s) 或七段數碼管/LED 矩陣驅動。Sleep(1000)、Linux sleep(1)、嵌入式硬體定時器中斷(1Hz)。Time current = {0,0,0};
State state = RUNNING;
while (1) {
if (kbhit()) {
char key = getch();
handle_key(key, &state, ¤t);
}
if (state == RUNNING) {
update_display(current);
sleep_ms(1000);
tick_time(¤t); // 秒進位邏輯
} else {
update_display_blink(current, state); // 設置位閃爍
sleep_ms(200);
}
}
void handle_key(char key, State *st, Time *t) {
switch(*st) {
case RUNNING:
if (key == 'A') *st = SET_HOUR;
break;
case SET_HOUR:
if (isdigit(key)) input_digit(&t->h, key);
else if (key == 'E') { if (t->h < 24) *st = SET_MIN; else beep_error(); }
break;
case SET_MIN:
if (isdigit(key)) input_digit(&t->m, key);
else if (key == 'E') { if (t->m < 60) *st = SET_SEC; else beep_error(); }
break;
case SET_SEC:
if (isdigit(key)) input_digit(&t->s, key);
else if (key == 'E') { if (t->s < 60) *st = RUNNING; else beep_error(); }
break;
}
}
| 難點 | 解決方案 |
|---|---|
| 控制台無閃爍顯示 | 用 \r 回車符覆蓋同一行,fflush(stdout) 強制刷新 |
| 非阻塞鍵盤輸入 | Windows: _kbhit()/_getch() (conio.h);Linux: select() 監聽 stdin 或 ncurses 庫 |
| 精確 1 秒定時 | 嵌入式用硬體定時器;PC 端用高精度計時器 QueryPerformanceCounter / clock_gettime 校準 sleep |
| 數字鍵多位輸入 | 維護輸入緩衝區,如連按 '1','2' 組成 12,超時或滿兩位自動確認 |
tick_time 放入 1Hz 定時器中斷服務程序 (ISR);主循環處理按鍵掃描與顯示刷新。#include <stdio.h>
#include <windows.h> // Sleep, system
#include <conio.h> // _kbhit, _getch
typedef struct { uint8_t h, m, s; } Time;
void tick(Time *t) {
if (++t->s == 60) { t->s = 0; if (++t->m == 60) { t->m = 0; if (++t->h == 24) t->h = 0; } }
}
int main() {
Time t = {0,0,0};
enum { RUN, SET_H, SET_M, SET_S } state = RUN;
int blink = 0; char buf[4] = {0}; int buf_len = 0;
while (1) {
if (_kbhit()) {
char k = _getch();
if (state == RUN && k == 'A') { state = SET_H; buf_len = 0; }
else if (state != RUN) {
if (k >= '0' && k <= '9' && buf_len < 2) { buf[buf_len++] = k; }
else if (k == 'E') {
int val = atoi(buf);
if (state == SET_H && val < 24) { t.h = val; state = SET_M; buf_len = 0; }
else if (state == SET_M && val < 60) { t.m = val; state = SET_S; buf_len = 0; }
else if (state == SET_S && val < 60) { t.s = val; state = RUN; }
else { printf("\a"); } // 蜂鳴報錯
}
}
}
if (state == RUN) {
printf("\r%02d:%02d:%02d", t.h, t.m, t.s);
fflush(stdout);
Sleep(1000);
tick(&t);
} else {
const char *labels[] = {"", "H", "M", "S"};
blink = !blink;
char h = (state==SET_H && !blink) ? ' ' : '0'+t.h/10;
// 簡化顯示邏輯...
printf("\r設置%s: %s", labels[state], buf);
fflush(stdout);
Sleep(200);
}
}
}
電子鐘看似簡單,實則包含狀態機、定時器、輸入處理、顯示驅動、數據校驗等嵌入式核心要素。建議先在 PC 控制台跑通邏輯,再移植至單片機(STM32/51/ESP32),逐步替換 HAL 層。