前置介绍
为什么要手动建立工程模板?

"CubeMX 是一个开发工具, 并不是开发本身, 学会借助它的便利, 而不依赖它的结构, 这是我们迈向高级开发者的关键的一步"
新建工程简要步骤

手动建立工程
1. 新建工程文件夹

2. 拷贝文件


2.1. 参考目录, 文件结构
对于 Inc 和 Src 等文件夹直接 copy 文件夹即可, 不需要一个个 copy 里面的具体文件
手动创建HAL库模板/
├── Doc/ # 文档目录
│ └── readme.txt # 项目说明文档
│
├── Libraries/ # 库文件目录
│ ├── CMSIS/ # ARM Cortex-M 软件接口标准
│ │ ├── Device/
│ │ │ └── ST/
│ │ │ └── STM32F1xx/ # STM32F1 系列设备文件
│ │ │ ├── Include/ # 头文件
│ │ │ │ ├── stm32f1xx.h # STM32F1xx 主头文件
│ │ │ │ ├── system_stm32f1xx.h # 系统配置文件
│ │ │ │ └── stm32f103xb.h # 具体型号头文件 (F103C8T6)
│ │ │ └── Source/
│ │ │ └── Templates/ # 启动模板
│ │ │ ├── arm/ # ARM 编译器启动文件
│ │ │ ├── gcc/ # GCC 启动文件 + 链接脚本
│ │ │ ├── iar/ # IAR 启动文件 + 链接脚本
│ │ │ └── system_stm32f1xx.c # 系统初始化文件
│ │ └── Include/ # CMSIS 核心头文件
│ │ ├── core_cm3.h # Cortex-M3 核心定义
│ │ ├── cmsis_compiler.h # 编译器抽象层
│ │ ├── cmsis_gcc.h # GCC 编译器支持
│ │ └── ... # 其他核心头文件
│ │
│ └── STM32F1xx_HAL_Driver/ # STM32 HAL 驱动库
│ ├── Inc/ # HAL 库头文件
│ │ ├── stm32f1xx_hal.h # HAL 库主头文件
│ │ ├── stm32f1xx_hal_conf_template.h # 配置文件模板
│ │ ├── stm32f1xx_hal_gpio.h # GPIO 驱动
│ │ ├── stm32f1xx_hal_rcc.h # RCC 时钟驱动
│ │ ├── stm32f1xx_hal_tim.h # 定时器驱动
│ │ ├── stm32f1xx_hal_uart.h # UART 驱动
│ │ ├── stm32f1xx_hal_spi.h # SPI 驱动
│ │ ├── stm32f1xx_hal_i2c.h # I2C 驱动
│ │ ├── stm32f1xx_hal_adc.h # ADC 驱动
│ │ ├── stm32f1xx_hal_dma.h # DMA 驱动
│ │ ├── stm32f1xx_hal_flash.h # Flash 驱动
│ │ ├── stm32f1xx_hal_pwr.h # 电源管理
│ │ ├── stm32f1xx_hal_cortex.h # Cortex 内核驱动
│ │ ├── Legacy/ # 传统 API 兼容头文件
│ │ └── stm32f1xx_ll_*.h # LL 库头文件 (底层驱动)
│ │
│ └── Src/ # HAL 库源文件
│ ├── stm32f1xx_hal.c # HAL 核心
│ ├── stm32f1xx_hal_cortex.c # Cortex 驱动
│ ├── stm32f1xx_hal_rcc.c # RCC 驱动
│ ├── stm32f1xx_hal_gpio.c # GPIO 驱动
│ ├── stm32f1xx_hal_tim.c # 定时器驱动
│ ├── stm32f1xx_hal_uart.c # UART 驱动
│ ├── stm32f1xx_hal_spi.c # SPI 驱动
│ ├── stm32f1xx_hal_i2c.c # I2C 驱动
│ ├── stm32f1xx_hal_adc.c # ADC 驱动
│ ├── stm32f1xx_hal_dma.c # DMA 驱动
│ ├── stm32f1xx_hal_flash.c # Flash 驱动
│ ├── stm32f1xx_hal_pwr.c # 电源管理
│ ├── stm32f1xx_it.c # 中断服务函数
│ ├── system_stm32f1xx.c # 系统初始化
│ ├── Legacy/ # 传统 API 源文件
│ └── stm32f1xx_ll_*.c # LL 库源文件
│
├── Project/ # Keil 项目文件目录
│ ├── DebugConfig/ # 调试配置
│ │ └── Target_1_STM32F103C8_1.0.0.dbgconf # 调试目标配置
│ ├── Listings/ # 编译列表文件
│ │ └── Fire_F103.map # 链接映射文件
│ ├── Objects/ # 编译输出文件
│ │ ├── Fire_F103.axf # 可执行文件
│ │ ├── Fire_F103.hex # HEX 烧录文件
│ │ ├── Fire_F103.build_log.htm # 编译日志
│ │ ├── *.o # 目标文件
│ │ ├── *.d # 依赖文件
│ │ └── *.crf # 交叉引用文件
│ ├── Fire_F103.uvprojx # Keil 项目文件
│ ├── Fire_F103.uvoptx # Keil 项目选项
│ └── Fire_F103.uvguix.HeartacheBoy # Keil 用户界面配置
│
└── User/ # 用户应用代码
├── main.c # 主程序入口
├── main.h # 主程序头文件
├── stm32f1xx_hal_conf.h # HAL 库配置文件
├── stm32f1xx_it.c # 中断服务函数实现
├── stm32f1xx_it.h # 中断服务函数声明
└── system_stm32f1xx.c # 系统时钟配置
3. 新建工程框架

4. 添加分组及文件

5. 魔术棒设置

5.1.1. Target

5.1.2. Output

5.1.3. Listing

5.1.4. C/C++

5.1.5. Debug

工程代码部分
/**
* @brief 模板
*/
#include "main.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* Private functions ---------------------------------------------------------*/
int main(void)
{
HAL_Init(); // 初始化 HAL 库
SystemClock_Config(); // 配置系统时钟, 设置为 72MHz
while (1)
{
}
}
/**
* @brief System Clock Configuration
* The system Clock is configured as follow :
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 72000000
* HCLK(Hz) = 72000000
* AHB Prescaler = 1
* APB1 Prescaler = 2
* APB2 Prescaler = 1
* HSE Frequency(Hz) = 8000000
* HSE PREDIV1 = 1
* PLLMUL = 9
* Flash Latency(WS) = 2
* @param None
* @retval None
*/
void SystemClock_Config(void)
{
RCC_ClkInitTypeDef clkinitstruct = {0};
RCC_OscInitTypeDef oscinitstruct = {0};
/* Enable HSE Oscillator and activate PLL with HSE as source */
oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
oscinitstruct.HSEState = RCC_HSE_ON;
oscinitstruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
oscinitstruct.PLL.PLLState = RCC_PLL_ON;
oscinitstruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
oscinitstruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&oscinitstruct)!= HAL_OK)
{
/* Initialization Error */
while(1);
}
/* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
clocks dividers */
clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
clkinitstruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1;
clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2)!= HAL_OK)
{
/* Initialization Error */
while(1);
}
}
修改完之后, 编译, 下载.
如果下载成功, 说明配置成功.
点灯测试
写个简单的点灯程序测试能否正常运行.

/**
* @brief 模板
*/
#include "main.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* Private functions ---------------------------------------------------------*/
int main(void)
{
HAL_Init(); // 初始化 HAL 库
SystemClock_Config(); // 配置系统时钟, 设置为 72MHz
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_PIN_LED_G;
GPIO_PIN_LED_G.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_PIN_LED_G.Pin = GPIO_PIN_2;
GPIO_PIN_LED_G.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_PIN_LED_G);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
while (1)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_2);
HAL_Delay(500);
}
}
/**
* @brief System Clock Configuration
* The system Clock is configured as follow :
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 72000000
* HCLK(Hz) = 72000000
* AHB Prescaler = 1
* APB1 Prescaler = 2
* APB2 Prescaler = 1
* HSE Frequency(Hz) = 8000000
* HSE PREDIV1 = 1
* PLLMUL = 9
* Flash Latency(WS) = 2
* @param None
* @retval None
*/
void SystemClock_Config(void)
{
RCC_ClkInitTypeDef clkinitstruct = {0};
RCC_OscInitTypeDef oscinitstruct = {0};
/* Enable HSE Oscillator and activate PLL with HSE as source */
oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
oscinitstruct.HSEState = RCC_HSE_ON;
oscinitstruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
oscinitstruct.PLL.PLLState = RCC_PLL_ON;
oscinitstruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
oscinitstruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&oscinitstruct)!= HAL_OK)
{
/* Initialization Error */
while(1);
}
/* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
clocks dividers */
clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
clkinitstruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1;
clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2)!= HAL_OK)
{
/* Initialization Error */
while(1);
}
}
这里有个注意点:
在STM32中,为了降低功耗,所有外设(包括GPIO端口)的时钟在默认情况下都是关闭的。
必须先手动开启对应端口的时钟,才能对其进行配置和操作。
例如: 使用 GPIOA , 那么就要使用 __HAL_RCC_GPIOA_CLK_ENABLE();
测试程序现象
