news 2026/9/29 2:06:06

STM32CubeMX 6.14 从下载安装到工程实战全攻略

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32CubeMX 6.14 从下载安装到工程实战全攻略

1. 为什么STM32CubeMX值得花时间折腾

搞STM32开发的人,绕不开一个工具——STM32CubeMX。这东西刚出来那几年,很多人觉得它就是个“点灯生成器”,生成的代码又臃肿又不好改,不如自己手撸寄存器来得痛快。但到了6.x版本,尤其是6.14这个版本,情况完全不一样了。它已经从一个单纯的代码生成器,变成了整个STM32开发生态的中枢:时钟树可视化配置、引脚冲突自动检测、中间件一键集成、固件包管理、功耗计算,甚至还能直接生成CMake工程配合VS Code开发。你如果还在用标准库一行行敲GPIO初始化,那效率差距不是一星半点。

我写这篇东西的起因很简单:身边太多人卡在“下载”和“配置”这两个最基础的环节上。有人下载了安装包结果装完打不开,有人打开之后发现找不到自己用的芯片型号,有人生成工程后编译报一堆错,还有人被固件包的下载速度折磨得想砸键盘。这些问题看起来琐碎,但每一个都能让你在起步阶段耗掉半天甚至一整天。而网上大部分教程要么太老,要么跳步严重,要么就是复制粘贴官方文档,读完了还是不知道具体怎么操作。

这篇文章面向的是所有准备用或者正在用STM32CubeMX的人,不管你是刚入坑的学生,还是从标准库转过来的老工程师,或者是被毕业设计逼着上手的准毕业生。我会从下载安装开始,把每一步的操作意图、可能踩的坑、以及我自己的处理习惯都讲清楚。你跟着走一遍,基本能避开90%的常见问题。文章里涉及的具体版本以6.14为主,但大部分操作逻辑对6.x系列都通用。

2. 下载与安装:从官网到本地环境就绪

2.1 官网下载的正确姿势与版本选择

STM32CubeMX的官方下载入口在ST官网的开发者工具区。直接搜“STM32CubeMX download”就能找到页面,但要注意,ST的官网有时候会引导你先登录账号。我的建议是提前注册一个ST账号,用邮箱注册就行,免费。登录之后下载有个好处:你能在账号里管理自己用到的固件包版本,换电脑的时候不用重新一个个找。

下载页面会提供多个平台的安装包:Windows、Linux、macOS都有。Windows下有两个选择,一个是带JRE的完整安装包,一个是不带JRE的。我强烈建议选带JRE的那个,虽然文件大一点(大概300MB左右),但省去了你自己配Java环境的麻烦。STM32CubeMX是基于JavaFX开发的,对Java版本有要求,自己配很容易遇到版本不兼容的问题。6.14版本内置的JRE已经调好了,装完直接能用。

版本选择上,6.14是2024年发布的稳定版,相比6.10之前的版本,最大的改进是固件包下载器换了新的CDN,国内下载速度有了明显提升。另外6.14对CMake工程的支持更完善了,如果你打算用VS Code + CMake开发,这个版本很合适。不建议去追最新的预览版,工具链这种东西,稳定比新功能重要。

注意:下载的时候看清楚是STM32CubeMX还是STM32CubeIDE。前者是配置工具,后者是集成开发环境。两个可以都装,但CubeMX是独立的配置工具,不依赖IDE。

2.2 安装过程中的关键选项与避坑点

安装过程本身不复杂,双击运行,一路Next就行。但有几个地方需要留意。

第一个是安装路径。默认路径在C盘用户目录下,路径里带空格和中文用户名的情况很常见。STM32CubeMX对中文路径的支持时好时坏,尤其是后面加载固件包的时候,如果路径里有中文,偶尔会出现找不到包的情况。我的习惯是直接装到D:\ST\STM32CubeMX这种纯英文、无空格的路径下。固件包的存储路径也单独设一个,比如D:\ST\Repository,不要用默认的。

第二个是安装过程中的“Repository Setup”环节。安装器会问你固件包存哪里,这里就可以改成你想要的路径。如果你之前装过旧版本,安装器可能会检测到旧的仓库路径,确认一下是不是你想要的。

第三个是快捷方式。安装完成后会在桌面和开始菜单创建快捷方式。如果你装了多个版本,建议把快捷方式重命名一下,比如“STM32CubeMX 6.14”,免得搞混。

安装完成后第一次启动,软件会检查更新和初始化一些配置。这个过程需要联网,如果网络环境不好,可能会卡在启动画面。遇到这种情况,等一会儿或者断网启动都可以。断网启动的话,它会跳过更新检查,直接进主界面。

2.3 固件包的下载策略与加速技巧

固件包(Firmware Package)是STM32CubeMX的核心资源,里面包含了每个芯片系列的HAL库、LL库、中间件和示例代码。没有固件包,你新建工程的时候选完芯片也生成不了代码。

6.14版本打开后,主界面会有一个“Install/Remove Packs”的入口,点进去就是固件包管理界面。这里列出了所有STM32系列的固件包,从F0到H7,还有MP1、WB、WL这些无线系列。每个包都标注了版本号和大小,一个完整的F4系列固件包大概300-500MB,H7的更大。

下载固件包最让人头疼的就是速度。ST的服务器在国外,国内直接下载经常只有几十KB/s,一个包下半小时是常事。我试过几种加速方式,比较靠谱的是在设置里把下载源改成国内镜像。具体操作:在CubeMX的Help菜单里找到Updater Settings,里面有一个Connection Parameters,可以设置代理或者镜像地址。不过6.14版本对这块做了简化,更直接的方式是手动下载固件包然后导入。

手动下载的方法是:在ST官网的固件包页面找到对应的.pack文件,用浏览器或者下载工具下载下来,然后在CubeMX的固件包管理界面选择From Local,导入你下载的包。这种方式的好处是你可以用下载工具的多线程加速,速度能跑满带宽。

提示:固件包不需要全部下载,用到哪个系列就下哪个。比如你只用F103,那就只下F1系列的包。全部下载的话,几十个G就没了。

3. 工程创建与芯片配置的核心逻辑

3.1 新建工程的两种入口与芯片选型技巧

打开STM32CubeMX 6.14,主界面有三个主要入口:New Project、Load Project、Example Selector。新建工程点第一个,会进入芯片选择界面。

芯片选择界面提供了三种筛选方式:按系列浏览、按参数搜索、按开发板选择。按系列浏览最直观,左边一列是STM32的各个系列,点进去之后右边会列出该系列下的所有芯片型号。按参数搜索适合你只知道需求不知道具体型号的情况,比如你要找“带USB OTG的F4芯片”,就可以在搜索框里输入条件筛选。

这里有个细节:芯片列表里每个型号后面会标注封装类型、Flash大小、引脚数。选的时候一定要确认封装和你手头的芯片一致,不然生成的引脚配置对不上。比如F103C8T6是LQFP48封装,你如果选了LQFP64的型号,引脚编号就全乱了。

选好芯片后点Start Project,进入配置主界面。这个界面是CubeMX的核心工作区,左边是外设列表,中间是芯片引脚图,右边是配置面板。第一次看到可能会觉得信息量很大,但用熟了之后效率很高。

3.2 时钟树配置的底层逻辑与实操

时钟树(Clock Configuration)是CubeMX里最值得花时间理解的部分。很多人配置时钟就是照着教程填几个数,能跑就行,但不知道为什么这么填。一旦换个芯片或者换个晶振频率,就不知道怎么改了。

STM32的时钟系统可以类比成一套供水系统。时钟源就是水源,有内部水源(HSI、LSI)和外部水源(HSE、LSE)。PLL是增压泵,把水源的压力提升到更高水平。各个总线和外设就是用水终端,每个终端对水压(频率)的要求不一样。CubeMX的时钟树界面就是让你可视化地配置这套供水系统。

以最常见的STM32F103C8T6为例,外部晶振是8MHz。配置目标:系统时钟72MHz,USB时钟48MHz,APB1总线36MHz,APB2总线72MHz。操作步骤:在时钟树界面,先把HSE选为Crystal/Ceramic Resonator,然后在PLL Source Mux里选HSE,PLL Mul设为9倍频,这样PLL输出就是8MHz × 9 = 72MHz。接着把System Clock Mux选为PLLCLK,系统时钟就变成了72MHz。APB1 Prescaler设为2,得到36MHz;APB2 Prescaler设为1,保持72MHz。USB Prescaler设为1.5,得到48MHz。

每一步都有原因:APB1总线最高只能跑36MHz,超了会不稳定;USB模块需要精确的48MHz时钟,所以PLL的分频系数要能整除出48MHz;APB2连接的是高速外设,保持和系统时钟一致能发挥最大性能。CubeMX会自动检测冲突,如果某个频率超了范围,对应的输入框会变红,这个反馈机制很实用。

注意:如果你的板子没有外部晶振,就把HSE改成Bypass或者直接用HSI。HSI的精度不如HSE,对时钟要求高的场景(比如USB、CAN)建议还是用外部晶振。

3.3 GPIO与引脚分配的操作要点

引脚分配是CubeMX最直观的功能之一。中间的芯片图可以直接点击引脚来分配功能,也可以在左边的外设列表里展开某个外设,然后选择它的引脚映射。

以点灯为例,假设你要用PC13控制一个LED。在芯片图上找到PC13,左键点击,会弹出一个菜单,列出这个引脚支持的所有功能。选择GPIO_Output,引脚就变成了绿色,表示已分配。然后在右边的GPIO配置面板里,可以设置这个引脚的详细参数:输出模式(推挽/开漏)、上下拉(Pull-up/Pull-down/No pull)、输出速度(Low/Medium/High/Very High)、初始电平(High/Low)。

推挽输出和开漏输出的区别:推挽输出能主动输出高电平和低电平,驱动能力强;开漏输出只能主动拉低,高电平需要外部上拉电阻。点灯用推挽就够了,如果是I2C总线这种需要线与逻辑的场景,才用开漏。

输出速度的选择也有讲究。速度越高,引脚翻转越快,但功耗和电磁干扰也越大。点灯这种应用,Low就够了。如果是SPI或者SDIO这种高速通信,才需要选High或Very High。

引脚分配还有一个隐藏功能:Pinout菜单里的Auto-assign。如果你已经确定了要用哪些外设,但不想手动一个个分配引脚,可以用这个功能让CubeMX自动分配。不过自动分配的结果不一定符合你的PCB布局,所以实际项目中还是手动分配更靠谱。

4. 中间件集成与代码生成

4.1 中间件配置的典型场景

CubeMX的中间件(Middleware)集成是它区别于普通代码生成器的核心价值。常用的中间件包括FreeRTOS、FatFS、LwIP、USB Device、USB Host等。每个中间件的配置界面都不一样,但逻辑是相通的:先使能,再配置参数,最后在代码生成时勾选对应的库。

以FreeRTOS为例。在左侧的Middleware分类里找到FREERTOS,选择Interface为CMSIS_V1或CMSIS_V2。CMSIS_V2是较新的版本,支持更多特性,但如果你用的HAL库版本较老,可能只有V1可选。选好之后,下面的Configuration面板里可以配置任务、队列、信号量、定时器等。CubeMX会自动生成freertos.c和对应的初始化代码,你只需要在生成的任务函数里填业务逻辑。

USB Device的配置稍微复杂一点。以USB虚拟串口(CDC)为例:在Connectivity里找到USB_OTG_FS或USB_OTG_HS,选择Device_Only模式。然后在Middleware里找到USB_DEVICE,选择Communication Device Class (Virtual Port Com)。这时候时钟树会自动检查USB时钟是否为48MHz,如果不是,需要回去调整时钟配置。配置完成后生成代码,CubeMX会生成USB设备初始化、CDC类驱动和收发数据的回调函数框架。

提示:USB虚拟串口在Windows下需要安装驱动才能识别。STM32的CDC驱动在Windows 10及以上版本通常能自动识别,如果不行,需要手动安装ST提供的VCP驱动。

4.2 工程生成的关键设置与工具链选择

配置完成后,点Project Manager进入工程生成设置。这里有几个关键选项:

Project Name和Location:工程名用英文,路径不要有中文和空格。Location建议单独建一个工程目录,不要和固件包仓库混在一起。

Toolchain/IDE:这是最重要的选项。6.14支持MDK-ARM(Keil)、STM32CubeIDE、Makefile、CMake、IAR等多种工具链。如果你用Keil,选MDK-ARM,版本选你安装的Keil版本对应的。如果你用VS Code + CMake,选CMake。选错了后面改起来很麻烦,所以这一步要确认好。

Code Generator:这里有几个勾选项需要留意。Copy only necessary library files建议勾上,这样生成的工程只包含用到的库文件,工程体积小,编译快。Generate peripheral initialization as a pair of .c/.h files per peripheral也建议勾上,这样每个外设的初始化代码单独成文件,结构清晰,方便管理。Set all free pins as analog这个选项在低功耗场景下有用,普通应用可以不勾。

HAL Settings:这里可以设置HAL库的时基源。默认是SysTick,如果你用了FreeRTOS,建议改成其他定时器(比如TIM6),因为FreeRTOS会占用SysTick。这个细节很多人忽略,结果FreeRTOS跑起来之后HAL_Delay不准了。

设置完成后点GENERATE CODE,CubeMX会生成完整的工程文件。生成过程需要几秒到几十秒,取决于工程复杂度。生成完成后会提示你打开工程或者打开所在文件夹。

4.3 生成代码的结构解析与二次开发

CubeMX生成的代码结构是有规律的,理解了这个结构,二次开发就很顺畅。

Core/Src/main.c是主文件,里面的main()函数包含了HAL初始化、系统时钟配置、外设初始化,然后进入while(1)主循环。用户代码要写在/* USER CODE BEGIN */和/* USER CODE END */之间,这样重新生成代码的时候不会被覆盖。这是CubeMX最重要的约定,一定要遵守。

Core/Src/stm32f1xx_hal_msp.c是MCU支持包文件,里面的函数负责外设的底层初始化,比如GPIO时钟使能、中断优先级配置等。这些函数由HAL库自动调用,一般不需要手动修改。

Core/Src/stm32f1xx_it.c是中断服务函数文件,里面包含了所有中断的处理入口。CubeMX会自动生成中断向量对应的函数框架,你只需要在对应的回调函数里填逻辑。比如串口接收中断,你可以在HAL_UART_RxCpltCallback里处理接收到的数据。

Middlewares文件夹下是中间件的源码和配置。Drivers文件夹下是HAL库和CMSIS。这些文件夹的内容在重新生成代码时会被覆盖,所以不要直接修改里面的文件。

二次开发的核心原则:所有用户代码都写在USER CODE区域,所有配置修改都通过CubeMX的.ioc文件进行。.ioc文件是工程的配置文件,保存了所有的引脚分配、时钟配置、中间件设置。下次打开工程时,CubeMX会读取这个文件恢复配置。如果你手动改了生成代码里的配置,重新生成时会被覆盖,所以一定要通过.ioc来管理配置。

5. 常见问题排查与实战避坑指南

5.1 安装与启动类问题

问题一:安装完成后双击图标没反应,或者闪一下就没了。

这是最常见的问题,原因通常是Java环境有问题。6.14虽然内置了JRE,但如果你系统里之前装过其他版本的Java,环境变量可能会冲突。排查方法:打开命令行,输入java -version,看看输出的是什么版本。如果显示的不是CubeMX内置的JRE版本,就需要调整环境变量,把CubeMX的JRE路径放到最前面。或者更简单的方法:在CubeMX的安装目录下找到jre文件夹,直接运行里面的java.exe来启动。

问题二:启动时卡在启动画面,一直转圈。

通常是网络问题。CubeMX启动时会检查更新,如果网络不通就会卡住。解决方法:断网启动,或者在设置里关闭自动更新检查。具体在Help→Updater Settings里,把Check for updates on startup取消勾选。

问题三:固件包下载速度极慢或者下载失败。

前面已经说了手动下载导入的方法。另外还有一个技巧:在Updater Settings里把Connection Parameters的Timeout调大,默认是30秒,改成120秒,能减少下载中断的概率。

5.2 配置与生成类问题

问题四:生成工程后编译报错,提示找不到头文件。

这种情况通常是工具链选错了,或者工程路径有问题。检查Project Manager里的Toolchain/IDE是否和你实际使用的IDE一致。如果用的是Keil,确认Keil的版本和CubeMX里选的一致。另外检查工程路径是否有中文或空格,有的话改成纯英文路径重新生成。

问题五:重新生成代码后,自己写的代码不见了。

这是因为你把代码写在了USER CODE区域之外。CubeMX重新生成代码时,只会保留USER CODE BEGIN和USER CODE END之间的内容,其他部分会被覆盖。养成习惯:所有自定义代码都写在USER CODE区域,所有配置修改都通过.ioc文件进行。

问题六:时钟配置里某个频率标红,无法生成代码。

标红表示该频率超出了芯片规格。把鼠标悬停在标红的输入框上,CubeMX会提示具体的限制条件。比如APB1总线标红,说明你设置的频率超过了36MHz(对于F1系列)。调整对应的分频系数即可。

问题七:USB虚拟串口配置后,电脑识别不到设备。

先检查时钟树里USB时钟是否为48MHz,这是硬性要求。然后检查USB的引脚是否分配正确,F103的USB引脚是PA11(DM)和PA12(DP)。如果都正确,检查电脑端的驱动。Windows 10以上通常能自动识别,如果不行,去设备管理器里看看有没有带感叹号的未知设备,手动安装ST的VCP驱动。

5.3 工具链配合类问题

问题八:CubeMX生成的Keil工程,打开后提示缺少器件支持包。

这是因为Keil没有安装对应芯片系列的Device Family Pack(DFP)。打开Keil的Pack Installer,找到对应的STM32系列,安装最新的DFP即可。或者直接在Keil的官网下载对应的pack文件手动安装。

问题九:用VS Code + CMake开发时,CubeMX生成的工程无法直接编译。

CubeMX生成的CMake工程需要配合ARM GCC工具链使用。你需要安装arm-none-eabi-gcc,并在CMake配置里指定工具链文件。另外,VS Code需要安装Cortex-Debug插件来支持调试。具体的配置步骤比较多,建议参考CubeMX生成的README.md文件,里面有详细的说明。

问题十:CubeMX生成的代码和标准库代码能混用吗?

技术上可以,但不建议。HAL库和标准库的底层实现不同,混用容易出现资源冲突。如果非要用标准库的某个功能,建议用HAL库的对应功能替代,或者把标准库的代码封装成独立的模块,避免直接操作寄存器。

5.4 常见问题速查表

问题现象可能原因解决方法
双击图标无反应Java环境冲突用安装目录下的jre启动
启动卡在画面网络检查更新断网启动或关闭自动更新
固件包下载慢服务器在国外手动下载后本地导入
编译找不到头文件工具链选错确认Toolchain设置一致
代码被覆盖写在USER CODE外所有代码写在USER CODE区域
频率标红超出芯片规格调整分频系数
USB识别不到时钟或驱动问题检查48MHz时钟和VCP驱动
Keil缺器件包未安装DFP通过Pack Installer安装
CMake编译失败工具链未配置安装ARM GCC并配置工具链
HAL与标准库混用底层冲突统一用HAL库实现

6. 从点灯到项目落地的进阶思路

6.1 用CubeMX快速搭建项目框架的实践

很多人用CubeMX就是配个时钟、点个灯,然后就把生成的代码丢到一边,继续用自己习惯的方式写业务逻辑。这其实浪费了CubeMX最大的价值——快速搭建项目框架。

我的做法是:拿到一个新项目,先用CubeMX把所有用到的外设都配置好,包括GPIO、UART、SPI、I2C、定时器、中断优先级,甚至FreeRTOS的任务划分。生成代码后,得到一个完整的、可编译的工程框架。然后在这个框架上填业务逻辑,而不是从零开始搭。

这样做的好处是:外设初始化、中断向量、时钟配置这些容易出错的底层工作,全部由CubeMX保证正确性。你只需要关注业务逻辑,开发效率能提升好几倍。而且.ioc文件本身就是一份完整的硬件配置文档,交接给别人的时候,对方打开.ioc就能看到所有的引脚分配和时钟配置,比看代码直观得多。

6.2 版本管理与团队协作中的CubeMX使用建议

CubeMX的.ioc文件是文本格式的XML,适合用Git做版本管理。但要注意,不同版本的CubeMX生成的.ioc文件格式可能有差异,团队协作时最好统一CubeMX的版本。

另外,固件包的版本也要统一。.ioc文件里记录了使用的固件包版本,如果团队成员的固件包版本不一致,打开工程时可能会提示版本不匹配。建议在项目文档里注明使用的CubeMX版本和固件包版本,新成员加入时按照文档配置环境。

生成的代码要不要提交到Git?我的建议是:Core、Middlewares、Drivers这些由CubeMX管理的文件夹可以提交,但要在.gitignore里排除编译输出目录。.ioc文件必须提交,这是工程的配置源头。用户自己写的业务代码如果放在单独的文件夹里,也提交。

6.3 我个人的几个使用习惯

最后分享几个我用了几年CubeMX之后养成的习惯,都是踩坑踩出来的。

第一个习惯:每次修改.ioc之前先提交一次Git。CubeMX重新生成代码时,虽然会保留USER CODE区域,但偶尔会有意外。先提交一次,出问题了可以回滚。

第二个习惯:固件包仓库单独放一个盘,不要和工程目录混在一起。固件包很大,混在一起会让工程目录变得很臃肿,备份和迁移都不方便。

第三个习惯:生成代码后先编译一次,确认没有错误再开始写业务逻辑。有时候配置有问题,生成代码时不会报错,但编译时会暴露出来。早发现早解决。

第四个习惯:善用Example Selector。CubeMX内置了大量官方示例,从点灯到USB到以太网都有。遇到不会配置的外设,先去看看对应的示例是怎么配的,比看文档快得多。

第五个习惯:定期更新固件包,但不要追最新版。固件包更新通常包含bug修复和新芯片支持,但偶尔也会引入新的问题。我的做法是:项目开发期间不更新,项目完成后如果新版本有需要的特性再更新。

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

物联网无线收发芯片选型实战:从原理到避坑指南

/* 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 2:04:56

Postman批量接口测试实战:从Collection Runner到Newman自动化

/* 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 2:04:47

VLP16点云转LaserScan:ROS 2D导航的必备投影原理与实战调优

/* 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 2:04:45

全连接神经网络入门:从原理到PyTorch实战与调参指南

/* 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 2:04:44

Cadence Capture CIS 17.4原理图设计核心原理与工程实践

/* 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 2:04:34

嵌入式烧录下载与仿真调试工具链实战指南:从选型到排障

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

作者头像 李华