Z-STACK 之LED驅動詳解
最近一段時間學習ZigBee,用的TI公司的cc2530,協議棧是z-stack,爲了深入瞭解整個Z-stack,我從底層的驅動代碼開始看起,首先是LED驅動。雖然是簡單的LED亮滅,但是z-stack中的LED驅動卻寫的非常好,在這給我們提供了很好的寫驅動的思路。
首先看一下hal_led.h頭文件中的一些宏定義,其中定義了四個LED,分別是HAL_LED_1,HAL_LED_2,HAL_LED_3,HAL_LED_4,分別對應板子上的Green,Red,Yellow,Blue四個LED,然後定義了五種LED模式,HAL_LED_MODE_OFF,燈滅,HAL_LED_MODE_ON,燈亮,HAL_LED_MODE_BLINK,燈閃爍,HAL_LED_MODE_FLASH,燈閃亮,在這裏,當調用HalLedSet (uint8 leds, uint8 mode)這個函數就會看到HAL_LED_MODE_BLINK和HAL_LED_MODE_FLASH的區別。HAL_LED_MODE_TOGGLE,燈狀態切換。還定義了LED的一些默認參數,HAL_LED_DEFAULT_MAX_LEDS,LED最大個數爲4個,HAL_LED_DEFAULT_DUTY_CYCLE,LED閃爍的默認亮滅的佔空比,HAL_LED_DEFAULT_FLASH_COUNT,默認的閃爍次數,HAL_LED_DEFAULT_FLASH_TIME,默認的閃爍一次的時間爲1000,這個是相對osal_systemClock這個系統tick的,具體單位時間是多少還要看用於tick的定時器,先不管,以後再分析。
再看hal_led.c這個源文件,其中定義了HalLedControl_t的結構體,這個結構體主要是對led控制的一些參數,
typedef struct {
uint8 mode; /* Operation mode */
uint8 todo; /* Blink cycles left */
uint8 onPct; /* On cycle percentage */
uint16 time; /* On/off cycle time (msec) */
uint32 next; /* Time for next change */
} HalLedControl_t;
看這個結構體,mode爲led的操作模式,就是頭文件中定義的那5種mode,todo即爲LED剩餘閃爍的次數,onPct爲LED閃爍的亮的時間所佔的比例,time即爲LED每一次閃爍的時間,next爲LED作出下一次轉變的時間,其單位是相對於系統的tick即osal_systemClock這個系統變量。然後定義了HalLedStatus_t這個結構體,其中HalLedControl_t HalLedControlTable[HAL_LED_DEFAULT_MAX_LEDS]是對四個LED的控制情況的數組,sleepActive是指在系統睡眠時候是否啓用LED,如果是則爲true,否則爲false。接下來看HalLedState這個全局變量記錄的是LED上次更新時候的狀態,通過後面的代碼,我的理解是HalLedState的低四位分別表示四個LED,如果某一位爲1,則它上次的狀態爲ON,否則爲OFF。HalSleepLedState這個全局變量主要是記錄了系統進入睡眠模式之前的四個LED的狀態。preBlinkState這個全局變量記錄了LED在進入閃爍模式之前的狀態。然後定義了這個HalLedStatus_t結構體的全局變量HalLedStatusControl記錄四個LED的控制狀態。
HalLedInit這個函數式LED的初始化,調用HalLedSet (HAL_LED_ALL, HAL_LED_MODE_OFF)將四個LED置OFF,然後HalLedStatusControl.sleepActive = FALSE即在睡眠狀態下不啓用LED;下面看下HalLedSet這個驅動函數:
uint8 HalLedSet (uint8 leds, uint8 mode)
{
#if (defined (BLINK_LEDS)) && (HAL_LED == TRUE)
uint8 led;
HalLedControl_t *sts;
switch (mode)
{
case HAL_LED_MODE_BLINK:
/* Default blink, 1 time, D% duty cycle */
HalLedBlink (leds, 1, HAL_LED_DEFAULT_DUTY_CYCLE, HAL_LED_DEFAULT_FLASH_TIME);
break;
case HAL_LED_MODE_FLASH:
/* Default flash, N times, D% duty cycle */
HalLedBlink (leds, HAL_LED_DEFAULT_FLASH_COUNT, HAL_LED_DEFAULT_DUTY_CYCLE, HAL_LED_DEFAULT_FLASH_TIME);
break;
case HAL_LED_MODE_ON:
case HAL_LED_MODE_OFF:
case HAL_LED_MODE_TOGGLE:
led = HAL_LED_1;
leds &= HAL_LED_ALL;
sts = HalLedStatusControl.HalLedControlTable;
while (leds)
{
if (leds & led)
{
if (mode != HAL_LED_MODE_TOGGLE)
{
sts->mode = mode; /* ON or OFF */
}
else
{
sts->mode ^= HAL_LED_MODE_ON; /* Toggle */
}
HalLedOnOff (led, sts->mode);
leds ^= led; //上一個led處理完進入下一個
}
led <<= 1;
sts++;
}
break;
default:
break;
}
#elif (HAL_LED == TRUE)
HalLedOnOff(leds, mode);
#else
// HAL LED is disabled, suppress unused argument warnings
(void) leds;
(void) mode;
#endif /* BLINK_LEDS && HAL_LED */
return ( HalLedState );
}
在這個函數中描述瞭如何根據不同的mode實現對LED的控制,如果爲mode爲HAL_LED_MODE_BLINK或HAL_LED_MODE_FLASH,則相應調用HalLedBlink這個函數,不同的模式傳進不同的參數。當然這個參數值是默認的。當mode爲HAL_LED_MODE_ON、HAL_LED_MODE_OFF、HAL_LED_MODE_TOGGLE之一時,程序裏面用一個while循環對四個LED進行相應的處理,其中leds ^= led表示上一個led已經處理完將其清零下次不再處理。在後面的led <<= 1; sts++;兩句代碼根據四個LED對應不同的低四位看,應該很容易明白。最後函數返回HalLedState即led的當前狀態,在上面函數中調用了HalLedOnOff函數,該函數在最後
if (mode)
{
HalLedState |= leds;
}
else
{
HalLedState &= (leds ^ 0xFF);
}
即如果mode不爲OFF時,則HalLedState將記錄leds爲ON,否則將leds中的某位置0,即爲OFF狀態。
回到HalLedSet這個函數,在mode爲HAL_LED_MODE_BLINK或HAL_LED_MODE_FLASH時調用了HalLedBlink這個函數,下面看看它的實現代碼
void HalLedBlink (uint8 leds, uint8 numBlinks, uint8 percent, uint16 period)
{
#if (defined (BLINK_LEDS)) && (HAL_LED == TRUE)
uint8 led;
HalLedControl_t *sts;
if (leds && percent && period)
{
if (percent < 100)
{
led = HAL_LED_1;
leds &= HAL_LED_ALL;
sts = HalLedStatusControl.HalLedControlTable;
while (leds)
{
if (leds & led)
{
/* Store the current state of the led before going to blinking */
preBlinkState |= (led & HalLedState);
sts->mode = HAL_LED_MODE_OFF; /* Stop previous blink */
sts->time = period; /* Time for one on/off cycle */
sts->onPct = percent; /* % of cycle LED is on */
sts->todo = numBlinks; /* Number of blink cycles */
if (!numBlinks) sts->mode |= HAL_LED_MODE_FLASH; /* Continuous */
sts->next = osal_GetSystemClock(); /* Start now */
sts->mode |= HAL_LED_MODE_BLINK; /* Enable blinking */
leds ^= led;
}
led <<= 1;
sts++;
}
osal_set_event (Hal_TaskID, HAL_LED_BLINK_EVENT);
}
else
{
HalLedSet (leds, HAL_LED_MODE_ON); /* >= 100%, turn on */
}
}
else
{
HalLedSet (leds, HAL_LED_MODE_OFF); /* No on time, turn off */
}
#elif (HAL_LED == TRUE)
percent = (leds & HalLedState) ? HAL_LED_MODE_OFF : HAL_LED_MODE_ON;
HalLedOnOff (leds, percent); /* Toggle */
#else
// HAL LED is disabled, suppress unused argument warnings
(void) leds;
(void) numBlinks;
(void) percent;
(void) period;
#endif /* BLINK_LEDS && HAL_LED */
}
這個函數中當percent小於100時先Store the current state of the led before going to blinking,將其保存在preBlinkState中,然後分別對sts指針變量中四個元素賦值,如果numBlinks爲0,則表示LED持續不停的閃爍。將
sts->next賦值爲osal_GetSystemClock(),即系統的tick值,後面會看到這個next的具體含義。最後調用osal_set_event這個函數將HAL_LED_BLINK_EVENT事件傳給Hal_TaskID任務,當系統輪詢任務時,HAL層任務則有事件要處理。在hal_drivers.c文件中Hal_ProcessEvent,即HAL層的事件處理函數,
if ( events & HAL_LED_BLINK_EVENT )
{
#if (defined (BLINK_LEDS)) && (HAL_LED == TRUE)
HalLedUpdate();
#endif /* BLINK_LEDS && HAL_LED */
return events ^ HAL_LED_BLINK_EVENT;
}
看這裏的代碼知道,當有LEDblink事件時調用了HalLedUpdate()這個函數,這個函數在hal_led.c文件裏面,
void HalLedUpdate (void)
{
uint8 led;
uint8 pct;
uint8 leds;
HalLedControl_t *sts;
uint32 time;
uint16 next;
uint16 wait;
next = 0;
led = HAL_LED_1;
leds = HAL_LED_ALL;
sts = HalLedStatusControl.HalLedControlTable;
/* Check if sleep is active or not */
if (!HalLedStatusControl.sleepActive)
{
while (leds)
{
if (leds & led)
{
if (sts->mode & HAL_LED_MODE_BLINK)
{
time = osal_GetSystemClock();
if (time >= sts->next)//到了該閃爍的時間了
{
if (sts->mode & HAL_LED_MODE_ON)
{
pct = 100 - sts->onPct; /* Percentage of cycle for off */
sts->mode &= ~HAL_LED_MODE_ON; /* Say it's not on */
HalLedOnOff (led, HAL_LED_MODE_OFF); /* Turn it off */
if (!(sts->mode & HAL_LED_MODE_FLASH)) //不是無限閃爍
{
sts->todo--; /* Not continuous, reduce count */
if (!sts->todo) //閃爍次數到
{
sts->mode ^= HAL_LED_MODE_BLINK; /* No more blinks */
}
}
}
else
{
pct = sts->onPct; /* Percentage of cycle for on */
sts->mode |= HAL_LED_MODE_ON; /* Say it's on */
HalLedOnOff (led, HAL_LED_MODE_ON); /* Turn it on */
}
if (sts->mode & HAL_LED_MODE_BLINK)
{
wait = (((uint32)pct * (uint32)sts->time) / 100);
sts->next = time + wait;
}
else
{
/* no more blink, no more wait */
wait = 0;
/* After blinking, set the LED back to the state before it blinks */
HalLedSet (led, ((preBlinkState & led)!=0)?HAL_LED_MODE_ON:HAL_LED_MODE_OFF);
/* Clear the saved bit */
preBlinkState &= (led ^ 0xFF);
}
}
else
{
wait = sts->next - time; /* Time left */
}
if (!next || ( wait && (wait < next) ))
{
next = wait;
}
}
leds ^= led;
}
led <<= 1;
sts++;
}
if (next)
{
osal_start_timerEx(Hal_TaskID, HAL_LED_BLINK_EVENT, next); /* Schedule event */
}
}
}
這個函數只要理解了結構體中幾個元素變量的含義,其實很好理解啦!當LED的mode爲HAL_LED_MODE_BLINK時,首先time = osal_GetSystemClock();獲取系統的tick值,然後比較time和sts->next,如果前者大則表明已經到了LED該閃爍的時間了,此時如果mode爲ON(這裏面有個技巧,即每次LED閃爍之後mode中記錄的都是上次閃爍的LED的狀態),則將mode置爲OFF,然後調用HalLedOnOff將LED置OFF,如果sts->mode不爲HAL_LED_MODE_FLASH,則LED不是無限閃爍模式,即有numBlinks次數的閃爍,在上一個函數中可以看到
if (!numBlinks) sts->mode |= HAL_LED_MODE_FLASH;這句,即numBlinks爲0時,將其mode增加HAL_LED_MODE_FLASH,這就說明要無限閃爍了。看這句
else
{
pct = sts->onPct; /* Percentage of cycle for on */
sts->mode |= HAL_LED_MODE_ON; /* Say it's on */
HalLedOnOff (led, HAL_LED_MODE_ON); /* Turn it on */
}
即當mode爲HAL_LED_MODE_OFF時,調整pct爲ON的比例,然後mode反轉,亮LED,如果mode中還有HAL_LED_MODE_BLINK,則說明LED還沒有閃爍完,還要接着閃,此時將wait賦值爲(((uint32)pct * (uint32)sts->time);sts->next = time + wait;wait爲下一次要閃爍時候的等待時間,next爲下一次要閃爍的tick值;如果mode中沒有
HAL_LED_MODE_BLINK則說明LED已經閃爍完了,將wait清零,然後調用HalLedSet將LED置爲閃爍之前的狀態。
如果time小於sts->next則閃爍的時間還沒到還要繼續等待,wait = sts->next - time;將等待時間wait調整。
if (!next || ( wait && (wait < next) ))
{
next = wait;
}
如果next爲0則說明是第一次閃爍,則將next置爲wait值,如果( wait && (wait < next) )則是某一次閃爍且閃爍時間沒到則重新將next賦值爲wait。
如果next不爲0即還需要下次的閃爍,此時調用osal_start_timerEx函數,此函數在經過next時間之後會向Hal_TaskID任務發送HAL_LED_BLINK_EVENT事件,LED需要繼續閃爍。
以上是整個LED驅動的詳細解析過程。函數HalLedEnterSleep和HalLedExitSleep很少用到,是在系統睡眠的時候用到,這裏不需要解釋。 z-stack的LED主要是用來在組建ZigBee網絡時能通過觀察LED的狀態從而回到網絡的狀態,如LED閃爍時候,協調器還沒有組建網絡,當組建網絡之後,LED不閃爍,而是某一個常亮。在以後的例程中我再仔細分析這個過程。下一次介紹一下key驅動。