?? 單片機c51編程幾個有用的模塊(轉載).txt
字號:
1:找到數據包頭。
2:找到數據包尾。
3:數據包出錯,需要拋棄。
然后更改源代碼來實現上面的協議。
注意:當用戶需要使用字符串的時候,可以利用簡單的包裝函數將字符串轉換為字節數組。所以沒有必要提供專用的字符串處理函數。
鍵盤掃描模塊
鍵盤掃描模塊有兩種工作方式, 一種為自動的由時鐘模塊調用, 另一種是由程序員自行調用。
1) 由時鐘模塊自動調用的方式
將時鐘模塊實現文件(Timer.h)及鍵盤掃描模塊的實現文件(KBScan。c)包含進工程, 在Config.h 文件中添加TIMER_KBSCANDELAY宏。時鐘模塊自動對時鐘中斷進行計數, 當達到TIMER_KBSCANDELAY宏所定義的值后, 自動調用鍵盤掃描模塊中的函數KBScanProcess()進行鍵盤掃描,也就是說,這個宏的值可以決定按鍵消抖動的時間。
用戶應該提供兩個回調函數OnKBScan()及onKeysPressed()。 在函數OnKBScan中進行鍵盤掃描, 并返回掃描碼。掃描碼的類型缺省為BYTE, 當鍵盤規模較大時, BYTE不能夠完全包含鍵盤信息時, 可在Config.h文件中重定義宏KBVALUE, 如下:
#define KBVALUE WORD
這樣, 就可以使用16位的鍵盤掃描碼, 如果此時還達不到要求, 可以將鍵盤掃描碼定義成一個結構, 但這樣做將會增加代碼量及消耗更多的RAM資源, 故不推薦。
掃描模塊調用OnKBScan取得掃描碼, 并調用用戶可以重定義的宏IsNoKeyPressed來判斷是否有鍵按下, 缺省的IsNoKeyPressed實現如下:
#define IsNoKeyPressed(x) ((x) == 0x00)
即認為OnKBScan返回0掃描碼時為沒有鍵按下, 如果掃描函數返回其它非零掃描碼做為無鍵按下的掃描碼時, 可以在Config.h文件中重定義IsNoKeyPressed宏的實現。
8位鍵盤掃描碼(缺省值)時, 相應的掃描函數為:
BYTE OnKBScan()
當掃描模塊經過軟件消抖動之后, 發現有鍵按下, 就會調用另一個回調函數onKeysPressed。 函數的聲明應該如下:
void onKeyPressed(BYTE byKBValue, BYTE byState)
其中中的參數byKBValue的類型為BYTE, 此為缺省值, 如果使用其它類型的掃描碼, 就將此參數變為相應類型。這個值由OnKBScan返回。另一個參數byState在通常情況下為零。但當用戶在Config.h中定義宏KBSCAN_BRUSTCOUNT,同時鍵盤上的某鍵被按住不放時, 掃描模塊對它自己的調用(注意這里和TIMER_KBSCANDELAY宏不同, TIMER_KBSCANDELAY是時鐘中斷足夠的次數后調用掃描模塊, 而KBSCAN_BRUSHCOUNT為掃描模塊自身的被調用次數)進行計數,當達到KBSCAN_BRUSTCOUNT時,掃描模塊調用 onKeysPressed,此時第一個參數的含義不變,而byState變成1, 同時計數器復位,又經過一段時間后,用值為3的byState 調用onKeysPressed。 這樣就可以很方便的實現多功能鍵或者檢測某鍵的長時間被按下。
2)由用戶自行調用
由用戶自行在程序中調用掃描模塊,而不是由時鐘中斷自行調用。其它與方式1相同。
注意:
1) 函數KBScanProcess為非阻塞函數,它將在很快的時間內返回,等待再次分配給它執行的機會。
2) 函數KBScanProcess是在時鐘中斷外部運行的,它的過程可以被任何中斷打斷,但不影響系統運行。
3) byState的最大值為250,之后被復位為零。
應用舉例
現在來舉例說明上述幾個模塊的使用方法。
硬件環境描述:
為了控制一盞燈,需要單片機提供一個做控制功能的開關量,這里不描述外部接口電路,只說明當單片機的P10腳為高電平時,燈滅,當P10腳為低電平時,燈亮。
可以通過計算機由串口發送命令來控制,或通過一個按鍵(push button不是自鎖式的按鍵)來手動控制(按鍵接在P11腳上,當鍵沒有按下時,P11電平為高,鍵按下時,引腳電平被接低),當使用按鍵手動控制的時候,需要給計算機發送通知。
設定串口通訊指令如下:
數據包由0xff做包頭,4個字節長,第二個字節為命令代碼,第三個字節為數據,最后一個字節為校驗位。
命令和數據代碼有如下組合:
(計算機發給單片機)
0x10 0x01: 計算機控制燈亮。(數據位是非零值即可)
0x10 0x00: 計算機控制燈滅。
(單片機發給計算機)
0x11 0x01:單片機正常執行控制指令,返回。(數據位是非零值即可)
0x11 0x00: 單片機不能夠正常執行控制指令,或控制指令錯(不明含義的數據包或校驗錯等)。
0x12 0x01:手動控制燈亮。(數據位是非零值即可)
0x12 0x00: 手動控制燈滅。
建立工程:
在硬盤上建立文件夾Projects,在Projects下建立Common文件夾及Example文件夾。將各模塊的頭文件及實現文件拷貝到 Common文件夾下(推薦使用這樣的文件組織結構,其它工程也可以建立在Projects下,各工程共享Common文件夾中的代碼)。
啟動KeilC的IDE,在Example下建立新工程,將各模塊的實現文件包含進工程。
在Example文件夾下建立Output文件夾,更改工程設置,將Output作為輸出文件和List文件的輸出文件夾(推薦使用這樣的結構,當保存工程文件時,可以簡單的刪除Output文件夾中的內容而不會誤刪有用的工程文件)。
建立工程配置頭文件Config.h及工程主文件Example.c,并將Exmaple.c文件加入工程。
輸入代碼:
代碼的具體編寫過程略。下面是最后的Config.h文件及Example.c文件。
//
// file: Config.h
//
#ifndef _CONFIG_H_
#define _CONFIG_H_
#i nclude <Atmel/At89x52.h> // 使用AT89C52做控制
#i nclude “../Common/Common.h” // 使用自定義的數據類型
#define TIMER_RELOAD 922 // 11.0592MHz晶振,1ms中斷周期
#define TIMER_KBSCANDELAY 40 // 40ms重檢測按鍵狀態,即40ms消抖
#define SCOMM_AsyncInterface // 使用異步通訊服務
#define IsPackageHeader(x) ((x) == 0xff) // 判斷包頭是不是0xff
#define IsPackageTailer(x, y, z) ((y) <= (z)) // 判斷包的長度是不是足夠
#endif // _CONFIG_H_
//
// file: Example.c
//
#i nclude <Atmail/At89x52.h>
#i nclude “../Common/Common.h”
#i nclude “../Common/Timer.h”
#i nclude “../Common/Scomm.h”
#i nclude “../Common/KBScan.h”
BIT gbitLampState = 1; // 燈的狀態,缺省為off
static void Initialize()
{
InitTimerModule(); // 初始化時鐘模塊
InitSCommModule(0xfd, TRUE); // 初始化通訊模塊,11.0592MHz晶振,
// 波特率為19200
EA = 1; // 開中斷
}
void main()
{
Initialize(); // 初始化
while(TRUE) // 主循環
{
ImpTimerService(); // 實現時鐘中斷服務,如鍵盤掃描
AsyncRecePackage(4); // 接收4個字節長的數據包
}
}
// 在中斷外部響應時鐘中斷事件
void OnTimerEvent()
{
// do nothing
}
// 控制外部燈
static void TriggerLamp(BIT bEnable)
{
P10 = ~bEnable; // 需要反相控制
}
// 鍵掃描回調函數
BYTE KBScan()
{
BIT b;
P11 = 1; // 讀之前拉高引腳電平
b = P11; // 讀入引腳狀態
return ~b; // 數據反相做掃描碼
}
// 計算校驗和
static BYTE CalcCheckSum(BYTE* pbyBuf, BYTE byLen)
{
BYTE by, bySum = 0;
for(by = 0; by < byLen; by++)
bySum += pbyBuf[by];
return 0 – bySum;
}
// 接收到鍵盤消息回調函數
void onKeyPressed(BYTE byValue, BYTE byState)
{
BYTE by[4];
if(byState == 0)
{
switch(byValue)
{
case 0x01:
gbitLampState = ~g bitLampState; // 燈狀態取反
TriggerLamp(gbitLampState); // 執行控制
by[0] = 0xff; // 構造數據包
by[1] = 0x12;
by[2] = (BYTE)gbitLampState;
by[3] = CalcCheckSum(by, 3); // 求校驗和
SendPackage(by, 4); // 發送數據包
break;
// 處理其它掃描碼
default:
break;
}
}
// 接收到數據包回調函數
void OnRecePackage(BYTE* pbyBuf, BYTE byBufLen)
{
BYTE by[4];
by[0] = 0xff;
by[1] = 0x11;
if(byBufLen != 4 || pbyBuf[3] != CalcCheckSum(pbyBuf, 3))
{
by[2] = 0;
by[3] = CalcCheckSum(by, 3);
SendPackage(by, 4); // 處理長度或校驗和不正確
}
switch(pbyBuf[1])
{
case 0x10:
gbitLampState = (BIT)pbyBuf[2];
TriggerLamp(gbitLampState);
by[2] = 1;
by[3] = CalcCheckSum(by, 3);
SendPackage(by, 4); // 發送成功執行通知
break;
default: // 不知道的命令
by[2] = 0;
by[3] = CalcCheckSum(by, 3);
SendPackage(by, 4); // 發送沒有成功執行通知
break;
}
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -