3、SPI数据帧格式:MSB/LSB先行、数据位宽与连续传输

SPI通信,说白了就是主从设备之间互相倒腾数据。但怎么倒腾、先倒腾哪一位、一次倒腾多少位,这些细节决定了你的驱动能不能正常工作。我见过不少工程师,时序调了半天,最后发现是数据帧格式配错了——这种坑,踩一次就记住了。

3.1 MSB先行 vs LSB先行:谁先谁后?

SPI协议本身没规定数据必须从哪一位开始传。但大多数设备默认MSB先行(Most Significant Bit First),也就是先传最高位。比如你要传0x53(二进制0101 0011),MSB模式下先传bit7(0),再传bit6(1),以此类推。

LSB先行则反过来,先传bit0(1),再传bit1(1)……最后传bit7(0)。

关键点:主设备和从设备的位序必须一致。不一致的话,你发0x53,对方收到0xCA——完全反了。

我在项目中遇到过一款温湿度传感器,数据手册里写着“Data is transmitted MSB first”,结果我按LSB配了,读回来的温度值一直在乱跳。排查了两天才发现是位序问题。嗯,从那以后我拿到新芯片第一件事就是确认位序配置。

MCU中的配置方式

以STM32为例,SPI_CR1寄存器里的LSBFIRST位控制这个:

// MSB先行(默认)
SPI1->CR1 &= ~SPI_CR1_LSBFIRST;

// LSB先行
SPI1->CR1 |= SPI_CR1_LSBFIRST;

其他MCU也大同小异,无非是寄存器名不同。配置前一定去翻数据手册的SPI章节,别凭经验猜。

位序模式 发送0x53(0101 0011) 典型应用
MSB先行 0 → 1 → 0 → 1 → 0 → 0 → 1 → 1 大多数SPI设备、Flash、ADC
LSB先行 1 → 1 → 0 → 0 → 1 → 0 → 1 → 0 部分LCD驱动、某些传感器

3.2 数据位宽:8位、16位还是32位?

SPI帧的数据位宽决定了每次传输交换多少位数据。常见的有8位、16位,部分MCU支持32位甚至可编程位宽。

你想想看,如果从设备寄存器是16位的,你用8位帧去读,就得读两次再拼起来。麻烦不说,还容易出错。

我的习惯:优先匹配从设备的寄存器位宽。如果从设备寄存器是8位的,就用8位帧;是16位的,就用16位帧。实在不行再用8位帧软件拼。

配置示例(STM32)

// 8位数据帧
SPI1->CR1 &= ~SPI_CR1_DFF;   // DFF=0

// 16位数据帧
SPI1->CR1 |= SPI_CR1_DFF;    // DFF=1

对于支持32位的MCU(比如NXP的LPC系列),配置方式类似,但寄存器名可能叫DSS(Data Size Select)。

注意:切换位宽时,一定要确保SPI处于禁用状态(SPE=0)。我曾经在SPI使能状态下改DFF位,结果下一帧数据直接错位,调试器都抓不到正确波形。

3.3 连续传输与片选控制

连续传输,也叫多字节传输,指的是在片选信号持续有效的情况下,连续发送多个数据帧。这在访问Flash、SD卡等需要批量读写的场景下特别常见。

片选信号(CS/SS)的作用是告诉从设备:“我要跟你说话了”。正常情况下,每次传输开始前拉低CS,传输结束后拉高CS。

但在连续传输模式下,CS在整个传输过程中保持低电平,直到所有数据传完才释放。

为什么需要连续传输?

  • 效率高:省去了每次传输都重新拉低CS的开销
  • 原子操作:某些命令(如Flash的Page Program)要求CS在整个命令+地址+数据期间不能拉高
  • 时序要求:部分从设备在CS拉高后会立即复位内部状态机

MCU中的实现方式

大多数MCU的SPI外设支持连续传输。以STM32为例,只要在TXE(发送缓冲区空)标志置位时及时写入新数据,SPI就会自动连续发送,CS由硬件自动控制(如果开启了硬件CS管理)。

// 连续发送3个字节
SPI1->CR1 |= SPI_CR1_SPE;    // 使能SPI

// 发送第一个字节
while(!(SPI1->SR & SPI_SR_TXE));
SPI1->DR = 0x01;

// 发送第二个字节(CS保持低)
while(!(SPI1->SR & SPI_SR_TXE));
SPI1->DR = 0x02;

// 发送第三个字节
while(!(SPI1->SR & SPI_SR_TXE));
SPI1->DR = 0x03;

// 等待传输完成
while(SPI1->SR & SPI_SR_BSY);

// 此时硬件自动拉高CS(如果配置了硬件CS)
避坑指南:我曾经用软件控制CS做连续传输,结果在两次写入之间CS被意外拉高了——因为中断服务程序插进来改了GPIO状态。从那以后,我改用硬件CS控制,省心多了。

3.4 知识结构图

下面这张图把SPI数据帧格式的核心要素串起来了,方便你对照理解:

SPI 数据帧格式核心要素 SPI 数据帧格式 位序:MSB / LSB 数据位宽:8/16/32 传输模式:单次/连续 MSB 先行 LSB 先行 8 位 16 位 32 位 单次传输 连续传输 片选控制 硬件CS / 软件CS 配置前务必确认从设备数据手册中的位序、位宽和CS时序要求 三者任一不匹配,通信都会失败

3.5 常见配置组合速查表

实际项目中,不同外设对数据帧格式的要求差异很大。我整理了一张速查表,方便你快速对照:

外设类型 位序 位宽 传输模式 CS控制
W25Qxx Flash MSB 8位 连续 硬件CS
ADXL345 加速度计 MSB 8位 单次/连续 软件CS
MAX7219 LED驱动 MSB 16位 单次 软件CS
SD卡(SPI模式) MSB 8位 连续 硬件CS
某些LCD驱动 LSB 8位/16位 单次 软件CS
个人建议:写驱动前,先把数据手册里关于数据帧格式的章节截图存下来。调试时对照着看,能省不少时间。

好了,SPI数据帧格式的核心内容就这些。记住三个关键点:位序要匹配、位宽要对齐、CS控制要符合从设备要求。这三样配对了,SPI通信就成功了一大半。