基于链表管理的单片机轮询程序框架

李肖遥 2022-07-02 22:09
    关注、星标公众号,直达精彩内容

来源:ERYUESANHI


大家好,我是肖遥,今天分享单片机裸机程序框架的文章。

1

总程序架构的重要性

很多人尤其是初学者在写代码的时候往往都是想一点写一点,最开始没有一个整体的规划,导致后面代码越写越乱,bug不断。
最终代码跑起来看似没有问题(有可能也真的没有问题),但是要加一个功能的时候会浪费大量的时间,甚至导致整个代码的崩溃。
所以,在一个项目开始的时候多花一些时间在代码的架构设计上是十分有必要的。代码架构确定好了之后你会发现敲代码的时候会特别快,并且在后期调试的时候也不会像无头苍蝇一样胡乱找问题。当然,调试也是一门技术。
在学习实时操作系统的过程中,发现实时操作系统框架与个人的业务代码之间的耦合性就非常低,都是只需要将业务代码通过一定的接口函数注册好后就交给操作系统托管了,十分方便。
但是操作系统的调度过于复杂,这里就使用操作系统的思维方式来重构这个时间片轮询框架。实现该框架的完全解耦,用户只需要包含头文件,并且在使用过程中不需要改动已经写好的库文件。

2

参考代码

首先来个demo,该demo是使用电脑开两个线程:一个线程模拟单片机的定时器中断产生时间片轮询个时钟,另一个线程则模拟主函数中一直运行的时间片轮询调度程序。
  1#include 
2#include 
3#include 
4#include "timeslice.h"
5
6// 创建5个任务对象
7TimesilceTaskObj task_1, task_2, task_3, task_4, task_5;
8
9// 具体的任务函数
10void task1_hdl()
11{
12    printf(">> task 1 is running ...\n");
13}
14
15void task2_hdl()
16{
17    printf(">> task 2 is running ...\n");
18}
19
20void task3_hdl()
21{
22    printf(">> task 3 is running ...\n");
23}
24
25void task4_hdl()
26{
27    printf(">> task 4 is running ...\n");
28}
29
30void task5_hdl()
31{
32    printf(">> task 5 is running ...\n");
33}
34
35// 初始化任务对象,并且将任务添加到时间片轮询调度中
36void task_init()
37{
38    timeslice_task_init(&task_1, task1_hdl, 110);
39    timeslice_task_init(&task_2, task2_hdl, 220);
40    timeslice_task_init(&task_3, task3_hdl, 330);
41    timeslice_task_init(&task_4, task4_hdl, 440);
42    timeslice_task_init(&task_5, task5_hdl, 550);
43    timeslice_task_add(&task_1);
44    timeslice_task_add(&task_2);
45    timeslice_task_add(&task_3);
46    timeslice_task_add(&task_4);
47    timeslice_task_add(&task_5);
48}
49
50
51// 开两个线程模拟在单片机上的运行过程
52void timeslice_exec_thread()
53{
54    while (true)
55    {
56        timeslice_exec();
57    }
58}
59
60void timeslice_tick_thread()
61{
62    while (true)
63    {
64        timeslice_tick();
65        Sleep(10);
66    }
67}
68
69int main()
70{
71    task_init();
72
73    printf(">> task num: %d\n", timeslice_get_task_num());
74    printf(">> task len: %d\n", timeslice_get_task_timeslice_len(&task_3));
75
76    timeslice_task_del(&task_2);
77    printf(">> delet task 2\n");
78    printf(">> task 2 is exist: %d\n", timeslice_task_isexist(&task_2));
79
80    printf(">> task num: %d\n", timeslice_get_task_num());
81
82    timeslice_task_del(&task_5);
83    printf(">> delet task 5\n");
84
85    printf(">> task num: %d\n", timeslice_get_task_num());
86
87    printf(">> task 3 is exist: %d\n", timeslice_task_isexist(&task_3));
88    timeslice_task_add(&task_2);
89    printf(">> add task 2\n");
90    printf(">> task 2 is exist: %d\n", timeslice_task_isexist(&task_2));
91
92    timeslice_task_add(&task_5);
93    printf(">> add task 5\n");
94
95    printf(">> task num: %d\n", timeslice_get_task_num());
96
97    printf("\n\n========timeslice running===========\n");
98
99    std::thread thread_1(timeslice_exec_thread);
100    std::thread thread_2(timeslice_tick_thread);
101
102    thread_1.join();
103    thread_2.join();
104
105
106    return 0;
107}


运行结果如下:

由以上例子可见,这个框架使用十分方便,甚至可以完全不知道其原理,仅仅通过几个简单的接口就可以迅速创建任务并加入到时间片轮询的框架中,十分好用。

3

时间片轮询架构

其实该部分主要使用了面向对象的思维,使用结构体作为对象,并使用结构体指针作为参数传递,这样作可以节省资源,并且有着极高的运行效率。

其中最难的部分是侵入式链表的使用,这种链表在一些操作系统内核中使用十分广泛,这里是参考RT-Thread实时操作系统中的侵入式链表实现。
h文件:
 1#ifndef _TIMESLICE_H
2#define _TIMESLICE_H
3
4#include "./list.h"
5
6typedef enum {
7    TASK_STOP,
8    TASK_RUN
9} IsTaskRun;
10
11typedef struct timesilce
12{
13    unsigned int id;
14    void (*task_hdl)(void);
15    IsTaskRun is_run;
16    unsigned int timer;
17    unsigned int timeslice_len;
18    ListObj timeslice_task_list;
19} TimesilceTaskObj;
20
21void timeslice_exec(void);
22void timeslice_tick(void);
23void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len);
24void timeslice_task_add(TimesilceTaskObj* obj);
25void timeslice_task_del(TimesilceTaskObj* obj);
26unsigned int timeslice_get_task_timeslice_len(TimesilceTaskObj* obj);
27unsigned int timeslice_get_task_num(void);
28unsigned char timeslice_task_isexist(TimesilceTaskObj* obj);
29
30#endif
.c文件:
 1#include "./timeslice.h"
2
3static LIST_HEAD(timeslice_task_list);
4
5void timeslice_exec()
6{
7    ListObj* node;
8    TimesilceTaskObj* task;
9
10    list_for_each(node, ×lice_task_list)
11    {
12
13        task = list_entry(node, TimesilceTaskObj, timeslice_task_list);
14        if (task->is_run == TASK_RUN)
15        {
16            task->task_hdl();
17            task->is_run = TASK_STOP;
18        }
19    }
20}
21
22void timeslice_tick()
23{
24    ListObj* node;
25    TimesilceTaskObj* task;
26
27    list_for_each(node, ×lice_task_list)
28    {
29        task = list_entry(node, TimesilceTaskObj, timeslice_task_list);
30        if (task->timer != 0)
31        {
32            task->timer--;
33            if (task->timer == 0)
34            {
35                task->is_run = TASK_RUN;
36                task->timer = task->timeslice_len;
37            }
38        }
39    }
40}
41
42unsigned int timeslice_get_task_num()
43{
44    return list_len(×lice_task_list);
45}
46
47void timeslice_task_init(TimesilceTaskObj* obj, void (*task_hdl)(void), unsigned int id, unsigned int timeslice_len)
48{
49    obj->id = id;
50    obj->is_run = TASK_STOP;
51    obj->task_hdl = task_hdl;
52    obj->timer = timeslice_len;
53    obj->timeslice_len = timeslice_len;
54}
55
56void timeslice_task_add(TimesilceTaskObj* obj)
57{
58    list_insert_before(×lice_task_list, &obj->timeslice_task_list);
59}
60
61void timeslice_task_del(TimesilceTaskObj* obj)
62{
63    if (timeslice_task_isexist(obj))
64        list_remove(&obj->timeslice_task_list);
65    else
66        return;
67}
68
69
70unsigned char timeslice_task_isexist(TimesilceTaskObj* obj)
71{
72    unsigned char isexist = 0;
73    ListObj* node;
74    TimesilceTaskObj* task;
75
76    list_for_each(node, ×lice_task_list)
77    {
78        task = list_entry(node, TimesilceTaskObj, timeslice_task_list);
79        if (obj->id == task->id)
80            isexist = 1;
81    }
82
83    return isexist;
84}
85
86unsigned int timeslice_get_task_timeslice_len(TimesilceTaskObj* obj)
87{
88    return obj->timeslice_len;
89}

4

底层侵入式双向链表

该链表是linux内核中使用十分广泛,也十分经典,其原理具体可以参考文章:

https://www.cnblogs.com/skywang12345/p/3562146.html
.h文件:
 1#ifndef _LIST_H
2#define _LIST_H
3
4#define offset_of(type, member)             (unsigned long) &((type*)0)->member
5#define container_of(ptr, type, member)     ((type *)((char *)(ptr) - offset_of(type, member)))
6
7typedef struct list_structure
8{
9    struct list_structure* next;
10    struct list_structure* prev;
11} ListObj;
12
13#define LIST_HEAD_INIT(name)    {&(name), &(name)}
14#define LIST_HEAD(name)         ListObj name = LIST_HEAD_INIT(name)
15
16void list_init(ListObj* list);
17void list_insert_after(ListObj* list, ListObj* node);
18void list_insert_before(ListObj* list, ListObj* node);
19void list_remove(ListObj* node);
20int list_isempty(const ListObj* list);
21unsigned int list_len(const ListObj* list);
22
23#define list_entry(node, type, member) \
24    container_of(node, type, member)
25
26#define list_for_each(pos, head) \
27    for (pos = (head)->next; pos != (head); pos = pos->next)
28
29#define list_for_each_safe(pos, n, head) \
30  for (pos = (head)->next, n = pos->next; pos != (head); \
31    pos = n, n = pos->next)
32
33#endif
.c文件:

1#include "list.h"
2
3void list_init(ListObj* list)
4{
5    list->next = list->prev = list;
6}
7
8void list_insert_after(ListObj* list, ListObj* node)
9{
10    list->next->prev = node;
11    node->next = list->next;
12
13    list->next = node;
14    node->prev = list;
15}
16
17void list_insert_before(ListObj* list, ListObj* node)
18{
19    list->prev->next = node;
20    node->prev = list->prev;
21
22    list->prev = node;
23    node->next = list;
24}
25
26void list_remove(ListObj* node)
27{
28    node->next->prev = node->prev;
29    node->prev->next = node->next;
30
31    node->next = node->prev = node;
32}
33
34int list_isempty(const ListObj* list)
35{
36    return list->next == list;
37}
38
39unsigned int list_len(const ListObj* list)
40{
41    unsigned int len = 0;
42    const ListObj* p = list;
43    while (p->next != list)
44    {
45        p = p->next;
46        len++;
47    }
48
49    return len;
50}

到此,一个全新的,完全解耦的,十分方便易用时间片轮询框架完成。

版权声明:本文来源网络,免费传达知识,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。

‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

关注我的微信公众号,回复“加群”按规则加入技术交流群。


点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。

李肖遥 公众号“技术让梦想更伟大”,作者:李肖遥,专注嵌入式,只推荐适合你的博文,干货,技术心得,与君共勉。
评论
  • 根据环洋市场咨询(Global Info Research)项目团队最新调研,预计2030年全球无人机锂电池产值达到2457百万美元,2024-2030年期间年复合增长率CAGR为9.6%。 无人机锂电池是无人机动力系统中存储并释放能量的部分。无人机使用的动力电池,大多数是锂聚合物电池,相较其他电池,锂聚合物电池具有较高的能量密度,较长寿命,同时也具有良好的放电特性和安全性。 全球无人机锂电池核心厂商有宁德新能源科技、欣旺达、鹏辉能源、深圳格瑞普和EaglePicher等,前五大厂商占有全球
    GIRtina 2025-01-07 11:02 128浏览
  • 本文介绍编译Android13 ROOT权限固件的方法,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。关闭selinux修改此文件("+"号为修改内容)device/rockchip/common/BoardConfig.mkBOARD_BOOT_HEADER_VERSION ?= 2BOARD_MKBOOTIMG_ARGS :=BOARD_PREBUILT_DTB
    Industio_触觉智能 2025-01-08 00:06 100浏览
  • 大模型的赋能是指利用大型机器学习模型(如深度学习模型)来增强或改进各种应用和服务。这种技术在许多领域都显示出了巨大的潜力,包括但不限于以下几个方面: 1. 企业服务:大模型可以用于构建智能客服系统、知识库问答系统等,提升企业的服务质量和运营效率。 2. 教育服务:在教育领域,大模型被应用于个性化学习、智能辅导、作业批改等,帮助教师减轻工作负担,提高教学质量。 3. 工业智能化:大模型有助于解决工业领域的复杂性和不确定性问题,尽管在认知能力方面尚未完全具备专家级的复杂决策能力。 4. 消费
    丙丁先生 2025-01-07 09:25 123浏览
  • By Toradex 秦海1). 简介嵌入式平台设备基于Yocto Linux 在开发后期量产前期,为了安全以及提高启动速度等考虑,希望将 ARM 处理器平台的 Debug Console 输出关闭,本文就基于 NXP i.MX8MP ARM 处理器平台来演示相关流程。 本文所示例的平台来自于 Toradex Verdin i.MX8MP 嵌入式平台。  2. 准备a). Verdin i.MX8MP ARM核心版配合Dahlia载板并
    hai.qin_651820742 2025-01-07 14:52 115浏览
  • 光伏逆变器是一种高效的能量转换设备,它能够将光伏太阳能板(PV)产生的不稳定的直流电压转换成与市电频率同步的交流电。这种转换后的电能不仅可以回馈至商用输电网络,还能供独立电网系统使用。光伏逆变器在商业光伏储能电站和家庭独立储能系统等应用领域中得到了广泛的应用。光耦合器,以其高速信号传输、出色的共模抑制比以及单向信号传输和光电隔离的特性,在光伏逆变器中扮演着至关重要的角色。它确保了系统的安全隔离、干扰的有效隔离以及通信信号的精准传输。光耦合器的使用不仅提高了系统的稳定性和安全性,而且由于其低功耗的
    晶台光耦 2025-01-09 09:58 28浏览
  • 村田是目前全球量产硅电容的领先企业,其在2016年收购了法国IPDiA头部硅电容器公司,并于2023年6月宣布投资约100亿日元将硅电容产能提升两倍。以下内容主要来自村田官网信息整理,村田高密度硅电容器采用半导体MOS工艺开发,并使用3D结构来大幅增加电极表面,因此在给定的占位面积内增加了静电容量。村田的硅技术以嵌入非结晶基板的单片结构为基础(单层MIM和多层MIM—MIM是指金属 / 绝缘体/ 金属) 村田硅电容采用先进3D拓扑结构在100um内,使开发的有效静电容量面积相当于80个
    知白 2025-01-07 15:02 150浏览
  • 根据环洋市场咨询(Global Info Research)项目团队最新调研,预计2030年全球中空长航时无人机产值达到9009百万美元,2024-2030年期间年复合增长率CAGR为8.0%。 环洋市场咨询机构出版了的【全球中空长航时无人机行业总体规模、主要厂商及IPO上市调研报告,2025-2031】研究全球中空长航时无人机总体规模,包括产量、产值、消费量、主要生产地区、主要生产商及市场份额,同时分析中空长航时无人机市场主要驱动因素、阻碍因素、市场机遇、挑战、新产品发布等。报告从中空长航时
    GIRtina 2025-01-09 10:35 25浏览
  • 在智能网联汽车中,各种通信技术如2G/3G/4G/5G、GNSS(全球导航卫星系统)、V2X(车联网通信)等在行业内被广泛使用。这些技术让汽车能够实现紧急呼叫、在线娱乐、导航等多种功能。EMC测试就是为了确保在复杂电磁环境下,汽车的通信系统仍然可以正常工作,保护驾乘者的安全。参考《QCT-基于LTE-V2X直连通信的车载信息交互系统技术要求及试验方法-1》标准10.5电磁兼容试验方法,下面将会从整车功能层面为大家解读V2X整车电磁兼容试验的过程。测试过程揭秘1. 设备准备为了进行电磁兼容试验,技
    北汇信息 2025-01-09 11:24 27浏览
  • 「他明明跟我同梯进来,为什么就是升得比我快?」许多人都有这样的疑问:明明就战绩也不比隔壁同事差,升迁之路却比别人苦。其实,之间的差异就在于「领导力」。並非必须当管理者才需要「领导力」,而是散发领导力特质的人,才更容易被晓明。许多领导力和特质,都可以通过努力和学习获得,因此就算不是天生的领导者,也能成为一个具备领导魅力的人,进而被老板看见,向你伸出升迁的橘子枝。领导力是什么?领导力是一种能力或特质,甚至可以说是一种「影响力」。好的领导者通常具备影响和鼓励他人的能力,并导引他们朝着共同的目标和愿景前
    优思学院 2025-01-08 14:54 82浏览
  • 一个真正的质量工程师(QE)必须将一件产品设计的“意图”与系统的可制造性、可服务性以及资源在现实中实现设计和产品的能力结合起来。所以,可以说,这确实是一种工程学科。我们常开玩笑说,质量工程师是工程领域里的「侦探」、「警察」或「律师」,守护神是"墨菲”,信奉的哲学就是「墨菲定律」。(注:墨菲定律是一种启发性原则,常被表述为:任何可能出错的事情最终都会出错。)做质量工程师的,有时会不受欢迎,也会被忽视,甚至可能遭遇主动或被动的阻碍,而一旦出了问题,责任往往就落在质量工程师的头上。虽然质量工程师并不负
    优思学院 2025-01-09 11:48 31浏览
  • 在过去十年中,自动驾驶和高级驾驶辅助系统(AD/ADAS)软件与硬件的快速发展对多传感器数据采集的设计需求提出了更高的要求。然而,目前仍缺乏能够高质量集成多传感器数据采集的解决方案。康谋ADTF正是应运而生,它提供了一个广受认可和广泛引用的软件框架,包含模块化的标准化应用程序和工具,旨在为ADAS功能的开发提供一站式体验。一、ADTF的关键之处!无论是奥迪、大众、宝马还是梅赛德斯-奔驰:他们都依赖我们不断发展的ADTF来开发智能驾驶辅助解决方案,直至实现自动驾驶的目标。从新功能的最初构思到批量生
    康谋 2025-01-09 10:04 26浏览
  • 在智能家居领域中,Wi-Fi、蓝牙、Zigbee、Thread与Z-Wave等无线通信协议是构建短距物联局域网的关键手段,它们常在实际应用中交叉运用,以满足智能家居生态系统多样化的功能需求。然而,这些协议之间并未遵循统一的互通标准,缺乏直接的互操作性,在进行组网时需要引入额外的网关作为“翻译桥梁”,极大地增加了系统的复杂性。 同时,Apple HomeKit、SamSung SmartThings、Amazon Alexa、Google Home等主流智能家居平台为了提升市占率与消费者
    华普微HOPERF 2025-01-06 17:23 211浏览
  • 故障现象一辆2017款东风风神AX7车,搭载DFMA14T发动机,累计行驶里程约为13.7万km。该车冷起动后怠速运转正常,热机后怠速运转不稳,组合仪表上的发动机转速表指针上下轻微抖动。 故障诊断 用故障检测仪检测,发动机控制单元中无故障代码存储;读取发动机数据流,发现进气歧管绝对压力波动明显,有时能达到69 kPa,明显偏高,推断可能的原因有:进气系统漏气;进气歧管绝对压力传感器信号失真;发动机机械故障。首先从节气门处打烟雾,没有发现进气管周围有漏气的地方;接着拔下进气管上的两个真空
    虹科Pico汽车示波器 2025-01-08 16:51 92浏览
  • 1月7日-10日,2025年国际消费电子产品展览会(CES 2025)盛大举行,广和通发布Fibocom AI Stack,赋智千行百业端侧应用。Fibocom AI Stack提供集高性能模组、AI工具链、高性能推理引擎、海量模型、支持与服务一体化的端侧AI解决方案,帮助智能设备快速实现AI能力商用。为适应不同端侧场景的应用,AI Stack具备海量端侧AI模型及行业端侧模型,基于不同等级算力的芯片平台或模组,Fibocom AI Stack可将TensorFlow、PyTorch、ONNX、
    物吾悟小通 2025-01-08 18:17 33浏览
  •  在全球能源结构加速向清洁、可再生方向转型的今天,风力发电作为一种绿色能源,已成为各国新能源发展的重要组成部分。然而,风力发电系统在复杂的环境中长时间运行,对系统的安全性、稳定性和抗干扰能力提出了极高要求。光耦(光电耦合器)作为一种电气隔离与信号传输器件,凭借其优秀的隔离保护性能和信号传输能力,已成为风力发电系统中不可或缺的关键组件。 风力发电系统对隔离与控制的需求风力发电系统中,包括发电机、变流器、变压器和控制系统等多个部分,通常工作在高压、大功率的环境中。光耦在这里扮演了
    晶台光耦 2025-01-08 16:03 80浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦