0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

浅述单片机程序的整体框架设计的思路体会

strongerHuang 来源:嵌入式客栈 作者:嵌入式客栈 2021-06-27 11:34 次阅读

一些初学单片机的同学,刚刚入手做单片机开发,还没有涉及到使用RTOS,且刚入手直接上RTOS可能会有些难度,有的使用的相对较老单片机资源还有限,也不适合跑RTOS。

或者使用RTOS,在整体思路上比较迷茫,不知从何入手,所以本文来聊聊我对单片机程序的整体框架设计的一些思路体会。

为啥要讨论架构

单片机系统开发人员的目标之一是在编程环境中创建固件,以实现低成本系统、软件可靠性以及快速的开发迭代时间。实现这种编程环境的最佳方法实践是使用统一的固件架构体系结构,该体系结构在产品开发过程中充当框架并支持“固件模块化”,或称为子系统。

如果不采用统一的设计架构,那么其业务需求耦合关系复杂,不采用先设计-后开发的方法论,想到哪里写到哪里,则程序后期维护将变得异常艰辛,而引入潜在bug/缺陷的风险也将大大增加,且不具备多人协同开发的可能。

可以结合固件模块化、可测试性和兼容性的正确组合的设计体系架构结构应用于任何固件开发项目,以最大程度地提高代码可复用性,加快固件调试速度并提高固件可移植性。

模块化架构设计?

模块化编程将程序功能分解为固件模块/子系统,每个模块执行一个功能,并包含完成该功能所需的所有源代码和变量。

模块化/子系统化有助于协调团队中许多人的并行工作,管理项目各个部分之间的相互依赖关系,并使设计人员、系统集成人员能够以可靠的方式组装复杂的系统。具体来说,它可以帮助设计人员实现和管理复杂性。

随着应用程序的大小和功能的增长,需要模块化才能将它们分成单独的部分(无论是作为“组件”,“模块”还是“子系统”)。然后,每个这样分离的部分就成为模块化体系结构的一个元素。这样,可以使用定义明确的界面隔离和访问每个组件。此外,模块化编程可提高固件的可读性,同时简化固件的调试,测试和维护。

即便是一个人独立开发一个项目,这样做依然在代码的调试、可读性、可移植性方面是最佳实践的整体策略。如果代码设计良好,则在其他项目可以轻松应用。而且模块经过上一项目的测试验证,在新的项目中再次应用其缺陷风险将大幅降低。

所以每做一个项目,以这种策略不断积累模块“轮子”组件,随着经验的增长,积累的“轮子”就越来越多,也越来越好。所以其优点是显而易见的,否则每做一个项目,都从轮子造起,开发时间长不说,开发水平也得不到提高,重复性工作也很枯燥。比如前文中谈到的非易失存储管理子系统,如设计良好,就变成一个可靠的可移植的轮子。这段话请深入理解,并拿走不谢!

固件模块原理

固件开发中模块化编程的基本概念是创建固件模块。从概念上讲,模块代表关注点分离。在计算机科学中,关注点分离是将计算机程序分解为功能很少重叠的独特功能的过程。关注点是程序的任何关注点或功能,并且与功能或行为同义。关注点分离的发展传统上是通过模块化和封装来实现的,其实也就是解耦思想。

固件模块可以分为几种类型:

与很多上层用户模块都有关的代码被实现为单独的固件模块。常见的如底层硬件相关的抽象实现。例如,hal_adc.c 是ADC用户模块的固件模块,而hal_timer.c是Timer用户模块的固件模块。

用于特定纯软件算法的代码被实现为单独的固件模块。例如,alg_filter.c是执行软件过滤器(例如中值过滤器,均值过滤器或加权均值过滤器、IIR/FIR滤波)的固件模块。

特定应用程序的代码实现为单独的固件模块。例如,app_battery.c是电池充电器应用程序的固件模块。特定工具的代码实现为单独的固件模块。例如,debug_print.c是用于实现日志打印功能的固件模块。

。。。。。。

实施估计模块化设计的一些规则:

所有与模块相关的功能都应集成到单个源文件中,这是高内聚的体现。

模块对外提供一个头文件,该文件声明了该模块的所有资源(硬件依赖/宏/常量/变量/函数)。尽量用struct将紧密相关的变量进行集总封装。

在源文件中包括自检代码部分,以实现该模块模块的所有自检功能。

固件模块的接口应经过精心设计和定义。

由于固件取决于硬件,因此需要在源文件头中明确提及硬件的相关性。比如利用宏将硬件依赖转定义,或者利用函数将基本操作进行封装。则在新的架构体系,仅仅需要移植这部分实现即可使用。

通常,固件模块可供其他团队成员在其他项目中使用。可能涉及到管理更改,缺陷修复、所有者应维护模块。源文件头应包含“作者”和“版本”信息

固件在某种程度上取决于编译器。源文件头中应声明基于什么开发环境进行过验证,以指定编译器或与IDE相关的信息。

需要注意的是,模块化设计会引入一些调用开销,也可能增加固件尺寸大小。在实际实现时,折中考量。不要过度模块化,所以建议采用高内聚、低耦合的实现策略。在前面文章中有谈到过的呼吸机PB560的设计,看过其代码,本打算解读一下其代码设计,但读下来发现,其设计过度模块化了,没有实现高内聚的思想。其源代码很多源文件仅仅实现了一个函数,而不是把一类问题集中抽象实现,后来就放弃了其代码解读。

如何拆分模块?

做工程开发,一定是需求驱动的。第一件事需要对需求有比较清晰的认知,然后才能设计一个比较合理的框架。我们需要实现什么?大致总体设计过程策略我的基本采用如下图所示思路(我比较喜欢绘图,图会让人比较直观)

f64b6982-d6f4-11eb-9e57-12bb97331649.png

问自己第一个问题是:这个项目要实现什么主要功能?这个来自哪里?如果是实际产品开发,则可能来自市场的需求,如果是自己的DIY项目,也一定会YY出一个大致的想法?总之不管源自何方,需求总要先梳理清楚。那么需求一般意义上包含哪些呢?

哪些是硬件IO接口需求,比如开关量输入,ADC采样,I2C/SPI通信等等

哪些是业务逻辑需求,比如要采集一个传感器量数据,控制一个加热装置,那么这是高内聚的需求。

哪些是算法相关的技术需求,比如产品中哪些信号需要滤波处理,哪些需要做频域分析等等。

是否有对外的通信协议需求。

是否有业务数据需要历史存储,或者设备参数需要掉电保存

是否需要有日志打印需求。

。。。。。。。。

不一而足。

结合固件模块原理以及相关指导原则,那么将相关性高的需求,抽象实现在一系列的模块中,在由这一系列模块配合实现某个相关性高的业务需求,再进一步这些模块就变成一个子系统。多个子系统在main.c的调度下,协调完成产品的整体功能。

如何集成调度

对于某些不使用RTOS的应用而言,可以使用如下的框架进行:

void main(void){ /*各模块初始化*/ init_module_1(); init_module_2(); 。。。。 while(1) { /*实现一个定时调度策略*/ if(timer50ms) { timer50ms = 0; app_module_1(); } if(timer100ms) { timer100ms = 0; app_module_2(); } /*异步请求处理,如中断后台处理*/ if(flag1) { communication_handler(); } 。。。。。 }}

对于基于RTOS的集成实现举例:

void task1(void){ /*处理子系统相关的初始化*/ init_task1(); while(1) { /*应用相关调用*/ task1_mainbody(); 。。。。 }}。。。.void taskn(void){ /*处理子系统相关的初始化*/ init_taskn(); while(1) { /*应用相关调用*/ taskn_mainbody(); 。。。。 }}

void main(void){ /*一些基本硬件相关初始化,比如IO,时钟,OS tick定时器等*/ init_hal(); 。。。。。。 /*一些基本RTOS初始化*/ init_os(); /*任务创建*/ os_creat(“task1”,task1,栈设置,优先级,。。。); 。。。。。。 os_creat(“taskn”,taskn,栈设置,优先级,。。。); /*启动OS调度器,交由OS调度管理应用任务*/ os_start();}

具体不同的RTOS,其函数名各有不同,但大致思路一般都差不多。

总结一下

本文从为什么需要模块化设计整体架构,到这样做的好处,以及具体做的一些指导原则,再到实际中如何实现,怎么做到高内聚低耦合,提供了一些个人工作中的体会以及思路。

同时对于裸机程序整体框架、基于RTOS的集成框架做了两个demo,基本能解决大部分的框架思路问题。将前文中的一些个人推崇的原则,在加粗总结下:

所有与模块相关的功能都应集成到单个源文件中,这是高内聚的体现。

模块对外提供一个头文件,该文件声明了该模块的所有资源(硬件依赖/宏/常量/变量/函数)。尽量用struct将紧密相关的变量进行集总封装。

在源文件中包括自检代码部分,以实现该模块模块的所有自检功能。

固件模块的接口应经过精心设计和定义。

由于固件取决于硬件,因此需要在源文件头中明确提及硬件的相关性。比如利用宏将硬件依赖转定义,或者利用函数将基本操作进行封装。则在新的架构体系,仅仅需要移植这部分实现即可使用。

通常,固件模块可供其他团队成员在其他项目中使用。可能涉及到管理更改,缺陷修复、所有者应维护模块。源文件头应包含“作者”和“版本”信息。

固件在某种程度上取决于编译器。源文件头中应声明基于什么开发环境进行过验证,以指定编译器或与IDE相关的信息。

极力建议采用先设计-后开发的模式,比较忌讳逐步debug,想到哪里写到哪里。当然对于新手学习而言,后一种模式,可以逐步渐进迭代,也可以比较快的增长经验。当然如何取舍,全凭个人意愿。相信您如深入阅读,细细体会,应该从设计思想上得到些领悟,有所提高。

编辑:jq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 单片机
    +关注

    关注

    6032

    文章

    44522

    浏览量

    633210
  • adc
    adc
    +关注

    关注

    98

    文章

    6443

    浏览量

    544135
  • 函数
    +关注

    关注

    3

    文章

    4308

    浏览量

    62442
  • RTOS
    +关注

    关注

    22

    文章

    809

    浏览量

    119442

原文标题:详述单片机固件模块化架构设计

文章出处:【微信号:strongerHuang,微信公众号:strongerHuang】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    单片机怎么写入程序

    程序通常涉及以下几个步骤: 选择单片机和开发环境 : 确定项目需求,选择合适的单片机型号。 安装相应的开发环境,如Keil、IAR、MPLAB等。 硬件连接 : 将单片机连接到开发板或
    的头像 发表于 10-21 11:21 282次阅读

    单片机的中断机制

    单片机的中断机制是一种重要的处理方式,它允许单片机在执行主程序的过程中,能够暂停当前任务,转而处理外部或内部紧急事件。这种机制极大地提高了系统的响应速度和处理能力,使得单片机在各种应用
    的头像 发表于 10-17 18:03 391次阅读

    keil可以读出单片机程序

    Keil是一款广泛应用于单片机程序开发的软件,它提供了包括C编译器、宏汇编、连接器、库管理和一个功能强大的仿真调试器等在内的完整开发方案。然而,关于Keil是否能直接“读出”单片机程序
    的头像 发表于 09-02 10:32 723次阅读

    单片机烧录程序用什么软件

    单片机烧录程序单片机开发过程中的一个重要环节,涉及到将编写好的程序代码通过烧录器写入单片机的ROM中,以实现对
    的头像 发表于 09-02 10:05 916次阅读

    单片机烧录程序可以重新烧吗

    单片机(Microcontroller Unit, MCU)是一种集成电路芯片,它将计算机的CPU、存储器、输入/输出接口等集成在一块芯片上,用于控制各种电子设备。单片机烧录程序是指将编写
    的头像 发表于 09-02 10:04 966次阅读

    单片机烧录程序的线比单片机上的少还能烧录吗

    单片机烧录原理 单片机烧录是指将编写好的程序代码通过一定的方式传输到单片机的存储器中,使其能够按照程序的指令运行。这个过程通常需要使用烧录器
    的头像 发表于 09-02 09:54 419次阅读

    单片机烧录程序的基本步骤是什么

    单片机烧录程序单片机开发过程中非常重要的一步,它涉及到将编写好的程序代码通过一定的方式传输到单片机内部的存储器中,使
    的头像 发表于 09-02 09:47 820次阅读

    stm32单片机烧录程序会擦除原来的程序

    在STM32单片机烧录程序的过程中, 通常情况下会擦除原来的程序 ,并将新程序写入单片机的闪存(Flash)中。这一过程是通过烧录工具(如S
    的头像 发表于 09-02 09:42 1500次阅读

    单片机中断功能及其应用

    单片机中断功能及其应用  单片机中断是指在程序执行过程中,根据特定的条件或事件自动暂时中断当前程序的执行,转而执行特定的中断服务程序。中断是
    的头像 发表于 01-30 14:45 5022次阅读

    单片机芯片怎么写入程序

    单片机芯片的程序写入是通过将程序代码写入单片机芯片的非易失性存储器(如Flash)中实现的。 在计算机科学和电子工程领域,单片机是一种集成电
    的头像 发表于 01-05 14:06 7954次阅读

    51单片机8位数码管显示学号程序编写思路

    51单片机是一种非常经典的单片机型号,具有广泛的应用领域。我们可以利用51单片机的IO口来控制8位数码管的显示,实现学号的显示功能。下面提供的详细步骤: 第一步:硬件连接 首先,我们需要将8位数
    的头像 发表于 01-04 15:21 7170次阅读

    单片机程序读取需要什么工具

    单片机程序读取的工具可以分为硬件工具和软件工具两类。下面将详细介绍这些工具的种类、功能和使用方法。 一、硬件工具 单片机开发板:单片机开发板是最基本的硬件工具之一,它通常由主控芯片、输
    的头像 发表于 01-02 18:10 3669次阅读

    keil5mdk怎么编写51单片机程序

    编写51单片机程序需要以下步骤: 编写程序框架:首先,你需要定义程序所需要的变量和常量,以及设置中断向量和寄存器的初始状态。这些信息可以根据
    的头像 发表于 12-25 14:54 1460次阅读

    单片机处理模拟量的程序流程是如何的

    单片机处理模拟量的程序流程是一种常见的应用场景,单片机通常被用来处理各种类型的传感器信号,例如温度传感器、压力传感器、光敏传感器等。在本文中,我将详尽地描述单片机处理模拟量的
    的头像 发表于 12-15 09:28 1412次阅读

    pic单片机汇编程序实例

    以下是一个简单的PIC单片机汇编程序示例,这个程序将实现一个LED闪烁的功能。 首先,我们需要了解PIC单片机的结构和指令集。PIC单片机
    的头像 发表于 12-14 11:01 1795次阅读