5. SPI通讯¶
在前面章节里,我们给大家讲到了I2C通讯协议使用相关的内容。
在本章里,我们接着为大家介绍另一种通讯协议的使用,SPI通讯协议。
SPI通讯协议同样也是在电子器件通讯中常见的一种通讯协议。
不过SPI通讯协议的通讯速率要更高,而且它采用的是全双工的通讯方式。
所以我们常常能在对通信速率有要求的电子器件上,发现SPI通讯协议下规定的接口。
比如一些 小尺寸的LCD屏幕
、AD转换芯片
、nRF系列射频模块
、SPI-FLASH
等等等等。
关于SPI协议的具体内容,我们在此不作过多探究,大家可以自行搜索SPI通讯协议的相关知识学习。
本章中,我们的重点是使用前面介绍的 python-periphery 及 Adafruit Blinka 两个Python库, 来使用到我们鲁班猫板卡上的SPI主机。
5.1. SPI实验¶
一般情况下,我们可以通过需要一个具体的通讯对象来完成本节实验。 但是由于SPI通讯协议支持全双工的方式进行通讯,所以我们可以通过回环测试来进行实验验证。
在实验中,我们将SPI接口的MOSI、MISO接口连接在一起即可。
当然大家在学习完回环实验之后,自然也可以通过鲁班猫板卡上的SPI资源去控制其他SPI接口的硬件。
5.2. 实验准备¶
在开始实验前,需要做一些准备。
5.2.1. 添加SPI资源¶
在板卡上的部分资源可能默认未被开启,在使用前请根据需要修改 /boot/uEnv.txt 文件, 可添加对应设备树插件的加载,重启系统,以在系统中添加对应资源。
如本节实验中,通常情况下在鲁班猫系统中默认使能 SPI
的功能,
如大家发现SPI的设备树插件未加载,请做相应修改。
同时如果大家在使用中发现SPI功能引脚被复用或有冲突,请要将对应占用SPI引脚资源的设备树插件取消加载, 否则SPI资源可能无法使用。若已开启对应资源,忽略以下步骤。
如笔者使用的鲁班猫i.MX6ULL MINI板卡,ec-spi3与uart2资源就互相冲突。
方法参考如下:
添加SPI设备树插件,以使系统支持SPI功能,如下:
# 以鲁班猫i.MX6ULL MINI板卡SPI设备树插件内容为例:
dtoverlay=/usr/lib/linux-image-4.19.35-imx6/overlays/imx-fire-ecspi3.dtbo
如若运行代码时出现“Device or resource busy”或者运行代码卡死等等现象, 请按上述情况检查并按上述步骤操作。
如出现 Permission denied
或类似字样,请注意用户权限,大部分操作硬件外设的功能,几乎都需要root用户权限,简单的解决方案是在执行语句前加入sudo或以root用户运行程序。
# 在终端中输入如下命令,可以查看到SPI资源:
ls /dev/spi*
板卡上的SPI资源示例,仅供参考:
5.2.2. 硬件连接¶
注:硬件连接小节,需要大家根据自身开发环境等情况对本章内容进行参考调整。
本章中,我们通过上述Python库,编写对SPI主机进行使用的测试代码, 并通过SPI回环来进行代码正确性测试。
笔者硬件连接如图,仅供参考:
回环连接如上图示。
5.3. 方式一:使用python-periphery¶
python-periphery 库支持的SPI功能是基于Linux的SPI系统实现的,所以要想利用该库使用到SPI的功能, 需要板卡提供支持。像鲁班猫板卡,就可以完美使用 python-periphery 库SPI通讯功能。 这样这样一来,就不需要我们在软件层面上利用GPIO模拟SPI主机功能了。
关于SPI子系统相关内容,参考 《Linux SPI通讯》 。
5.3.1. 安装 python-periphery¶
重要
如在前面小节中安装了此库,务必跳过此操作。
python-periphery 的安装方式如下:
# 在板卡使用如下命令安装
sudo pip3 install python-periphery
5.3.2. periphery使用SPI功能¶
使用 python-periphery 库进行SPI功能使用的示例代码如下:
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资源,打开 spidev2.0 控制器,配置SPI主机为工作模式0、工作速率为1MHz
spi = SPI("/dev/spidev2.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 i.MX6ULL MINI板卡,操作如下:
# 确认已安装python-periphery包
# 确认使能了SPI设备树插件
# 在板卡spi_test.py所在的目录执行如下命令
python3 spi_test.py
# 可看到终端打印出的接受到的数据与发送出去的一致
由于在硬件连接时,已经将SPI接口的MOSI、MISO接口连接, 所以SPI控制器发送出数据的同时也会将数据接收回来,这就是回环测试。
5.4. 方式二:使用Adafruit Blinka¶
Adafruit Blinka 与 python-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 进行输入输出的示例代码如下:
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仅读取数据,不发送
Adafruit Blinka 库未提供SPI对象的使用示例,相关的API功能大家可以参考: CircuitPython SPI 下给出的介绍。