RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

單片機軟件定時器的實現(xiàn)方法

CHANBAEK ? 來源:固件工人 ? 作者:固件工人 ? 2023-01-17 15:14 ? 次閱讀

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)
  {
  }
}
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 單片機
    +關注

    關注

    6035

    文章

    44554

    瀏覽量

    634634
  • STM32
    +關注

    關注

    2270

    文章

    10895

    瀏覽量

    355729
  • 軟件定時器
    +關注

    關注

    0

    文章

    18

    瀏覽量

    6738
  • 定時器
    +關注

    關注

    23

    文章

    3246

    瀏覽量

    114719
  • 函數(shù)
    +關注

    關注

    3

    文章

    4327

    瀏覽量

    62569
收藏 人收藏

    評論

    相關推薦

    單片機定時器的用法

    本章以CW32通用定時器為例介紹單片機定時器的用法。
    的頭像 發(fā)表于 01-04 10:37 ?1390次閱讀
    <b class='flag-5'>單片機</b><b class='flag-5'>定時器</b>的用法

    Winbond 51單片機定時器初值計算(工具軟件)

    Winbond 51單片機定時器初值計算(工具軟件)
    發(fā)表于 06-14 07:53 ?237次下載

    單片機視頻教程06:使用定時器方法

    《手把手教你學單片機單片機視頻教程06:使用定時器方法 單片機視頻教程06:使用定時器
    發(fā)表于 08-21 09:33 ?1.7w次閱讀
    <b class='flag-5'>單片機</b>視頻教程06:使用<b class='flag-5'>定時器</b>的<b class='flag-5'>方法</b>

    51單片機定時器計算軟件

    電子發(fā)燒友網(wǎng)站提供《51單片機定時器計算軟件.zip》資料免費下載
    發(fā)表于 08-10 12:48 ?1次下載

    基于51單片機定時器2的操作與實現(xiàn)

    基于51單片機定時器2的操作與實現(xiàn),51單片機定時器2的使用!
    發(fā)表于 02-22 17:53 ?14次下載

    52單片機有幾個定時器?52單片機定時器1和52單片機定時器2程序對比

    52單片機有幾個定時器?STC89C52RC其實是有三個定時器單片機,STC89C52RC共有3個定時器,分別是T0、T1、T2。而51
    發(fā)表于 11-10 14:30 ?3.5w次閱讀

    如何基于51單片機利用定時器實現(xiàn)PWM的方法詳細概述

    51單片機是可以輸出PWM的,比較的麻煩。此時需要用到內部定時器實現(xiàn),可用兩個定時器實現(xiàn),也可以用一個
    的頭像 發(fā)表于 06-12 20:01 ?3.3w次閱讀

    ESP8266的管腳的控制和軟件定時器的使用

    先說定時器,ESP8266內部的定時器分為軟件定時器和硬件定時器。手冊中指出硬件定時器其實就跟
    的頭像 發(fā)表于 07-29 14:57 ?9499次閱讀
    ESP8266的管腳的控制和<b class='flag-5'>軟件</b><b class='flag-5'>定時器</b>的使用

    使用單片機實現(xiàn)定時器的程序免費下載

      本文檔的主要內容詳細介紹的是使用單片機實現(xiàn)定時器的程序免費下載。
    發(fā)表于 06-05 17:35 ?6次下載
    使用<b class='flag-5'>單片機</b><b class='flag-5'>實現(xiàn)</b><b class='flag-5'>定時器</b>的程序免費下載

    基于單片機定時器的設計方法

    單片機實現(xiàn)一個定時器只要對單片機里的特殊寄存進行設置就可以實現(xiàn)了,下面我與朋友們說說這個0到
    的頭像 發(fā)表于 11-02 16:58 ?1w次閱讀
    基于<b class='flag-5'>單片機</b>的<b class='flag-5'>定時器</b>的設計<b class='flag-5'>方法</b>

    基于51單片機定時器

    設計思路。這樣自己拿到任何型號的51單片機,只要有原理圖,都可以自主設計。博主剛接觸單片機,才疏學淺,可能會出現(xiàn)設計不足和錯誤,歡迎大家評論區(qū)交流。^ _ ^/********************************************************
    發(fā)表于 11-04 21:06 ?35次下載
    基于51<b class='flag-5'>單片機</b>的<b class='flag-5'>定時器</b>

    51單片機——定時器

    51單片機——定時器為什么使用定時器?定時器原理?定時器設置中斷配置源代碼為什么使用定時器?之前
    發(fā)表于 11-11 16:36 ?40次下載
    51<b class='flag-5'>單片機</b>——<b class='flag-5'>定時器</b>

    51單片機定時器中斷

    定時器介紹:51單片機定時器屬于單片機的內部資源,其電路連接和運轉均在單片機的內部完成定時器
    發(fā)表于 11-11 19:36 ?7次下載
    51<b class='flag-5'>單片機</b><b class='flag-5'>定時器</b>中斷

    51單片機定時器/計數(shù)

    CPU的參與。3、51單片機中的 定時器/計數(shù) 是根據(jù) 機器內部的時鐘 或者是 外部的脈沖信號 對寄存中的數(shù)據(jù)加1。4、有了 定時器/計
    發(fā)表于 11-20 20:06 ?47次下載
    51<b class='flag-5'>單片機</b><b class='flag-5'>定時器</b>/計數(shù)<b class='flag-5'>器</b>

    51單片機定時器中斷

    定時器介紹:51單片機定時器屬于單片機的內部資源,其電路連接和運轉均在單片機的內部完成定時器
    發(fā)表于 11-20 20:36 ?15次下載
    51<b class='flag-5'>單片機</b><b class='flag-5'>定時器</b>中斷
    RM新时代网站-首页