首页 > 新闻 > 硬件新闻 > 正文

举例说明嵌入式框架该如何分层

爱谁谁
发布: 2025-04-24 19:38:36
原创
487人浏览过

素材来源:https://www.php.cn/link/4ac4cbd6ebfd454fd1d1c310cad3c644

整理:技术让梦想更伟大 | 李肖遥

前言为了提升产品开发速度和未来迭代及移植的便利性,框架分层设计是必不可少的。然而,对于中小型项目严格遵循这些原则,可能会耗费过多精力在系统设计上,这是一个需要权衡的过程。

一、框架分层是什么?在嵌入式架构中,通常分为硬件架构与软件架构。这里主要讨论的是嵌入式软件设计,这是大多数人接触的设计。

所谓的分层,也可以理解为模块化的设计,但框架分层的设计通常遵循以下原则:

每个模块提供的接口必须统一,只能增加,不能修改。设计时需考虑兼容性和使用便利性。同级模块之间应相互独立,不能相互调用,只能调用下一层的接口。不同模块构成不同的层,层与层之间不能跨级调用。模块内部可以继续分层,根据项目需求增减分层。一般可以分为:硬件驱动层->功能模块层->应用接口层->业务逻辑层->应用层

让我们通过一个经典的图示,简单了解一下框架分层的结构。

举例说明嵌入式框架该如何分层从图中可以看出,设计是遵循设计原则的,层与层之间不能相互调用。

二、框架分层的优劣势1.优势单一职责:每一层只负责一个职责,职责边界清晰,不会造成跨级调用。在大型项目中,每个人负责的部分不同,加快整个项目的开发进度。高内聚:分层将相同职责放在同一层中,所有业务逻辑内聚在领域层。测试时,只需测试该领域的层,通常不需要考虑其他层的问题。低耦合:依赖关系简单,上层只能依赖下层,没有循环依赖。易维护:面对变更容易修改。在平台更改后,如果仅改了驱动,其他层无需修改,只需更改驱动层即可。易复用:如果功能模块变动,只需升级相应的功能模块,其他模块不受影响,应用层也不受影响。要充分利用这些优势,必须严格遵循设计原则。

2.劣势开发成本高:因为多层分别承担各自的职责,增加功能需要在多个层增加代码,这难免会增加开发成本。但合理的抽象和根据项目设置合理的层级可以降低开发成本。性能略低:业务流需要经过多层代码处理,性能会有所消耗。可扩展性低:因为上下层之间存在耦合度,有些功能变化可能涉及多层的修改。有优势也有劣势,需根据项目需求进行取舍。对于中小型项目,如果不考虑未来迭代,可以不分层或部分分层,既能利用框架分层的部分优势,又能降低开发成本。

三、一个简单的例子主要讨论的是软件框架的分层设计,这里使用STM32cubemx进行硬件初始化,尽可能少考虑硬件驱动部分。

以一个智能小灯为例:

功能

通过按键控制小灯的亮度,等级为:0,1,2,3通过串口观察当前小灯亮度等级通过OLED观察当前小灯亮度等级下面是这个例子的一个简单图示。

这个例子较为简单,业务逻辑层可以省略,直接从应用层调用功能模块层,加快开发进度。

举例说明嵌入式框架该如何分层最后附上一点关于LED如何在不同层进行封装的代码。

如此AI写作
如此AI写作

AI驱动的内容营销平台,提供一站式的AI智能写作、管理和分发数字化工具。

如此AI写作 137
查看详情 如此AI写作

硬件层

首先查看HAL库生成的代码,这是LED硬件层,即GPIO层,cubemx已经生成,在stm32f4xx_hal_gpio.c(我使用的是F4)中,已有相应的GPIO驱动,这里不需要我们处理。

举例说明嵌入式框架该如何分层硬件层驱动层

查看LED部分的驱动,即下面的两个函数:

void MX_TIM1_Init(void);
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle);
/* 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 Configuration
    PE11     ------> 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 */
  }
}
登录后复制

对其进行封装,就是我们想要的LED小灯驱动,未来如果需要修改驱动,直接修改底层即可。

void Led_init(){
  MX_TIM1_Init();
  HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);//启动PWM
}
登录后复制

功能模块层

根据上面的需求要求,划分为四个不同等级,同时需要对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();
}
登录后复制

业务逻辑层

这里仅以启动层为例:

void Start_app(){
  Led_Start();
}
登录后复制

应用层

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

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

版权声明:本文来源网络,免费传达知识,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。

‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

以上就是举例说明嵌入式框架该如何分层的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号