news 2026/9/20 7:32:32

RIOT 的 Silicon Labs SLWSTK6000B + SLWRB4162A 板级支持:硬件定位、构建系统集成与外设配置全解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RIOT 的 Silicon Labs SLWSTK6000B + SLWRB4162A 板级支持:硬件定位、构建系统集成与外设配置全解

RIOT 的 Silicon Labs SLWSTK6000B + SLWRB4162A 板级支持:硬件定位、构建系统集成与外设配置全解

【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT

SLWSTK6000B 搭配 SLWRB4162A 无线子模块,是 RIOT 中面向 EFR32MG(Mighty Gecko)系列 MCU 的 2.4 GHz 多协议无线开发板。本文基于仓库中的板级文档 boards/slwstk6000b-slwrb4162a/doc.md 展开,结合该板在 RIOT 构建系统、Kconfig、外设配置与引脚映射中的真实实现,说明如何在 RIOT 中构建、烧录和调试这块开发板。读完本文,读者可以掌握该板的硬件定位(MCU 型号、模块选择机制)、Makefile/Kconfig 的继承结构,以及 ADC、I2C、SPI、UART、Timer、时钟树等外设的具体配置方式。

1. 硬件平台定位:EFR32MG 无线 Gecko 平台

boards/slwstk6000b-slwrb4162a/doc.md 对该板的一句话定位是:

SLWSTK6000B 搭配 SLWRB4162A 提供基于 EFR32MG 系列 MCU 的 Wireless Gecko 平台,具备扩展内存与射频特性,适合高级 2.4 GHz 与多协议无线开发。

具体来说,SLWSTK6000B 是一块“底板 + 可换无线模块”的开发套件:主底板负责供电、调试接口、板控制器(Board Controller)和高级能量监测器(Advanced Energy Monitor, AEM),而真正决定 MCU 型号与射频能力的是插入其中的 SLWRB 系列无线模块。SLWRB4162A 模块的 MCU 为EFR32MG12P332F1024GL125,这可以直接从该板的 Kconfig 配置中确认,见 boards/slwstk6000b-slwrb4162a/Kconfig:

config BOARD_SLWSTK6000B_SLWRB4162A bool default y select CPU_MODEL_EFR32MG12P332F1024GL125 select BOARD_COMMON_SLWSTK6000B

select BOARD_COMMON_SLWSTK6000B揭示了 RIOT 的组织方式:同一底板的所有模块变体共享一套公共板级支持代码,位于 boards/common/slwstk6000b/;具体板目录只承担“底板 + 模块”组合的声明职责。

1.1 底板支持的模块与 MCU 映射

公共文档 boards/common/slwstk6000b/doc.md 给出了底板与模块的完整映射表,所有模块均基于 EFR32 Series 1(Mighty Gecko)MCU:

模块MCU
SLWRB4150AEFR32MG1P233F256GM48
SLWRB4150BEFR32MG1P233F256GM48
SLWRB4151AEFR32MG1P232F256GM48
SLWRB4152AEFR32MG1P232F256GM48
SLWRB4153AEFR32MG1P132F256GM48
SLWRB4154AEFR32MG1P732F256GM32
SLWRB4158AEFR32MG13P733F512GM48
SLWRB4159AEFR32MG13P632F512GM48
SLWRB4161AEFR32MG12P432F1024GL125
SLWRB4162AEFR32MG12P332F1024GL125
SLWRB4163AEFR32MG12P433F1024GL125
SLWRB4164AEFR32MG12P433F1024GL125
SLWRB4167AEFR32MG13P733F512GM48
SLWRB4168AEFR32MG13P732F512GM48
SLWRB4169AEFR32MG14P733F256GM48
SLWRB4169BEFR32MG14P733F256GM48
SLWRB4170AEFR32MG12P433F1024GM68

注意:并非表中所有 MCU 都获得 RIOT 的默认支持,具体以仓库中实际存在的板级目录为准。

1.2 从板名解析 CPU 型号:info.mk 的实现

构建系统如何知道slwstk6000b-slwrb4162a对应哪颗 MCU?答案是 boards/common/slwstk6000b/slwstk6000b.info.mk,其中每一行记录“模块名 -> CPU_MODEL、JLink 设备”的映射:

# CPU_MODEL - JLINK_DEVICE SLWSTK6000B_MAINBOARD_VARS_slwrb4162a = efr32mg12p332f1024gl125 efr32mg12p332f1024gl125

第二列的 JLink 设备名efr32mg12p332f1024gl125用于烧录时让 J-Link 驱动识别目标芯片。

2. 构建系统集成:一个“薄”板目录如何工作

该板在仓库中的目录结构非常精简,全部文件如下:

  • boards/slwstk6000b-slwrb4162a/Makefile
  • boards/slwstk6000b-slwrb4162a/Makefile.dep
  • boards/slwstk6000b-slwrb4162a/Makefile.features
  • boards/slwstk6000b-slwrb4162a/Makefile.include
  • boards/slwstk6000b-slwrb4162a/Kconfig
  • boards/slwstk6000b-slwrb4162a/doc.md

这是 RIOT 板级支持的典型分层模式,下面逐个文件说明。

2.1 Makefile:委托公共板目录

boards/slwstk6000b-slwrb4162a/Makefile 只有三行有效内容:

MODULE = board DIRS += $(RIOTBOARD)/common/slwstk6000b include $(RIOTBASE)/Makefile.base

DIRS追加公共板目录,意味着该板的所有板级 C 源文件(如 boards/common/slwstk6000b/board.c)和头文件(include/board.hinclude/periph_conf.hinclude/gpio_params.h)都来自公共目录。

2.2 Makefile.features:模块识别与功能声明

boards/slwstk6000b-slwrb4162a/Makefile.features 定义了本板的关键属性:

CPU_FAM = efr32mg12p include $(RIOTBOARD)/common/slwstk6000b/Makefile.features

CPU_FAM = efr32mg12p表明 CPU 家族是 EFR32MG12P。随后包含的公共 features 文件 boards/common/slwstk6000b/Makefile.features 做了两件重要的事:

  1. 从 BOARD 变量解析模块名
BOARD_MODULE = $(word 2, $(subst -, ,$(BOARD)))

即把slwstk6000b-slwrb4162a-拆开取第二段slwrb4162a作为模块名,再结合 info.mk 得到CPU_MODEL = efr32mg12p332f1024gl125。若模块无法识别,构建会直接报错:

ifeq (,$(CPU_MODEL)) $(error $(BOARD_MODULE) can not be used with slwstk600b mainboard) endif

这解释了为什么BOARD必须使用“底板-模块”完整命名:模块名是 CPU 型号解析的输入。

  1. 声明提供的外设特性(FEATURES_PROVIDED):periph_adcperiph_i2cperiph_rtcperiph_rttperiph_spiperiph_timerperiph_uart。这些特性使 RIOT 的依赖解析机制(features 检查)能够判断本板可运行的应用集合。

公共目录还进一步包含 Silicon Labs 平台的公共 features boards/common/silabs/Makefile.features:

CPU = efm32 FEATURES_PROVIDED += arduino_analog FEATURES_PROVIDED += arduino_pins FEATURES_PROVIDED += efm32_coretemp FEATURES_PROVIDED += riotboot

即该板按 EFM32 系列 CPU 处理,同时提供 Arduino 兼容的模拟/数字引脚接口(Arduino 引脚映射定义于 boards/common/silabs/include/arduino_iomap.h)、片上核心温度传感器特性和 RIOT 引导加载器支持。

2.3 Makefile.dep:模块依赖

板级依赖声明在 boards/slwstk6000b-slwrb4162a/Makefile.dep:

USEMODULE += board_common_slwstk6000b include $(RIOTBOARD)/common/slwstk6000b/Makefile.dep

被包含的公共依赖文件 boards/common/slwstk6000b/Makefile.dep 揭示了编译该板时自动引入的驱动模块:

# add board common drivers USEMODULE += boards_common_silabs USEMODULE += silabs_aem USEMODULE += silabs_bc
  • silabs_bc:板控制器(Board Controller)驱动,提供虚拟串口,实现见 boards/common/silabs/drivers/bc/bc.c;
  • silabs_aem:高级能量监测器驱动,实现见 boards/common/silabs/drivers/aem/aem.c;
  • 若启用saul_default(SAUL 默认传感器驱动集),还会额外引入efm32_coretempsaul_gpiosi7021(底板上的温度/湿度传感器)。

2.4 Kconfig 继承链

Kconfig 侧同样是两级继承,见 boards/slwstk6000b-slwrb4162a/Kconfig:

config BOARD default "slwstk6000b-slwrb4162a" if BOARD_SLWSTK6000B_SLWRB4162A config BOARD_SLWSTK6000B_SLWRB4162A bool default y select CPU_MODEL_EFR32MG12P332F1024GL125 select BOARD_COMMON_SLWSTK6000B source "$(RIOTBOARD)/common/slwstk6000b/Kconfig"

公共层 boards/common/slwstk6000b/Kconfig 则继续select BOARD_COMMON_SILABS并引入 boards/common/silabs/Kconfig。整体形成BOARD_SLWSTK6000B_SLWRB4162A -> BOARD_COMMON_SLWSTK6000B -> BOARD_COMMON_SILABS的选择链,同时锁定 CPU 型号CPU_MODEL_EFR32MG12P332F1024GL125

3. 外设配置:periph_conf.h 逐项解读

该板所有 MCU 外设的静态配置集中在 boards/common/slwstk6000b/include/periph_conf.h,配置通过 Silicon Labs 的 EMLIB 抽象层完成。以下按外设逐项说明。

3.1 时钟树配置

periph_conf.h 第 33-47 行 定义了四条时钟分支的默认源:

#ifndef CLOCK_HF #define CLOCK_HF cmuSelect_HFXO /* 默认使用外部晶振 38.4 MHz */ #endif #ifndef CLOCK_CORE_DIV #define CLOCK_CORE_DIV cmuClkDiv_1 #endif #ifndef CLOCK_LFA #define CLOCK_LFA cmuSelect_LFRCO #endif #ifndef CLOCK_LFB #define CLOCK_LFB cmuSelect_LFRCO #endif #ifndef CLOCK_LFE #define CLOCK_LFE cmuSelect_LFRCO #endif

注意每个宏都用#ifndef包裹,即可以通过编译定义(compiler defines)覆盖。可用的时钟源(频率为本底板特有值):

时钟源内/外部频率备注
HFRCO内部19 MHz启动时默认使能,可切换
HFXO外部38.4 MHz
LFRCO内部32.768 kHz
LFXO外部32.768 kHz
ULFRCO内部1 kHz作为时间源不可靠

时钟源与分支的对应关系:

分支可用时钟源用途
HFHFRCO、HFXO内核与外设
LFALFRCO、LFXO低功耗定时器
LFBLFRCO、LFXO、CORELEDIV2低功耗 UART
LFELFRCO、LFXO实时时钟与日历(RTCC)

其中 CORELEDIV2 是一个依赖内核时钟源的派生源,EMLIB 会自动将其分频 2 或 4 倍以避免超过最大允许频率。

由于代码需要知道精确的时钟频率才能正确计算分频与波特率,若板载晶振与上表不同,需要向编译器传入EFM32_HFXO_FREQ=freq_in_hzEFM32_LFXO_FREQ=freq_in_hz;也可以通过编译定义覆盖分支时钟源,例如CLOCK_LFA=cmuSelect_LFRCO

3.2 低功耗 UART 的波特率限制

这是一个与省电直接相关的约束:当 LFB 分支的时钟源为 LFRCO 或 LFXO 时,低功耗 UART 在 EM2 睡眠模式下仍可工作,但波特率被限制在9600 baud。若需要更高波特率,应将 LFB 时钟源切换为 CORELEDIV2。公共文档同时提醒:这个设置不影响periph_conf.h中的引脚映射,但应用代码不应再引用低功耗外设。

3.3 RTCC:RTT 与 RTC 二选一

EFR32MG 系列拥有一个 32 位实时时钟与日历(RTCC)外设,它可以配置为计时器(ticker)模式日历(calendar)模式,两者互斥——因此 RIOT 的 Real-Time Ticker 与 Real-Time Clock 只能启用其一。以 1 Hz 间隔配置时,RTCC 约每 136 年溢出一次。periph_conf.h中 RTT 默认频率定义为 1 Hz(第 106-108 行)。

3.4 ADC

ADC 使用 ADC0,提供两个通道(第 54-77 行):

static const adc_chan_conf_t adc_channel_config[] = { { .dev = 0, .input = adcPosSelTEMP, .reference = adcRef1V25, .acq_time = adcAcqTime8 }, { .dev = 0, .input = adcPosSelAVDD, .reference = adcRef5V, .acq_time = adcAcqTime8 } };

第一通道测量片上核心温度(TEMP 输入,1.25 V 基准),第二通道测量 AVDD 电源电压。

3.5 I2C

I2C0 配置在模块引脚 P13(SDA)/ P12(SCL),使用路由位置 LOC16/LOC14,标准速率(100 kHz 档),并启用内部上拉(第 84-98 行):

static const i2c_conf_t i2c_config[] = { { .dev = I2C0, .sda_pin = MODULE_PIN_P13, .scl_pin = MODULE_PIN_P12, .loc = I2C_ROUTELOC0_SDALOC_LOC16 | I2C_ROUTELOC0_SCLLOC_LOC14, .speed = I2C_SPEED_NORMAL, .cmu = cmuClock_I2C0, .irq = I2C0_IRQn, .use_internal_pull_ups = true } };

3.6 SPI(USART 复用)与 UART

SPI 由 USART1 复用实现,仅支持主机模式(第 115-127 行):MOSI 在 F16、MISO 在 P3、CLK 在 F15,路由位置均为 LOC11。默认控制台 UART 使用 USART0,RX 在 F7、TX 在 F6,路由位置 LOC0(第 174-184 行)。

3.7 Timer:级联定时器与低功耗定时器

periph_conf.h声明了两套 timer(第 138-163 行):

  • TIMER0(预分频级)+ TIMER1(定时级):两路硬件定时器以级联(cascade)方式组合成一路 64 位定时器,提供 3 个捕获/比较通道,中断为TIMER1_IRQn
  • LETIMER0:低功耗定时器,2 个通道,中断为LETIMER0_IRQn,可配合 LFRCO/LFXO 在 EM2 下工作。

3.8 引脚映射:MODULE_PIN 的定义来源

上面配置中反复出现的MODULE_PIN_P13等宏,来自模块专属头文件 boards/common/slwstk6000b/modules/slwrb4162a/include/board_module.h。该文件定义了 SLWRB4162A 模块的全部引脚(注意注释强调:引脚编号对应模块而非底板),例如:

#define MODULE_PIN_F0 GPIO_PIN(PF, 1) #define MODULE_PIN_F6 GPIO_PIN(PA, 0) /* USART0 TX */ #define MODULE_PIN_F7 GPIO_PIN(PA, 1) /* USART0 RX */ #define MODULE_PIN_P12 GPIO_PIN(PC, 10) /* I2C SCL */ #define MODULE_PIN_P13 GPIO_PIN(PC, 11) /* I2C SDA */ #define MODULE_PIN_P23 GPIO_PIN(PI, 0) /* ... 完整列表共 F0-F21、P0-P45 */

GPIO_UNDEF表示该编号在 SLWRB4162A 上没有对应引脚(如MODULE_PIN_F4MODULE_PIN_P18)。这也体现了公共底板的扩展机制:每个模块变体(如 slwrb4150a)都有各自独立的modules/<module>/include/board_module.hperiph_conf.h通过#include "board_module.h"拿到当前模块的引脚表,从而同一套公共配置代码适配不同模块。

EFR32 平台支持外设引脚扩展映射(extended pin mapping):每个外设引脚可独立连接到该外设的一组预定义引脚之一。因此periph_conf.h中的定义由“实际 GPIO 引脚 + 路由位置(loc)”两部分组成,两者必须匹配(详见芯片数据手册)。

4. 板控制器(Board Controller)与能量监测(AEM)

4.1 板控制器与虚拟串口

SLWSTK6000B 底板自带一个板控制器 MCU,它提供一个虚拟串口,通过一个 GPIO 使能。RIOT 默认启用该模块(对应上文USEMODULE += silabs_bc),可以通过向make传入DISABLE_MODULE=silabs_bc关闭。使用时需注意两点(摘自 boards/common/slwstk6000b/doc.md):

  1. 使用虚拟串口前,请确保板控制器固件为最新版本;
  2. 板控制器始终把虚拟串口配置为 115200 baud、8 数据位、无校验、1 停止位(115200 8N1),MCU 侧输出必须与之匹配。

4.2 高级能量监测器(AEM)

该套件搭载 Advanced Energy Monitor,可与 Silicon Labs Simplicity Studio 开发软件配合,对硬件与代码做代码级实时功耗测量:板控制器负责能量采样,MCU 侧则配置 CoreDebug 外设,通过 SWO 端口输出寄存器与中断数据,用于将功耗曲线与代码执行相关联。

该特性默认开启,可通过DISABLE_MODULE=silabs_aem关闭。一个容易踩的坑:Simplicity Studio 做代码关联需要调试符号,RIOT 默认生成 GDB 格式调试符号,而 Simplicity Studio 要求 DWARF-2 符号(GCC 的-gdwarf-2选项),需要相应调整编译选项。

5. 使用 EMLIB:外设抽象与调试断言

该板级移植基于 Silicon Labs 的 EMLIB 对外设寄存器做抽象。其收益与代价:

  • 保证外设配置正确,并自动处理芯片勘误(errata),简化开发;
  • 大部分读写/获取/设置方法以 inline 函数或宏实现(零开销);主要开销出现在外设初始化阶段;
  • EMLIB 自带断言可以校验外设使用是否正确,向编译器传入DEBUG_EFM定义即可启用;
  • EMLIB 采用 zlib 风格许可证,允许源码分发。

6. 编译、烧录与调试

6.1 编译

该板属于 Cortex-M 类目标板,需要交叉编译工具链(如arm-none-eabi)。以examples下任一应用为例,标准编译命令为:

make -C examples/basic/blinky BOARD=slwstk6000b-slwrb4162a

如板载晶振与默认值不同,或希望切换低功耗分支时钟源,可在命令中附加编译定义,例如:

make -C examples/basic/blinky BOARD=slwstk6000b-slwrb4162a CFLAGS='-DEFM32_HFXO_FREQ=38400000' make -C examples/basic/blinky BOARD=slwstk6000b-slwrb4162a CFLAGS='-DCLOCK_LFA=cmuSelect_LFRCO'

6.2 烧录

烧录需要 SEGGER J-Link 调试器(底板已集成接口)。RIOT 内置烧录支持:

make -C examples/basic/blinky BOARD=slwstk6000b-slwrb4162a flash

J-Link 目标设备名(efr32mg12p332f1024gl125)来自 boards/common/slwstk6000b/slwstk6000b.info.mk 的映射表。

6.3 调试

# 启动 RIOT 内置 GDB 调试会话 make -C examples/basic/blinky BOARD=slwstk6000b-slwrb4162a debug # 只启动 OpenOCD 调试服务器,连接自己的 GDB make -C examples/basic/blinky BOARD=slwstk6000b-slwrb4162a debug-server

此外,部分板支持有限的仿真(emulate)流程,可用make emulate启动:

make -C examples/basic/blinky BOARD=slwstk6000b-slwrb4162a emulate

7. 实现状态与限制一览

公共文档 boards/common/slwstk6000b/doc.md 给出的外设支持状态:

设备ID支持备注
MCUEFR32MG1P/12P/13P/14P支持电源模式
底层驱动ADC
Flash
GPIO中断在各引脚间共享(见参考手册)
HWRNG仅 EFR32MG12P 有 TRNG,EFR32MG1P 没有
I2C
PWM
RTCC可配置为 RTT 或 RTC(二选一)
SPI仅主机模式
Timer
UARTUSART 与 SPI 共享;LEUART 波特率受限(见 3.2)
USB

对本板(SLWRB4162A / EFR32MG12P332F1024GL125)而言:拥有真随机数发生器(TRNG),无 USB 外设;SPI 仅主机模式;GPIO 中断在引脚间共享是 EFM32 系列特性,编写中断密集应用时需要留意。

8. 相关文件索引

内容路径
板级文档(本文核心依据)boards/slwstk6000b-slwrb4162a/doc.md
板级构建与 Kconfigboards/slwstk6000b-slwrb4162a/Makefile.features、boards/slwstk6000b-slwrb4162a/Kconfig
公共底板文档(模块表/时钟/烧录)boards/common/slwstk6000b/doc.md
模块-MCU 映射表boards/common/slwstk6000b/slwstk6000b.info.mk
外设静态配置boards/common/slwstk6000b/include/periph_conf.h
SLWRB4162A 模块引脚定义boards/common/slwstk6000b/modules/slwrb4162a/include/board_module.h
板控制器 / 能量监测驱动boards/common/silabs/drivers/bc/bc.c、boards/common/silabs/drivers/aem/aem.c
Silicon Labs 平台公共特性boards/common/silabs/Makefile.features

综合来看,SLWSTK6000B + SLWRB4162A 在 RIOT 中的支持呈现清晰的三层结构:具体板目录(slwstk6000b-slwrb4162a)负责声明 CPU 家族与模块依赖,公共底板目录(common/slwstk6000b)承载时钟树、外设配置、引脚映射与模块识别逻辑,Silicon Labs 平台层(common/silabs)提供 Arduino 引脚兼容、板控制器与能量监测等跨板能力。掌握这一结构后,无论是适配新模块、调整时钟源还是排查串口功耗问题,都可以直接定位到上表对应的源码文件。

【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

Claude Code免确认指南:玩转权限模式,告别频繁回车Yes

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

作者头像 李华
网站建设 2026/9/20 7:30:39

Word中高效输入LaTeX公式:内置模式、插件替代与进阶工作流

搞科研或者理工科写材料的朋友&#xff0c;应该都体会过公式排版的别扭&#xff1a;Word自带的公式编辑器用鼠标一点点点选符号&#xff0c;写一个带分式、上下标、矩阵的式子&#xff0c;手速直接退回到“一指禅”时代&#xff1b;MathType确实好用&#xff0c;但新版走订阅制…

作者头像 李华
网站建设 2026/9/20 7:29:58

Maxwell与Workbench热耦合:从损耗映射到双向耦合的完整指南

简介&#xff1a;围绕Maxwell与Ansys Workbench热耦合联合仿真&#xff0c;这份PPT整理了电机、变压器、电子元件以及生物医学等场景下电磁-热单向耦合的完整学习路径。内容从热耦合基本概念入手&#xff0c;区分单向和双向耦合类型&#xff0c;说明各自适用条件&#xff0c;并…

作者头像 李华
网站建设 2026/9/20 7:28:40

Highcharts测量图实战:从零搭建设备温度实时监控仪表盘

做监控大屏这几年&#xff0c;我用到最多的图表类型&#xff0c;除了趋势类的折线图&#xff0c;剩下的可能就是测量图了。设备温度、电机转速、管道压力、机房湿度、CPU使用率、水位高低&#xff0c;几乎所有需要“实时盯住一个参数有没有越界”的场景&#xff0c;最后都会落到…

作者头像 李华
网站建设 2026/9/20 7:28:34

WPS自启动六层防护穿透指南:从任务计划到服务依赖的精准阻断

1. 项目概述&#xff1a;为什么“防止WPS自启动”成了高频刚需&#xff1f; 最近两周&#xff0c;我连续收到17位不同行业朋友的私信&#xff0c;问题高度集中&#xff1a;“WPS一开机就自己蹦出来&#xff0c;托盘图标占着不走&#xff0c;CPU占用30%还关不掉”“刚关掉WPS主…

作者头像 李华
网站建设 2026/9/20 7:28:32

一小时Python入门:环境配置、语法速通与实战项目

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

作者头像 李华