背景
在不运行实时操作系统的单片机中,面对一些复杂的项目需求时,仅有的几个硬件定时器显得有些捉襟见肘,这主要体现在两个方面:其一是硬件定时器数量十分有限(一般4~8个);其二是硬件定时器难以直接产生周期很长(秒级)的延时。针对这两个缺陷,笔者编写了一个使用的软件定时器库,通过占用了一个硬件定时器虚拟出大量的软件定时器以供项目工程调用。无数的项目实践证明,本软件定时器库是方便、试用而又可靠的。
软件定时器库特性
软件定时器库主要针对弥补硬件定时器数量十分有限、难以直接产生长周期的延时这两个缺陷而设计的。通过封装软件定时器结构体类型,通过声明和初始化一个软件定时器结构体数组就能自由的增减软件定时器数量。通过将一个硬件定时器的更新周期作为时基,可以轻松实现长时间的延时或者长周期的轮询。通过修改软件定时器结构体成员的值,可以十分方便的实现设置循环次数、循环周期、注册处理函数等功能。同时,通过监控软件定时器结构体成员的值,也能实时了解软件定时器的工作状态与所处阶段。
源码介绍
softwaretimer.c
#include "softwaretimer.h"
SoftwareTimerStruct SoftwareTimerList[SOFTWARE_TIMER_NUM] =
{
{ 0, 0, 0, NULL, 0, 0 },
{ 1, 0, 0, NULL, 0, 0 },
{ 2, 0, 0, NULL, 0, 0 },
{ 3, 0, 0, NULL, 0, 0 },
{ 4, 0, 0, NULL, 0, 0 },
{ 5, 0, 0, NULL, 0, 0 },
{ 6, 0, 0, NULL, 0, 0 },
{ 7, 0, 0, NULL, 0, 0 },
{ 8, 0, 0, NULL, 0, 0 },
{ 9, 0, 0, NULL, 0, 0 },
{ 10, 0, 0, NULL, 0, 0 },
{ 11, 0, 0, NULL, 0, 0 },
{ 12, 0, 0, NULL, 0, 0 },
{ 13, 0, 0, NULL, 0, 0 },
{ 14, 0, 0, NULL, 0, 0 },
};
/**
* @brief Generally placed in the interrupt handler of the hardware timer
* and called periodically.
* @param none
* @retval none
*/
void SoftwareTimer_Tick(void)
{
for(uint8_t i=0;i<SOFTWARE_TIMER_NUM ;i++)
{
if(SoftwareTimerList[i].isEnable == 0 || SoftwareTimerList[i].expire == 0)
{
continue;
}
SoftwareTimerList[i].count++;
if(SoftwareTimerList[i].count >= SoftwareTimerList[i].expire)
{
SoftwareTimerList[i].count = 0;
if(SoftwareTimerList[i].repeat > 0)
{
SoftwareTimerList[i].repeat--;
if(SoftwareTimerList[i].callback != NULL)
SoftwareTimerList[i].callback();
}
else if(SoftwareTimerList[i].repeat == 0)
{
SoftwareTimerList[i].isEnable = 0;
// memset(&SoftwareTimerList[i], 0, sizeof(SoftwareTimerList[i]));
}
else
{
if(SoftwareTimerList[i].callback != NULL)
SoftwareTimerList[i].callback();
}
}
}
}
/**
* @brief Initialization of software timer.
* @param id: ID of the timer, which need to be initialized.
* @param expire: The period of time to execute callback function (trigger timeout event).
* @param callback: The callback function of timer. When the timer is expired, this function will be called.
* @param repeat: Repeat times of the timer.
* @arg -1: repeat forever
* @arg 0: invalid
* @arg more than 0: repeat times
* @retval none
*/
void SoftwareTimer_Init(uint8_t id,uint32_t expire,timeout_handler callback,int16_t repeat)
{
SoftwareTimerList[id].id = id;
SoftwareTimerList[id].expire = expire;
SoftwareTimerList[id].callback = callback;
SoftwareTimerList[id].repeat = repeat;
SoftwareTimerList[id].count = 0;
SoftwareTimerList[id].isEnable = 0;
}
/**
* @brief Enable the timer, which specified by ID.
* @param id: Id of the timer.
* @retval none
*/
void SoftwareTimer_Enable(uint8_t id)
{
SoftwareTimerList[id].isEnable = 1;
}
/**
* @brief Disable the timer, which specified by ID.
* @param id: Id of the timer.
* @retval none
*/
void SoftwareTimer_Disable(uint8_t id)
{
SoftwareTimerList[id].isEnable = 0;
}
softwaretimer.h
#ifndef __SOFTWARETIMER_H__
#define __SOFTWARETIMER_H__
#include "stm32f10x.h"
#define SOFTWARE_TIMER_NUM 15
typedef void (*timeout_handler)();
typedef struct
{
uint8_t id;
uint32_t count;
uint32_t expire;
timeout_handler callback;
uint8_t isEnable; //0,disable 1,enable
int16_t repeat; //-1,forever
} SoftwareTimerStruct;
extern SoftwareTimerStruct SoftwareTimerList[SOFTWARE_TIMER_NUM];
void SoftwareTimer_Tick(void);
void SoftwareTimer_Init(uint8_t id,uint32_t expire,timeout_handler callback,int16_t repeat);
void SoftwareTimer_Enable(uint8_t id);
void SoftwareTimer_Disable(uint8_t id);
#endif
使用指南
- 定义软件定时器结构体数组
由于通过C语言封装了软件定时器,因此在使用前首先需要定义软件定时器结构体数组。以上一小节提供的源码为例说明:
在源文件中定义结构体数组:
SoftwareTimerStruct SoftwareTimerList[SOFTWARE_TIMER_NUM] =
{
{ 0, 0, 0, NULL, 0, 0 },
{ 1, 0, 0, NULL, 0, 0 },
{ 2, 0, 0, NULL, 0, 0 },
{ 3, 0, 0, NULL, 0, 0 },
{ 4, 0, 0, NULL, 0, 0 },
{ 5, 0, 0, NULL, 0, 0 },
{ 6, 0, 0, NULL, 0, 0 },
{ 7, 0, 0, NULL, 0, 0 },
{ 8, 0, 0, NULL, 0, 0 },
{ 9, 0, 0, NULL, 0, 0 },
{ 10, 0, 0, NULL, 0, 0 },
{ 11, 0, 0, NULL, 0, 0 },
{ 12, 0, 0, NULL, 0, 0 },
{ 13, 0, 0, NULL, 0, 0 },
{ 14, 0, 0, NULL, 0, 0 },
};
在头文件中声明外部变量,以便其他地方可以调用定时器:
extern SoftwareTimerStruct SoftwareTimerList[SOFTWARE_TIMER_NUM];
- 在程序的合适地方初始化定时器,并为它们注册回调函数
/**
* @brief 初始化定时器
*/
void User_SetupTimer()
{
//Timer0 is used for system led flash.
SoftwareTimer_Init(0, 1, User_SoftwareTimer0_Handler, -1);
//Timer1 is used for ...
SoftwareTimer_Init(1, 60, User_SoftwareTimer1_Handler, -1);
}
/**
* @brief Timer1 回调函数
*/
void User_SoftwareTimer0_Handler()
{
/*项目相关代码*/
}
/**
* @brief Timer2 回调函数
*/
void User_SoftwareTimer1_Handler()
{
/*项目相关代码*/
}
- 设置时基服务函数
一般在单片机的硬件定时器中断服务函数中调用软件定时器的时基服务函数void SoftwareTimer_Tick(void)
,调用周期即为软件定时器的时基
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET)
{
SoftwareTimer_Tick();
TIM_ClearFlag(TIM3,TIM_FLAG_Update);
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}
}
- 在程序的合适地方启动定时器
SoftwareTimer_Enable(0);
SoftwareTimer_Enable(1);
- 停止使用时手动关闭定时器
SoftwareTimer_Disable(0);
SoftwareTimer_Disable(1);