談談關於嵌入式軟件分層框架

爲了能夠使得產品得到更好的開發速度與以後更好的迭代和移植,框架分層是很有必要的。但如對於中小型項目嚴格遵循這些原則,勢必會消耗過多精力去思考怎麼設計系統,這是一個抉擇的過程。 

一、框架分層是什麼?

在嵌入式架構中:一般分爲硬件架構與軟件架構。這裏是嵌入式軟件設計,也是大多數人接觸的設計。

所謂的分層,也可以理解爲模塊化的設計,但是框架分層的設計一般會遵循以下幾點原則:

一般可以分爲:硬件驅動層–> 功能模塊層–> 應用接口層–> 業務邏輯層–> 應用層

讓我們看看這個經典的圖,簡單瞭解一下框架分層。

從圖中不難觀察出,設計都是遵循設計的原則的,層與層之間不能相互調用。

二、框架分層的優劣勢

1. 優勢

如果想要更好地利用這些優勢,那得嚴格遵循設計的原則。

2. 劣勢

有優勢也有劣勢,需要根據自己的項目需要,進行部分的取捨,如果是中小型項目,可以不需要分層(如果不考慮到以後會迭代的話),或者部分分層就夠了,既能利用框架分層的部分優勢,也能降低開發成本。

三、一個簡單的例子

由於主要討論的是軟件框架的分層設計,這裏使用 STM32cubemx 來進行硬件的初始化,儘可能少考慮到硬件驅動的部分。

以一個智能小燈的作爲例子:

功能

下面就是這個例子的一個簡單的圖示。

這和例子比較簡單,業務邏輯層完全可以去除,直接從應用層調用功能模塊層,加快開發進度。

最後附上一點點代碼,就是關於 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 */

  /* 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 這個簡單的例子,已經大概瞭解到這個設計的複雜了,如果是大型項目,運用起來會很爽,小型的話完全沒必要這樣分層,太麻煩了,嚴重減慢開發效率,時間都用在思考如何進行分層才能符合框架分層的原則。

原文:http://t.csdn.cn/OWG8f

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/8G6ThSuccr-t6UcHWyMaVg