当前位置:   article > 正文

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

正点原子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
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/205725
推荐阅读
相关标签
  

闽ICP备14008679号