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

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

一款可無(wú)限擴(kuò)展的軟件定時(shí)器MultiTimer

strongerHuang ? 來(lái)源:Mculover666 ? 作者:Mculover666 ? 2021-11-16 09:23 ? 次閱讀

1. MultiTimer

今天給大家?guī)?lái)的開源項(xiàng)目是 MultiTimer,一款可無(wú)限擴(kuò)展的軟件定時(shí)器,作者0x1abin,目前收獲 95 個(gè) star,遵循 MIT 開源許可協(xié)議。

MultiTimer 是一個(gè)軟件定時(shí)器擴(kuò)展模塊,可無(wú)限擴(kuò)展你所需的定時(shí)器任務(wù),取代傳統(tǒng)的標(biāo)志位判斷方式, 更優(yōu)雅更便捷地管理程序的時(shí)間觸發(fā)時(shí)序。

項(xiàng)目地址:https://github.com/0x1abin/MultiTimer

2. 移植MultiTimer

2.1. 移植思路

開源項(xiàng)目在移植過程中主要參考項(xiàng)目的readme文檔,一般只需兩步:

  • ① 添加源碼到裸機(jī)工程中;
  • ② 實(shí)現(xiàn)需要的接口;

2.2. 準(zhǔn)備裸機(jī)工程

本文中我使用的是小熊派IoT開發(fā)套件,主控芯片STM32L431RCT6:

移植之前需要準(zhǔn)備一份裸機(jī)工程,我使用STM32CubeMX生成,需要初始化以下配置:

  • 配置一個(gè)串口用于打印信息
  • printf重定向

2.3. 添加MultiTimer到工程中

① 復(fù)制MultiTimer源碼到工程中

② 在keil中添加 MultiTimer的源碼文件

③ 將MultiTimer頭文件路徑添加到keil中

3. 使用MultiTimer

使用時(shí)包含頭文件:

#include "multi_timer.h"

如果遇到multi_timer.c文件中NULL宏定義報(bào)錯(cuò),則在multi_timer.h中添加頭文件即可。

3.1. 創(chuàng)建Timer對(duì)象

/* USER CODE BEGIN PV */struct Timer timer1;struct Timer timer2;
/* USER CODE END PV */

3.2. Timer回調(diào)函數(shù)

/* Private user code ---------------------------------------------------------*//* USER CODE BEGIN 0 */void timer1_callback(){    printf("timer1 timeout!
");}
void timer2_callback(){    printf("timer2 timeout!
");}/* USER CODE END 0 */

3.3. 初始化并啟動(dòng)Timer

始化定時(shí)器對(duì)象,注冊(cè)定時(shí)器回調(diào)處理函數(shù),設(shè)置定時(shí)時(shí)間(ms),循環(huán)定時(shí)觸發(fā)時(shí)間:

/* USER CODE BEGIN 2 */printf("multi timer test...
");
//重復(fù)計(jì)時(shí),周期為1000次,即1000ms=1stimer_init(&timer1, timer1_callback, 1000, 1000);timer_start(&timer1);
//單次計(jì)時(shí),周期為50次,即50mstimer_init(&timer2, timer2_callback, 50, 0);timer_start(&timer2);
/* USER CODE END 2 */

3.4. Timer對(duì)象處理

在循環(huán)中調(diào)用Timer對(duì)象處理函數(shù),處理函數(shù)會(huì)判斷鏈表上的每個(gè)定時(shí)器是否超時(shí),如果超過,則拉起注冊(cè)的回調(diào)函數(shù):

/* Infinite loop *//* USER CODE BEGIN WHILE */while (1){  /* USER CODE END WHILE */
  /* USER CODE BEGIN 3 */  timer_loop();} /* USER CODE END 3 */

3.5. 提供Timer時(shí)基信號(hào)

MultiTimer中所有的定時(shí)器都是通過一個(gè)32位的計(jì)數(shù)值_timer_ticks來(lái)判斷的,所以需要一個(gè)硬件定時(shí)器提供時(shí)基信號(hào),遞增該值。

本文中使用的是STM32HAL庫(kù),所以通過Systick來(lái)提供,無(wú)需設(shè)置額外的定時(shí)器。

main.c文件的最后編寫Systick回調(diào)函數(shù):

/* USER CODE BEGIN 4 */void HAL_SYSTICK_Callback(void){    //給multitimer提供時(shí)基信號(hào)    timer_ticks(); //1ms ticks}
/* USER CODE END 4 */

然后在stm32l4xx_it.c中調(diào)用該回調(diào)函數(shù):

/**  * @brief This function handles System tick timer.  */void SysTick_Handler(void){  /* USER CODE BEGIN SysTick_IRQn 0 */  HAL_SYSTICK_IRQHandler();
  /* USER CODE END SysTick_IRQn 0 */  HAL_IncTick();  /* USER CODE BEGIN SysTick_IRQn 1 */
  /* USER CODE END SysTick_IRQn 1 */}

接下來(lái)編譯下載,看在串口助手中看到打印的日志:b416dbb8-4441-11ec-b939-dac502259ad0.png

4. MultiTimer設(shè)計(jì)思想解讀

4.1. 軟件定時(shí)器設(shè)計(jì)思想

MultiTimer的設(shè)計(jì)比較簡(jiǎn)潔。

設(shè)置一個(gè)計(jì)數(shù)值_timer_ticks不斷遞增,由定時(shí)器提供的中斷驅(qū)動(dòng),只計(jì)次數(shù),不計(jì)時(shí)間,有了很大的自由度,一般時(shí)基信號(hào)設(shè)置為1ms一次:

/**  * @brief  background ticks, timer repeat invoking interval 1ms.  * @param  None.  * @retval None.  */void timer_ticks(){  _timer_ticks++;}

在程序運(yùn)行時(shí)循環(huán)比較定時(shí)器設(shè)置的超時(shí)值是否大于當(dāng)前_timer_ticks的計(jì)數(shù)值,如果是則再次判斷是否重復(fù)計(jì)數(shù)值是否為0,是則停止定時(shí)器,完成單次計(jì)時(shí)效果,否則修改計(jì)數(shù)值,最后拉起注冊(cè)到該定時(shí)器的回調(diào)函數(shù)執(zhí)行:

/**  * @brief  main loop.  * @param  None.  * @retval None  */void timer_loop(){  struct Timer* target;  for(target=head_handle; target; target=target->next) {    if(_timer_ticks >= target->timeout) {      if(target->repeat == 0) {        timer_stop(target);      } else {        target->timeout = _timer_ticks + target->repeat;      }      target->timeout_cb();    }  }}

4.2. 單鏈表操作

MultiTimer的代碼少,非常適合拿來(lái)學(xué)習(xí)單鏈表的操作,學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)的過程是乏味的,不如直接來(lái)個(gè)實(shí)例看看是如何操作的。

① 鏈表的節(jié)點(diǎn)設(shè)計(jì)為一個(gè)軟件定時(shí)器,所以理論上支持的定時(shí)器數(shù)量只受內(nèi)存限制。

typedef struct Timer {    uint32_t timeout;    uint32_t repeat;    void (*timeout_cb)(void);    struct Timer* next;}Timer;

定時(shí)器初始化函數(shù)timer_init就是初始化一個(gè)鏈表節(jié)點(diǎn):

void timer_init(struct Timer* handle, void(*timeout_cb)(), uint32_t timeout, uint32_t repeat){  // memset(handle, sizeof(struct Timer), 0);  handle->timeout_cb = timeout_cb;  handle->timeout = _timer_ticks + timeout;  handle->repeat = repeat;}

② 設(shè)置鏈表頭指針,只需知道頭指針就能完成對(duì)整個(gè)單鏈表的操作:

//timer handle list head.static struct Timer* head_handle = NULL;

③ 向單鏈表增加一個(gè)節(jié)點(diǎn)

向單鏈表增加一個(gè)節(jié)點(diǎn)有三種方式:

  • 在單鏈表尾部增加一個(gè)節(jié)點(diǎn)
  • 在單鏈表頭部增加一個(gè)節(jié)點(diǎn)
  • 在單鏈表中間增加一個(gè)節(jié)點(diǎn)

MultiTimer中所有的結(jié)點(diǎn)都是定時(shí)器,每個(gè)定時(shí)器之間相互獨(dú)立,不存在先后次序關(guān)系,所以無(wú)論加到中間,還是加到尾部,還是加到頭部,最后的功能都是一樣的,但是在插入算法上有優(yōu)劣性能之分。

先來(lái)看看再單鏈表尾部增加一個(gè)節(jié)點(diǎn)的算法:b4a1d22c-4441-11ec-b939-dac502259ad0.gif( 我會(huì)動(dòng)哦 )

int timer_start(struct Timer* handle){  /**    * 算法1 —— 向單鏈表尾部添加節(jié)點(diǎn)   * 時(shí)間復(fù)雜度O(n)   * Mculover666   */  struct Timer* target = head_handle;  if(head_handle == NULL)  {    /* 鏈表為空 */    head_handle = handle;    handle->next = NULL;  }  else  {    /* 鏈表中存在節(jié)點(diǎn),遍歷找最后一個(gè)節(jié)點(diǎn) */    while(target->next != NULL)    {      if(target == handle)        return -1;      target = target->next;    }    target->next = handle;    handle->next = NULL;  }
  return 0;}

這種算法理解簡(jiǎn)單,實(shí)現(xiàn)簡(jiǎn)單,但是算法時(shí)間復(fù)雜度秒變?yōu)镺(n),當(dāng)n很大時(shí),插入一個(gè)節(jié)點(diǎn)的時(shí)間就會(huì)非常久。

再來(lái)看看在鏈表頭部插入一個(gè)新節(jié)點(diǎn)的情況:

(我會(huì)動(dòng)哦)

int timer_start(struct Timer* handle){  /**    * 算法2 —— 向單鏈表頭部添加節(jié)點(diǎn)   * 時(shí)間復(fù)雜度O(n),如果去掉判斷重復(fù),則時(shí)間復(fù)雜度O(1)   * 0x1abin   */   struct Timer *target = head_handle;
   //判斷是否有重復(fù)的定時(shí)器   while(target)   {    if(target == handle)    {      return -1;    }    target = target->next;   }   handle->next = head_handle;   head_handle = handle;   return 0;}

這里第二種頭部插入節(jié)點(diǎn)的算法時(shí)間復(fù)雜度依然是O(n),emmm?

其實(shí),這里因?yàn)閱捂湵砉?jié)點(diǎn)是定時(shí)器,在插入的時(shí)候需要對(duì)整個(gè)鏈表進(jìn)行判斷,避免重復(fù)添加同樣的定時(shí)器節(jié)點(diǎn),所以無(wú)論任何一種算法,都需要對(duì)單鏈表進(jìn)行遍歷。

如果在不需要判斷重復(fù)的情況下,尾部插入算法仍然需要遍歷,但是頭部插入算法只需要插入就可以,時(shí)間復(fù)雜度為O(1),算法更優(yōu)

④ 單鏈表刪除其中一個(gè)節(jié)點(diǎn)

刪除單鏈表的節(jié)點(diǎn)時(shí),因?yàn)楣?jié)點(diǎn)自身只保存有下一個(gè)節(jié)點(diǎn)的指針,并沒有指向上一個(gè)節(jié)點(diǎn)的指針,所以不能直接入手刪除節(jié)點(diǎn),那么如何刪除單鏈表的節(jié)點(diǎn)呢?

方法是:設(shè)置二級(jí)指針(指向Timer類型指針的指針),通過遍歷鏈表的方式來(lái)尋找節(jié)點(diǎn)中next指針指向刪除節(jié)點(diǎn)的那個(gè)節(jié)點(diǎn),代碼如下。

void timer_stop(struct Timer* handle){  struct Timer** curr;  for(curr = &head_handle; *curr; ) {    struct Timer* entry = *curr;    if (entry == handle) {      *curr = entry->next;//      free(entry);    } else      curr = &entry->next;  }}

責(zé)任編輯:haq
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 軟件
    +關(guān)注

    關(guān)注

    69

    文章

    4570

    瀏覽量

    86704
  • 定時(shí)器
    +關(guān)注

    關(guān)注

    23

    文章

    3218

    瀏覽量

    113693

原文標(biāo)題:MultiTimer,一款可無(wú)限擴(kuò)展的軟件定時(shí)器

文章出處:【微信號(hào):strongerHuang,微信公眾號(hào):strongerHuang】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    定時(shí)器的工作方式介紹

    定時(shí)器是計(jì)算機(jī)和嵌入式系統(tǒng)中常見的種硬件模塊,用于實(shí)現(xiàn)定時(shí)和計(jì)數(shù)功能。定時(shí)器的工作方式通常由組寄存
    的頭像 發(fā)表于 07-12 10:29 ?454次閱讀

    鴻蒙開發(fā)系統(tǒng)基礎(chǔ)能力:Timer定時(shí)器

    設(shè)置個(gè)定時(shí)器,該定時(shí)器定時(shí)器到期后執(zhí)行個(gè)函數(shù)。
    的頭像 發(fā)表于 06-28 11:33 ?575次閱讀
    鴻蒙開發(fā)系統(tǒng)基礎(chǔ)能力:Timer<b class='flag-5'>定時(shí)器</b>

    如何實(shí)現(xiàn)個(gè)軟件定時(shí)器?

    在Linux,uC/OS,F(xiàn)reeRTOS等操作系統(tǒng)中,都帶有軟件定時(shí)器,原理大同小異。典型的實(shí)現(xiàn)方法是:通過個(gè)硬件定時(shí)器產(chǎn)生固定的時(shí)鐘節(jié)拍,每次硬件
    的頭像 發(fā)表于 04-29 11:00 ?474次閱讀

    使用555定時(shí)器的可調(diào)雙定時(shí)器電路

    定時(shí)器 IC 555 是最通用和最常用的 IC 之,因?yàn)樗膽?yīng)用范圍更廣,如 PWM放大器、延遲定時(shí)器、開關(guān)電路、占空比選擇、時(shí)鐘脈沖發(fā)生
    的頭像 發(fā)表于 02-25 15:16 ?1640次閱讀
    使用555<b class='flag-5'>定時(shí)器</b>的可調(diào)雙<b class='flag-5'>定時(shí)器</b>電路

    定時(shí)器原理能控制馬達(dá)嗎為什么

    定時(shí)器原理可以用于控制馬達(dá)。馬達(dá)是種將電能轉(zhuǎn)換為機(jī)械能的設(shè)備,通常由電動(dòng)機(jī)和傳動(dòng)裝置組成。定時(shí)器種電子設(shè)備,用來(lái)生成和計(jì)時(shí)精確而穩(wěn)定的時(shí)間信號(hào)。通過將
    的頭像 發(fā)表于 01-23 15:21 ?487次閱讀

    555定時(shí)器可以構(gòu)成哪三種電路 555定時(shí)器屬于時(shí)序邏輯電路嗎

    555定時(shí)器一款廣泛應(yīng)用于各種電子設(shè)備中的集成電路,它能夠創(chuàng)建不同的電路以滿足多種定時(shí)和脈沖生成需求。在本文中,我將詳細(xì)介紹555定時(shí)器能夠構(gòu)成的三種常見電路,并回答其是否屬于時(shí)序邏
    的頭像 發(fā)表于 01-22 10:21 ?2239次閱讀

    555定時(shí)器的基本功能 555定時(shí)器的工作原理及其應(yīng)用

    555定時(shí)器種非常常見和常用的集成電路,它具有廣泛的應(yīng)用領(lǐng)域,例如計(jì)時(shí)、頻率分頻、脈沖寬度調(diào)制等。本文將詳細(xì)介紹555定時(shí)器的基本功能、工作原理以及應(yīng)用。 、555
    的頭像 發(fā)表于 01-18 11:12 ?1.2w次閱讀

    單片機(jī)定時(shí)器的用法

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

    51單片機(jī)定時(shí)器定時(shí)1秒程序流水燈結(jié)果分析

    51單片機(jī)是種廣泛應(yīng)用的嵌入式微控制,具有高性能、低功耗、易擴(kuò)展等優(yōu)勢(shì)。在實(shí)際應(yīng)用中,定時(shí)器常用于定時(shí)操作,如控制程序運(yùn)行時(shí)間、生成精確
    的頭像 發(fā)表于 12-26 14:57 ?5307次閱讀

    定時(shí)器會(huì)阻塞線程嗎 定時(shí)器指令有哪幾種

    定時(shí)器會(huì)阻塞線程嗎 定時(shí)器指令有哪幾種? 定時(shí)器般不會(huì)阻塞線程,但具體是否會(huì)阻塞取決于所使用的定時(shí)器實(shí)現(xiàn)方式和使用方式。
    的頭像 發(fā)表于 12-19 14:03 ?800次閱讀

    定時(shí)器設(shè)計(jì)實(shí)現(xiàn)

    由于目前C++標(biāo)準(zhǔn)中沒有現(xiàn)成的定時(shí)器,本設(shè)計(jì)使用C++11相關(guān)語(yǔ)法并進(jìn)行封裝。 本定時(shí)器包含個(gè)TimerManager類用于創(chuàng)建定時(shí)器和進(jìn)行定時(shí)
    的頭像 發(fā)表于 11-08 16:50 ?505次閱讀

    C51單片機(jī)的定時(shí)器介紹

    定時(shí)器是生活中常見的定時(shí)裝置,常見的定時(shí)器有機(jī)械定時(shí)器和電子定時(shí)器
    的頭像 發(fā)表于 11-02 10:48 ?2083次閱讀
    C51單片機(jī)的<b class='flag-5'>定時(shí)器</b>介紹

    定時(shí)器定時(shí)啟動(dòng)信號(hào)特點(diǎn)

    定時(shí)器定時(shí)啟動(dòng)信號(hào)特點(diǎn)? 定時(shí)器種常用的計(jì)時(shí),可以在指定的時(shí)間后發(fā)出信號(hào)。它們?cè)诟鞣N電器、儀器和控制系統(tǒng)中被廣泛應(yīng)用。
    的頭像 發(fā)表于 10-24 11:49 ?715次閱讀

    STM32速成筆記(6)—定時(shí)器

    關(guān)于什么是定時(shí)器,簡(jiǎn)單來(lái)講,就是是用來(lái)定時(shí)的。STM32F103ZET6有兩個(gè)基本定時(shí)器TIM6和TIM7,四個(gè)通用定時(shí)器TIM2~TIM5和兩個(gè)高級(jí)
    的頭像 發(fā)表于 10-24 09:41 ?3763次閱讀
    STM32速成筆記(6)—<b class='flag-5'>定時(shí)器</b>

    關(guān)于軟件定時(shí)器些討論

    這就是簡(jiǎn)單的軟件定時(shí)器,是的,這就是特別簡(jiǎn)潔版本的軟件定時(shí)器。當(dāng)然它是有缺點(diǎn)的,比如systick_ms每1ms加1,所以軟件
    的頭像 發(fā)表于 10-13 16:14 ?441次閱讀
    關(guān)于<b class='flag-5'>軟件</b><b class='flag-5'>定時(shí)器</b>的<b class='flag-5'>一</b>些討論