树莓派与STM32之间的SPI通信

树莓派的SPI模块

树莓派的GPIO引脚支持UART、SPI和I2C三种通信协议。SPI是一种双向、同步、全双工的串行协议,是三种协议中通信速率最快的。树莓派的官方文档从如下几个方面详细介绍了SPI模块的基本特性:

  1. 硬件引脚分配
  2. 在树莓派的Linux系统中,SPI硬件抽象成/dev/spidev0.0文件
  3. 树莓派的SPI只支持主模式(Master Mode)
  4. 配置参数:除了通信速率、CPOL、CPHA这三个参数外,其他的参数一般都不会去修改
  5. 提供了一段回环测试程序,可用于检测SPI模块是否正常工作

接下来介绍树莓派SPI模块的使用方法。看到/dev/spidev0.0文件,熟悉Linux系统编程的人马上会想起通用I/O模型中的几个函数。的确,可以在该文件上执行open和close操作,就跟操作普通文件一样。同样地,read和write函数可以读写数据,但只能进行基本的半双工数据传输,片选信号在读写操作之间会失效。使用ioctl()函数的SPI_IOC_MESSAGE(N)请求可实现全双工数据传输,而且整个过程中片选信号不会失效。

Linux内核代码的/tools/spi/目录下有个spidev_test.c文件,是SPI的示例程序。这段代码实现了一个命令行工具,既可以直接使用,也可以借鉴它的代码来实现自己的需求。编译代码生成可执行文件spidev_test,下面的命令使用默认配置将一个文件的内容通过SPI发送出去,并把从SPI接收的数据保存到另一个文件中:

$ ./spidev_test -D /dev/spidev0.0 -i send.txt -o receive.txt -v

通过-s-O-H选项可以分别修改通信速率、CPOL和CPHA三个参数,-v选项可以打印发送和接收的内容。正如上面提到的,这段代码就是使用ioctl()函数实现全双工数据传输,详情可以阅读源文件中的transfer函数。

STM32代码

首先将树莓派和STM32对应的SPI引脚连接好。为了验证树莓派和STM32之间的SPI通信是正确的,我将实现如下功能:

  1. 树莓派发送一个32字节大小的文件给STM32,STM32收完之后用串口发送出去,在PC端的串口助手上查看数据
  2. STM32同时也给树莓派发送数据,树莓派上可通过-v选项查看STM32发送过来的数据

STM32的SPI配置成全双工、从模式(Slave Mode),CPHA和CPOL参数都为0。部分代码如下:

//
// 省略GPIO配置、其他初始化和NVIC配置代码
//
// 双线全双工通信,从模式,软件控制片选信号
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;    // CPOL = 0
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;  // CPHA = 0
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

// 使能TXE和RXNE中断
SPI_I2S_ITConfig(SPI_MASTER, SPI_I2S_IT_TXE, ENABLE);
SPI_I2S_ITConfig(SPI_MASTER, SPI_I2S_IT_RXNE, ENABLE);

STM32接收和发送数据都在中断中进行。发送缓冲区为空时会触发TXE中断,这时没有可发的数据,调用SendData函数发送数据;接收缓冲区不为空时说明有新的数据到了,此时会触发RXNE中断,调用ReceiveData函数读出数据。

#define TX_SIZE 32
#define RX_SIZE 32

// 发给树莓派的数据
uint8_t spi_tx[TX_SIZE] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
                           0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
                           0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12,
                           0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
                           0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E,
                           0x1F, 0x20};
uint32_t tx_idx = 0;

uint8_t spi_rx[RX_SIZE];
uint32_t rx_idx = 0;

void SPI1_IRQHandler(void)
{
    int i;
    if (SPI_I2S_GetITStatus(SPI1, SPI_I2S_IT_TXE) != RESET) {
        SPI_I2S_SendData(SPI1, spi_tx[tx_idx++]);
        // 发送32个数据后禁止TXE中断
        if (tx_idx == TX_SIZE) {
            SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_TXE, DISABLE);
        }
    }
    // 从SPI读取数据
    if (SPI_I2S_GetITStatus(SPI1, SPI_I2S_IT_RXNE) != RESET) {
        spi_rx[rx_idx++] = SPI_I2S_ReceiveData(SPI1);
        // 收完32个字节后将收到的内容通过串口发送出去
        if (rx_idx == RX_SIZE) {
            for (i = 0; i < RX_SIZE; i++) {
                USART_SendData(USART1, (uint16_t)spi_rx[i]);
                while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
            }
        }
    }
}

将STM32串口线连接到PC上,打开串口助手软件,打开对应的COM口,然后在树莓派上运行命令向STM32发送文件的内容。如果串口助手中接受到的数据和文件内容完全符合,而且树莓派上显示的也是STM32发送的内容,就证明双向通信是正确的。

参考链接

  1. SPI - Raspberry Pi Documentation
  2. Can Raspberry PI function as SPI slave?
  3. spidev - kernel.org
  4. SPLibrary-STM32F103-SPI-FULLDUBLEX-MASTER-SLAVE_COMMUNICATION

你可能感兴趣的:(树莓派与STM32之间的SPI通信)