軟件定時器實現源碼

原理:軟件定時器的原理其實很簡單,就是在啓動定時器的時候獲取當前系統的時間戳start_ts,然後用start_ts加上要定時的時間dly_ts(timeout= start_ts + dly_ts),在心跳定時器中斷中查詢當前時間計數值(時間戳),如果查詢到的計數值大於timeout,說明定時時間到,並調用回調函數執行操作。

資源:1個硬件定時器,用作心跳時鐘

下面我們來實現一個簡單的基於裸機(不帶操作系統)的軟件定時器設施,共實現了6個基本接口,設計中沒有使用時間輪盤的算法,軟件用指針數組代替了需要動態分配內存的列表,從而簡化了定時器的實現,定時器的數量可通過調整數組列表的長度來實現,默認最大支持5個軟定時器。啓動定時器時把定時器指針加入到數組,停止定時器時把定時器對應的指針設定爲NULL。


一、timer.h

/**
  ******************************************************************************
  *  File Name              : timer.h
  *  Describe               :
  *  Created                : 09/10/2016
  ******************************************************************************
  */

#ifndef _I_TIMER_H
#define _I_TIMER_H

#include "stddef.h"
#include "stdint.h"

typedef void   (*tmr_fnct_ptr) (void);
typedef uint8_t TMR_STATE;



typedef struct{
	uint32_t		expire;					/* 定時器到期時間             */
    uint32_t        period;
	tmr_fnct_ptr	callback;				/* 回調函數					  */
	TMR_STATE		state;					/* 定時器狀態				  */
	uint32_t		opt;					/* 操作類型        			  */
}timer_t;


#define   TIMER_LIST_MAX		5U		/* 計時器列表大小,支持5個軟件定時器 */
 

/* timer state */
#define  TMR_STATE_UNUSED                    (TMR_STATE)(0u)
#define  TMR_STATE_STOPPED                   (TMR_STATE)(1u)
#define  TMR_STATE_RUNNING                   (TMR_STATE)(2u)
#define  TMR_STATE_COMPLETED                 (TMR_STATE)(3u)

#define  OPT_TMR_NONE                        (0u)  /* No option selected                                 */

#define  OPT_TMR_ONE_SHOT                    (1u)  /* Timer will not auto restart when it expires        */
#define  OPT_TMR_PERIODIC                    (2u)  /* Timer will     auto restart when it expires        */






void timer_init(void);

uint8_t timer_create(timer_t   		*ptimer,	/* point to timer_t */
					 uint32_t  		period,		/* period			*/
					 uint32_t  		opt,
					 tmr_fnct_ptr	pcallback);

uint8_t timer_del(timer_t *ptimer);
uint8_t timer_start(timer_t *ptimer);
uint8_t timer_cancel(timer_t *ptimer);

void timer_task(void);



#endif

二、timer.c

/**
  ******************************************************************************
  *  File Name              :  timer.c
  *  Describe               :
  *  Created                :
  ******************************************************************************
  */



#include "timer.h"



/* timer list(arry) */
static timer_t		*timer_list[TIMER_LIST_MAX] = {NULL};
static uint8_t		timer_count = 0; /* 已經使用的軟件計時器數量,取值範圍0...TIMER_LIST_MAX */
static uint32_t     timer_tick;      /* 軟件計時器時基,表示1個timer_tick代表多少時間,這裏爲10ms */


/* privat prototype **************************************/
static void timer_cleanup(timer_t *ptimer);
static uint8_t timer_link(timer_t *ptimer);
static uint8_t timer_unlink(timer_t *ptimer);





/**************** public function implement ***********************************/
/*
 * 初始化軟件計時器,
 */
void timer_init(void)
{
    for(uint8_t i=0;iexpire)
        {
            if(ptmr->callback != NULL){
                ptmr->callback();
            }
            
            if(ptmr->opt == OPT_TMR_PERIODIC){
                //timer_link(timer_list[i]);
				ptmr->expire = timer_tick + ptmr->period;
            }else{
                ptmr->state = TMR_STATE_COMPLETED;
                timer_unlink(timer_list[i]);
            }
        }
    }
}


/* initalize timer,1:OK ; 0: fail   
 * 創建1個計時器,該操作分配一個軟件計時器結構,任何企圖安裝軟件計時器的
 * 的軟件模塊必須首先創建一個計時器結構。計時器結構包含允許更改計時器處理
 * 設施和超過軟件計時器的控制信息。此操作創立的計時器將引用到軟件計時器列
 * 表的一個入口
 */
uint8_t timer_create(timer_t   		*ptimer,	/* point to timer_t */
					 uint32_t  		period,		/* period			*/
					 uint32_t  		opt,
					 tmr_fnct_ptr	pcallback)
{
	if(!ptimer)
		return  0;
	
	ptimer->expire   = 0;
    ptimer->period   = period;
	ptimer->opt      = opt;
	ptimer->callback = pcallback;
	ptimer->state    = TMR_STATE_STOPPED;
	
	return 1;
}
					 
/* 
 * 刪除一個計時器。此操作刪除先前創立的軟件計時器,釋放計時器結構佔據的內存。
 * 1:成功;0:失敗
 */
uint8_t timer_del(timer_t *ptimer)
{
	if(!ptimer)
		return 0;
	timer_cleanup(ptimer);
	ptimer = NULL;
	
	return 1;
}


/*
 * 啓動一個計時器。該操作安裝一個先前創立的軟件計時器到計時器處理設施中
 * 計時器在此操作完成後開始運行
 */
uint8_t timer_start(timer_t *ptimer)
{
    if(!ptimer)
        return 0;
    
    switch(ptimer->state)
    {
    case TMR_STATE_RUNNING:
        timer_unlink(ptimer);
        timer_link(ptimer);
        return 1;
    case TMR_STATE_STOPPED:
    case TMR_STATE_COMPLETED:
        timer_link(ptimer);
        return 1;
    case TMR_STATE_UNUSED:
        return 0;
    default:
        return 0;
    }
}


/* 取消當前運行的計時。此操作通過從計時器處理設施中刪除當前運行的計時器,
 * 取消一個計時器,把timer_id指向的列表項設置爲NULL
 */
uint8_t timer_cancel(timer_t *ptimer)
{
    if(!ptimer)
        return 0;
    
   return timer_unlink(ptimer);
}




/* static function implement **************************************/
static void timer_cleanup(timer_t *ptimer)
{
	if(!ptimer)
		return;
	ptimer->state    = TMR_STATE_UNUSED;
	ptimer->callback = NULL;
	ptimer->expire   = 0;
	ptimer->opt      = 0;
}


static uint8_t timer_link(timer_t *ptimer)
{
	uint8_t i;
	
	if(!ptimer)
		return 0;
	
	if(timer_count >= TIMER_LIST_MAX)
		return 0;
	
	for(i=0;istate  = TMR_STATE_RUNNING;
        ptimer->expire = ptimer->period + timer_tick;
        timer_list[i]  = ptimer;
        timer_count++;
        return 1;
	}
    
    return 0;
}


static uint8_t timer_unlink(timer_t *ptimer)
{
    uint8_t i;
    
    if(!ptimer)
        return 0;
    
    for(i=0;istate = TMR_STATE_STOPPED;
        timer_count--;
        return 1;
    }
    return 0;
}



三、hal_tick.h

/**
  ******************************************************************************
  * File Name          : hal_tick.h
  * Description        : 
  ******************************************************************************
  */


#ifndef  HAL_TICK_H
#define  HAL_TICK_H



#include "stm32f1xx.h"

   
#define TIM_MST             TIM4
#define TIM_MST_IRQ         TIM4_IRQn
#define TIM_MST_RCC         __TIM4_CLK_ENABLE()

#define TIM_MST_RESET_ON    __TIM4_FORCE_RESET()
#define TIM_MST_RESET_OFF   __TIM4_RELEASE_RESET()

#define HAL_TICK_DELAY      1000u // 1 ms
#define HAL_HZ				1000000U





void TIM4_IRQHandler(void);
void hal_tick_init( void);

uint32_t us_ticker_read(void);

#endif

四、hal_tick.c

/**
  ******************************************************************************
  * File Name          : hal_tick.c
  * Description        : 
  ******************************************************************************
  */

#include "hal_tick.h"
#include "timer.h"


#define  TIMER_TICK_RATE                      10000U  /* 10ms */

TIM_HandleTypeDef   htim4;
volatile uint32_t   hal_jiffies;
uint8_t             tick_inited = 0;


void TIM4_IRQHandler(void)
{
    // Clear Update interrupt flag
    if (__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE) == SET) {
        __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);
        hal_jiffies++;
        timer_task();               /* soft timer task */
    }    
}


void hal_tick_init( void)
{
    // Enable timer clock
    TIM_MST_RCC;

    // Reset timer
    TIM_MST_RESET_ON;
    TIM_MST_RESET_OFF;

    // Update the SystemCoreClock variable
    SystemCoreClockUpdate();

    htim4.Instance = TIM_MST;
    
    htim4.Init.Period        = TIMER_TICK_RATE;        /* timer tick period 10ms */
    htim4.Init.Prescaler     = (uint32_t)(SystemCoreClock / 1000000) - 1; // 1 us tick
    htim4.Init.ClockDivision = 0;
    htim4.Init.CounterMode   = TIM_COUNTERMODE_UP;
    HAL_TIM_Base_Init(&htim4);

    HAL_NVIC_SetPriority(TIM_MST_IRQ,15,0);
    NVIC_EnableIRQ(TIM_MST_IRQ);

    // Enable interrupts
    __HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE); // For 32-bit counter
    
    // Enable timer
    HAL_TIM_Base_Start(&htim4);
    
    tick_inited =1;
}


uint32_t us_ticker_read(void)
{
    uint32_t counter, counter2;
    
    if(!tick_inited)
        hal_tick_init();
    
    counter = (uint32_t)(hal_jiffies * TIMER_TICK_RATE);
    counter += TIM_MST->CNT;
    while (1) {
        counter2 = (uint32_t)(hal_jiffies * TIMER_TICK_RATE);
        counter2 += TIM_MST->CNT;
        if (counter2 > counter) {
            break;
        }
        counter = counter2;
    }
    return counter2;    
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章