設計一個單片機控制的簡易定時報警器。要求根據設定的初始值(1-59秒)進行倒計時,當計時到0時數碼管閃爍“00”(以1Hz閃爍),按鍵功能如下:
(1)設定鍵:在倒計時模式時,按下此鍵后停止倒計時,進入設置狀態;如果已經處于設置狀態則此鍵無效。
(2)增一鍵:在設置狀態時,每按一次遞增鍵,初始值的數字增1。
(3)遞一鍵:在設置狀態時,每按一次遞減鍵,初始值的數字減1。
(4)確認鍵:在設置狀態時,按下此鍵后,單片機按照新的初始值進行倒計時及顯示倒計時的數字。如果已經處于計時狀態則此鍵無效。
3.1.2 模塊1:系統設計
(1)任務分析與整體設計思路
根據題目的要求,需要實現如下幾個方面的功能。
計時功能:要實現計時功能則需要使用定時器來計時,通過設置定時器的初始值來控制溢出中斷的時間間隔,再利用一個變量記錄定時器溢出的次數,達到定時1秒中的功能。然后,當計時每到1秒鐘后,倒計時的計數器減1。當倒計時計數器到0時,觸發另一個標志變量,進入閃爍狀態。
顯示功能:顯示倒計時的數字要采用動態掃描的方式將數字拆成“十位”和“個位”動態掃描顯示。如果處于閃爍狀態,則可以不需要動態掃描顯示,只需要控制共陰極數碼管的位控線,實現數碼管的滅和亮。
鍵盤掃描和運行模式的切換:主程序在初始化一些變量和寄存器之后,需要不斷循環地讀取鍵盤的狀態和動態掃描數碼管顯示相應的數字。根據鍵盤的按鍵值實現設置狀態、計時狀態的切換。
?? (2)單片機型號及所需外圍器件型號,單片機硬件電路原理圖
選用MCS-51系列AT89S51單片機作為微控制器,選擇兩個四聯的共陰極數碼管組成8位顯示模塊,由于AT89S51單片機驅動能力有限,采用兩片74HC244實現總線的驅動,一個74HC244完成位控線的控制和驅動,另一個74HC244完成數碼管的7段碼輸出,在輸出口上各串聯一個100歐姆的電阻對7段數碼管限流。
由于鍵盤數量不多,選擇獨立式按鍵與P1口連接作為四個按鍵輸入。沒有鍵按下時P1.0-P1.3為高電平,當有鍵按下時,P1.0-P1.3相應管腳為低電平。電路原理圖如圖3-1所示。
圖3-1 定時報警器電路原理圖
(3)程序設計思路,單片機資源分配以及程序流程
?①單片機資源分配
采用單片機的P3口作為按鍵的輸入,使用獨立式按鍵與P3.0-P3.3連接,構成四個功能按鍵。
在計時功能中,需要三個變量分別暫存定時器溢出的次數(T1_cnt)、倒計時的初始值(init_val)以及當前倒計時的秒數(cnt_val)。
按鍵掃描功能中,需要兩個變量,一個變量(key_val_new)用來存儲當前掃描的鍵值(若無按鍵按下則為255),另一個變量(key_val_old)用來存儲上一次掃描的鍵值。只有這兩個變量值不一樣時,才能說明是一次新的按鍵按下或彈起了,同時將新的鍵值賦給key_val_old變量。
在顯示功能中,需要定義一組數組(code類型),值為0-9數字對應的數碼管7段碼。還需要定義一個變量(show_val)暫存要顯示的數據,用于動態掃描顯示中。
在整個程序中,定義了一個狀態變量(state_val)用來存儲當前單片機工作在哪種狀態。
②程序設計思路
鑒于題目要求,存在三種工作模式:初始值設置模式、倒計時模式、計時到0時的閃爍模式。變量state_val為0時,處于倒計時模式。變量state_val為1時,處于初始值設置模式。變量state_val為2時,處于閃爍模式。這些狀態的切換取決于按下哪一個鍵以及是否計時到0。狀態的切換圖如圖3-2
?圖3-2 狀態的切換
單片機復位之后,默認處于倒計時模式,啟動定時器,定時器每隔250us溢出一次,根據定時器溢出次數來計時,到1秒時將時間的計數器減1。當“設置鍵”按下時,變量state_val由0變為1,切換到設置模式。可以使用“遞增鍵”“遞減鍵”對計時初始值進行修改。按下“確認鍵”時,回到計時模式開始以新的初始值進行倒計時。當倒計時到0時,變量state_val由1變為2,處于閃爍狀態,在這種狀態下,根據按鍵的情況分別又切換到計時和設置狀態。
③程序流程
主程序首先需要初始化定時器的參數和一些變量,然后進入一個循環結構,在循環中始終只做兩件事,一是鍵盤的掃描,二是數碼管的動態掃描。
在掃描鍵盤后,根據前一次按鍵的結果是否與本次鍵值相同。如果不同,表示有鍵按下或彈起,同時用本次按鍵值更新上一次的按鍵值。這樣設計旨在避免一個按鍵長時間按下時被重復判為有新鍵按下,使得當前按下的鍵只有松開后,下一次按下時才算為一次新的按鍵。
根據按鍵的值分別改變變量(state_val)的值或者在設置狀態時的倒計時初始值。完整的主程序圖如圖3-3所示。
?圖3-3 主程序的流程圖
??? 在定時器的參數中,選擇定時器T1的8位自動裝載模式,每250us產生一次溢出中斷,中斷服務程序如圖3-4所示。
中斷服務程序流程圖
(4)軟硬件調試方案
???? 軟件調試方案:偉福軟件中,在“文件\新建文件”中,新建C語言源程序文件,編寫相應的程序。在“文件\新建項目”的菜單中,新建項目并將C語言源程序文件包括在項目文件中。
?在 “項目\編譯”菜單中將C源文件編譯,檢查語法錯誤及邏輯錯誤。在編譯成功后,產生以 “*.hex”和“*.bin” 后綴的目標文件。
硬件調試方案:在設計平臺中,將單片機的P3.0-P3.3分別與獨立式鍵盤的相應位通過插線連接起來。
在偉福中將程序文件編譯成目標文件后,運行MCU下載程序,選擇相應的flash 數據文件,點擊“編程”按鈕,將程序文件下載到單片機的Flash中。
然后,上電重新啟動單片機,檢查所編寫的程序是否達到題目的要求,是否全面完整地完成試題的內容。
3.1.3 程序設計(僅供參考的C語言源程序)
//晶振:11.0592M? T1-250微秒 按鍵P10 P11 P12 P13
/*變量的定義:
? show_val:???? 顯示的值0-59
? init_val:???? 初始值
? state_val:??? 狀態值 0-計數狀態;1-設置狀態;2-閃爍狀態
? shan_val:
? key_val1:???? 四個按鍵的值 255-無鍵;1-設置鍵? 2-增一鍵? 3-減一鍵? 4-確定鍵
? T1_cnt:?????? 定時器計數溢出數
? cnt_val:????? 倒計時的數值
? led_seg_code:數碼管7段碼
*/
#include "reg51.h"???? //包含文件
sbit???? P1_0=P1^0;??? //設置鍵
sbit???? P1_1=P1^1;??? //增一鍵
sbit???? P1_2=P1^2;??? //減一鍵
sbit???? P1_3=P1^3;??? //確定鍵
unsigned char data shan_val;? //閃爍時LED的開/關狀態
unsigned char data cnt_val;?? //保存倒計數的當前值
unsigned int? data T1_cnt;??? //保存定時器溢出次數
unsigned char data key_val_new,key_val_old;//存放當前掃描的鍵和前一次按下的鍵值
unsigned char data state_val; //狀態值
unsigned char data show_val;? //存放需要在數碼管顯示的數字
unsigned char data init_val;? //暫存倒計數的初始值
char code led_seg_code[10]={0x3f,0x06,0x05b,0x04f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//----------延時--------------
void delay(unsigned int i)?? //大約延時i*2個微秒
{? while(--i);}
//-----------按鍵掃描-------------
unsigned char scan_key()
{? unsigned char i;
?? i=P1&0x0f;
?? delay(100);?????????? //延時,去抖動
?? if (i==(P1&0x0f))
?? { if (P1_0==0)
???? {? i=1;??? }
???? else
???? { if (P1_1==0)
?????? { i=2;}
?????? else
?????? { if (P1_2==0)
????????? { i=3;}
????????? else
????????? { if (P1_3==0)
??????????? { i=4;} }
?????? }? }? }
?? else
?? {? i=255; }
?? return i;
}
//---------數碼管顯示---------------
void led_show(unsigned int v)
{
?unsigned char i;
?if (state_val!=2)???? //動態掃描
?{i=v%10;????????????? //取要顯示的數的個位
? P0=led_seg_code[i];? //轉換為7段碼
? P2=0xfe;???????????? //顯示個位
? delay(15);?????????? //延時
? i=v%100/10;????????? //取十位
? P0=led_seg_code[i];? //轉換為7段碼
? P2=0xfd;???????????? //顯示十位
? delay(5);??????????? //延時
?}
?else
?{ P0=led_seg_code[0]; //處于閃爍狀態
?? if (shan_val)
?? { P2=0xff; }??????? //將數碼管的關閉
?? else
?? { P2=0xfc; }???????? //將數碼管的打開
?}
}
//----------定時器T1中斷服務程序---------------
void? timer1() interrupt 3???? //T1中斷,250us中斷一次
{ T1_cnt++;
? switch (state_val)
?? { case 0:
???????? if(T1_cnt>3999)?????? //如果計數>3999, 計時1s
???????? {? T1_cnt=0;
??????????? if(cnt_val!=0)
??????????? { cnt_val--;}
??????????? else
??????????? {state_val=2;}???? //定時計數到0時,切換狀態
??????????? show_val=cnt_val;
????????? }
????????? break;
????? case 2:
???????? if(T1_cnt>1999)??????? //如果計數>1999, 計時0.5s
???????? { T1_cnt=0; shan_val=!shan_val; } //閃爍狀態
???????? break;
???? }
}
//---------主程序----------------
main()
{init_val=59;???????? //初始化各變量
?cnt_val=init_val;
?show_val=cnt_val;
?state_val=0;
?key_val_old=255;
?T1_cnt=0;
?shan_val=0; //初始化51的寄存器
TMOD=0x20;? //用T1計時 8位自動裝載定時模式
?TH1=0x19;?? //250微秒溢出一次;?? 250=(256-x)*12/11.0592 -> x= 230.4
?TL1=0x19;
?EA=1;?????? //打開總中斷允許
?ET1=1;????? //開中斷允許
?TR1=1;????? //開定時器T1
while(1)
{? key_val_new=scan_key();???????? // 255表示無鍵按下
?? if (key_val_new!=key_val_old)
{ // 只有當前掃描的鍵值與上次掃描的不同,才判斷是有鍵按下
???? key_val_old=key_val_new;
???? switch (key_val_new)
???? { case 1: //設置鍵
?????????????? state_val=1;??????? //處于設置狀態
?????????????? TR1=1;????????????? //停止計時
?????????????? show_val=init_val;? //顯示原來的倒計數初始值
?????????????? break;
?????? case 2: if(state_val==1)??? //只有在設置狀態,增1鍵才有用
?????????????? { if (init_val>0)?? //更改原來的倒計數初始值
???????????????? {init_val--; }
???????????????? else
???????????????? {init_val=59;}
???????????????? show_val=init_val;//顯示更改后的倒計數初始值
?????????????? }
?????????????? break;
?????? case 3: if(state_val==1)??? //只有在設置狀態,減1鍵才有用
?????????????? {? if (init_val<59) //更改原來的倒計數初始值
???????????????? {init_val++; }
???????????????? else
???????????????? {init_val=0;}
???????????????? show_val=init_val; //顯示更改后的計數初始值
?????????????? }
?????????????? break;
?????? case 4: if(state_val!=0)???? //如果已處于計數模式,確認鍵不起作用
?????????????? { cnt_val=init_val;? //將初始值賦給計數變量
???????????????? show_val=cnt_val;? //將計數變量的數字顯示
???????????????? TR1=1;???????????? //啟動定時器T1
???????????????? state_val=0;?????? //將狀態切換為計數模式
?????????????? }
?????????????? break;
????? }
?? }
?? led_show(show_val);????????????? //動態掃描
}
}
評論
查看更多