Linux驱动之SPI子系统

一、SPI子系统组成

I2C子系统与SPI子系统涉及的几乎一样,就是在调用底层API时不一样

1.SPI设备驱动

先上结构体:
	SPI BUS:
        struct bus_type spi_bus_type = {
            .name		= "spi",
            .dev_groups	= spi_dev_groups,
            .match		= spi_match_device,
            .uevent		= spi_uevent,
        };
	SPI BUS的"device结构体":
        struct spi_device {
            struct device		dev;
            struct spi_master	*master;//重点,用于调用API
            u32			max_speed_hz;
            u8			chip_select;
            u8			bits_per_word;
            u16			mode;
            ....
        };
	I2C BUS 的"driver结构体":
        struct spi_driver {
            const struct spi_device_id *id_table;//不支持设备树的id_table
            int			(*probe)(struct spi_device *spi);
            int			(*remove)(struct spi_device *spi);
            void			(*shutdown)(struct spi_device *spi);
            struct device_driver	driver;//支持设备树的id_table
        };

2.spi总线驱动

先上结构体:
    struct spi_master {
        struct device	dev;
        struct list_head list;

        int	(*transfer)(struct spi_device *spi,struct spi_message *mesg);//实现与spi设备通信的函数
        int (*transfer_one_message)(struct spi_master *master,struct spi_message *mesg);
        int (*unprepare_transfer_hardware)(struct spi_master *master);
        int (*prepare_message)(struct spi_master *master,struct spi_message *message);
        int (*unprepare_message)(struct spi_master *master,struct spi_message *message);
    }

与I2C子系统几乎相同,I2C的适配器就相当于SPI的控制器,I2C的i2c_adapter到了SPI这里就变成了spi_master
步骤如下:
	1.在设备树中加入spi控制器
	2.写platform driver去的probe中定义了spi_master并且实现了底层的与SPI通信的API然后将spi_master注册进了内核。
        这样在spi总线中只要有spi设备加入了dtb文件那么内核就会自动实现将spi_master加入到I2C设备结构体下,以便我们设备驱动的driver能够通过spi设备获取到API,从而实现对spi设备的读写控制。

3.原理性问题不深究

由于我们谈怎么使用所以关于内核如何实现这个spi子系统我们不做很深入研究(比如在注册完spi_master内核是怎么把spi_master传递给注册入spi bus的device的呢?)

二、spi 设备驱动编写

1.步骤

//1.更改设备树
	1.如果要使用pinctl子系统,我们就要去配置它复用为spi与其电气属性
     如:
     pinctrl_ecspi3: icm20608 {
        	fsl,pins = <
        	MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x10b0 /* CS */
        	MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x10b1 /* SCLK */
        	MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x10b1 /* MISO */
        	MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x10b1 /* MOSI */
        	>;
     };
	2.增加i2c设备节点到对应的i2c大节点下
	如:
     &ecspi3 {
        	fsl,spi-num-chipselects = <1>;
        	cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>;//片选引脚要配置GPIO子系统
        	pinctrl-names = "default";
        	pinctrl-0 = <&pinctrl_ecspi3>;//必须对应上面的pinctl
        	status = "okay";
        
       		spidev: icm20608@0 {
        	compatible = "alientek,icm20608";
        	spi-max-frequency = <8000000>;
        	reg = <0>;
        	};
    };
//2.编写driver.c
	1.定义与初始化struct i2c_driver
  		/* 传统匹配方式 ID 列表 */
        static const struct spi_device_id icm20608_id[] = {
        	{"alientek,icm20608", 0}, 
        	{}
        };
        /* 设备树匹配列表 */
    	static const struct of_device_id icm20608_of_match[] = {
    		{ .compatible = "alientek,icm20608" },
    		{ /* Sentinel */ }
    	};

    	/* SPI 驱动结构体 */
    	static struct spi_driver icm20608_driver = {
    		.probe = icm20608_probe,
    		.remove = icm20608_remove,
    		.driver = {
    		.owner = THIS_MODULE,
    		.name = "icm20608",
    		.of_match_table = icm20608_of_match,
    		},
    		.id_table = icm20608_id,
    	};
	2.实现probe与remove
        1.static int xx_probe(struct spi_device *spi);
	        注册字符设备,实现fops,记录下spi中的spi_master,在open的时候传入filp->private_data,这样就可以在read/write是调用到API
        2.static int xx_remove(struct spi_device *spi);
			注销与释放字符设备
	3.注册i2c_driver
        spi_register_driver(&icm20608_driver);
	4.注销i2c_driver
        spi_unregister_driver(&icm20608_driver);

2.如何使用API

先上结构体:
    struct spi_transfer {
        const void	*tx_buf;
        void		*rx_buf;
        unsigned	len;
        ....
        struct list_head transfer_list;
    };

	struct spi_message {
		struct list_head	transfers;
		struct spi_device	*spi;
		....
	};
写spi设备
    unsigned char *txdata;
    struct spi_message m;
    struct spi_transfer *t
    struct spi_device *spi = (struct spi_device *)dev->private_data;//我们将probe获取的spi赋值给了private_data
    /* 一共发送 len+1 个字节的数据,第一个字节为
    寄存器首地址,len 为要写入的寄存器的集合,*/
    *txdata = reg & ~0x80; /* 写数据的时候首寄存器地址 bit8 要清零 */
    memcpy(txdata+1, buf, len); /* 把 len 个寄存器拷贝到 txdata 里 */
    t->tx_buf = txdata; /* 要发送的数据 */
    t->len = len+1; /* t->len=发送的长度+读取的长度 */
    spi_message_init(&m); /* 初始化 spi_message */
    spi_message_add_tail(t, &m);
    ret = spi_sync(spi, &m); /* 同步发送
读spi设备
    unsigned char txdata[1];
	unsigned char * rxdata;
    struct spi_message m;
    struct spi_transfer *t
    struct spi_device *spi = (struct spi_device *)dev->private_data;//我们将probe获取的spi赋值给了private_data
	/* 一共发送 len+1 个字节的数据,第一个字节为
	寄存器首地址,一共要读取 len 个字节长度的数据,*/
	txdata[0] = reg | 0x80; //读发送要读的寄存器地址(最高位要为1)
	t->tx_buf = txdata; /* 要发送的数据 */
	t->rx_buf = rxdata; /* 要读取的数据 */
	t->len = len+1; /* t->len=发送的长度+读取的长度 */
	spi_message_init(&m); /* 初始化 spi_message */
	spi_message_add_tail(t, &m);
	ret = spi_sync(spi, &m); /* 同步发送
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值