为 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. 设备进行 SPI 操作。

这样的好处就是任何 linux 设备过来都只需要提供相应的 SPI 设备即可,具体引脚定义已经被设备树确定了。

所以我们现在要为 V831 添加 spidev 设备的定义。

python中的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

posted @ 2021-01-28 18:27  Juwan  阅读(98)  评论(0编辑  收藏