• 正点原子linux阿尔法开发板使用——SPI驱动


    SPI控制器驱动

    主机控制器驱动:SOC的spi外设驱动,是半导体厂家编写号的,为spi-imx.c当spi控制器的设备和驱动匹配之后,spi_imx_probe函数就会执行。

    SPI控制驱动核心就是spi_master的构建,spi_master

    spi_master->transfer
    spi_master-> transfer_one_message 6ULL主机控制器使用此函数,

    设备驱动:具体的SPI芯片驱动

    Emc20608

    SPI_Driver 非常重要,重点是申请或者定义一个spi_driver 然后初始化spi_driver的各个成员变量,

    驱动以及设备树

    SPI四个引脚的 pinctrl的电气属性分配:SCLK、NS、MOSI、MISO
    在这里插入图片描述
    在这里插入图片描述
    @后面的0表示接到哪一个硬件片选信号!

    参考文档是document下的devicetree下的设计文档。

    在这里插入图片描述
    1、修改设备树,添加IO相关信息
    ECSPI3_SCLK -> UART2_RXD
    ECSPI3_MOSI -> UART2_CTS
    ECSPI3_SS0 -> UART2_TXD
    ECSPI3_MISO -> UART2_RTS

    片选信号不作为硬件片选,而是作为普通的GPIO,我们在程序里面自行控制片选引脚。

    2、在ECSPI3节点下创建icm20608子节点

    3、需要初始化icm20608芯片、然后从里面读取原始数据!这个过程就要用到如何使用linux内的SPI驱动API来读写ICM20608
    用到两个重要的结构体:spi_transfer和spi_message
    spi_transfer用来构建收发数据内容。

    构建spi_transfer,然后将其打包到spi_message里面,需要使用spi_message_init初始化spi_message,然后在使用spi_message_add_tail将spi_transfer添加到spi_message里面,最终使用spi_sync和spi_async来发送。

    驱动编写

    获取设备树的父节点:
    在这里插入图片描述
    在这里插入图片描述

    SPI驱动代码

    #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>
    #include "icm20608.h"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    设备结构体

    #define ICM20608_CNT 	1
    #define ICM20608_NAME	"icm20608"
    
    /*设备结构体*/
    struct icm20608_dev {
    	
    	struct cdev cdev;
    	struct class *class;/*类:为了自动创建节点*/
    	struct device *device;/*设备:为了自动创建节点*/
    	dev_t devid; //设备号
    	int major;   //主设备号
    	int minor;   //次设备号
    
    	void *private_data;
    
    	int cs_gpio;
    	struct device_node *nd;
    };
    
    struct icm20608_dev icm20608;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    file_operation操作函数

    static int icm20608_open(struct inode *inode, struct file *filp)
    {
    	/*设置私有数据*/
    	//filp->private_data = &icm20608;
    	return 0;
    
    }
    
    static ssize_t icm20608_read(struct file *filp, __user char *buf, size_t count,loff_t *ppos)
    {
    	
    	return 0;
    }
    
    static int icm20608_release(struct inode *inode, struct file *filp)
    {
    	//struct icm20608_dev *dev = (struct icm20608_dev *)filp->private_data;
    	return 0;
    }
    
    
    
    /**
     * 字符设备的操作集合
    */
    const struct file_operations icm20608_fops = {
    	.owner = THIS_MODULE,
    	.open = icm20608_open,
    	.read = icm20608_read,
    	.release = icm20608_release,
    };
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    读写寄存器

    
    /*spi读寄存器*/
    static int icm20608_read_regs(struct icm20608_dev *dev,u8 reg,void *buf,int len)
    {
    	int ret = 0;
    	unsigned char txdata[len];
    	struct spi_message m;
    	struct spi_transfer *t;
    	struct spi_device *spi = (struct spi_device*)dev->private_data;
    
    	//片选拉低
    	gpio_set_value(dev->cs_gpio,0);
    	//构建spitransfer
    	t= kzalloc(sizeof(struct spi_transfer),GFP_KERNEL);
    
    	/*第一步:发送要读取的寄存器的地址*/
    	txdata[0] = reg|0x80;//读取寄存器地址的话,寄存器地址最高位要置1
    	t->tx_buf = txdata;
    	t->len = 1;
    
    	spi_message_init(&m);
    	spi_message_add_tail(t,&m);
    	ret = spi_sync(spi,&m);
    
    	//第二步 读取数据
    	txdata[0] = 0xff; //无效的
    	t->rx_buf = buf;
    	t->len = len;
    
    	spi_message_init(&m);
    	spi_message_add_tail(t,&m);
    	ret = spi_sync(spi,&m);
    
    	//释放内存
    	kfree(t);
    
    	gpio_set_value(dev->cs_gpio,1);
    	return ret;
    
    }
    
    
    /*spi写寄存器*/
    static int icm20608_write_regs(struct icm20608_dev *dev,u8 reg,u8 *buf,int len)
    {
    	int ret = 0;
    	unsigned char txdata[len];
    	struct spi_message m;
    	struct spi_transfer *t;
    	struct spi_device *spi = (struct spi_device*)dev->private_data;
    
    	//片选拉低
    	gpio_set_value(dev->cs_gpio,0);
    	//构建spitransfer
    	t = kzalloc(sizeof(struct spi_transfer),GFP_KERNEL);
    
    	/*第一步:发送要读取的寄存器的地址*/
    	txdata[0] = reg&~0x80;//读取寄存器地址的话,寄存器地址最高位要置1
    	t->tx_buf = txdata;
    	t->len = 1;
    
    	spi_message_init(&m);
    	spi_message_add_tail(t,&m);
    	ret = spi_sync(spi,&m);
    
    	//第二步 读取数据
    	t->rx_buf = buf;
    	t->len = len;
    
    	spi_message_init(&m);
    	spi_message_add_tail(t,&m);
    	ret = spi_sync(spi,&m);
    
    	//释放内存
    	kfree(t);
    
    	gpio_set_value(dev->cs_gpio,1);
    
    	return ret;
    }
    
    
     /*
    * @description : 读取 icm20608 指定寄存器值,读取一个寄存器
    * @param – dev : icm20608 设备
    * @param – reg : 要读取的寄存器
    * @return : 读取到的寄存器值
    */
    static unsigned char icm20608_read_onereg(struct icm20608_dev *dev,u8 reg)
    {
    	u8 data = 0;
    
    	icm20608_read_regs(dev, reg, &data, 1);
    
    	return data;
    }
    
    
    /*
    * @description : 向 icm20608 指定寄存器写入指定的值,写一个寄存器
    * @param – dev : icm20608 设备
    * @param – reg : 要写的寄存器
    * @param – data : 要写入的值
    * @return : 无
    */
    
    static void icm20608_write_onereg(struct icm20608_dev *dev, u8 reg,u8 value)
    {
    	u8 buf = value;
    	icm20608_write_regs(dev, reg, &buf, 1);
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113

    ICM20608初始化

    
    void icm20608reg_init(void)
    {
    	u8 value = 0;
    
    	icm20608_write_onereg(&icm20608, ICM20_PWR_MGMT_1, 0x80);
    	mdelay(50);
    	icm20608_write_onereg(&icm20608, ICM20_PWR_MGMT_1, 0x01);
    	mdelay(50);
    
    	value = icm20608_read_onereg(&icm20608, ICM20_WHO_AM_I);
    	printk("ICM20608 ID = %#X\r\n", value);
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    匹配驱动成功后的probe函数

    static int icm20608_probe(struct spi_device *spi)
    {
    	int ret = 0;
    	/*搭建字符设备驱动框架*/
    	/*2、注册字符设备*/
    	printk("icm20608_init ok\r\n");
    
    	icm20608.major = 0;//设置为0,表示由系统申请设备号
    	if(icm20608.major)   //给定主设备号
    	{
    		icm20608.devid = MKDEV(icm20608.major,0);	
    		ret = register_chrdev_region(icm20608.devid,ICM20608_CNT,ICM20608_NAME);
    	}else{
    		ret = alloc_chrdev_region(&icm20608.devid,0,ICM20608_CNT,ICM20608_NAME);
    		icm20608.major = MAJOR(icm20608.devid);
    		icm20608.minor = MINOR(icm20608.devid);
    	}
    	if(ret < 0){
    		printk("icm20608 chrdev_region err\r\n");
    		return -1;
    	}
    
    	printk("icm20608 majorid = %d,minorid = %d\r\n",icm20608.major,icm20608.minor);
    
    	/*3、注册字符设备*/
    	icm20608.cdev.owner = THIS_MODULE;
    
    	cdev_init(&icm20608.cdev, &icm20608_fops);
    
    	cdev_add(&icm20608.cdev,icm20608.devid,ICM20608_CNT);
    	
    	/* 4、自动创建设备节点 */
    	icm20608.class = class_create(THIS_MODULE, ICM20608_NAME);
    	if (IS_ERR(icm20608.class)) {
    		return PTR_ERR(icm20608.class);
    	}
    
    	/* 5、创建设备 */
    	icm20608.device = device_create(icm20608.class, NULL, icm20608.devid, NULL, ICM20608_NAME);
    	if (IS_ERR(icm20608.device)) {
    		return PTR_ERR(icm20608.device);
    	}
    
    
    
    	//获取软件片选引脚
    	icm20608.nd = of_get_parent(spi->dev.of_node);
    	
    	icm20608.cs_gpio = of_get_named_gpio(icm20608.nd,"cs-gpio",0);
    	if (icm20608.cs_gpio<0)
    	{
    		printk("can't get gpio\r\n");
    		return -1;
    	}
    	
    	ret =  gpio_request(icm20608.cs_gpio,"cs");
    	if (ret <0)
    	{
    		printk("gpio_request failed\n");
    		return -1;
    	}
    	
    	ret = gpio_direction_output(icm20608.cs_gpio,1);//默认高电平
    
    
    	/*初始化spi_device*/
    	spi->mode = SPI_MODE_0;
    	spi_setup(spi);
    
    
    	icm20608.private_data = spi; //设置私有数据为spi
    
    	/*初始化icm20608*/
    	icm20608reg_init();
    
    	return 0;
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    spi_driver 初始化

    static int icm20608_remove(struct spi_device *spi)
    {
    
    	/*删除字符设备*/
    	cdev_del(&icm20608.cdev);
    
    	/*注销设备号*/
    	unregister_chrdev_region(icm20608.devid,ICM20608_CNT);
    
    	/*先摧毁设备再摧毁类*/
    	device_destroy(icm20608.class,icm20608.devid);
    
    	/*摧毁类*/
    	class_destroy(icm20608.class);
    
    	/*释放片选*/
    	gpio_free(icm20608.cs_gpio);
    
    	return 0;
    
    }
    
    /*传统的匹配表*/
    static struct spi_device_id icm20608_id[] ={
    	{"alientek,icm20608",0},
    	{}
    };
    
    /*设备树匹配表*/
    static struct of_device_id icm20608_of_match[] ={
    	{.compatible = "alientek,icm20608"},
    	{}
    
    };
    
    
    /*spi driver*/
    
    static struct spi_driver icm20608_driver = {
    	.probe = icm20608_probe,
    	.remove = icm20608_remove,
    	.driver = {
    		.name = "icm20608",
    		.owner = THIS_MODULE,
    		.of_match_table = of_match_ptr(icm20608_of_match),
    
    	},
    	.id_table = icm20608_id,//传统的设备匹配!!!
    
    };
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    驱动加载与卸载

    static int __init icm20608_init(void)
    {
    	int ret;
    
    	ret = spi_register_driver(&icm20608_driver);
    
    	return ret;
    }
    
    static void __exit icm20608_exit(void)
    {
    	spi_unregister_driver(&icm20608_driver);
    }
    
    //模块加载函数
    module_init(icm20608_init);
    
    //模块卸载
    module_exit(icm20608_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("qhy");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
  • 相关阅读:
    再谈exception——异常抛出时会发生什么?
    (71)MIPI DSI LLP介绍(十一)
    Java中的Map接口(Hashtable+LinkedHashMap)[77]
    双非本计算机从零开始三年努力能做到什么程度【学习路线回顾&总结&问答】
    App线上网络问题优化策略
    Abnova丨CMV CISH 探头解决方案
    C Primer Plus(6) 中文版 第1章 初识C语言 1.1 C语言的起源 1.2 选择C语言的理由 1.3 C语言的应用范围
    低代码与国产化部署:软件开发的未来趋势与应用实践
    Spring3.2.3+Quartz2.2.1 整合配置
    Kafka Stream 学习笔记-3 DSL‘s stateless stateful
  • 原文地址:https://blog.csdn.net/m0_46152793/article/details/125343465