單片機C51編程幾個(gè)有用的模塊
為了能夠使用用SyncRecePackage或AsyncRecePackage函數從接收到的數據中識別出如上格式的數據包,有兩種方法:
第一種辦法是在Config.h文件中定義宏SCOMM_SimplePackageFormat,說(shuō)明數據包為一種簡(jiǎn)單格式,比如上面的協(xié)議。
之后還要定義兩個(gè)宏分別用來(lái)識別數據包頭和數據包尾,兩個(gè)宏分別是:
IsPackageHeader(x)和IsPackageTailer(x,y,z)
接收函數(SyncRecePackage和AsyncRecePackage)在沒(méi)有開(kāi)始接收數據包(準確的說(shuō)是還沒(méi)有從接收到的數據包中找到包頭的時(shí)候),會(huì )對接收到的每一個(gè)字節的數據調用IsPackageHeader宏,將相應的數據作為參數,如果IsPackageHeader宏的結果為T(mén)RUE,則認為找到了數據包頭,否則繼續對下一個(gè)字節進(jìn)行判斷。
上面的協(xié)議對應的IsPackageHeader宏可以寫(xiě)為:
#defineIsPackageHeader(x)((x)==0xff)
當接收到包頭之后,接收函數會(huì )對接下來(lái)的每一個(gè)字節數據調用IsPackagTailer宏來(lái)判斷是不是已經(jīng)接收完數據包,三個(gè)參數分別為:
x:當前判斷的數據。
y:從包頭開(kāi)始到當前被判斷的數據止的計數值,即當前已經(jīng)接收到的字節數。
z:用戶(hù)在調用SyncRecePackage或AsyncRecePackage時(shí)指定的byParam參數。
與IsPackageHeader相似,如果宏IsPackageTailer的運算結果為T(mén)RUE,則認為接收到完整的數據包,則調用相應的回調函數(對于異步接收函數)或返回(對于同步接收函數)。如果運算結果為FALSE則繼續判斷下一個(gè)字節的數據。
上面的協(xié)議對應的IsPackageTailer宏可以寫(xiě)為:
#defineIsPackageTailer(x,y,z)((y)>=(z))
當然,用戶(hù)也可以將IsPackageHeader和IsPackageTailer定義成為函數,通過(guò)BIT類(lèi)型的返回值來(lái)向調用者提供與相應宏相同的信息。
另一種辦法需要在Config.h文件中定義宏SCOMM_ComplexPackageFormat。(需要注意的是,不能夠同時(shí)定義SCOMM_SimplePackageFormat和SCOMM_ComplexPackageFormat宏,否則會(huì )造成嚴重的不可預見(jiàn)性錯誤。
這時(shí)需要提供回調函數QueryPackageFormat,原形如下:
BYTEQueryPackageFormat(BYTEbyData,BYTEbyCount,BYTEbyParam);
函數中三個(gè)參數的含義與使用簡(jiǎn)單數據包格式時(shí)判斷數據包尾的宏的參數相同。
函數通過(guò)返回值來(lái)通知作為調用者的接收函數對接收到的數據如何處理,但目前這種方法僅為需要處理復雜數據包格式時(shí)的一種可選方法,但不推薦。用戶(hù)如果想使用這種方法可以自己更改接收函數中相應的
#ifdefSCOM_ComplexPackageFormat
#endif//SCOMM_ComplexPackageFormat
預編譯指令之間的內容。
例如指定QueryPackageFormat的返回值的含義:
0:繼續找數據包頭或繼續找數據包尾。
1:找到數據包頭。
2:找到數據包尾。
3:數據包出錯,需要拋棄。
然后更改源代碼來(lái)實(shí)現上面的協(xié)議。
注意:當用戶(hù)需要使用字符串的時(shí)候,可以利用簡(jiǎn)單的包裝函數將字符串轉換為字節數組。所以沒(méi)有必要提供專(zhuān)用的字符串處理函數。
鍵盤(pán)掃描模塊
鍵盤(pán)掃描模塊有兩種工作方式,一種為自動(dòng)的由時(shí)鐘模塊調用,另一種是由程序員自行調用。
1)由時(shí)鐘模塊自動(dòng)調用的方式
將時(shí)鐘模塊實(shí)現文件(Timer.h)及鍵盤(pán)掃描模塊的實(shí)現文件(KBScan。c)包含進(jìn)工程,在Config.h文件中添加TIMER_KBSCANDELAY宏。時(shí)鐘模塊自動(dòng)對時(shí)鐘中斷進(jìn)行計數,當達到TIMER_KBSCANDELAY宏所定義的值后,自動(dòng)調用鍵盤(pán)掃描模塊中的函數KBScanProcess()進(jìn)行鍵盤(pán)掃描,也就是說(shuō),這個(gè)宏的值可以決定按鍵消抖動(dòng)的時(shí)間。
用戶(hù)應該提供兩個(gè)回調函數OnKBScan()及OnKeysPressed()。在函數OnKBScan中進(jìn)行鍵盤(pán)掃描,并返回掃描碼。掃描碼的類(lèi)型缺省為BYTE,當鍵盤(pán)規模較大時(shí),BYTE不能夠完全包含鍵盤(pán)信息時(shí),可在Config.h文件中重定義宏KBVALUE,如下:
#defineKBVALUEWORD
這樣,就可以使用16位的鍵盤(pán)掃描碼,如果此時(shí)還達不到要求,可以將鍵盤(pán)掃描碼定義成一個(gè)結構,但這樣做將會(huì )增加代碼量及消耗更多的RAM資源,故不推薦。
掃描模塊調用OnKBScan取得掃描碼,并調用用戶(hù)可以重定義的宏IsNoKeyPressed來(lái)判斷是否有鍵按下,缺省的IsNoKeyPressed實(shí)現如下:
#defineIsNoKeyPressed(x)((x)==0x00)
即認為OnKBScan返回0掃描碼時(shí)為沒(méi)有鍵按下,如果掃描函數返回其它非零掃描碼做為無(wú)鍵按下的掃描碼時(shí),可以在Config.h文件中重定義IsNoKeyPressed宏的實(shí)現。
8位鍵盤(pán)掃描碼(缺省值)時(shí),相應的掃描函數為:
BYTEOnKBScan()
當掃描模塊經(jīng)過(guò)軟件消抖動(dòng)之后,發(fā)現有鍵按下,就會(huì )調用另一個(gè)回調函數OnKeysPressed。函數的聲明應該如下:
voidOnKeyPressed(BYTEbyKBValue,BYTEbyState)
其中中的參數byKBValue的類(lèi)型為BYTE,此為缺省值,如果使用其它類(lèi)型的掃描碼,就將此參數變?yōu)橄鄳?lèi)型。這個(gè)值由OnKBScan返回。另一個(gè)參數byState在通常情況下為零。但當用戶(hù)在Config.h中定義宏KBSCAN_BRUSTCOUNT,同時(shí)鍵盤(pán)上的某鍵被按住不放時(shí),掃描模塊對它自己的調用(注意這里和TIMER_KBSCANDELAY宏不同,TIMER_KBSCANDELAY是時(shí)鐘中斷足夠的次數后調用掃描模塊,而KBSCAN_BRUSHCOUNT為掃描模塊自身的被調用次數)進(jìn)行計數,當達到KBSCAN_BRUSTCOUNT時(shí),掃描模塊調用OnKeysPressed,此時(shí)第一個(gè)參數的含義不變,而byState變成1,同時(shí)計數器復位,又經(jīng)過(guò)一段時(shí)間后,用值為3的byState調用OnKeysPressed。這樣就可以很方便的實(shí)現多功能鍵或者檢測某鍵的長(cháng)時(shí)間被按下。
2)由用戶(hù)自行調用
由用戶(hù)自行在程序中調用掃描模塊,而不是由時(shí)鐘中斷自行調用。其它與方式1相同。
注意:
1)函數KBScanProcess為非阻塞函數,它將在很快的時(shí)間內返回,等待再次分配給它執行的機會(huì )。
2)函數KBScanProcess是在時(shí)鐘中斷外部運行的,它的過(guò)程可以被任何中斷打斷,但不影響系統運行。
3)byState的最大值為250,之后被復位為零。
評論