超輕量級網(wǎng)紅軟件定時器multi_timer(51+stm32小熊派雙平臺實戰(zhàn))

      網(wǎng)友投稿 1403 2025-04-01

      一、multi_timer簡介


      網(wǎng)紅multi_timer是一個極其輕量的軟件定時器,只要你的MCU容量夠的情況下,就可以無限拓展成為N個定時器,這在一定程度上方便了定期器資源較少的MCU,但有經(jīng)驗的老工程師會說:"我可以只用一個定時器,用計數(shù)器+標(biāo)志位的方式也可以COPY出N個定時器呀,資源少也阻擋不了我對它的充分利用"。是的沒錯,但multi_timer對比老工程師方法的優(yōu)勢在哪里呢?它可以取代傳統(tǒng)的標(biāo)志位+計數(shù)器的判斷方式,讓程序看起來更加優(yōu)雅更加好維護。

      特點:簡單、優(yōu)雅、便捷、易維護

      二、multi_timer的使用方法

      1、定義一個multi_timer結(jié)構(gòu)體變量

      Timer timer1 ;

      2、注冊并初始化multi_timer定時器

      timer_init(&timer1, timer1_callback, TIMER_TIMEOUT_500MS, TIMER_TIMEOUT_500MS);

      3、啟動multi_timer定時器

      timer_start(&timer1);

      4、設(shè)置1ms硬件定時器循環(huán)調(diào)用計數(shù)器以提供時基

      void xxx_callback(void)

      {

      timer_ticks();

      }

      5、在while循環(huán)中循環(huán)調(diào)用multi_timer的后臺處理函數(shù)

      while(1)

      {

      //....

      timer_loop();

      }

      三、multi_timer實戰(zhàn)

      實戰(zhàn)演練1:在STC15F104W-35I-SOP8上實踐

      買這個小模塊的原因是后面需要做一些開源項目,想做一些傳感器,最后用stm32或者其它的MCU與它建立通信用,還有一個用途就是以后移植一些開源項目,我希望現(xiàn)在低端一點的平臺上驗證(如果低端跑不了就直接上能跑的),后面再在高端點的平臺上實踐,有時間有條件我也會多在別的平臺上跑跑,這樣相當(dāng)于積累了多個平臺的開發(fā)經(jīng)驗,這款CPU完全兼容51單片機的指令集,所以把它當(dāng)成51單片機來用就行了。

      這個小板子對應(yīng)的原理圖如下:

      限于文章篇幅,如果想多了解這個小板子的信息,可以去我的CSDN博客上看看,之前寫了介紹:

      https://blog.csdn.net/morixinguan/article/details/105130462

      下面直接看實戰(zhàn)需求功能描述:

      1、用multi_timer創(chuàng)建軟件定時器1,用來以500ms的頻率讓LED燈交替閃爍。

      2、用multi_timer創(chuàng)建軟件定時器2,當(dāng)定時10s到達以后,常亮LED,并且刪除multi_timer創(chuàng)建的軟件定時器1和軟件定時器2。

      創(chuàng)建51的Keil4工程,然后開始編寫代碼:

      1、打開Keil4,然后創(chuàng)建一個AT89C51的工程

      2、將multi_timer添加到keil4工程

      3、創(chuàng)建一個Package目錄,將multi_timer的程序文件添加進來

      4、編寫代碼

      #include

      #include "multi_timer.h"

      Timer timer1 ;

      Timer timer2 ;

      /*用于定時10s的計數(shù)器*/

      int Counter = 0 ;

      /*根據(jù)板子原理圖,燈位于P3^3*/

      sbit LED = P3 ^ 3 ;

      /*晶振頻率為12M*/

      #define FOSC 12000000L

      /*指令速度為12T*/

      #define command_speed 12

      /*用multi_timer創(chuàng)建的定時器1定時時間 ?單位:ms*/

      #define TIMER_TIMEOUT_500MS 500

      /*用multi_timer創(chuàng)建的定時器2定時時間 ?單位:ms*/

      #define TIMER_TIMEOUT_1S 1000

      void timer0_init(void);

      void timer1_callback(void);

      void timer2_callback(void);

      void main(void)

      {

      LED = 0;

      timer0_init();

      timer_init(&timer1, timer1_callback, TIMER_TIMEOUT_500MS, TIMER_TIMEOUT_500MS);

      timer_init(&timer2, timer2_callback, TIMER_TIMEOUT_1S, TIMER_TIMEOUT_1S);

      timer_start(&timer1);

      timer_start(&timer2);

      while(1)

      {

      timer_loop();

      }

      }

      /*multi_timer回調(diào)函數(shù)1調(diào)用*/

      void timer1_callback(void)

      {

      /*LED燈電平翻轉(zhuǎn)*/

      LED = !LED ;

      }

      /*multi_timer回調(diào)函數(shù)2調(diào)用*/

      void timer2_callback(void)

      {

      /*當(dāng)計數(shù)器到達10次以后刪除所有創(chuàng)建的軟件定時器

      計數(shù)器清0,將LED電平置為1,常亮

      */

      ++Counter ;

      if(Counter == 10)

      {

      Counter = 0 ;

      LED = 1 ;

      timer_stop(&timer1);

      timer_stop(&timer2);

      }

      }

      /*硬件定時器初始化*/

      void timer0_init(void)

      {

      TMOD = 0x00;

      TH0 = (65536 - FOSC / command_speed / 1000) >> 8;

      TL0 = (65536 - FOSC / command_speed / 1000);

      EA = 1;

      ET0 = 1;

      TR0 = 1;

      }

      /*利用系統(tǒng)定時器產(chǎn)生1ms的定時中斷*/

      void timer0() interrupt 1

      {

      TH0 = (65536 - FOSC / command_speed / 1000) >> 8;

      TL0 = (65536 - FOSC / command_speed / 1000);

      /*multi_timer計數(shù)器自增*/

      timer_ticks();

      }

      4、程序編譯與固件生成

      我們看到編譯過后,整個程序的大小僅占用1.3K多,確實夠輕量!接下來將生成的.hex文件下載到開發(fā)板上。

      最終程序按照我的設(shè)計思路完美運行!這里相當(dāng)于帶大家重新復(fù)習(xí)了下51單片機平臺的基本使用。

      實戰(zhàn)演練2:在小熊派開發(fā)板上實戰(zhàn)

      接下來我們在這個平臺上把實戰(zhàn)演練1的需求實現(xiàn)一下,首先先看小熊派開發(fā)板的原理圖,找到LED的位置:

      使用stm32cubmx配置基礎(chǔ)工程:

      1、芯片選型,這里選擇stm32l431rctx

      2、配置rcc時鐘以及串行調(diào)試接口

      這里我選擇的是高速,時鐘的話,直接用系統(tǒng)默認的內(nèi)部時鐘也可以,時鐘默認配置最高80MHz。

      因為以前被坑過,導(dǎo)致程序沒法下載了,所以習(xí)慣性配置這個選項,后續(xù)有時間我寫篇文章解釋下。

      3、配置LED

      4、配置串口調(diào)試

      方便根據(jù)調(diào)試信息查看程序執(zhí)行流程。

      5、生成Keil5基礎(chǔ)工程

      實際開發(fā)建議硬件外設(shè)分模塊,這樣看起來不要把所有的生成全部都擠到main.c里面去了,這點讓我非常討厭,所以生成工程時候習(xí)慣點擊設(shè)置以下這一項:

      接下來點擊生成代碼:

      1、將multi_timer添加到keil5工程

      2、創(chuàng)建一個Package目錄,將multi_timer的程序文件添加進來

      3、編寫代碼

      由于篇幅限制,只看我自己代碼添加的位置:

      main.h

      /* Private includes ----------------------------------------------------------*/

      /* USER CODE BEGIN Includes */

      /*添加必要的頭文件*/

      #include

      #include "multi_timer.h"

      /* USER CODE END Includes */

      main.c

      /* Private define ------------------------------------------------------------*/

      /* USER CODE BEGIN PD */

      /*用multi_timer創(chuàng)建的定時器1定時時間 ?單位:ms*/

      #define TIMER_TIMEOUT_500MS 500

      /*用multi_timer創(chuàng)建的定時器2定時時間 ?單位:ms*/

      #define TIMER_TIMEOUT_1S 1000

      /* USER CODE END PD */

      /* USER CODE BEGIN PV */

      Timer timer1 ;

      Timer timer2 ;

      /*用于定時10s的計數(shù)器*/

      int Counter = 0 ;

      /* USER CODE END PV */

      /* USER CODE BEGIN PFP */

      /*定義重定向,這樣才能使用printf函數(shù)*/

      int fputc(int ch, FILE *file)

      {

      return HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);

      }

      /*multi_timer回調(diào)函數(shù)1調(diào)用*/

      void timer1_callback(void)

      {

      /*LED燈電平翻轉(zhuǎn)*/

      //LED = !LED ;

      HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);

      }

      /*multi_timer回調(diào)函數(shù)2調(diào)用*/

      void timer2_callback(void)

      {

      /*當(dāng)計數(shù)器到達10次以后刪除所有創(chuàng)建的軟件定時器

      計數(shù)器清0,將LED電平置為1,常亮

      */

      ++Counter ;

      if(Counter == 10)

      {

      Counter = 0 ;

      //LED = 1 ;

      HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);

      printf("LED燈常亮\n");

      timer_stop(&timer1);

      printf("關(guān)閉定時器1\n");

      timer_stop(&timer2);

      printf("關(guān)閉定時器2\n");

      }

      }

      /**

      * @brief ?The application entry point.

      * @retval int

      */

      int main(void)

      {

      /* USER CODE BEGIN 1 */

      /* USER CODE END 1 */

      /* MCU Configuration--------------------------------------------------------*/

      /* Reset of all peripherals, Initializes the Flash interface and the Systick. */

      HAL_Init();

      /* USER CODE BEGIN Init */

      /* USER CODE END Init */

      /* Configure the system clock */

      SystemClock_Config();

      /* USER CODE BEGIN SysInit */

      /* USER CODE END SysInit */

      /* Initialize all configured peripherals */

      MX_GPIO_Init();

      MX_USART1_UART_Init();

      /* USER CODE BEGIN 2 */

      timer_init(&timer1, timer1_callback, TIMER_TIMEOUT_500MS, TIMER_TIMEOUT_500MS);

      timer_init(&timer2, timer2_callback, TIMER_TIMEOUT_1S, TIMER_TIMEOUT_1S);

      timer_start(&timer1);

      timer_start(&timer2);

      /* USER CODE END 2 */

      /* Infinite loop */

      /* USER CODE BEGIN WHILE */

      while (1)

      {

      /* USER CODE END WHILE */

      /* USER CODE BEGIN 3 */

      timer_loop();

      }

      /* USER CODE END 3 */

      }

      stm32l4xx_it.c

      這里利用系統(tǒng)時鐘的1ms的時基,就不用重新去創(chuàng)建一個硬件定時器了。

      /**

      * @brief This function handles System tick timer.

      */

      void SysTick_Handler(void)

      {

      /* USER CODE BEGIN SysTick_IRQn 0 */

      timer_ticks();

      /* USER CODE END SysTick_IRQn 0 */

      HAL_IncTick();

      /* USER CODE BEGIN SysTick_IRQn 1 */

      /* USER CODE END SysTick_IRQn 1 */

      }

      4、程序編譯與固件生成

      生成固件

      選擇調(diào)試和下載器,這里是st-link

      選擇下載程序后復(fù)位,如果不選擇則需要按開發(fā)板上的硬件復(fù)位。

      編譯成功后直接點擊下載

      5、程序執(zhí)行(看串口調(diào)試助手)

      實驗成功!

      四、multi_timer設(shè)計思想

      所謂有道是知其然而知所以然,這么優(yōu)秀的作品,我們有必要來了解一下:

      4.1 multi_timer的數(shù)據(jù)結(jié)構(gòu)及參數(shù)含義

      typedef struct Timer

      {

      uint32_t timeout;

      uint32_t repeat;

      void (*timeout_cb)(void);

      struct Timer* next;

      } Timer;

      參數(shù)含義:

      4.2 multi_timer的函數(shù)解析

      程序文件里的全局變量:

      //timer handle list head.

      static struct Timer* head_handle = NULL;

      //Timer ticks

      static uint32_t _timer_ticks = 0;

      /**

      * @brief ?background ticks, timer repeat invoking interval 1ms.

      * @param ?None.

      * @retval None.

      */

      void timer_ticks()

      {

      _timer_ticks++;

      }

      這個函數(shù)的功能主要是產(chǎn)生計數(shù),而產(chǎn)生計數(shù)一定要有另外一個介質(zhì)去驅(qū)動它運行。

      //Timer ticks

      static uint32_t _timer_ticks = 0;

      /**

      * @brief ?Initializes the timer struct handle.

      * @param ?handle: the timer handle strcut.

      * @param ?timeout_cb: timeout callback.

      * @param ?repeat: repeat interval time.

      * @retval None

      */

      void timer_init(struct Timer* handle, void(*timeout_cb)(), uint32_t timeout, uint32_t repeat)

      {

      超輕量級網(wǎng)紅軟件定時器multi_timer(51+stm32小熊派雙平臺實戰(zhàn))

      handle->timeout_cb = timeout_cb;

      handle->timeout = _timer_ticks + timeout;

      handle->repeat = repeat;

      }

      初始化主要是給結(jié)構(gòu)體參數(shù)進行賦值操作,首先要確定是哪個結(jié)構(gòu)體成員,由handle參數(shù)確定,當(dāng)定時時間到了要做什么事情,由timeout_cb(定時器回調(diào)處理函數(shù))參數(shù)確定,定時時間多長才會觸發(fā)所謂的事情,由timeout(定時時間)參數(shù)確定,如果這個功能需要重復(fù)觸發(fā),我們就需要給repeat(循環(huán)定時觸發(fā)時間)參數(shù)()指定。

      /**

      * @brief ?Start the timer work, add the handle into work list.

      * @param ?btn: target handle strcut.

      * @retval 0: succeed. -1: already exist.

      */

      int timer_start(struct Timer* handle)

      {

      struct Timer* target = head_handle;

      while(target)

      {

      if(target == handle) return -1; //already exist.

      target = target->next;

      }

      handle->next = head_handle;

      head_handle = handle;

      return 0;

      }

      這里將定時器句柄添加到鏈表里進行保存,循環(huán)指向鏈表的下一個節(jié)點去添加定時器節(jié)點,如果發(fā)現(xiàn)是同一個定時器句柄,則直接返回-1,表示當(dāng)前添加句柄不合法。

      /**

      * @brief ?Stop the timer work, remove the handle off work list.

      * @param ?handle: target handle strcut.

      * @retval None

      */

      void timer_stop(struct Timer* handle)

      {

      struct Timer** curr;

      for(curr = &head_handle; *curr; )

      {

      struct Timer* entry = *curr;

      if (entry == handle)

      {

      *curr = entry->next;

      }

      else

      curr = &entry->next;

      }

      }

      這里非常巧妙的使用了一個二級指針curr,指向了對應(yīng)定時器句柄的地址,通過循環(huán)遍歷,找到對應(yīng)的句柄后將其刪除。

      /**

      * @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();

      }

      }

      }

      這個函數(shù)實現(xiàn)非常簡單,就是通過不斷遍歷鏈表各個節(jié)點,判斷是否到達定時時間(timeout參數(shù)),如果到達了定時時間,沒有指定循環(huán)定時觸發(fā)時間(repeat參數(shù))的時候,這時就會把當(dāng)前定時器句柄給移除,如果指定了循環(huán)定時觸發(fā)時間(repeat參數(shù)),則定時時間會被重新賦值,直到下一個定時到來,接下來會一直循環(huán)觸發(fā)。

      實踐工程下載

      鏈接:https://pan.baidu.com/s/1xwCnkMDnwjPTrKd8ulw58w

      提取碼:eo5y

      復(fù)制這段內(nèi)容后打開百度網(wǎng)盤手機App,操作更方便哦

      注意:

      大部分朋友的電腦都同時裝了Keil5和Keil4,不小心操作有時打開Keil4工程會卡死,解決方法如下:

      將下面這個文件刪除,再重新打開就不會了。

      軟件開發(fā) 小熊派 IoT

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:excel怎么并排查看兩個工作表?
      下一篇:Office2016新功能預(yù)覽(office2016預(yù)覽版)
      相關(guān)文章
      激情内射亚洲一区二区三区| 亚洲五月六月丁香激情| 亚洲最大在线观看| 亚洲韩国—中文字幕| 亚洲色四在线视频观看| 亚洲大尺度无码无码专区| 亚洲VA中文字幕无码毛片| 亚洲午夜国产精品无码老牛影视| 国产亚洲精品久久久久秋霞| 久久精品亚洲福利| 国产亚洲一区二区三区在线| 久久久久久久综合日本亚洲| 亚洲va无码va在线va天堂| 亚洲A∨无码无在线观看| 亚洲AV无码一区二区三区DV| 久久精品国产亚洲AV麻豆王友容| 亚洲国产成人私人影院| 日韩精品亚洲人成在线观看| 亚洲第一香蕉视频| 亚洲伦理中文字幕| 亚洲日韩一区二区一无码| 久久久亚洲精华液精华液精华液| 亚洲va中文字幕无码| 亚洲午夜无码AV毛片久久| 亚洲中文久久精品无码ww16| 亚洲成在人线av| 亚洲一区二区影院| 亚洲二区在线视频| 亚洲人片在线观看天堂无码| 国产精品无码亚洲一区二区三区| 亚洲国产V高清在线观看| 国产亚洲精久久久久久无码AV| 日韩精品亚洲aⅴ在线影院| 亚洲国产精品一区二区第一页| 亚洲AV无码久久精品蜜桃| 777亚洲精品乱码久久久久久| 亚洲一卡2卡3卡4卡国产网站| 亚洲中文字幕久久精品蜜桃| 国产99久久亚洲综合精品| 国产gv天堂亚洲国产gv刚刚碰| 亚洲爆乳无码一区二区三区|