嵌入式软件分层框架设计,举个例子

点击蓝字

f4112894b437ff817cd9b0f33512d2af.png

关注我们

因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享

来源于网络,侵删

前言

为了能够使得产品得到更好的开发速度与以后更好的迭代和移植,框架分层是很有必要的。但如对于中小型项目严格遵循这些原则,势必会消耗过多精力去思考怎么设计系统,这是一个抉择的过程。

一、框架分层是什么?

在嵌入式架构中:一般分为硬件架构与软件架构。这里是嵌入式软件设计,也是大多数人接触的设计。

所谓的分层,也可以理解为模块化的设计,但是框架分层的设计一般会遵循以下几点原则

  • 每个模块提供的接口要统一,只能增加,不能改。在设计的时候得考虑好兼容性,使用起来麻烦不麻烦等等。

  • 同一级模块与模块之间相互独立,互不影响,不能相互调用,只能调用它下一层的接口。

  • 不同模块构成不同的层,层与层之间不能跨级调用。

  • 模块中又可以继续分层,可以增减分层,这个需要根据自己的项目需求来进行设置。

一般可以分为:硬件驱动层–>功能模块层–>应用接口层–>业务逻辑层–>应用层

让我们看看这个经典的图,简单了解一下框架分层。

0b32684ee380e1f394e60626bd4d8bca.png

从图中不难观察出,设计都是遵循设计的原则的,层与层之间不能相互调用。

二、框架分层的优劣势

1.优势

  • 单一职责:每一层只负责一个职责,职责边界清晰,不会造成跨级调用,在大型项目中,每个人负责的部分不一样,加快整个项目的开发进度。

  • 高内聚:分层是把相同的职责放在同一个层中,所有业务逻辑内聚在领域层。在测试的时候,只需要测试该领域的层即可,一般不需要考虑其他层的问题。

  • 低耦合:依赖关系非常简单,上层只能依赖于下层,没有循环依赖。

  • 易维护:面对变更容易修改。在平台更改后,如果只是改了驱动,其他层都不需要动,只需要把驱动层给更改,其他层的功能不需要更改。

  • 易复用:如果功能模块变动了,只需升级相应的功能模块,其他的模块不受影响,应用层也不受影响。

如果想要更好地利用这些优势,那得严格遵循设计的原则。

2.劣势

  • 开发成本高:因为多层分别承担各自的职责,增加功能需要在多个层增加代码,这样难免会增加开发成本。但是合理的抽象,根据自己的项目设置合理的层级是能降低开发成本的。

  • 性能略低:业务流需要经过多层代码的处理,性能会有所消耗。

  • 可扩展性低:因为上下层之间存在耦合度,有些功能变化可能涉及到多层的修改。

有优势也有劣势,需要根据自己的项目需要,进行部分的取舍,如果是中小型项目,可以不需要分层(如果不考虑到以后会迭代的话),或者部分分层就够了,既能利用框架分层的部分优势,也能降低开发成本。

三、一个简单的例子

由于主要讨论的是软件框架的分层设计,这里使用STM32cubemx来进行硬件的初始化,尽可能少考虑到硬件驱动的部分。

以一个智能小灯的作为例子:

功能

  • 按键控制小灯的亮度,等级为:0,1,2,3

  • 串口可以观察当前小灯亮度等级

  • OLED也可以观察当前小灯亮度等级

下面就是这个例子的一个简单的图示。

这和例子比较简单,业务逻辑层完全可以去除,直接从应用层调用功能模块层,加快开发进度。

73b5611bbc5d881bd5da75b877423e07.png

最后附上一点点代码,就是关于LED如何进行在不同层进行封装

硬件层

首先看HAL库生成提供的代码,这个就是LED硬件层,也就是GPIO层,cubemx已经生成了,在stm32f4xx_hal_gpio.c(我用的是F4),以及有相应的GPIO的驱动了,这里不需要我们进行处理。

1f873bffccab6d4420e8ab2df40bf485.png

硬件层驱动层

看LED部分的驱动,也就是下面的这两个函数

void MX_TIM1_Init(void);
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle);
12
/* TIM1 init function */
void MX_TIM1_Init(void)
{/* USER CODE BEGIN TIM1_Init 0 *//* USER CODE END TIM1_Init 0 */TIM_ClockConfigTypeDef sClockSourceConfig = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};TIM_OC_InitTypeDef sConfigOC = {0};TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};/* USER CODE BEGIN TIM1_Init 1 *//* USER CODE END TIM1_Init 1 */htim1.Instance = TIM1;htim1.Init.Prescaler = 168-1;htim1.Init.CounterMode = TIM_COUNTERMODE_UP;htim1.Init.Period = 10000;htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim1.Init.RepetitionCounter = 0;htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;if (HAL_TIM_Base_Init(&htim1) != HAL_OK){Error_Handler();}sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK){Error_Handler();}if (HAL_TIM_PWM_Init(&htim1) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK){Error_Handler();}sConfigOC.OCMode = TIM_OCMODE_PWM1;sConfigOC.Pulse = 0;sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK){Error_Handler();}sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;sBreakDeadTimeConfig.DeadTime = 0;sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK){Error_Handler();}/* USER CODE BEGIN TIM1_Init 2 *//* USER CODE END TIM1_Init 2 */HAL_TIM_MspPostInit(&htim1);}void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(timHandle->Instance==TIM1){/* USER CODE BEGIN TIM1_MspPostInit 0 *//* USER CODE END TIM1_MspPostInit 0 */__HAL_RCC_GPIOE_CLK_ENABLE();/**TIM1 GPIO ConfigurationPE11     ------> TIM1_CH2*/GPIO_InitStruct.Pin = GPIO_PIN_11;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);/* USER CODE BEGIN TIM1_MspPostInit 1 *//* USER CODE END TIM1_MspPostInit 1 */}}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798

对其进行封装,就是我们想要的Led小灯的驱动了,到时候如果需要,改驱动直接改底层就行了。

void Led_init()
{MX_TIM1_Init();HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);//启动PWM
}
12345

功能模块层

根据上面的需求要求划分为四个不同等级,同时也需要对LED驱动进行进一步封装,以便满足层与层之间不能跨级调用的原则(到这里是不是发现很麻烦!小项目就不要用啦!)

//ARR计数器设置值为0~10000
#define LED_GRADE_0  0
#define LED_GRADE_1  3000
#define LED_GRADE_2  6000
#define LED_GRADE_3  10000
//设置LED亮度功能
void Led_Set_brightness(int Grade)
{if(Grade==LED_GRADE_0){__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, Grade);HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_2);//关闭PWM输出}else{HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2, Grade);__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, Grade);}
}//启动LED功能
void Led_Start()
{Led_init();
}
12345678910111213141516171819202122232425

业务逻辑层

这里仅仅以启动层为例:

void Start_app()
{Led_Start();
}
1234

应用层

基本流程是:启动业务逻辑->读取业务逻辑->处理业务逻辑->显示业务逻辑。

四、总结

到这里,一个简单的例子也解释完毕了,通过LED这个简单的例子,已经大概了解到这个设计的复杂了,如果是大型项目,运用起来会很爽,小型的话完全没必要这样分层,太麻烦了,严重减慢开发效率,时间都用在思考如何进行分层才能符合框架分层的原则。

如果你年满18周岁以上,又觉得学【C语言】太难?想尝试其他编程语言,那么我推荐你学Python,现有价值499元Python零基础课程限时免费领取,限10个名额!
▲扫描二维码-免费领取

戳“阅读原文”我们一起进步

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

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

相关文章

mockito入门_Mockito入门

mockito入门本文是我们名为“ 用Mockito测试 ”的学院课程的一部分。 在本课程中,您将深入了解Mockito的魔力。 您将了解有关“模拟”,“间谍”和“部分模拟”的信息,以及它们相应的存根行为。 您还将看到使用测试双打和对象匹配器进行验证的…

windows7安dns服务器_在Windows 7 上安装DNS服务器bind9方法详解

本文主要介绍在WIN7上利用ntbind部署DNS服务器的方法。ntbind是Bind的Windows版本,1.下载BIND9.11下载地址:http://ftp.isc.org/isc/bind9/9.11.0rc3/。我的系统是window 7 64位需要下载BIND9.11.0rc3.x64.zip,建议下载9.11以上的版本&#x…

腾讯大举退出美团!

点击蓝字关注我们因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络,侵删11月16日,腾讯发布第三季度财报,同时表示将“分红式减持”美团。腾讯分派90.9%美团持股 腾讯系中概股美股盘前多数下跌腾…

csr8670 修改key_CSR8670 DFU user guide

DFU使用1)产生DFU keys:dfukeygenerate.exe -o keys或dfukeygenerate.exe -o keys -r random.txt生成keys.private.key和keys.public.key两个文件。2)loader和firmware签名:a)dfukeyinsert.exe -v -o loader_signed -lC:\ADK_CSR867x.WIN4.3.1.5\firmwar…

matchers依赖_Hamcrest Matchers教程

matchers依赖本文是我们名为“ 用Mockito测试 ”的学院课程的一部分。 在本课程中,您将深入了解Mockito的魔力。 您将了解有关“模拟”,“间谍”和“部分模拟”的信息,以及它们相应的存根行为。 您还将看到使用测试双打和对象匹配器进行验证…

谷歌开源替代 C++ 的编程语言:Carbon

点击蓝字关注我们因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络,侵删谷歌工程师 Chandler Carruth 近日在多伦多举办的 CppNorth 大会上宣布①,正式开源谷歌内部打造的编程语言:Carbon&#…

mediumtext和string转换_数据库用varchar和text的差别

数据库用varchar和text的差别发布时间:2018-05-09 20:41,浏览次数:1268, 标签:varchartext最近有几个同学问我varchar和text有啥别吗,这个问题,以前说真的也没太多的整理,以前遇到text在设计中就是尽可能的…

eai app_EAI的Spring集成教程

eai app课程大纲 Spring Integration是用于企业应用程序集成的开源框架。 这是一个轻量级的框架,建立在核心Spring框架之上。 它旨在支持开发事件驱动的体系结构和以消息为中心的体系结构典型的集成解决方案。 Spring Integration扩展了Spring编程模型,…

C语言灵魂拷问:++i为何比i++执行效率高!有何区别?

点击蓝字关注我们因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络,侵删背景相信很多人遇到过这样的问题:printf("%d,%d",i,i);也纠结过这个问题,到底答案是什么。确没有一个参考的资…

java代码防止sql注入_动态Java代码注入

java代码防止sql注入在本文中,我们将研究如何将Java代码动态加载到正在运行的jvm中。 该代码可能是全新的,或者我们可能想更改程序中某些现有代码的功能。 (在开始之前,您可能想知道为什么到底有人会这样做。显而易见的示例是规则…

指标实现层级_企业如何构建核心指标系统,实现业务运营效率提升90%?

本文为帆软数据生产力大赛获奖案例,未经授权禁止转载。01企业简介西安怡康医药连锁有限责任公司成立于2001年,总部设在西安市大庆路副24号,是一家由零售连锁药店发展起来的大型医药连锁企业,除药品零售外,同时兼营药品批发与器械批…

学生时代,你有遇到过像我这样理解C语言的吗?

点击蓝字关注我们因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络,侵删今天我讲一下我个人对C语言的认识以及自己的理解,若有错误,还望指出,不甚感激。首先是C语言整体的脉络&#…

scrapyd部署_第八章 第一节 scrapyd和scrapy-client

如果觉得文章对您产生帮助的话, 欢迎关注Python开发之路(微信公众号: python-developer), 及时获取更多教程假设有我们做了一个项目是抓取很多网站(每个网站对应一个爬虫), 需要分别部署到不同的服务器上(单台扛不住), scrapy官方也提供了一种部署工具scrapyd。这个工具是用来将…

apache lucene_Apache Lucene基础教程

apache lucene课程大纲 Apache Lucene是一个免费/开源信息检索软件库,它提供基于Java的索引和搜索技术,以及拼写检查,命中突出显示和高级分析/令牌化功能。 Lucene是完全用Java编写的高性能,功能齐全的文本搜索引擎库。 它是一项…

C++ 首超 Java,与 Python、C语言共角逐年度最佳编程语言奖!

点击蓝字关注我们因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络,侵删最新的 TIOBE 12 月编程语言已发布,先来预测一波今年的年度编程语言大奖究竟会花落谁家吧?C 首超 Java和上个月相比&…

matchers依赖_定制Hamcrest Matchers

matchers依赖本文是我们名为“ 用Mockito测试 ”的学院课程的一部分。 在本课程中,您将深入了解Mockito的魔力。 您将了解有关“模拟”,“间谍”和“部分模拟”的信息,以及它们相应的存根行为。 您还将看到使用测试双打和对象匹配器进行验证…

乔安监控云存储能存多长时间_干货 | 监控磁盘阵列知识介绍,不了解还不来看看?...

一、磁盘阵列的概念要定义磁盘阵列的概念,是一个简单的工作,因为这个概念已经形成了共识——磁盘阵列(DiskArray)是由一个硬盘控制器来控制多个硬盘的相互连接,使多个硬盘的读写同步,减少错误,增加效率和可靠度的技术。…

我要是在学习 C 语言之前知道这些就好了!

点击蓝字关注我们因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络,侵删对于我来说,学习 C 语言好难啊。这门语言本身的基础知识并不是很难,但是“用 C 语言编程”需要用到各种知识,…

opencv yuv保存本地_OpenCV-dlib-python3实现人脸戴墨镜和含Y的抖音效果

1 说明:1.1 吸烟有害健康!!纯属娱乐和学习python的相关知识。1.2 虽然是娱乐,但是opencv、dlib和python在人工智能、人脸识别、自动化等有很大作用,目前已经或者未来会有更多的应用,作为一名普通人&#xf…

lucene_Lucene组件概述

lucene本文是我们名为“ Apache Lucene基础知识 ”的学院课程的一部分。 在本课程中,您将了解Lucene。 您将了解为什么这样的库很重要,然后了解Lucene中搜索的工作方式。 此外,您将学习如何将Lucene Search集成到您自己的应用程序中&#xf…