1. 程式人生 > >用連結串列+函式指標+定時器中斷實現的一個軟體定時器(試用於所有微控制器)

用連結串列+函式指標+定時器中斷實現的一個軟體定時器(試用於所有微控制器)

因為需要移植nrf51822的程式到普通微控制器上,於是分析了協議棧裡的軟體定時器,用連結串列+函式指標+定時器中斷的方法實現了軟體定時器的功能。 下面介紹程式碼和使用方法 1、函式指標和連結串列初始化
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()函式放入硬體定時器中斷中