nordic mesh 任務調度實現
nordic mesh的任務調度室基於定時器實現的,有兩個鏈表結構維護任務。
需要注意的是,任務調度的部分接口只能在“bearer event”的中段級別被調用,因此調用的形式是通過設置"bearer event"事件來實現的。
結構及接口 @timer_scheduler.h
任務調度的結構定義如下:
typedef enum
{
TIMER_EVENT_STATE_UNUSED, /**< Not present in the scheduler */
TIMER_EVENT_STATE_ADDED, /**< Added for processing */
TIMER_EVENT_STATE_QUEUED, /**< Queued for firing */
TIMER_EVENT_STATE_RESCHEDULED, /**< Rescheduled, but not resorted */
TIMER_EVENT_STATE_ABORTED, /**< Aborted, but still in the list */
TIMER_EVENT_STATE_IGNORED, /**< Aborted, but added for processing */
TIMER_EVENT_STATE_IN_CALLBACK /**< Currently being called */
} timer_event_state_t;
/**
* Timer event structure for schedulable timers.
*/
typedef struct timer_event
{
volatile timer_event_state_t state; /**< Timer event state. */
timestamp_t timestamp; /**< 定時器激發時間 */
timer_sch_callback_t cb; /**< 定時器激發時的回調函數 */
uint32_t interval; /**< 周期事件的時間間隔,0時表示單次事件 */
void * p_context; /**< 調用回調函數cb時傳入的參數 */
struct timer_event* p_next; /**< 指向下一個事件結構的指針,構成鏈表 */
} timer_event_t;
相關的幾個接口如下:
/**
* Initializes the scheduler module.
* 在nrf_mesh_init()中被調用
*/
void timer_sch_init(void);
/**
* Schedules a timer event.
*
* @warning This function must be called from @ref BEARER_EVENT "bearer event" IRQ level or lower.
* 該函數必須在BEARER_EVENT的中斷優先級上被調用
* @warning The structure parameters must not be changed after the structure has been added to the
* scheduler, as this may cause race conditions. If a change in timing is needed, please use the
* @ref timer_sch_reschedule() function. If any other changes are needed, abort the event, change the
* parameter, and schedule it again.
*
* @param[in] p_timer_evt A pointer to a statically allocated timer event, which will be used as
* context for the schedulable event.
*/
void timer_sch_schedule(timer_event_t* p_timer_evt);
/**
* Aborts a previously scheduled event.
*
* @warning This function must be called from @ref BEARER_EVENT "bearer event" IRQ level or lower.
* 該函數必須在BEARER_EVENT的中斷優先級上被調用
* @param[in] p_timer_evt A pointer to a previously scheduled event.
*/
void timer_sch_abort(timer_event_t* p_timer_evt);
/**
* Reschedules a previously scheduled event.
*
* @warning This function must be called from @ref BEARER_EVENT "bearer event" IRQ level or lower.
* 該函數必須在BEARER_EVENT的中斷優先級上被調用
* @param[in] p_timer_evt A pointer to a previously scheduled event.
* @param[in] new_timestamp When the event should time out, instead of the old time.
*/
void timer_sch_reschedule(timer_event_t* p_timer_evt, timestamp_t new_timestamp);
任務調度實現 @timer_scheduler.c
調度器中維護兩個任務鏈表p_head
、p_add_head
,當有新的任務加入調度時,首先會加到p_add_head
鏈表中,此時新任務的狀態會設置為"TIMER_EVENT_STATE_ADDED";之后在process_add_list()
函數遍歷p_add_head
鏈表,將其中狀態為"TIMER_EVENT_STATE_ADDED"的任務加入到第一個鏈表p_head
中。定時器激發時會遍歷第一個鏈表,將其中滿足運行時間的事件運行。
調度器結構的定義如下:
typedef struct
{
timer_event_t* p_head;
timer_event_t* p_add_head;
uint32_t dirty_events; /**< Events in the fire list that needs processing */
uint32_t event_count;
} scheduler_t;
/**
* Static globals
*/
static volatile scheduler_t m_scheduler; /**<調度器實體 */
static bearer_event_flag_t m_event_flag; /**<bearer event 事件 */
需要注意的是,部分定時器接口是在”bearer event“的中斷優先級的被調用的。因此,定義了m_event_flag的全局變量,以產生bearer-event事件調用。
timer_sch_init()
調度器初始化函數的實現代碼如下,在其中首先將調度器實體全部歸零,然后注冊了一個bearer-event事件。
void timer_sch_init(void)
{
// 調度器結構置零
memset((scheduler_t*) &m_scheduler, 0, sizeof(m_scheduler));
// 注冊一個bearer-event事件
// 當調用函數 bearer_event_flag_set(m_event_flag) 時
// 會產生bearer-event中斷,來調用flag_event_cb()回調函數
m_event_flag = bearer_event_flag_add(flag_event_cb);
}
bearer-event事件回調函數
static bool flag_event_cb(void)
{
/* 處理被污染的任務
* 被污染的任務要么直接移除
* 需要再次調度的,則加入到p_add_head鏈表中
*/
process_dirty_events();
/* add all the popped events back in, at their right places.
* 該函數遍歷第二個鏈表,將其中的新加的任務添加到第一個鏈表中
* 第一個鏈表的任務是根據運行時間排序的,從頭到尾運行時間越來越晚
* 即第一個鏈表的鏈表頭是最先運行的節點
*/
process_add_list();
/* 激發定時器
* 遍歷第一個鏈表,一次運行到達運行時間的任務
* p_head依次后移
*/
fire_timers(timer_now());
/*
* 設置定時器函數下次激發時間
*/
setup_timeout(timer_now());
return true;
}
timer_sch_schedule()函數實現如下:
// 任務加入調度時,首先是加入第二個任務鏈表中
void timer_sch_schedule(timer_event_t* p_timer_evt)
{
NRF_MESH_ASSERT(p_timer_evt != NULL);
NRF_MESH_ASSERT(p_timer_evt->cb != NULL);
uint32_t was_masked;
_DISABLE_IRQS(was_masked);
p_timer_evt->p_next = NULL;
add_to_add_list(p_timer_evt);
_ENABLE_IRQS(was_masked);
bearer_event_flag_set(m_event_flag);
}
timer_sch_abort()
終止某個任務
void timer_sch_abort(timer_event_t* p_timer_evt)
{
NRF_MESH_ASSERT(p_timer_evt != NULL);
uint32_t was_masked;
_DISABLE_IRQS(was_masked);
if (p_timer_evt->state == TIMER_EVENT_STATE_IN_CALLBACK)
{
p_timer_evt->state = TIMER_EVENT_STATE_UNUSED;
}
else if (p_timer_evt->state == TIMER_EVENT_STATE_ADDED)
{
p_timer_evt->state = TIMER_EVENT_STATE_IGNORED;
}
else if (p_timer_evt->state != TIMER_EVENT_STATE_UNUSED)
{
if (!is_dirty_state(p_timer_evt->state))
{
m_scheduler.dirty_events++;
}
p_timer_evt->state = TIMER_EVENT_STATE_ABORTED;
bearer_event_flag_set(m_event_flag);
}
_ENABLE_IRQS(was_masked);
}
timer_sch_reschedule()
再次調度
void timer_sch_reschedule(timer_event_t* p_timer_evt, timestamp_t new_timeout)
{
NRF_MESH_ASSERT(p_timer_evt != NULL);
uint32_t was_masked;
_DISABLE_IRQS(was_masked);
/* The events in the added queue will reinsert themselves in the processing. */
if (p_timer_evt->state == TIMER_EVENT_STATE_UNUSED ||
p_timer_evt->state == TIMER_EVENT_STATE_IN_CALLBACK)
{
add_to_add_list(p_timer_evt);
}
else if (p_timer_evt->state == TIMER_EVENT_STATE_ADDED ||
p_timer_evt->state == TIMER_EVENT_STATE_IGNORED)
{
p_timer_evt->state = TIMER_EVENT_STATE_ADDED;
}
else
{
/* Mark the rescheduled event as dirty, will be processed at the next opportunity. */
if (!is_dirty_state(p_timer_evt->state))
{
m_scheduler.dirty_events++;
}
p_timer_evt->state = TIMER_EVENT_STATE_RESCHEDULED;
}
p_timer_evt->timestamp = new_timeout;
bearer_event_flag_set(m_event_flag);
_ENABLE_IRQS(was_masked);
}