1. SPI接口核心原理与MPC8323E特性解析
串行外设接口,也就是我们常说的SPI,在嵌入式开发里就像电路板上的“万能胶”,但凡需要高速、实时地跟Flash、传感器或者显示屏这类外设打交道,基本都绕不开它。我接触过不少通信处理器,MPC8323E这款PowerQUICC II Pro系列芯片里的SPI模块设计得相当典型,功能也足够强大,从基本的四线全双工通信到专用的MIIMCOM以太网PHY管理都支持。今天我就结合手册和实际调板子的经验,把SPI从信号原理到MPC8323E的寄存器编程,再到缓冲描述符这套“组合拳”怎么用,给大家拆解明白。
SPI的本质是一种同步、全双工的串行通信协议。它的核心优势在于简单和高速。说它简单,是因为协议本身没有复杂的帧头和校验,通信完全由主设备发出的时钟信号(SPICLK)来同步节奏;说它高速,是因为在典型的嵌入式处理器上,SPI的时钟频率轻松能达到几十甚至上百兆赫兹,远高于UART或I2C。在MPC8323E上,SPI模块被集成在QUICC Engine子系统里,这意味着数据传输可以由独立的RISC处理器来驱动,大大减轻了主CPU的负担,对于需要持续进行大数据量交换的应用场景(比如通过SPI接口读取AD采样数据流)来说,这是个非常关键的设计。
它的工作模式离不开那四根关键信号线。SPIMOSI(Master Out Slave In)是主设备输出、从设备输入的数据线;SPIMISO(Master In Slave Out)则反过来,是从设备输出、主设备输入。SPICLK是主设备产生的同步时钟,而SPISEL(Slave Select)则是主设备用来选中特定从设备的片选信号,通常低电平有效。这里有个容易混淆的点:在MPC8323E中,当SPI配置为主模式时,SPISEL信号必须保持为高(即未被断言),如果外部设备错误地拉低了SPISEL,模块会报告一个“多主错误”(MME)。所以,做主设备时,片选信号通常需要用普通的GPIO口来模拟,而不是直接使用SPI模块的SPISEL引脚,这一点在硬件设计和软件初始化时务必留意。
SPI的通信时序灵活性很高,这主要通过配置时钟极性(CPOL, Clock Polarity)和时钟相位(CPHA, Clock Phase)来实现,也就是MPC8323E的SPMODE寄存器里的CI和CP位。CPOL决定了时钟空闲时的电平:CI=0时,SPICLK空闲时为低电平;CI=1时则为高电平。CPHA则决定了数据在时钟的哪个边沿被采样:CP=0时,数据在时钟的第一个边沿(即从空闲状态跳变到有效状态的边沿)被采样;CP=1时,数据在时钟的第二个边沿被采样。这四种组合(模式0到模式3)需要与从设备的数据手册要求严格匹配,否则通信根本无法建立。手册里的图21-9和21-10非常直观地展示了这两种相位下的数据与时钟关系,编程时对照着看能省不少调试时间。
注意:SPI通信前,务必确认主从设备的CPOL和CPHA设置一致。这是SPI调试中最常见的问题之一。很多传感器或存储芯片默认是模式0或模式3,如果没配对,你用逻辑分析仪看到的可能就是一堆乱码。
MPC8323E的SPI还有一个特殊模式叫MIIMCOM,这是为了管理以太网PHY而设计的。在这种模式下,SPIMOSI引脚会被复用作MDIO(管理数据输入输出),SPICLK则作为MDC(管理数据时钟),从而可以通过SPI接口去配置连接在同一MII/RMII总线上的多个PHY芯片的寄存器,无需额外的MDIO控制器,节省了引脚和逻辑资源。当SPMODE[MII]位被置1时,模块就进入了这个状态。
2. MPC8323E SPI寄存器详解与配置策略
要驱动MPC8323E的SPI,核心就是玩转那几个寄存器。手册里列出了SPMODE、SPIE、SPIM和SPCOM,它们各自管着一摊事。配置的顺序和细节直接决定了SPI是跑得飞快还是原地踏步,甚至通信是否正常。
2.1 SPMODE寄存器:设定通信的“宪法”
SPMODE寄存器是SPI模块的总控制开关。它的每一个位都至关重要。我们结合表21-4来逐项分析:
- EN (Bit 7): SPI使能位。这是总闸门,必须在配置好其他所有参数后才能置1。在EN=0时,SPI模块处于复位低功耗状态。一个重要的原则是:不要在EN=1的时候去修改SPMODE的其他位,否则可能导致不可预知的行为。
- M/S (Bit 6): 主从模式选择。0为从模式,1为主模式(或MIIMCOM模式)。这个选择决定了SPICLK和SPISEL引脚的方向和行为。
- REV (Bit 5): 数据位序反转。0表示先发送/接收字符的最低位(LSB),1表示先发送/接收最高位(MSB)。这个必须和通信对端匹配。大部分SPI设备是MSB在先,但也有一些例外(比如某些音频编解码器)。配反了会导致数据解析完全错误。
- DIV16 (Bit 4) & PM (Bits 12-15): 这两个位共同决定了作为主设备时的SPI波特率。波特率发生器(BRG)的输入时钟是
QUICC Engine clk / 2。当DIV16=0时,BRG的输入时钟就是QUICC Engine clk / 2;当DIV16=1时,输入时钟进一步被除以16,变为QUICC Engine clk / 32。然后,这个输入时钟再经过一个分频器,分频系数是4 * ([PM] + 1),其中PM是4位值(0-15)。因此,最终的SPICLK频率公式为:SPICLK = (QUICC Engine clk) / (2 * 4 * (PM+1))当 DIV16=0SPICLK = (QUICC Engine clk) / (32 * 4 * (PM+1))当 DIV16=1 例如,如果QUICC Engine时钟为266MHz,DIV16=0,PM=0(即分频系数为4),则SPICLK = 266MHz / (2*4) = 33.25MHz。这是理论最大值,实际要考虑从设备能承受的最高时钟频率。
- LEN (Bits 8-11): 字符长度。这个字段定义了每次传输的数据位宽,有效值是3(4位)到15(16位),或者0(代表32位)。这里有个极易出错的细节:LEN设置的是“长度减1”。也就是说,如果你想传输8位数据,应该设置LEN=7(因为7+1=8)。设置LEN=0xF表示传输16位数据。对于32位传输,则必须设置LEN=0。如果设置的值小于3(除了0),手册明确说了会导致“erratic behavior”( erratic behavior),即不可预测的错误行为。
- CI (Bit 2) & CP (Bit 3): 这就是前面提到的时钟极性和相位,共同构成SPI的四种模式。
- LOOP (Bit 1): 回环模式。置1后,发送器的输出会在内部直接连接到接收器的输入。这个功能主要用于模块自测试,在不连接外部硬件的情况下验证SPI控制器本身的发送和接收逻辑是否正常。
- CG (Bits 20-24): 时钟间隙。在主模式下,这个字段用于在两个连续传输的字符之间插入指定比特时间的空闲间隔。比如CG=5,就在两个字符间插入5个SPICLK周期的高电平(或低电平,取决于CI)间隙。这在驱动某些需要字符间准备时间的慢速外设时有用。在MIIMCOM模式下,它被用来配置发送帧前的空闲(IDLE)比特数,用于生成符合IEEE 802.3标准的MDIO帧前导码。
2.2 SPIE与SPIM寄存器:中断与事件管理
SPI模块通过事件驱动的方式与CPU协作。SPIE(事件寄存器)用来标志各种事件的发生,比如接收缓冲区满(RXB)、发送缓冲区空(TXB)、发送错误(TXE)、多主错误(MME)等。当事件发生时,对应的位会被硬件置1。
SPIM(掩码寄存器)则用于控制哪些事件可以触发中断。SPIM的位与SPIE一一对应。如果SPIM的某个位被置1,那么当SPIE中对应的事件位被置起时,就会向CPU产生一个中断请求;如果SPIM的位为0,则该事件被屏蔽,即使发生也不会触发中断,但事件标志位(SPIE)依然会被置起,可以通过轮询的方式读取。
一个标准的初始化流程是:先向SPIE写入0xFF来清除所有可能残留的旧事件标志,然后根据你的需求配置SPIM,使能你需要的中断源。例如,如果你希望每次收满一个缓冲区或发完一个缓冲区都让CPU来处理,那就应该使能RXB和TXB中断。
2.3 SPCOM寄存器:启动传输的扳机
SPCOM寄存器只有一个有效位:STR(Start Transmit, Bit 8)。对于主设备,当你设置好TxBD和RxBD,并配置好SPMODE后,向STR位写1,SPI模块就会立即开始数据传输。对于从设备,在使能后(SPMODE[EN]=1)写STR位为1,会让SPI模块进入就绪状态,等待主设备的SPISEL信号和时钟到来,然后开始收发数据。STR位会在一个QUICC Engine clk/2周期后被硬件自动清零。
实操心得:在启动主设备传输前,务必确保至少有一个有效的TxBD(R=1)和一个有效的RxBD(E=1)已经准备好。否则,SPI模块可能不会启动,或者启动后立即因错误而停止。对于从设备,除了准备好BD,还必须正确配置SPISEL引脚为输入,并等待主设备的选中信号。
3. 缓冲描述符(BD)机制与参数RAM配置
MPC8323E的SPI(以及QUICC Engine中的其他通信控制器)采用了一套非常高效的DMA机制来搬运数据,其核心就是缓冲描述符(Buffer Descriptor, BD)。这套机制把CPU从繁重的数据搬运工作中解放出来,你只需要告诉控制器数据在哪、怎么处理,剩下的就交给硬件了。
3.1 参数RAM:BD表的基石
在使能SPI模块之前,必须先初始化参数RAM中的几个关键字段。参数RAM是QUICC Engine内部的一块共享内存区域,用于存放各个控制器的配置和状态信息。
- RBASE 和 TBASE:这两个半字(16位)字段分别指向接收BD表和发送BD表在Multi-User RAM中的起始地址。这两个地址必须是8字节对齐的(即能被8整除)。通常我们会把RxBD表和TxBD表连续存放。例如,RBASE = 0x0000, TBASE = 0x0008,表示RxBD表从偏移0开始,TxBD表紧接着从偏移8开始(因为一个BD占8字节)。
- MRBLR:最大接收缓冲区长度的寄存器。它定义了QUICC Engine模块一次最多可以向一个接收缓冲区写入多少字节。用户分配的接收缓冲区大小绝对不能小于MRBLR的值。如果一次接收的数据超过了MRBLR,模块会关闭当前BD,并尝试使用下一个BD(如果可用)。对于发送缓冲区,长度是由TxBD中的Data Length字段单独指定的,不受MRBLR限制。手册特别指出,如果传输的字符长度超过8位(比如9-16位),MRBLR应该是偶数;如果是32位字符,MRBLR应该是4的倍数。
- Rx/Tx Bus Mode寄存器:这两个字节寄存器控制DMA访问外部内存时的总线事务属性。最重要的位是
BO(Byte Ordering,位3-4),它必须设置为10,表示大端字节序(Big-Endian),这是Power架构处理器的默认字节序。GBL位(位2)指示是否启用总线侦听(snooping),通常在不涉及多核缓存一致性的简单系统中可以设为0。
3.2 缓冲描述符(BD)详解
BD是一个8字节的数据结构,包含状态控制、数据长度和缓冲区指针。RxBD和TxBD的结构类似,但某些状态位的含义不同。
接收BD(RxBD)的关键字段:
| 位 | 名称 | 描述 |
|---|---|---|
| 0 | E (Empty) | 1:缓冲区为空,QUICC Engine可以写入数据(模块拥有此BD)。0:缓冲区已满或发生错误,CPU可以读取数据。CPU应在处理完数据后,将此位置1,将BD“归还”给模块以供下次使用。 |
| 2 | W (Wrap) | 1:表示这是BD表中的最后一个BD。当模块处理完此BD后,会自动跳回RBASE指向的第一个BD,形成环形队列。 |
| 3 | I (Interrupt) | 1:当此BD被关闭(填满)时,置位SPIE[RXB]事件标志,如果SPIM[RXB]已使能,则产生中断。 |
| 4 | L (Last) | 由模块设置。1:表示此缓冲区包含了接收消息的最后一个字符(例如,在从模式下SPISEL被取消断言,或在主模式下遇到了指定的结束条件)。 |
| 6 | CM (Continuous Mode) | 仅主模式有效。1:启用连续模式。当此BD被关闭后,模块不会自动清除E位,而是会循环使用同一个缓冲区,用新数据覆盖旧数据。这对于需要持续采样(如ADC扫描)且不需要CPU频繁干预的场景非常有用。 |
| 14 | OV (Overrun) | 1:表示接收时发生溢出错误。新数据到来时,旧数据还未被CPU读取,导致新数据被丢弃。 |
| 15 | ME (Multimaster Error) | 1:当SPI处于主模式时,外部设备拉低了SPISEL信号,触发了多主错误。 |
发送BD(TxBD)的关键字段:
| 位 | 名称 | 描述 |
|---|---|---|
| 0 | R (Ready) | 1:缓冲区已准备好发送(CPU已填充数据并设置好长度)。模块发送完此缓冲区后,会清除此位(除非CM=1)。0:缓冲区未就绪或已发送完毕,CPU可以修改此BD。 |
| 2 | W (Wrap) | 1:表示这是TxBD表中的最后一个BD。 |
| 3 | I (Interrupt) | 1:当此BD被处理完毕(发送完成或出错)时,置位SPIE[TXB]或SPIE[TXE]事件标志,如果相应中断被使能,则产生中断。 |
| 4 | L (Last) | 由CPU设置。1:表示此缓冲区包含要发送消息的最后一个字符。对于主设备,发送完这个缓冲区后,传输停止。在MIIMCOM模式下,此位应仅在最后一个缓冲区字是MII读命令时才设置。 |
| 6 | CM (Continuous Mode) | 仅主模式有效。1:启用连续发送模式。发送完此BD后,R位不会被清除,缓冲区会被自动重复发送。这在需要产生周期性的测试信号时很有用。 |
| 14 | UN (Underrun) | 仅从模式有效。1:表示发送时发生下溢错误。主设备的时钟到来时,从设备的数据还未准备好。 |
| 15 | ME (Multimaster Error) | 1:发送过程中发生了多主错误。 |
数据长度(Data Length)字段:对于TxBD,这个字段由CPU在发送前写入,告诉模块这个缓冲区里有多少字节需要发送。这里有一个极其重要的细节:这个长度是以字节为单位的。但是,如果SPI配置的字符长度(LEN)超过8位,那么数据在内存中是以半字(16位)或字(32位)的形式存放的。例如,要发送3个12位的数据,由于12位超过一个字节,数据在内存中需要占用3个半字(6个字节)。因此,TxBD的Data Length字段应该设置为6,而不是3。手册明确给出了计算规则:如果字符长度超过8位,数据长度应为偶数;如果字符长度是32位,数据长度应为4的倍数。
缓冲区指针(Buffer Pointer)字段:这是一个32位的指针,指向存放实际数据的内存地址。这个地址可以是内部Multi-User RAM的地址,也可以是外部内存的地址。对于RxBD,这个指针必须是偶数(2字节对齐)。对于TxBD,如果字符长度不超过8位,指针可以是任意地址;如果超过8位,则必须偶数对齐。
4. SPI主从模式编程实践与调试实录
理论讲得再多,不如动手调一遍。我们分别以手册中的主从模式示例为基础,补充一些实际开发中必须考虑的细节和步骤。
4.1 主模式初始化与数据传输流程
假设我们需要将MPC8323E的SPI1配置为主设备,以8位数据长度、模式0(CPOL=0, CPHA=0)、最高速度与一个SPI Flash芯片通信。
步骤1:��件与引脚配置首先,需要通过处理器内部的I/O控制器,将对应引脚的功能复用为SPI1。对于MPC8323E,需要查阅其引脚复用表,找到SPI1_MISO、SPI1_MOSI、SPI1_CLK对应的引脚,并将其配置为SPI功能(通常是Primary功能)。对于片选信号SS,我们必须使用一个普通的GPIO引脚来手动控制,因为主模式下SPI模块的SPISEL是输入信号,不能用于驱动从设备。将这个GPIO配置为输出,并初始化为高电平(无效状态)。
步骤2:参数RAM与BD表初始化在内存中(通常是内部SRAM或外部DDR)规划好BD表和缓冲区的空间。假设我们在Multi-User RAM的起始位置进行分配:
- RxBD表起始地址 (RBASE): 0x0000
- TxBD表起始地址 (TBASE): 0x0008 (因为一个BD占8字节)
- 接收缓冲区地址: 0x0000_1000
- 发送缓冲区地址: 0x0000_2000 我们需要先初始化参数RAM:
// 假设已获得参数RAM基地址 spi_param_base *(volatile uint16_t *)(spi_param_base + 0x00) = 0x0000; // RBASE *(volatile uint16_t *)(spi_param_base + 0x02) = 0x0008; // TBASE *(volatile uint8_t *)(spi_param_base + 0x04) = 0x48; // Rx Bus Mode: GBL=0, BO=10 (Big Endian) *(volatile uint8_t *)(spi_param_base + 0x05) = 0x48; // Tx Bus Mode: 同上 *(volatile uint16_t *)(spi_param_base + 0x06) = 0x0010; // MRBLR = 16字节步骤3:初始化BD接着,初始化第一个接收BD和发送BD。
// 初始化RxBD @ 0x0000 // Status: E=1 (空,等待接收), W=0 (非最后一个BD), I=1 (完成后中断), L=0, CM=0, 其他位0 // 即 0b1001 0000 0000 0000 = 0x9000, 但手册示例给的是0xB000,包含了保留位,我们按手册来。 *(volatile uint16_t *)(0x0000) = 0xB000; // Status and Control *(volatile uint16_t *)(0x0002) = 0x0000; // Data Length (初始为0,由模块填写) *(volatile uint32_t *)(0x0004) = 0x00001000; // Buffer Pointer (32位) // 初始化TxBD @ 0x0008 // Status: R=1 (就绪), W=0, I=1 (完成后中断), L=1 (这是要发送的最后一个缓冲区), CM=0 // 即 0b1011 1000 0000 0000 = 0xB800 *(volatile uint16_t *)(0x0008) = 0xB800; // Status and Control *(volatile uint16_t *)(0x000A) = 0x0005; // Data Length: 发送5个字节 *(volatile uint32_t *)(0x000C) = 0x00002000; // Buffer Pointer // 在地址0x00002000处准备好5字节数据,例如 {0x03, 0x00, 0x00, 0x00, 0x00} (Flash读命令)步骤4:执行初始化命令并配置寄存器通过QUICC Engine命令寄存器(CECR)执行INIT RX AND TX PARAMETERS命令,这将重置SPI控制器内部的Tx/Rx状态机。
// 假设CECR地址为cecr_base *(volatile uint32_t *)cecr_base = CMD_INIT_RX_TX_PARAM; // 具体命令值需查手册然后配置SPI事件和中断寄存器:
// 清除所有事件标志 SPI1_SPIE = 0x00FF; // 使能TXB, RXB, TXE, MME中断 (根据SPIM位定义,使能位18,19,22,23) SPI1_SPIM = 0x0370; // 0b0011 0111 0000最后,配置SPMODE寄存器并启动传输:
// 计算SPMODE值: // EN=1, M/S=1 (主), REV=1 (MSB先,常见), DIV16=0 (高速), CP=0, CI=0 (模式0) // LEN=7 (8位数据), PM=0 (最高分频,即最快速度) // 其他位默认0。 即 0b0000 0000 0011 0111 = 0x0037? 不对,需要按位对齐。 // 实际计算:Bit15-12 PM=0, Bit11-8 LEN=7, Bit7 EN=1, Bit6 M/S=1, Bit5 REV=1, Bit4 DIV16=0, Bit3 CP=0, Bit2 CI=0, Bit1 LOOP=0, Bit0 EM=0 // 合并起来: PM=0, LEN=7, EN=1, M/S=1, REV=1, DIV16=0, CP=0, CI=0, LOOP=0, EM=0 // 对应32位值: 0x0000 0370 (手册示例值) SPI1_SPMODE = 0x00000370; // 拉低GPIO片选信号,选中从设备 GPIO_SET_SS_LOW(); // 启动传输 SPI1_SPCOM = 0x0100; // 设置STR位为1传输启动后,SPI模块会自动从0x00002000地址读取5字节数据发出,同时将接收到的数据存入0x00001000。发送完成后,TxBD的R位被清零,SPIE[TXB]事件置位,触发中断(如果使能)。接收完成后,RxBD的E位被清零,SPIE[RXB]事件置位。
4.2 从模式配置要点与差异
从模式的配置流程与主模式高度相似,主要区别在于:
- SPMODE[M/S]位:必须设置为0(从模式)。
- SPISEL引脚:必须配置为输入功能,并启用内部上拉电阻(如果支持且外部无上拉),以避免悬空。主设备通过控制这个引脚的低电平来选中本从设备。
- 波特率设置:在从模式下,SPI模块的波特率发生器(BRG)不工作,SPICLK由外部主设备提供。因此,SPMODE中的DIV16和PM位在从模式下被忽略。
- 启动时机:从设备需要在被选中前就做好准备。因此,在配置好BD和SPMODE(M/S=0)后,就需要写SPCOM[STR]=1,让SPI模块进入“待命”状态。一旦主设备拉低SPISEL并开始提供SPICLK,传输立即开始。
- 传输控制:从设备的传输完全由主设备主导。传输的长度由主设备发出的时钟数量决定。如果主设备发送的数据量小于TxBD中设定的数据长度,那么TxBD可能不会关闭(R位仍为1)。如果主设备发送的数据超过了RxBD缓冲区大小(MRBLR),且没有新的空RxBD可用,就会发生溢出(OV错误)。
手册中的从模式示例将SPMODE设置为0x0170。对比主模式的0x0370,区别就在于Bit 6 (M/S) 从1变成了0。
4.3 常见问题排查与调试技巧
在实际调试中,SPI通信失败是家常便饭。以下是一些快速定位问题的思路:
完全没有波形:
- 检查引脚复用:这是最容易被忽略的一步。用示波器或逻辑分析仪检查SPIMOSI、SPICLK、SPISEL(从模式)或GPIO片选(主模式)引脚,确认它们已经正确输出或响应。
- 检查SPI使能:确认SPMODE[EN]位已设置为1。
- 检查主模式启动:主模式下,确认已向SPCOM[STR]位写1。同时检查TxBD的R位是否已设置为1(缓冲区就绪)。
- 检查从模式待命:从模式下,确认在SPISEL被拉低前,已执行SPCOM[STR]=1。
有时钟和数据,但数据不对:
- 检查时钟极性和相位(CPOL & CPHA):这是最高频的错误。用逻辑分析仪捕获波形,对照从设备的数据手册,检查空闲电平、采样边沿是否匹配。MPC8323E的CI和CP位必须与从设备严格一致。
- 检查数据位序(MSB/LSB):确认SPMODE[REV]位设置是否正确。大部分设备是MSB先传。
- 检查字符长度(LEN):确认SPMODE[LEN]设置的数据位宽与从设备期望的一致。例如,与一个12位ADC通信,LEN应设置为11(12-1)。
- 检查字节序和内存对齐:如果数据位宽超过8位,确保TxBD的Data Length字段是按字节计算的长度(字符数*(位宽/8)),且缓冲区指针是偶数对齐的。同时确认处理器和SPI模块的字节序(Big-Endian)设置正确。
只能发送一次,后续传输失败:
- 检查BD回收:传输完成后,SPI模块会关闭BD(清除RxBD的E位或TxBD的R位)。CPU必须在中断服务程序或轮询程序中,重新初始化这些BD(将E或R位置1),并将其“归还”给SPI模块,否则模块没有可用的BD,后续传输就会停止。
- 检查BD表环(W位):如果你的BD表有多个BD,确保最后一个BD的W位被设置为1,这样模块在处理完最后一个BD后会回到第一个BD,形成环形队列。
中断不触发:
- 检查SPIE和SPIM:首先读取SPIE寄存器,查看期望的事件标志位是否已经置1。如果已置1但没中断,检查SPIM中对应的中断掩码位是否已使能。另外,确认处理器的全局中断以及SPI模块对应的中断线已经开启。
- 清除事件标志:中断服务程序中,在处理完事件后,必须通过向SPIE的对应位写1来清除事件标志。否则,该中断会持续触发。
使用逻辑分析仪:一个支持SPI协议解码的逻辑分析仪是调试SPI的利器。它能直观地显示时钟、数据、片选波形,并自动解码出十六进制或二进制数据,能快速帮你定位时序、数据内容、帧长度等问题。调试时,首先用逻辑分析仪确认硬件引脚上有正确的波形,这是隔离软件问题的基础。
踩坑记录:曾经调试一个SPI Flash,发现读取的ID号总是错一位。最后发现是LEN设置问题。Flash数据手册要求是8位传输,但我错误地认为命令是8位,后续数据是16位,于是将LEN设为了15。结果SPI模块试图以16位为单位去解释所有数据,导致字节边界全部错位。将LEN改为7(8位模式)后问题立刻解决。切记,SPI的字符长度是针对整个传输过程的,不能在一次传输中动态改变。