正如我早先說的。這個應(yīng)用程序所做的兩件事之一是使紅色指示燈閃爍。這可以通過下面的代碼來做到。這里函數(shù)flashRed()作為一個任務(wù)來執(zhí)行。然而,如果忽略這一點(diǎn)以及新的函數(shù)名,那么這段代碼和第七章“外圍設(shè)備”中我們研究過的LED 閃爍函數(shù)幾乎是一樣的。在這一級上的唯一差別就是新的頻率(10HZ)和指示燈的顏色(紅色)。
#include "led.h"
#include "timer.h"
/*******************************************************************
*
* Function : flashRed()
*
* Description : Blink the red LED ten times a second.
*
* Notes : This outer loop is hardware-independent, However, it
* calls the hardware-dependent function toggleLed().
*
* Returns : This routine contains an infinite loop.
*
*******************************************************************/
void
flashRed(void)
{
Timer timer;
timer.start(50, Periodic); // Start a periodic 50 ms timer.
while(1)
{
toggleLed(LED_RED); // Toggle the red LED.
timer.waitfor(); // Wait for the timer to expire.
}
} /* flashRed() */
LED 閃爍程序主要的改變在這段代碼中是看不見的。這些主要的變化是對toggleLed()函數(shù)和Timer 類進(jìn)行了修改以兼容多任務(wù)的環(huán)境。toggleLed()函數(shù)就是我現(xiàn)在稱之為指示燈驅(qū)動的東西。一旦你開始這樣考慮這個問題,tb也許會考慮把驅(qū)動重寫成一個C++類,并已加入新的方法以明確地設(shè)置和清除指示燈,這個方法也是講得通的。但是,采用和第七章一樣的實現(xiàn)方法,并且僅使用一個互斥體來保護(hù)P2LTCH 寄存器不被一個以上的任務(wù)同時訪問,就已經(jīng)足夠了(注1)。這里是修改后的代碼:
#include "i8018xEB.h"
#include "adeos.h"
static Mutex gLedMutex;
/*******************************************************************
*
* Function : toggleLed()
*
* Description : Toggle the state of one or both LEDs.
*
* Notes : This version is ready for multitasking.
*
* Returns : None defined.
*
*******************************************************************/
void
toggleLed(unsigned char ledMask)
{
gLedMutex.take();
// Read P2LTCH, modify its value, and write the result.
//
gProcessor.pPCB->ioPort[1].latch ^= ledMask;
gLedMutex.release();
} /* toggleLed() */
注 1:在早先的那個toggleLed()函數(shù)中存在競爭的情況。為了理解這一點(diǎn),返回去查看這段代碼,并且假想兩個任務(wù)在共享指示燈,第一個任務(wù)恰好調(diào)用了那個切換紅色指示燈的函數(shù),突然之間,第一個任務(wù)被第二個任務(wù)占先,此時,在toggleLed()函數(shù)中,指示燈的狀態(tài)都被讀入并存入一個處理器的寄存器?,F(xiàn)在第二個任務(wù)導(dǎo)致兩個指示燈的狀態(tài)都被再次讀入并且存入另一個處理器的寄存器,然后兩個指示燈的狀態(tài)都被修改以改變綠色指示燈的狀態(tài),并且導(dǎo)致結(jié)果寫出到P2LTCH 寄存器。當(dāng)中斷的任務(wù)重新啟動時,它已經(jīng)有一個指示燈狀態(tài)的拷貝。但是這個拷貝不再準(zhǔn)確了。當(dāng)發(fā)生這個變化后,指示燈變成紅燈,并且比新的指示燈狀態(tài)寫到P2LTCH 寄存器。這樣,第二個任務(wù)的改變將會被撤銷。加入一個互斥體可以消除這個潛在的危險。
第七章中的時鐘驅(qū)動程序在應(yīng)用于一個多任務(wù)的環(huán)境之前,tbw必須對它做類似的改動。然而這時不存在競爭的情況(注2)。我們需要利用一個互斥體來消除waitfor 方法中的輪詢。通過把一個互斥體和每一個軟件時鐘關(guān)聯(lián),我們可以使任何一個在等待時鐘的任務(wù)休眠,因此,釋放了處理器以執(zhí)行就緒的低優(yōu)先級的任務(wù)。當(dāng)?shù)却臅r鐘到期了,休眠的任務(wù)會被操作系統(tǒng)喚起。為此,一個指向互斥體的指針,叫做 pMutex,被加入到類的定義中:
class Timer
{
public:
Timer();
~Timer();
int start(unsigned int nMilliseconds, TimerType = OneShot);
int waitfor();
void cancel();
TimerState state;
TimerType type;
unsigned int length;
Mutex * pMutex;
unsigned int count;
Timer * pNext;
private:
static void interrupt Interrupt();
};
每次構(gòu)造函數(shù)創(chuàng)建軟件時鐘的時候,這個指針被初始化。此后,無論何時一個時鐘對象被啟動,它的互斥體可以像下面這樣獲得:
——————————————————————————————————
注 2:記得時鐘硬件只初始化一次(在第一次構(gòu)造函數(shù)請求的時候),并且此后時鐘專用寄存器只能由一個函數(shù)(即中斷服務(wù)例程)來讀寫。
/*******************************************************************
*
* Function : start()
*
* Description : Start a software timer, based on the tick from the
* underlying hardware timer.
*
* Notes : This version is ready for multitasking.
*
* Returns : 0 on success, -1 if the timer is already in use.
*
*******************************************************************/
int
Timer::start(unsigned int nMilliseconds, TimerType timerType)
{
if(state != Idle)
{
return(-1);
}
//
// Take the mutex. It will be released when the timer expires.
//
pMutex->take();
//
// Initialize the software timer.
//
state = Active;
type = timerType;
length = nMilliseconds / MS_PER_TICK;
//
// Add this timer to the active timer list.
//
timerList.insert(this);
return(0);
} /* start() */
通過在時鐘啟動的時候獲得互斥體,我們可以保證在同一個互斥體釋放之前沒有其他任務(wù)(甚至是啟動時鐘的任務(wù))能夠再獲得它。并且這在時鐘自然到期(中斷服務(wù)程序)或是人為取消(通過取消的辦法)之前是不會發(fā)生的。所以在waitfor()中的輪詢可以由pMutex->take()來替換如下:
/*******************************************************************
*
* Function : waitfor()
*
* Description : Wait for the software timer to finish.
*
* Notes : This version is ready for multitasking.
*
* Returns : 0 on success, -1 if the timer is not running.
*
*******************************************************************/
int
Timer::waitfor()
{
if(state != Active)
{
return(-1);
}
//
// Wait for the timer to expire.
//
pMutex->take();
//
// Restart or idle the timer, depending on its type.
//
if(type == Periodic)
{
state == Active;
timerList.insert(this);
}
else
{
pMutex->release();
state = Idle;
}
return(0);
} /* waitfor() */
當(dāng)時鐘最后到期時,中斷服務(wù)程序?qū)尫呕コ怏w,調(diào)用的任務(wù)會在waitfor()中被喚起。喚起的過程中,互斥體已經(jīng)為下一個運(yùn)行的時鐘獲得?;コ怏w只有當(dāng)時鐘是OneShot 類型時才會被釋放,因此,是不會自動重啟的。