STM32CubeIDE构建通用freertos项目(一)

感慨

本人大约三四年没有碰单片机了,遥想当年我还是用的keil工具。
有幸以援助的身份介入公司的嵌入式项目,结合自身经验讲讲。
工作是一个长期的过程,开头不注意则会产生蝴蝶效应,导致接下来的工作一直处于挖坑填坑的状态,最终大好青春年华耗费在一些无谓的事情上。
本文不过多去讲具体操作,只描述针对问题的思考方式。
工程方面指的是freertos工程。

背景需求

做任何事情都要有目标,朝着目标去前进。本文关注的目标如下:
1. 代码如何复用,达到多项目复用的目的
2. 如何满足多项目复用的前提下,又不混淆各自的代码模块

分析

代码复用,在嵌入式中,关键是模块的复用。模块又分为硬件模块和软件模块两类。同时,不管是硬件模块还是软件模块,关键点上要有相应的模块测试。

硬件模块

硬件模块主要是驱动相关的函数操作,见识过linux驱动的小伙伴应该知道有四个关键点需要实现: 加载、卸载、读、写。
同时,硬件模块分为内设和外设。内设驱动通过工程配置可以自动生成;外设驱动往往需要自己开发。

软件模块

软件模块是衔接硬件和业务主体的中间层,开发过程中一定要注意解耦。软件模块必须在不同硬件平台上是可移植的!

模块测试

代码要复用,必须有相应的模块测试,否则更换硬件平台时,很大概率看着一堆跑不起来的代码发呆。

工程结构

.ioc文件和.xml文件配置

要实现不同硬件平台下的工程代码复用,首先要按照不同工程去创建不同的ioc文件,该文件涉及硬件平台,每次调整修改后,会重新生成内设驱动,内设驱动文件直接生成在Src目录下;并且include配置也会被刷新掉。
随便打开一个内设驱动代码,添加部分说明,如下:

void ADC_IRQHandler(void)
{
  /* USER CODE BEGIN ADC_IRQn 0 */
  // USER CODE注释之间的代码,重新生成是不会消失的
  /* USER CODE END ADC_IRQn 0 */
  HAL_ADC_IRQHandler(&hadc1);
  /* USER CODE BEGIN ADC_IRQn 1 */
  // USER CODE注释之间的代码,重新生成是不会消失的
  /* USER CODE END ADC_IRQn 1 */
}

include配置被刷掉的话,通过以下Import和Export即可解决,将会保存为.xml文件,其中包括了include路径和define;define的设置尤为关键,用它来实现硬件设施的切换
导入导出
总结,硬件设施由.ioc来定义,而.xml则定义了软件行为,在不同的硬件设施下,对应不同的软件行为,采用切换文件配置来实现代码统一维护。

代码目录

	在了解STM32CubeIDE开发工具的特点以及模块测试相关内容后,简要讲解一下代码上要如何排布。
	源文件目录,分为硬件模块dev、框架配置framework、业务主体main、软件架构soft、测试单元test,如下:

在这里插入图片描述

	头文件目录,则少了业务主体和测试单元相应的头文件,因为这两者是引用别人,同时不必暴露自身变量和函数,故无需提供头文件。	

在这里插入图片描述

依赖关系

学过java的同学,会知道一个细节,当import一个不使用的包时,IDE会给你相应的提示。而在C/C++的IDE中,则没有那么方便的提示。所以在写C工程时,需要仔细处理好依赖关系,要有一定的章法,而不是随意引用,能够运行就不管了。
仿造Nginx源码的组织方式,在Nginx中,有一个头文件ngx_core.h包含了所有头文件,而其他文件统一引用该文件即可。
那么在STM32CubeIDE中,哪个文件适合干这种事呢?答案是main.h文件!主要原因在于,其包含是自动生成的内设驱动;并在其中添加咱自定义的config.h文件,之后不再动main.h文件。

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "config.h"
/* USER CODE END Includes */

config

config主要用途在于控制一个函数指针指向,参数及返回不是关键点,为void即可,声明如下:

void (*init)(void);

调用方放在freertos.c中,该文件生成时已经引用了main.h,从而引用了config.h

  /* USER CODE BEGIN RTOS_THREADS */
	init();
  /* USER CODE END RTOS_THREADS */

至此,已经分离解耦了硬件设施和软件设施,通过一个init函数指针来衔接。
控制工程行为自然是采用宏定义来完成,代码如下:

#if(PRO==CONFIG_MAIN)
	extern void main_init();
	void(*init)(void) = main_init;
#elif(PRO==CONFIG_TEST_HTTP)
	extern void test_http_init(void);
	void(*init)(void) = test_http_init;
#endif

只需要在Symbols中配置好,即可完成不同功能的切换。
在这里插入图片描述

开发流程

上面讲述了关键点,具备了一个通用工程架构的基础雏形,还需要一定的开发流程规范才能完整。
首先,你需要一个完整的A项目,而B项目和A项目,仅仅是少量需求不同,硬件接口也可能稍有不同。
接着,构建好B项目的ioc文件,自动生成代码,然后将A项目中的测试逐个在B项目上运行,运行不通过的地方,使之兼容,并确保A项目不受影响。
最后,开开心心在上面开发你的B项目吧,可别忘了export你的xml配置哟。

发布了16 篇原创文章 · 获赞 1 · 访问量 3594

猜你喜欢

转载自blog.csdn.net/a215095167/article/details/104820123