LoopBack

该示例演示使用 SPI 自发自收的方式进行数据通信。 将SPI1配置为Master来使用,SPI0配置为Slave来使用,Master和Slave之间互相发送数据来进行通信。

环境需求

该示例支持以下开发套件:

开发套件

Hardware Platforms

Board Name

RTL87x2G HDK

RTL87x2G EVB

更多信息请参考 快速入门

硬件连线

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)。

编译和下载

该示例的工程路径如下:

Project file: samples\peripheral\spi\loopback\proj\rtl87x2g\mdk

Project file: samples\peripheral\spi\loopback\proj\rtl87x2g\gcc

请按照以下步骤操作构建并运行该示例:

  1. 打开工程文件。

  2. 按照 快速入门编译APP Image 给出的步骤构建目标文件。

  3. 编译成功后,在路径 mdk\bingcc\bin 下会生成 app bin app_MP_xxx.bin 文件。

  4. 按照 快速入门MPTool 给出的步骤将app bin烧录至EVB内。

  5. 按下复位按键,开始运行。

测试验证

  1. EVB启动后,在DebugAnalyzer工具内观察LOG。

    Start spi loopback test!
    
  2. Slave 端接收到Master端发来的数据,进入中断,打印Slave端接收数据长度,Slave端接收数据信息和Master端发送的数据信息。

    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. Master端接收到Slave端发送的信息,进入中断,打印Master端接收数据长度,Master端接收数据信息和Slave端发送的数据信息、

    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
    

代码介绍

该章节分为以下几个部分:

  1. 源码路径

  2. 初始化函数将在 初始化 章节介绍。

  3. 初始化后的功能实现将在 功能实现 章节介绍。

源码路径

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

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

该工程的工程文件代码结构如下:

└── Project: loopback
    └── secure_only_app
        └── Device                   includes startup code
            ├── startup_rtl.c
            └── system_rtl.c
        ├── CMSIS                    includes CMSIS header files
        ├── CMSE Library             Non-secure callable lib
        ├── Lib                      includes all binary symbol files that user application is built on
            └── rtl87x2g_io.lib
        ├── Peripheral               includes all peripheral drivers and module code used by the application
            ├── rtl_rcc.c
            ├── rtl_pinmux.c
            ├── rtl_nvic.c
            └── rtl_spi.c
        └── APP                      includes the ble_peripheral user application implementation
            ├── main_ns.c
            ├── spi_flash.c          includes a wrapper implementation of functions to communicate with external flash
            └── io_spi.c

初始化

初始化流程包括了 board_spi_slave_initboard_spi_master_initdriver_spi_slave_initdriver_spi_master_init


board_spi_slave_init 中包含了对SPI0_SLAVE的PAD与PINMUX设置:

  1. 配置PAD:设置引脚、PINMUX模式、PowerOn、内部上拉。

  2. 配置PINMUX:分配引脚分别为SPI0_CLK_SLAVE、SPI0_SI_SLAVE、SPI0_SO_SLAVE、SPI0_CSN_0_SLAVE功能。


board_spi_master_init 中包含了对SPI1的PAD与PINMUX设置:

  1. 配置PAD:参考 board_spi_slave_init 中PAD设置。

  2. 配置PINMUX:分配引脚分别为SPI1_CLK_MASTER、SPI1_MO_MASTER、SPI1_MI_MASTER、SPI1_CSN_0_MASTER功能。


driver_spi_slave_init 包含了对SPI0_SLAVE外设的初始化:

  1. 使能PCC时钟源。

  2. 设置传输模式为全双工模式。

  3. 配置传输数据宽度

  4. 配置串行时钟的稳态悬空高,数据捕获于第二个时钟沿。

  5. 配置接收数据长度阈值为 SEND_LENGTH - 1

  6. 设置数据传输格式。

  7. 使能SPI0_SLAVE。

  8. 配置接收缓冲区满中断 SPI_INT_RXF

  9. 准备Slave端需要发送的数据,执行 SPI_SendBuffer() 函数,将数据存放至FIFO中。

RCC_PeriphClockCmd(APBPeriph_SPI_SLAVE, APBPeriph_SPI_CLOCK_SLAVE, ENABLE);
...
SPI_InitStruct.SPI_Direction   = SPI_Direction_FullDuplex;
SPI_InitStruct.SPI_DataSize    = SPI_DataSize_8b;
SPI_InitStruct.SPI_CPOL        = SPI_CPOL_High;
SPI_InitStruct.SPI_CPHA        = SPI_CPHA_2Edge;
/* SPI_Direction_EEPROM mode read data lenth. */
SPI_InitStruct.SPI_RxThresholdLevel  = SEND_LENGTH - 1;/* Flash id lenth = 3*/
/* cause SPI_INT_RXF interrupt if data length in receive FIFO  >= SPI_RxThresholdLevel + 1*/
SPI_InitStruct.SPI_FrameFormat = SPI_Frame_Motorola;

SPI_Init(SPI_SLAVE, &SPI_InitStruct);
SPI_Cmd(SPI_SLAVE, ENABLE);

SPI_INTConfig(SPI_SLAVE, SPI_INT_RXF, ENABLE);
...
for (uint32_t i = 0; i < SEND_LENGTH; i++)
{
    slave_tx_buf[i] = 0x10 + i;
}
SPI_SendBuffer(SPI_SLAVE, slave_tx_buf, SEND_LENGTH);

driver_spi_master_init 包含了对SPI1外设的初始化:

  1. 基本初始化设置参考 driver_spi_slave_init

  2. 使能SPI MASTER。

  3. 准备Master端需要发送的数据,执行 SPI_SendBuffer() 函数,将数据存放至FIFO中。

备注

master端发起通信之前,slave端必须将数据准备好放至FIFO内。

功能实现

  1. Master端向slave端发送数据。slave端收到clock信号后,将FIFO中的数据发送至master端。

  2. Master端收到slave发来的数据后,触发 SPI_INT_RXF 中断,进入中断函数 SPI_Handler_MASTER

    1. 清除SPI_INT_RXF中断挂起位。

    2. 执行 SPI_GetRxFIFOLen() 获取FIFO中数据长度。

    3. 执行 SPI_ReceiveData() 接收slave端发送的数据并打印,比较接收到的数据和发送的数据是否相同。

SPI_INTConfig(SPI_MASTER, SPI_INT_RXF, DISABLE);
SPI_ClearINTPendingBit(SPI_MASTER, SPI_INT_RXF);
uint32_t master_rx_len = SPI_GetRxFIFOLen(SPI_MASTER);
for (uint32_t i = 0; i < master_rx_len; i++)
{
   master_rx_buf[i] = SPI_ReceiveData(SPI_MASTER);
   ...
}
SPI_INTConfig(SPI_MASTER, SPI_INT_RXF, ENABLE);
  1. Slave端收到master端发送的数据后,触发 SPI_INT_RXF 中断,进入中断函数 SPI_Handler_SLAVE

    1. 清除SPI_INT_RXF中断挂起位。

    2. 执行 SPI_GetRxFIFOLen() 获取FIFO中数据长度。

    3. 执行 SPI_ReceiveData() 接收master端发送的数据并打印,比较接收到的数据和发送的数据是否相同。

SPI_INTConfig(SPI_MASTER, SPI_INT_RXF, DISABLE);
SPI_ClearINTPendingBit(SPI_MASTER, SPI_INT_RXF);
uint32_t master_rx_len = SPI_GetRxFIFOLen(SPI_MASTER);
for (uint32_t i = 0; i < master_rx_len; i++)
{
   master_rx_buf[i] = SPI_ReceiveData(SPI_MASTER);
   ...
}
SPI_INTConfig(SPI_MASTER, SPI_INT_RXF, ENABLE);