SmartTimer——一種基於STM32的輕量級時鐘調度器

来源:http://www.cnblogs.com/elecsun/archive/2016/08/03/5733355.html
-Advertisement-
Play Games

一直以來,都渴望為開源世界貢獻自己的作品,但總是心有顧慮。首先是之前一直忙碌於公司的項目,沒有時間做自己的東西,公司利益為先,確實也沒法開源;二是總覺得自己的代碼醜陋,不夠優雅、簡潔。怕貼出來後貽笑大方。 最近一段時間沒有那麼忙了,於是有時間把以前的積累沉澱一下。對之前做過的項目會有更多的反思。比如 ...


一直以來,都渴望為開源世界貢獻自己的作品,但總是心有顧慮。首先是之前一直忙碌於公司的項目,沒有時間做自己的東西,公司利益為先,確實也沒法開源;二是總覺得自己的代碼醜陋,不夠優雅、簡潔。怕貼出來後貽笑大方。

最近一段時間沒有那麼忙了,於是有時間把以前的積累沉澱一下。對之前做過的項目會有更多的反思。比如,

  1. 怎麼做才能把業務邏輯和工具組件分開,使工具組件可以在多個項目中復用;
  2. 怎麼做才能使整個系統的框架更加簡潔優雅,使其更容易維護、升級;
  3. 怎樣才能合理的以多人協作的方式開發單片機程式;

帶著這3個問題,回顧之前做過的項目,不敢說完全想清楚,但是有了一個初步的結果,於是就誕生了我的第一個開源項目,我給他取名為SmartTimer

實際上,目前的SmartTimer,無論從代碼嚴謹性上,還是代碼美學上,和之前我用過的比較知名的開源項目,如RT-Thread、EasyFlash等相比,有相當大的差距。但是開源精神不僅是分享技術,另外很重要的一點是,只有拿出來曬,被各位高手”噴過”,你的項目才會更加完善,對你個人來說,才會有更大的收穫。

1.SmartTimer能幹什麼?

簡單說來,SmartTimer是一個輕量級的基於STM32的定時器調度器,在單片機”裸跑”的情況下,可以很方便的實現非同步編程。

它可以應用在對實時性要求沒那麼高的場合,比如說一個空氣檢測裝置,每200ms收集一次甲醛數據,這個任務顯然對實時性要求沒那麼高,如果時間上相差幾毫秒,甚至幾十毫秒也沒關係,那麼使用SmartTimer非常適合;而如果開發一個四軸飛行器,無論是對陀螺儀數據的採集、計算,以及對4個電機的控制,在時間的控制上都需要非常精確。那麼這種場合下SmartTimer無法勝任,你需要一個帶有搶占優先順序機制的實時系統。

不同的場景,選擇不同的工具和架構才是最合理的,SmartTimer只能做它力所能及的事情。

雖然SmartTimer是基於STM32開發的,但是它可以很方便的移植到其他的單片機上。

2. SmartTimer的一般用法

2.1 Runlater。

在單片機編程中,想實現在”xxx毫秒後調用xxx函數”的功能,一般有3種方法:

  1. 用阻塞的,非精確的方式,就是用for(i=0;i<0xffff;i++);這種迴圈等待的方式,來非精確的延遲一段時間,然後再順序執行下麵的程式;
  2. 利用硬體定時器實現非同步的精確延時,把XXX函數在定時器中斷里執行;
  3. 同樣是利用硬體定時器,但是只在定時器中斷里設置標誌位,在系統的主While迴圈中檢測這個標誌位,當檢測到標誌置位後,去運行XXX函數。

從理論上來說,以上3種方式中,第3種採用定時器設定標誌位的方法最好。因為首先主程式不用阻塞,在等待的時間里,MCU完全可以去做其他的事情,其次在定時器中斷里不用占用太多的時間,節約中斷資源。但這種方式有個缺點,就是實現起來相對麻煩一些。因為如果你要有N個runlater的需求,那麼就得設置N個標誌位,還要考慮定時器的分配、設定。在程式主While迴圈里也會遍佈N個查詢標誌位的if語句。如果N足夠多,其實大於5個,就會比較頭疼。這樣會使主While迴圈看起來很亂。這樣的實現不夠簡潔、優雅。

SmartTimer首先解決的就是這個問題,它可以優雅地延遲調用某函數。

2.2 Runloop

在定時器編程方面還有另一個典型需求,就是“每隔xxx毫秒運行一次XXX函數,一共運行XXX次”。這個實現起來和runlater差不多,就是加一個運行次數的技術標誌。我就不再贅述了。還是那句話:

SmartTimer可以優雅的實現Runloop功能。

2.3 Delay

並不是說非阻塞就一定比阻塞好,因為在某些場景下,必須得用到阻塞,使單片機停下來等待某個事件。那麼SmartTimer也可以提供這個功能。

3. SmartTimer的高級用法

所謂的高級用法,並不是說SmartTimer有隱藏模式,能開啟黑科技。而是說,如果你能轉變思路,舉一反三地話,可以利用SmartTimer提供的簡單功能實現更加優化、合理的系統結構。

傳統的單片機裸跑一般採用狀態機模式,就是在主While迴圈里設定一些標誌位或是設定好程式進行的步驟,根據事件的進程來跳轉程式。簡單的說來,這是一種順序執行的程式結構。其靈活性和實時性並不高,尤其是當需要處理的業務越來越多,越來越複雜時,狀態機會臃腫不堪,一不留神(其實是一定以及肯定)就會深埋bug於其中,調試解決BUG時也會異常痛苦。

如果你能轉換一下思路,不再把業務邏輯中各個模塊的關係看成基於因果(順序),而是基於時間,模塊間如果需要確定次序可以採用標誌位進行同步。那麼恭喜你,你已經有了採用實時系統的思想,可以嘗試使用RT-thread等操作系統來完成你的項目了。但是,使用操作系統有幾個問題,第一是當單片機資源有限的時候,使用操作系統恐怕不太合適;第二是學習操作系統本身有一定的難度,至少你需要花費一定的時間;第三如果你的項目複雜度沒有那麼高,使用操作系統有點大材小用。

那麼,請允許我沒羞沒臊的說一句,其實利用SmartTimer中的Runloop功能可以簡單的實現基於時間的主程式框架。

4.關於Demo

與源碼一起提供的,還有一個Demo程式。這個Demo比較簡單,主要是為了測試SmartTimer的功能。Demo程式基本可以體現Runlater,Runloop,Delay功能。同時也能基本體現基於時間的編程思想(單片機裸跑程式框架)。

5.SmartTimer的使用

SmartTimer.h中聲明的公開函數並不多,總共有8個:

void stim_init ( void );

void stim_tick (void);

void stim_mainloop ( void );

int8_t stim_loop ( uint16_t delayms, void (*callback)(void), uint16_t times);

int8_t stim_runlater ( uint16_t delayms, void (*callback)(void));

void stim_delay ( uint16_t delayms);

void stim_kill_event(int8_t id);

void stim_remove_event(int8_t id);

下麵我將逐一介紹

5.1 必要的前提

SmartTimer能夠工作的必要條件是:

  • A. 設置Systick的定時中斷(也可以是其他的硬體定時器TIMx,我選擇的是比較簡單的Systick),我預設設置為1ms中斷一次,使用者可以根據自己的情況來更改。Systick時鐘的設置在stim_init函數中,該函數必須在主程式初始化階段調用一次。
  • B. 在定時器中斷函數中調用stim_tick();可以說,這個函數是SmartTimer的引擎,如A步驟所述,預設情況下,每1ms,定時器中斷會調用一次stim_tick();
  • C. 在主While迴圈中執行stim_mainloop(),這個函數主要有兩個作用,一是執行定時結束後的回調函數;二是回收使用完畢的timer事件的資源。

5.2 開始使用SmartTimer

做好以上的搭建工作後,就可以開始使用SmartTimer了。

int8_t stim_runlater ( uint16_t delayms, void (*callback)(void));

該函數接受兩個參數,返回定時事件的id。參數delayms傳入延遲多長時間,註意這裡的單位是根據之前A步驟里,你設置的時間滴答來確定的(預設單位是1ms);第二個參數是回調函數的函數指針,目前只支持沒有參數,且無返回值的回調函數,未來會考慮加入帶參數和返回值的回調。
舉例:

timer_runlater(100,ledflash); //100豪秒(100*1ms=100ms)後,執行void ledflash(void)函數

//如果在stim_init()中,設置的時鐘滴答為10ms執行一次,那麼傳入同樣的參數,意義就會改變:

timer_runlater(100,ledflash); //1秒(100*10ms=1000ms=1S)後,執行void ledflash(void)函數
int8_t stim_loop ( uint16_t delayms, void (*callback)(void), uint16_t times);

這個函數的參數意義同runlater差不多,我就不詳細說明瞭。 該函數接���3個參數,delayms為延遲時間,callback為回調函數指針,times是迴圈次數。 舉例(以1ms滴答為例):

timer_runloop(50,ledflash,5); // 每50ms,執行一次ledflash(),總共執行5次
timer_runloop(80,ledflash, TIMER_LOOP_FOREVER); // 每80ms,執行一次ledflash(),無限迴圈。
void timer_delay ( uint16_t delayms);   //延遲xx ms 

這個函數會阻塞主程式,並延遲一段時間。

void stim_kill_event(int8_t id); 
void stim_remove_event(int8_t id);

這兩個函數,可以將之前設定的定時事件取消。比如之前用stim_loop無限迴圈了一個事件,當獲取某個指令後,需要取消這個任務,則可以用這兩個函數取消事件調度。這兩個函數的區別是:

void stim_kill_event(int8_t id); //直接取消事件,忽略未處理完成的調度任務。
void stim_remove_event(int8_t id);//將已經完成計時的調度任務處理完畢之後,再取消事件

5.3 註意事項

SmartTimer可接受的Timer event數量是有上限的,這個上限由smarttimer.h中的巨集定義

#define        TIMEREVENT_MAX_SIZE      20

來決定的。預設為20個,你可以根據實際情況增加或減少。但不可多於128個

SmartTimer的源碼和Demo程式下載地址在GitHub上:https://github.com/lmooml/SmartTimer.git


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一. 創建表的方法 語法:create table 表名( 屬性名數據類型完整約束條件, 屬性名數據類型條完整約束件, 。。。。。。。。。 屬性名數據類型 ); (1)舉例:1 create table example0( 2 id int, 3 name varchar(20), 4 sexboo ...
  • Kafka是目前非常流行的消息隊列中間件,常用於做普通的消息隊列、網站的活性數據分析(PV、流量、點擊量等)、日誌的搜集(對接大數據存儲引擎做離線分析)。 全部內容來自網路,可信度有待考證!如有問題,還請及時指正。 概念介紹 在Kafka中消息隊列分為三種角色: ,即生產者,負責產生日誌數據。 ,存 ...
  • 需求: 一篇文章里有很多評論,每個評論又有很多回覆評論,要求: 頁面將文章展示出來,且文章的主評論按照評論時間分頁展示,回覆評論的評論完全展示在每個主評論下麵,且按照回覆時間排序 最終查詢結果SQL查詢結果如下: Code: 評論編碼,ParentCode:回覆評論編碼,num:主評論序號,lvl: ...
  • 問題描述:在表列里有肉眼不可見字元,導致一些更新或插入失敗。 幾年前第一次碰見這種問題是在讀取考勤機人員信息時碰見的,折騰了一點時間,現在又碰到了還有點新發現就順便一起記錄下。 轉載註明出處:http://www.cnblogs.com/zzry/p/5729404.html 如下圖所示 golds ...
  • 背景 使用Exp命令在oracle 11g 以後不導出空表(rowcount=0),是最近在工作中遇到一個很坑的問題,甚至已經被坑了不止一次,所以這次痛定思痛,準備把這個問題徹底解決。之所以叫新方法,那一定有老方法了,這個方法是一位博友很早就提出了,以下是原文,其實也說明瞭問題的原因 Oracle1 ...
  • 網路進程間通信:socket API簡介 不同電腦(通過網路相連)上運行的進程相互通信機制稱為網路進程間通信(network IPC)。 在本地可以通過進程PID來唯一標識一個進程,但是在網路中這是行不通的。其實TCP/IP協議族已經幫我們解決了這個問題,網路層的“ip地址”可以唯一標識網路中的主 ...
  • 今天來講一講我開發SmartTimer的思路。在上一篇介紹SmartTimer的文章 "《SmartTImer——一個基於STM32的時鐘管理器》" 中,我提到了要實現延遲XX毫秒執行XX函數的功能,比較好的方式是在定時器中斷中設置溢出標誌,而在程式主迴圈中檢測這個標誌,如果標誌置位則運行回調函數。 ...
  • 儘管大部分常見的linux系統的包管理器內都會包含texlive,然而最好還是安裝TUG發佈的原版texlive。這是由於:1,軟體倉庫內的texlive通常版本較陳舊;2,由於版權因素一些tex巨集包沒有被軟體倉庫收錄;3,軟體倉庫內的texlive分割混亂,更新和維護都不方便,事實上,texliv ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...