1.1 背景
目前市面上的單片機基本都帶有硬件定時器功能,單片機應用程序開發(fā)中也經(jīng)常會用到定時器進行一些和時間相關的開發(fā),比如延時或者周期性地執(zhí)行一些操作。單片機的硬件定時器個數(shù)一般都是固定的,而且一些低端單片機的定時器個數(shù)一般都比較少,在一些有多個周期性操作的應用場合就無法滿足要求。這時,就可以基于硬件定時器派生出軟件定時器,來滿足這種多種周期性或多個單次延時操作的需求。軟件定時器的優(yōu)點就是個數(shù)可以根據(jù)實際需求進行靈活配置,而且可以實現(xiàn)多種不同的定時周期。
1.2 測試平臺
這里使用的開發(fā)環(huán)境和相關硬件如下。
- 操作系統(tǒng):Ubuntu 20.04.2 LTS x86_64(使用uname -a命令查看)
- 集成開發(fā)環(huán)境(IDE):Eclipse IDE for Embedded C/C++ Developers,Version: 2021-06 (4.20.0)
- 硬件開發(fā)板:STM32F429I-DISCO
- 本文對應的例程代碼鏈接如下。
https://download.csdn.net/download/goodrenze/85106391
1.3 軟件定時器實現(xiàn)方法
這里就結合開發(fā)板STM32F429I-DISCO上的STM32F429ZI的單片機來演示軟件定時器的實現(xiàn)方法。
一般定時器的計數(shù)方式有2種:一種是單次定時,即定時時間到了之后,自動停止定時;另一種是周期定時,定時時間到了之后,自動按照之前的定時周期重新定時。對于周期定時,可以手動進行定時器的啟動、關閉和刪除。
下面講解軟件定時器的實現(xiàn)步驟。
1)由于軟件定時器是基于硬件定時器的,所以需要先初始化一個硬件定時器,并啟動硬件定時器。這里使用STM32F429ZI的硬件定時器7,定時器的定時周期為10ms,即每10ms產(chǎn)生一次定時器中斷。初始化代碼如下。
TIM_HandleTypeDef Tim7Handle;
uint8_t InitTim7(uint32_t period_ms)
{
uint16_t uwPrescalerValue;
if(0 == period_ms)
{
return 1;
}
__HAL_RCC_TIM7_CLK_ENABLE();
HAL_NVIC_SetPriority(TIM7_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(TIM7_IRQn);
/* Compute the prescaler value to have TIM7 counter clock equal to 10 KHz */
uwPrescalerValue = (uint32_t) ((SystemCoreClock /2) / 10000) - 1;
/* Set TIM7 instance */
Tim7Handle.Instance = TIM7;
Tim7Handle.Init.Period = period_ms * 10 - 1;
Tim7Handle.Init.Prescaler = uwPrescalerValue;
Tim7Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
Tim7Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
if(HAL_TIM_Base_Init(&Tim7Handle) != HAL_OK)
{
return 1;
}
/* Start the TIM Base generation in interrupt mode */
if(HAL_TIM_Base_Start_IT(&Tim7Handle) != HAL_OK)
{
return 1;
}
return 0;
}
2)硬件定時器定時時間到了之后,會產(chǎn)生中斷,所以需要實現(xiàn)定時器中斷處理函數(shù)。這里基于STM32的HAL進行開發(fā),所以在定時器的中斷入口函數(shù)中直接調用HAL_TIM_IRQHandler()函數(shù),然后實現(xiàn)實際的中斷處理回調函數(shù)HAL_TIM_PeriodElapsedCallback()。對應的代碼如下。其中調用的軟件定時器更新函數(shù)會在后面介紹。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &Tim7Handle)
{
SwTimerUpdateCount();
}
}
3)設計軟件定時器對應的結構體。按照軟件定時器的實際使用特點,必須要包含定時器計數(shù)值和周期定時器的重裝載值,另外還要有定時器時間到之后需要執(zhí)行的回調函數(shù)。對應的軟件定時器結構體如下所示。當結構體中的TimerCount和TimerReload都為0時,說明軟件定時器處于空閑狀態(tài),可以分配使用;如果TimerCount非0,而TimerReload為0,說明軟件定時器是單次定時器;如果TimerCount和TimerReload都非0,說明軟件定時器是周期定時器。
typedef void (*TimerCallbackFunc)(void);
typedef struct _SwTimer_t
{
uint32_t TimerCount;
uint32_t TimerReload;
TimerCallbackFunc TimerCallback;
}SwTimer_t;
4)這里將軟件定時器設置成10個,可以通過宏定義來設置軟件定時器個數(shù)。使用軟件定時器結構體定義一個具有10個定時器的數(shù)組。如下代碼所示。
#define SW_TIMER_NUM 10
SwTimer_t SwTimer[SW_TIMER_NUM];
5)軟件定時器復位函數(shù),用于實現(xiàn)所有軟件定時器的重置操作,重置后,所有軟件定時器都處于空閑狀態(tài),可供分配使用。函數(shù)代碼如下。
void SwTimerReset(void)
{
uint8_t i;
for(i = 0; i < SW_TIMER_NUM; i++)
{
SwTimer[i].TimerCount = 0;
SwTimer[i].TimerReload = 0;
SwTimer[i].TimerCallback = 0;
}
}
6)啟動單次定時器函數(shù),用于實現(xiàn)單次定時,定時時間到了之后,執(zhí)行對應回調函數(shù),并停止定時和釋放定時器資源。函數(shù)代碼如下。如果啟動成功,函數(shù)返回定時器的索引號(該值小于定時器個數(shù)值);啟動失敗,返回的定時器索引號等于定時器的個數(shù)。
uint8_t SwTimerStartSingleTimer(uint32_t single_ms, TimerCallbackFunc TimerCallback)
{
uint8_t i;
single_ms /= MINI_PERIOD_MS;
if(0 == single_ms)
{
single_ms = 1;
}
for(i = 0; i < SW_TIMER_NUM; i++)
{
if((SwTimer[i].TimerCount == 0) && (SwTimer[i].TimerReload == 0))
{
SwTimer[i].TimerCount = single_ms;
SwTimer[i].TimerCallback = TimerCallback;
break;
}
}
return i;
}
7)添加周期定時器函數(shù),用于添加一個新的周期定時器但不啟動定時,需要手動啟動定時器。函數(shù)代碼如下。如果添加成功,函數(shù)返回定時器的索引號(該值小于定時器個數(shù)值);添加失敗,返回的定時器索引號等于定時器的個數(shù)。
uint8_t SwTimerAddPeriodTimer(uint32_t period_ms, TimerCallbackFunc TimerCallback)
{
uint8_t i;
period_ms /= MINI_PERIOD_MS;
if(0 == period_ms)
{
period_ms = 1;
}
for(i = 0; i < SW_TIMER_NUM; i++)
{
if((SwTimer[i].TimerCount == 0) && (SwTimer[i].TimerReload == 0))
{
SwTimer[i].TimerReload = period_ms;
SwTimer[i].TimerCallback = TimerCallback;
break;
}
}
return i;
}
8)啟動周期定時器函數(shù),用于啟動指定定時器索引號的周期定時器開始定時。函數(shù)代碼如下。如果啟動成功,函數(shù)返回定時器的索引號(該值小于定時器個數(shù)值);啟動失敗,返回的定時器索引號等于定時器的個數(shù)。
uint8_t SwTimerStartPeroidTimer(uint8_t timer_no)
{
if(SW_TIMER_NUM <= timer_no)
{
return SW_TIMER_NUM;
}
else if((SwTimer[timer_no].TimerCount == 0) && (SwTimer[timer_no].TimerReload == 0))
{
return SW_TIMER_NUM;
}
else
{
SwTimer[timer_no].TimerCount = SwTimer[timer_no].TimerReload;
return timer_no;
}
}
9)停止定時器函數(shù),用于結束指定定時器索引號的定時器的定時,可用于停止單次或周期定時器。函數(shù)代碼如下。如果停止成功,函數(shù)返回定時器的索引號(該值小于定時器個數(shù)值);停止失敗,返回的定時器索引號等于定時器的個數(shù)。
uint8_t SwTimerStopTimer(uint8_t timer_no)
{
if(SW_TIMER_NUM <= timer_no)
{
return SW_TIMER_NUM;
}
else
{
SwTimer[timer_no].TimerCount = 0;
return timer_no;
}
}
10)刪除周期定時器函數(shù),用于結束指定定時器索引號的周期定時器的定時,并釋放定時器資源。函數(shù)代碼如下。如果刪除成功,函數(shù)返回定時器的索引號(該值小于定時器個數(shù)值);刪除失敗,返回的定時器索引號等于定時器的個數(shù)。
uint8_t SwTimerDeletePeroidTimer(uint8_t timer_no)
{
if(SW_TIMER_NUM <= timer_no)
{
return SW_TIMER_NUM;
}
else
{
SwTimer[timer_no].TimerCount = 0;
SwTimer[timer_no].TimerReload = 0;
return timer_no;
}
}
11)軟件定時器計數(shù)值更新函數(shù),用于更新每個已經(jīng)啟動定時的軟件定時器的計數(shù)值,該函數(shù)必須在硬件定時器的中斷處理函數(shù)中調用。函數(shù)的實現(xiàn)思路是:遍歷所有的軟件定時器,如果遍歷到的定時器的計數(shù)值非0,則進行減1操作。如果減1后計數(shù)值為0,如果定時器的重裝載值非0,說明是周期定時器,需要將計數(shù)值更新成對應的重裝載值以便重新定時,同時執(zhí)行對應的回調函數(shù);如果定時器的重裝載值是0,說明是單次定時器,執(zhí)行完回調函數(shù)后自動停止定時并釋放定時器資源。如果減1后計數(shù)值不為0,繼續(xù)遍歷更新后續(xù)的定時器,直到所有定時器都遍歷完畢。函數(shù)流程圖和對應代碼如下。
圖1 軟件定時器計數(shù)值更新函數(shù)
void SwTimerUpdateCount(void)
{
uint8_t i;
for(i = 0; i < SW_TIMER_NUM; i++)
{
if(SwTimer[i].TimerCount != 0)
{
SwTimer[i].TimerCount -= 1;
if(SwTimer[i].TimerCount == 0)
{
if(SwTimer[i].TimerReload != 0)
{
SwTimer[i].TimerCount = SwTimer[i].TimerReload;
}
if(SwTimer[i].TimerCallback != 0)
{
SwTimer[i].TimerCallback();
}
}
}
}
}
12)軟件定時器的實際使用示例。代碼如下。
int main(void)
{
uint8_t no;
HAL_Init();
/* Configure the system clock to 168 MHz */
SystemClock_Config();
BSP_LED_Init(LED3);
BSP_LED_Init(LED4);
InitTim7(MINI_PERIOD_MS);
SwTimerReset();
no = SwTimerAddPeriodTimer(500, ToggleLed3);
if(no < SW_TIMER_NUM)
{
SwTimerStartPeroidTimer(no);
}
#if 1
no = SwTimerAddPeriodTimer(1000, ToggleLed4);
if(no < SW_TIMER_NUM)
{
SwTimerStartPeroidTimer(no);
}
#else
no = SwTimerStartSingleTimer(5000, ToggleLed4);
#endif
while (1)
{
}
}
-
單片機
+關注
關注
6035文章
44554瀏覽量
634634 -
STM32
+關注
關注
2270文章
10895瀏覽量
355729 -
軟件定時器
+關注
關注
0文章
18瀏覽量
6738 -
定時器
+關注
關注
23文章
3246瀏覽量
114719 -
函數(shù)
+關注
關注
3文章
4327瀏覽量
62569
發(fā)布評論請先 登錄
相關推薦
評論