前言:
这个系列,我主要写我用32f103实现的各种功能模块,已经程序编写过程中,硬件调试中出现的问题,一边记录,一边分享,一边复盘。
使用的是STM32cubemax,自动生成代码,所以为了保证程序可读性,逻辑性,我们在不影响系统自动生成代码的情况下,自己要有自己的程序框架,这样,在我们后期不断增加模块功能时,改动更小,兼容性更强,可阅读性更好。
目录
1.STM32CUBEMAX自动生成代码
1.1软件配置
2.系统代码
2.1包含的头文件
2.2系统时钟函数
2.3系统初始化
3. 标准化程序框架
3.1MyApplication.h文件
3.2main.c文件
3.3.1头文件
3.3.2源文件
3.3.3run函数
为了方便演示程序框架,我用最简单也是最入门的点亮LED灯来操作。
首先,我们需要配置stm32cubemax的各种配置,包括芯片选型,GPIO外设,时钟RCC,等等,如下图所示:
GPIO口的配置,推挽还是开漏,已经不同选择的原因,rcc时钟频率的配置,这些都跟挂载总线有关,时钟源这些等等底层逻辑相关的,后期我会单独分享复盘底层逻辑;这个章节我主要想介绍程序框架。
当我们配置好cubemax之后,点击GENERATE CODE 就会在我们保存的文件路径底下自动生成project,如下图所示:
因为我是用点亮led灯来演示的,所以系统除了主函数,只有gpio,后期我们用到的模块越多,源文件也会相应的增加,我们主要分析一下系统生成文件都包含那些以及主要的功能
引用了main.h以及gpio.h这两个头文件,这两个头文件内部主要是对gpio口的引脚及端口进行定义 ,包括gpio初始化的声明。
我们配置完系统时钟之后,这些文函数就会自动生成。
系统初始化部分就包含了 HAL库的初始化以及gpio的初始化。
我们在不影响上述文件的情况下,新增属于我们自己的框架文件,在保证接口固定的情况下,每次只修改部分接口函数即可。
1、新增MyApplication文件夹,放置4个标准c文件,分别是公共文件,回调文件,系统文件,用户初始化文件,后续应用代码均放在此文件夹;
2、新增MyApplication.h文件,包含所有用户代码的头文件与外设头文件,调整外设或用户文件,只需要调整此文件内的相应头文件即可;
3、main.c文件标准化
#ifndef __MyApplication_H__
#define __MyApplication_H__
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
#include "stdio.h"
#include "stdlib.h"
#include "System.h"
#include "Public.h"
#include "MyInit.h"
#endif
我们将所有能用到的头文件集中在一个大的头文件中,只要所有源文件调用这一个头文件即可。
//调用头文件
/* USER CODE BEGIN Includes */
#include "MyApplication.h"
/* USER CODE END Includes */
//初始化
/* USER CODE BEGIN 2 */
MyInit.Peripheral_Set(); //Óû§³õʼ»¯
/* USER CODE END 2 */
//主循环
while (1)
{
System.Run();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
//标准化错误处理函数
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
System.Error_Handler();
/* USER CODE END Error_Handler_Debug */
}
//标准化断言失败处理函数
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
System.Assert_Failed();
/* USER CODE END 6 */
}
我们定义一个标准化的main.c文件,在这个文件中我们把实现的功能放入system函数中,并添加标准初始化文件,用来初始化单片机的上电状态,已经标准化断言失败文件,错误处理文件。
3.3system文件
typedef struct
{
void (*Run)(void);
void (*Error_Handler)(void);
void (*Assert_Failed)(void);
} System_t;
/* extern variables-----------------------------------------------------------*/
extern System_t System;
/* extern function prototypes-------------------------------------------------*/
#endif
我们用结构体封住了三个函数指针,分别指向run函数,错误处理函数,和断言失败函数,结构体封装的好处 就是我们调用函数时候,只需要用指针寻址解引用,这样的话如果函数需要修改的话,不影响地址指向,也就是说主函数并不需要修改,我们只需要修改指向的函数内容即可。
typedef struct
{
void (*Run)(void);
void (*Error_Handler)(void);
void (*Assert_Failed)(void);
} System_t;
/* extern variables-----------------------------------------------------------*/
extern System_t System;
/* extern function prototypes-------------------------------------------------*/
#endif
主要定义结构体System以及3个函数,并将3个函数的名称(首地址)赋值给System结构体,完成结构体的初始化。 如此一来,main.c文件可以通过System结构体的函数指针调用System.c文件的3个函数了。
static void Run()
{
HAL_Delay(1000);
HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
}
我们要实现的功能模块在这里实现就可以了,我们只是点亮小灯,所以功能简单,延时函数以及翻转灯光就行。