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

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

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

單片機(jī)程序時(shí)間輪片法框架

strongerHuang ? 來(lái)源:ERYUESANHI ? 作者:ERYUESANHI ? 2021-08-26 11:07 ? 次閱讀

來(lái)源 | ERYUESANHI

編排 | strongerHuang

今天分享一篇單片機(jī)程序框架的文章。

程序架構(gòu)重要性

很多人尤其是初學(xué)者在寫代碼的時(shí)候往往都是想一點(diǎn)寫一點(diǎn),最開始沒(méi)有一個(gè)整體的規(guī)劃,導(dǎo)致后面代碼越寫越亂,bug不斷。

最終代碼跑起來(lái)看似沒(méi)有問(wèn)題(有可能也真的沒(méi)有問(wèn)題),但是要加一個(gè)功能的時(shí)候會(huì)浪費(fèi)大量的時(shí)間,甚至導(dǎo)致整個(gè)代碼的崩潰。

所以,在一個(gè)項(xiàng)目開始的時(shí)候多花一些時(shí)間在代碼的架構(gòu)設(shè)計(jì)上是十分有必要的。代碼架構(gòu)確定好了之后你會(huì)發(fā)現(xiàn)敲代碼的時(shí)候會(huì)特別快,并且在后期調(diào)試的時(shí)候也不會(huì)像無(wú)頭蒼蠅一樣胡亂找問(wèn)題。當(dāng)然,調(diào)試也是一門技術(shù)。

在學(xué)習(xí)實(shí)時(shí)操作系統(tǒng)的過(guò)程中,發(fā)現(xiàn)實(shí)時(shí)操作系統(tǒng)框架與個(gè)人的業(yè)務(wù)代碼之間的耦合性就非常低,都是只需要將業(yè)務(wù)代碼通過(guò)一定的接口函數(shù)注冊(cè)好后就交給操作系統(tǒng)托管了,十分方便。

但是操作系統(tǒng)的調(diào)度過(guò)于復(fù)雜,這里就使用操作系統(tǒng)的思維方式來(lái)重構(gòu)這個(gè)時(shí)間片輪詢框架。實(shí)現(xiàn)該框架的完全解耦,用戶只需要包含頭文件,并且在使用過(guò)程中不需要改動(dòng)已經(jīng)寫好的庫(kù)文件。

Demo

首先來(lái)個(gè)demo,該demo是使用電腦開兩個(gè)線程:一個(gè)線程模擬單片機(jī)的定時(shí)器中斷產(chǎn)生時(shí)間片輪詢個(gè)時(shí)鐘,另一個(gè)線程則模擬主函數(shù)中一直運(yùn)行的時(shí)間片輪詢調(diào)度程序。

#include 《thread》#include 《stdio.h》#include 《windows.h》#include “timeslice.h”

// 創(chuàng)建5個(gè)任務(wù)對(duì)象TimesilceTaskObj task_1, task_2, task_3, task_4, task_5;

// 具體的任務(wù)函數(shù)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 。..

”);}

// 初始化任務(wù)對(duì)象,并且將任務(wù)添加到時(shí)間片輪詢調(diào)度中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);}

// 開兩個(gè)線程模擬在單片機(jī)上的運(yùn)行過(guò)程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;}

由以上例子可見(jiàn),這個(gè)框架使用十分方便,甚至可以完全不知道其原理,僅僅通過(guò)幾個(gè)簡(jiǎn)單的接口就可以迅速創(chuàng)建任務(wù)并加入到時(shí)間片輪詢的框架中,十分好用。

時(shí)間片輪詢架構(gòu)

其實(shí)該部分主要使用了面向?qū)ο蟮乃季S,使用結(jié)構(gòu)體作為對(duì)象,并使用結(jié)構(gòu)體指針作為參數(shù)傳遞,這樣作可以節(jié)省資源,并且有著極高的運(yùn)行效率。

其中最難的部分是侵入式鏈表的使用,這種鏈表在一些操作系統(tǒng)內(nèi)核中使用十分廣泛,這里是參考RT-Thread實(shí)時(shí)操作系統(tǒng)中的侵入式鏈表實(shí)現(xiàn)。

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內(nèi)核中使用十分廣泛,也十分經(jīng)典,其原理具體可以參考文章: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 “l(fā)ist.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;}

到此,一個(gè)全新的,完全解耦的,十分方便易用時(shí)間片輪詢框架完成。

責(zé)任編輯:haq

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

    關(guān)注

    6030

    文章

    44489

    瀏覽量

    631996
  • 框架
    +關(guān)注

    關(guān)注

    0

    文章

    398

    瀏覽量

    17404
  • 程序
    +關(guān)注

    關(guān)注

    116

    文章

    3756

    瀏覽量

    80754

原文標(biāo)題:?jiǎn)纹瑱C(jī)面向?qū)ο笏季S的架構(gòu):時(shí)間輪片法

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

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    單片機(jī)怎么寫入程序

    程序通常涉及以下幾個(gè)步驟: 選擇單片機(jī)和開發(fā)環(huán)境 : 確定項(xiàng)目需求,選擇合適的單片機(jī)型號(hào)。 安裝相應(yīng)的開發(fā)環(huán)境,如Keil、IAR、MPLAB等。 硬件連接 : 將單片機(jī)連接到開發(fā)板或
    的頭像 發(fā)表于 10-21 11:21 ?217次閱讀

    keil可以讀出單片機(jī)程序

    Keil是一款廣泛應(yīng)用于單片機(jī)程序開發(fā)的軟件,它提供了包括C編譯器、宏匯編、連接器、庫(kù)管理和一個(gè)功能強(qiáng)大的仿真調(diào)試器等在內(nèi)的完整開發(fā)方案。然而,關(guān)于Keil是否能直接“讀出”單片機(jī)程序
    的頭像 發(fā)表于 09-02 10:32 ?602次閱讀

    單片機(jī)燒錄程序用什么軟件

    單片機(jī)燒錄程序單片機(jī)開發(fā)過(guò)程中的一個(gè)重要環(huán)節(jié),涉及到將編寫好的程序代碼通過(guò)燒錄器寫入單片機(jī)的ROM中,以實(shí)現(xiàn)對(duì)
    的頭像 發(fā)表于 09-02 10:05 ?736次閱讀

    單片機(jī)燒錄程序可以重新燒嗎

    單片機(jī)(Microcontroller Unit, MCU)是一種集成電路芯片,它將計(jì)算機(jī)的CPU、存儲(chǔ)器、輸入/輸出接口等集成在一塊芯片上,用于控制各種電子設(shè)備。單片機(jī)燒錄程序是指將編寫
    的頭像 發(fā)表于 09-02 10:04 ?780次閱讀

    單片機(jī)燒錄程序時(shí)為什么一直在檢測(cè)

    單片機(jī)燒錄程序時(shí),如果軟件一直在顯示“正在檢測(cè)單片機(jī)”,可能是由多種原因?qū)е碌?。以下是一些常?jiàn)的原因及其解決方法: 1. 連接問(wèn)題 USB轉(zhuǎn)串口接觸不良 :檢查單片機(jī)與電腦的連接是否穩(wěn)
    的頭像 發(fā)表于 09-02 09:57 ?2652次閱讀

    單片機(jī)燒錄程序的線比單片機(jī)上的少還能燒錄嗎

    單片機(jī)燒錄原理 單片機(jī)燒錄是指將編寫好的程序代碼通過(guò)一定的方式傳輸?shù)?b class='flag-5'>單片機(jī)的存儲(chǔ)器中,使其能夠按照程序的指令運(yùn)行。這個(gè)過(guò)程通常需要使用燒錄器
    的頭像 發(fā)表于 09-02 09:54 ?367次閱讀

    單片機(jī)燒錄程序的線叫什么

    單片機(jī)燒錄程序時(shí)所使用的線,根據(jù)不同的燒錄方式和接口標(biāo)準(zhǔn),可能會(huì)有所不同。以下是一些常見(jiàn)的單片機(jī)燒錄接口和對(duì)應(yīng)的線: JTAG接口 : JTAG(Joint Test Action Group
    的頭像 發(fā)表于 09-02 09:52 ?675次閱讀

    單片機(jī)燒錄程序的基本步驟是什么

    單片機(jī)燒錄程序單片機(jī)開發(fā)過(guò)程中非常重要的一步,它涉及到將編寫好的程序代碼通過(guò)一定的方式傳輸?shù)?b class='flag-5'>單片機(jī)內(nèi)部的存儲(chǔ)器中,使
    的頭像 發(fā)表于 09-02 09:47 ?697次閱讀

    上系統(tǒng)與單片機(jī)的區(qū)別

    上系統(tǒng)(SoC)與單片機(jī)在多個(gè)方面存在明顯的區(qū)別。
    的頭像 發(fā)表于 03-28 14:39 ?1175次閱讀

    RENESAS-RA家族32位單片機(jī)命名

    電子發(fā)燒友網(wǎng)站提供《RENESAS-RA家族32位單片機(jī)命名.pdf》資料免費(fèi)下載
    發(fā)表于 02-19 14:10 ?0次下載
    RENESAS-RA家族32位<b class='flag-5'>單片機(jī)</b>命名<b class='flag-5'>法</b>

    有些汽車ECU會(huì)使用2單片機(jī),請(qǐng)問(wèn)這2單片機(jī)是如何分配任務(wù)的?

    來(lái)自一位用戶的咨詢,麻煩幫忙解答。越詳細(xì)越好,有圖有真相。如有相關(guān)的視頻、文檔也可以輔助說(shuō)明,謝謝。 有些汽車ECU會(huì)使用2單片機(jī),請(qǐng)問(wèn)這2單片機(jī)是如何分配任務(wù)的? 下圖是某F1動(dòng)
    發(fā)表于 02-02 08:13

    軟件架構(gòu)之時(shí)間

    首先來(lái)個(gè)demo,該demo是使用電腦開兩個(gè)線程:一個(gè)線程模擬單片機(jī)的定時(shí)器中斷產(chǎn)生時(shí)間輪詢個(gè)時(shí)鐘,另一個(gè)線程則模擬主函數(shù)中一直運(yùn)行的時(shí)間
    的頭像 發(fā)表于 01-18 09:29 ?494次閱讀
    軟件架構(gòu)之<b class='flag-5'>時(shí)間</b><b class='flag-5'>輪</b><b class='flag-5'>片</b><b class='flag-5'>法</b>

    keil5mdk怎么編寫51單片機(jī)程序

    編寫51單片機(jī)程序需要以下步驟: 編寫程序框架:首先,你需要定義程序所需要的變量和常量,以及設(shè)置中斷向量和寄存器的初始狀態(tài)。這些信息可以根據(jù)
    的頭像 發(fā)表于 12-25 14:54 ?1377次閱讀

    pic單片機(jī)匯編程序實(shí)例

    以下是一個(gè)簡(jiǎn)單的PIC單片機(jī)匯編程序示例,這個(gè)程序將實(shí)現(xiàn)一個(gè)LED閃爍的功能。 首先,我們需要了解PIC單片機(jī)的結(jié)構(gòu)和指令集。PIC單片機(jī)
    的頭像 發(fā)表于 12-14 11:01 ?1716次閱讀

    一個(gè)線程模擬單片機(jī)程序框架分享

    首先來(lái)個(gè)demo,該demo是使用電腦開兩個(gè)線程:一個(gè)線程模擬單片機(jī)的定時(shí)器中斷產(chǎn)生時(shí)間輪詢個(gè)時(shí)鐘,另一個(gè)線程則模擬主函數(shù)中一直運(yùn)行的時(shí)間
    發(fā)表于 11-19 10:39 ?2287次閱讀
    一個(gè)線程模擬<b class='flag-5'>單片機(jī)</b><b class='flag-5'>程序</b><b class='flag-5'>框架</b>分享