來源 | ERYUESANHI
編排 | strongerHuang
今天分享一篇單片機程序框架的文章。
程序架構重要性
很多人尤其是初學者在寫代碼的時候往往都是想一點寫一點,最開始沒有一個整體的規劃,導致后面代碼越寫越亂,bug不斷。
最終代碼跑起來看似沒有問題(有可能也真的沒有問題),但是要加一個功能的時候會浪費大量的時間,甚至導致整個代碼的崩潰。
所以,在一個項目開始的時候多花一些時間在代碼的架構設計上是十分有必要的。代碼架構確定好了之后你會發現敲代碼的時候會特別快,并且在后期調試的時候也不會像無頭蒼蠅一樣胡亂找問題。當然,調試也是一門技術。
在學習實時操作系統的過程中,發現實時操作系統框架與個人的業務代碼之間的耦合性就非常低,都是只需要將業務代碼通過一定的接口函數注冊好后就交給操作系統托管了,十分方便。
但是操作系統的調度過于復雜,這里就使用操作系統的思維方式來重構這個時間片輪詢框架。實現該框架的完全解耦,用戶只需要包含頭文件,并且在使用過程中不需要改動已經寫好的庫文件。
Demo
首先來個demo,該demo是使用電腦開兩個線程:一個線程模擬單片機的定時器中斷產生時間片輪詢個時鐘,另一個線程則模擬主函數中一直運行的時間片輪詢調度程序。
#include 《thread》#include 《stdio.h》#include 《windows.h》#include “timeslice.h”
// 創建5個任務對象TimesilceTaskObj task_1, task_2, task_3, task_4, task_5;
// 具體的任務函數void task1_hdl(){ printf(“》》 task 1 is running 。..
”);}
void task2_hdl(){ printf(“》》 task 2 is running 。..
”);}
void task3_hdl(){ printf(“》》 task 3 is running 。..
”);}
void task4_hdl(){ printf(“》》 task 4 is running 。..
”);}
void task5_hdl(){ printf(“》》 task 5 is running 。..
”);}
// 初始化任務對象,并且將任務添加到時間片輪詢調度中void task_init(){ timeslice_task_init(&task_1, task1_hdl, 1, 10); timeslice_task_init(&task_2, task2_hdl, 2, 20); timeslice_task_init(&task_3, task3_hdl, 3, 30); timeslice_task_init(&task_4, task4_hdl, 4, 40); timeslice_task_init(&task_5, task5_hdl, 5, 50); timeslice_task_add(&task_1); timeslice_task_add(&task_2); timeslice_task_add(&task_3); timeslice_task_add(&task_4); timeslice_task_add(&task_5);}
// 開兩個線程模擬在單片機上的運行過程void timeslice_exec_thread(){ while (true) { timeslice_exec(); }}
void timeslice_tick_thread(){ while (true) { timeslice_tick(); Sleep(10); }}
int main(){ task_init();
printf(“》》 task num: %d
”, timeslice_get_task_num()); printf(“》》 task len: %d
”, timeslice_get_task_timeslice_len(&task_3));
timeslice_task_del(&task_2); printf(“》》 delet task 2
”); printf(“》》 task 2 is exist: %d
”, timeslice_task_isexist(&task_2));
printf(“》》 task num: %d
”, timeslice_get_task_num());
timeslice_task_del(&task_5); printf(“》》 delet task 5
”);
printf(“》》 task num: %d
”, timeslice_get_task_num());
printf(“》》 task 3 is exist: %d
”, timeslice_task_isexist(&task_3)); timeslice_task_add(&task_2); printf(“》》 add task 2
”); printf(“》》 task 2 is exist: %d
”, timeslice_task_isexist(&task_2));
timeslice_task_add(&task_5); printf(“》》 add task 5
”);
printf(“》》 task num: %d
”, timeslice_get_task_num());
printf(“
========timeslice running===========
”);
std::thread thread_1(timeslice_exec_thread); std::thread thread_2(timeslice_tick_thread);
thread_1.join(); thread_2.join();
return 0;}
由以上例子可見,這個框架使用十分方便,甚至可以完全不知道其原理,僅僅通過幾個簡單的接口就可以迅速創建任務并加入到時間片輪詢的框架中,十分好用。
時間片輪詢架構
其實該部分主要使用了面向對象的思維,使用結構體作為對象,并使用結構體指針作為參數傳遞,這樣作可以節省資源,并且有著極高的運行效率。
其中最難的部分是侵入式鏈表的使用,這種鏈表在一些操作系統內核中使用十分廣泛,這里是參考RT-Thread實時操作系統中的侵入式鏈表實現。
h文件:
#ifndef _TIMESLICE_H#define _TIMESLICE_H
#include “。/list.h”
typedef enum { TASK_STOP, TASK_RUN} IsTaskRun;
typedef struct timesilce{ unsigned int id; void (*task_hdl)(void); IsTaskRun is_run; unsigned int timer; unsigned int timeslice_len; ListObj timeslice_task_list;} TimesilceTaskObj;
void timeslice_exec(void);void timeslice_tick(void);void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len);void timeslice_task_add(TimesilceTaskObj* obj);void timeslice_task_del(TimesilceTaskObj* obj);unsigned int timeslice_get_task_timeslice_len(TimesilceTaskObj* obj);unsigned int timeslice_get_task_num(void);unsigned char timeslice_task_isexist(TimesilceTaskObj* obj);
#endif
c文件:
#include “。/timeslice.h”
static LIST_HEAD(timeslice_task_list);
void timeslice_exec(){ ListObj* node; TimesilceTaskObj* task;
list_for_each(node, ×lice_task_list) { task = list_entry(node, TimesilceTaskObj, timeslice_task_list); if (task-》is_run == TASK_RUN) { task-》task_hdl(); task-》is_run = TASK_STOP; } }}
void timeslice_tick(){ ListObj* node; TimesilceTaskObj* task;
list_for_each(node, ×lice_task_list) { task = list_entry(node, TimesilceTaskObj, timeslice_task_list); if (task-》timer != 0) { task-》timer--; if (task-》timer == 0) { task-》is_run = TASK_RUN; task-》timer = task-》timeslice_len; } } }}
unsigned int timeslice_get_task_num(){ return list_len(×lice_task_list);}
void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len){ obj-》id = id; obj-》is_run = TASK_STOP; obj-》task_hdl = task_hdl; obj-》timer = timeslice_len; obj-》timeslice_len = timeslice_len;}
void timeslice_task_add(TimesilceTaskObj* obj){ list_insert_before(×lice_task_list, &obj-》timeslice_task_list);}
void timeslice_task_del(TimesilceTaskObj* obj){ if (timeslice_task_isexist(obj)) list_remove(&obj-》timeslice_task_list); else return;}
unsigned char timeslice_task_isexist(TimesilceTaskObj* obj){ unsigned char isexist = 0; ListObj* node; TimesilceTaskObj* task;
list_for_each(node, ×lice_task_list) { task = list_entry(node, TimesilceTaskObj, timeslice_task_list); if (obj-》id == task-》id) isexist = 1; }
return isexist;}
unsigned int timeslice_get_task_timeslice_len(TimesilceTaskObj* obj){ return obj-》timeslice_len;}
底層侵入式雙向鏈表
該鏈表是linux內核中使用十分廣泛,也十分經典,其原理具體可以參考文章:https://www.cnblogs.com/skywang12345/p/3562146.html
h文件:
#ifndef _LIST_H#define _LIST_H
#define offset_of(type, member) (unsigned long) &((type*)0)-》member#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offset_of(type, member)))
typedef struct list_structure{ struct list_structure* next; struct list_structure* prev;} ListObj;
#define LIST_HEAD_INIT(name) {&(name), &(name)}#define LIST_HEAD(name) ListObj name = LIST_HEAD_INIT(name)
void list_init(ListObj* list);void list_insert_after(ListObj* list, ListObj* node);void list_insert_before(ListObj* list, ListObj* node);void list_remove(ListObj* node);int list_isempty(const ListObj* list);unsigned int list_len(const ListObj* list);
#define list_entry(node, type, member) container_of(node, type, member)
#define list_for_each(pos, head) for (pos = (head)-》next; pos != (head); pos = pos-》next)
#define list_for_each_safe(pos, n, head) for (pos = (head)-》next, n = pos-》next; pos != (head); pos = n, n = pos-》next)
#endif
c文件:
#include “list.h”
void list_init(ListObj* list){ list-》next = list-》prev = list;}
void list_insert_after(ListObj* list, ListObj* node){ list-》next-》prev = node; node-》next = list-》next;
list-》next = node; node-》prev = list;}
void list_insert_before(ListObj* list, ListObj* node){ list-》prev-》next = node; node-》prev = list-》prev;
list-》prev = node; node-》next = list;}
void list_remove(ListObj* node){ node-》next-》prev = node-》prev; node-》prev-》next = node-》next;
node-》next = node-》prev = node;}
int list_isempty(const ListObj* list){ return list-》next == list;}
unsigned int list_len(const ListObj* list){ unsigned int len = 0; const ListObj* p = list; while (p-》next != list) { p = p-》next; len++; }
return len;}
到此,一個全新的,完全解耦的,十分方便易用時間片輪詢框架完成。
責任編輯:haq
-
單片機
+關注
關注
6039文章
44574瀏覽量
636324 -
框架
+關注
關注
0文章
403瀏覽量
17510 -
程序
+關注
關注
117文章
3791瀏覽量
81153
原文標題:單片機面向對象思維的架構:時間輪片法
文章出處:【微信號:strongerHuang,微信公眾號:strongerHuang】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論