跳转至

15.Linux SPI驱动(未完成)

实验说明

该部分使用正点原子MINI开发板实现,由于板子上未接入SPI相关设备,外接一个0.96OLED作为测试。

驱动编写

修改设备树,在spi设备中,添加oled节点。

&ecspi3 {
    fsl,spi-num-chipselects = <1>;
    cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_ecspi3>;
    status = "okay";

    spidev: oled@0 {
        compatible = "alientek,oled";
        spi-max-frequency = <8000000>;
        reg = <0>;        
        oled-dc-gpio  = <&gpio1 6 GPIO_ACTIVE_HIGH>;    
        oled-rst-gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>;    
    };
};

然后添加pinctrl节点

pinctrl_ecspi3: ecspi3grp {
    fsl,pins = <
        MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO        0x100b1  /* MISO*/
        MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI        0x100b1  /* MOSI*/
        MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK      0x100b1  /* CLK*/
        MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20       0x100b0  /* CS*/
        MX6UL_PAD_GPIO1_IO06__GPIO1_IO06          0x17059  /*dc*/
        MX6UL_PAD_GPIO1_IO07__GPIO1_IO07          0x17059  /*rst*/
    >;
};

包含所需头文件

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

然后写驱动出入口相关函数,这里知道spi_register_driver是添加一个spi设备,spi_unregister_driver是卸载。这里spi驱动结构体结构体还未定义。

static int __init oled_init(void)
{
    return spi_register_driver(&oled_driver);
}

static void __exit oled_exit(void)
{
    spi_unregister_driver(&oled_driver);
}

module_init(oled_init);
module_exit(oled_exit);
MODULE_LICENSE("GPL");

然后完善设备树匹配列表和spi驱动结构体结构体。结构体内部相关函数未声明。传统和设备树匹配列表必须同时存在,不然无法进入probe函数。

/* 传统匹配方式ID列表 */
static const struct spi_device_id oled_id[] = {
    {"alientek,oled", 0},  
    {}
};

/* 设备树匹配列表 */
static const struct of_device_id oled_of_match[] = {
    { .compatible = "alientek,oled" },
    { /* Sentinel */ }
};

/* SPI驱动结构体 */  
static struct spi_driver oled_driver = {
    .probe = oled_probe,
    .remove = oled_remove,
    .driver = {
            .owner = THIS_MODULE,
            .name = "oled",
            .of_match_table = oled_of_match, 
           },
    .id_table = oled_id,
};

然后完善probe和remove函数,这里主要是注册设备,以及删除设备

#define OLED_DEV_MAJOR        0
#define OLED_DEV_NAME         "oled"
#define OLED_DEV_CLASS_NAME   "oled_class"
#define OLED_DEV_NODE_NAME    "oled" 

struct oled_dev_t{  
    struct class *class;        
    struct device *device;
    struct device_node  *nd;
    struct spi_device *spi;

    int major;              
    int minor;              
};

struct oled_dev_t oled_dev;

static int oled_probe(struct spi_device *spi)
{
    int ret;
    printk("oled_probe \r\n");
    ret = register_chrdev(OLED_DEV_MAJOR, OLED_DEV_NAME, &oled_drv_fops);

    if(ret < 0){
        printk("oled_dev driver register failed\r\n");
        return ret;
    }

    if(0 == OLED_DEV_MAJOR)
    {
        oled_dev.major = ret;
    }else
    {
        oled_dev.major = OLED_DEV_MAJOR;
    }
    printk("oled_dev.major = %d\r\n",oled_dev.major);

    //创建类
    oled_dev.class = class_create(THIS_MODULE, OLED_DEV_CLASS_NAME);
    if (IS_ERR(oled_dev.class)) {
        return PTR_ERR(oled_dev.class);
    }
    //创建设备
    oled_dev.device = device_create(oled_dev.class, NULL, MKDEV(oled_dev.major, 0), NULL, OLED_DEV_NODE_NAME); 
    if (IS_ERR(oled_dev.device)) {
        return PTR_ERR(oled_dev.device);
    }
    /*初始化spi_device */
    spi->mode = SPI_MODE_0; /*MODE0,CPOL=0,CPHA=0*/
    spi_setup(spi);
    oled_dev.spi = spi; /* 设置私有数据 */

    return 0;

}


static int oled_remove(struct spi_device *spi)
{
    printk("oled_remove \r\n");
    unregister_chrdev(oled_dev.major, OLED_DEV_NAME);

    //删除类、删除设备
    device_destroy(oled_dev.class, MKDEV(oled_dev.major, 0));
    class_destroy(oled_dev.class);
    return 0;
}```

然后编写文件操作函数

```C

编写spi的write和read函数