一、SPI 核心基础

1.1 什么是 SPI

  • SPI(Serial Peripheral Interface):同步串行外设接口,一种高速、全双工的串行通信协议。

  • 架构:采用「1 个主机 + 多个从机」的主从架构,主机主导通信,从机被动响应。

  • 通信本质:同步时钟驱动,核心特点是「发即收」,收发同时进行,无单独的读/写指令。

1.2 SPI 硬件引脚(4根核心线)

SPI 标准通信需4根核心引脚,不同厂商命名可能略有差异,但功能一致:

  • SCLK(Serial Clock):串行时钟线,由「主机单独产生」,用于同步收发双方的数据传输节奏,决定通信速率。

  • MOSI(Master Out Slave In):主机输出、从机输入线,主机通过该线向从机发送数据。

  • MISO(Master In Slave Out):主机输入、从机输出线,从机通过该线向主机回传数据。

  • CS/SS/NSS(Chip Select/Slave Select):片选信号线,低电平有效(默认),主机通过拉低对应从机的CS引脚,选中该从机进行通信;通信结束后拉高,释放总线,避免多从机干扰。

1.3 SPI 核心特点

  • 全双工:收发数据同时进行,无需等待一方完成再执行另一方,通信效率高。

  • 同步通信:数据传输严格由SCLK时钟边沿控制,时序稳定,适合高速传输。

  • 主从模式:在绝大多数工程场景中,均工作在「主机模式」(产生时钟、发起通信)。

  • 多从机支持:通过多个CS引脚扩展,同一时刻只能有一个从机被选中。

  • 高速性:通信速率通常为几MHz~几十MHz,远高于I2C协议。

  • 无应答机制:数据传输无确认环节,可靠性略低于I2C,但胜在速度快、配置简单。

1.4 时钟极性与相位(CPOL/CPHA)—— SPI配置核心

SPI 一共有4种工作模式,完全由「CPOL(时钟极性)」和「CPHA(时钟相位)」两个参数决定,这两个参数必须与从机设备的配置完全一致,否则会出现通信失败、数据乱码等问题,是SPI配置的重中之重。

1.4.1 CPOL(时钟极性)

定义SCLK时钟线在「空闲状态」(无数据传输时)的电平:

  • CPOL = 0:SCLK空闲时为低电平(最常用)。

  • CPOL = 1:SCLK空闲时为高电平(少数特殊外设使用)。

1.4.2 CPHA(时钟相位)

定义数据的「采样边沿」(即从机/主机在SCLK的哪个边沿读取数据):

  • CPHA = 0:在SCLK的「第一个边沿」(上升沿或下降沿,由CPOL决定)采样数据。

  • CPHA = 1:在SCLK的「第二个边沿」采样数据。

1.4.3 4种SPI通信模式对照表

SPI 模式

CPOL

CPHA

SCLK空闲电平

数据采样边沿

常用设备

Mode 0(常用)

0

0

低电平

上升沿

SPI Flash(W25Q系列)、LCD、大多数传感器

Mode 1

0

1

低电平

下降沿

部分TFT屏、专用通信芯片

Mode 2

1

0

高电平

下降沿

少数音频芯片、ADC

Mode 3

1

1

高电平

上升沿

部分高速外设、工业芯片

1.5 SPI 数据传输顺序

  • MSB first(高位先行):数据传输时,先发送/接收最高位(如0x12,先传0x1,再传0x2),是绝大多数SPI设备的默认配置。

  • LSB first(低位先行):数据传输时,先发送/接收最低位,仅少数特殊芯片支持(如部分传感器),需手动配置。

1.6 SPI 数据位宽

  • 常用位宽:8bit(最普遍,适配绝大多数SPI外设,如Flash、LCD、传感器)。

  • 扩展位宽:部分外设支持16bit(如部分ADC、DAC),TI C2000系列DSP支持3~16位可配置,按需调整。

1.7 NSS/CS 管理方式

1.7.1 软件 NSS(推荐)

  • 原理:将CS引脚配置为普通GPIO,通过代码手动拉低(选中从机)、拉高(释放从机)。

  • 优势:灵活度高,支持多从机扩展,适配绝大多数工程场景,是实际开发中最常用的方式。

1.7.2 硬件 NSS

  • 原理:利用SPI外设自带的NSS引脚,由SPI模块自动控制拉低/拉高,无需手动操作。

  • 劣势:仅适合单从机场景,灵活性差,工程中极少使用。

1.8 SPI 通信本质:发即收

SPI 协议没有“只发不收”或“只收不发”的操作,核心本质是「发即收」:

  • 主机发送1字节数据的同时,必然会从MISO线接收1字节数据(即使不需要接收,也会自动接收)。

  • 若想从从机读取数据,必须向从机发送一个「虚拟字节」(通常为0x00或0xFF),用于产生SCLK时钟,触发从机输出数据。

  • 结论:SPI的读函数和写函数是同一个,本质都是“发送+接收”的组合操作。


二、F28035 SPI 初始化标准流程

TI C2000系列DSP(如F28035、F28335)的SPI初始化,遵循以下6个步骤,顺序错误会导致SPI无法正常工作,记牢即可:

  1. GPIO 复用配置:将普通IO引脚复用为SPI功能引脚(SIMO、SOMI、SCLK),部分引脚需配置上拉、异步输入。

  2. 时钟使能:SPI的时钟由LSPCLK(低速外设时钟)提供,C2000默认已使能,无需额外配置,只需关注后续波特率分频即可。

  3. SPI 软件复位:先将SPI模块复位(清空所有配置),再进行参数配置,避免残留配置影响功能。

  4. 配置核心参数:包括主从模式、CPOL/CPHA(工作模式)、波特率分频、数据位宽、MSB/LSB、NSS管理方式。

  5. 退出软件复位,使能SPI:所有参数配置完成后,退出复位状态,SPI模块开始工作。

  6. 调试与优先级配置:配置调试模式(断点不影响通信)、片选引脚极性等,提升工程实用性。


三、SPI IO 初始化(GPIO复用,硬件基础)

SPI IO初始化的核心是“将普通GPIO复用为SPI功能”,以下代码可直接复制使用,适配SPIA/SPIB双模块,注释详细,无需修改核心逻辑,仅需根据硬件修改引脚编号即可。

3.1 代码

void SPI_IOinit()
{
EALLOW;
    // FLASH、LCD 相关控制脚配置为普通 GPIO
    EECSMUX = 0;    EECSDIR = 1;
    LCD_BKMUX = 0;  LCD_BKDIR = 1;
    LCD_RSTMUX = 0; LCD_RSTDIR = 1;
    LCD_CDMUX = 0;  LCD_CDDIR = 1;
    LCD_CSMUX = 0;  LCD_CSDIR = 1;

#ifdef TFT_SPIA
    // 上拉使能
    GpioCtrlRegs.GPAPUD.bit.GPIO16 = 0;
    GpioCtrlRegs.GPAPUD.bit.GPIO17 = 0;
    GpioCtrlRegs.GPAPUD.bit.GPIO18 = 0;

    // 异步输入模式
    GpioCtrlRegs.GPAQSEL2.bit.GPIO16 = 3;
    GpioCtrlRegs.GPAQSEL2.bit.GPIO17 = 3;
    GpioCtrlRegs.GPAQSEL2.bit.GPIO18 = 3;

    // 复用为 SPIA
    GpioCtrlRegs.GPAMUX2.bit.GPIO16 = 1;
    GpioCtrlRegs.GPAMUX2.bit.GPIO17 = 1;
    GpioCtrlRegs.GPAMUX2.bit.GPIO18 = 1;
#else
    // SPIB 对应 GPIO 配置
#endif

    CE_High();  // FLASH 片选拉高,避免总线冲突
EDIS;
}

3.2SPI FIFO 初始化(提升速度)

void spi_fifo_init()
{
    PtrSPI->SPIFFTX.bit.SPIRST = 1;
    PtrSPI->SPIFFTX.bit.SPIFFENA = 1;     // 使能 FIFO
    PtrSPI->SPIFFTX.bit.TXFFIENA = 0;     // 关闭发送中断
    PtrSPI->SPIFFRX.bit.RXFFIENA = 0;     // 关闭接收中断
    PtrSPI->SPIFFRX.bit.RXFFIL = 4;       // FIFO 触发深度
    PtrSPI->SPIFFCT.all = 0x0;
}
  • 开启 FIFO 可以大幅提高刷屏速度
  • 不使用中断,查询方式更简单、稳定、易调试。

3.3 配置要点

  • EALLOW/EDIS必须成对使用:TI C2000的GPIO、SPI等控制寄存器是受保护的,必须先解锁(EALLOW)才能修改,配置完成后重新锁定(EDIS)。

  • 上拉电阻必须开启:SPI通信属于高速串行传输,开启内部上拉可以增强信号抗干扰能力,避免因信号抖动导致通信错误。

  • 异步输入配置:将SPI引脚配置为异步输入(GPAQSELx=3),可以消除同步延迟,保证高速传输时的数据采样准确。

  • 复用编号必须查数据手册:不同DSP型号、不同SPI模块(SPIA/SPIB)的GPIO复用编号不同(如SPIA复用1,SPIB复用3),绝对不能写错,否则引脚无法正常工作。

  • CS引脚初始化:初始化时必须将CS引脚拉高,避免总线冲突,后续通信时再手动拉低选中对应从机。


四、SPI 寄存器初始化

SPI核心参数均通过寄存器配置,以下代码对应TI C2000的SPI寄存器操作,配置为「主机模式、8bit数据、Mode 1、波特率LSPCLK/4」,注释详细,逐行解析核心作用,可直接复制使用。

4.1 代码

// SPI模块指针(根据使用的模块修改:SpiaRegs=SPIA,SPIBRegs=SPIB)
#define PtrSPI  SpiaRegs

/**
 * @brief  SPI核心参数初始化(主机模式)
 * @note   配置:8bit数据、CPOL=0、CPHA=1(Mode1)、波特率LSPCLK/4、软件NSS
 */
void spi_init()
{
    CE_High();  // 初始化前拉高CS,避免总线冲突

    // 1. SPI软件复位(必须最先执行,清空原有配置)
    PtrSPI->SPICCR.bit.SPISWRESET = 0; // 拉低复位,开启复位状态

    // 2. 配置数据位宽:8bit(SPICHAR=7 → 7+1=8bit)
    PtrSPI->SPICCR.bit.SPICHAR = 7;    // TI C2000:SPICHAR=n → 数据位宽=n+1

    // 3. 配置CPOL=0(SCLK空闲时为低电平,对应Mode1/Mode0)
    PtrSPI->SPICCR.bit.CLKPOLARITY = 0 ; 

    // 4. 配置CPHA=1(下降沿采样数据,对应Mode1)
    PtrSPI->SPICTL.bit.CLK_PHASE = 1;   // 数据在SCLK上升沿输出,下降沿采样

    // 5. 配置主从模式:1=主机模式(DSP产生SCLK,发起通信)
    PtrSPI->SPICTL.bit.MASTER_SLAVE  = 1;

    // 6. 配置波特率:SPIBRR=3 → 波特率=LSPCLK/(SPIBRR+1)=LSPCLK/4
    PtrSPI->SPIBRR = 3;             // SPIBRR≥3时,波特率公式:LSPCLK/(SPIBRR+1)

    // 7. 退出软件复位,使能SPI模块(所有参数配置完成后执行)
    PtrSPI->SPICCR.bit.SPISWRESET = 1; 

    // 8. 调试与优先级配置(实用配置,避免调试时中断通信)
    PtrSPI->SPIPRI.bit.FREE = 1;   // 断点调试时,SPI继续传输,不中断
    PtrSPI->SPIPRI.bit.STEINV = 0; // SPISTE引脚低电平有效(硬件NSS用,软件NSS不影响)
}

4.2 关键配置总结

  • 软件复位顺序:必须先执行「SPISWRESET=0」(复位),配置完所有参数后,再执行「SPISWRESET=1」(退出复位),顺序绝对不能错。

  • 数据位宽配置:TI C2000的SPICHAR寄存器,值为n时,数据位宽为n+1,常用「SPICHAR=7」(8bit)。

  • 工作模式配置:代码中「CPOL=0,CPHA=1」,对应SPI Mode 1,需根据从机设备的数据手册修改,确保与从机一致。

  • 主从模式:「MASTER_SLAVE=1」为主体模式,是工程中最常用的模式;若需配置为从机,改为0即可。

  • 波特率计算(TI C2000专属):

    • 当SPIBRR ≥ 3时,波特率 = LSPCLK / (SPIBRR + 1)

    • 当SPIBRR < 3时,波特率 = LSPCLK / 4

    • 注意:LSPCLK通常为系统时钟SYSCLK的二分频,波特率不能超过从机的最大支持时钟。

  • 调试配置:「FREE=1」是实用配置,断点调试时SPI不会中断传输,避免调试时通信异常。


五、SPI 单字节收发函数(体现“发即收”本质)

结合SPI“发即收”的核心特点,读写函数是同一个,以下代码实现SPI单字节收发,适配8bit数据传输,可直接调用。

5.1 代码

/**
 * @brief  SPI单字节收发函数(发即收)
 * @param  a: 要发送的字节数据
 * @retval 接收从机返回的字节数据
 */
uint8_t Send_Byte(uint16_t a)
{
    Uint16 rdata;  // 接收数据(C2000 SPI接收寄存器为16bit)

    PtrSPI->SPICTL.bit.TALK  = 1;                // 使能SPI发送通道,允许主机输出数据
    PtrSPI->SPITXBUF = (a << 8) & 0xff00;        // 发送数据:8bit数据左移8位,占用SPITXBUF高8位
    while(PtrSPI->SPIFFRX.bit.RXFFST >= 4 ) { } 	// 等待接收完成(FIFO模式,等待FIFO清空)
    // while(SpiaRegs.SPISTS.bit.INT_FLAG == 0) {} // 备选:等待中断标志位(非FIFO模式,更常用)
    rdata = PtrSPI->SPIRXBUF;                    // 读取接收缓冲区的数据(16bit)
    return (uint8_t)(rdata >> 8);                // 右移8位,提取有效8bit数据,返回
}
uint16_t Get_Byte(void)
{
    uint8_t rdata;
    PtrSPI->SPICTL.bit.TALK = 0;      // 关闭发送通路
    PtrSPI->SPITXBUF = DUMMYDATA;     // 发空包产生时钟
    while(PtrSPI->SPIFFRX.bit.RXFFST == 0);
    rdata = PtrSPI->SPIRXBUF;
    return rdata;
}

5.2 核心原理(理解“发即收”)

  • TALK位使能:「TALK=1」开启SPI发送通道,主机才能向从机发送数据;若为0,发送通道关闭,无法发送数据。

  • 发送数据:TI C2000的SPITXBUF(发送寄存器)是16bit,而我们配置的是8bit数据传输,因此需将8bit数据左移8位,占用寄存器的高8位,避免数据错位。

  • 等待接收:两种等待方式二选一,FIFO模式(代码中方式)适合高速传输,中断标志位方式适合普通场景,均能实现“等待收发完成”。

  • 接收数据:SPIRXBUF(接收寄存器)也是16bit,接收的8bit数据存放在高8位,因此需右移8位,提取有效数据并返回。


    本文完,感谢阅读!如果对你有帮助,欢迎点赞、收藏、关注~

作者:自动化小小

日期:2026年4月

更多推荐