14-Linux gpio模擬spi

1.config配置

首先是spidev,要在/dev/下面產生設備文件,需要spidev的支持

CONFIG_SPI_SPIDEV=y

使用的是gpio模擬spi,gpio模擬spi的時序原理是bitbang文件實現的,所以這個也需要打開,如果是在openwrt下動態加載的話就是如下兩個配置

CONFIG_PACKAGE_kmod-spi-bitbang=y
+CONFIG_PACKAGE_kmod-spi-gpio=y

如果是直接內核的話是如下兩個

CONFIG_SPI_BITBANG=y
CONFIG_SPI_GPIO=y

2.arch層添加device和board_info

跟I2C的arch層一樣,主要是devices的添加和board_info的添加,如下

//nfc mfrc522
    platform_add_devices(bsp_spi_gpio_devs, ARRAY_SIZE(bsp_spi_gpio_devs));

    spi_register_board_info(nfc_spi_gpio_board_info,
                    ARRAY_SIZE(nfc_spi_gpio_board_info));

對於platform_add_devices,因爲是使用spi_gpio,所以name是"spi_gpio"這樣纔可以與driver裏面的spi_gpio相互匹配probe到。

因爲SPI是可以一個總線上面掛多個,然後通過片選腳CS進行硬件切換,所以這變有個num_chipselect需要設置,如果有2個設置就設置2,一個設備就設置1,這邊設置好之後,後面board_info也要有對應的個數,而且片選引腳需要不同。

I2C是通過每個設備有自己不同的地址,通過地址來進行軟件切換。


#include <linux/spi/spi.h>
#include <linux/spi/spi_gpio.h

#define SPI_GPIO_BUS_NUM        1

#define SPI_GPIO_SCK               3 /* set gpio 2 as sck */
#define SPI_GPIO_MOSI              1 /* set gpio 3 as MOSI */
#define SPI_GPIO_MISO              0 /* set gpio 4 as MISO */

#define SPI_GPIO_NUM_CHIPSELECT    1 //2

struct spi_gpio_platform_data spi_gpio_info = { 
    .sck = SPI_GPIO_SCK,
    .mosi = SPI_GPIO_MOSI,
    .miso = SPI_GPIO_MISO,
    .num_chipselect = SPI_GPIO_NUM_CHIPSELECT,
};

struct platform_device bsp_spi_gpio_device = {
        .name          = "spi_gpio",
        .id            = SPI_GPIO_BUS_NUM,
        .dev    = {
            .platform_data  = &spi_gpio_info,
    }
};

static struct platform_device *bsp_spi_gpio_devs[] = {
    &bsp_spi_gpio_device,
};

對於board_info使用的是spidev,drivers/spi/spidev.c文件,該文件的內容是註冊一個spidev驅動。該驅動是一個字符設備驅動。

如果設備與驅動匹配,那麼就會執行spidev_probe()的內容。在spidev_probe()函數中會調用device_create()成功後在 /dev 目錄下就會生成 spidev 相關的設備節點。

這邊有幾個參數要注意:

  • bus_num說的是總線,如果爲0,生成的就是/dev/spidev0.x,如果爲1,生成的就是/dev/spidev1.x的設備。
  • chip_select,說的是第幾個設備,如果配置了有兩個設備,則就兩個一個0一個1,對應生成/dev/spidev1.0,/dev/spidev1.1兩個設備。
  • controller_data就是片選CS對應的硬件GPIO引腳。
#define SPI_GPIO_CHIP_SELECT_MFRC522             0
//#define SPI_GPIO_CHIP_SELECT_SPIDEV              1
#define SPI_GPIO_CONTROLLER_DATA_MFRC522           (void *)2 /* set gpio 2 as CS for spidev */
//#define SPI_GPIO_CONTROLLER_DATA_SPIDEV          (void *)4 /* set gpio 4 as CS for spidev */

static struct spi_board_info nfc_spi_gpio_board_info[] __initdata = { 
    {   
        .modalias               = "spidev",
        .mode                   = SPI_MODE_0, // CPOL=0, CPHA=0
        .bus_num                = SPI_GPIO_BUS_NUM,
        .chip_select            = SPI_GPIO_CHIP_SELECT_MFRC522,
        .controller_data        = SPI_GPIO_CONTROLLER_DATA_MFRC522,
        .max_speed_hz           = 25000000,
    },  
    /*{ 
        .modalias               = "spidev",
        .mode                   = SPI_MODE_0, // CPOL=0, CPHA=0
        .bus_num                = SPI_GPIO_BUS_NUM,
        .chip_select            = SPI_GPIO_CHIP_SELECT_SPIDEV,
        .controller_data        = SPI_GPIO_CONTROLLER_DATA_SPIDEV,
        .max_speed_hz           = 10000000,
    },*/
};

3.打開內核debug

調試過程想看一些細節的debug信息可以打開內核的動態debug信息,這個在以前的print system裏面有

CONFIG_DYNAMIC_DEBUG=y

CONFIG_DEBUG_DRIVER=y
CONFIG_DEBUG_DEVRES=y
CONFIG_SPI_DEBUG=y

printk的等級設置成8.

4.調試過程

開始

   13.650000] bus: 'platform': add driver spi_gpio
[   13.660000] bus: 'platform': driver_probe_device: matched device spi_gpio.0 with driver spi_gpio
[   13.680000] bus: 'platform': really_probe: probing driver spi_gpio with device spi_gpio.0
[   13.690000] spi_gpio spi_gpio.0: 11111-spi_gpio_probe
[   13.700000] spi_gpio spi_gpio.0: 4444-spi_gpio_request
[   13.710000] bus:5555
[   13.720000] spi_gpio: 2222-probe of spi_gpio.0 failed with error -16

定位到是spi_gpio_request的時候報錯

後面就將zkernel/3.10.49/arch/mips/mtk/ziroom/zrmt7628.c裏面GPIO的信息調整下,因爲SPI的引腳和LED的引腳號一樣,內核不知道哪裏會檢測到。

修改後打印如下:

[   13.610000] bus: 'platform': add driver spi_gpio
[   13.620000] bus: 'platform': driver_probe_device: matched device spi_gpio.1 with driver spi_gpio
[   13.640000] bus: 'platform': really_probe: probing driver spi_gpio with device spi_gpio.1
[   13.660000] spi_gpio spi_gpio.1: 11111-spi_gpio_probe
[   13.680000] device: 'spi1': device_add
[   13.680000] spi_gpio spi_gpio.1: registered master spi1
[   13.690000] spi_gpio spi_gpio.1: master is unqueued, this is deprecated
[   13.710000] spi spi1.0: spi_bitbang_setup, 100 nsec/bit
[   13.720000] spi spi1.0: setup mode 0, 8 bits/w, 10000000 Hz max --> 0
[   13.730000] device: 'spi1.0': device_add
[   13.740000] bus: 'spi': add device spi1.0
[   13.760000] bus: 'spi': driver_probe_device: matched device spi1.0 with driver spidev
[   13.780000] bus: 'spi': really_probe: probing driver spidev with device spi1.0
[   13.790000] device: 'spidev1.0': device_add
[   13.840000] driver: 'spi1.0': driver_bound: bound to device 'spidev'
[   13.860000] bus: 'spi': really_probe: bound device spi1.0 to driver spidev
[   13.870000] spi_gpio spi_gpio.1: registered child spi1.0
[   13.880000] spi spi1.1: spi_bitbang_setup, 40 nsec/bit
[   13.890000] spi spi1.1: setup mode 0, 8 bits/w, 25000000 Hz max --> 0
[   13.900000] device: 'spi1.1': device_add
[   13.910000] bus: 'spi': add device spi1.1
[   13.940000] spi_gpio spi_gpio.1: registered child spi1.1
[   13.950000] driver: 'spi_gpio.1': driver_bound: bound to device 'spi_gpio'
[   13.960000] bus: 'platform': really_probe: bound device spi_gpio.1 to driver spi_gpio
[   14.110000] xt_time: kernel timezone is -0000

之後在/dev/下面就生成了spidev1.0的設備

5.應用層測試

有了/dev/spidev1.0設備之後,就可以在應用成操作改設備收發數據。

在drivers/spi/spidev.c裏面已經封裝好了ioctl的對應接口,根據這些接口就可以測試使用。

在Documentation/spi/spidev_test.c下面有個應用層的實例,打開看下就清除了。

$(cc) spidev_test.c -o spidev_test生成可執行文件spidev_test

然後拷貝到板子上,將MOSI和MISO短接就可以測試迴環數據是否正常。

root@zihome:/tmp# ./spidev_test -D /dev/spidev1.0 
[ 3142.770000] spidev spi1.0: spi_bitbang_setup, 2000 nsec/bit
[ 3142.780000] spidev spi1.0: setup mode 0, 8 bits/w, 500000 Hz max --> 0
[ 3142.800000] spidev spi1.0: spi mode 00
[ 3142.800000] spidev spi1.0: spi_bitbang_setup, 2000 nsec/bit
[ 3142.820000] spidev spi1.0: setup mode 0, 8 bits/w, 500000 Hz max --> 0
[ 3142.830000] spidev spi1.0: 8 bits per word
[ 3142.840000] spidev spi1.0: spi_bitbang_setup, 2000 nsec/bit
[ 3142.850000] spidev spi1.0: setup mode 0, 8 bits/w, 500000 Hz max --> 0
[ 3142.860000] spidev spi1.0: 500000 Hz (max)
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 

有邏輯分析儀的接上logic看波形就更加直觀。

gpio模擬SPI:
https://blog.csdn.net/luckywang1103/article/details/70145870

在ARM Linux下使用GPIO模擬SPI時序詳解:
https://blog.csdn.net/yangzheng_yz/article/details/50470577

linux SPI驅動:
https://www.cnblogs.com/xuyh/category/903809.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章