文章標題雖然是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;
}
}
}
}
以上就是軟件定時器的實現思路,因為有回調函數的存在,可以放心大膽的並行執行其他任務,當定時結束,將會自動回調到相應的函數位置。
下一篇文章,我會大概講述一下,這個軟件定時器的使用示例。