嵌入式软件开发要求-可以嵌入字幕的软件
我是老温,一名热爱学习的嵌入式工程师 关注我,一起变得更加优秀!
前言
为了在未来能够实现更好的产品开发速度和更好的迭代移植,框架分层是很有必要的。 但是,对于中小型项目,如果严格遵循这些原则,势必会消耗过多的精力去思考如何设计系统。 这是一个选择的过程。
1、什么是框架分层?
在嵌入式架构中:一般分为硬件架构和软件架构。 这就是嵌入式软件设计,也是大多数人接触的东西。
所谓分层也可以理解为模块化设计,但是框架分层的设计一般遵循以下原则
一般可分为:硬件驱动层->功能模块层->应用接口层->业务逻辑层->应用层
下面我们通过这张经典图来简单了解下框架分层。
从图中不难看出,设计遵循设计原则,层之间不能互相调用。
二、框架分层的优缺点 1、优点
要想更好地利用这些优势嵌入式软件开发要求,就必须严格遵循设计原则。
2.缺点
有优点也有缺点。 你需要根据自己的项目需求做一些取舍。 如果是中小型项目,可以不用分层(如果不考虑以后的迭代),或者一些层就够了。 您可以使用框架分层的部分优势还可以降低开发成本。
3.一个简单的例子
由于主要讨论的是软件框架的分层设计,这里使用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);
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 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 */
}
}
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这个简单的例子,大致了解了这个设计的复杂性。 如果是大型项目,用起来会很爽。 小的话就没必要这么分层了,太麻烦了,严重拖慢开发效率,时间都花在想怎么分层才能符合框架分层的原则上了。
原始来源: