嵌入式框架分层_嵌入式软件架构设计之分层设计(1),面试经历分享

81 阅读6分钟

前言

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


一、框架分层是什么?

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

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

  • 每个模块提供的接口要统一,只能增加,不能改。在设计的时候得考虑好兼容性,使用起来麻烦不麻烦等等。
  • 同一级模块与模块之间相互独立,互不影响,不能相互调用,只能调用它下一层的接口
  • 不同模块构成不同的层,层与层之间不能跨级调用。
  • 模块中又可以继续分层,可以增减分层,这个需要根据自己的项目需求来进行设置。

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

让我们看看这个经典的图,简单了解一下框架分层。
在这里插入图片描述
从图中不难观察出,设计都是遵循设计的原则的,层与层之间不能相互调用。

二、框架分层的优劣势

1.优势

  • 单一职责:每一层只负责一个职责,职责边界清晰,不会造成跨级调用,在大型项目中,每个人负责的部分不一样,加快整个项目的开发进度。
  • 高内聚:分层是把相同的职责放在同一个层中,所有业务逻辑内聚在领域层。在测试的时候,只需要测试该领域的层即可,一般不需要考虑其他层的问题。
  • 低耦合:依赖关系非常简单,上层只能依赖于下层,没有循环依赖。
  • 易维护:面对变更容易修改。在平台更改后,如果只是改了驱动,其他层都不需要动,只需要把驱动层给更改,其他层的功能不需要更改。
  • 易复用:如果功能模块变动了,只需升级相应的功能模块,其他的模块不受影响,应用层也不受影响。

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

2.劣势

  • 开发成本高:因为多层分别承担各自的职责,增加功能需要在多个层增加代码,这样难免会增加开发成本。但是合理的抽象,根据自己的项目设置合理的层级是能降低开发成本的。
  • 性能略低:业务流需要经过多层代码的处理,性能会有所消耗。
  • 可扩展性低:因为上下层之间存在耦合度,有些功能变化可能涉及到多层的修改。

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

三、一个简单的例子

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

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

  • 按键控制小灯的亮度,等级为:0,1,2,3
  • 串口可以观察当前小灯亮度等级
  • OLED也可以观察当前小灯亮度等级

下面就是这个例子的一个简单的图示。
这和例子比较简单,业务逻辑层完全可以去除,直接从应用层调用功能模块层,加快开发进度。
在这里插入图片描述
最后附上一点点代码,就是关于LED如何进行在不同层进行封装

硬件层

首先看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 \*/



![img](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/231ec7a24a14428497a506c124c5b848~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771262868&x-signature=SOA4kAfutoEfoScXKQRMzPdTzLM%3D)
![img](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/418d888cc32d4dcc92521ad142cfa393~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771262868&x-signature=rcev%2FsJECDxfTiOHdJnaOz3N%2Fb0%3D)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://gitee.com/vip204888)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**