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

目录

裸机编程模式/架构 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.mzph.cn/news/725352.shtml

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

相关文章

常见的几种echarts类型

一&#xff1a;折线图 let option {tooltip: {},animation: false,grid: {top: "20%",bottom: "33%", //也可设置left和right设置距离来控制图表的大小left: 5%,right: 5%},xAxis: {boundaryGap:false,data: [1,2,3,4,5],axisLine: {show: true, //隐藏X轴…

Leetcode : 147. 对链表进行插入排序

给定单个链表的头 head &#xff0c;使用 插入排序 对链表进行排序&#xff0c;并返回 排序后链表的头 。 插入排序 算法的步骤: 插入排序是迭代的&#xff0c;每次只移动一个元素&#xff0c;直到所有元素可以形成一个有序的输出列表。 每次迭代中&#xff0c;插入排序只从输…

leetcode 3.5

普通数组 1.最大子数组和 最大子数组和 前缀和pre 动态规划 pre保留的是当前包含了当前遍历的最大的前缀和&#xff0c;如果之前的pre 对结果有增益效果&#xff0c;则 pre 保留并加上当前遍历, 如果pre 对结果无增益效果&#xff0c;需要舍弃&#xff0c;则 pre 直接更新为…

贝叶斯树定义与构建的寻行数墨

Title: 贝叶斯树定义与构建的寻行数墨 —— Notes for “The Bayes Tree: An Algorithmic Foundation for Probabilistic Robot Mapping” 文章目录 I. 前言II. 贝叶斯树的定义1. 贝叶斯树的背景2. 贝叶斯树的特点3. 贝叶斯树的定义 III. 贝叶斯树的构建1. 贝叶斯树的构建算法2…

CTP-API开发系列之接口对接准备

CTP-API开发系列之接口对接准备 CTP-API开发系列之接口对接准备CTP-API文件清单CTP-API通用规则命名规则Spi与Api CTP-API通讯模式开发语言选择 CTP-API开发系列之接口对接准备 CTP-API文件清单 文件名说明ThostFtdcTraderApi.h交易接口&#xff0c;C头文件&#xff0c;包括 …

紧跟潮流,再整一个短剧搜索网站

前面一大批的转存量太大了&#xff0c;有些小伙伴用不上&#xff0c;所以整了个搜索网站&#xff0c;输入关键词搜索即可。 搜短剧 http://wjsyyx.top/sdj/ 界面依旧主打朴实无华&#xff0c;搜索一步到位。 ▼ 网站界面 ▼ 搜索结果 剩下的就都会了。 ▼ 往期推荐 【Python】…

Ubuntu 安装谷歌拼音输入法

一、Fcitx 安装 在Ubuntu 下&#xff0c;谷歌拼音输入法是基于Fcitx输入法的。所以&#xff0c;首先需要安装Fcitx。一般来说&#xff0c;Ubuntu最新版中都默认安装了Fcitx&#xff0c;但是为了确保一下&#xff0c;我们可以在系统终端中运行如下命令&#xff1a; sudo apt ins…

【WPS】Excel查重数据对比

数据对比 数据对比标记重复数据查询过滤处理

使用pyannote-audio实现声纹分割聚类

使用pyannote-audio实现声纹分割聚类 # GitHub地址 https://github.com/MasonYyp/audio1 简单介绍 pyannote.audio是用Python编写的用于声纹分割聚类的开源工具包。在PyTorch机器学习基础上&#xff0c;不仅可以借助性能优越的预训练模型和管道实现声纹分割聚类&#xff0c;还…

你知道katalon studio 如何完成 get/post 请求发送吗?

katalon studio作为目前最火的自动化测试工具之一&#xff0c;不仅仅只能完成webUI自动化&#xff0c;更是能完成api、app以及桌面应用程序的自动化测试。 本文将讲解一下katalon studio是如果完成接口测试的。 请求发送 get请求 1、先在object repository里new一个请求 2、…

惯性导航 | 航迹推算与gazebo仿真

惯性导航 | 航迹推算与gazebo仿真 IMU数据进行短时间航迹推算代码gazebo中进行仿真测试 IMU数据进行短时间航迹推算 代码 声明一个用与 IMU积分的类 &#xff0c;来实现 短时间内的航迹推算 类的名字叫 IMUIntegration 构造函数 有三个变量进行私有变量初始化 重力、初始陀螺…

Python调用edge-tts实现在线文字转语音

edge-tts是一个 Python 模块&#xff0c;允许通过Python代码或命令的方式使用 Microsoft Edge 的在线文本转语音服务。 项目源码 GitHub - rany2/edge-tts: Use Microsoft Edges online text-to-speech service from Python WITHOUT needing Microsoft Edge or Windows or an…

Docker_设置docker服务以及容器开机自启

本文目录 docker服务开机自启动查询docker服务开机自启动状态将docker服务设置为开机自启动取消docker服务开机自启动 容器开机自启动修改docker容器为自启动容器启动时设置自启动-docker版容器启动时设置自启动-docker-compose版 docker服务开机自启动 查询docker服务开机自启…

33岁大马女星赴港打拼十年终夺「最佳女配」。

近两年有多位「大马女神」在香港走红&#xff0c;最火的莫过于甜美可人、样靓身材正的林明祯&#xff0c;不仅在电影圈有好成绩&#xff0c;还成了广告界的宠儿。 不过说到演技最精湛的「大马女神」&#xff0c;就不得不提近年在香港电影圈炙手可热的廖子妤&#xff0c;前年她凭…

C++_布隆过滤器

目录 1、布隆过滤器的用法 2、布隆过滤器的查找 3、布隆过滤器的删除 4、布隆过滤器的实现 结语 前言&#xff1a; 布隆过滤器是一种概率型数据结构&#xff0c;采用的是哈希思想&#xff0c;他在位图的原有基础上做了升级&#xff0c;因为位图处理不了数据为字符串的情…

安全增强型 Linux

书接上篇 一查看selinux状态 SELinux的状态&#xff1a; enforcing&#xff1a;强制&#xff0c;每个受限的进程都必然受限 permissive&#xff1a;允许&#xff0c;每个受限的进程违规操作不会被禁止&#xff0c;但会被记录于审计日志 disabled&#xff1a;禁用 相关命令…

springboot集成logback打印彩色日志

一、logback介绍 Logback是由log4j创始人设计的另一个开源日志组件,官方网站&#xff1a; logback.qos.ch。它当前分为以下三个模块&#xff1a; logback-core&#xff1a;其它两个模块的基础模块。logback-classic&#xff1a;它是log4j的一个改良版本&#xff0c;同时它完整实…

板级PDN(电源分配网络)设计要点综述

目录 目标阻抗去耦方法 确定目标阻抗 确定目标频点 VRM 去耦电容 安装电感 平面电容 总结 去耦电容 PCB叠层设计 扩展阅读 目标阻抗去耦方法 确定PCB去耦方案的策略是使用频域目标阻抗法&#xff0c;通过层间电容和分立电容器组合的使用&#xff0c;保证电源轨阻抗在…

TypeScript学习笔记(上):TypeScript的介绍、安装及常用类型

我对TypeScript的理解就是&#xff0c;TypeScript是增加了类型校验的JavaScript&#xff0c;能够把运行期错误提升至编译期 TypeScript是什么&#xff1f; TypeScript&#xff08;简称&#xff1a;TS&#xff09;是 JavaScript 的超集&#xff08;JS 有的 TS 都有&#xff09…

【LeetCode】升级打怪之路 Day 15:二叉树解题的思维模式 —— 遍历、分解问题

今日题目&#xff1a; 226. 翻转二叉树101. 对称二叉树114. 二叉树展开为链表 目录 LC 226. 翻转二叉树 【easy】LC 101. 对称二叉树 ⭐⭐⭐LC 114. 二叉树展开为链表 ⭐⭐⭐ 今天的题目主要是对二叉树递归遍历的应用&#xff0c;东哥带你刷二叉树&#xff08;思路篇&#xff0…