基于AT89S51的單片機無線雙顯示搶答器
1關于搶答器
1.1目前常見的搶答器有以下幾種類別:
(1) 數字電路:只要使用555定時電路作為時序觸發,配合相應的數字電路實現。
(2) 單片機式:以單片機為作為總控制單元利用單片機的掃描讀取外部輸入, 并進行相應的判斷以及數據處理。
(3) 電腦程序實現的:以電腦作為上位機,利用USB端口或者串口編程技術實現,邏輯上面的判斷以及處理均由程序來完成。這種搶答器可以在電腦上面顯示并且可以與相應的由高級語言完成答題系統一起使用,配合單片機控制能力強的優勢,將會是完美的結合,這也是我們追求的目標。
1.2搶答器的常見功能:
(1) 判斷搶答端的序號。
(2) 判斷是否犯規并且在主機顯示。
(3) 在搶答端顯示犯規或是搶答成功。
(4) 設定倒計時時間并顯示倒計時時間。
(5) 顯示各組分數。
(6) 主持人的控制。既主持人可以開始和取消倒計時。
(7) 無線搶答端的實現
(8) 電腦顯示搶答情況
(9) 選擇搶答方式。一種是有倒計時有犯規的倒計時結束開始答題的,另外一種是無倒計時無犯規可以直接答題的,比如說像Lucky52那樣的,兩種只是在程序邏輯以及顯示上面稍有區別。
1.3功能實現的選擇
我們的搶答器選擇了上面的(1),(2)(3)(4)(6)(7)(8)進行實現。
搶答的邏輯具體流程如下圖:
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps1847.tmp.png
0009、八路掃描式搶答器設計論文.rar
(159.69 KB, 下載次數: 14)
2016-4-2 16:02 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps1857.tmp.png
2主要芯片及設備的選擇:
2.1 AT89S51芯片
很熟悉的ATMEL公司的51單片機,主要具有一下功能:
40個引腳
4k Bytes Flash片內程序存儲器
128 bytes的隨機存取數據存儲器(RAM)
32個外部雙向輸入/輸出(I/O)口
5個中斷優先級2層中斷嵌套中斷
2個16位可編程定時計數器
2個全雙工串行通信口
看門狗(WDT)電路
片內時鐘振蕩器
2.2 Max7219芯片
MAX7219是一種串行接口的8位數碼管顯示驅動器。它與通用微處理器只有3根串行線相連,最多可驅動8個共陰數碼管或64個發光二極管。它內部有可存儲顯示信息的8×8靜態RAM,動態掃描電路,以及段、位驅動器。
它的特點有:串行接口的傳輸速率可達10MHz;獨立的發光二極管段控制;譯碼與非譯碼兩種顯示方式可選;數字、模擬兩種亮度控制方式;可以級聯使用。
2.3 Max232芯片
Max232用于TTL電平向RS-232串口電平轉換。該產品是由德州儀器公司(TI)推出的一款兼容RS232標準的芯片。由于電腦串口rs232電平是-10v +10v,而一般的單片機應用系統的信號電壓是ttl電平0 +5v,max232就是用來進行電平轉換的,該器件包含2驅動器、2接收器和一個電壓發生器電路提供TIA/EIA-232-F電平。
2.4 PT2262/2272芯片
PT2262和PT2272是CMOS三態編碼集成芯片,這組器件廣泛用于各種遙控器件上,只需較低的+3V電壓就能工作(本機選用12V作為發射電壓)。PT2262 是發射編碼芯片,PT2272是接收解碼芯片,兩者的地址必須配對,而且振蕩電阻必須符合要求。PT2262的TE端是發射允許端,接受低電平時,17腳DOUT端輸出一串編碼。該串編碼在載波上發送出去,被接收端接受和解調,輸入PT2272的14腳,當地址配對時,VT解碼有效端輸出高電平,數據端口就會輸出與PT2262發射端口一致的數據,從而實現遙控功能。
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps1858.tmp.pngfile:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps1878.tmp.png 2.5 超再生無線發射模塊(F04P)與接收模塊(J04V)
2.5.1 超再生低功耗射頻發射模塊(315Hz) file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps1879.tmp.png
主要特點:
低功耗發射,聲表穩頻,無數據時發射電流為零,較寬的工作電壓范圍
發射電路:
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps187A.tmp.png
2.5.2超再生低功耗射頻接受模塊(315Hz)
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps188B.tmp.png
主要特點:
(0.15mA)特低功耗超再生接收模塊,輸出無噪聲干擾,接收靈敏度高,具有接受鎖存功能
接受電路于發射電路基本相似。
2.5.3 發射接收簡要原理
通過引腳的接地,懸空或者接高電平設置好發射模塊的發射地址,當發射使能端從高電位到低點位變化時,獎發射地址和發射的數據組成16為編碼發射。
2.6 ULN2003達林頓管陣列
ULN2003 是高耐壓、大電流達林頓陳列,由七個硅NPN 達林頓管組成。
達林頓管又稱復合管。它將二只三極管適當的連接在一起,以組成一只等效的新的三極管。這等于效三極管的放大倍數是二者之積。在電子學電路設計中,達林頓接法常用于功率放大器和穩壓電源中。
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps188C.tmp.png
最大的作用與特點是每路可以介紹500mA的灌流,這也是我們使用的目的。
2.7 USB轉串口模塊(USB/RS232)
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps189D.tmp.png
采用USB轉串口的標準芯片PL2303和RS232電平接口芯片MAX211的組合。
2.8高亮度Super Red四寸共陰極數碼管
正常發光電壓約5--6V, 正常導通電流約40--80mA
3硬件電路的實現以及各個部分的作用
3.1 硬件電路圖(Proteus仿真圖)
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps18EC.tmp.png
3.2各個模塊的說明與簡介
3.2.1 單片機最小系統:
這個不用多說,可惜是買別人的。上面得是提供的是12MHz的晶振,與程序中的保持一樣.因為在計算定時的時候,要用到晶振的大小。
3.2.2 Max7219模塊
這個,還是買的別人做的PCB,我們自己用面包版連了,可以不行,懷疑是沒有選擇正確的電容的問題,或者說,我們不會選擇電容。
3.2.3 無線搶答端發射模塊
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps18ED.tmp.png
無線搶答端采用PT2262發射編碼芯片+F05P發射模塊的組合,另外使用一根拉直的長24CM的天線。
關于按鈕,買的不知道用來控制什么的按鈕,反正正和我們意,買這個的時候要注意手感,要讓使用者用的舒適。從院科協L同學那里淘來的半圓形按鈕外殼。
3.2.4無線接收模塊
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps18FD.tmp.png
無線搶答端采用PT2272發射編碼芯片+F04V接收模塊的組合。Pt2272與PT2262要設置為地址相同,電阻匹配。接受到的信號傳至單片機中。
3.2.5 主持人控制按鍵
這里有兩個按鈕,分別為OK/START,CANCEL/CLEAR. 前者是用來設置完畢搶答倒計時時間和開始倒計時,后者是用來取消搶答和清理數碼管并顯示倒計時開始時間,用來等待再次開始搶答。
3.2.6 設置倒計時時間的撥碼開關
仿“8421”碼設置的“2321”碼,全部低位的時候為“1”,四個開關可以設置1-9任何數字。缺點就是買的撥碼開關小了點。
3.2.7 譯碼器以及搶答端的發光二極管
譯碼器從單片機得到輸入,然后低位選通發光二極管。
3.2.8 位選反向驅動
說不清了,這樣的了。
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps191E.tmp.png
3.2.9 段選反向驅動
兩個反向驅動的本意在于,雖然Max7219驅動的是共陰極數碼管,但是從datasheet上面可以看出來,段選的拉流不可以滿足這種型號的數碼管,位選的灌流也不能接受,所以這里配合ULN2003來使用。因為ULN2003的特點,高位接受灌流,低位為高阻,所以要使用反向驅動。這里,段選的反向驅動在程序里面的反向了,而對于位選,用于是該芯片自己進行的,所以程序控制不了,所以在Max7219位選的下游使用了一個與非門來當作反向器來使用。
3.3.10 上位機接口
采用MAX232芯片進行RS232-TTL電平轉換,輸出至串口,然后再通過USB轉串口模塊連接至上位機。
3.2.11 大尺寸數碼管
這個前面說過了。
4硬件實現的步驟
4.1硬件實現的各個步驟
(1) 通過實驗板來驗證各個主要功能模塊的正確實現。
這個就是在實驗板接線,來分開的檢測各個部分的正確性,當然有的部分還是沒有辦法驗證的。
(2) 仿真器+面包板上面的插線實驗
恩,仿真器很好使。就是上面的限流電阻給不小心被燒了,郁悶。
(3) 最小系統+面包板上面插線實驗
使用仿真器的時候用的是電腦提供的+5V電壓,可能和用自己的供電的不太一樣,尤其是同時也在使用+12V電源的時候,電腦給出的+5V可能的比較低,這樣一來會有些不必要的問題,而用最小系統則沒有這些問題。
(4) 焊接萬能板
這個很有趣也很關鍵。
(5) 仿真器+電路板的實現
用來驗證電路板上的各個部分是否焊接成功。
(6) 最小系統+電路板的實現
恩這個就是最后的驗證了。
4.2一個原則
由上可見,在實驗過程中,總體上也采取單變量原則,這樣方便有效地對于每一部分進行檢驗。另外,如果在檢驗焊接板上面的錯誤的話,要配合著面包板使用才行,也是保證單變量,這樣對比出來面包上成功的時候和現在失敗的時候只有哪個地方不同,那百分之九十就是這個地方了。
5軟件實現(單片機上的軟件)
5.1程序編寫
模塊化,使整個程序的每個部分具有較好的可移植性。
5.2程序測試
在Keil Uvision + Proteus聯調環境下進行邏輯的測試,不過這個只是邏輯上面的測試,距離能成功還有很遠。
5.3最終代碼
最后的代碼將在另見。
6電腦端(上位機)顯示程序
電腦端使用C#編寫程序,利用了.Net的SerialPort類對電腦端口檢測和接受數據。這個將會另有介紹。
7 后續工作和幾個問題
(1) 電源還需優化, 可以采用三穩壓管電源。
(2) 電阻發熱問題: 我們使用的3W的大功率電阻竟然不到1.5W的情況下就發熱了,還沒有搞懂。
(3) 我們的74HC00與非門在未加高電平的情況下就已經正常工作了,不清楚這是什么情況。
(4) 可以考慮增加搶答模式與小組積分,實現按著使用者的要求對于程序進行選擇。
(5) 無線端搶答端電池的問題。因為目前的設計是電池如果沒有取出的話,將一直對發射系統供電,這樣會消耗電池,但采用一次按鍵先后接通電路,發射編碼的方法,總是不穩定
#include <REG51.h>
#include <STRING.H>
/******************************************************************
* 自定義Macro
*******************************************************************/
//編碼的均為反向編碼
#define CLEAR 0x7f //定義清空的反碼
#define LED_BEGIN 0x01 // 定義開始時數碼管的顯示
#define LED_FOUL 0x38 // 犯規后顯示字母"F",數碼管編碼
#define LED_C 0x31 // 字母"C"的編碼
#define LED_L 0x71 // 字母"L"的編碼,兩個用來在主持人取消之后顯示"CL"--cancel
#define GET 1 // 這個是作為一個函數的參數來混的,就是成功搶答的意思
#define FOUL 0 // 和上面的參數一起混的,犯規---這兩個的用法在后面體現
#define READY 0x7e
//下面是給上位機發送的指令,對應的是cmdID
#define _STRING_READY_ 9//調整好搶答倒計時,準備開始搶答
#define _STRING_START_ 8//讀秒結束,搶答開始
#define _STRING_CANCEL_ 7//取消搶答
#define _CHANGE_TIME_ 6//每次讀秒
//發送1--4的指令代表搶答端的序號
//因為在板子上面采用的是12M的晶振,仿真時候采用的是11.0529M的晶振,為了方便不同時候編譯方便,這里與后面的條件編譯一起使程序修改參數比較方便
//#define CLOCK_FREQUENCY_12M 1
/******************************************************************
* 自定義數據類型
*******************************************************************/
typedef unsigned char Byte; // 一個字節
typedef unsigned int Word; // 一個字,兩個字節
typedef bit Bool; // 模仿布爾型變量
//typedef sbit Port; // 本想用自定義一個端口類型的變量,比較方便,但是這句話步知道為何通不過編譯
/******************************************************************
* 定義MAX7219寄存器
*******************************************************************/
#define REG_NO_OP 0x00 // 定義空操作 register
#define DIG_1 0x01 // 定義數碼管1 register
#define DIG_2 0x02 // 定義數碼管2 register
#define DIG_3 0x03 // 定義數碼管3 register
#define DIG_4 0x04 // 定義數碼管4 register
#define DIG_5 0x05 // 定義數碼管5 register
#define DIG_6 0x06 // 定義數碼管6 register
#define DIG_7 0x07 // 定義數碼管7 register
#define DIG_8 0x08 // 定義數碼管8 register
#define REG_DECODE 0x09 // 定義解碼控制 register
#define REG_INTENSITY 0x0a // 定義顯示亮度 register
#define REG_SCAN_LIMIT 0x0b // 定義掃描限制 register
#define REG_SHUTDOWN 0x0c // 定義"shutdown"模式 register
#define REG_DISPLAY_TEST 0x0f // 定義"display test"模式 register
#define INTENSITY_MIN 0x00 // 定義最低顯示亮度
#define INTENSITY_MAX 0x0f // 定義最高顯示亮度
/*********************************************************************
* 定義硬件引腳連接
**********************************************************************/
sbit DATA=P2^0; // MAX7219的數據口
sbit LOAD=P2^1; // MAX7219的鎖存端口
sbit CLK=P2^2; // MAX7219的時鐘端口
sbit HOST_START=P0^0; //主持人按鍵,用來重新開始的按鍵 start
sbit HOST_CANCEL=P0^1; //主持人用來取消搶答的按鍵 clear
sbit SWITCH1_3=P1^4; // 調節倒計時時間的撥碼開關,下劃線前面的號代表開關的序號,下劃線后面的號代表該開關的數值
sbit SWITCH2_2=P1^5; // 同上
sbit SWITCH3_2=P1^6; // 同上
sbit SWITCH4_1=P1^7; // 同上
sbit BEEP=P0^7; //定義蜂鳴器端口
#ifdef USE_SOUND //可以通過define來選擇要不要使用仿真時候的聲音
sbit LS138_C=P2^4; //定義譯碼器輸入端
sbit LS138_B=P2^5; //同上
sbit LS138_A=P2^6; //同上
sbit LS138_E1=P2^7; //定義譯碼器使能端
#endif
/*********************************************************************
* 定義全局變量
**********************************************************************/
Byte data intrCounter; // 計時器中斷次數
Byte data beginNum; // 開始倒計時的時間
Byte data counterBack; // 將中斷次數放在里面以備后用
Byte data showNum; // 數碼管正在顯示的時間
Bool data isStart; // 是否開始搶答
Bool data isFoul; // 是否犯規
Bool data isPressed; // 是否有搶答的鍵按下
Byte data number_temp; // 用來記錄P1口上次狀態的一個變量
Bool data needResetTimes;//記錄是否需要重設Timer0的溢出次數
code unsigned char C51BOX2[3] _at_ 0x43; //使用C51Box時候防止程序跑丟
/***********************************************************************
* 共陰極七段數碼管顯示對應段查詢表(數字0-9分別對應code_table[0]-[9])
***********************************************************************/
Byte code code_table_zheng[10]=
{0x7e,0x30,0x6d,0x79,0x33,0x5b,0x5f,0x70,0x7f,0x7b};
Byte code code_table[10]=
{0x01,0x4f,0x12,0x06,0x4c,0x24,0x20,0x0f,0x00,0x04};
/***********************************************************************
* 函數聲明
***********************************************************************/
void MAX7219_SendByte (Byte dataout);
void MAX7219_Write (Byte reg_number, Byte dataout);
void MAX7219_DisplayChar(Byte digit, Byte character);
void MAX7219_Clear (void);
void MAX7219_SetBrightness (Byte brightness);
void MAX7219_DisplayTestStart (void);
void MAX7219_DisplayTestStop (void);
void MAX7219_ShutdownStart (void);
void MAX7219_ShutdownStop (void);
void MAX7219_Init (void);
void Delay10ms(void);
Bool GetHostStartKey (void);
Bool GetHostCancelKey (void);
void GetCounter(void);
Byte GetPressed(Byte KeyState);
Byte GetPressedWireless(Byte KeyState);
void IT0_Init(void);
void Timer0_Overflow();
void PressedHandle(Byte keyPressed);
void GetOrFoulHandle(Bool state);
void CancelHandle();
void SPEAKER_count (void); //聲明倒計時聲音函數
void SPEAKER_start(void); //聲明開始搶答聲音函數
void SPEAKER_get(void); //聲明搶到聲音函數
void SPEAKER_foul(void); // 聲明犯規聲音函數
void initialSerial();
void sendNumber(int number);//串口發送數字,這里發送的是CommandID
void sendString(unsigned char *string);//串口發送字符串
/***********************************************************************
* MAX7219_SendByte()
*
* 描述: 向MAX7219傳送一個字節的數據
* Arguments : dataout = data to send
* Returns : none
*************************************************************************/
void MAX7219_SendByte (Byte dataout)
{
Byte i;
for (i=8;i>0;i--)
{
Byte mask=1<<(i-1);//mask是個掩碼,取位使用
CLK=0;//MAX7219的位傳入是在時鐘的上升沿之前,所以在每發一位之前都要變為低電平
if (dataout&mask)
DATA=1;
else
DATA=0;
CLK=1;//八個bit都傳遞完成后變為高電平,鎖存
}
}
/***********************************************************************
* MAX7219_Write()
*
* 描述: 向 MAX7219 寫命令
* Arguments : reg_number = register to write to
* dataout = data to write to MAX7219
* Returns : none
未完~
***************************************************************************/
void MAX7219_Write (Byte reg_number, Byte dataout)
{
LOAD=0;//也是鎖存上升沿之前的,發這兩個字節之前要變為低電平
MAX7219_SendByte(reg_number);//發送寄存器地址
MAX7219_SendByte(dataout);//發送數據
LOAD=1;//變為高電平,鎖存
}
/**************************************************************************
* MAX7219_DisplayChar()
*
* 描述: 使某一位顯示一個數字
* Arguments : digit = digit number (0-7)
* character = character to display (0-9, A-Z)
* Returns : none
**************************************************************************/
void MAX7219_DisplayChar(Byte digit, Byte character)
{
MAX7219_Write(digit, character);
}
/**************************************************************************
* MAX7219_Clear()
*
* 描述: 清除所有位的顯示
* Arguments : none
* Returns : none
***************************************************************************/
void MAX7219_Clear (void)
{
Byte i;
for (i=1; i<=2; i++)
MAX7219_Write(i, CLEAR);//把八個數碼管全都清零了,已經寫反了^_^
}
/**************************************************************************
* MAX7219_SetBrightness()
*
* 描述: 設置數碼管顯示亮度
* Arguments : brightness (0-15)
* Returns : none
***************************************************************************/
void MAX7219_SetBrightness (Byte brightness)
{
brightness &= 0x0f;
MAX7219_Write(REG_INTENSITY, brightness);
}
/**************************************************************************
* MAX7219_DisplayTestStart()
*
* 描述: 進入 test 模式
* Arguments : none
* Returns : none
***************************************************************************/
void MAX7219_DisplayTestStart (void)
{
MAX7219_Write(REG_DISPLAY_TEST, 1);
}
/**************************************************************************
* MAX7219_DisplayTestStop()
*
* 描述: 退出 test 模式
* Arguments : none
* Returns : none
***************************************************************************/
void MAX7219_DisplayTestStop (void)
{
MAX7219_Write(REG_DISPLAY_TEST, 0);
}
/**************************************************************************
* MAX7219_ShutdownStart()
*
* 描述: 進入 shutdown 模式
* Arguments : none
* Returns : none
***************************************************************************/
void MAX7219_ShutdownStart (void)
{
MAX7219_Write(REG_SHUTDOWN, 0);
}
/**************************************************************************
* MAX7219_ShutdownStop()
*
* 描述: 退出 shutdown 模式
* Arguments : none
* Returns : none
***************************************************************************/
void MAX7219_ShutdownStop (void)
{
MAX7219_Write(REG_SHUTDOWN, 1);
}
/**************************************************************************
* MAX7219_Init()
*
* Description: MAX7219初始化模塊; 應該先于其他MAX7219函數而被調用
* Arguments : none
* Returns : none
***************************************************************************/
void MAX7219_Init (void)
{
DATA=1;
CLK=1;
LOAD=1;
MAX7219_Write(REG_SCAN_LIMIT,1);//這里設置的是掃描兩個數碼管
MAX7219_Write(REG_DECODE, 0x00);
MAX7219_SetBrightness(INTENSITY_MAX);//設置最大亮度顯示
MAX7219_DisplayTestStart();
MAX7219_DisplayTestStop();
MAX7219_ShutdownStop();
MAX7219_Clear();
}
/**************************************************************************
* Delay_100us()
*
* 描述: 延時100us,主要用在消除開關抖動時
* Arguments : none
* Returns : none
***************************************************************************/
void Delay10ms(void)
{
unsigned char i,j;
for(i=20;i>0;i--)
for(j=248;j>0;j--);
}
/**************************************************************************
* GetHostStartKey()
*
* Description: 取得主持人開始按鍵的鍵值
* Arguments : none
* Returns : 1-->主持人按鍵; 0-->主持人未按鍵
***************************************************************************/
Bool GetHostStartKey (void)
{
if (HOST_START ==1)
return 0;
else
Delay10ms ();//如果發現主持人按鍵接通,要先延時100us,防止抖動
if (HOST_START==1)
return 0;
else
return 1;//延時時候還是接通,則判斷為該鍵確實按下
}
/**************************************************************************
* GetHostCancelKey()
*
* Description: 取得主持人取消按鍵的鍵值
* Arguments : none
* Returns : 1-->主持人按鍵; 0-->主持人未按鍵
***************************************************************************/
Bool GetHostCancelKey (void)
{
if (HOST_CANCEL ==1)
return 0;
else
Delay10ms ();//如果發現主持人按鍵接通,要先延時100us,防止抖動
if (HOST_CANCEL ==1)
return 0;
else
return 1;//延時時候還是接通,則判斷為該鍵確實按下
}
/**************************************************************************
* GetCounter
*
* Description: 取得預先設置的倒計時時間
* Arguments : none
* Returns : none
***************************************************************************/
void GetCounter(void)
{
beginNum=1;//在所有開關都沒有撥動的時候倒計時為1秒,比設置為0秒要好
intrCounter=20;//每一秒對應的中斷次數為20次
if (SWITCH1_3==1)
{
beginNum+=3;
}
if (SWITCH2_2==1)
{
beginNum+=2;
}
if (SWITCH3_2==1)
{
beginNum+=2;
}
if (SWITCH4_1==1)
{
beginNum+=1;
}//以上判斷語句為判斷撥碼開關狀態
intrCounter=20*beginNum;//計算總掃描次數
}
/**************************************************************************
* GetPressed
*
* Description: 從P2口連接搶答端的四位來判斷搶答情況
* Arguments : Byte KeyState-->P2 state
* Returns : 搶答端的號碼 ; 0-->沒人搶答
***************************************************************************/
Byte GetPressed(Byte KeyState)
{
Byte key;//記錄搶答端的號碼
KeyState&=0xf0;//取P1口的低四位
switch (KeyState)
{
case 0xf0: key=0;break;//全高,無人搶答
case 0xe0: key=1;break;//只有P1.1,key1搶答
case 0xd0: key=2;break;//只有P1.2,key2搶答
case 0xb0: key=3;break;//只有P1.3,key3搶答
case 0x70: key=4;break;//只有P1.4,key4搶答
}
/*
switch (KeyState)
{
case 0x00: key=0;break;//全高,無人搶答
case 0x01: key=1;break;//只有P1.0,key1搶答
case 0x02: key=2;break;//只有P1.1,key2搶答
case 0x04: key=3;break;//只有P1.2,key3搶答
case 0x08: key=4;break;//只有P1.3,key4搶答
}
*/
//上面是在用高電平來判斷搶答狀態時的程序,經證明不知道為何無效
return key;
}
/**************************************************************************
* GetPressedWireless
*
* Description: P2口的高四位于Pt2272接受模塊相連,該方法用來判斷無線搶答序號
* Arguments : Byte KeyState-->P2 state
* Returns : 搶答端的號碼 ; 0-->沒人搶答
***************************************************************************/
Byte GetPressedWireless(Byte KeyState)
{
Byte key;//記錄搶答端的號碼
KeyState&=0xf0;//取P2口的高四位
/*
switch (KeyState)
{
case 0x0f: key=0;break;//全高,無人搶答
case 0x0e: key=1;break;//只有P1.1,key1搶答
case 0x0d: key=2;break;//只有P1.2,key2搶答
case 0x0b: key=3;break;//只有P1.3,key3搶答
case 0x07: key=4;break;//只有P1.4,key4搶答
}
*/
switch (KeyState)
{
case 0x00: key=0;break;//全低,無人搶答
case 0x10: key=1;break;//只有P1.0,key1搶答
case 0x20: key=2;break;//只有P1.1,key2搶答
case 0x40: key=3;break;//只有P1.2,key3搶答
case 0x80: key=4;break;//只有P1.3,key4搶答
}
return key;
}
/**************************************************************************
* IT0_Init
*
* Description: 初始化計時器T0的狀態
* Arguments : none
* Returns : none
***************************************************************************/
void IT0_Init(void)
{
TMOD=0x21;//設置T0在方式1下工作,同時還要保證T1,也就是波特率所學的定時器的正常工作
#ifdef CLOCK_FREQUENCY_12M//對使用哪種晶振進行條件編譯
TH0=0x3C;//12M晶振時的裝入值
TL0=0xAF;
#else
TH0=0x4C;//11.0529M晶振時裝入值
TL0=0x00;
#endif
ET0=1;//使T0中斷可以溢出
EA=1;//開啟總中斷
TF0=0;//溢出位清零
TR0=1;//開啟T0
}
/**************************************************************************
* Timer0_Overflow() interrupt 1
*
* Description: 中斷溢出服務程序, 采用的是中斷方式1, 后面最好不加using選擇寄存器組以免與系統用在主程序的寄存器沖突
* Arguments : none
* Returns : none
***************************************************************************/
void Timer0_Overflow() interrupt 1
{
static Byte times=20;//溢出次數,用20次中斷來判斷1秒
//這里存在重大bug,到由于有相應而停止Timer0后,再次啟用時,這里的second沒有回歸原值
if (needResetTimes==1)
{
times=20;
needResetTimes=0;//已重設,不需要再次重新設置溢出次數
}
#ifdef CLOCK_FREQUENCY_12M//對使用哪種晶振進行條件編譯
TH0=0x3C;//12M晶振時的裝入值
TL0=0xAF;//這兩個寄存器存的是計數器的計數開始的值,計算發現這兩個值累加至溢出后正好是50ms
#else
TH0=0x4C;//11.0529M晶振時裝入值
TL0=0x00;//同理,這兩個寄存器存的是計數器的計數開始的值,計算發現這兩個值累加至溢出后正好是50ms
#endif
times--;
intrCounter--;
/* 原來使用的方法
if (times==0)//每隔一秒的操作
{
MAX7219_DisplayChar(DIG_2,code_table[--showNum]); //要避免用上面的會造成顯示"0"后面一秒鐘,才進入開始
times=20;//重新賦值每秒計數器
if (intrCounter==0)
{
TR0=0;//關閉T0計數器
isStart=1;//計時結束,進入正常搶答
//SPEAKER_start();//開始搶答的聲音
}
//待顯示"0"以后就開始搶答
else
{
//SPEAKER_count();//倒計時聲音
}
}
*/
if (times==0)//每隔一秒的操作
{
//if (showNum!=1) SPEAKER_count();//倒計時聲音
//else SPEAKER_start();//開始搶答的聲音
MAX7219_DisplayChar(DIG_2,code_table[--showNum]);//顯示數字
sendNumber(_CHANGE_TIME_);//給上位機發送要顯示數字的命令
times=20;//重新賦值每秒計數器
}//待顯示"0"以后就開始搶答
if (intrCounter==0)
{
TR0=0;//關閉T0計數器
isStart=1;//計時結束,進入正常搶答
}
}
/**************************************************************************
* PressedHandle
*
* Description: 按鍵處理
* Arguments : Byte keyPressed-->按下的按鍵
* Returns : none
***************************************************************************/
void PressedHandle(Byte keyPressed)
{
MAX7219_Clear();//LED clear
MAX7219_DisplayChar(DIG_2,code_table[keyPressed]);//在右側數碼管顯示搶答選手號碼,此時沒有去判斷是否犯規
//給上位機發搶答選手的號碼
sendNumber(keyPressed);
}
/**************************************************************************
* GetOrFoulHandle(Bool state)
*
* Description: 正常搶答或是犯規處理
* Arguments : Bool state-->是GET和FOUL兩個宏的取之之一
* Returns : none
***************************************************************************/
void GetOrFoulHandle(Bool state)
{
if (!state)
{
MAX7219_DisplayChar(DIG_1,LED_FOUL);//如果是犯規的話左邊的LED要顯示"F",foul
}
}
/**************************************************************************
* CancelHandle()
*
* Description: 處理主持人取消倒計時
* Arguments : none
* Returns : none
***************************************************************************/
void CancelHandle()
{
MAX7219_DisplayChar(DIG_1,LED_C);
MAX7219_DisplayChar(DIG_2,LED_L);//主持人取消倒計時之后,兩個數碼管顯示"CL"-->cancel
}
/**************************************************************************
* delayus()
*
* Description: 延時程序
* Arguments : t-->us
* Returns : time delayed
***************************************************************************/
void delayus(unsigned char t )
{
unsigned char j;
for(;t>0;t--)
for(j=19;j>0;j--);
}
/**************************************************************************
* SPEAKER_count/start/foul/get()
*
* Description: speaker發聲程序->計數/開始/犯規/搶答 四種聲音
* Arguments : none
* Returns : none
***************************************************************************/
void SPEAKER_count(void)
{
unsigned char i;
for (i=0;i<10;i++)
{
BEEP =1; //點亮
delayus(20);
BEEP =0; //熄滅
delayus(20);
}
}
void SPEAKER_start(void)
{
unsigned char i;
for(i=0;i<200;i++)
{
BEEP =1; //點亮
delayus(10);
BEEP =0; //熄滅
delayus(10);
}
}
void SPEAKER_foul(void)
{
unsigned char i;
for(i=0;i<250;i++)
{
BEEP =1; //點亮
delayus(15);
BEEP =0; //熄滅
delayus(17);
}
}
void SPEAKER_get(void)
{
unsigned char i;
for(i=0;i<250;i++)
{
BEEP =1; //點亮
delayus(10);
BEEP =0; //熄滅
delayus(10);
}
for(i=0;i<250;i++)
{
BEEP =1; //點亮
delayus(20);
BEEP =0; //熄滅
delayus(20);
}
}
/**************************************************************************
* initialSerial()
*
* Description: 初始化串口
* Arguments : none
* Returns : none
***************************************************************************/
void initialSerial()
{
EA=0;//關閉所有中斷
TMOD=0x21;//設置T1定時器的工作模式:方式2;同時保證T0的工作模式正常.這兒太重要了
TH1=0xFA;
TL1=0xFA;
PCON=0x80;//設置SMOD=1
SCON=0x50;//選擇工作方式1,即為UART
TR1=1;//設置好定時器并打開定時器,定好波特率9600bit/s
}
/**************************************************************************
* sendNumber(int number)
*
* Description: 通過串口發送一位數字
* Arguments : number->要發送的數字
* Returns : none
***************************************************************************/
void sendNumber(int number)
{
TI=0;
SBUF=number+48;
while(!TI);
TI=0;
}
/**************************************************************************
* sendString(unsigned char *string)
*
* Description: 通過串口發送字符串
* Arguments : 要發送的字符串的頭指針
* Returns : none
***************************************************************************/
void sendString(unsigned char *string)
{
do
{
SBUF=*string;
while(!TI);
TI=0;
string++;
}while(*(string-1)!='\0');
/**************************************************************************
* 主程序
***************************************************************************/
void main()
{
//P1=0xff;
Byte keyPressed,i;//選手按鍵號碼,沒有的話為0
Bool hostPressed;//用來記錄主持人按鍵取消,0為沒有動作,1為取消
Byte buf[10];//設置發送緩沖區
number_temp=P1&0xf0;//P1口上次的狀態,在調整倒計時時間的時候用到的
#ifdef USE_SOUND
LS138_E1=1; //譯碼器初始化
#endif
needResetTimes=0;//不需要重新設置溢出次數
MAX7219_Init();//數碼管初始化
initialSerial();//初始化串口發送
GetCounter();//獲取開始時候設置的倒計時時間
MAX7219_DisplayChar(DIG_1,code_table[beginNum]); //顯示開始時設置的倒計時時間,在左位設置
MAX7219_DisplayChar(DIG_2,READY);//調時間的時候右位的顯示
while(GetHostStartKey()==0)//當主持人沒有按鍵的時候進入循環
{
if (number_temp!=(P1&0xf0)) //若調整了倒計時時間,則P1口狀態變了,就要重新設置和顯示
{
GetCounter();//獲取調整以后的倒計時時間
MAX7219_DisplayChar(DIG_1,code_table[beginNum]); //顯示調整以后的倒計時時間
number_temp=P1&0xf0;//記錄下來現在P1口的狀態,以備后面的比較
}
} //當主持人按鍵以后就結束調整進入搶答倒計時
sendNumber(beginNum);
MAX7219_DisplayChar(DIG_1,READY);
while(GetHostCancelKey()==0);
sendNumber(_STRING_READY_);//發送調節完畢準備搶答的命令;
//調整好倒計時時間后,按下start顯示"--",再按下cancel則顯示倒計時時間,此時可以開始倒計時了.
MAX7219_DisplayChar(DIG_1,READY);//清空右邊一位數碼管
MAX7219_DisplayChar(DIG_2,code_table[beginNum]);
for (i=100;i--;i>0)
Delay10ms();//防止后面出現連讀的情況..
counterBack=intrCounter; //備份要中斷的總次數
while(1)//這里要用自己加的循環來把程序束縛在這里運行
{
showNum=beginNum;//設置要顯示的時間,當然時從倒計時時間開始
intrCounter=counterBack;//設置總中斷的次數
TR0=0;//禁用計時器0
isPressed=0;//記錄是否有人按鍵
isStart=0;//沒有開始搶答
while(GetHostStartKey()==0);
//給上位機發送開始倒計時的指令
sendNumber(_STRING_START_);
needResetTimes=1;//Timer0已經是第二輪開始,需要重新設置溢出次數
IT0_Init();//初始化計時器0, 啟用.
MAX7219_DisplayChar(DIG_1,CLEAR);//清空左邊一位數碼管
while(!isPressed)//如果沒有記錄到有人按鍵就進入
{
keyPressed=GetPressed(P2); //查詢一下P2口的狀態,即按鍵情況,P2口的后四位作為搶答端輸入,有線時候使用
//keyPressed=GetPressedWireless(P2);//查詢一下P2口的狀態,即按鍵情況,無線使用
hostPressed=GetHostCancelKey();
if (!keyPressed&&!hostPressed)//如果沒有人按鍵,就進入下次循環
continue;
else
{
TR0=0;//關閉定時器
isPressed=1;//記錄到有人按鍵,提供條件跳出循環
}
}
if (keyPressed!=0)
{
if (isStart)//如果已經開始搶答
{
PressedHandle(keyPressed);//處理按鍵,即顯示搶答選手號碼
GetOrFoulHandle(GET);//處理搶答
#ifdef USE_SOUND//對是否使用揚聲器進行條件編譯
LS138_E1=0; //譯碼器準備工作
switch (keyPressed)
{
case 1: LS138_A=0;LS138_B=0;LS138_C=0;break; //1號成功燈亮
case 2: LS138_A=0;LS138_B=1;LS138_C=0;break; //2號成功燈亮
case 3: LS138_A=1;LS138_B=0;LS138_C=0;break; //3號成功燈亮
case 4: LS138_A=1;LS138_B=1;LS138_C=0;break; //4號成功燈亮
default : break;
}
#endif
}
else//否則,沒有開始搶答
{
PressedHandle(keyPressed);//處理按鍵,即顯示搶答選手號碼
GetOrFoulHandle(FOUL);//處理犯規,必須要放在后面,因為顯示數字的里面有一個clear
#ifdef USE_SOUND//對是否使用揚聲器進行條件編譯
LS138_E1=0; //譯碼器準備工作
switch (keyPressed)
{
case 1: LS138_A=0;LS138_B=0;LS138_C=1;break; //1號犯規燈亮
case 2: LS138_A=0;LS138_B=1;LS138_C=1;break; //2號犯規燈亮
case 3: LS138_A=1;LS138_B=0;LS138_C=1;break; //3號犯規燈亮
case 4: LS138_A=1;LS138_B=1;LS138_C=1;break; //4號犯規燈亮
default : break;
}
#endif
}
}
if (hostPressed==1)
{
CancelHandle();
//向上位機發送主持人取消的指令
sendNumber(_STRING_CANCEL_);
for (i=100;i--;i>0)
Delay10ms();//防止后面出現連讀的情況..
}
while(GetHostCancelKey()==0);//如果主持人沒有按鍵再次開始,則停在次死循環處
#ifdef USE_SOUND//對是否使用揚聲器進行條件編譯
LS138_E1=1; //關閉譯碼器
#endif
MAX7219_DisplayChar(DIG_1,READY);//清空右邊一位數碼管
MAX7219_DisplayChar(DIG_2,code_table[beginNum]);//左位顯示設置的倒計時時間
//給上位機發送重新開始倒計時的指令
sendNumber(_STRING_READY_);
//到這里一次循環結束
}
}
|