news 2026/9/24 8:37:41

SPI四种模式详解:从CPOL/CPHA原理到实战配置与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SPI四种模式详解:从CPOL/CPHA原理到实战配置与避坑指南

SPI 这玩意儿,说简单也简单,四根线一接,时钟一给,数据就能跑起来。但说坑也真坑,我见过太多人调试 SPI 的时候,示波器一挂,波形全对,数据就是读不出来,最后发现是模式选错了。Mode 0 和 Mode 3 在大多数芯片上都能凑合用,但一旦遇到对时序敏感的外设,比如某些 ADC、Flash 或者传感器,模式不对就是死活不通。这篇文章就把 SPI 的四种模式彻底拆开讲清楚,从时钟极性和相位的基本定义,到实际抓波形的对比,再到不同芯片上的配置方法和踩坑记录,争取让你看完之后不用再靠“四个模式挨个试”这种笨办法来调 SPI。

1. SPI 四种模式到底在说什么

1.1 从两根线的默认状态说起

SPI 协议的核心就四根线:SCLK、MOSI、MISO、CS。其中最容易让人迷糊的就是 SCLK 这根时钟线。很多人以为时钟就是方波,高低翻转就完事了,但实际上 SPI 协议对时钟线有两个维度的定义:时钟极性(CPOL)时钟相位(CPHA)。这两个参数一组合,就是四种模式。

先说 CPOL,它定义的是时钟线在空闲状态时是高电平还是低电平。CPOL=0 表示空闲时 SCLK 保持低电平,CPOL=1 表示空闲时 SCLK 保持高电平。这个“空闲”指的是 CS 拉低之前、以及数据传输间隙中时钟线的状态。你可以把它理解为“没开始干活的时候,时钟线默认待在哪一边”。

再说 CPHA,它定义的是数据在时钟的第几个边沿被采样。CPHA=0 表示在第一个边沿采样,CPHA=1 表示在第二个边沿采样。这里的“第一个边沿”和“第二个边沿”是相对于数据传输开始后的时钟翻转来说的。如果 CPOL=0,第一个边沿就是上升沿;如果 CPOL=1,第一个边沿就是下降沿。

这两个参数一交叉,就得到了四种组合:

模式CPOLCPHA空闲电平采样边沿移出边沿
Mode 000上升沿下降沿
Mode 101下降沿上升沿
Mode 210下降沿上升沿
Mode 311上升沿下降沿

这张表建议你直接记在脑子里,或者贴在工位上。因为不管你用的是 STM32、FPGA 还是 Linux 上的 spidev,配置的时候都绕不开这四个组合。

1.2 为什么采样边沿和移出边沿是错开的

这里有一个很多人没想明白的问题:为什么数据采样和移出要在不同的边沿?能不能在同一个边沿既采样又移出?

答案是不行。SPI 是全双工通信,主机和从机同时发送和接收数据。如果采样和移出在同一个边沿,那么发送方刚把数据推出去,接收方就要在同一时刻采样,这时候信号线上的电平可能还没稳定,采到的就是亚稳态或者错误数据。所以 SPI 协议规定,一个边沿用来移出数据(也就是发送方改变数据线电平),另一个边沿用来采样数据(接收方读取数据线电平)。两个边沿之间隔了半个时钟周期,给了信号足够的建立时间和保持时间。

这个设计思路和 I2C 有点像,但 I2C 是半双工,而且有应答机制,SPI 没有应答,全靠时序对齐。所以一旦模式配错,数据就全乱了,而且没有任何错误提示,你只能看到读出来的数据是 0x00 或者 0xFF,或者一堆乱码。

1.3 四种模式的实际使用分布

虽然协议定义了四种模式,但实际项目中并不是平均分布的。根据我这些年的经验,Mode 0 和 Mode 3 占了绝大多数场景,Mode 1 和 Mode 2 相对少见,但偏偏有些芯片就只支持这两种。

常见器件的模式分布大概是这样的:

  • SPI Flash(W25Q 系列、GD25 系列):通常支持 Mode 0 和 Mode 3,手册里一般写“Mode 0 or Mode 3”。
  • ADC 芯片(如 MCP3208、ADS1256):MCP3208 是 Mode 0/1,ADS1256 是 Mode 1。
  • 传感器(如 MPU6500、BMP280):多数是 Mode 0 或 Mode 3。
  • 显示屏(如 ST7789、ILI9341):通常是 Mode 0 或 Mode 3。
  • 某些专用芯片(如 MT6701 磁编码器):Mode 1 或 Mode 3,具体要看手册。

所以你在选型或者调试的时候,第一件事就是翻手册确认从机支持哪种模式。如果手册写了“支持 Mode 0 和 Mode 3”,那你就优先用 Mode 0,因为大多数 MCU 的默认配置就是 Mode 0,省事。

2. 时钟极性和相位的底层逻辑

2.1 CPOL 决定了时钟线的“休息状态”

CPOL 这个参数看起来简单,但它影响的是整个通信过程中的电平基准。如果 CPOL=0,时钟线在空闲时是低电平,那么第一个有效边沿就是上升沿。如果 CPOL=1,时钟线空闲时是高电平,第一个有效边沿就是下降沿。

这里有一个容易忽略的细节:CPOL 不仅影响空闲状态,还影响 CS 拉低到第一个时钟边沿之间的电平。有些从机芯片对这段时间的时钟线状态有要求,如果 CPOL 配错了,从机可能会误判为一次无效的起始条件,直接忽略后面的数据。

我遇到过这样一个案例:用 STM32F103 驱动一颗国产 SPI Flash,Mode 0 死活读不出 ID,换成 Mode 3 就正常了。后来用示波器抓波形发现,Mode 0 下 CS 拉低后时钟线有一个短暂的毛刺,虽然很窄,但 Flash 芯片把它当成了一个时钟边沿,导致后续数据错位。换成 Mode 3 之后,空闲电平是高,毛刺被淹没了,通信就正常了。这个案例说明,CPOL 的选择不只是协议匹配问题,还和硬件信号质量有关。

2.2 CPHA 决定了数据在哪个边沿被锁存

CPHA 的选择直接决定了数据采样时刻。CPHA=0 时,数据在第一个边沿被采样,也就是说发送方必须在第一个边沿之前就把数据准备好。CPHA=1 时,数据在第二个边沿被采样,发送方可以在第一个边沿时改变数据,接收方在第二个边沿采样。

用生活化的例子来理解:假设你和另一个人隔着一条传送带传递零件。CPHA=0 就像是你先把零件放在传送带上,然后按下启动按钮(第一个边沿),对方在按钮按下的瞬间就把零件取走。CPHA=1 就像是你按下启动按钮(第一个边沿)之后,传送带开始动,你在传送带移动的过程中把零件放上去,对方在传送带停止(第二个边沿)时取走零件。

这个类比不完全精确,但能帮你理解为什么 CPHA=1 时数据建立时间更充裕。实际上,CPHA=1 的时序对高速通信更友好,因为发送方有半个时钟周期的时间来稳定数据线。但 CPHA=0 的时序更简单,适合低速场景。

2.3 四种模式的波形对比

光看表格不够直观,我们直接看波形。假设要发送一个字节 0xA5(二进制 10100101),MSB 先发,四种模式的波形差异如下:

Mode 0(CPOL=0, CPHA=0):时钟空闲低电平,第一个上升沿采样。数据在下降沿改变,上升沿采样。所以你在示波器上会看到,数据线在时钟下降沿翻转,在上升沿中间位置稳定。

Mode 1(CPOL=0, CPHA=1):时钟空闲低电平,第二个边沿(下降沿)采样。数据在上升沿改变,下降沿采样。数据线的翻转发生在时钟上升沿,采样发生在下降沿。

Mode 2(CPOL=1, CPHA=0):时钟空闲高电平,第一个边沿(下降沿)采样。数据在上升沿改变,下降沿采样。和 Mode 0 刚好镜像。

Mode 3(CPOL=1, CPHA=1):时钟空闲高电平,第二个边沿(上升沿)采样。数据在下降沿改变,上升沿采样。和 Mode 1 刚好镜像。

如果你手头有示波器或者逻辑分析仪,建议实际抓一下四种模式的波形,对比看一遍,比看十遍文字都管用。没有仪器的话,可以用 MCU 的 SPI 外设配合 GPIO 模拟,用串口打印时序状态,也能大致验证。

3. 不同平台上的模式配置实战

3.1 STM32 HAL 库配置 SPI 模式

STM32 的 HAL 库把 SPI 模式配置封装得很简单,在SPI_HandleTypeDef结构体里有两个字段:CLKPolarityCLKPhase。配置 Mode 0 的代码如下:

hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_2LINES; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL = 0 hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA = 0 hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial = 10; HAL_SPI_Init(&hspi1);

如果要改成 Mode 3,只需要把CLKPolarity改成SPI_POLARITY_HIGHCLKPhase改成SPI_PHASE_2EDGE。Mode 1 是SPI_POLARITY_LOW+SPI_PHASE_2EDGE,Mode 2 是SPI_POLARITY_HIGH+SPI_PHASE_1EDGE

用 CubeMX 的话更简单,在 SPI 配置界面直接选 CPOL 和 CPHA 就行,生成的代码会自动填好这两个字段。但要注意,CubeMX 默认是 Mode 0,如果你用的从机是 Mode 3,记得手动改。

注意:STM32F1 系列的 SPI 在 Mode 0 和 Mode 3 下都能正常工作,但 Mode 1 和 Mode 2 在某些批次芯片上可能存在时序偏差,建议优先选 Mode 0 或 Mode 3。

3.2 FPGA 中手动实现 SPI 时序

FPGA 没有现成的 SPI 外设(除非用硬核),通常需要自己写状态机。这时候四种模式的差异就体现在状态机的状态转移上。以 Mode 0 为例,一个简单的发送状态机大概是这样的:

// Mode 0: CPOL=0, CPHA=0 // 空闲时 sclk = 0 // 第一个上升沿采样,下降沿改变数据 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin state <= IDLE; sclk <= 0; mosi <= 0; end else begin case (state) IDLE: begin sclk <= 0; if (start) begin state <= SEND_BIT; bit_cnt <= 7; shift_reg <= tx_data; end end SEND_BIT: begin // 下降沿改变数据 sclk <= 0; mosi <= shift_reg[7]; shift_reg <= {shift_reg[6:0], 1'b0}; state <= SAMPLE_BIT; end SAMPLE_BIT: begin // 上升沿采样 sclk <= 1; rx_data <= {rx_data[6:0], miso}; if (bit_cnt == 0) begin state <= IDLE; end else begin bit_cnt <= bit_cnt - 1; state <= SEND_BIT; end end endcase end end

如果是 Mode 3,只需要把空闲时的sclk改成 1,采样和改变的边沿对调即可。FPGA 的好处是你可以精确控制每个边沿的时刻,不受 MCU 外设限制。但坏处是代码量大,调试麻烦,尤其是高速通信时,时序约束要写得很仔细。

3.3 Linux spidev 下的模式设置

在 Linux 系统上,SPI 设备通常通过 spidev 接口访问。模式设置是在打开设备之后,通过ioctl调用SPI_IOC_WR_MODE来完成的:

int fd = open("/dev/spidev0.0", O_RDWR); uint8_t mode = SPI_MODE_0; // 或者 SPI_MODE_1, SPI_MODE_2, SPI_MODE_3 ioctl(fd, SPI_IOC_WR_MODE, &mode);

Linux 内核里定义的SPI_MODE_0SPI_MODE_3宏分别对应四种模式。如果你用的是设备树(Device Tree),也可以在设备树节点里直接指定spi-cpolspi-cpha属性:

&spi0 { status = "okay"; spidev@0 { compatible = "rohm,dh2228fv"; reg = <0>; spi-max-frequency = <1000000>; spi-cpol; // CPOL = 1 spi-cpha; // CPHA = 1 }; };

上面这个配置就是 Mode 3。如果只写spi-cpol不写spi-cpha,那就是 Mode 2。两个都不写就是 Mode 0。

提示:在 Linux 上调试 SPI 时,如果发现数据不对,先用spidev_test工具跑一下自发自收测试,确认模式配置正确后再接实际从机。

4. 模式选错会怎样:典型故障与排查

4.1 读出来全是 0x00 或 0xFF

这是最常见的症状。模式配错之后,从机根本没有正确采样到主机发送的命令,或者主机采样从机数据时采到了错误的位置,导致读回来的数据全是 0x00 或者 0xFF。这时候不要急着怀疑硬件连接,先检查模式配置。

排查步骤很简单:拿示波器或者逻辑分析仪抓 CS、SCLK、MOSI、MISO 四根线。先看 CS 拉低之后,SCLK 的第一个边沿是上升还是下降,判断 CPOL 是否匹配。再看 MOSI 上的数据是在哪个边沿稳定的,判断 CPHA 是否匹配。如果 MOSI 数据在第一个边沿还没稳定就被采样了,那就是 CPHA 配错了。

4.2 数据偶尔正确偶尔错误

这种间歇性故障最让人头疼。可能的原因有几个:一是模式虽然对了,但时钟频率太高,信号建立时间不够;二是 CPHA 配置在临界点上,某些批次芯片能容忍,某些不能;三是硬件上缺少上拉或下拉电阻,导致空闲电平时钟线不稳定。

我遇到过一个案例:用 STM32F103 通过 SPI 读取 MT6701 磁编码器的角度数据,Mode 1 配置下,低速时数据正常,一旦把 SPI 时钟提到 10MHz 以上,数据就开始跳变。后来用示波器看波形,发现 MISO 上的数据在采样边沿附近有振铃,导致采样不稳定。解决方法是在 MISO 线上串了一个 22 欧姆的电阻,振铃明显减弱,数据就稳定了。这个案例说明,模式配置只是第一步,信号完整性同样重要。

4.3 从机完全无响应

如果从机连 ID 都读不出来,CS 拉低后 MISO 一直是高阻态或者固定电平,那可能是模式差异太大,从机根本没有进入通信状态。这时候可以尝试把四种模式挨个试一遍,但更高效的方法是直接翻手册确认从机支持的模式。

有些芯片的手册写得很隐晦,比如只写“数据在时钟上升沿采样”,没有明确说 CPOL 和 CPHA。这时候你需要根据这句话推断:如果上升沿采样,且空闲电平是低,那就是 Mode 0;如果空闲电平是高,那就是 Mode 3。如果手册写了“数据在时钟下降沿采样”,空闲低就是 Mode 1,空闲高就是 Mode 2。

4.4 常见问题速查表

症状可能原因排查方法解决方案
读出全 0x00CPHA 配错抓 MOSI 波形看数据稳定时刻切换 CPHA
读出全 0xFFCPOL 配错抓 SCLK 空闲电平切换 CPOL
数据偶尔错误时钟太快或信号振铃降低时钟频率测试降频或加匹配电阻
从机无响应模式完全不匹配查手册确认支持模式换模式或换芯片
多字节传输错位CS 时序问题抓 CS 和 SCLK 关系调整 CS 建立/保持时间

5. 硬件片选与软件片选的模式影响

5.1 硬件片选下的时序特点

硬件片选(NSS)由 SPI 外设自动控制,CS 拉低和拉高的时刻由硬件决定,通常和时钟边沿对齐得很好。在 Mode 0 和 Mode 3 下,硬件片选的时序比较标准,不容易出问题。但在 Mode 1 和 Mode 2 下,有些 MCU 的硬件片选会在第一个时钟边沿之前就拉低,导致从机误判起始条件。

STM32 的硬件 NSS 在 Mode 1 下有一个已知问题:NSS 拉低后,第一个时钟边沿是上升沿,但 NSS 的拉低时刻可能和时钟边沿太近,导致从机来不及准备。解决方法通常是改用软件片选,手动控制 CS 的拉低和拉高时刻,在 CS 拉低之后加一个小延时再开始时钟。

5.2 软件片选的实际操作

软件片选就是用一个普通 GPIO 来控制 CS,SPI 外设只负责时钟和数据。这样做的好处是你可以精确控制 CS 的时序,比如在 CS 拉低之后延时 1 微秒再发时钟,在最后一个时钟之后延时 1 微秒再拉高 CS。很多对时序敏感的芯片(比如某些 ADC 和 Flash)都要求 CS 建立时间和保持时间,硬件片选可能满足不了,软件片选就灵活得多。

用 STM32 HAL 库实现软件片选的代码大概是这样:

// CS 拉低 HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); // 延时,满足建立时间 for (volatile int i = 0; i < 10; i++); // 发送数据 HAL_SPI_Transmit(&hspi1, tx_buf, len, 100); // 延时,满足保持时间 for (volatile int i = 0; i < 10; i++); // CS 拉高 HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);

这个延时循环的时长需要根据从机手册的时序要求来定。一般来说,CS 建立时间(tCSS)和保持时间(tCSH)在几十纳秒到几微秒之间,具体看芯片。

5.3 DMA 传输下的模式注意事项

用 DMA 传输 SPI 数据时,模式配置本身不变,但要注意 DMA 的传输完成中断和 CS 拉高的时机。如果 DMA 传输还没完成就拉高 CS,最后一个字节可能丢失。STM32 的 HAL 库提供了HAL_SPI_Transmit_DMAHAL_SPI_Receive_DMA,在 DMA 完成回调里拉高 CS 比较稳妥。

另外,DMA 传输时如果模式配错,症状和普通传输一样,但排查起来更麻烦,因为 DMA 是后台搬运,你看不到中间状态。建议先用普通传输调通模式,再切换到 DMA。

6. 高速通信下的模式选择与信号完整性

6.1 模式对最高时钟频率的影响

四种模式对最高时钟频率的容忍度是不一样的。一般来说,Mode 0 和 Mode 3 能跑到更高的频率,因为采样边沿和移出边沿的间隔比较标准。Mode 1 和 Mode 2 在某些芯片上高速时容易出现建立时间不足的问题。

以 STM32F103 为例,SPI1 最高可以跑到 18MHz(APB2 72MHz 分频 4),但在 Mode 1 下,实测超过 12MHz 就开始出现误码。Mode 0 和 Mode 3 可以稳定跑到 18MHz。这个差异在选型阶段就要考虑进去,如果从机只支持 Mode 1,那就要评估 MCU 能不能在目标频率下稳定工作。

6.2 信号完整性对模式匹配的干扰

高速 SPI 通信中,信号完整性是绕不开的话题。时钟线上的过冲、振铃、串扰都会影响采样时刻的电平判断。如果 CPHA 配置在临界点上,信号完整性问题会被放大,导致间歇性误码。

改善信号完整性的常用手段包括:在时钟线上串匹配电阻(通常 22 到 33 欧姆),缩短走线长度,增加地平面,避免时钟线和数据线平行走线。这些措施在 Mode 0 和 Mode 3 下效果比较明显,因为采样边沿和信号稳定区间的重叠度更高。

6.3 实测波形对比与经验值

我用逻辑分析仪抓过四种模式在 10MHz 下的波形,发现一个规律:Mode 0 和 Mode 3 的数据线在采样边沿附近最稳定,眼图张开度最大。Mode 1 和 Mode 2 的数据线在采样边沿附近有轻微抖动,眼图张开度小一些。这个差异在低速时不明显,但频率越高越显著。

所以我的经验是:如果从机同时支持 Mode 0 和 Mode 3,优先选 Mode 0;如果从机只支持 Mode 1 或 Mode 2,尽量把时钟频率降到 10MHz 以下,并做好信号完整性处理。

7. 几个容易踩的坑和实操心得

7.1 不要迷信“四个模式挨个试”

很多教程教你“SPI 不通就把四个模式挨个试一遍”,这个方法在低速、短距离、单从机的情况下确实能碰对,但效率极低,而且掩盖了真正的问题。正确的做法是先查手册确认从机支持的模式,再用示波器验证波形,最后才考虑换模式。

7.2 注意从机手册里的“隐藏模式”

有些芯片手册写的是“支持 Mode 0 和 Mode 3”,但实际上只支持其中一种,另一种是兼容性描述。比如某些国产 Flash 芯片,手册写支持 Mode 0/3,但实测 Mode 3 下写入操作会失败,只有 Mode 0 能正常读写。这种情况只能通过实测发现,所以新芯片上手时,建议把手册声称支持的模式都测一遍。

7.3 多从机共享 SPI 总线时的模式冲突

如果一条 SPI 总线上挂了多个从机,而它们的模式要求不一样,那就麻烦了。SPI 总线上的所有设备必须使用相同的模式,因为时钟极性和相位是总线级别的参数,不能针对每个从机单独配置。如果从机模式冲突,只能通过软件切换模式,或者在两次通信之间重新配置 SPI 外设。这种做法在 Linux 上比较常见,通过 spidev 的 ioctl 动态切换模式,但切换开销比较大,不适合高频通信。

7.4 逻辑分析仪的采样率要足够高

用逻辑分析仪抓 SPI 波形时,采样率至少要是 SPI 时钟频率的 10 倍以上。比如 SPI 跑 10MHz,逻辑分析仪采样率至少要 100MHz,否则抓到的波形会失真,边沿位置判断不准,反而误导排查方向。我早期用过一个 24MHz 采样率的逻辑分析仪抓 8MHz 的 SPI,波形看起来全是毛刺,换了 200MHz 的之后才发现实际波形很干净。

7.5 模式配置和 GPIO 复用要一起检查

STM32 的 SPI 引脚需要配置为复用推挽输出(AF_PP),如果配成了普通推挽输出,时钟和数据线可能出不来波形,或者波形幅度不对。这个问题在 CubeMX 里一般不会出现,因为 CubeMX 会自动配置复用功能,但手动写初始化代码时容易漏掉。检查方法是看 GPIO 的Mode字段是不是GPIO_MODE_AF_PP

8. 从模式选择延伸到协议理解

SPI 的四种模式看似只是两个参数的组合,但它背后反映的是同步串行通信的基本设计哲学:发送方和接收方必须对“什么时候数据有效”达成一致。CPOL 解决的是“空闲时线在哪”,CPHA 解决的是“第几个边沿算数”。这两个问题在任何同步通信协议里都存在,只是 SPI 把它显式地参数化了。

理解了这一点,你再去看 I2C、UART 甚至一些自定义的同步协议,会发现很多相似的设计思路。比如 I2C 的时钟极性是固定的(空闲高),但数据采样边沿也是固定的(上升沿),所以 I2C 不需要模式配置。UART 是异步通信,没有时钟线,靠波特率对齐,所以也不需要模式配置。SPI 之所以有四种模式,是因为它把时钟的灵活性留给了用户,代价就是配置复杂度增加。

我在实际项目中养成了一个习惯:拿到任何 SPI 从机,第一件事不是写代码,而是翻到时序图那一页,用笔把 CPOL 和 CPHA 标出来,然后对照 MCU 的配置选项确认一遍。这个习惯帮我省了很多调试时间,也避免了不少“明明代码没问题但就是不通”的困惑。

最后分享一个小技巧:如果你手头没有示波器,可以用 MCU 的一个 GPIO 在 SPI 传输的关键时刻翻转电平,然后用另一个 GPIO 读取从机的 MISO 状态,通过串口打印出来。虽然精度不高,但能大致判断数据是在哪个边沿被采样的。这个方法在紧急调试时救过我好几次。

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

RV1126B MIPI-CSI图像采集失败的三大隐性断点与实操修复

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

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

树莓派串口全解析:UART、SPI、I2C与GPIO配置实战指南

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

作者头像 李华
网站建设 2026/9/24 7:36:48

STM32F407 USB Host直连4G模块:从硬件设计到AT指令状态机实战

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

作者头像 李华