使用BBB的SPI

SPI是可以全双工通信的一种串行总线,两个设备之间双向通信的话一般使用3根线:SCLK,MISO,MOSI,多个设备之间双向通信的话,每个设备还需要再加上一根地址线CSn。相比之下I2C只能半双工,而且一般需要上拉电阻,但无论几个设备,都只需要2根线。更多基础知识请谷歌百度。

Beaglebone Black使用的AM3359芯片上有两个SPI,但SPI1连接到了板子的HDMI芯片上,所以除非禁用HDMI,否则我们只能使用SPI0。本文将利用自带的spidev驱动使能SPI0,并进行一下简单的验证。

1、配置device tree

首先我们用我在《使用BBB的I2C》这篇文章中使用的方法检验一下SPI相关的引脚功能是否配置正确。检查结果是,不正确,也就是说SPI默认是没有启用的,新版arm linux配置硬件的方式是利用device tree,所以我们必须要配置一个device tree来启用它。我们先到 /lib/firmware 目录中看看有没有现成的device tree source (.dts)文件可供使用。我们发现有一个BB-SPI0-00A0.dts。内容如下

/dts-v1/;  
/plugin/;  
  
/ {  
    compatible = "ti,beaglebone", "ti,beaglebone-black";  
  
    /* identification */  
    part-number = "BB-SPI0";  
    version = "00A0";  
  
    /* state the resources this cape uses */  
    exclusive-use =  
        /* the pin header uses */  
        "P9.17",    /* spi0_cs0 */  
        "P9.18",    /* spi0_d1 */  
        "P9.21",    /* spi0_d0 */  
        "P9.22",    /* spi0_sclk */  
        /* the hardware ip uses */  
        "spi0";  
  
    fragment@0 {  
        target = <&am33xx_pinmux>;  
        __overlay__ {  
            /* default state has all gpios released and mode set to uart1 */  
            bb_spi0_pins: pinmux_bb_spi0_pins {  
                pinctrl-single,pins = <  
                    0x150 0x30  /* spi0_sclk.spi0_sclk, INPUT_PULLUP | MODE0 */  
                    0x154 0x30  /* spi0_d0.spi0_d0, INPUT_PULLUP | MODE0 */  
                    0x158 0x10  /* spi0_d1.spi0_d1, OUTPUT_PULLUP | MODE0 */  
                    0x15c 0x10  /* spi0_cs0.spi0_cs0, OUTPUT_PULLUP | MODE0 */  
                >;  
            };  
        };  
    };  
  
    fragment@1 {  
        target = <&spi0>; /* spi0 is numbered correctly */  
        __overlay__ {  
            status = "okay";  
            pinctrl-names = "default";  
            pinctrl-0 = <&bb_spi0_pins>;  
  
            #address-cells = <1>;  
            #size-cells = <0>;  
  
            /* add any spi devices connected here */  
            /* note that you can do direct SPI via spidev now */  
  
            // commented out example of an adafruit 1.8" TFT display  
            // from firmare/capes/cape-bone-adafruit-lcd-00A0.dts  
            // lcd@0 {  
            //  #address-cells = <1>;  
            //  #size-cells = <0>;  
            //  
            //  compatible = "adafruit,tft-lcd-1.8-red", "sitronix,st7735";  
            //  reg = <0>;  
            //  
            //  spi-max-frequency = <8000000>;  
            //  spi-cpol;  
            //  spi-cpha;  
            //  
            //  pinctrl-names = "default";  
            //  pinctrl-0 = <&bone_adafruit_lcd_pins>;  
            //  
            //  st7735-rst = <&gpio4 19 0>;  
            //  st7735-dc = <&gpio4 21 0>;  
            // };  
        };  
    };  
};

从这个文件里我们能得到很多信息(我在此唠叨两句,也算跟大家分享一下我学习的过程),首先我们从exclusive-use这一部分能看出来AM3359芯片对SPI引脚的命名是跟一般不太一样的,它没用MISO和MOSI,而是D0和D1。通过查询4000页手册我们得知,原来是因为这两个引脚的功能是可以通过配置寄存器来互换的。默认的对应方式是d0对应miso,d1对应mosi。

再接着看,发现有一句注释

/* note that you can do direct SPI via spider now */


这个spidev就是我们要用的spi驱动,然后谷歌一下它的用法就可以了。再下面有一些被注释掉的东西,是要根据不同设备来替换的。

(以下操作都在Beaglebone上进行)

我们把自带的文件复制一份,保存为 BB-SPI0-01-00A0.dts ,然后增加一个节点,内容如下(就是原文件中注释部分要替换的内容)

spidev@0 {  
       spi-max-frequency = <24000000>;  
       reg = <0>;  
       compatible = "linux,spidev";  
};  


保存以后编译这个dts文件

# dtc -O dtb -o BB-SPI0-01-00A0.dtbo -b 0 -@ BB-SPI0-01-00A0.dts

然后把生成的.dtbo文件放到/lib/firmware目录中

# cp BB-SPI0-01-00A0.dtbo /lib/firmware/

然后把它“插”到“插槽”中(请看我的这篇博文

# echo BB-SPI0-01 > /sys/devices/bone_capemgr.*/slots

OK,这时我们进入/dev目录中就会发现比原来多了一个设备 spidev1.0 ,说明device tree配置没有问题,该设备已成功加载。

2、使用SPI

因为我手边没有SPI设备,所以我把D0和D1也就是P9.18和P9.21这两个引脚连接起来进行自发自收,如果收到了发送的数据即成功。时钟线就不必管了,因为自己跟自己的时钟肯定是同步的。

测试程序使用的是linux自带的一个spidev_test.c程序(下载地址在这里,不过还是建议直接把整个kernel下载下来比较方便搜索查询)。这个程序的内容就是发送一串16进制数,然后 printf 接收到的内容(不知道这串数有没有什么别的含义)。

下面把这个文件传到Beaglebone上,用gcc编译一下,生成可执行文件spidev_test。假设现在就在这个文件的目录下,那么我们输入

# ./spidev_test -D /dev/spidev1.0

来进行测试。得到输出

spi mode: 0  
bits per word: 8  
max speed: 500000 Hz (500 KHz)  
  
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   
DE AD BE EF BA AD   
F0 0D  


说明测试成功了。否则会输出一串FF。

3、为什么dts文件要那样改?

刚刚我在自带的BB-SPI0-00A0.dts文件中加了一个节点,然后向其中加了几个属性,SPI0就能用了。增加一个节点还能够理解,但为什么要加这几个属性?这个问题我想了几天也没想得很清楚。不过我知道的是,这3个属性缺一不可。

其中compatible属性是每个节点必须有的,它的作用是将这个设备和某个驱动进行绑定。比如这里就是将这个spi设备与 linux -> spidev 这个驱动绑定。我把逗号换成了箭头,是因为我觉得其实这个逗号表达的是从属关系,用箭头更合适。但是,我在kernel文件中翻遍了也没找到哪里有“linux,spidev”这样的字眼。spidev驱动倒是找到了,而且在这个驱动文件中发现了如下内容

static const struct of_device_id spidev_dt_ids[] = {  
    { .compatible = "rohm,dh2228fv" },  
    {},  
};  


我试着把BB-SPI0-01-00A0.dts里的 compatible 值换成 “rohm,dh2228fv”,结果居然也成功了!这似乎说明以后如果我们知道要用哪个驱动的话,到驱动文件里搜索compatible找到相应内容就可以了。不过,我遗憾地发现大部分驱动文件里都没有这个属性。可能只有一些硬件外设的驱动,或者是别的公司做的驱动里才会有。所以,我又迷惘了……

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s