文章标题虽然是STM32系列,其实这个思路适用于所有MCU。
在上一篇文章STM32系列(HAL库)—Delay函数重写中,对Delay延时函数进行了重写,文章中也有提到,延时函数是串行的思维,MCU将会等在那个地方,没办法执行其他任务。因此,本文会提供一种思路来实现并行的定时函数。
首先,要实现定时函数,那么首先可肯定需要定时器,我相信市面上可以看到是MCU,定时器基本上都是标配,以STM32为例,我们选择其中一个定时器作为一个基准定时。
然后,需要设计一个软件定时器,包含定时器名称,定时器周期,定时器计数,然后就是回调函数,如下面代码所示:
typedef uint8_t timer_id_t; //定时器名称
typedef void (*timer_cb_t)(timer_id_t time_name);//定时器回调函数格式
typedef struct
{
timer_id_t timer_name;
uint16_t count;
uint16_t period;
timer_cb_t timer_cb;
}system_timer_t; //软件定时器结构体
#define SYSTEM_TIMER_NUM 32//软件定时器总数,该数目可以增加,会消耗RAM资源
static system_timer_t system_timer[SYSTEM_TIMER_NUM];//定义软件定时器
然后,我这边的思路如下:system_timer中的32个定时器中timer_name都初始化为一个确定一个值,作为一个无效值,当要启动软件定时器时,首先要个某一个软件定时器确定一个timer_name。当STM32的硬件定时器1ms中断出现时,查询一下哪一个软件定时器的timer_name不是无效值,那么它对应的count值会加1,当count值和period值相同时,如果有回调函数时,调用timer_cb的回调函数,如果没有就释放软件定时器资源。
那么先对软件定时器进行初始化:
#define TIMER_INDEX_INVALID SYSTEM_TIMER_NUM //无效的定时器索引值
#define TIMER_ID_INVALID 0xFF//无效的软件定时器Name
void system_timer_init()
{
uint8_t temp;
//将所有软件定时器的Timer_name初始化为无效值
for(temp=0;temp<SYSTEM_TIMER_NUM;temp++)
{
system_timer[temp].timer_name=TIMER_ID_INVALID;
}
system_timer_status.timer_current_num=0;
}
void system_timer_enable()
{
//使能硬件定时器
HAL_TIM_Base_Start_IT(&htim2);
}
void system_timer_disable()
{
//停止硬件定时器
HAL_TIM_Base_Stop_IT(&htim2);
}
最后对软件定时器的操作如下:
//根据定时器名称,反向定位软件定时器数组index
unsigned char get_timer_index(timer_id_t timer_name)
{
unsigned char index;
for(index=0;index<SYSTEM_TIMER_NUM;index++)
{
//查询软件定时器数组是否有相应的定时器名称
if(system_timer[index].timer_name==timer_name)
{
break;
}
}
return index;//返回相应的软件定时器索引
}
//启动软件定时器
//timer_name:定时器名称,period:定时器周期,timer_cb:回调函数
//返回值:status:0x01表示启动成功,0x00表示启动失败,软件定时器已满
unsigned char timer_start(timer_id_t timer_name, uint16_t period, timer_cb_t timer_cb)
{
unsigned char status=0x00;
uint8_t index;
if((timer_name!=TIMER_ID_INVALID)&&(period >0)&&(get_timer_index(timer_name)>=TIMER_INDEX_INVALID))
{
for(index=0;index < SYSTEM_TIMER_NUM;index++)
{
//找到空闲软件定时器资源
if(system_timer[index].timer_name==TIMER_ID_INVALID)
{
system_timer[index].count=0;//清空定时器计数位
system_timer[index].period=period;//记录定时器设定周期
system_timer[index].timer_cb=timer_cb;//记录回调函数位置
system_timer[index].timer_name=timer_name;//记录软件定时器名称,占据软件定时器的一个资源
status=0x01;
break;
}
}
}
return status;
}
//停止软件定时器,释放定时器资源
//timer_name:定时器名称
void timer_stop(timer_id_t timer_name)
{
uint8_t index=get_timer_index(timer_name);//查看是否有该定时器名称的定时器
if(index<TIMER_INDEX_INVALID)
{
system_timer[index].timer_name=TIMER_ID_INVALID;//释放软件定时器资源
system_timer[index].count=0;
system_timer[index].timer_cb=NULL;
}
}
//查看软件定时器是否已经在运行
unsigned char timer_is_running(timer_id_t timer_name)
{
unsigned char status=0x00;
if(get_timer_index(timer_name)!=TIMER_INDEX_INVALID)
{
status=0x01;
}
return status;
}
//硬件定时器中断处理函数
//htim:硬件定时器句柄
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
uint8_t index;
if(htim->Instance==TIM2)//确定是硬件定时器2产生的中断
{
//查询是否有软件定时器在使用
for(index=0;index<SYSTEM_TIMER_NUM;index++)
{
if(system_timer[index].timer_name!=TIMER_ID_INVALID)
{
system_timer[index].count++;
if(system_timer[index].count >=system_timer[index].period)//定时结束
{
//看是否有回调函数,如果有回调函数,那么就执行
if(system_timer[index].timer_cb!=NULL)
{
system_timer[index].timer_cb(system_timer[index].timer_name);//执行回调函数
}
//释放定时器资源
system_timer[index].count=0;
system_timer[index].timer_cb=NULL;
system_timer[index].timer_name=TIMER_ID_INVALID;
}
}
}
}
以上就是软件定时器的实现思路,因为有回调函数的存在,可以放心大胆的并行执行其他任务,当定时结束,将会自动回调到相应的函数位置。
下一篇文章,我会大概讲述一下,这个软件定时器的使用示例。