为 AW V831 配置 spidev 模块,使用 py-spidev 进行用户层的 SPI 通信。
本文目前先记录一些关键词,因为我还没来得及整理,但相关的联系很多,我怕忘了就先记下来。
起因
最近在做 MaixPy3 的开发中适配 Linux V831 的外设驱动接口,从 MCU 的思维去是希望直接在 Python 就可以使用 SPI 的驱动来调试外设。
现状
截止(2021-01-28)提供的 Sipeed MaixII Dock (V831) 镜像基于 linux 4.9 的版本,并在设备树描述中还未提供 spidev 设备结点(/dev/spidev
所以根据原理图补一下 spidev 的定义,然后通过 spidev_test 测试该设备,最后再通过 py-spidev 去访问即可完成本功能。
Python 示例
使用 py-spidev 完成 SPI 接口的 Python 代码。
import spidev
spi = spidev.SpiDev()
spi.open(bus, device)
to_send = [0x01, 0x02, 0x03]
spi.xfer(to_send)
- open(bus, device)
Connects to the specified SPI device, opening /dev/spidev
从 open 接口可以得知我们需要 linux 为它提供 /dev/spidev
这样的好处就是任何 linux 设备过来都只需要提供相应的 SPI 设备即可,具体引脚定义已经被设备树确定了。
所以我们现在要为 V831 添加 spidev 设备的定义。
加载 spidev 模块(.ko)
先在 kernel_menuconfig 中选中 spidev.c 的驱动。
在 arch/arm/configs/xxx_defconfig中添加CONFIG_SPI_SPIDEV=y那么就会编译drivers/spi/spidev.c文件,该文件的内容是注册一个spidev驱动。该驱动是一个字符设备驱动。
make kernel_menuconfig
Device Drivers --->
SPI support --->
<*> User mode SPI device driver support
模块本身没有什么好说明的,加载上就行,补充一下可以参考的 spidev_test 测试用法。
在设备树添加 spi 结点
如果没有相应的设备树定义,虽然可以加载 spidev.ko 模块,但你是看不到类似 /dev/spidev1.0 的设备出现的。
那么怎么做呢?
由于每个芯片的设备树都有些许出入,所以要先从原理图下手,确定好引脚资源后,再从官方 bsp 的设备树中提取设备的定义,再给其到绑定相应的驱动模块。
首先 MaixII dock 的给用户使用的 SPI 引脚定义如下图。
所以我们确定这组引脚可以被定义为某一个 spi1 设备(假设的),也就是期望可以出现一个 /dev/spidev1.0 设备,如下图。
先确定 V831 芯片的类型为 sun8iw19p1,所以找到源头 lichee/linux-4.9/arch/arm/boot/dts/sun8iw19p1.dtsi 获取关于芯片的定义,我们可以确保自己的定义不会与官方的有所出入,可以看到我提取了 spi1 的定义。
spi1: spi@05011000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "allwinner,sun8i-spi";
device_type = "spi1";
reg = <0x0 0x05011000 0x0 0x1000>;
interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk_pll_periph0>, <&clk_spi1>;
clock-frequency = <200000000>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&spi1_pins_a &spi1_pins_b>;
pinctrl-1 = <&spi1_pins_c>;
spi1_cs_number = <2>;
spi1_cs_bitmap = <3>;
status = "disabled";
};
然后这个定义要被修改到适合当前版型的硬件:
spi1_pins_a: spi1@0 {
allwinner,pins = "PH0", "PH1", "PH2";
allwinner,pname = "spi1_sclk", "spi1_mosi",
"spi1_miso";
allwinner,function = "spi1";
allwinner,muxsel = <4>;
allwinner,drive = <1>;
allwinner,pull = <0>;
};
spi1_pins_b: spi1@1 {
allwinner,pins = "PH3";
allwinner,pname = "spi1_cs0";
allwinner,function = "spi1";
allwinner,muxsel = <4>;
allwinner,drive = <1>;
allwinner,pull = <1>; // only CS should be pulled up
};
spi1_pins_c: spi1@2 {
allwinner,pins = "PH0", "PH1", "PH2", "PH3";
allwinner,function = "io_disabled";
allwinner,muxsel = <7>;
allwinner,drive = <1>;
allwinner,pull = <0>;
};
spi@05011000 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0x0 0x05011000 0x0 0x1000>;
interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk_pll_periph0>, <&clk_spi1>;
clock-frequency = <200000000>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&spi1_pins_a &spi1_pins_b>;
pinctrl-1 = <&spi1_pins_c>;
spi1_cs_number = <2>;
spi1_cs_bitmap = <3>;
status = "okay";
spi_board1 {
device_type = "spi_board1";
compatible = "spidev";
spi-max-frequency = <0x5f5e100>;
reg = <0x0>;
spi-rx-bus-width = <0x1>;
spi-tx-bus-width = <0x1>;
};
};
注意的地方有 spi1_pins_xxx 的引脚映射,这是与原理图对应的,最后在这个结点添加一个 spi_board1 为 compatible = "spidev"; 这时系统就会自行加载 /dev/spidev1.0 设备了。
其中:
- spi_cs_bitmap,由于 SPI 控制器支持多个 CS,这一个参数表示 CS 的掩码;
- spi_cs0、spi_sclk、spi_mosi 和 spi_miso 用于配置相应的 GPIO。
至于想要添加多个片选的设备定义我还没测试和整理,这个问题留给之后有需要的时候再来补充。
感兴趣的同学可以参考这几篇,都是全志芯片系列的文档。
测试 SPI 功能和最终效果
现在确保你系统已经有 /dev/spidevX.X 设备,然后准备一下 spidev_test -D /dev/spidevX.X 测试程序。
如果未短接 MOSI MISO 两个引脚就会全为 0xFF (不要在这里跟我说原理图引脚没对好),如果短接了,就按 spidev_test 定义字符串进行数据的回环。
uint8_t default_tx[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xF0, 0x0D,
};
root@sipeed:/# spidev_test -D /dev/spidev1.0
spi mode: 0x0
bits per word: 8
max speed: 500000 Hz (500 KHz)
RX | FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF | ................................
跳线帽短接一下。
现在结果如下:
root@sipeed:/# spidev_test -D /dev/spidev1.0
spi mode: 0x0
bits per word: 8
max speed: 500000 Hz (500 KHz)
RX | FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF | ................................
root@sipeed:/# spidev_test -D /dev/spidev1.0
spi mode: 0x0
bits per word: 8
max speed: 500000 Hz (500 KHz)
RX | FF FF FF FF FF FF 40 00 00 00 00 95 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF F0 0D | ......@....�..................�.
root@sipeed:/#
现在内部测试完成,拿到逻辑分析仪来验证一遍,因为这个时钟有点不太确定,量一下具体的数据信号确认事实。
现在确定了数据符合预期,但时钟为 400hz ,说明 spidev_test 测试时的时钟是不对的,只是软件上的主观配置(最好多测几种频率,不过我还没测)。
后记
本次记录是怕自己关了所有窗口就什么都不剩而做的记录。
2021年01月28日 junhuanchen