STM32F103 HAL库环境搭建与DAP调试避坑指南
发布时间:2026/10/29 16:31:56 锦皓数字建站

简介《野火EmbedFire STM32 HAL库开发实战指南—基于F103》是一份面向嵌入式初学者与入门工程师的PDF教程围绕STM32 HAL库展开帮助读者从工具链搭建过渡到外设编程与小型项目实践。全书从KEIL5安装、STM32芯片包配置讲起逐步说明DAP仿真器的硬件连接、Debug选项设置与程序下载流程再进入GPIO、中断、定时器和串口通信等常用外设的HAL库配置方法并配合LED闪烁、串口协议实现与数据采集等实战案例配有硬件说明、学习顺序建议与技术论坛入口适合按章节循序推进。整包仅含1个PDF文件体积约43.41MB单文件即可离线阅读便于打印或分屏对照操作。目前已有1881人学习可作为STM32入门阶段系统梳理HAL库与开发环境的参考资料。1. 版本先对上F103-MINI 的 STM32 HAL 库环境装错一步后面全是坑在 F103-MINI 这块板子上第一次安装 STM32 HAL 库开发环境真正让人反复返工的往往不是代码而是版本对不上。这套教程对应的工具链固定成四件事Keil5MDK-ARM作为 IDE 与编译器、STM32F1 系列芯片包、DAP 仿真器驱动、STM32CubeMX 负责生成 HAL 库初始化代码。四个人凑齐了基于 ARM Cortex-M3 的 STM32F103 才能从“能编译”走到“能下载、能跑”。少装一个芯片包新建工程时器件列表里就是空的仿真器选项卡填错一格编译零错误却死活下载不进去CubeMX 的时钟树点错一个倍频系数串口打印全是乱码。这一套下来最怕的就是顺序乱先装 CubeMX 再补 Java、先建工程再补 pack、装完 Keil 直接插板子。下面按我实际装机的顺序走一遍每步都给出能直接照抄的参数最后再把装坏之后怎么救讲清楚。2. Keil5 安装与 STM32F1 芯片包从双击安装包到能新建工程Keil5 装得好不好差别不会立刻出现只有过了两三天才会在某次编译或某次下载时别扭地显现出来。所以每次给新机器配环境我都不图快按照“确认安装包 → 定安装路径 → 装芯片包 → 定编译器版本 → 配宏和头文件路径”这个固定顺序走宁可多花二十分钟也省得后面花一整晚排查。2.1 安装前三件事安装包版本、路径、管理员权限安装包拿到手先看文件名和大小MDK5 的安装包是个单独的 exe几百兆量级如果下载下来只有几十兆那多半是下载器抓到的错误页面装到一半会提示组件缺失。安装之前把 Keil、CubeMX、串口助手全部关掉杀毒软件的实时防护临时关掉——它会拦截 Pack 解压和 armcc 的临时文件写入表现是安装进度条卡在最后一步不动或者装完打开 Pack Installer 一片空白。安装路径是第一个分水岭。我见过太多因为路径里带中文或空格导致 Pack Installer 打不开、编译中间文件报权限错误的案例所以路径规则我从来不含糊安装路径实际结果C:\Keil_v5推荐短、无空格、无中文Pack 写入不会触发 UAC 拦截C:\Program Files\Keil_v5部分组件写入需要提权编译中间文件偶发权限报错D:\嵌入式\keil5中文路径在 Pack Installer 与 armcc 调用链里容易出错D:\Keil v5路径带空格个别老版本工程脚本解析失败安装程序右键选择“以管理员身份运行”不要直接双击。安装过程中会弹出一堆驱动安装确认ULINK、J-LINK 之类的 USB 驱动这里全部点安装即便你手上只有 DAP 也用得上后面换仿真器不用重装。装完最后会问要不要启动 Pack Installer先点否——芯片包我们下一步单独装让它自己联网拉容易超时。提示安装完成后先别插开发板。Keil 首次启动会初始化许可证和配置文件此时插着板子Windows 有可能把板子的虚拟串口识别成未知设备并装上错误驱动后面排查时会多出一条干扰线索。2.2 装完立刻补 STM32F1 芯片包别等新建工程才发现列表是空的Keil5 本体只是个空壳它不认识任何一颗芯片。所谓“芯片包”Device Family PackDFP里装的是三样东西启动文件startup_stm32f10x_hd.s、Flash 下载算法、调试时用的寄存器描述文件SVD。少了它新建工程时 Device 那一栏就是空的下载时 Programming Algorithm 里也选不到任何算法。安装有两条路我一般优先走离线拿到Keil.STM32F1xx_DFP.x.x.x.pack文件直接双击它会自动关联到已安装的 Keil 并开始安装或者在 Keil 里点工具栏的 Pack Installer左侧 Devices 找到 STM32F1 Series右侧 Packs 里点 Install走在线安装。离线包的优势是版本可控团队里几个人装出来的 DFP 版本一致编译行为就不会有差异。在线安装则可能出现“当前最新版 DFP 已经不支持老启动文件命名”的情况表现为编译时报找不到startup_stm32f10x_hd.s。装完之后验证方式很简单Keil 里 Project → New uVision Project随便找个目录建弹出的器件选择框里能展开 STM32F1 Series 并看到 STM32F103RC、STM32F103VE 这类具体型号就说明包装好了。看不到就打开 Pack Installer 看 STM32F1 那一行是不是显示 Installed。顺带说一句 Flash 算法怎么选这步在后面 DAP 配置里还会用到先记住对应关系芯片型号后缀Flash 容量密度等级Keil 里对应的下载算法C8 / CB64K / 128KMedium-densitySTM32F10x Med-density FlashRC / VC256KHigh-densitySTM32F10x High-density FlashZE / VE512KHigh-densitySTM32F10x High-density Flash选错算法的典型现象是下载时提示“Flash Download failed - Target DLL has been cancelled”或者下载进度到一半直接报擦除错误。程序本身没问题问题在于算法只认识前 128K 的地址空间。2.3 编译器版本AC5 与 AC6 的取舍还有那个 32KB 限制这个坑是我踩得最深的一个。MDK 从 5.37 版本开始安装包里不再自带 ARM Compiler 5也就是大家常说的 AC5、armcc默认只带 ARM Compiler 6armclang。而这套 F103 教程里的例程当年是按 AC5 写的语法上有一批 AC6 不认的写法__asm关键字、内联汇编的__ASM volatile形式、部分老版本__weak修饰。打开工程直接编译报错信息通常是Error: *** Target LED uses ARM-Compiler Default Compiler Version 5 which is not available.解决办法有两个看你的场景选想和教程完全对齐去官网单独下载 ARM Compiler 5 的安装包装到 Keil 目录下的ARM\ARMCC然后在 Options for Target → Target → ARM Compiler 里选 “Use default compiler version 5”想用新工具链就切到 AC6然后把内联汇编改成__ASM volatile的写法__weak换成__attribute__((weak))。工程量不大但凡是教程里带汇编的章节启动文件、寄存器操作都得跟着改。我自己的习惯是跟教程走一遍的时候用 AC5把例程跑通等自己写业务代码再切 AC6顺手把编译告警清干净。两种方式不要混着来同一个工程里一会儿 AC5 一会儿 AC6编出来的中间文件不兼容最直接的表现是“全编译能过、增量编译报一堆重复定义”。另一个绕不开的限制是 MDK-Lite 的 32KB 代码上限。F103-MINI 这套教程前期的点灯、按键、串口例程基本都在这个范围内超了会报Error: L6050U: The code size of this image (34xxx bytes) exceeds the maximum allowed for this version of the linker.这不是环境装坏了是许可证限制。前期验证阶段够用等你要往工程里塞 FatFs、USB 之类的大组件时再考虑正式许可证。别在环境搭建阶段为了这个报错反复重装浪费时间。2.4 新建工程的三个宏和五条头文件路径工程建起来之后真正决定能不能编译通过的是 Options for Target 里的两处配置。第一处是 C/C 选项卡的 Define 宏/* Options for Target - C/C - Define多个宏用英文逗号分隔不要加分号 */ USE_HAL_DRIVER, STM32F103xEUSE_HAL_DRIVER决定了stm32f1xx.h里要不要把 HAL 库的头文件包含进来删掉它编译到HAL_GPIO_Init就会报隐式声明STM32F103xE决定选中哪一份启动文件和寄存器定义其中xE对应 512K Flash 的大容量型号如果你的板子是 RCT6256K这里要写STM32F103xE对应的中等容量宏得看具体器件头文件的分类写错了的表现是中断向量表和实际芯片对不上——中断进不去或者进了错误的中断服务函数。这类问题属于典型的“编译能过、运行不对”排查起来毫无头绪。第二处是 Include Paths也就是头文件搜索路径。HAL 库的目录结构分层很清楚少加一条就报cannot open source input file stm32f1xx_hal.h..\User ..\Drivers\STM32F1xx_HAL_Driver\Inc ..\Drivers\STM32F1xx_HAL_Driver\Inc\Legacy ..\Drivers\CMSIS\Device\ST\STM32F1xx\Include ..\Drivers\CMSIS\Include第一条放用户代码和main.h第二条是 HAL 驱动本体Legacy目录里是旧版 API 的兼容层老教程里偶尔会用到后两条是 CMSIS 的器件定义和内核定义core_cm3.h就在最后一条路径下。这五条缺一条编译就停在某个头文件找不到的地方。我在团队里推行过一个笨办法把这段路径列表直接贴在工程目录的readme.txt里谁装环境就照着填比口头描述靠谱得多。3. DAP 仿真器配置Debug、Utilities 与 Settings 三处要一起改编译通过只是走了一半能不能把程序烧进芯片、烧进去之后能不能跑全看仿真器这一块。DAP 这类 CMSIS-DAP 仿真器便宜、免驱Windows 10 以上自带 HID 驱动、支持 SWD 两线调试是 F103 这类小板子最省事的选择。但它有个特点Keil 里跟它相关的配置分散在三个地方只改其中一处程序要么下载失败要么下载成功但不运行。3.1 接线与供电SWD 四根线的顺序和共地DAP 仿真器到开发板之间一般接四根线3V3、GND、SWDIO、SWCLK。有的仿真器还带RESET可以接也可以不接接了之后“Connect under Reset”模式更好用不接就靠软件复位。接线的顺序我习惯先接 GND 再接电源和信号理由很朴素带电插拔时先接触地线能避免信号线上出现瞬态电平把 IO 打坏虽然概率不高但一块板子几十块钱没必要赌。供电这块得分清楚谁给谁供电。F103-MINI 板上自带 USB 供电和 3.3V LDO正常情况是板子自己供电仿真器的3V3悬空不接只接GND、SWDIO、SWCLK三根。如果板子没插 USB只靠仿真器的 3.3V 供电遇到功耗稍大的外设比如点亮一排 LED、驱动 OLED就会掉压表现为下载时好时坏、调试时突然断开连接。我一般统一约定板子自己插 USB 供电仿真器只做信号连接。接好之后插上电脑设备管理器里应该能看到一个 HID 设备名字里带 CMSIS-DAP。看不到就换根 USB 线——这个血泪经验值得单独说一句很多“仿真器坏了”最后查出来是充电线只有电源针没有数据针。3.2 Debug 与 Utilities 两个选项卡必须指向同一个驱动打开 Options for Target → Debug 选项卡右上角的下拉框选CMSIS-DAP Debugger然后点旁边的 Settings。弹出的窗口里做三件事配置位置配置项建议取值说明Debug → SettingsPortSW只用 SWD 两线JTAG 五线在这类板子上没必要Debug → SettingsMax Clock1 MHz排线较长或干扰大时降频识别更稳Debug → SettingsSW Device能列出 IDCODE这一栏为空说明根本没连上芯片UtilitiesUse Target Driver for Flash Programming勾选并选 CMSIS-DAP下载和调试共用同一驱动Utilities → SettingsProgramming Algorithm按上文的密度等级选决定擦写哪片 FlashSettings 窗口左侧 SW Device 那一栏是判断生死的分水岭。如果里面显示了一串 IDCODE 和器件名称说明链路通了如果显示Error: No Cortex-M SW Device Found先别怀疑软件配置去查供电和接线具体排查方法放在第 5 章。Utilities 选项卡容易被忽略。它的作用是告诉 Keil“烧录时用哪个工具”如果不勾选Use Target Driver for Flash ProgrammingKeil 会去用默认的 ULINK 配置现象是点下载按钮后弹窗提示找不到 ULINK。Debug 选项卡里配好了 DAPUtilities 里没跟上这是最典型的“只改一半”。3.3 Flash 算法、复位方式与 Reset and Run下载成功却不跑的三个源头点 Utilities 旁边的 Settings进入 Flash Download 页这里有三项配置需要一次性配好第一项是 Programming Algorithm 列表。点 Add按 2.2 节的密度对照表选对应算法比如 256K 的 RCT6 选STM32F10x High-density Flash 256K。列表里如果出现两条算法删掉多余的那条否则会出现“擦除地址越界”的报错。第二项是 Reset 方式。默认勾选Reset and Run的话下载完成后芯片会自动复位并运行不勾的话程序烧进去了但停在复位向量处等你手动按复位键。很多新手的第一反应是“下载成功了怎么灯不亮”实际上板子正在等你按一下 NRST。第三项是Connect模式。正常用Normal如果板子里的程序把 SWD 引脚复用成了普通 GPIO常见于低功耗例程芯片一上电就脱离了调试状态这时要改成Connect under Reset让仿真器在芯片复位还没跑用户代码的那一瞬间抢占连接。这个选项在 Debug → Settings 里也能设两处保持一致。配置完点 OK回到主界面按 F8 或者点下载按钮。输出窗口正常应该看到这几行Load ..\\Objects\\LED.axf Erase Done. Programming Done. Verify OK. Application running ...看到Application running才算真正走完。只看到Programming Done而没有最后一行说明 Reset and Run 没勾或者复位电路上有问题。4. STM32CubeMX 与 HAL 库工程Java 依赖、时钟树与用户代码区教程里前后出现了两种建工程的方式一种是纯手工建从启动文件、CMSIS、HAL 驱动一个个往里加另一种是用 CubeMX 图形化配置后生成。前者适合理解 HAL 库的目录结构后者是实际项目里的主流做法。CubeMX 这条路上有三个容易翻车的点Java 运行时、时钟树、用户代码区。4.1 Java 运行时先装CubeMX 才不会白屏CubeMX 是基于 Java 的桌面程序早期版本对 Java 运行时JRE有硬依赖机器上没装或者装了个位数不对的版本双击图标后要么没反应要么弹出一个空白窗口然后消失。安装顺序必须是先 Java 后 CubeMX装 64 位 JRE安装路径同样不要带中文和空格再装 CubeMX 本体安装目录单独建不要塞进 Keil 目录里首次启动时如果提示选择固件库仓库路径Repository Folder指定一个纯英文路径比如D:\STM32Cube\Repository。第三点很关键。CubeMX 生成代码时需要对应系列的固件包F1 系列对应STM32Cube_FW_F1_Vx.x.x仓库路径里带中文或者没权限写入生成代码时会卡在最后一步报一个含义模糊的Error generating code。老版本 CubeMX 需要手动点 Help → Manage embedded software packages 去下载并安装 F1 固件包新版可以直接从本地导入离线包。如果公司网络访问外网受限就提前把固件包下载好放在仓库目录下省得生成代码时干等。4.2 时钟树HSE 8MHz 到 72MHz 这条链的每一环新建工程、选好 STM32F103 具体型号之后第一件事是确认时钟源。F103 有两个时钟源可选内部高速时钟 HSI8MHz精度差、受温度影响和外部高速时钟 HSE板上那颗 8MHz 晶振精度高。板上既然焊了晶振就选 HSE然后在 Clock Configuration 页把时钟树配到 72MHz 上限HSE 输入 8MHzPLL Source 选 HSEPLL Mul 选 ×9得到 72MHzSystem Clock Mux 选 PLLCLKSYSCLK 就是 72MHzAHB Prescaler 选 /1HCLK 保持 72MHzAPB1 Prescaler 选 /2得到 36MHz——这是 F103 的硬性限制APB1 上的外设时钟不能超过 36MHzAPB2 Prescaler 选 /1保持 72MHz。CubeMX 会在时钟图上用红色标出超限的节点看到红色就说明某一环配错了。配完之后它生成的SystemClock_Config函数大致长这样理解这段代码比记住界面上点了哪几下更重要void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct {0}; /* 打开 HSE 并配置 PLL8MHz * 9 72MHz */ RCC_OscInitStruct.OscillatorType RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.PLL.PLLState RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL RCC_PLL_MUL9; HAL_RCC_OscConfig(RCC_OscInitStruct); /* 切换系统时钟源到 PLL并设置各总线分频 */ 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; RCC_ClkInitStruct.APB2CLKDivider RCC_HCLK_DIV1; HAL_RCC_ClockConfig(RCC_ClkInitStruct, FLASH_LATENCY_2); }HSEPredivValue是 F103 特有的预分频接 8MHz 晶振时选不分频PLLMUL决定倍频系数写错就会得到 36MHz 或者干脆起振失败。FLASH_LATENCY_2是 72MHz 下的等待周期数低于 48MHz 用 1、高于 48MHz 必须用 2配错的表现是程序跑飞、偶发 HardFault这类问题最容易让人误以为是代码写错了。还有一个关联参数在stm32f1xx_hal_conf.h里/* 外部晶振频率必须和板子上实际焊的晶振一致 */ #if !defined (HSE_VALUE) #define HSE_VALUE 8000000U /* 8MHz填成其他值会导致串口波特率整体偏移 */ #endifHAL 库靠这个宏计算各个外设的时钟进而算出串口波特率的分频值。如果板上是 8MHz 而你把它改成了 12MHz代码能编译能跑但串口输出全是乱码且没有任何报错提示。我在现场排查过一次整整花了两个小时最后发现就是这一行。4.3 工程属性与用户代码区重新生成代码不丢逻辑的唯一办法Project Manager 页里有两个设置决定了生成出来的工程能不能直接编译Toolchain/IDE 选MDK-ARM版本选 V5Code Generator 里勾选Generate peripheral initialization as a pair of .c/.h files per peripheral这样每个外设的初始化代码会单独成文件后面改起来不会全挤在main.c里。最重要的一条是用户代码区。CubeMX 生成的代码里所有你能改的地方都被注释块包着/* USER CODE BEGIN 2 */ /* 你自己的初始化代码写在这里 */ /* USER CODE END 2 */ while (1) { /* USER CODE BEGIN 3 */ HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin); HAL_Delay(500); /* USER CODE END 3 */ }BEGIN和END之间的内容会被 CubeMX 记住下次重新生成代码时原样保留写在注释块外面的代码重新生成时会被直接覆盖掉而且没有任何提示。这个破坏属于典型的“悔之晚矣”我现在的习惯是工程一建好先往USER CODE区里写一行// here占位确认重新生成代码后它还在再开始写业务逻辑。另外生成的工程第一次打开时Keil 里的宏定义和头文件路径已经自动配好了就是第 2.4 节那套但芯片包和编译器版本还是得自己确认一遍。CubeMX 不认识你机器上装的是 AC5 还是 AC6它只写工程文件。5. 排查清单六条把环境装坏的真实记录下面这六条都是我在装这套环境时真实遇到过的顺序按遇到频率从高到低排每条按“现象 → 原因 → 解决”写遇到问题可以对着翻。第一条编译报cannot open source input file stm32f1xx_hal.h现象是点编译第一行就中断错误指向stm32f1xx.h里的包含语句。原因是 Include Paths 里缺了Drivers\STM32F1xx_HAL_Driver\Inc或者虽然加了但路径层级写错一级比如加成了Drivers\Inc。解决方式是对着第 2.4 节的五条路径逐条核对Keil 的 C/C → Include Paths 里能看到每一条的完整展开路径发现哪条显示为无效就删掉重新加。还有一种隐蔽情况工程是从别人机器上拷来的路径里带着对方的绝对路径比如C:\Users\xxx\Desktop\...这种路径在你机器上不存在必须全部换成相对路径..\Drivers\...。第二条下载时提示No Cortex-M SW Device Found或RDDI-DAP Error现象是点下载按钮Keil 卡住几秒后弹错误框Debug Settings 左侧 SW Device 一片空白。原因按概率排一是板子没供电只接了 SWDIO/SWCLK/GND 却没插 USB二是杜邦线接触不良或者SWDIO和SWCLK接反了三是芯片里的程序把 SWD 引脚复用掉了。解决办法是先用万用表量板子 3.3V 是否有电再换一组线最后在 Debug → Settings 里把 Connect 改成Connect under Reset并勾选Reset after Connect让仿真器在芯片跑用户代码之前抢占连接连上之后立刻全片擦除把复用引脚的程序清掉。第三条下载成功但程序不运行必须手动按复位现象是输出窗口显示Programming Done和Verify OK没有Application running板子毫无反应。原因通常是 Reset and Run 没勾选或者 BOOT0 跳线被拨到了 1从系统存储器启动。解决方式是 Utilities → Settings → Flash Download 里勾上Reset and Run然后检查板子上的 BOOT0、BOOT1 跳线帽位置正常运行必须都在 0。这条我遇到过两次第一次排查了半小时硬件最后发现是同事借板子时把跳线帽挪了位置。第四条串口打印全是乱码但程序逻辑明显在跑现象是串口助手收到一堆烫烫烫或者随机字符改成各种波特率都不对。原因基本锁定在时钟HSE_VALUE定义的频率和板载晶振不一致或者时钟树配成了 HSI 而代码里按 HSE 算。解决方式是先确认板上晶振丝印是 8.000再检查stm32f1xx_hal_conf.h里HSE_VALUE是否为8000000U最后用调试器读一下SystemCoreClock变量的实际值具体方法见第 6 章。三个地方对齐了115200 一次就能通。第五条CubeMX 重新生成代码后自己写的逻辑全没了现象是改完 CubeMX 配置点 GENERATE CODE回到 Keil 编译发现main.c里的初始化代码消失编译报一堆未定义。原因就是代码写在了USER CODE BEGIN/END注释块外面被生成器当成“可覆盖区域”清掉了。解决办法只能是从版本管理里找回上一次的代码然后全部挪进用户代码区。如果没做版本管理那就只能重写——这也是为什么我从那以后所有 CubeMX 工程都在 git 里留一个初始提交专门用来做“后悔药”。第六条Keil 编译报Target uses ARM-Compiler V5 which is not available现象是打开老工程或者教程配套工程一编译就报编译器不存在Target 选项卡里 ARM Compiler 显示为灰色或者选不到 V5。原因是 MDK 5.37 之后的版本不再内置 AC5。解决办法有两种装回 ARM Compiler 5 组件或者把工程切到 AC6 并处理语法差异。切 AC6 时最先撞上的通常是启动文件里的内联汇编和__weak修饰报错集中在core_cm3.h和stm32f1xx_hal_def.h这时不要急着改库文件先在 C/C → Misc Controls 里加上-Wno-inline-asm之类的兼容开关能压掉大部分告警。真正需要改代码的地方不多但每一处都得改对否则编出来的行为不对。6. 装完怎么验证点灯、读时钟、串口打一行字环境装完很多人点一下编译通过就以为结束了等到写业务代码时才发现问题那时候已经分不清是新代码的问题还是环境的问题。我的做法是做三步验证每步都给出唯一的、可观测的结果任何一步不对都能立刻定位到某一层。第一步是点灯验证的是 HAL 库、启动文件、下载链路整条通路/* main.c 的 while(1) 内注意写在 USER CODE BEGIN 3 和 END 3 之间 */ while (1) { HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin); /* 翻转 LED 电平引脚宏由 CubeMX 生成 */ HAL_Delay(500); /* 依赖 SysTick 的 1ms 中断 */ }HAL_Delay的精度取决于 SysTick 配置HAL_Init()里会调用HAL_InitTick把它配成 1ms。如果在高于 SysTick 优先级的中断服务函数里调HAL_Delay程序会直接卡死——因为默认的TICK_INT_PRIORITY是最低优先级中断嵌套时 SysTick 永远进不来。这个现象极具迷惑性表现为“灯亮过一次就再不动了”。第二步是读时钟验证的是时钟树和HSE_VALUE/* 在 main 函数里 HAL_Init 之后打印一次 */ printf(SYSCLK %lu Hz, HCLK %lu Hz\r\n, HAL_RCC_GetSysClockFreq(), HAL_RCC_GetHCLKFreq());正常应该输出SYSCLK 72000000 Hz, HCLK 72000000 Hz。如果读到 8000000说明 PLL 没起来系统还在用 HSI如果读到 72000000 但串口是乱码那问题就在波特率计算或者HSE_VALUE定义上跟时钟树无关。用 printf 需要先把fputc重定向到串口这个在教程的串口章节里有现成写法直接抄。第三步是断点验证验证的是调试链路。在HAL_GPIO_TogglePin那一行打个断点按 F5 全速运行程序应该停在这里打开 Peripherals 菜单里的 System Viewer能看到 GPIO 寄存器的值随着单步执行变化。这一步能过说明 DAP 的下载、调试、寄存器查看三条链路都是通的。验证步骤观测点通过的标志失败指向点灯板载 LED500ms 周期闪烁HAL 库、启动文件、下载算法读时钟串口输出72000000 Hz时钟树、HSE_VALUE、晶振断点Keil 停住 寄存器变化单步可观测DAP 配置、复位方式三步全部通过之后这套环境才算真正装完。后面再学 GPIO 输入、外部中断、定时器出的问题都是配置问题不会再有“到底是环境坏了还是代码错了”这种扯不清的情况。从那以后我每次把环境交付给别人或者换一台新机器重装都强制走一遍这三步——点灯、读时钟、打一行字一步都不跳。省下来的那十分钟往往要用一晚上去还。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。