LoopBack

该示例演示了通过中断方式使 SPI 主设备与从设备进行通信。

在通信过程中,主设备和从设备之间互相发送数据来进行通信,并通过中断方式进行数据接收。

当主设备与从设备接收的数据达到设定的阈值时,会触发 SPI_INT_RXF 中断,进行数据的接收。

在该示例中,SPI1 被配置为主设备,SPI0 被配置为从设备。

环境需求

该示例的环境需求,可参考 环境需求

硬件连线

EVB 连接 P2_4(Master SCK)和 P3_0(Slave SCK),P2_5(Master MOSI)和 P3_1(Slave MOSI),P2_6(Master MISO)和 P3_2(Slave MISO),P2_7(Master CS)和 P3_3(Slave CS)。

配置选项

  1. 可配置如下宏修改引脚定义。

    /* SPI1 MASTER pin define*/
    #define SPI_MASTER_SCK_PIN               P2_4
    #define SPI_MASTER_MOSI_PIN              P2_5
    #define SPI_MASTER_MISO_PIN              P2_6
    #define SPI_MASTER_CS_PIN                P2_7
    
    /* SPI0 SLAVE pin define*/
    #define SPI_SLAVE_SCK_PIN                P3_0
    #define SPI_SLAVE_MOSI_PIN               P3_1
    #define SPI_SLAVE_MISO_PIN               P3_2
    #define SPI_SLAVE_CS_PIN                 P3_3
    

编译和下载

该示例的编译和下载流程,可参考 编译和下载

测试验证

  1. EVB 启动后,在 Debug Analyzer 工具内观察 log。

    Start spi loopback test!
    
  2. 主设备端向从设备端发送数据 {0, 1, …, 0x14}。从设备端接收到主设备端发来的数据,进入中断,打印从设备端接收数据长度,从设备端接收数据信息和主设备端发送的数据信息。

    SPI SLAVE receieves 20 data.
    slave_rx_buf[0] = 0x00000001, master_tx_buf[0] = 0x00000001
    slave_rx_buf[1] = 0x00000002, master_tx_buf[1] = 0x00000002
    ...
    slave_rx_buf[19] = 0x00000014, master_tx_buf[19] = 0x00000014
    
  3. 从设备端向主设备端发送数据 {0x10, 0x11, …, 0x23}。主设备端接收到从设备端发送的信息,进入中断,打印主设备端接收数据长度,主设备端接收数据信息和从设备端发送的数据信息。

    SPI MASTER receieves 20 data
    master_rx_buf[0] = 0x00000010, slave_tx_buf[0] = 0x00000010
    master_rx_buf[1] = 0x00000011, slave_tx_buf[1] = 0x00000011
    ...
    master_rx_buf[19] = 0x00000023, slave_tx_buf[19] = 0x00000023
    

代码介绍

该章节主要介绍示例中的初始化和相应功能实现的代码和流程说明。

源码路径

工程文件和源码路径如下:

  • 工程路径: sdk\samples\peripheral\spi\loopback\proj

  • 源码路径: sdk\samples\peripheral\spi\loopback\src

初始化

外设的初始化流程可参考 General Introduction 中的 初始化流程 部分。

  1. 调用 Pad_Config()Pinmux_Config(),配置对应引脚的 PAD 和 PINMUX。将 SPI1 相关引脚 PINMUX 为主设备功能,将 SPI0 相关引脚 PINMUX 为从设备功能。

    void board_spi_master_init(void)
    {
       Pad_Config(SPI_MASTER_SCK_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
       Pad_Config(SPI_MASTER_MOSI_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
       Pad_Config(SPI_MASTER_MISO_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
       Pad_Config(SPI_MASTER_CS_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
    
       Pinmux_Deinit(SPI_MASTER_SCK_PIN);
       Pinmux_Deinit(SPI_MASTER_MOSI_PIN);
       Pinmux_Deinit(SPI_MASTER_MISO_PIN);
       Pinmux_Deinit(SPI_MASTER_CS_PIN);
    
       Pinmux_Config(SPI_MASTER_SCK_PIN, SPI1_CLK_MASTER);
       Pinmux_Config(SPI_MASTER_MOSI_PIN, SPI1_MO_MASTER);
       Pinmux_Config(SPI_MASTER_MISO_PIN, SPI1_MI_MASTER);
       Pinmux_Config(SPI_MASTER_CS_PIN, SPI1_CSN_0_MASTER);
    }
    
    void board_spi_slave_init(void)
    {
       Pad_Config(SPI_SLAVE_SCK_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
       Pad_Config(SPI_SLAVE_MOSI_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
       Pad_Config(SPI_SLAVE_MISO_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
       Pad_Config(SPI_SLAVE_CS_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
    
       /* Must deinit P4_0~P4_3 before use SPI SLAVE!!! */
       Pinmux_Deinit(P4_0);
       Pinmux_Deinit(P4_1);
       Pinmux_Deinit(P4_2);
       Pinmux_Deinit(P4_3);
    
       Pinmux_Deinit(SPI_SLAVE_SCK_PIN);
       Pinmux_Deinit(SPI_SLAVE_MOSI_PIN);
       Pinmux_Deinit(SPI_SLAVE_MISO_PIN);
       Pinmux_Deinit(SPI_SLAVE_CS_PIN);
    
       Pinmux_Config(SPI_SLAVE_SCK_PIN, SPI0_CLK_SLAVE);
       Pinmux_Config(SPI_SLAVE_MOSI_PIN, SPI0_SI_SLAVE);
       Pinmux_Config(SPI_SLAVE_MISO_PIN, SPI0_SO_SLAVE);
       Pinmux_Config(SPI_SLAVE_CS_PIN, SPI0_CSN_0_SLAVE);
    }
    
  2. 调用 RCC_PeriphClockCmd() ,开启 SPI 时钟。

  3. 对 SPI 外设进行初始化:

    1. 定义 SPI_InitTypeDef 类型 SPI_InitStruct ,调用 SPI_StructInit()SPI_InitStruct 预填默认值。

    2. 根据需求修改 SPI_InitStruct 参数,SPI0_SLAVE 与 SPI1 的初始化参数配置如下表。

    3. 调用 SPI_Init(),初始化 SPI 外设。

SPI 初始化参数

SPI Hardware Parameters

Setting in the SPI_InitStruct variables

SPI1

SPI0_SLAVE

SPI Transfer Mode

SPI_InitTypeDef::SPI_Direction

SPI_Direction_FullDuplex

SPI_Direction_FullDuplex

SPI Data Size

SPI_InitTypeDef::SPI_DataSize

SPI_DataSize_8b

SPI_DataSize_8b

Clock Mode - CPOL

SPI_InitTypeDef::SPI_CPOL

SPI_CPOL_High

SPI_CPOL_High

Clock Mode - CPHA

SPI_InitTypeDef::SPI_CPHA

SPI_CPHA_2Edge

SPI_CPHA_2Edge

Clock Prescaler

SPI_InitTypeDef::SPI_BaudRatePrescaler

100

100

Frame Format

SPI_InitTypeDef::SPI_FrameFormat

SPI_Frame_Motorola

SPI_Frame_Motorola

RX Threshold

SPI_InitTypeDef::SPI_RxThresholdLevel

SEND_LENGTH - 1

SEND_LENGTH - 1

  1. 调用 SPI_Cmd(),使能相应 SPI 外设。

功能实现

  1. 在主设备调用 SPI_SendBuffer() 发送数据之前,从设备必须先调用 SPI_SendBuffer() 将数据预先放入 FIFO 内。主设备开始发送回数据后,发起时钟信号,从设备随之将数据打出。

  2. 主设备接收从设备发送的数据,接收数据达到阈值后,会触发 SPI_INT_RXF 中断,从设备收到数据时同理。在中断函数内,调用 SPI_GetRxFIFOLen()SPI_ReceiveData() 接收数据。

    void SPI_Handler_MASTER(void)
    {
       if (SPI_GetINTStatus(SPI_MASTER, SPI_INT_RXF))
       {
          SPI_INTConfig(SPI_MASTER, SPI_INT_RXF, DISABLE);
          SPI_ClearINTPendingBit(SPI_MASTER, SPI_INT_RXF);
          uint32_t master_rx_len = SPI_GetRxFIFOLen(SPI_MASTER);
          ...
          SPI_INTConfig(SPI_MASTER, SPI_INT_RXF, ENABLE);
       }
    }
    
    void SPI_Handler_SLAVE(void)
    {
       if (SPI_GetINTStatus(SPI_SLAVE, SPI_INT_RXF))
       {
          SPI_INTConfig(SPI_SLAVE, SPI_INT_RXF, DISABLE);
          SPI_ClearINTPendingBit(SPI_SLAVE, SPI_INT_RXF);
          uint32_t slave_rx_len = SPI_GetRxFIFOLen(SPI_SLAVE);
          ...
          SPI_INTConfig(SPI_SLAVE, SPI_INT_RXF, ENABLE);
       }
    
    }