|
|
最近通過(guò)專(zhuān)業(yè)綜合實(shí)驗(yàn)接觸單片機(jī)編程。對(duì)歷程中的Delay函數(shù)有所疑問(wèn),最終在大神們的幫助下解決。寫(xiě)了筆記備忘一下。
問(wèn)題的出現(xiàn):不管是郭天祥的單片機(jī)教程,還是開(kāi)發(fā)板配套資料,都會(huì)用到類(lèi)似函數(shù)來(lái)完成延時(shí):
void Delay(unsigned int t)
{
unsigned int k;
while(t--)
{
for(k=0; k<80; k++)
;
}
}
例如在按鍵檢測(cè)中,線(xiàn)delay一會(huì)兒來(lái)完成去抖動(dòng),檢測(cè)按下后再delay一會(huì)兒來(lái)完成等待彈起。再比如在數(shù)碼管顯示中,控制第一個(gè)數(shù)碼管顯示,delay一會(huì)再控制第二個(gè),否則就會(huì)導(dǎo)致整個(gè)數(shù)碼管都被刷亮,一直顯示8。再比如步進(jìn)電機(jī)控制,先發(fā)110000再發(fā)011000之間用delay來(lái)控制轉(zhuǎn)速,delay的久轉(zhuǎn)的慢。
但是如果需要程序通過(guò)按鍵來(lái)控制步進(jìn)電機(jī)的速度,同時(shí)將控制參數(shù)顯示在數(shù)碼管上。這時(shí)問(wèn)題就出現(xiàn)了:在檢測(cè)按鍵的時(shí)候電機(jī)不轉(zhuǎn)了,數(shù)碼管也不現(xiàn)實(shí)了,因?yàn)镃PU在delay,在做沒(méi)有用的空運(yùn)算。同樣的,控制電機(jī)轉(zhuǎn)的時(shí)候數(shù)碼管也不亮了,按鍵也不能檢測(cè)了。控制數(shù)碼管也是同理。可見(jiàn)歷程中每個(gè)模塊的代碼都是不可復(fù)用的,不可擴(kuò)展的。那么怎么寫(xiě)出可復(fù)用、可擴(kuò)展的單片機(jī)程序呢。
問(wèn)題的解決:先把這種方法起個(gè)名字就叫帶計(jì)數(shù)的輪詢(xún)模式吧。
CPU.h中:
unsigned int CPU_Count = 0; //全局變量CPU循環(huán)計(jì)數(shù)
main.cpp中:
#include "CPU.h"
#include "... //其他設(shè)備控制
void main()
{
while(1)
{
CPU_Count ++ ;
ProcessingMotor(); //電機(jī)控制
ProcessingLED(); //LED顯示
ProcessingKey(); //按鍵檢測(cè)
}
}
下面看具體的ProcessingMotor的實(shí)現(xiàn)。
在Motor.h中
#define GPIO_MOTOR P1
unsigned char code FFW[8]={0xf1,0xf3,0xf2,0xf6,0xf4,0xfc,0xf8,0xf9}; //反轉(zhuǎn)順序
unsigned char code FFZ[8]={0xf9,0xf8,0xfc,0xf4,0xf6,0xf2,0xf3,0xf1}; //正轉(zhuǎn)順序
unsigned char Direction,Speed;
void Motor()
{
if(Speed == 0)
return;
if(CPU_Count%Speed == 0)
{
if(Direction==1)
GPIO_MOTOR = FFW[(CPU_Count/Speed)%8]&0x1f; //取數(shù)據(jù)
if(Direction==2)
GPIO_MOTOR = FFZ[(CPU_Count/Speed)%8]&0x1f;
}
}
我們可以通過(guò)CPU_Count%Speed==0來(lái)控制while循環(huán)幾次來(lái)進(jìn)行一次脈沖發(fā)送,同時(shí)可以通過(guò)(CPU_Count/Speed)%8的值來(lái)得到該發(fā)地幾個(gè)波了。這樣CPU就可以得到充分的利用。
LED的控制也是類(lèi)似,這里就不再贅述。
可復(fù)用可擴(kuò)展的按鍵檢測(cè)
其實(shí)按鍵檢測(cè)關(guān)鍵就在兩個(gè)地方,一個(gè)是按鍵接在哪個(gè)引腳上,一個(gè)是按鍵按下應(yīng)該響應(yīng)那個(gè)函數(shù)。如果我們能通過(guò)一些后臺(tái)機(jī)制,最終實(shí)現(xiàn)一個(gè)接口:connectButton(uchar PX,uchar PMask,(*SLOT)());以后再需要用到按鍵的檢測(cè),只需要寫(xiě)一個(gè)按鍵按下對(duì)應(yīng)的函數(shù),并調(diào)用connectButton即可。
那么怎么樣實(shí)現(xiàn)這樣一個(gè)借口呢?
首先我們使用這樣一種檢測(cè)按鍵的方法。在CPU的while循環(huán)中每次都去讀按鍵的狀態(tài),如果按鍵按下測(cè)PressCount++;如果按鍵沒(méi)有按下則unPressCount++;在pressCount加到一個(gè)臨界值說(shuō)明按鍵按下,同時(shí)將unPressCount清零。unPressCount加到一個(gè)臨界值,說(shuō)明按鍵彈起,將pressCount清零。
下面是代碼實(shí)現(xiàn):
typedef void(*SLOT)();
#define B_CNT 4
unsigned char listSize = 0;
typedef struct ButtonNode
{
unsigned char isPressed; //是否被按下
unsigned char px; // 標(biāo)記P0等
unsigned char pmask; // 標(biāo)記P0等第幾個(gè)管腳
unsigned int pressCount; //cpu一次輪轉(zhuǎn)中,如果被按下則加一
unsigned int unPressCount; //cpu一次輪轉(zhuǎn)中,如果彈起則加一
SLOT func; //回調(diào)函數(shù)
}ButtonNode;
ButtonNode Button[B_CNT]
這里用的是結(jié)構(gòu)體的數(shù)組來(lái)保存需要的檢測(cè)的按鍵,其實(shí)最好用鏈表動(dòng)態(tài)申請(qǐng)空間,因?yàn)橛面湵聿拍苷嬲龑?shí)現(xiàn)開(kāi)閉性,不用對(duì)代碼做任何修改。而我們這里為了節(jié)約內(nèi)存,每次都要修改檢測(cè)的按鍵個(gè)數(shù)B_CNT。
然后實(shí)現(xiàn)ProcessingKey,對(duì)保存的按鍵信息進(jìn)行循環(huán)檢測(cè)。
void ProcessingKey()
{
unsigned char num = CPU_Count%BUTTON_NUM;
unsigned char p;
if( num >= listSize)
return;
switch (Button[num].px)
{
case P0_X:
p = P0;
break;
case P1_X:
p = P1;
break;
case P2_X:
p = P2;
break;
case P3_X:
p = P3;
break;
}
if(!((p >> Button[num].pmask) & 1))
{
Button[num].pressCount ++ ;
if(Button[num].pressCount==150)
{
Button[num].pressCount=0;
if(Button[num].isPressed==0)
{
Button[num].func();
Button[num].isPressed = 1;
Button[num].unPressCount=0;
}
}
}else
{
Button[num].unPressCount ++;
if(Button[num].unPressCount==150)
{
Button[num].unPressCount=0;
if(Button[num].isPressed==1)
{
Button[num].isPressed = 0;
Button[num].pressCount=0;
}
}
}
}
這里我們使用150次作為臨界值。
最后我們來(lái)實(shí)現(xiàn)最想要的接口connectButton:
void connectButton(unsigned char _px,unsigned char _pmask,SLOT _func)
{
Button[listSize].isPressed = 0;
Button[listSize].px = _px;
Button[listSize].pmask = _pmask;
Button[listSize].pressCount = 0;
Button[listSize].unPressCount = 0;
Button[listSize].func = _func;
listSize ++;
}
大功告成。該enjoy it了。
以后我們的單片機(jī)程序要使用某個(gè)按鍵按下執(zhí)行一些內(nèi)容時(shí),只需要這樣
實(shí)現(xiàn)按下后的響應(yīng)
void onP3_0Pressed(){ ... }
調(diào)用connectButton(P3_X,0,onP3_0Pressed)
只要你使用的是計(jì)數(shù)輪詢(xún)模式,那么按鍵檢測(cè)就可以直接實(shí)現(xiàn)了。
|
|