#include <reg52.h>
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
sbit KEY1 = P2^4;
sbit KEY2 = P2^5;
sbit KEY3 = P2^6;
sbit KEY4 = P2^7;
unsigned char code LedChar[] = { //數碼管顯示字符轉換表
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[6] = { //數碼管顯示緩沖區
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
unsigned char KeySta[4] = { //按鍵當前狀態
1, 1, 1, 1
};
bit StopwatchRunning = 0; //秒表運行標志
bit StopwatchRefresh = 1; //秒表計數刷新標志
unsigned char DecimalPart = 0; //秒表的小數部分
unsigned int IntegerPart = 0; //秒表的整數部分
unsigned char T0RH = 0; //T0 重載值的高字節
unsigned char T0RL = 0; //T0 重載值的低字節
void ConfigTimer0(unsigned int ms);
void StopwatchDisplay();
void KeyDriver();
void main(){
EA = 1; //開總中斷
ENLED = 0; //使能選擇數碼管
ADDR3 = 1;
P2 = 0xFE; //P2.0 置 0,選擇第 4 行按鍵作為獨立按鍵
ConfigTimer0(2); //配置 T0 定時 2ms
while (1){
if (StopwatchRefresh){ //需要刷新秒表示數時調用顯示函數
StopwatchRefresh = 0;
StopwatchDisplay();
}
KeyDriver(); //調用按鍵驅動函數
}
}
/* 配置并啟動 T0,ms-T0 定時時間 */
void ConfigTimer0(unsigned int ms){
unsigned long tmp; //臨時變量
tmp = 11059200 / 12; //定時器計數頻率
tmp = (tmp * ms) / 1000; //計算所需的計數值
tmp = 65536 - tmp; //計算定時器重載值
tmp = tmp + 18; //補償中斷響應延時造成的誤差
T0RH = (unsigned char)(tmp>>8); //定時器重載值拆分為高低字節
T0RL = (unsigned char)tmp;
TMOD &= 0xF0; //清零 T0 的控制位
TMOD |= 0x01; //配置 T0 為模式 1
TH0 = T0RH; //加載 T0 重載值
TL0 = T0RL;
ET0 = 1; //使能 T0 中斷
TR0 = 1; //啟動 T0
}
/* 秒表計數顯示函數 */
void StopwatchDisplay(){
signed char i;
unsigned char buf[4]; //數據轉換的緩沖區
//小數部分轉換到低 2 位
LedBuff[0] = LedChar[DecimalPart%10];
LedBuff[1] = LedChar[DecimalPart/10];
//整數部分轉換到高 4 位
buf[0] = IntegerPart%10;
buf[1] = (IntegerPart/10)%10;
buf[2] = (IntegerPart/100)%10;
buf[3] = (IntegerPart/1000)%10;
for (i=3; i>=1; i--){ //整數部分高位的 0 轉換為空字符
if (buf[i] == 0){
LedBuff[i+2] = 0xFF;
}else{
break;
}
}
for ( ; i>=0; i--){ //有效數字位轉換為顯示字符
LedBuff[i+2] = LedChar[buf[i]];
}
LedBuff[2] &= 0x7F; //點亮小數點
}
/* 秒表啟停函數 */
void StopwatchAction(){
if (StopwatchRunning){ //已啟動則停止
StopwatchRunning = 0;
}else{ //未啟動則啟動
StopwatchRunning = 1;
}
}
/* 秒表復位函數 */
void StopwatchReset(){
StopwatchRunning = 0; //停止秒表
DecimalPart = 0; //清零計數值
IntegerPart = 0;
StopwatchRefresh = 1; //置刷新標志
}
/* 按鍵驅動函數,檢測按鍵動作,調度相應動作函數,需在主循環中調用 */
void KeyDriver(){
unsigned char i;
static unsigned char backup[4] = {1,1,1,1};
for (i=0; i<4; i++){ //循環檢測 4 個按鍵
if (backup[i] != KeySta[i]){ //檢測按鍵動作
if (backup[i] != 0){ //按鍵按下時執行動作
if (i == 1){ //Esc 鍵復位秒表
StopwatchReset();
}else if (i == 2){//回車鍵啟停秒表
StopwatchAction();
}
}
backup[i] = KeySta[i]; //刷新前一次的備份值
}
}
}
/* 按鍵掃描函數,需在定時中斷中調用 */
void KeyScan(){
unsigned char i;
static unsigned char keybuf[4] = { //按鍵掃描緩沖區
0xFF, 0xFF, 0xFF, 0xFF
};
//按鍵值移入緩沖區
keybuf[0] = (keybuf[0] << 1) | KEY1;
keybuf[1] = (keybuf[1] << 1) | KEY2;
keybuf[2] = (keybuf[2] << 1) | KEY3;
keybuf[3] = (keybuf[3] << 1) | KEY4;
//消抖后更新按鍵狀態
for (i=0; i<4; i++){
if (keybuf[i] == 0x00){
//連續 8 次掃描值為 0,即 16ms 內都是按下狀態時,可認為按鍵已穩定的按下
KeySta[i] = 0;
}else if (keybuf[i] == 0xFF){
//連續 8 次掃描值為 1,即 16ms 內都是彈起狀態時,可認為按鍵已穩定的彈起
KeySta[i] = 1;
}
}
}
/* 數碼管動態掃描刷新函數,需在定時中斷中調用 */
void LedScan(){
static unsigned char i = 0; //動態掃描索引
P0 = 0xFF; //關閉所有段選位,顯示消隱
P1 = (P1 & 0xF8) | i; //位選索引值賦值到 P1 口低 3 位
P0 = LedBuff[i]; //緩沖區中索引位置的數據送到 P0 口
if (i < 5){ //索引遞增循環,遍歷整個緩沖區
i++;
}else{
i = 0;
}
}
/* 秒表計數函數,每隔 10ms 調用一次進行秒表計數累加 */
void StopwatchCount(){
if (StopwatchRunning){ //當處于運行狀態時遞增計數值
DecimalPart++; //小數部分+1
if (DecimalPart >= 100){ //小數部分計到 100 時進位到整數部分
DecimalPart = 0;
IntegerPart++; //整數部分+1
if (IntegerPart >= 10000){ //整數部分計到 10000 時歸零
IntegerPart = 0;
}
}
StopwatchRefresh = 1; //設置秒表計數刷新標志
}
}
/* T0 中斷服務函數,完成數碼管、按鍵掃描與秒表計數 */
void InterruptTimer0() interrupt 1{
static unsigned char tmr10ms = 0;
TH0 = T0RH; //重新加載重載值
TL0 = T0RL;
LedScan(); //數碼管掃描顯示
KeyScan(); //按鍵掃描
//定時 10ms 進行一次秒表計數
tmr10ms++;
if (tmr10ms >= 5){
tmr10ms = 0;
StopwatchCount(); //調用秒表計數函數
}
} |