news 2026/9/29 1:13:25

STM32CubeMX与Keil5安装配置全攻略:从零搭建嵌入式开发环境

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32CubeMX与Keil5安装配置全攻略:从零搭建嵌入式开发环境

嵌入式开发入门的第一道门槛,往往不是写代码,而是把工具链装明白。STM32CubeMX 和 Keil5 这套组合,几乎是国内 STM32 学习者的标配起点,但真正动手装的时候,各种问题能把人劝退:CubeMX 打开一片空白、固件包下载卡住、Keil5 编译报错找不到器件、烧录时提示找不到目标芯片。这篇内容就是把我自己反复装过十几台机器、帮学弟学妹远程排障几十次的经验,完整地摊开讲一遍。不管你是刚买开发板的大一新生,还是从 51 单片机转过来的老手,只要跟着走,装完就能点亮第一颗 LED。下面涉及的所有工具都从官方渠道获取,安装路径、版本搭配、常见报错的根因,我都会逐个说清楚。

1. 装之前先想明白:这套工具链到底谁管什么

很多人装到一半就乱了,根本原因是没搞清楚 STM32CubeMX 和 Keil5 各自的职责边界。把这两个软件的关系理顺,后面每一步你都知道自己在干什么,遇到问题也能自己判断该去哪个软件里找。

1.1 CubeMX 是"配置生成器",不是编译器

STM32CubeMX 的核心工作是图形化配置芯片的引脚、时钟树、外设参数,然后一键生成初始化代码。它本身不负责编译,也不负责烧录。你可以把它理解成一个"代码脚手架生成器"——你告诉它要用哪些外设、引脚怎么分配、时钟跑多少兆,它帮你把底层初始化那几百行寄存器配置代码写好。

这一点非常关键。新手最常见的误解是以为装了 CubeMX 就能写程序了,结果生成完代码发现没法编译,又回头折腾。正确的认知是:CubeMX 负责"生成工程骨架",Keil5 负责"编辑、编译、烧录"。

CubeMX 还有一个容易被忽略的价值:它内置了芯片选型器和功耗计算器。选型的时候可以直接按外设需求筛选,比如你要两个 SPI、一个 CAN、一个 USB,它能直接列出满足条件的型号,省得对着数据手册一页页翻。

1.2 Keil5 是"编辑器+编译器+调试器"三合一

Keil MDK5(也就是大家说的 Keil5)是 ARM 官方工具链的集成环境,它包含了代码编辑器、ARMCC/ARMCLANG 编译器、调试器和烧录功能。你写的 C 代码在这里编译成 hex 或 axf 文件,再通过 ST-Link 或 J-Link 烧进芯片。

Keil5 有个版本坑必须提前说:MDK 从 5.37 版本开始,默认不再自带 ARM Compiler 5(AC5),只带 ARM Compiler 6(AC6)。而很多老教程、老工程模板是基于 AC5 写的,用 AC6 编译会报一堆语法错误。所以如果你要跟着老教程走,建议装 5.36 或更早的版本,或者单独把 AC5 编译器补装上去。这个细节后面第 4 节会详细讲。

1.3 两者如何衔接:从 .ioc 到 .uvprojx

CubeMX 生成的工程,如果目标工具链选的是 MDK-ARM,会输出一个.uvprojx文件,这就是 Keil5 的工程文件。同时还会生成.ioc文件,这是 CubeMX 自己的配置文件,以后要改配置,双击.ioc重新打开 CubeMX 就行。

衔接的关键在于:CubeMX 生成代码时,用户代码必须写在/* USER CODE BEGIN */和/* USER CODE END */之间。这样下次在 CubeMX 里改了配置重新生成,你写的代码不会被覆盖掉。这个规则我从第一天就被坑过——第一次不知道,把代码写在外面,改了个引脚重新生成,几百行代码全没了。

工具核心职责不负责的事关键文件
STM32CubeMX引脚/时钟/外设配置,生成初始化代码编译、烧录、调试.ioc、生成的 .c/.h
Keil MDK5编辑、编译、烧录、调试图形化配置外设.uvprojx、.uvoptx
ST-Link Utility / STM32CubeProgrammer独立烧录、读保护设置编译.hex、.bin

2. 下载渠道与版本选择:别在第一步就埋雷

装软件最怕的就是下载来源不对,装完一堆莫名其妙的问题。这一节把两个软件的获取方式、版本搭配讲透,让你少走弯路。

2.1 STM32CubeMX 的获取与版本考量

CubeMX 是 ST 官方免费工具,直接去 ST 官网搜 "STM32CubeMX" 就能找到下载页。官网下载需要注册一个 ST 账号,填个邮箱就行,不花钱。下载下来是一个安装包,Windows 平台大概是几百兆。

版本选择上,我建议用较新的稳定版,比如 6.10 或 6.11 这个区间。太老的版本(6.0 以前)对新芯片支持不好,比如 G0、H5 这些新系列可能找不到;太新的版本偶尔会有小 bug,而且对老 Java 环境依赖有变化。CubeMX 是基于 Java 的,新版自带 JRE,一般不用你单独装 Java,但如果启动报 Java 相关错误,那就是 JRE 环境的问题,后面排错章节会讲。

注意:CubeMX 安装路径千万不要带中文和空格。我见过有人装在D:\我的软件\STM32CubeMX下面,结果固件包路径解析出错,一直提示找不到包。路径用纯英文,比如D:\STM32\STM32CubeMX。

2.2 Keil MDK5 的获取与"版本陷阱"

Keil MDK5 的官方下载地址在 keil.com(现在归属 Arm)。官网下载的是 MDK-ARM 安装包,同样需要填个表单。这里要重点说的是版本问题。

MDK 的版本号规律是 5.xx,比如 5.36、5.38、5.40。前面提到的编译器分水岭在 5.37:

  • 5.36 及以前:自带 ARM Compiler 5(AC5),兼容绝大多数老教程和老工程。
  • 5.37 及以后:默认只带 ARM Compiler 6(AC6),AC5 需要单独下载安装。

如果你是完全的新手,跟着网上大多数教程走,建议直接装 5.36。如果你要用比较新的芯片或者想用 AC6 的新特性,那就装新版然后手动补 AC5。我个人的做法是装 5.38,然后单独把 AC5 编译器装到 Keil 的 ARM 目录下,这样两套编译器都能用,工程里可以自由切换。

2.3 芯片支持包(Device Family Pack)的安装逻辑

Keil5 装完之后,你会发现新建工程时找不到 STM32 的芯片型号。这是因为 Keil5 本体不含芯片支持包,需要单独安装 DFP(Device Family Pack)。

DFP 有两种装法:一种是在 Keil 里点 Pack Installer 在线装,另一种是去 Keil 官网下载离线包手动装。在线装的问题是服务器在国外,速度慢还容易断;离线包装得快,推荐用离线方式。

ST 的 DFP 包命名类似Keil.STM32F1xx_DFP.2.4.0.pack,F1、F4、H7 这些系列是分开的包。你用什么系列就装什么包,不用全装,全装会占好几个 G 的空间。

3. STM32CubeMX 安装全流程与首次配置

这一节进入实操。我把每一步的意图都讲清楚,你照着做就行。

3.1 安装过程的关键选项

运行 CubeMX 安装包,前面几步都是常规的下一步。到了选择安装路径那一步,记住前面说的:纯英文路径。另外有一个选项是"是否创建桌面快捷方式"和"是否关联 .ioc 文件",这两个都勾上,方便以后双击 .ioc 直接打开。

安装过程中它会让你确认是否安装 ST-Link 驱动。这个建议勾上,虽然后面装 Keil 或 CubeProgrammer 时也会装驱动,但多装一次没坏处,能避免烧录时找不到设备。

安装完成后第一次启动,CubeMX 会问你是否要检查更新、是否要登录 ST 账号。更新可以检查,登录可以跳过,不登录也能正常用,只是不能从里面直接下载固件包(需要手动指定本地包路径)。

3.2 固件包(Firmware Package)的下载与本地管理

这是 CubeMX 最容易卡住的地方。CubeMX 生成代码需要对应的 HAL 固件包,比如你选 STM32F103C8T6,就需要 STM32CubeF1 这个固件包。

默认情况下 CubeMX 会尝试从网上在线下载固件包,但国内网络下载经常失败或者极慢。解决办法有两个:

方法一:手动下载离线包。去 ST 官网的 GitHub 仓库(搜索 STM32CubeF1 就能找到)下载对应的固件包压缩包,解压后放到 CubeMX 的固件仓库目录。默认路径是C:\Users\你的用户名\STM32Cube\Repository。放进去之后,在 CubeMX 的 Help -> Manage embedded software packages 里就能看到本地已安装的包。

方法二:在 CubeMX 里指定本地包路径。在 Manage embedded software packages 界面,有个 "From Local" 按钮,可以指定你下载的离线包位置。

固件包的版本也要注意。同一个系列有多个版本,比如 F1 有 1.8.0、1.8.5 等。新版本修复了一些 bug,但偶尔也会引入新的兼容问题。一般用较新的稳定版就行,如果遇到某个外设的 HAL 函数行为异常,可以回退到上一个版本试试。

提示:固件包目录不要放在中文路径下,也不要有空格。我遇到过有人把 Repository 目录设到D:\学习资料\STM32 固件,结果 CubeMX 死活识别不到包。

3.3 中文界面与基础偏好设置

CubeMX 支持中文界面。在 Help -> Updater Settings 或者直接在安装时选择语言,也可以后续在设置里切换。不过我个人建议新手阶段用英文界面,因为网上绝大多数教程、报错信息都是英文的,用中文界面反而对不上。等你熟悉了再切中文无所谓。

另外建议在 Project Manager 里把默认的工具链设成 MDK-ARM,这样每次生成工程不用再选。默认的代码生成选项里,"Copy only necessary library files" 建议勾上,这样生成的工程只包含用到的库文件,工程体积小,编译也快。

4. Keil MDK5 安装与编译器配置的深水区

Keil5 的安装看着简单,但编译器配置和芯片包这两块是重灾区,很多人卡在这里。

4.1 安装路径与许可证管理

Keil5 安装同样是纯英文路径。安装完成后需要激活许可证,否则有 32KB 代码限制。对于 STM32F103C8T6 这种 64KB Flash 的芯片,32KB 限制意味着稍微大一点的工程就编译不过。

许可证的获取方式这里不展开(涉及第三方工具,合规性自己把握),但要说的是:如果你只是学习,很多开发板厂商会提供正版授权的 License,买板子的时候问一下。另外 Keil 也有社区版(MDK-Community),非商业用途可以免费申请,功能上对学习完全够用。

4.2 ARM Compiler 5 与 6 的共存配置

前面说了 5.37 之后默认只有 AC6。如果你需要 AC5,去 Arm 官网下载 "Arm Compiler 5" 的独立安装包,装到一个目录,然后在 Keil 的 Project -> Manage -> Project Items -> Folders/Extensions 里,把 AC5 的路径填进去。

配置好之后,在工程的 Options for Target -> Target 标签页,有个 "ARM Compiler" 下拉框,可以选 "Use default compiler version 5" 或 "6"。老工程选 5,新工程可以选 6。

AC5 和 AC6 的差异不只是版本号。AC6 基于 LLVM/Clang,对代码规范要求更严,很多在 AC5 下能过的写法(比如隐式类型转换、未初始化变量)在 AC6 下会报 warning 甚至 error。所以切换编译器后如果报一堆错,先别慌,多半是代码规范问题,不是环境坏了。

4.3 芯片支持包(DFP)的离线安装

前面提过 DFP 的离线安装。具体操作:去 Keil 官网的 Pack 下载页,找到 STM32 对应系列的 DFP,下载.pack文件。然后双击这个文件,它会自动调用 Keil 的 Pack Installer 进行安装。或者打开 Keil,点 Pack Installer 图标,在 File -> Import 里导入 pack 文件。

装完之后,新建工程时在 Device 里搜索你的芯片型号,比如 "STM32F103C8",就能找到了。如果搜不到,说明 DFP 没装成功,检查一下 pack 文件是不是对应系列、安装时有没有报错。

问题现象可能原因解决方向
新建工程找不到芯片DFP 未安装或装错系列装对应系列 DFP
编译报 "cannot open source input file"库文件路径不对检查工程包含路径
编译报 AC6 语法错误工程基于 AC5 写的切换编译器到 AC5
烧录提示 "No target connected"驱动未装或接线问题装 ST-Link 驱动,检查 SWD 接线

5. 从零跑通第一个工程:CubeMX 配置到 Keil 烧录

工具装好了,得跑通一个完整流程才算数。这一节用一个最经典的例子——STM32F103C8T6 点亮 PC13 上的 LED——把整个链路走一遍。

5.1 CubeMX 新建工程与引脚配置

打开 CubeMX,点 "New Project",在芯片选择器里输入 STM32F103C8,选中 STM32F103C8Tx,点 Start Project。

进入配置界面后,先做几件事:

第一,配置时钟源。在 System Core -> RCC 里,把 HSE(高速外部时钟)设为 Crystal/Ceramic Resonator,因为大多数开发板外部接了 8MHz 晶振。LSE 如果板子上有 32.768kHz 晶振也设上,没有就 Disable。

第二,配置调试接口。在 System Core -> SYS 里,Debug 选 Serial Wire。这一步极其重要,不选的话芯片的 SWD 调试引脚会被当成普通 GPIO,下次烧录就烧不进去了,得用特殊方法解锁。我见过太多人因为漏了这一步,板子"变砖"。

第三,配置 GPIO。在芯片图上找到 PC13,左键点击选 GPIO_Output。然后在 System Core -> GPIO 里,选中 PC13,把输出模式设为 Push Pull,速度 Low,初始电平根据你的板子 LED 接法定(共阳极接法初始设 High 灭,共阴极设 Low 灭)。

第四,配置时钟树。切到 Clock Configuration 标签页,在 HCLK 那一栏输入 72,回车,CubeMX 会自动算出各分频系数。F103 的最高主频就是 72MHz。

5.2 工程生成选项与代码保护规则

切到 Project Manager 标签页。Project Name 填个英文名,比如 "LED_Test"。Project Location 选纯英文路径。Toolchain/IDE 选 MDK-ARM,版本选你装的 Keil 版本对应的(一般选 V5)。

在 Code Generator 那一栏,勾上 "Generate peripheral initialization as a pair of .c/.h files per peripheral",这样每个外设的初始化代码会单独成文件,工程结构更清晰。另外 "Copy only necessary library files" 也勾上。

点右上角的 "GENERATE CODE",CubeMX 会生成工程。生成完成后点 "Open Project",会自动用 Keil 打开。

5.3 Keil 里的编译配置与烧录设置

Keil 打开工程后,先别急着编译。检查几个地方:

在 Options for Target -> Debug 标签页,选择调试器。如果你用的是 ST-Link,选 "ST-Link Debugger",然后点旁边的 Settings,在 Debug 标签页确认能识别到芯片的 IDCODE。识别不到就是驱动或接线问题。

在 Utilities 标签页,勾上 "Use Debug Driver",这样烧录和调试用同一个驱动。

然后点编译按钮(F7)。第一次编译会稍慢,因为要编译整个 HAL 库。编译成功后,点下载按钮(F8)烧录。

5.4 在 main 函数里写第一段用户代码

烧录完默认程序,LED 可能不亮,因为生成的代码里没有点灯逻辑。打开main.c,找到while (1)循环,在/* USER CODE BEGIN 3 */和/* USER CODE END 3 */之间写入:

HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_13); HAL_Delay(500);

注意这里我用的是 GPIOB,因为很多 F103C8T6 最小系统板的 LED 接在 PB13 而不是 PC13,具体看你板子原理图。改完重新编译烧录,LED 应该开始闪烁。

提示:所有用户代码必须写在 USER CODE BEGIN/END 之间。写在别的地方,下次 CubeMX 重新生成代码会被清掉。这个规则我强调多少遍都不为过。

6. 那些年踩过的坑:典型报错与排查链路

装环境这件事,不出问题是不可能的。这一节我把最常见的几类问题按"现象-排查-根因-解决"的链路讲清楚,你遇到类似问题可以照着排查。

6.1 CubeMX 打不开或启动闪退

现象:双击 CubeMX 图标,转两圈就没了,或者弹个 Java 错误框。

排查链路:先看是不是 Java 环境问题。CubeMX 依赖 JRE,虽然新版自带,但如果系统里装了多个 Java 版本,可能冲突。打开命令行输入java -version看看。如果报 "不是内部或外部命令",说明系统 PATH 里没有 Java。

根因通常是两种:一是安装路径有中文或空格,导致 JRE 路径解析失败;二是系统缺少某些运行库,比如 VC++ Redistributable。

解决办法:重装到纯英文路径;装一下 VC++ 运行库合集;如果还不行,去 CubeMX 安装目录下找到jre文件夹,手动把里面的bin目录加到系统 PATH。

6.2 固件包下载卡在 0% 或报网络错误

现象:CubeMX 里点下载固件包,进度条不动,或者弹 "Connection error"。

排查链路:这基本就是网络问题,CubeMX 的固件服务器在国外。先确认你的网络能访问外网(正常上网即可,这里不涉及任何特殊网络手段)。如果网络正常还是下不动,那就是服务器端限速。

根因:在线下载通道不稳定。

解决办法:走离线包路线。去 ST 的 GitHub 仓库下载对应系列的固件包,解压放到 Repository 目录。这是最稳的方式,我后来所有机器都这么装,再没卡过。

6.3 Keil 编译报 "cannot open source input file stm32f1xx_hal.h"

现象:编译时提示找不到头文件。

排查链路:打开 Options for Target -> C/C++ 标签页,看 Include Paths 里有没有包含 HAL 库的头文件目录。正常生成的工程应该自动包含Drivers/STM32F1xx_HAL_Driver/Inc和Drivers/CMSIS/Device/ST/STM32F1xx/Include等路径。

根因:多半是工程路径被移动过,或者 CubeMX 生成时库文件没复制全。

解决办法:检查工程目录下 Drivers 文件夹是否完整;在 Include Paths 里手动补上缺失的路径;实在不行重新用 CubeMX 生成一次工程。

6.4 烧录失败 "No target connected" 或 "Flash Download failed"

现象:Keil 点下载,提示找不到目标或下载失败。

排查链路:第一步,检查硬件接线。SWD 需要接四根线:VCC、GND、SWDIO、SWCLK。很多人只接了三根忘了 GND,或者 SWDIO/SWCLK 接反。第二步,在 Keil 的 Debug Settings 里看能不能读到 IDCODE。第三步,检查芯片是不是进了读保护或者调试引脚被占用。

根因:接线问题占一半,剩下的是驱动没装好,或者芯片被锁。

解决办法:先确认接线;重装 ST-Link 驱动;如果是芯片被锁(比如之前 SYS Debug 没设成 Serial Wire),用 STM32CubeProgrammer 连接后执行全片擦除解锁。

6.5 编译通过但程序不运行

现象:烧录成功,但 LED 不闪,或者程序跑飞。

排查链路:先确认烧录的 hex 是不是最新编译的;再确认板子供电正常;然后用调试器单步,看程序卡在哪里。

根因:常见的是时钟配置错误(比如外部晶振没起振,但代码里配了 HSE),导致 HAL_Delay 死等;或者中断向量表地址不对。

解决办法:如果板子没有外部晶振,RCC 里 HSE 要选 Disable,用内部 HSI;检查 SystemClock_Config 函数里的时钟源配置和实际硬件是否一致。

报错关键词高频根因快速验证方法
No target connected接线/驱动换根 USB 线,重装驱动
cannot open source file包含路径缺失检查 Include Paths
AC6 语法 error编译器版本不匹配切到 AC5
程序不运行时钟配置与硬件不符单步调试看卡点
固件包下载失败网络通道不稳改用离线包

7. 装完之后:让这套环境真正好用的几个习惯

环境装通只是开始,用起来顺不顺手,取决于一些日常习惯。这些是我用了几年之后沉淀下来的,分享给你。

7.1 工程目录的规范化管理

我见过太多人的工程目录一团糟,一个文件夹里塞了几十个工程,找都找不到。建议按"芯片系列/项目名/版本"的层级来放,比如STM32/F103/LED_Test/v1。每个工程独立一个文件夹,不要多个工程共用一个 Drivers 目录,否则改了一个工程的库,别的工程跟着出问题。

另外,CubeMX 生成的工程里,.ioc文件一定要跟着工程一起保存。以后要改配置,双击.ioc就能回到 CubeMX,不用重新建工程。

7.2 版本控制与备份策略

嵌入式工程用 Git 管理是完全可行的。但要注意,Keil 编译产生的中间文件(Objects 文件夹、.axf、.hex 等)不要提交,写个.gitignore过滤掉。只提交源码、工程文件、.ioc 和必要的库文件。

CubeMX 生成的 Drivers 目录体积不小,如果团队协作,可以考虑把库文件单独管理,工程里只保留引用。个人学习的话,直接全提交也无所谓,就是仓库大一点。

7.3 多芯片系列的包管理

如果你同时玩 F1、F4、H7 多个系列,CubeMX 的 Repository 目录会越来越大。建议定期清理不用的固件包版本,同一个系列保留一个稳定版就行。Keil 的 DFP 也是,不用的系列可以卸载,省空间。

7.4 从 CubeMX 到实际项目的过渡

CubeMX 生成的工程适合快速验证,但真正做项目时,往往需要更规范的代码结构。我的做法是:用 CubeMX 生成初始化代码,然后把 HAL 初始化部分抽出来,业务逻辑单独建文件,用模块化的方式组织。这样既享受了 CubeMX 的配置便利,又保持了代码的可维护性。

还有一点,CubeMX 生成的 HAL 库代码效率不是最高的,对性能敏感的场景可能需要用 LL 库(Low Layer)或者直接操作寄存器。CubeMX 也支持生成 LL 库代码,在 Project Manager 的 Advanced Settings 里可以按外设选择 HAL 还是 LL。这个进阶用法等你熟悉了基础流程再研究。

装环境这件事,说难不难,说简单也不简单。难的不是操作步骤,而是那些没人告诉你、只能自己踩出来的细节。上面这些内容,如果能让少一个人卡在"装完打不开"或者"烧录失败"上,那这篇就没白写。工具是死的,人是活的,装通之后多动手写代码,才是正经事。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 1:13:11

反激式开关电源启动浪涌电流六种抑制方案与选型计算

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 1:13:07

纯HTML制作个人博客页面:从零搭建完整静态网站结构

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 1:10:42

旁挂部署与策略路由PBR:网络流量引流的实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 1:10:21

神经风格迁移原理与PyTorch实战:从VGG特征解耦到工业级调优

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 1:09:56

基于朴素贝叶斯与SVM的垃圾邮件识别系统实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 1:09:48

从单片机到u-boot:嵌入式Linux分水岭与QEMU ARM64实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华