news 2026/9/25 4:49:13

ZYNQ入门:Vivado 2023.2与Vitis跑通Hello World并生成BOOT.BIN全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ZYNQ入门:Vivado 2023.2与Vitis跑通Hello World并生成BOOT.BIN全流程

拿到一块ZYNQ开发板,装好Vivado 2023.2和Vitis,兴冲冲点开Hello World,结果在"不识别芯片"、“implementation变红”、“BOOT.BIN到底怎么来”这三个问题上各卡了一宿——如果你也处在这个状态,这篇文章就是给你准备的。

先说清楚这篇要解决的问题:从环境搭建开始,到在Vivado里搭一个最小的ZYNQ硬件工程并生成比特流,再把硬件信息导出给Vitis跑通Hello World,最后生成BOOT.BIN,固化到SD卡或者QSPI Flash,让板子脱离电脑独立启动。整个过程我按照2023.2这个版本的实际表现来写,里面的坑都是我真实踩过的,步骤尽量按“能复现”的标准给,适合手里有ZYNQ-7000系列板子、刚接触这两款工具的同学快速把流程走通。

1. 从下载到打开工具:环境搭建这一步就卡住一半人

1.1 Vivado和Vitis到底什么关系,2023.2该怎么装

Vivado 2023.2和Vitis 2023.2其实是同一个安装包里的两个组件,很多新手以为装了Vivado就自动有了Vitis,结果装完发现整个安装过程根本没让你选,打开后菜单里也找不到Vitis的入口。原因很简单:安装的时候没勾选Vitis组件。

AMD的安装向导分了好几步,到了Components选择那一页,默认只勾了Vivado ML Standard,Vitis要自己勾上。这里注意一下,如果只做嵌入式方向、不碰复杂数字信号处理和射频方向,没必要把Vitis里所有组件都装上,只选Vitis核心部分就行,能省不少磁盘空间。

磁盘空间是个很现实的坑。Vivado 2023.2完整安装轻松吃掉150GB左右,如果你在Device Support里只勾ZYNQ-7000和7系列,可以压到60GB上下。我见过有人C盘只剩20GB就硬着头皮装,装到一半提示磁盘空间不足,整个安装进程回滚,浪费一下午。装之前务必给安装盘留够空间,另外安装路径和Windows用户名都不要带中文,否则后续建工程时各种路径解析错误会让人怀疑人生。

还要说一句内存的事。Vivado 2023.2综合和布局布线的资源消耗比老版本明显更大,16GB内存是底线,8GB的机器建完Block Design再综合基本就是卡死或者闪退的结局。如果你发现Vivado频繁闪退,先加内存,其次再考虑是不是软件问题。

1.2 License报错2037:免费授权其实够用,别急着买

Vivado不是没有免费授权,AMD官方提供Vivado ML Standard Edition的免费License,支持ZYNQ-7000和大多数入门级器件,日常学习完全够用。你只需要在官网注册账号,去License中心生成一个Node-Locked类型的License文件,这个文件绑定的是电脑网卡的MAC地址。

这里有个细节坑:生成License填MAC地址的时候,尽量填有线网卡的,不要用无线网卡。笔记本如果睡觉唤醒后无线网卡重新协商了地址,License偶尔会失效,表现就是Vivado突然开始报错。另外,License文件下载后路径不要乱动,Vivado里设置的License路径是固定的,你移动了文件,工具就找不到授权了。

很多人遇到类似“ERROR: [Xilinx 12-2037] License check failed”这种报错就慌了,其实不用慌。带2037这种编号的License错误,本质都是授权检查环节出了问题。按顺序排查:

  1. 确认License文件路径和Vivado里配置的路径一致。
  2. 确认电脑没有改过主机名,改过主机名后最好重新生成一份License。
  3. 确认你生成的License是2023.2版本对应的,拿2020.2的License文件给2023.2用,多数情况是过不了的。

1.3 WinPcap装不上、安装器闪退,基本都是Windows环境问题

Vivado 2023.2的安装包在Windows上会顺带安装WinPcap,这个老字号网络抓包库是给部分硬件调试功能用的。WinPcap的官方版本已经很久没更新了,在Win10和Win11上经常安装失败,表现是安装向导卡在WinPcap那一步不动,甚至整个安装程序无响应。

我试过最稳的办法:安装向导里WinPcap失败之后,先手动下载WinPcap 4.1.3安装包,右键以管理员身份运行,兼容模式选Windows 7或者Windows XP SP3,基本能装上去。实在不行,用Npcap的安装包,在安装选项里勾上“Support WinPcap API compatible mode”,一样能满足Vivado的调用。这条路径我验证过,不用重装整个Vivado。

安装器闪退的情况,多数是杀毒软件实时防护在搞鬼,或者系统临时目录空间不够。装Vivado这种大型软件之前,建议把Windows Defender的实时保护临时关掉,把系统TEMP目录清理一遍,至少留个20GB给安装过程临时文件用。安装完再恢复防护,顺序不要搞反。

1.4 驱动其实有两套:JTAG Cable和板载USB-UART

这一节容易忽略,但后面“不识别芯片”的问题有一大半根子在这。Vivado安装过程中有一步会让你选择是否安装Cable Drivers,这个驱动对应的是Xilinx平台的JTAG下载器。如果这里没装,后面在Hardware Manager里Open Target完全是灰的。补救办法也简单,到Vivado安装目录下的cable_drivers\nt64文件夹里,右键管理员运行install_drivers.bat。

另一套是板载USB-UART串口驱动。绝大多数ZYNQ开发板的USB转串口芯片是Silicon Labs的CP210x或者FTDI的FT2232,插上USB线后设备管理器里能看到对应设备。看不到就是驱动没装,去芯片厂商官网下载驱动装上就能解决。这个驱动不装好的话,后面Vitis里的串口终端根本连不上,Hello World打印什么你完全看不见。

2. 在Vivado里搭最小硬件工程:三个最容易翻车的环节

2.1 器件选错,是新手最隐蔽的坑

新建工程时,器件型号一定不能选错。很多新手随手选一个型号,想着后面再改,结果综合、布局布线全走完了,烧录到板子上一点反应没有,回头才发现器件型号对不上。我的建议是:新建工程时在Boards选项卡里直接选你手上的板子型号,Vivado会帮你把DDR颗粒型号、UART引脚、时钟约束这些全配置好,能省掉后面一大半麻烦。

如果Vivado的Boards列表里没有你的板子,就老老实实去查原理图,记下准确型号。ZYNQ-7000系列常见的xc7z010和xc7z020不能混用,选错后在实现阶段偶尔会出现莫名其妙的时序问题,让人误以为是代码问题,实际上从器件型号开始就已经错了。

2.2 Block Automation帮你省事,也顺手埋了雷

新建Block Design后添加ZYNQ7 Processing System IP,点Run Block Automation,Vivado会根据板级文件自动配置DDR、UART、时钟等外设。看起来是一键配置,但里面有两个细节不确认好,到Vitis阶段会很痛苦。

第一个是UART1的波特率。ZYNQ-7000的PS侧有两个UART,板载USB转串口一般接在UART1,对应MIO14和MIO15。Block Automation自动勾选UART1后,要确认波特率是不是115200。部分板子的配置模板会把波特率设成别的值,如果后面串口输出乱码,先回来改这里。

第二个是DDR的配置。ZYNQ的PS运行程序需要DDR,Block Automation会根据板级文件自动填充DDR型号和参数。如果你没有板级文件,必须手动填DDR型号、数据位宽、频率。这一步填错,程序下进DDR就直接卡死,表现就是Hello World没输出,而且这类问题在Vivado里还看不出明显报错。DDR配置没有捷径,必须对着板载颗粒型号一个一个核。

2.3 Implementation变红:不要急着重跑工程,先学会看报错

“vivado implement design变红”这个现象太典型了。很多人的第一反应是重新生成工程,或者换一个综合策略,结果折腾完了还是红。其实变红只说明一件事:布局布线阶段有错误,而错误信息一定写在日志里。

我自己的排查链路是固定的,照着走一般十分钟内能定位:

  1. 在Design Runs窗口里找到失败的run,看状态是不是红叉。
  2. 打开Messages面板,按级别筛选,只看ERROR,别让黄色Warnings干扰你。
  3. 每条ERROR都有编号,比如[Place 30-575]、[Vivado 12-1345],编号前缀对应模块,Place是布局、DRC是设计规则检查。根据编号去查对应方向。
  4. 直接打开工程目录/project.runs/impl_1/runme.log,用文本编辑器搜索ERROR,从下往上找第一条错误,那往往是根因。

新手最常遇到的变红原因,是约束文件里引用了不存在的管脚,报错类似[Common 17-55] 'set_property' failed - object 'led_0' not found。这是因为Block Design里把LED、按键这些做成了外部端口,但XDC文件里没有正确约束。解决方法是打开Constraints里的XDC,对照原理图把每个外部端口绑到具体的FPGA引脚上:

set_property PACKAGE_PIN M14 [get_ports led_0] set_property IOSTANDARD LVCMOS33 [get_ports led_0]

PACKAGE_PIN后面的编号必须和原理图一致,多核对一遍再综合,比自己瞎猜稳得多。

2.4 生成比特流失败的两个隐藏原因

管脚约束之外,比特流失败还有两个常见但不容易想到的原因。

第一个是ILA调试核。如果Block Design里加了集成逻辑分析仪ILA,但时钟引脚没接对,或者采样深度配置不合理,生成比特流时很容易触发DRC错误。新手阶段建议先不加ILA,等整个流程通了再回头加,否则一次多引入一个变量,出问题不好定位。

第二个是Block Design里的地址冲突。添加AXI外设比如AXI GPIO后,Vivado会自动分配地址。如果你手动改过地址产生重叠,Validating Design时不一定爆出来,但生成比特流阶段会炸。遇到这种情况,打开Block Design的Address Editor,把所有外设地址恢复成自动分配,再重新生成一次比特流。

3. 导出XSA并在Vitis里创建平台:两个工具握手的关键

3.1 Export Hardware时勾不勾Include bitstream,差别很大

比特流生成成功之后,第一步是把硬件信息导出给Vitis。在Vivado菜单栏选File > Export Hardware,弹出的对话框里有一个“Include bitstream”复选框。

这个选项的含义非常实际:勾上,导出的XSA文件里会带上比特流,Vitis在调试运行时自动把PL侧配置好;不勾,XSA里只有硬件描述,PS侧程序能跑,但PL侧不加载任何逻辑。既然我们要走完整的Hello World到BOOT.BIN流程,建议勾上,后面生成BOOT.BIN时可以直接引用XSA里的比特流资源。

导出的XSA路径自己选,建议和Vivado工程放一起。XSA文件名不要带中文、不要带空格,Vitis对路径的支持没有Vivado那么好,这种细节踩过一次永远记得。

3.2 版本不匹配:XSA导入失败的常见原因

XSA本质上是一个硬件平台描述包,Vivado和Vitis的版本必须严格对应。用Vivado 2022.2导出的XSA,拿到Vitis 2023.2里创建平台,大概率会提示找不到硬件定义或者直接创建失败。这套工具链的版本耦合非常紧密,混用版本是各种怪异bug的头号来源。

这里还要提一个2023.2特有的混淆点:这版Vitis同时提供Vitis Classic和Vitis Unified IDE两种界面。从Vivado的Tools菜单Launch Vitis IDE,打开的是哪个界面取决于你的安装选项。如果你习惯传统SDK那种界面,务必找Vitis Classic入口;Unified IDE的工程结构和操作逻辑完全不一样,网上大部分教程截图还是Classic的,新手用Classic能少走很多弯路。

3.3 Vitis工作空间的三个忌讳

启动Vitis时会让选工作空间目录,这里有几个经验:

  1. 不要选C盘系统目录,权限问题会引发各种你根本想不到的错误。
  2. 不要和旧的SDK工程共用工作空间,平台名冲突会让你导入XSA时直接报错。
  3. 路径不要带中文,和XSA文件的约束同理。

进入Vitis之后,推荐先创建平台工程:File > New > Platform Project,选择导出的XSA文件,OS选standalone,CPU选ps7_cortexa9_0。Vitis会自动生成BSP(板级支持包),这个过程通常要跑一会儿,BSP生成失败的话,多半是前面XSA导出或者版本匹配环节出了问题,回头查,别在Vitis里硬撑。

4. Hello World跑通:从模板选择到串口里蹦出字

4.1 模板选错,程序能编译但表现完全不对

在Vitis里创建Application Project时,最后一步会让你选模板,常见的有Empty Application(C)、Hello World、Peripheral Tests等。新手如果选了Empty Application,就得自己手写helloworld.c和链接脚本,完全没必要。直接选Hello World模板,它会自动生成一个带main函数、调用xil_printf的源文件,点编译就能出elf。

另外一个容易被忽略的选项是处理器选择。ZYNQ-7000平台下要选ps7_cortexa9_0,也就是双核Cortex-A9的CPU0。如果选成ps7_cortexa9_1,编译能过,但下载调试时的行为会让新手摸不着头脑。选CPU0,准没错。

4.2 串口终端连不上:先看COM口,再看波特率

Hello World跑起来后,输出通过UART打印,板载USB转串口芯片把UART信号转成USB,在电脑上显示为一个COM口。很多人在Vitis里点了Run As > Launch Hardware,程序其实已经在跑了,但终端窗口里什么都没有,于是开始怀疑程序。

问题多半出在串口终端根本没连上。在Vitis Classic里,需要手动打开终端视图:Window > Show View > Terminal,然后在Terminal窗口里点那个绿色的小图标,选择Launch Terminal,连接类型选Serial,选择正确的COM口,波特率填115200,点OK连接。如果设备管理器里压根没有COM口,回去装USB-UART驱动,就是1.4节那一步。

还有一种情况是串口有输出但全是乱码。这基本就是波特率不匹配。要么在Vitis终端里改波特率,要么回Vivado的Block Design里改UART1的波特率,两边保持一致,乱码立刻消失。

4.3 程序下进去没反应:从DDR配置到启动模式逐项排查

如果串口终端已经连好,波特率也对,Run之后仍然没有任何输出,我的排查顺序是这样:

先用Debug As > Launch Hardware走一遍,看调试器能不能正常连上目标。如果连调试器都连不上,跳回第5节;如果调试器能连上,程序也确实跑到了main函数,但串口还是没东西,重点查两个地方。

第一是DDR是否真的初始化成功。Hello World的链接脚本会把堆栈放在DDR里,如果Vivado里DDR配置不对,程序一旦访问内存就异常。这类问题在Vivado里往往看不到明显报错,只能打开Block Design逐项核对DDR型号、位宽、频率是否和板载颗粒一致。

第二是串口引脚接的是PS侧还是PL侧。UART1的默认位置是MIO14/15,但有些开发板的USB转串口芯片接到的是PL侧引脚,不走PS的MIO。这种情况Hello World默认从PS UART打印,你当然什么都看不到。方法只有一个:看板子原理图,确认USB转串口芯片的TXD/RXD接到的是MIO还是PL引脚,再决定怎么配置。

4.4 程序停在断点的“假失败”:新手最容易误判

还有一个细节很多人不知道:在Vitis里用Debug As > Launch Hardware调试时,程序默认会在main函数入口停住。这时候如果你看Terminal窗口,发现没有输出,以为程序出问题了,其实只是停在了断点。按一下F8(Resume),程序继续跑,串口才开始输出。

我第一次调试时就因为不知道这个,把同一个工程反复编译重跑了好几遍,最后才发现只是一个断点的问题。调试模式下看到绿色箭头和暂停状态是正常的,先让它跑起来再说。

5. 下载调试时“不识别芯片”:完整排查链路拆给你看

5.1 先分清是哪个环节不识别

“vitis 下载调试的时候 不识别芯片”这个搜索词出现的频率极高,但真正是芯片坏掉的情况少之又少。我习惯把这个大问题拆成三段来看:

  1. Vivado的Hardware Manager能不能扫到JTAG链上的目标。
  2. Vitis里能不能连上调试目标(target)。
  3. 调试器能不能正常读写ARM核。

逐段排查比在Vitis里反复试有效得多。先在Vivado里打开Flow > Open Hardware Manager,然后Open Target > Auto Connect。如果这里能识别到板子的器件IDCODE,说明JTAG链路通了,问题在Vitis侧;如果这里就报错,驱动或者硬件连接占大头。这一步能帮你把排查范围缩小一半。

5.2 JTAG扫不到目标:驱动、线缆、供电、BOOT模式四连查

在Hardware Manager里就扫不到目标,按出现概率排序:

首先是驱动问题。设备管理器里如果有带黄色感叹号的未知设备,基本就是Cable驱动没装好,按1.4节的方式重装驱动。

其次是线缆和接口。很多ZYNQ开发板不止一个USB接口:有供电口、有串口、还有JTAG口。插错口的表现就是USB设备能识别,但Hardware Manager空无一物。查板子说明书确认哪个口对应JTAG,别想当然。

第三是供电不足。ZYNQ对供电很敏感,如果只靠JTAG下载线USB供电,部分板子会出现电压不稳,JTAG扫描时有时无。大板子务必接上配套电源,再插调试线。

第四是BOOT模式跳线,这是最容易被忽视的原因。ZYNQ的启动模式由MIO[5:2]引脚电平决定,板子上通常做成跳线帽或拨码开关。调试时必须把启动模式切到JTAG模式,如果跳线在QSPI或SD的位置,核心可能处于异常状态,JTAG链就扫不到。具体跳线位置查板子手册的BOOT MODE章节,这一步不能跳过。

5.3 Vitis里0 targets可用:多数是调试配置问题

Vivado里能扫到芯片,但Vitis的Debug Configuration显示0 targets,这种一般是调试配置问题。打开Debug Configurations,找到你的hello_world调试配置,确认Target连接方式是Hardware Server,连接地址选localhost,端口3121不要乱改。Vitis通过本地调试服务和JTAG通信,端口配错了自然找不到目标。

还有一种情况:Vitis启动后工作空间里的平台工程信息过期。最简单有效的办法是把Vitis完全关闭重开,重新打开平台工程,新建一个调试配置再试。这类刷新问题重启能解决大半。

5.4 一个真实案例:折腾两天的“不识别芯片”,败给一个跳线帽

说个我自己的案例。调试一块ZYNQ板子时,Vivado的Hardware Manager一切正常,设备管理器里驱动也都正常,但Vitis里就是连不上目标。重装驱动、换线、换USB口、换电脑,折腾了将近两天,最后才发现板子上有个标注BOOT MODE的三针跳线,出厂默认插在QSPI位置,需要拔下来插到JTAG位置。就这一个跳线帽,浪费了整整一天半。

从那以后,我拿到任何一块新板子,第一件事就是翻开原理图,把BOOT MODE对应关系抄下来贴在板子上。排查到焦头烂额的时候,回头看一眼跳线位置,往往答案就在那里。

6. 生成BOOT.BIN并固化启动:让板子脱离电脑自己跑

6.1 BOOT.BIN里到底装了什么东西

BOOT.BIN不是一个普通的应用程序,而是符合ZYNQ启动协议的镜像打包文件。它内部按顺序包含三段内容:

  1. FSBL(First Stage Boot Loader,第一级引导程序),负责初始化DDR、时钟,然后把后续镜像从启动介质搬到内存。
  2. 比特流,也就是PL侧的硬件配置。
  3. 应用程序的elf文件,就是我们的Hello World程序。

理清这个顺序很关键。ZYNQ上电后,片内BootROM先跑,它根据BOOT模式引脚决定从哪里读FSBL,FSBL再引导后面的东西。所以你必须把这三段按协议打包成BOOT.BIN放到启动介质里,单独一个裸elf文件是没法上电自动启动的。很多人问“为什么我放个elf进SD卡启动不了”,原因就在这里。

6.2 在Vitis Classic里构建FSBL并生成BOOT.BIN

生成BOOT.BIN之前,要先有FSBL这个工程。在Vitis里新建一个Application Project,平台选之前建好的同一个平台,工程名建议叫fsbl,模板选择“Zynq FSBL”。编译这个工程,生成fsbl.elf。

接着回到Hello World工程,右键选择Create Boot Image。弹出的对话框是bootgen这个工具的图形界面,默认会创建一个BIF配置文件。你要检查分区列表里是不是有三样东西,顺序如下:

  1. FSBL的elf,文件类型选bootloader。
  2. 比特流文件(.bit),文件类型datafile。如果导出XSA时勾了Include bitstream,Vitis通常会自动关联;没有的话手动添加Vivado工程impl_1目录下的.bit文件。
  3. Hello World的elf,文件类型datafile。

哪个分区缺失就用Add按钮手动加,文件类型和顺序不能乱。点Generate Image后,BOOT.BIN会输出到Hello World工程的_ide/bootimage目录下。

如果你更习惯命令行,也可以直接写BIF文件调用bootgen:

bootgen -image boot.bif -o BOOT.BIN -w on

BIF文件里就是上面三段分区的描述,效果和图形界面完全一样。

6.3 SD卡启动验证:文件名、格式和跳线一个都不能错

生成BOOT.BIN之后,最简单的验证方式是SD卡启动。

准备一张SD卡,格式化成FAT32,把BOOT.BIN复制到根目录。文件名必须叫BOOT.BIN,全部大写,不能改成hello.BIN之类。BootROM是按固定文件名查找的,改名就找不到。这个细节看着弱智,但我确实见过有人卡在这里。

把板子的启动模式跳线切到SD启动,插上SD卡,接好串口终端,上电。一切正常的话,串口会打印出Hello World,流程闭环。没输出就检查三件事:SD卡是不是FAT32、BOOT.BIN在不在根目录、启动模式跳线有没有切对。有一个操作细节可以分享:上电前按住板上的复位按键,上电后再松开,有些板子的SD卡识别不稳定,这样做能提高启动成功率,本质上是让复位时序更干净。

6.4 固化到QSPI Flash:为什么还要再绕一次FSBL

SD卡启动适合开发调试,但想脱离外部存储上电自启,就得把BOOT.BIN烧进板载QSPI Flash。在Vitis Classic里,菜单Xilinx > Program Flash打开烧写工具。

这个对话框有四个关键字段:

字段填写内容注意点
Image File选择BOOT.BIN就是刚生成的镜像
FSBL File选择fsbl.elf烧写过程需要FSBL先驻留OCM执行
Flash Type选择板子QSPI规格常见qspi-x4-single,选错直接烧写失败
Offset默认0从Flash起始地址写入

有人会问,为什么烧Flash还要指定FSBL?因为这整个烧写过程其实是先把FSBL通过JTAG加载进OCM运行,再由FSBL去擦除和写入Flash。所以FSBL这个文件是烧写流程的一部分,不能空缺。

点Program之后,Vitis会先连JTAG、下载FSBL,然后擦除Flash并写入镜像,整个过程几分钟。中途不要断电,不要拔USB线,等状态栏提示完成。烧完后把启动模式切到QSPI,重新上电,串口应该直接打印Hello World。

6.5 固化后不启动的三个排查方向

固化完成但上电没反应,从高频到低频列三个方向:

第一,启动模式跳线是否真的切对了。QSPI和SD的跳线位置容易混淆,板子标注不清晰就用万用表量MIO[5:2]的电平确认,这一步最基础但最关键。

第二,Flash Type选错。如果烧写显示成功,但上电后连FSBL都没跑起来,大概率是Flash通道配置不对。回去查板载Flash型号,确认是单通道还是四通道,重新烧一次。

第三,BOOT.BIN本身是Debug编译的。调试阶段的elf和固化用的elf最好都用Release编译,Debug编译的elf固化后偶尔会出现上电启动异常。重新编译Release版本再打包一次,成本很低,但能排除一类很隐蔽的问题。

最后聊一个老生常谈但永远有人栽跟头的事:版本一致性。Vivado、Vitis、XSA、FSBL、BSP这一整条链路上的每一环都必须来自同一个版本,混用版本是各种灵异bug的最大温床。如果你遇到的问题不在上面列出,先把整个workspace删掉,从版本链路开始重新捋一遍,比在论坛里翻帖子高效得多。

再补一个实用小习惯:把每个版本的XSA、BOOT.BIN、Vivado工程都归档好,文件名带上日期和板卡型号。我手里至少有两次“这个版本上周还能启动”的惨痛经历,最后发现是找不到上周那份BOOT.BIN到底存在哪。把版本归档做好,在这套工具链上踩坑的频率会明显低下去。

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

USB转I2C适配器扫总线:400KHz速率下设备枚举与Excel报表实战

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

作者头像 李华
网站建设 2026/9/25 4:47:59

Windows上用Podman替代Docker:WSL2+rootless容器实战指南

1. 为什么在 Windows 上用 Podman 而不是 Docker?——一个容器老兵的真实选择Podman 在 Windows 上的出现,不是为了“替代 Docker”,而是为了解决 Docker Desktop 在企业级、合规性、资源占用和许可政策上越来越明显的硬伤。我从 2018 年开始…

作者头像 李华
网站建设 2026/9/25 4:47:41

allpairs正交表测试用例生成实战:下载配置与组合覆盖优化

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

作者头像 李华
网站建设 2026/9/25 4:47:28

VSCode+Keil搭建STC8G1K08A开发环境:从零到一键编译烧录

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

作者头像 李华