简介STM32Cube_FW_F1_V1.8.0是意法半导体面向STM32F1系列微控制器推出的官方固件库属于STM32Cube生态的核心组件适合嵌入式初学者、工程师及高校师生用于外设驱动开发与项目快速原型搭建。包内集成HAL与LL两层驱动覆盖ADC、CAN、GPIO、I2C、SPI、TIM、USART等常用外设并附带USB OTG、FatFS、FreeRTOS等中间件、丰富应用示例及HAL/LL API文档兼容Keil MDK、IAR与GCC ARM等开发环境。压缩包为rar格式整体约95.73MB文件类型以驱动源码、头文件、示例工程与说明文档为主便于按外设模块检索与移植。目前已有1088人学习下载读者可借此快速掌握外设初始化流程、中间件集成方式与底层寄存器操作思路减少从零搭建工程的时间成本专注应用逻辑设计。1. STM32Cube_FW_F1_V1.8.0 安装包落地从解压到能编译我踩过的第一个坑STM32Cube_FW_F1_V1.8.0 这个包名字看着平平无奇但如果你正在用 STM32F1 系列做项目它基本是绕不开的一套固件库。我见过太多人拿到 .rar 之后直接双击解压到桌面然后打开 Keil 新建工程发现找不到 stm32f1xx_hal.h编译报一堆 undefined symbol最后怀疑是自己 Keil 版本不对。其实问题根本不在编译器而在于这个包的目录结构没有被正确理解。STM32Cube_FW_F1 是 ST 官方为 STM32F1 系列提供的固件包里面包含 HAL 库、LL 库、CMSIS 内核文件、板级支持包以及大量例程。V1.8.0 这个版本对应的是较新的 HAL 接口和早期 V1.4、V1.5 在回调函数命名和句柄结构上有差异。如果你从旧项目迁移过来直接替换库文件大概率会编译失败。这篇记录适合刚接触 STM32F1 的开发者也适合从标准库转 HAL 库、或者从旧版本 Cube 包升级的人。装不好不会炸板子但会让你在编译阶段反复怀疑人生。2. 环境准备与版本选择装之前先把这几个东西对齐2.1 确认你手上的芯片和工具链版本STM32Cube_FW_F1_V1.8.0 支持的是 STM32F1 全系包括 F100、F101、F102、F103、F105、F107。但不同芯片的启动文件、链接脚本、外设初始化代码不一样。我一般会先确认三件事芯片具体型号、用的 IDE 是 Keil MDK 还是 STM32CubeIDE、编译器版本是 AC5 还是 AC6。V1.8.0 的例程默认工程文件同时提供 Keil、IAR、SW4STM32 三种但 Keil 工程里默认用的是 AC5 编译器。如果你装的是 Keil MDK 5.37 以上默认编译器已经切到 AC6直接打开例程会报一堆 armcc 找不到的错。常见做法是手动在 Keil 里把编译器切回 AC5或者把工程迁移到 AC6 并处理语法差异。我一般会先装好 AC5 编译器再打开工程省得后面来回折腾。2.2 解压路径里不要有中文和空格这个坑我踩过不止一次。STM32Cube_FW_F1_V1.8.0 解压后的目录里有大量例程例程的工程文件里写的是相对路径。如果你把包解压到D:\学习资料\STM32\Cube_F1这种带中文的路径下Keil 打开工程时会提示找不到源文件或者编译到一半报路径错误。原因是 Keil 的工程文件对中文路径支持不好尤其是 .uvprojx 里的 include path 包含中文时编译器直接罢工。我一般会解压到D:\STM32Cube\F1\这种纯英文、无空格的短路径下。另外解压工具建议用 7-Zip 或 WinRAR不要用 Windows 自带的解压自带解压对长路径和大量小文件的支持不稳定偶尔会漏文件。2.3 目录结构先看懂再动手解压完不要急着打开工程先花两分钟把目录结构看清楚。STM32Cube_FW_F1_V1.8.0 的根目录下通常有这些文件夹目录内容是否必须Drivers/CMSIS内核头文件、启动文件、系统初始化必须Drivers/STM32F1xx_HAL_DriverHAL 库源码和头文件必须Drivers/BSP官方板级支持包如 LCD、EEPROM 驱动按需Middlewares中间件如 FatFs、USB Device、FreeRTOS按需Projects各开发板的例程按板子型号分目录参考用Utilities杂项工具和配置文件按需我一般会把 Drivers 整个文件夹复制到自己的工程目录下而不是直接在原包里改例程。这样后续升级或换芯片时只需要替换 Drivers 里的对应文件不会把例程改乱。Projects 里的例程用来参考外设初始化顺序和回调函数写法不要直接拿来当项目骨架因为例程的时钟配置和引脚定义是针对特定开发板的和你自己的板子大概率不一致。3. 手动搭建第一个 HAL 工程从零开始把库文件挂进 Keil3.1 新建工程并选好芯片型号打开 Keil MDKProject → New uVision Project选一个纯英文路径保存工程文件。弹出芯片选择窗口时展开 STMicroelectronics → STM32F1 Series → STM32F103 → STM32F103ZE根据你实际芯片选。选完后会弹出一个 Manage Run-Time Environment 窗口这里直接点 Cancel不要在这里勾选任何东西。因为我们要手动挂载 STM32Cube_FW_F1_V1.8.0 的库文件用 Keil 自带的 RTE 会和 Cube 库冲突导致重复定义。Cancel 之后工程是空的接下来手动添加文件。3.2 把 Cube 库文件复制进工程目录在工程根目录下新建三个文件夹Startup、CMSIS、HAL_Driver。然后从解压好的 STM32Cube_FW_F1_V1.8.0 里复制文件启动文件从Drivers/CMSIS/Device/ST/STM32F1xx/Source/Templates/arm/下找到startup_stm32f103xe.s复制到Startup文件夹。注意文件名里的xe对应大容量型号如果你的芯片是中容量或小容量要换成对应的启动文件。内核文件从Drivers/CMSIS/Include/下复制core_cm3.h、cmsis_compiler.h、cmsis_armcc.h等头文件到CMSIS文件夹。系统文件从Drivers/CMSIS/Device/ST/STM32F1xx/Source/Templates/下复制system_stm32f1xx.c到CMSIS文件夹。HAL 库文件从Drivers/STM32F1xx_HAL_Driver/Src/下复制你需要的 .c 文件到HAL_Driver文件夹。最少需要stm32f1xx_hal.c、stm32f1xx_hal_cortex.c、stm32f1xx_hal_gpio.c、stm32f1xx_hal_rcc.c、stm32f1xx_hal_rcc_ex.c。用到串口就加stm32f1xx_hal_uart.c用到定时器就加stm32f1xx_hal_tim.c。复制的时候注意HAL 库的 .c 文件里会 include 对应的 .h 文件所以头文件也要从Inc文件夹复制到HAL_Driver里或者单独建一个Inc文件夹统一放头文件。我一般会在工程目录下建一个User文件夹放自己的main.c和stm32f1xx_hal_conf.h。stm32f1xx_hal_conf.h从Drivers/STM32F1xx_HAL_Driver/Inc/下复制这个文件用来裁剪 HAL 模块不需要的模块把对应的#define HAL_XXX_MODULE_ENABLED注释掉能省不少编译时间。3.3 在 Keil 里添加文件分组和头文件路径回到 Keil在 Project 窗口里右键 Target → Manage Project Items。新建四个 GroupStartup、CMSIS、HAL_Driver、User。分别把刚才复制到对应文件夹里的 .c 和 .s 文件添加进去。启动文件startup_stm32f103xe.s加到Startup组system_stm32f1xx.c加到CMSIS组HAL 的 .c 文件加到HAL_Driver组main.c加到User组。然后点 Options for Target → C/C → Include Paths把以下路径加进去# 在 Keil 的 Include Paths 里逐条添加不要用相对路径的上级目录 .\Startup .\CMSIS .\HAL_Driver .\User这里有个细节Include Paths 里不要加.\HAL_Driver\Inc这种子目录因为 HAL 库的源文件里 include 头文件时写的是#include stm32f1xx_hal.h不带路径前缀。所以你要么把头文件直接放在HAL_Driver文件夹里要么在 Include Paths 里把HAL_Driver和HAL_Driver/Inc都加上。我一般会把头文件和源文件放在同一个文件夹下省得路径写错。3.4 配置全局宏定义和时钟频率在 Options for Target → C/C → Define 里填入两个宏USE_HAL_DRIVER, STM32F103xEUSE_HAL_DRIVER告诉编译器启用 HAL 库STM32F103xE是芯片型号宏用来让stm32f1xx.h选择正确的寄存器定义。如果你的芯片是 STM32F103C8T6就改成STM32F103xB。这个宏写错会导致编译时报寄存器未定义或者启动文件里的中断向量表对不上。然后在system_stm32f1xx.c里确认HSE_VALUE的值。默认是 8000000也就是 8MHz 外部晶振。如果你的板子用的是 12MHz 晶振要改成 12000000否则串口波特率会偏。这个值在stm32f1xx_hal_conf.h里也有定义两个地方要一致。我一般会在stm32f1xx_hal_conf.h里统一改然后确保system_stm32f1xx.c里 include 了这个头文件。3.5 写一个最小 main.c 验证编译在User文件夹下新建main.c写一个最简单的初始化代码#include stm32f1xx_hal.h void SystemClock_Config(void); int main(void) { HAL_Init(); // 初始化 HAL 库配置 SysTick 和中断优先级 SystemClock_Config(); // 配置系统时钟下面单独实现 __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能 GPIOB 时钟以 PB0 为例 GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin GPIO_PIN_0; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; // 推挽输出 GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, GPIO_InitStruct); while (1) { HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); HAL_Delay(500); // 依赖 SysTickHAL_Init 里已配好 1ms 中断 } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct {0}; RCC_OscInitStruct.OscillatorType RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL RCC_PLL_MUL9; // 8MHz * 9 72MHz HAL_RCC_OscConfig(RCC_OscInitStruct); RCC_ClkInitStruct.ClockType RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider RCC_HCLK_DIV2; // APB1 最高 36MHz RCC_ClkInitStruct.APB2CLKDivider RCC_HCLK_DIV1; HAL_RCC_ClockConfig(RCC_ClkInitStruct, FLASH_LATENCY_2); }这段代码里HAL_Init()会配置 Flash 预取、SysTick 1ms 中断和 NVIC 优先级分组。SystemClock_Config()把 HSE 8MHz 经 PLL 9 倍频到 72MHzAHB 不分频APB1 二分频到 36MHzAPB2 不分频。FLASH_LATENCY_2是 72MHz 下需要的 Flash 等待周期写少了会跑飞。编译这个工程如果 0 Error 0 Warning说明库文件挂载正确。如果报stm32f1xx_hal.h找不到检查 Include Paths如果报HAL_GPIO_Init未定义检查stm32f1xx_hal_gpio.c有没有加到工程里。4. 避坑与常见问题这五个坑我替你踩过了4.1 编译报错 “cannot open source input file stm32f1xx_hal_conf.h”现象Keil 编译时提示找不到stm32f1xx_hal_conf.h但明明已经复制到 User 文件夹了。原因stm32f1xx_hal.h里 include 这个配置文件时用的是#include stm32f1xx_hal_conf.h编译器会在 Include Paths 里找。如果你只把文件放在 User 文件夹但没把 User 加进 Include Paths或者文件名大小写不一致就会报这个错。解决确认 Options for Target → C/C → Include Paths 里有.\User并且文件名严格是stm32f1xx_hal_conf.h不是STM32F1xx_HAL_Conf.h之类。Windows 不区分大小写但 Keil 在某些情况下会区分统一用小写最稳。4.2 串口打印乱码波特率怎么算都不对现象用 HAL 库配了 USART1波特率设 115200串口助手收到乱码。原因HAL_RCC_GetPCLK2Freq()返回的时钟频率和你实际配置的不一致。常见情况是SystemClock_Config()里 APB2 分频系数写错或者HSE_VALUE和实际晶振不匹配。比如板子是 12MHz 晶振但HSE_VALUE还是默认的 8000000PLL 倍频后系统时钟就不是 72MHz串口波特率自然偏。解决先确认板子晶振频率改stm32f1xx_hal_conf.h里的HSE_VALUE。然后在SystemClock_Config()里用HAL_RCC_GetHCLKFreq()和HAL_RCC_GetPCLK2Freq()把实际频率读出来通过串口打印或者调试器看。如果 PCLK2 不是 72MHz检查 APB2 分频系数。4.3 中断进不去回调函数不执行现象配了定时器中断或串口接收中断但HAL_TIM_PeriodElapsedCallback()或HAL_UART_RxCpltCallback()一直不触发。原因V1.8.0 的 HAL 库把中断处理拆成了HAL_XXX_IRQHandler()和回调函数两层。你需要在stm32f1xx_it.c里调用HAL_TIM_IRQHandler(htim2)这个函数内部会判断中断标志并调用对应的回调。如果你只写了回调函数没在中断服务函数里调用 HAL 的 IRQHandler回调永远不会执行。解决打开stm32f1xx_it.c找到对应的中断服务函数比如TIM2_IRQHandler()在里面调用HAL_TIM_IRQHandler(htim2)。htim2是你在外设初始化时定义的句柄要确保在stm32f1xx_it.c里能访问到通常用extern声明。4.4 从标准库迁移过来RCC 配置全乱了现象原来用标准库的RCC_Configuration()能跑换成 HAL 库后时钟起不来或者外设不工作。原因标准库直接操作寄存器HAL 库用RCC_OscInitTypeDef和RCC_ClkInitTypeDef结构体配置两者的时钟树理解方式不同。标准库的RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9)在 HAL 里要拆成PLLSource和PLLMUL两个字段而且 HSE 预分频在HSEPredivValue里单独设。解决不要试图把标准库的 RCC 代码逐行翻译成 HAL直接参考 STM32Cube_FW_F1_V1.8.0 里Projects/STM32F103RB-Nucleo/Examples/RCC/下的例程复制它的SystemClock_Config()函数改一下晶振频率和倍频系数就行。4.5 例程编译通过但下载后不运行现象用 Cube 包里的例程编译 0 Error下载到板子后没反应LED 不闪串口没输出。原因例程的时钟配置和引脚定义是针对官方开发板的。比如例程里 LED 接在 PA5你的板子接在 PB0那肯定不亮。另外例程的HSE_VALUE可能和你的晶振不一致导致系统时钟没起来程序卡在HAL_RCC_OscConfig()里的while等待 HSE 就绪循环。解决先确认例程对应的开发板型号和你自己的板子是否一致。不一致的话把例程里的 GPIO 初始化部分改成你板子的引脚时钟配置里的HSE_VALUE改成你的晶振频率。如果程序卡死用调试器单步走看是不是停在HAL_RCC_OscConfig()里等待 HSE 就绪。如果是检查晶振有没有起振负载电容对不对。5. 装完怎么验证跑通我的三步确认法装完 STM32Cube_FW_F1_V1.8.0 并手动搭好工程之后不要急着写业务代码。我一般会走三步验证确认环境真的没问题。第一步编译一个纯 HAL 初始化的空工程不配任何外设只调用HAL_Init()和SystemClock_Config()然后在while(1)里空转。编译下载后用调试器看SystemCoreClock变量的值。如果它是 72000000说明时钟配置正确。如果还是默认的 8000000说明SystemClock_Config()没生效或者 PLL 没锁。这一步能排除时钟问题时钟不对后面全是玄学。第二步加一个 GPIO 翻转用示波器或者逻辑分析仪看波形。没有仪器的话接一个 LED 和限流电阻肉眼确认闪烁频率。HAL_Delay(500)在 72MHz 下应该是 500ms 翻转一次LED 亮灭各 500ms。如果闪烁频率明显偏快或偏慢回去检查HAL_RCC_GetHCLKFreq()的返回值以及SysTick的配置。HAL_Init()里默认把 SysTick 配成 1ms 中断但如果SystemClock_Config()在HAL_Init()之后才改时钟SysTick 的重装载值不会自动更新需要重新调用HAL_InitTick()。第三步加一个串口发送用printf重定向到 USART1打印一行System Clock: %d Hz。串口助手收到正确频率且不乱码说明时钟和串口都对了。这一步我一般会顺手把__io_putchar()和fputc()重定向写好后面调试会一直用。重定向代码放在main.c里#include stdio.h #ifdef __GNUC__ int __io_putchar(int ch) #else int fputc(int ch, FILE *f) #endif { HAL_UART_Transmit(huart1, (uint8_t *)ch, 1, HAL_MAX_DELAY); return ch; }huart1是你在main.c里定义的 UART 句柄HAL_UART_Transmit的最后一个参数是超时时间HAL_MAX_DELAY表示一直等。如果你在中断里调用printf不要用HAL_MAX_DELAY改成具体的毫秒数否则会卡死中断。这三步走完基本能确认 STM32Cube_FW_F1_V1.8.0 的库文件挂载、时钟配置、外设初始化、中断向量表都没问题。从那以后我每次新建 F1 工程都会先跑一遍这三步再开始写业务逻辑。看起来多花十分钟但比后面调试半天时钟问题划算得多。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?