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

目录

裸机编程模式/架构 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.mfbz.cn/a/434360.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android开发技巧,最详细的解释小白也能听懂

今天&#xff0c;跟大家聊聊&#xff0c;Framework开发的那些事。 系统应用开发&#xff0c;现在来说&#xff0c;已经开始脱离系统&#xff0c;单独拿出来开发&#xff0c;系统定制接口&#xff0c;已提供给应用调用&#xff0c;用来增强功能。 原生的桌面&#xff0c;拨号&…

C++之获取Windows系统信息

目录 1. 操作系统版本 2. 获取CPU信息 3. 获取内存信息 4. 获取硬盘信息 5.获取网络接口信息 6.获取计算机名称、用户名 在C中&#xff0c;你可以使用Windows API函数来获取Windows系统的各种信息。以下是一些常见的API函数和示例代码&#xff0c;用于获取Windows系统信息…

深入了解 Android 中的 RelativeLayout 布局

RelativeLayout 是 Android 中常用的布局之一&#xff0c;它允许开发者基于子视图之间的相对位置来排列界面元素。在这篇博客中&#xff0c;我们将详细介绍 RelativeLayout 的各种属性&#xff0c;并提供代码示例和解释。 第一个示例 <RelativeLayoutandroid:layout_width…

腾讯云服务器99元一年是真的吗?只要61元!

腾讯云服务器99元一年是真的吗&#xff1f;假的&#xff0c;不要99&#xff0c;只要61元&#xff01;又降价了&#xff01;腾讯云服务器多少钱一年&#xff1f;61元一年起&#xff0c;2核2G3M配置&#xff0c;腾讯云2核4G5M轻量应用服务器165元一年、756元3年&#xff0c;4核16…

数据分析-Pandas数据的直方图探查

数据分析-Pandas数据的直方图探查 数据分析和处理中&#xff0c;难免会遇到各种数据&#xff0c;那么数据呈现怎样的规律呢&#xff1f;不管金融数据&#xff0c;风控数据&#xff0c;营销数据等等&#xff0c;莫不如此。如何通过图示展示数据的规律&#xff1f; 数据表&…

网络编程 24/3/6 作业

1、数据库的增删改 #include <myhead.h> int main(int argc, const char *argv[]) {//定义数据库句柄指针sqlite3 *kdbNULL;//打开数据库&#xff0c;不存在则创建if(sqlite3_open("./my.db",&kdb)!SQLITE_OK){printf("sqlite3_open error\n");…

如何解决无法联网的IP代理问题

目录 前言 一、检查网络连接问题 二、检查IP代理配置问题 三、更换IP代理 四、使用IP池 总结 前言 在进行网络爬虫、数据采集等涉及到频繁请求的操作中&#xff0c;IP代理是一个必不可少的工具。通过使用IP代理&#xff0c;我们可以隐藏真实的IP地址&#xff0c;防止被目…

解决DBeaver执行脚本报错No active connection

解决DBeaver执行脚本报错No active connection 1、报错问腿 2、问题解决 2.1、右键点击该数据库&#xff0c;选择SQL编辑器&#xff0c;选择新建SQL编辑器&#xff0c;然后将sql语句复制过去。 或者左击选中数据库后直接使用快捷键 Ctrl] 2.2、在Project-General中找到Scr…

Nginx入门

Nginx入门 本章目标 Ninux简介 安装 Nginx配置 负载均衡 静态化处理 本章内容 一、什么是Nginx&#xff1f; Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔赛索耶夫为俄罗斯访问量第二的http:/…

Claude3深夜震撼发布!模型特点分析,附使用教程

Claude3深夜震撼发布&#xff01;模型特点分析&#xff0c;附使用教程 引言 最新发布的Claude3引起了广泛关注&#xff0c;这次发布一举推出了三个不同类型的模型&#xff0c;分别是Claude 3 Haiku、Claude 3 Sonnet和Claude 3 Opus。每个模型都具有独特的特点和能力&#xff…

逆向案例五、爬取b站评论,表单MD5加密

1.便捷写爬虫网站&#xff1a; Convert curl commands to code 使用流程&#xff1a;又点击想要抓的包&#xff0c;复制URL&#xff08;base&#xff09;格式复制 在上面链接中粘贴即可 2.找到含有评论的包&#xff08;即main?oid)&#xff1a;观察表单发现两处参数在变化&…

protobuf 25.3在window的安装

1、下载地址 protobuf 25.3 下载完后&#xff0c;执行下面语句更新第三方库 git submodule update --init --recursive2、 cmake安装 注意&#xff1a;编译需要在vs2019及以上&#xff0c;因为abseil-cpp的编译需要是vs2019及以上

Nacos基础(注册中心和配置中心)

文章目录 参考文章一、 配置管理1、添加依赖2、添加配置信息3、创建例子4、在Nacos创建配置命名空间&#xff1a;配置集配置id配置组加载多配置文件 二、注册中心2.1、添加依赖2.2、添加项目Nacos配置2.3、添加服务发现注解这个时候你启动Nacos 如果遇到跳转链接失效请在评论区…

numpy——基础知识(创建/类型/形状/运算)(python)

简介 NumPy 是一个 Python 包。它代表 Numeric Python。 它是一个由多维数组对象和用于处理数组的例程集合组成的库。 Numeric&#xff0c;即 NumPy 的前身&#xff0c;是由 Jim Hugunin 开发的。 也开发了另一个包 Numarray &#xff0c;它拥有一些额外的功能。 2005年&#…

2024年腾讯云发红包了,可用于抵扣订单金额,你们领了吗?

在2024年腾讯云新春采购节优惠活动上&#xff0c;可以领取新年惊喜红包&#xff0c;打开活动链接 https://curl.qcloud.com/oRMoSucP 会自动弹出红包领取窗口&#xff0c;如下图&#xff1a; 腾讯云2024新春采购节红包领取 如上图所示&#xff0c;点击“领”红包&#xff0c;每…

【DevSecOps】2024 年需要警惕的 10 大 Web 应用程序安全威胁

【DevSecOps】2024 年需要警惕的 10 大 Web 应用程序安全威胁 由于 2023 年出现了许多创新,我们之前所了解的许多内容都发生了巨大变化;随着其中一些重大变化,威胁格局也发生了转变,一些旧威胁减少了,一些新威胁增加了。 技术每天都在不断变化,当我们谈论技术和相关威胁…

Redis核心数据结构之SDS和链表(三)

SDS与C字符串的区别 二进制安全 C字符串中的字符必须符合某种编码(比如ASCII),并且除了字符串的末尾之外&#xff0c;字符串里面不能包含空字符&#xff0c;否则最先被程序读入的空字符将被误认为是字符串结尾&#xff0c;这些限制使得C字符串只能保存文本数据&#xff0c;而…

数据结构(七)——线性表的基本操作

&#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是尘觉&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我的动力&#x1f609; 在csdn获奖荣誉: &#x1f3c6;csdn城市之星2名 ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ …

Pytorch学习 day05(RandomCrop、Transforms工具使用总结)

RandomCrop 将PIL或Tensor格式的输入图片&#xff0c;随机裁剪指定尺寸的部分输入尺寸可以为序列或单个整形数字代码如下&#xff1a; from PIL import Image from torchvision import transforms from torch.utils.tensorboard import SummaryWriterimg Image.open("i…

CSS常见用法 以及JS基础语法

CSS简介 首先我们要明白css对网页的页面效果就类似于化妆的效果,使得页面更好看 我们需要明白的就是CSS怎么使用即可 首先CSS的基本语法是<style></style>标签来修改 基本语法规范是选择器n条选择规范 例如 <style>p{color : red;} </style> 这里就是将…
最新文章