原理:软件定时器的原理其实很简单,就是在启动定时器的时候获取当前系统的时间戳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;
}