裸机编程的几种模式、架构、缺陷

news2024/10/6 22:24:10

目录

裸机编程模式/架构 1:初始化代码的编写

裸机编程模式/架构 2:轮询模式

裸机编程模式/架构 3:轮询加中断执行模式

裸机编程模式/架构 4:中断+定时器+主循环的前后台架构

裸机编程模式/架构 5:前后台 + 状态机架构 

嵌入式常见的操作系统


绝大多数嵌入式初学者都从裸机编程开始,因为它更加直观、简单。通过裸机编程,你能够直接操作硬件,代码所见即所得,调试也非常方便。相比使用操作系统,裸机编程无需掌握大量的操作系统基础知识和调度机制的常识,也不需要考虑资源共享和竞争等概念。此外,裸机编程的调试过程也更加直观。

下面是裸机编程中常见的模式和架构:

1. 引脚配置和外设初始化:裸机编程的第一步是配置芯片的引脚和初始化外设。通过配置引脚的功能和模式,你可以连接和配置各种外设,如串口、定时器等。

2. 中断处理:中断是裸机编程中处理外部事件的常见方式,如定时器溢出、串口接收等。通过设置中断向量表和编写中断服务函数,你可以对外部事件进行及时响应和处理。

3. 状态机:裸机编程中经常使用状态机来处理复杂的任务。状态机将任务拆分为不同的状态,并根据当前状态和外部事件的触发来进行状态转移和处理。

4. 轮询方式:对于较简单的任务,裸机编程可以使用轮询方式来实现。也就是不断地查询和检测外部事件的状态,并根据需要做出相应的响应和处理。

5. 低功耗模式:裸机编程中可以通过设置芯片的低功耗模式来降低系统功耗。这可以通过配置和操作控制器状态机来实现。

 6. 调试和测试:裸机编程具有直观性和简单性的特点,因此在调试和测试方面也相对容易。你可以使用调试器、示波器等工具来查看寄存器的值和程序的执行流程,以便定位问题并进行调试。 这些是裸机编程中常见的模式和架构。

对于初学者来说,裸机编程在一些相对简单的项目上具有一定的优势。然而,对于复杂的应用场景,使用操作系统和软件抽象层会更具优势。

裸机编程模式/架构 1:初始化代码的编写

裸机编程模式/架构 2:轮询模式

这些函数依次执行,全部执行完毕后再次从 第一个逻辑开始,以此不断循环。

这种模式是最简单也是最初级的模式,但其也存在很多问题。由于上述的每一个逻辑会依次执行,那么就会相互影响,因为是裸机嘛, 代码是串行执行的, 就会出现实行性不好的情况。

比如后续逻辑中存在一些交互行为,Key_Task()会判断一个按键的按下状态并做出响应,而此时还在 RTc_Task()中执行延时指令,那么整体运行就会显得非常卡顿,甚至还会因为错过用户按键的时机而导致即使按下了按键,也没有执行对应的反馈。这个实行性的问题也就是裸机的最大缺陷!

裸机编程模式/架构 3:轮询加中断执行模式

/* 按键中断的ISR */
void Key_Isr(void)
{
    do_c();
}

void main()
{
    /* 初始化 */
    /*
    .
    .
    .
    */

    while(1) {
        do_a();
        do_b();
    }

}

如上图所示, 当程序中出现交互的设计的时候, 采用外部中断确实很好的解决了按键按下立马得到响应, 这种模式其实在很多简单的应用场景下已经够用了, 那我们接下来来挑一下这种模式的缺陷。

假设现在我有这样的一个需求, 需要在while(1)的轮询模式中, do_a()和do_b()每隔一定的时间调用一次, 是不是相当于这样。

/* 按键中断的ISR */
void Key_Isr(void)
{
    do_c();
}

void d0_a()
{
    delay(100);
}

void d0_b()
{
    delay(200);
}

void main()
{
    /* 初始化 */
    /*
    .
    .
    .
    */

    while(1) {
        do_a();
        do_b();
    }

}

最初的想法是do_a()这个函数每隔100ms调用一次, 如果while(1)只有这个任务, 且不产生中断的情况, 是可以达到我们设想的要求的。但是有了中断和while(1)中不只执行一个函数的时候, 这种设计就是失败的。

裸机编程模式/架构 4:中断+定时器+主循环的前后台架构

__IO uint32_t a_tick, b_tick;

/* 按键中断的ISR */
void Key_Isr(void)
{
    do_c();
}

void d0_a()
{
    if(uwTick - a_tick < 100)
        return;
    a_tick = uwTick ;

    /*
    .
    .
    */
}

void d0_b()
{
    if(uwTick - b_tick < 200)
        return;
    b_tick = uwTick ;

    /*
    .
    .
    */
}

void main()
{
    /* 初始化 */
    /*
    .
    .
    .
    */

    while(1) {
        do_a();
        do_b();
    }

}

上述代码使用的system timer, 每隔1ms将uwTick这个全局变量加1, 使用定时器来辅助确定调用函数的时间间隔。这样, 就能保证在这种模式下while(1)中的每一个任务每隔一定的时间调用一次。

由于去掉了每个逻辑中的延时,取而代之的是标志位的判断,其执行速度是非常快的,如上图所示 ,灰色的块表示在运行判断逻辑并且没有满足运行要求。这种情况下每个逻辑都能在其指定的周期内得到执行。

这种架构在裸机编程中可以算得上一种中高级的架构,能够满足大多数不是特别复杂的需求。当然,在上图中我们可以看到 do_a 和 do_b 一个为 100 毫秒,一个为 50 毫秒,存在公倍数情况,也就是说在某一时刻,如这里的 0 毫秒和 100 毫秒,就会出现两个逻辑同时运行的场景。实际在项目中如果要求比较严格,会对这个周期进行一个控制和计算,尽量减少各逻辑同时执行的概率,避免由于同时执行的逻辑过多且过于频繁,执行时间的总和仍然会太长,从而影响整体运行稳定性的问题。

到这里请思考一下,假如 do_a 逻辑本身的执行时间就很长,比如进行一个非常复杂的运算,或者需要读取一个 G 级别的文件,导致单一逻辑的执行时间就超过了最小周期(如例子中的 50 毫秒),那即使 50 毫秒的周期到了,由于 do_a 还没运行完,do_c 也无法得到运行,这时候时间标志已经形同虚设,甚至由于此处是取余判断,假如 do_a 运行了 51 毫秒结束,do_b 在判断的时候已经是 52 毫秒,52%50 不为零,do_b 直接无法执行,时间标志甚至产生了负面影响!

虽说将 “通过取余运算判断是否可以执行的逻辑” 修改为 “设置多个时间标志(如 50ms_flag、100ms_flag等),在中断中判断满足时间就将这些标志置位,主循环中直接对这些标志进行判断的逻辑” 可以避免由于时间后延导致的无法触发逻辑执行问题,但仍然无法解决周期被影响的本质。

裸机编程模式/架构 5:前后台 + 状态机架构 

void do_a(void) {
    static unsigned char step = 0;
    if (tick % 100 == 0) {
        switch (step) {
            case 0:
                // 执行第一步
                step++;
                break;
            case 1:
                // 执行第二步
                step++;
                break;
            case 2:
                // 执行第三步
                step = 0;
                break;
            default:
                //  未知步骤,归零重来
                step = 0;
                break;
        }
    } else {
        return;
    }
}

可以观察到原先的执行方式do_a,我们将其视为一个不可拆分的逻辑,直到完整执行完成才会退出。而现在,我们将其分解为三个步骤,在执行完一个步骤后就会退出do_a函数,在下一次进入时执行下一个步骤。这样一来,能够有效缩短每次执行do_a所需的时间,大大降低了执行时间超过最小周期的可能性。主循环中的其他应用逻辑也采用了类似的状态机模式,以加快主循环的响应效率,进一步提高了裸机编程的稳定性和时间可控性。 状态机的引入使裸机编程达到了其终极形态,使其能够处理更复杂的逻辑和应用。同时,代码量和复杂度也急剧增加,特别是当主循环中存在十几个甚至几十个任务逻辑时,面对的编程难度就变得非常高。 当然,即使你能够应对极高的挑战,最终仍会遇到一个问题——随着应用逻辑的增加,同时执行大量状态机分支步骤的时间总和很难再人工进行分解,而且不幸的是,它们的执行时间总和超过了预定的周期,导致了各种问题的出现。

此时恭喜你,已经达到了裸机编程的巅峰,也是裸机编程的极限。是时候迈开脚步,进入操作系统编程的领域了!

嵌入式常见的操作系统

类Unix操作系统

物联网操作系统/实时操作系统

以及uc/os, 华为的lite os等等, 大家都可以去学习学习其操作系统提供给我们的机制, 为什么使得我们的编程, 提升了上限。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1494641.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c1-周考2

c1-第二周 9月-技能1.一个岛上有两种神奇动物&#xff0c;其中神奇鸟类2个头3只脚&#xff0c;神奇兽类3个头8只脚。游客在浓雾中看到一群动物&#xff0c;共看到35个头和110只脚&#xff0c;求可能的鸟类和兽类的只数2.构建一个长度为5的数组&#xff0c;并且实现下列要求3.构…

外包干了10天,技术退步明显。。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;2019年我通过校招踏入了南京一家软件公司&#xff0c;开始了我的职业生涯。那时的我&#xff0c;满怀热血和憧憬&#xff0c;期待着在这个行业中闯出一片天地。然而&#xff0c;随着时间的推移&#xff0c;我发现自己逐渐陷入…

Linux系统——SElinux

目录 前言 一、SELinux 的作用及权限管理机制 1.SELinux 的作用 1.1DAC 1.2MAC 1.3DAC 和 MAC 的对比 2.SELinux 基本概念 2.1主体&#xff08;Subject&#xff09; 2.2对象&#xff08;Object&#xff09; 2.3政策和规则&#xff08;Policy & Rule&#xff09;…

【牛客】VL65 状态机与时钟分频

描述 题目描述&#xff1a; 使用状态机实现时钟分频&#xff0c;要求对时钟进行四分频&#xff0c;占空比为0.25 信号示意图&#xff1a; clk为时钟 rst为低电平复位 clk_out 信号输出 Ps 本题题解是按照1000的状态转移进行的&#xff0c;不按照此状态进行&#xff0c;编译器…

小火星露谷管理器 如何一键安装模组

1 选择你喜欢的一键安装模组模板 有两种渠道获取一键安装模板&#xff1a; 通过管理器首页的新手上路按钮&#xff0c;获取sve或里奇赛德村的一键安装模板通过小火星露谷社区获取一键安装模板 1.1 小火星露谷管理器首页获取一键安装模板 管理器的首页有两个内置的一键安装模…

【Docker】提交Docker镜像改变(自定义Redis镜像)

查看某个容器发生改变(操作日志) docker diff redis A: 添加文件或目录(ADD)D:文件或者目录删除(DELETE)C:文件或者目录更改(CHANGE) 对更改的容器进行保存 我们平时使用镜像&#xff0c;会做一些自定义&#xff0c;比如配置文件的修改&#xff0c;数据的增删改等等有很多&…

Linux操作系统性能调优

Linux操作系统性能调优 前言: ​ Linux服务器运行了很多应用&#xff0c;在高负载下&#xff0c;服务器可能会出现性能瓶颈&#xff0c;例如CPU利用率过高、内存不足、磁盘I/O瓶颈等&#xff0c;从而导致系统卡顿&#xff0c;服务无法正常运行等问题。所以针对以上问题&#…

用ChatGPT计算植被归一化指数NDVI并出图的详细教程

用ChatGPT结合GIS计算植被归一化指数NDVI出图教程 用ENVI计算比较繁琐&#xff0c;如今AI的盛行&#xff0c;我们可以轻松解决计算问题&#xff0c;只需1一分钟变可以出图。 详细教学请看上方视频步骤。 更多ChatGPT教学内容请见&#xff1a;ChatGPT结合GIS&#xff1a;一分钟…

SpringCloud2023最新版本该如何进行组件选型?

前言 Developing distributed systems can be challenging. Complexity is moved from the application layer to the network layer and demands greater interaction between services. Making your code ‘cloud-native’ means dealing with 12-factor issues such as exte…

万赞提问:自动化测试的发展前景怎么样?

一个行业前景怎么样? 先看看市场需求。 下面是一部分自动化测试岗位的相关数据&#xff1a; 据统计&#xff0c;当前企业对自动化的需求已经接近一半左右了&#xff0c;请看下图&#xff1a; 自动化发展的前景如何&#xff0c;市场给出的薪资也是一个重要参考&#xff0c;来看…

Linux笔记--GCC

GCC编译器是Linux系统下最常用的CIC编译器&#xff0c;大部分Linux发行版中都会默认安装。GCC编译器通常以gcc指令的形式在终端中使用。 一.gcc指令 1.直接编译 创建a.c文件 #使用gcc对程序进行编译&#xff0c;默认得到可执行文件的文件名为a.out gcc [文件名].c gcc a.c .…

【框架学习 | 第一篇】一篇文章读懂MyBatis

文章目录 1.Mybatis介绍1.1Mybatis历史1.2Mybatis特点1.3与其他持久化框架对比1.4对象关系映射——ORM 2.搭建Mybatis2.1引入依赖2.2创建核心配置文件2.3创建表、实体类、mapper接口2.4创建映射文件2.4.1映射文件命名位置规则2.4.2编写映射文件2.4.3修改核心配置文件中映射文件…

智能驾驶规划控制理论学习06-基于优化的规划方法

目录 一、优化概念 1、一般优化问题 2、全局最优和局部最优 二、无约束优化 1、无约束优化概述 2、梯度方法 通用框架 线性搜索 回溯搜索 3、梯度下降 基本思想 实现流程 ​4、牛顿法 基本思想 实现流程 5、高斯牛顿法 6、LM法&#xff08;Le…

怎么判断晶振是否起振?晶振不起振该怎么办?

如果怀疑晶振不起振造成电路板上电不良&#xff0c;该如何进一步判定是晶振本身的不良呢?这一步的判定非常关键&#xff0c;因为若为晶振不振&#xff0c;就可以排除晶振与电路板不匹配造成电路板上电不良发生的假定。晶发电子以下介绍针对晶振单体判定的方法&#xff1a; 1.…

Python内置模块

目录 什么是模块 模块分类 通过模块创建者分类 系统内置模块 第三方模块 在线安装 离线安装 模块导入 math和random模块介绍 math模块 random模块 什么是模块 在我们编写程序时&#xff0c;需要导入包。例如随机数的产生&#xff0c;需要import random。import XXX&…

C++引入

引用不是新定义一个变量&#xff0c;而是给已经存在的变量取一个别名&#xff0c;编译器不会为了引用变量开辟内存空间&#xff0c;它和它引用的变量公用同一块内存空间。如李白被称为诗仙。李白和诗仙都是同一个人。 语法: 类型& 引用变量名(对象名)引用实体; 特性: 引用在…

一体化对账和结算平台的设计与实现

随着企业规模的扩大和业务的复杂化&#xff0c;对账和结算工作变得愈发繁琐和复杂。为了提高工作效率和降低错误率&#xff0c;许多企业开始寻求一体化对账和结算平台的解决方案。本文将探讨一体化对账和结算平台的设计原则和实施步骤&#xff0c;以及其在企业管理中的重要性。…

Canvas笔记04:绘制九大基本图形的方法,重头戏是贝塞尔曲线

hello&#xff0c;我是贝格前端工场&#xff0c;最近在学习canvas&#xff0c;分享一些canvas的一些知识点笔记&#xff0c;本期分享canvas绘制图形的知识&#xff0c;欢迎老铁们一同学习&#xff0c;欢迎关注&#xff0c;如有前端项目可以私信贝格。 Canvas是HTML5中的一个绘…

全方位碾压chatGPT4的全球最强模型Claude 3发布!速通指南在此!保姆级教学拿脚都能学会!

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &a…

HTML静态网页成品作业(HTML+CSS)——舞蹈网页设计制作(5个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码CSS部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有5个页面。 &#x1…