原理:軟件定時器的原理其實很簡單,就是在啓動定時器的時候獲取當前系統的時間戳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;
}