//======
#define Start_Halt 0 //0:開始就計時; 1:一開始先暫停
#define Speed_Up 1 //0:正常速度顯示 小時:分鐘; 1:加速顯示,分鐘以秒遞增
//======
#if (Speed_Up==1)
#define Time_Step 2 //2*0.5=1s 時間以秒為單位遞增(加快顯示)
#else
#define Time_Step 120 //120*0.5=60s 時間以分為單位遞增
#endif
//======
#define Time_Debounce 250000 //0.25s 避開按鈕開關彈跳的延遲
#define LSB 11 //共陰極腳索引最低位
#define MSB 8 //共陰極腳索引最高位
#define DPB 9 //共陰極腳索引次高位,顯示點用的位元
#include "seven_leds_4digits.h"
#include <TimerOne.h>
seven_leds_4 led(A0,3,4,5,6,7,8,9,10,11,12,13); //建立4位七段顯示器物件,同時設定對應控制腳位
/*
pin A0->A
pin 3 ->B
pin 4 ->C
pin 5 ->D
pin 6 ->E
pin 7 ->F
pin 8 ->G
pin 9 ->DP
pin 10->D1 (MSB)
pin 11->D2
pin 12->D3
pin 13->D4 (LSB)
*/
int val_temp=0,val=0; //序列埠輸入接收、儲存用變數
int val2=0; //顯示用變數
int com_i=LSB; //共陰極腳索引到最低位
int count_5ms=0, count_500ms=0; //時間累加變數
bool flag_5ms=0, flag_500ms=0, flag_1min=0; //時間點旗標
bool flag_dot=0; //LED點狀態旗標
bool flag_halt= Start_Halt; //時鐘暫停旗標
const byte INT0_Pin = 2;
void Timer1_ISR();
void Clock_SR();
void INT0_ISR();
unsigned long last_push=micros(); //紀錄開關按下時間
void setup()
{
//LED 設定
led.turn_off(); //共陰極腳位規劃輸出HIGH,確保先不亮燈
led.init(); //所有腳位初始化為輸出方能控制
//Timer1 設定
Timer1.initialize(5000); //每5000uSec=5mSec中斷一次
Timer1.attachInterrupt(Timer1_ISR); //指定中斷服務程式
//INT0 設定
pinMode(INT0_Pin, INPUT_PULLUP); //使用內部提升電阻
attachInterrupt(digitalPinToInterrupt(INT0_Pin), INT0_ISR, FALLING); //下緣(H轉L)觸發中斷
//串列埠 設定
Serial.begin(9600); //速率
//提示
if(flag_halt==1)
{
Serial.println("Clock paused...");
Serial.println("Enter 4 digits(without :) to set time HH:MM(24H)");
Serial.println("Press the button to alternately resume or pause.");
}
else
{
Serial.println("Clock is running...");
Serial.println("Enter 4 digits(without :) to set time HH:MM(24H)");
Serial.println("Press the button to alternately pause or resume.");
}
}
void loop()
{
if (Serial.available())
{
val_temp = Serial.parseInt();
Serial.print("=>Time entered: ");
Serial.print(val_temp/100);
Serial.print(":");
Serial.println(val_temp%100);
if (val_temp/100>23||val_temp%100>59) //檢查輸入時間合理性
Serial.println("Wrong!!! Re-enter 4 digits(without :) to set time HH:MM(24H)");
else
{
val=val_temp; //合理時,更新時間
Serial.println("Enter 4 digits(without :) to set time HH:MM(24H)");
}
}
/*每5m秒
產生500m秒旗標
*/
if(flag_5ms==1)
{
flag_5ms=0;
if(flag_halt==0) //未暫停
count_5ms++;
if(count_5ms>=100)
{
flag_500ms=1;
count_5ms=0;
}
}
/*每0.5秒
1. 改變LED點狀態旗標,做點滅依據
2. 產生1分鐘旗標
*/
if(flag_500ms==1)
{
flag_500ms=0; //旗標先清0
flag_dot=!flag_dot; //改變LED點狀態旗標,做點滅依據
count_500ms++; //增加計數
if(count_500ms>=Time_Step)
{
flag_1min=1; //產生1分鐘旗標
count_500ms=0; //重置計數
}
}
/*每1分
1. 呼叫時鐘函數 遞增val(HH:MM)
*/
if(flag_1min==1)
{
flag_1min=0; //旗標先清0
Clock_SR(); //呼叫時鐘函數
}
}
//以下為自訂函數
/*處理時間中斷
1. 設flag_5ms旗標,另由主程式依旗標處理
2. 輪位點燈(視覺暫留) 改放中斷內,優先權高可避免輸入時影響顯示
*/
void Timer1_ISR()
{
flag_5ms=1;
val2=led.display(val2,com_i);
com_i--; //預換位置索引
if(com_i<MSB) //已超過最高位索引
{
com_i=LSB; //重置索引到最低位
val2=val; //重新載值
}
}
/*處理時鐘數字增加、歸零及進位
每分依狀態將 當作時鐘時和分的四位數 HH:MM 做適當遞增、進位歸零
23:59->00:00 59分加1=分歸零 23時+1=00時
12:59->13:00 59分加1=分歸零 時加1
12:34->12:35 單純分加1
*/
void Clock_SR(void)
{
if (val==2359) //23:59 加1分等於數字全歸零 00:00
val=0;
else if(val%100==59) //xx:59 (但非23:59)
{
val=val-59+100; //加1分等於分數歸零,時加1 (xx+1):00
}
else //58分或以下,直接加1
val++;
}
/*處理按鈕開關
先檢查進中斷的時間差,若小於Time_Debounce,則視為開關彈跳
若非彈跳,則將flag_halt(暫停與否旗標)反相,供時鐘暫停與否判斷
*/
void INT0_ISR()
{
if ( (unsigned long) (micros()-last_push) >= Time_Debounce )
{
flag_halt=!flag_halt;
last_push=micros();
}
}