i.MX6嵌入式调试环境搭建:MfgTool烧录与启动流程详解
1. 为什么i.MX6的调试环境搭建值得单独写一篇拿到一块i.MX6开发板很多人第一反应是插上USB线打开串口终端等着看启动日志。结果往往是板子毫无反应或者卡在某个阶段反复重启甚至连串口都识别不到。这不是板子坏了而是i.MX6这颗芯片的启动链路比大多数人想象的要复杂得多。i.MX6系列是嵌入式领域非常经典的一颗ARM Cortex-A系列处理器广泛用于工业控制、车载娱乐、智能家居网关等场景。它的启动流程涉及内部Boot ROM、启动模式引脚配置、镜像格式封装、烧录工具交互等多个环节。任何一个环节出问题板子都不会给你任何友好提示——它只会沉默。MfgTool是官方提供的一套制造烧录工具最初用于产线批量烧录但因为它支持USB OTG方式与芯片内部Boot ROM通信所以也被大量开发者用来做开发阶段的固件下载和调试环境搭建。相比JTAG调试器MfgTool的门槛更低不需要额外购买仿真器一根USB线就能完成裸机程序的下载和运行验证。这篇内容适合以下几类人刚拿到i.MX6开发板、不知道从哪里下手的新手之前用STM32或其他MCU做开发、第一次接触ARM Linux平台需要搭建调试环境的工程师以及想系统梳理i.MX6启动流程和烧录原理的进阶学习者。我会从硬件连接、启动模式配置、MfgTool工具链的使用、镜像文件格式、常见问题排查等几个维度把整个调试环境搭建过程拆开讲清楚。2. 动手之前必须搞清楚的i.MX6启动链路2.1 从按下复位键到程序运行芯片内部发生了什么i.MX6上电或复位后第一个执行的不是用户程序而是固化在芯片内部ROM里的一段代码通常称为Boot ROM。这段代码做的事情非常关键它根据启动模式引脚BOOT_MODE[1:0]的电平状态决定从哪里加载下一步的启动代码。具体来说i.MX6支持多种启动方式从NOR Flash启动、从NAND Flash启动、从SD/eMMC启动、从USB OTG启动等。Boot ROM会依次尝试这些启动源找到可用的就加载并执行。如果所有启动源都失败芯片会进入USB串行下载模式Serial Download Protocol等待主机通过USB发送可执行镜像。这就是MfgTool能工作的根本原因当你把启动模式设置为USB下载模式时Boot ROM会主动在USB OTG接口上等待主机通信MfgTool就是那个主机端。理解这一点非常重要因为它解释了为什么有些操作看起来不按常理出牌——比如你不需要先烧录任何东西到板子上就能通过USB让板子运行一段代码。这不是魔法而是Boot ROM内置的通信协议在起作用。2.2 启动模式引脚一个容易被忽略的硬件细节不同厂商的i.MX6开发板启动模式配置方式不同。有的是拨码开关有的是跳线帽有的直接焊电阻。但核心逻辑是一样的BOOT_MODE[1:0]两个引脚的电平组合决定了启动源。以常见的配置为例BOOT_MODE[1:0]启动模式典型用途00从Fuses启动量产固化配置01从内部Boot ROM串行下载MfgTool烧录/调试10内部保留不使用11从内部Boot ROM启动正常启动流程实际开发板上你通常需要把拨码开关拨到USB下载模式或Serial Download位置。这个位置在不同板子上的标注可能不同有的写USB有的写Download有的直接标01。建议拿到板子后第一件事就是找到原理图确认BOOT_MODE引脚对应的开关位置。注意有些开发板在USB下载模式下串口可能没有输出。这是正常的因为Boot ROM阶段的串行下载协议不一定会初始化调试串口。不要因为串口没输出就以为板子坏了。2.3 为什么选择MfgTool而不是其他工具市面上能用于i.MX6调试的工具不止MfgTool一种。JTAG调试器如J-Link、OpenJTAG功能更强大支持单步调试、断点、寄存器查看等。但JTAG方案有几个现实问题需要额外购买硬件、需要配置复杂的调试脚本、对新手不够友好。MfgTool的优势在于官方提供、免费、操作相对简单、不需要额外硬件。它通过USB OTG接口与Boot ROM通信可以把编译好的镜像文件直接下载到板子的DDR内存中运行也可以烧录到eMMC或NAND Flash中持久化存储。当然MfgTool也有局限性。它不支持源码级调试不能设断点不能查看寄存器。所以它更适合做下载-运行-看结果这种验证性工作而不是复杂的调试任务。对于裸机程序开发来说这已经够用了。3. 搭建MfgTool调试环境的完整操作链路3.1 硬件准备与连接检查在开始软件操作之前先把硬件链路理清楚。你需要准备的东西包括i.MX6开发板一块确保板子供电正常USB线一根注意接口类型通常是Micro-USB或Type-C串口调试工具USB转TTL模块用于查看启动日志电脑一台Windows系统对MfgTool支持最好连接顺序很重要。先把串口线接好打开串口终端波特率通常为1152008N1再连接USB OTG线到电脑最后给板子上电。这个顺序能让你看到完整的启动日志方便判断板子处于什么状态。如果你先上电再连USB线板子可能已经尝试过USB下载模式但没找到主机然后跳到了其他启动源。这时候再连USB线MfgTool可能识别不到设备。串口终端的配置参数参数值波特率115200数据位8校验位None停止位1流控None3.2 MfgTool工具包的获取与目录结构解读MfgTool工具包通常由开发板厂商提供也可以从官方渠道获取。解压后你会看到类似这样的目录结构MfgTool2/ ├── MfgTool2.exe # 主程序 ├── cfg.ini # 配置文件 ├── Profiles/ │ └── Linux/ │ └── OS Firmware/ │ ├── files/ # 存放镜像文件 │ └── ucl2.xml # 烧录脚本 └── Drivers/ # USB驱动其中最关键的两个文件是cfg.ini和ucl2.xml。cfg.ini决定了MfgTool加载哪个配置文件ucl2.xml则定义了具体的烧录命令和流程。cfg.ini的典型内容[profiles] chip Linux [platform] board CustomBoard [LIST] name Linux这里的chip和board字段必须和Profiles目录下的文件夹名称对应否则MfgTool会找不到配置文件。3.3 镜像文件的准备与格式要求MfgTool烧录的镜像文件不是随便一个二进制文件就能用的。i.MX6的Boot ROM对镜像格式有严格要求通常需要包含IVTImage Vector Table和Boot Data结构。对于裸机程序你需要用厂商提供的工具如mkimage或elftosb把编译出来的ELF文件转换成i.MX6可识别的镜像格式。这个过程叫镜像封装。一个典型的i.MX6镜像文件结构如下偏移地址内容说明0x000IVT镜像向量表包含入口地址、DCD指针等0x020Boot Data启动数据包含镜像大小、起始地址0x040DCD设备配置数据用于初始化DDR等外设0x400用户代码实际的程序代码DCD部分特别重要它负责在加载用户代码之前初始化DDR内存。如果你的程序需要运行在DDR中DCD配置必须正确否则程序加载后会跑飞。3.4 实际操作从零跑通一个裸机程序假设你已经有了一个编译好的LED闪烁裸机程序接下来把它通过MfgTool下载到板子上运行。第一步把封装好的镜像文件通常叫firmware.bin或led.bin复制到Profiles/Linux/OS Firmware/files/目录下。第二步编辑ucl2.xml添加或修改烧录命令。一个简单的下载到DDR运行的配置如下LIST nameCustomBoard-USB descUSB Download CMD stateBootStrap typeboot bodyBootStrap filefirmware.binLoading firmware/CMD CMD stateBootStrap typejump Jumping to firmware/CMD /LIST第三步把板子的启动模式拨到USB下载模式连接USB线上电。第四步打开MfgTool2.exe如果一切正常你会看到工具识别到一个HID设备界面上出现设备列表。点击Start按钮工具会依次执行ucl2.xml中定义的命令。第五步观察串口终端输出。如果程序正常运行你应该能看到LED开始闪烁或者串口打印出预期的调试信息。提示第一次操作时建议先用厂商提供的官方镜像测试确认硬件链路和工具配置没问题再换成自己的程序。这样可以排除变量快速定位问题。4. 那些让人抓狂的典型问题与排查思路4.1 MfgTool识别不到设备怎么办这是最常见的问题没有之一。MfgTool界面上设备列表空空如也点击Start也没反应。排查思路如下先确认板子是否真的进入了USB下载模式。把串口终端打开给板子重新上电观察有没有输出。如果串口完全没有输出可能是启动模式引脚配置不对或者串口线接错了。再确认USB驱动是否安装成功。在Windows设备管理器中查看如果看到带黄色感叹号的未知设备说明驱动没装好。MfgTool工具包里的Drivers目录下有驱动文件手动指定安装即可。还有一个容易被忽略的点USB线本身。有些USB线只能充电不能传输数据。换一根确认能传数据的线试试。最后检查cfg.ini中的配置是否和实际使用的板子匹配。如果chip或board字段写错了MfgTool会加载错误的配置文件自然识别不到设备。4.2 下载成功但程序不运行MfgTool显示下载完成但板子没有任何反应。这种情况通常和镜像文件本身有关。先检查DCD配置。如果DDR初始化参数不对程序加载到DDR后无法正常运行。不同型号的i.MX6如i.MX6Q、i.MX6DL、i.MX6ULLDDR配置不同不能混用。再检查入口地址。IVT中的入口地址必须和链接脚本中定义的起始地址一致。如果链接脚本把程序链接到0x80000000但IVT中写的入口地址是0x90000000程序肯定会跑飞。还有一种可能是镜像文件没有正确封装。直接用objcopy把ELF转成bin文件是不够的必须加上IVT和Boot Data头部。用mkimage工具处理后的文件才是Boot ROM能识别的格式。4.3 串口输出乱码或不输出串口乱码通常是波特率不匹配。i.MX6的Boot ROM阶段和用户程序阶段的波特率可能不同。有些板子的Boot ROM使用默认波特率而用户程序可能重新配置了UART时钟。如果串口完全不输出先确认串口线是否接在了正确的UART接口上。i.MX6通常有多个UART调试串口一般是UART1。查看原理图确认。另外有些开发板在USB下载模式下会关闭调试串口。这时候你需要先完成烧录把启动模式切回正常启动再重新上电观察串口输出。4.4 烧录到eMMC后无法启动把程序烧录到eMMC后切换启动模式到eMMC启动结果板子不启动。这个问题通常出在分区表或启动头配置上。i.MX6从eMMC启动时Boot ROM会读取eMMC的第一个扇区512字节检查是否包含有效的IVT。如果烧录时没有把镜像写到正确的偏移地址Boot ROM就找不到启动头。通常eMMC启动要求镜像烧录到偏移0x400字节处即第二个扇区而不是从0x0开始。这个偏移量在ucl2.xml中通过file标签的offset属性指定。5. 学习资料的选择与使用策略5.1 正点原子资料的特点与适用阶段正点原子的i.MX6资料以手把手风格著称文档详细步骤截图多适合完全零基础的新手。他们的资料通常从开发板开箱开始讲起包括硬件连接、驱动安装、工具使用、第一个程序的编译和下载每一步都有截图和说明。正点原子的资料在快速上手阶段非常有用。你可以跟着文档一步步操作先把板子跑起来建立信心。但到了深入理解启动流程、DDR初始化、uboot移植等进阶内容时正点原子的资料可能不够深入。使用建议新手阶段以正点原子资料为主快速完成环境搭建和第一个程序的运行。遇到不懂的概念先记下来不要卡在一个地方死磕。5.2 韦东山资料的特点与进阶价值韦东山的资料更偏向原理讲解和底层分析。他的课程通常会从芯片手册出发讲解启动流程、时钟树、内存映射等底层知识然后带你一步步编写和移植代码。韦东山的资料在理解为什么阶段非常有价值。比如他会详细讲解IVT结构中每个字段的含义DCD命令的格式Boot ROM的加载流程等。这些内容对于排查问题和做定制化开发非常重要。使用建议当你能跑通基本程序后回头用韦东山的资料补底层原理。不要一上来就看韦东山容易因为概念太多而放弃。5.3 如何把两份资料结合起来用一个比较高效的学习路径是先用正点原子的资料把环境搭起来跑通第一个程序然后用韦东山的资料理解启动流程和镜像格式最后回到正点原子的资料做更多外设实验同时用韦东山的原理知识分析实验中遇到的问题。两份资料不是对立的而是互补的。正点原子告诉你怎么做韦东山告诉你为什么这样做。结合起来既能快速上手又能深入理解。6. 调试环境稳定后的效率提升技巧6.1 建立自己的镜像封装脚本每次手动调用mkimage封装镜像很麻烦而且容易忘记参数。建议写一个简单的Shell脚本把编译、封装、复制到MfgTool目录这几个步骤自动化。#!/bin/bash # build_and_pack.sh make clean make mkimage -A arm -O linux -T firmware -C none -a 0x80000000 -e 0x80000000 -n LED Demo -d led.bin firmware.bin cp firmware.bin /path/to/MfgTool2/Profiles/Linux/OS Firmware/files/ echo Done. Ready to download.这样每次修改代码后只需要运行一次脚本就可以直接在MfgTool中点击Start下载。6.2 用串口日志快速定位问题串口日志是调试i.MX6最重要的信息源。建议在程序的关键阶段添加打印语句比如DDR初始化完成、时钟配置完成、外设初始化完成等。这样当程序跑飞时你能通过最后一条打印判断问题出在哪个阶段。另外养成保存串口日志的习惯。每次调试时把串口输出保存到文件方便对比不同版本的行为差异。6.3 备份可用的配置当你成功搭建好调试环境后把整个MfgTool目录、镜像文件、配置文件、脚本都备份一份。嵌入式开发中环境配置往往比代码更脆弱。一次误操作可能让环境无法工作有备份就能快速恢复。我在实际项目中遇到过因为修改了ucl2.xml中的一个参数导致MfgTool无法识别设备的情况。排查了半天才发现是XML格式错误。从那以后每次修改配置文件前都会先备份。6.4 关于DDR配置的一点经验DDR配置是i.MX6调试中最容易出问题的环节之一。不同批次的DDR芯片甚至同一批次不同温度下最佳参数都可能不同。厂商提供的DCD配置通常是一个能用的保守配置但不一定是最优的。如果你发现程序在DDR中运行不稳定可以尝试调整DCD中的DDR参数比如驱动强度、ODT电阻、刷新周期等。但这些参数需要参考DDR芯片的数据手册和i.MX6的参考手册不能随意修改。一个实用的技巧是先用厂商提供的DCD配置把程序跑起来确认功能正常后再考虑是否需要优化DDR参数。不要一上来就调DDR那样会把问题复杂化。7. 从调试环境搭建延伸到更远的地方调试环境搭建只是i.MX6学习的第一步。当你能稳定地下载和运行程序后接下来可以做的事情很多移植uboot、裁剪Linux内核、构建根文件系统、开发字符设备驱动、调试网络功能等。但无论后续做什么调试环境都是基础。一个稳定、可复现的调试环境能让你在遇到问题时快速定位而不是把时间浪费在为什么下载不了为什么串口没输出这些基础问题上。我个人在实际操作中的体会是花在环境搭建上的时间永远不会白费。前期把环境理顺后期开发效率会高很多。相反如果环境搭建时偷懒后面会遇到各种莫名其妙的问题而且很难排查。最后分享一个小技巧在MfgTool的ucl2.xml中可以定义多个LIST每个LIST对应不同的烧录场景。比如一个用于下载到DDR运行一个用于烧录到eMMC一个用于烧录到NAND。这样切换场景时只需要在MfgTool界面选择对应的配置不需要手动改文件。这个技巧在同时维护多个项目时特别有用。

相关新闻

Spring依赖注入原理与实战:从IoC容器到三级缓存与踩坑指南

Spring依赖注入原理与实战:从IoC容器到三级缓存与踩坑指南

1. 从"new对象"困局到控制反转:这套机制到底解决了什么我先说一个特别真实的场景。前两年接手一个老项目,订单模块里创建订单的代码,从上到下一串new:new UserService()、new ProductService()、new InventoryService()…

2026/10/12 18:50:22 阅读更多 →
微信小程序养老服务平台:从架构设计到部署实战解析

微信小程序养老服务平台:从架构设计到部署实战解析

我见过太多类似的养老平台项目了,有的只写了前端页面交差,有的后端接口调不通,有的数据库表建得毫无逻辑——最可惜的情况是,源码和部署文档都有,但讲解视频只讲功能演示,你根本不知道这套系统为什么这样设…

2026/10/12 18:50:22 阅读更多 →
Cursor编辑器编程辅助工具:把Base URL改到TaoToken提升编程效率

Cursor编辑器编程辅助工具:把Base URL改到TaoToken提升编程效率

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

2026/10/12 18:50:22 阅读更多 →

最新新闻

ArcEngine MapControl底层原理与GIS交互开发实战

ArcEngine MapControl底层原理与GIS交互开发实战

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

2026/10/13 5:49:38 阅读更多 →
STM32入门纯软件方案:零硬件跑通GPIO、中断、定时器与串口

STM32入门纯软件方案:零硬件跑通GPIO、中断、定时器与串口

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

2026/10/13 5:49:38 阅读更多 →
智能车竞赛疯狂电路组开源项目全解析:从硬件设计到控制算法

智能车竞赛疯狂电路组开源项目全解析:从硬件设计到控制算法

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

2026/10/13 5:49:38 阅读更多 →
SENet通道注意力机制详解:从原理到PyTorch实现与调参实践

SENet通道注意力机制详解:从原理到PyTorch实现与调参实践

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

2026/10/13 5:49:38 阅读更多 →
Harbor企业级镜像治理:从权限隔离到漏洞扫描的落地实践

Harbor企业级镜像治理:从权限隔离到漏洞扫描的落地实践

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

2026/10/13 5:49:38 阅读更多 →
爬虫学习入门:公开数据集分析与网页解析实践

爬虫学习入门:公开数据集分析与网页解析实践

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

2026/10/13 5:48:38 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/12 18:11:17 阅读更多 →