用連結串列+函式指標+定時器中斷實現的一個軟體定時器(試用於所有微控制器)
阿新 • • 發佈:2019-01-26
因為需要移植nrf51822的程式到普通微控制器上,於是分析了協議棧裡的軟體定時器,用連結串列+函式指標+定時器中斷的方法實現了軟體定時器的功能。
下面介紹程式碼和使用方法
1、函式指標和連結串列初始化
2、為連結串列申請空間
3、建立定時器 引數 定時器名字 定時器id 定時器工作時長 定時器回撥函式
4、將節點插入連結串列的最後 引數: 連結串列頭指標 節點指標 返回:指向連結串列表頭的指標
5、遍歷連結串列 計算是否有定時器超時 將這個函式放入到定時器中斷中,每進入一次定時器中斷(軟體定時器),將設定的軟體定時器值-1,減為0之後就呼叫一開始建立定時器時註冊的函式。
6、修改定時器工作狀態 可以讓指定id的定時器啟動或停止 引數 定時器id 定時器使能
7、使用方法 1)通過巨集定義來設定id和定時時間 //定時器id設定 #define MAIN_TIMER_ID 1 //定時器初值設定 #define MAIN_TIMER_INTERVAL 5000 2)通過巨集預申請連結串列空間 APP_TIMER_DEF(main_timer); 3)建立定時器 app_timer_create(&main_timer,MAIN_TIMER_ID,MAIN_TIMER_INTERVAL,main_task_timeout_handler); 4)使能定時器 app_timer_isrunning(MAIN_TIMER_ID,TRUE); 5)初始化硬體定時器,將TraverseList()函式放入硬體定時器中斷中
typedef void (*app_timer_timeout_handler_t)(void); typedef struct app_timer { u8 id; u16 time; u16 interval_time; app_timer_timeout_handler_t p_timeout_handler; struct app_timer *next; //指向下一節點的指標 bool isrunning ; }app_timer;
2、為連結串列申請空間
typedef struct app_timer_t { uint32_t data[sizeof(uint32_t) ]; } app_timer_t; /**@brief Timer ID type. * Never declare a variable of this type, but use the macro @ref APP_TIMER_DEF instead.*/ typedef app_timer_t * app_timer_name_t; /** * @brief Create a timer identifier and statically allocate memory for the timer. * * @param timer_name Name of the timer identifier variable that will be used to control the timer. */ #define APP_TIMER_DEF(timer_name) \ static app_timer_t timer_name##_data = { {0} }; \ static app_timer_name_t timer_name = &timer_name##_data
3、建立定時器 引數 定時器名字 定時器id 定時器工作時長 定時器回撥函式
u32 app_timer_create(app_timer_name_t const * p_timer_name, u8 id, u16 time, app_timer_timeout_handler_t timeout_handler) { app_timer * p_node = (app_timer *)*p_timer_name; p_node->id = id; p_node->time = time; p_node->interval_time = time; //記錄下定時器的觸發時間 p_node->p_timeout_handler = timeout_handler ; p_node->isrunning = FALSE; p_head = Insert(p_head,p_node); }
4、將節點插入連結串列的最後 引數: 連結串列頭指標 節點指標 返回:指向連結串列表頭的指標
struct app_timer *Insert (struct app_timer *head,struct app_timer *node)
{
struct app_timer *p1; //p1儲存當前需要檢查的節點的地址
//當頭節點為空時,將傳入的節點作為頭節點,返回頭節點
if (head == NULL)
{
head = node;
node->next = NULL;
return head;
}
p1 = head;
while(p1->next != NULL)
{
p1 = p1->next; //後移一個節點
}
if(p1->next == NULL) //將該節點插入連結串列的末尾
{
p1->next = node;
node->next = NULL;
}
else
{
}
return head;
}
5、遍歷連結串列 計算是否有定時器超時 將這個函式放入到定時器中斷中,每進入一次定時器中斷(軟體定時器),將設定的軟體定時器值-1,減為0之後就呼叫一開始建立定時器時註冊的函式。
void TraverseList(void)
{
struct app_timer *p1 = p_head;
while( p1!= NULL) //下一個節點如果不為空
{
if(p1->isrunning == TRUE)
{
p1->time--;
}
else
{
p1->time = p1->interval_time;
}
if(p1->time == 0 )
{
p1->time = p1->interval_time;
p1->p_timeout_handler();
}
if(p1->next != NULL)
{
p1 = p1->next;
}
else
{
return ;
}
}
}
6、修改定時器工作狀態 可以讓指定id的定時器啟動或停止 引數 定時器id 定時器使能
void app_timer_isrunning(int id, bool isrunning)
{
struct app_timer *p1 = p_head;
while((p1->id != id) && (p1->next != NULL))
{
p1 = p1->next; //後移一個節點
}
if (p1->id==id) //找到了
{
p1->isrunning = isrunning;
}
else
{
return ;
}
}
7、使用方法 1)通過巨集定義來設定id和定時時間 //定時器id設定 #define MAIN_TIMER_ID 1 //定時器初值設定 #define MAIN_TIMER_INTERVAL 5000 2)通過巨集預申請連結串列空間 APP_TIMER_DEF(main_timer); 3)建立定時器 app_timer_create(&main_timer,MAIN_TIMER_ID,MAIN_TIMER_INTERVAL,main_task_timeout_handler); 4)使能定時器 app_timer_isrunning(MAIN_TIMER_ID,TRUE); 5)初始化硬體定時器,將TraverseList()函式放入硬體定時器中斷中