本來是用匯編寫的,但是匯編屬實學的不怎么好,於是自學用C來寫,效果還不錯
比賽計分器
一、 需求分析(從用戶角度 UML)
1.1 設計內容
本項目設計開發一款個比賽計分器,在重競技比賽項目中(拳擊,跆拳道),由3個裁判員對運動員評判。當一方運動員有效攻擊后,裁判員立刻給這個運動員加分。如果有2個或2個以上裁判同時(一秒內)按鍵,則給該方運動員加一分。比賽期間(2分鍾)得分多判定為勝利方。
1.2 設計目標
比賽計分器要實現的功能如下:
(1)能夠提供三個裁判的評分按鍵,當一秒鍾內有2個或者3個裁判按鍵,則有效分加1。
(2)顯示1分鍾倒計時,並可以按鍵控制實踐走停,開始和結束時候有蜂鳴器響聲,時間開始響1s,時間結束響1s。
(3)按下按鍵可以對兩隊進行警告,同時記錄警告次數。
(4)對誤判可以進行減分。
(5)數碼管顯示時間,得分,警告次數。
1.3實驗平台和工具
實驗平台為:keil
實驗工具為:89c51單片機
1.3 設計的功能分析
單片機實現比賽計分器功能。使用單片機數碼管,獨立按鍵,蜂鳴器以及矩陣鍵盤四個模塊。數碼管用來顯示時間倒計時,雙方警告次數以及得分。蜂鳴器在開始時以及快結束前五秒時響1s。獨立按鍵k1按下開始,k2按下暫停,k3按下第一隊警告次數加1,k4按下第二隊警告次數加1。矩陣按鍵s1,s2,s3,一秒內按下任意兩個或三個,第一隊得分加1,按下s4得分減1。矩陣按鍵s5,s6,s7,一秒內按下任意兩個或三個,第二隊得分加1,按下s8得分減1。
1.5 設計的非功能性分析
通過本程序的設計可設計出一個簡易的比賽計分器,應用於實際生活中也是非常簡易方便的。
二、 總體設計
2.1總體方案設計
(1)裁判按鍵設置
S1:提供裁判1對第一隊的評分按鍵
S2:提供裁判2對第一隊的評分按鍵
S3:提供裁判3對第一隊的評分按鍵
S5:提供裁判1對第二隊的評分按鍵
S6:提供裁判2對第二隊的評分按鍵
S7:提供裁判3對第二隊的評分按鍵
(2)評分判斷設置
通過啟動定時器T0來判斷1s之內有幾個裁判按鍵,設計一個50ms的定時器,中斷20次后即為1s,若一秒鍾內有2個或者3個裁判按鍵,則有效分加1;否則,不加分。
(3) 分數顯示設置
通過P0口將分數顯示至數碼管。
(4) 減分設置
按下S4或S5第一隊或第二隊分數減1。
(5) 警告次數顯示設置
按下K3或K4,第一隊或第二隊警告次數加1。
(6) 開始,暫停設置
按下K1開始,按下K2暫停
2.2 演示支持方案(如何利用開發板資源或外接模塊,實現功能演示的方案,傳感器(或模擬)演示方案)
在單片機下載完成后,51單片機的8位動態數碼管全部顯示為0,
一二個顯示時間,三四個分別顯示兩隊警告次數,五六個顯示第一隊得分,七八個顯示第二隊得分。
在按下獨立按鍵K1后,第一二個數碼管顯示60並開始倒計時,同時蜂鳴器響1s,若按下獨立按鍵K2便暫停並且要再次按下K1才能開始。當倒計時為5s時,蜂鳴器也再次響1s。在這60s期間,矩陣按鍵s1,s2,s3在1秒內按下任意兩個第一隊得分加一,若出現誤判,按下矩陣按鍵s4使得分減一,若第一隊出現犯規可按下獨立按鍵K3便使警告次數加1。同理第二隊矩陣按鍵s5,s6,s7在1秒內按下任意兩個第二隊得分加一,若出現誤判,按下矩陣按鍵s8使得分減一,若第二隊出現犯規可按下獨立按鍵K4便使警告次數加1。
2.3 單片機資源分配設計(包括RAM資源分配,I/O資源分配)
沒有具體的要求。
三、 模塊設計與實現(介紹本課設主要模塊的設計思路,實現方法,可以通過流程圖,狀態圖,關鍵代碼,顯示效果等多種手段說明)
1.獨立按鍵模塊:
由圖可知k1對應的為p3.1口,k2對應的為p3.0口,k3對應的為p3.2口,k4對應的為p3.3口;本課設中使用到的是k1和k2按鍵,由上圖知按鍵的一端已經連接到GND,而在另一端則是通過按下來檢測,當未被按下時p3口的前四位默認為高電平,即1。按下后即為0,其中的抖動可使用延時來消除;代碼如圖:
- 動態數碼管:
首先是數碼管的片選,本次課設采用的52單片機使用的是38譯碼器,即如上圖中:通過p2.2、p2.3、p2.4三位2^3=8位來判斷使用哪一個數碼管工作(亮);例如當p2.2=1;p2.3=1;p2.4=0時,對應的十六進制數值為6,則從右往左數的第七個數碼管會亮,即圖中的LED7;具體對應關系如下圖:
P2.2 |
P2.3 |
P2.4 |
LED(位) |
0 |
0 |
0 |
LED1 |
0 |
0 |
1 |
LED2 |
0 |
1 |
0 |
LED3 |
0 |
1 |
1 |
LED4 |
1 |
0 |
0 |
LED5 |
1 |
0 |
1 |
LED6 |
1 |
1 |
0 |
LED7 |
1 |
1 |
1 |
LED8 |
然后是數碼管模塊的數字顯示,如下圖:
數碼管電路的接法有共陰和共陽兩種,本次用的單片機里的數碼管采用的是共陰接法。
從其中取出一個數碼管進行分析
單個數碼管:
此處為一個8位的數碼管,想要數碼管顯示出想要的數字,則需要對數碼管進行高低電平的設置1為高電平,0為低電平,分別對 a,b,c,d,e,f,g,dp進行1和0的編寫;
如圖中所示,數字2的 八位二進制就可以表示為 0101 1011,讀數為從dp依次讀到a,轉化為十六進制則是0x5b。
- 定時器中斷:
由該圖可知定時器T0的中斷入口為0BH,將中斷的子程序入口設為0BH即可。由於本課設整體就是基於時間的累加,因此核心也是定時器的計時功能。在使用定時器0工作於方式1,中斷使用定時器的溢出中斷,設置TH0、TL0初值為0D8H、0F0H,即可產生每10ms一次的溢出中斷,又定義R0為100,通過DJNZ指令達到使每過10ms*100=1s才執行對應的計時計費操作來實現功能。如圖:
- 蜂鳴器模塊:
蜂鳴器發聲原理是電流通過電磁線圈,使電磁線圈產生磁場來驅動振動膜發聲的,因此需要一定的電流才能驅動它,單片機IO引腳輸出的電流較小,單片機輸出的TTL電平基本上驅動不了蜂鳴器,因此需要增加一個電流放大的電路。S51增強型單片機實驗板通過一個三極管C8550來放大驅動蜂鳴器。蜂鳴器的正極接到VCC(+5V)電源上面,蜂鳴器的負極接到三極管的發射極E,三極管的基級B經過限流電阻R1后由單片機的P1.5引腳控制,當P1.5輸出高電平電磁式有源蜂鳴器,電磁式有源蜂鳴器產品質量好時,三極管T1截止,沒有電流流過線圈,蜂鳴器不發聲;當P1.5輸出低電平時,三極管導通,這樣蜂鳴器的電流形成回路,發出聲音。因此,我們可以通過程序控制P1.5腳的電平來使蜂鳴器發出聲音和關閉.程序中改變單片機P1.5引腳輸出波形的頻率,就可以調整控制蜂鳴器音調,產生各種不同音色、音調的聲音。
四、 系統集成設計(與其它系統的通信、連接,和相互作用設計,參考結構,根據項目增減)
沒有使用。
五、 系統測試
5.1 系統測試
5.2 單元測試
5.3非功能性測試
開始前;
開始時:
、
具體介紹:
六、 收獲與總結
通過本次單片機課程設計,我更加理解了實踐出真知的重要性,不僅要擁有足夠的理論知識,同時也應該懂得用於實踐,只有通過不斷反復的測試與失敗,才能獲得成功。當然,這其中也有很多問題,第一、由於對課本理論的不熟悉導致編程出現錯誤。第二,這次課設是對我的學習態度的一次檢驗。這次課設所遇到的多半問題多數都是由於我們不夠嚴謹,考慮問題全部全面,周不周到。第三,我認識到,無論做什么事情,只要你足夠堅強,有足夠的毅力與決心,有足夠的挑戰困難的勇氣,就沒有什么辦不到的。
附錄1原理圖
附錄2 源程序清單
#include <reg51.h>
#define uint unsigned int //無符號化
#define uchar unsigned char
uchar num = 0;
sbit led0 = P2^0; //位定義
sbit led2 = P2^2;
sbit led3 = P2^3;
sbit led4 = P2^4;
sbit beep = P1^5;
sbit K1 = P3^1;
sbit K2 = P3^0;
sbit K3 = P3^2;
sbit K4 = P3^3;
uchar DisplayData[8];
#define GPIO_KEY P1
uchar KeyValue1=0xff; //存放讀取到的鍵值
uchar KeyValue2=0xff;
uchar KeyValue=0xff;
uint time_jsq1=0;
uint time_jsq2=0;
uchar flag_start1=0; //記錄當前時間
uchar flag_start2=0;
uint time_start1=0; //記錄加分開始時間
uint time_start11=0; //記錄減分開始時間
uint time_start111=0;//記錄警告開始時間
uint time_start2=0;
uint time_start22=0;
uint time_start222=0;
uint time_end1=0; //記錄結束時間
uint time_end2=0;
uchar key_num1=-1; //記錄第一次讀取到的鍵值
uchar key_num11=-1; //記錄第二次讀取到的鍵值
uchar key_num2=-1;
uchar key_num22=-1;
uchar num_fenshu1=0; //記錄分數
uchar num_fenshu2=0;
uint i=0,time=0;
uint j=0,k=0,A=0;
uint A1=0;
uint jinggao1=0,jinggao2=0;
void KeyDown(void);
uchar code duanxuan[] = {0x3f,0x06,0x5b,0x4f, //0,1,2,3
0x66,0x6d,0x7d,0x07, //4,5,6,7
0x7f,0x6f,0x77,0x7c, //8,9,A,B
0x39,0x5e,0x79,0x71}; //C,D,E,F
/*******************************************************************************
* 函數名: : fp
* 功能 : 蜂鳴器延時
*******************************************************************************/
void fp(uint i) //蜂鳴器延時
{
while(i--);
}
/*******************************************************************************
* 函數名: : delay
* 功能 : 延時函數,i=1大概1us
*******************************************************************************/
void delay(uint i)
{
while(i--);
}
/*******************************************************************************
* 函數名: : Display
* 功能 : 顯示數碼管
*******************************************************************************/
void Display(int j,int k,uchar m,uchar n,int a,int b) //xianshi
{
uchar ii;
DisplayData[0]=duanxuan[j]; //duanxuan[j];
DisplayData[1]=duanxuan[k]; //duanxuan[k];
DisplayData[2]=duanxuan[m%100/10]; //duanxuan[m%100/10];
DisplayData[3]=duanxuan[m%10]; //duanxuan[m%10];
DisplayData[4]=duanxuan[n%100/10];
DisplayData[5]=duanxuan[n%10];
DisplayData[6]=duanxuan[a];
DisplayData[7]=duanxuan[b];
for(ii=0;ii<8;ii++)
{
switch(ii)//位選點亮的燈管
{
case(0):led2=1,led3=1,led4=1;break; //1
case(1):led2=0,led3=1,led4=1;break; //2
case(2):led2=1,led3=1,led4=0;break; //5
case(3):led2=0,led3=1,led4=0;break; //6
case(4):led2=1,led3=0,led4=0;break; //7
case(5):led2=0,led3=0,led4=0;break; //8
case(6):led2=1,led3=0,led4=1;break; //3
case(7):led2=0,led3=0,led4=1;break; //4
}
P0=DisplayData[ii];//送到P0口
delay(50); //延時
P0=0x00;//消影
}
}
void main()
{
TMOD = 0X01;
TH0 = (65536-45872)/256; //高八位
TL0 = (65536-45872)%256; //低八位
EA = 1; //總開關
ET0 = 1; //定時器0
TR0 = 1;
led0 = 0;
if(K1==0)
time=60;
while(time!=0)
{
KeyDown();
if(num == 20)
{
num = 0;
time--;
j=time/10,k=time%10;
}
if(led0 == 0&&(time==60||time==5))
{
beep = ~beep;
fp(1);
}
Display(j,k,num_fenshu1,num_fenshu2,jinggao1,jinggao2);
if(KeyValue1!=0xff) //按鍵是否按下
{
if((KeyValue1==0||KeyValue1==1||KeyValue1==2)&&flag_start1==0&&A==0&&TR0==1)
{
flag_start1=1;
time_start1=time_jsq1;
key_num1=KeyValue1;
KeyValue1=0xff;
KeyValue=0xff;
}
if((KeyValue1==0||KeyValue1==1||KeyValue1==2)&&flag_start1==1&&key_num1!=KeyValue1&&key_num11==-1&&time_start1!=0&&TR0==1)
{
num_fenshu1=num_fenshu1+1; //分數加一
key_num11=KeyValue1;
}
if(KeyValue1==7&&time_jsq1-time_start11>=5&&num_fenshu1>0&&TR0==1)
{
time_start11=time_jsq1;
num_fenshu1=num_fenshu1-1; //減分
KeyValue1=0xff;
KeyValue=0xff;
}
}
if(time_jsq1-time_start1>20||TR0==0)//大於1s全部恢復初值進行下一次
{
A=0;
time_start1=0;
time_end1=0;
flag_start1=0;
key_num11=-1;
key_num1=-1;
KeyValue1=0xff;
KeyValue=0xff;
}
if(KeyValue2!=0xff) //同上
{
if((KeyValue2==3||KeyValue2==4||KeyValue2==5)&&flag_start2==0&&A1==0&&TR0==1)
{
flag_start2=1;
time_start2=time_jsq2;
key_num2=KeyValue2;
KeyValue2=0xff;
}
if((KeyValue2==3||KeyValue2==4||KeyValue2==5)&&flag_start2==1&&key_num2!=KeyValue2&&key_num22==-1&&time_start2!=0&&TR0==1)
{
num_fenshu2=num_fenshu2+1;
key_num22=KeyValue2;
}
if(KeyValue2==6&&time_jsq2-time_start22>=5&&num_fenshu2>0&&TR0==1)
{
time_start22=time_jsq2;
num_fenshu2=num_fenshu2-1;
KeyValue2=0xff;
}
}
if(time_jsq2-time_start2>20||TR0==0)
{
A1=0;
time_start2=0;
time_end2=0;
flag_start2=0;
key_num22=-1;
key_num2=-1;
KeyValue2=0xff;
}
if(K2==0)
{
beep = ~beep;
fp(10);
TR0=0;
}
if(K1==0)
{
beep = ~beep;
fp(10);
TR0=1;
}
if(K3==0&&time_jsq1-time_start111>=5&&TR0==1) //A隊警告
{
time_start111=time_jsq1;
jinggao1=jinggao1+1;
if(jinggao1>9)
{
jinggao1=15;
break;
}
}
if(K4==0&&time_jsq2-time_start222>=5&&TR0==1) //B隊警告
{
time_start222=time_jsq2;
jinggao2=jinggao2+1;
if(jinggao2>9)
{
jinggao2=15;
break;
}
}
}
for(i=1;i>=0;i++) //結束后顯示
{
Display(j,k,num_fenshu1,num_fenshu2,jinggao1,jinggao2);
if(K1==0)
break;
}
}
/*******************************************************************************
* 函數名: : TR0_time
* 功能 : 定時器中斷,每溢出一次0.05s
*******************************************************************************/
void TR0_time() interrupt 1 //dingshiqizhongduan
{
TH0 = (65536-45872)/256;
TL0 = (65536-45872)%256;
num++;
time_jsq1++;
time_jsq2++;
}
/*******************************************************************************
* 函數名 : KeyDown
* 功能 : 判斷按下哪個按鍵
*******************************************************************************/
void KeyDown(void)
{
char a=0;
GPIO_KEY=0x0f;
if(GPIO_KEY!=0x0f)//判斷是否按下
{
delay(10);//延時消抖
if(GPIO_KEY!=0x0f)//再次判斷是否按下
{
//列
GPIO_KEY=0X0F;
switch(GPIO_KEY)
{
case(0X07): KeyValue=0;break;
case(0X0b): KeyValue=1;break;
case(0X0d): KeyValue=2;break;
case(0X0e): KeyValue=7;break;
}
//行
GPIO_KEY=0XF0;
switch(GPIO_KEY)
{
case(0X70): KeyValue1=KeyValue;break;
}
}
if(GPIO_KEY!=0x0f)//同上
{
delay(10);
if(GPIO_KEY!=0x0f)
{
GPIO_KEY=0X0F;//低四位全置1
switch(GPIO_KEY)
{
case(0X07): KeyValue2=0;break;
case(0X0b): KeyValue2=1;break;
case(0X0d): KeyValue2=2;break;
case(0X0e): KeyValue2=3;break;
}
GPIO_KEY=0XF0;
switch(GPIO_KEY)
{
case(0Xb0): KeyValue2=KeyValue2+3;break;
}
}
}
}
while((a<50)&&(GPIO_KEY!=0xf0)) //判斷是否松開
{
a++;
}
}