赞
踩
主机控制器驱动:SOC的spi外设驱动,是半导体厂家编写号的,为spi-imx.c当spi控制器的设备和驱动匹配之后,spi_imx_probe函数就会执行。
SPI控制驱动核心就是spi_master的构建,spi_master
spi_master->transfer
spi_master-> transfer_one_message 6ULL主机控制器使用此函数,
设备驱动:具体的SPI芯片驱动
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来发送。
获取设备树的父节点:
#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"
#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;
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, };
/*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); }
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);
}
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; }
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,//传统的设备匹配!!! };
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");
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。