5. SPI通讯

在前面章节里,我们给大家讲到了I2C通讯协议使用相关的内容。 在本章里,我们接着为大家介绍另一种通讯协议的使用,SPI通讯协议。 SPI通讯协议同样也是在电子器件通讯中常见的一种通讯协议。 不过SPI通讯协议的通讯速率要更高,而且它采用的是全双工的通讯方式。 所以我们常常能在对通信速率有要求的电子器件上,发现SPI通讯协议下规定的接口。 比如一些 小尺寸的LCD屏幕AD转换芯片nRF系列射频模块SPI-FLASH 等等。

关于SPI协议的具体内容,我们在此不作过多探究,大家可以自行搜索SPI通讯协议的相关知识学习。

本章中,我们的重点是使用前面介绍的 python-periphery 库, 来使用到我们鲁班猫板卡上的SPI主机。

5.1. SPI实验

由于SPI通讯协议支持全双工的方式进行通讯,所以我们可以通过回环测试来进行实验验证, 在实验中,我们将SPI接口的MOSI、MISO接口连接在一起即可。

5.2. 实验准备

5.2.1. 添加SPI资源

在板卡上的GPIO可能没有复用为I2C,可通过设置设备树插件来加载, LubanCat_RK系列板卡的引脚参考下: 《LubanCat-RK系列-40pin引脚对照图》

下面以LubanCat 2为例开启下SPI的设备树插件(LubanCat 2引出的引脚可使用spi3):

# 不同板卡的配置文件和i2c不同,以LubanCat 2为例,配置文件为uEnvLubanCat 2.txt,
# 登录系统终端,打开/boot/uEnv/uEnvLubanCat 2.txt文件

sudo vim /boot/uEnv/uEnvLubanCat 2.txt
#进入编辑模式,取消I2c3前面的注释,保存并退出文件,重启系统
broken

重启后,查看板卡的SPI资源是否加载:

# 在终端中输入如下命令,可以查看到SPI资源:
ls -l /dev/spi*

板卡上的I2C资源示例,仅供参考:

broken

关于在linux上的SPI测试,可以参考下 《SPI通讯》

5.2.2. 硬件连接

注:硬件连接小节,需要大家根据自身开发环境等情况对本章内容进行参考调整。

进行回环测试,连接如下(LubanCat 2):

broken

5.3. 使用python-periphery

python-periphery 库支持的SPI功能是基于Linux的SPI系统实现的,所以要想利用该库使用到SPI的功能, 需要板卡提供支持。像鲁班猫板卡,就可以完美使用 python-periphery 库SPI通讯功能。 这样这样一来,就不需要我们在软件层面上利用GPIO模拟SPI主机功能了。

5.3.1. 安装 python-periphery

重要

如在前面小节中安装了此库,务必跳过此操作。

python-periphery 的安装方式如下:

# 在板卡使用如下命令安装
sudo pip3 install python-periphery

5.3.2. periphery使用SPI功能

使用 python-periphery 库进行SPI功能使用的示例代码如下:

配套代码 io/spi/spi_test_periphery.py文件内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
""" periphery spi 测试 """
from periphery import SPI

# 待发送数据列表
data_out = [0xAA, 0xBB, 0xCC, 0xDD]

try:
    # 申请SPI资源,打开 spidev3.0 控制器,配置SPI主机为工作模式0、工作速率为1MHz
    spi = SPI("/dev/spidev3.0", 0, 1000000)

    # 发送数据,同时接收数据到data_in列表中
    data_in = spi.transfer(data_out)

    # 打印发送的数据内容
    print("发送的数据: [0x{:02x}, 0x{:02x}, 0x{:02x}, 0x{:02x}]".format(*data_out))
    print("接收到的数据: [0x{:02x}, 0x{:02x}, 0x{:02x}, 0x{:02x}]".format(*data_in))
finally:
    # 关闭申请的SPI资源
    spi.close()

代码说明:

  • 第5行,申请SPI资源,占用SPI3主机,配置对应的工作模式

  • 第9行,构造准备发送的消息列表

  • 第12行,开启SPI传输,因为SPI为全双工通讯方式,发送数据的同时也可接收数据

  • 第19行,释放SPI资源,释放SPI3主机

5.3.2.1. 实验操作

示例代码使用LubanCat 2板卡,操作如下:

# 在板卡spi_test.py所在的目录执行如下命令
sudo python3 spi_test.py

# 可看到终端打印出的接受到的数据与发送出去的一致
broken

由于在硬件连接时,已经将SPI接口的MOSI、MISO接口连接, 所以SPI控制器发送出数据的同时也会将数据接收回来,这就是回环测试。

5.4. 方式二:使用Adafruit Blinka

Adafruit Blinkapython-periphery 功能类似,SPI通讯也是基于Linux的SPI子系统实现的。 只是在代码上的用法上有较大差异。

5.4.1. 安装Adafruit Blinka

重要

如在前面小节中安装了此库,务必跳过此操作。

Adafruit Blinka 的安装方式如下:

# 在板卡使用如下命令安装
sudo apt -y install python3-libgpiod python-periphery # 注:如已安装,忽略此步
sudo pip3 install Adafruit-Blinka

5.4.2. Adafruit-Blinka使用SPI主机

使用这个 Adafruit Blinka 进行输入输出的示例代码如下:

配套代码 io/spi/spi_test_adafruit.py文件内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
""" blinka spi 测试 """
import busio
from board import *

# 数据发送、接受缓冲区
OutBuffer = [0xAA, 0xBB, 0xCC, 0xDD]
InBuffer = bytearray(4)

try:
    # 申请spi资源
    spi = busio.SPI(SCLK, MOSI, MISO)

    # 配置时先锁定SPI
    spi.try_lock()
    # 配置SPI主机工作速率为1MHz、时钟极性为0、时钟相位为0、单个数据位数为8位
    spi.configure(1000000, 0, 0, 8)
    # 配置操作完成解锁
    spi.unlock()

    # SPI通讯,发送的同时也进行读取,发送的数据存放在OutBuffer,读取的数据存放在InBuffer
    spi.write_readinto(OutBuffer, InBuffer)

    print(
        "(write_readinto)接收到的数据: [0x{:02x}, 0x{:02x}, 0x{:02x}, 0x{:02x}]".format(
            *InBuffer
        )
    )

    # SPI通讯,只写使用方法演示
    spi.write(OutBuffer)
    print(
        "(OutBuffer)发送的数据: [0x{:02x}, 0x{:02x}, 0x{:02x}, 0x{:02x}]".format(*OutBuffer)
    )

    InBuffer = [0, 0, 0, 0]
    # SPI通讯,只读使用方法演示
    spi.readinto(InBuffer)
    print(
        "(readinto)接收到的数据: [0x{:02x}, 0x{:02x}, 0x{:02x}, 0x{:02x}]".format(*InBuffer)
    )
finally:
    # 释放spi资源
    spi.deinit()

代码说明:

  • 第11行,申请SPI资源,占用SPI3主机

  • 第14~18行,配置SPI3主机的工作模式,在配置前需要先锁定资源

  • 第21行,使用SPI3主机发送字节数组OutBuffer里的数据,同时读取数据到InBuffer

  • 第30行,使用SPI3仅发送数据,不读取

  • 第37行,使用SPI3仅读取数据,不发送

运行后显示:

broken

另外,还有一个配套例程io/spi/ssd1306_spi_status.py,将使用spi控制屏幕显示, 具体参考下前面i2c使用Adafruit_CircuitPython_SSD1306库控制屏幕显示的小节。

Adafruit Blinka 库未提供SPI对象的使用示例,相关的API功能大家可以参考: CircuitPython SPI 下给出的介绍。