46.Linux SPI 驱动
整个框架与I2C十分类似,
不同的是数据通信那部分
实战
修改设备树
c
&ecspi3 {
fsl,spi-num-chipselects = <1>; //一个片选
cs-gpio = <&gpio1 20 GPIO_ACTIVE_LOW>; //如果是cs-gpios则时硬件片选,这里修改为cs-gpio,使用自己的软件控制,低电平有效
pinctrl-names = "default"; //
pinctrl-0 = <&pinctrl_ecspi3>;
status = "okay";
//对应的spi子节点
spidev: icm20608@0 { //接到了spi3 ss0 第0号片选上,我们没有使用到,无效
compatible = "alientek,icm20608";
spi-max-frequency = <8000000>; //spi速率(时钟频率) 8Mhz
reg = <0>;
};
};
...
pinctrl_ecspi3:icm20608 {
fsl,pin = <
MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x10b1 /* MISO*/
MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x10b1 /* MOSI*/
MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x10b1 /* CLK*/
MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x10b0 /* CS,作为普通的GPIO,使用软件片选*/
>;
};
注意屏蔽其他引脚,复用、gpio
编译 复制 上电
c
ls /sys/bus/spi/devices/
spi2.0
spi2.0是在linux下的命名,对应节点 spi3 0号片选
编写驱动
框架:
c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/spi/spi.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define icm20608_CNT 1
#define icm20608_NAME "icm20608"
struct icm20608_dev
{
dev_t devid;
int MAJOR;
int MINOR;
struct cdev cdev;
struct class *class;
struct device *device;
void *private_data;
};
struct icm20608_dev icm20608;
static int icm20608_open (struct inode *inode, struct file *file){
file->private_data = &icm20608;
printk("icm20608_open\r\n");
return 0;
}
static int icm20608_close(struct inode *inode, struct file *file){
//struct icm20608_dev *dev = file->private_data;
printk("icm20608_close\r\n");
return 0;
}
static ssize_t icm20608_read(struct file *file, char __user *buf, size_t size, loff_t *ppos){
//struct icm20608_dev *dev = file->private_data;
printk("icm20608_read\r\n");
//读取icm20608的数据
return 0;
}
const struct file_operations icm20608_opts={
.owner = THIS_MODULE,
.open = icm20608_open,
.release = icm20608_close,
.read = icm20608_read,
};
static int icm20608_probe(struct spi_device *spi){
printk("icm20608_probe\r\n");
//搭建字符设备驱动框架
int ret = 0;
//注册设备号
icm20608.MAJOR = 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 error\r\n");
goto fail_devid;
}
//注册设备
icm20608.cdev.owner = THIS_MODULE;
cdev_init(&icm20608.cdev,&icm20608_opts);
ret = cdev_add(&icm20608.cdev, icm20608.devid, icm20608_CNT);
if (ret < 0 )
{
goto fail_cdev;
}
//自动创建设备节点
icm20608.class = class_create(THIS_MODULE,icm20608_NAME);
if (IS_ERR(icm20608.class))
{
ret = PTR_ERR(icm20608.class);
goto fail_class;
}
icm20608.device = device_create(icm20608.class,NULL,icm20608.devid,NULL,icm20608_NAME);
if (IS_ERR(icm20608.device ))
{
ret = PTR_ERR(icm20608.device);
goto fail_device;
}
return 0;
fail_device:
class_destroy(icm20608.class);
fail_class:
cdev_del(&icm20608.cdev);
fail_cdev:
unregister_chrdev_region(icm20608.devid,icm20608_CNT);
fail_devid:
return ret;
}
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);
return 0;
}
//设备树匹配
const struct of_device_id icm20608_of_match[]= {
{ .compatible = "alientek,icm20608"},
{},
};
//传统匹配表
const struct spi_device_id icm20608_id[] = {
{"alientek,icm20608",0},
{},
};
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,
};
static int __init icm20608_init(void){
int ret = 0;
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");
对于spi的协议传输
需要用到两个结构体:spi_message 和 spi_transfer
使用方法:
c
/* SPI 多字节发送 */
static int spi_send(struct spi_device *spi, u8 *buf, int len)
{
int ret;
struct spi_message m;
struct spi_transfer t = {
.tx_buf = buf,
.len = len,
};
spi_message_init(&m); /* 初始化 spi_message */
spi_message_add_tail(t, &m);/* 将 spi_transfer 添加到 spi_message 队列 */
ret = spi_sync(spi, &m); /* 同步传输 */
return ret;
}
/* SPI 多字节接收 */
static int spi_receive(struct spi_device *spi, u8 *buf, int len)
{
int ret;
struct spi_message m;
struct spi_transfer t = {
.rx_buf = buf,
.len = len,
};
spi_message_init(&m); /* 初始化 spi_message */
spi_message_add_tail(t, &m);/* 将 spi_transfer 添加到 spi_message 队列 */
ret = spi_sync(spi, &m); /* 同步传输 */
return ret;
}
构建spi_transfer,然后将其打包到spi_message里面,需要使用spi_message_init初始
化spi_message,然后在使用spi_message_add_tail将spi_transfer添加到spi_message里面,
最终使用spi_sync 和spi_async 来发送。
获取父节点
值得注意的时,spi_driver自带了device_driver结构体 其指向的of_node,表示设备节点,即带有compatible = "alientek,icm20608";属性的节点,我们需要控制spi的pinctl就需要获取其父节点ecspi3,从而控制pinctrl_ecspi3中的MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20
c
&ecspi3 {
fsl,spi-num-chipselects = <1>; //一个片选
cs-gpio = <&gpio1 20 GPIO_ACTIVE_LOW>; //如果是cs-gpios则时硬件片选,这里修改为cs-gpio,使用自己的软件控制,低电平有效
pinctrl-names = "default"; //
pinctrl-0 = <&pinctrl_ecspi3>;
status = "okay";
//对应的spi子节点
spidev: icm20608@0 { //接到了spi3 ss0 第0号片选上,我们没有使用到,无效
compatible = "alientek,icm20608";
spi-max-frequency = <8000000>; //spi速率(时钟频率) 8Mhz
reg = <0>;
};
};
c
pinctrl_ecspi3:icm20608 {
fsl,pin = <
MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x10b1 /* MISO*/
MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x10b1 /* MOSI*/
MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x10b1 /* CLK*/
MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x10b0 /* CS,作为普通的GPIO,使用软件片选*/
>;
};
控制该节点的代码:
c
//获取父节点
icm20608.nd= of_get_parent(spi->dev.of_node);
icm20608.cs_gpio = of_get_named_gpio(icm20608.nd,"cs-gpio",3);
完善驱动1
c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "icm_20608_reg.h"
#define icm20608_CNT 1
#define icm20608_NAME "icm20608"
struct icm20608_dev
{
dev_t devid;
int MAJOR;
int MINOR;
struct cdev cdev;
struct class *class;
struct device *device;
void *private_data;
int cs_gpio;
struct device_node *nd;
};
struct icm20608_dev icm20608;
/*
* @description : 从icm20608读取多个寄存器数据
* @param - dev: icm20608设备
* @param - reg: 要读取的寄存器首地址
* @param - val: 读取到的数据
* @param - len: 要读取的数据长度
* @return : 操作结果
*/
//spi读寄存器
static int icm20608_read_regs(struct icm20608_dev *dev , u8 reg , void *buf , int len){
int ret= 0 ;
int value = 0;
unsigned char txdata[len];
struct spi_message m;
struct spi_transfer *t;
struct spi_device *spi = dev->private_data;
//拉低片选
gpio_set_value(dev->cs_gpio,0);
t = kzalloc(sizeof(struct spi_transfer),GFP_KERNEL); //向内核申请内存
//第一步、发送要读取的地址
txdata[0] = reg | 0x80 ; //读取时最高位为1 即1000 0000
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;
int value = 0;
unsigned char txdata[len];
struct spi_message m;
struct spi_transfer *t;
struct spi_device *spi = dev->private_data;
//拉低片选
gpio_set_value(dev->cs_gpio,0);
t = kzalloc(sizeof(struct spi_transfer),GFP_KERNEL); //向内核申请内存
//第一步、发送要写的地址
txdata[0] = reg &~ 0x80 ; //读取时最高位为0 即0XXX XXXX
t->tx_buf = txdata;
t->len = 1;
spi_message_init(&m);
spi_message_add_tail(t,&m);
ret = spi_sync(spi,&m);
//第二部,读取数据
t->tx_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;
}
//读取单个寄存器
static unsigned char icm20608_read_Onereg(struct icm20608_dev *dev , u8 reg){
u8 data = 0;
icm20608_read_regs(dev,reg,&data,1);
return data;
}
//写入单个寄存器
static void icm20608_write_Onereg(struct icm20608_dev *dev , u8 reg , u8 value){
u8 buf = value;
icm20608_write_regs(dev,reg,&buf,1);
}
//初始化icm20608
void driver_icm20608_init(struct icm20608_dev *dev){
u8 value = 0;
//从睡眠模式唤醒
icm20608_write_Onereg(dev,ICM20_PWR_MGMT_1,0x80); //复位,睡眠模式
mdelay(50);
icm20608_write_Onereg(dev,ICM20_PWR_MGMT_1,0x01); //关闭睡眠模式
mdelay(50);
value = icm20608_read_Onereg(dev,ICM20_WHO_AM_I);
printk("icm20608 reg = %x\r\n",value);
}
static int icm20608_open (struct inode *inode, struct file *file){
file->private_data = &icm20608;
printk("icm20608_open\r\n");
return 0;
}
static int icm20608_close(struct inode *inode, struct file *file){
//struct icm20608_dev *dev = file->private_data;
printk("icm20608_close\r\n");
return 0;
}
static ssize_t icm20608_read(struct file *file, char __user *buf, size_t size, loff_t *ppos){
//struct icm20608_dev *dev = file->private_data;
printk("icm20608_read\r\n");
//读取icm20608的数据
return 0;
}
const struct file_operations icm20608_opts={
.owner = THIS_MODULE,
.open = icm20608_open,
.release = icm20608_close,
.read = icm20608_read,
};
static int icm20608_probe(struct spi_device *spi){
int ret = 0;
printk("icm20608_probe\r\n");
//搭建字符设备驱动框架
//注册设备号
icm20608.MAJOR = 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 error\r\n");
goto fail_devid;
}
//注册设备
icm20608.cdev.owner = THIS_MODULE;
cdev_init(&icm20608.cdev,&icm20608_opts);
ret = cdev_add(&icm20608.cdev, icm20608.devid, icm20608_CNT);
if (ret < 0 )
{
goto fail_cdev;
}
//自动创建设备节点
icm20608.class = class_create(THIS_MODULE,icm20608_NAME);
if (IS_ERR(icm20608.class))
{
ret = PTR_ERR(icm20608.class);
goto fail_class;
}
icm20608.device = device_create(icm20608.class,NULL,icm20608.devid,NULL,icm20608_NAME);
if (IS_ERR(icm20608.device ))
{
ret = PTR_ERR(icm20608.device);
goto fail_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 find gpio \r\n");
goto fail_gpio;
}
ret = gpio_request(icm20608.cs_gpio,"CS");
if (ret<0)
{
printk("failed request gpio !\r\n");
goto fail_gpio;
}
printk("request gpio %d!\r\n",icm20608.cs_gpio);
ret = gpio_direction_output(icm20608.cs_gpio,1); //默认高电平
//初始化spi,设置spi的模式
spi->mode = SPI_MODE_0; /*MODE0, CPOL=0, CPHA=0*/
spi_setup(spi);
//设置私有数据
icm20608.private_data = spi;
//初始化icm20608芯片
driver_icm20608_init(&icm20608);
return 0;
fail_gpio:
fail_device:
class_destroy(icm20608.class);
fail_class:
cdev_del(&icm20608.cdev);
fail_cdev:
unregister_chrdev_region(icm20608.devid,icm20608_CNT);
fail_devid:
return ret;
}
static int icm20608_remove(struct spi_device *spi){
cdev_del(&icm20608.cdev);
gpio_free(icm20608.cs_gpio);
unregister_chrdev_region(icm20608.devid,icm20608_CNT);
device_destroy(icm20608.class,icm20608.devid);
class_destroy(icm20608.class);
return 0;
}
//设备树匹配
const struct of_device_id icm20608_of_match[]= {
{ .compatible = "alientek,icm20608"},
{},
};
//传统匹配表
const struct spi_device_id icm20608_id[] = {
{"alientek,icm20608",0},
{},
};
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,
};
static int __init icm20608_init(void){
int ret = 0;
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");
这里有一个坑
c
/lib/modules/4.1.15 # modprobe icm20608.ko
icm20608_probe
request gpio 20!
icm20608 reg = 0x0
读取出来是0 ,有可能不是代码的问题
回到设备树试试将所有uart2的pinctl给屏蔽了
或者&uart2的状态改为disabled
再不行试试将例程源码复制,将设备树中的cs-gpio 改为 cs-gpios(由spi硬件直接控制,不需要我们手动控制),再试试。
两个函数
spi_write
c
static inline int
spi_write(struct spi_device *spi, const void *buf, size_t len)
{
struct spi_transfer t = {
.tx_buf = buf,
.len = len,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spi_sync(spi, &m);
}
spi_read
c
static inline int
spi_read(struct spi_device *spi, void *buf, size_t len)
{
struct spi_transfer t = {
.rx_buf = buf,
.len = len,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spi_sync(spi, &m);
}
可以使用内核操作spi的这两个函数来实现更简介的代码
c
static int icm20608_read_regs(struct icm20608_dev *dev , u8 reg , void *buf , int len){
struct spi_device *spi = dev->private_data;
u8 data =0;
//拉低片选
gpio_set_value(dev->cs_gpio,0);
data = reg | 0x80;
spi_write(spi,&data,1);//发送要读取的寄存器地址
spi_read(spi,buf,len);
//拉高片选
gpio_set_value(dev->cs_gpio,1);
}
static int icm20608_write_regs(struct icm20608_dev *dev , u8 reg, u8 *buf , int len){
struct spi_device *spi = dev->private_data;
u8 data = 0;
//拉低片选
gpio_set_value(dev->cs_gpio,0);
data = reg &~ 0x80;
spi_write(spi,&data,1);//发送要读取的寄存器地址
spi_write(spi,buf,len);//发送要读取的寄存器地址
//拉高片选
gpio_set_value(dev->cs_gpio,1);
}
NXP将SPI的cs片选信号作为软件片选,只需要指定gpio为cs-gpios即可。
c
&ecspi3 {
fsl,spi-num-chipselects = <1>; //一个片选
cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>; //如果是cs-gpios则时硬件片选,这里修改为cs-gpio,使用自己的软件控制,低电平有效
pinctrl-names = "default"; //
pinctrl-0 = <&pinctrl_ecspi3>;
status = "okay";
//对应的spi子节点
spidev: icm20608@0 { //接到了spi3 ss0 第0号片选上,我们没有使用到,无效
compatible = "alientek,icm20608";
spi-max-frequency = <8000000>; //spi速率(时钟频率) 8Mhz
reg = <0>;
};
};
pinctrl_ecspi3: ecspi3grp {
fsl,pins = <
MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x10b0
MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x10b1 /* MISO*/
MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x10b1 /* MOSI*/
MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x10b1 /* CLK*/
>;
};
但注意
c
u8 data =0;
data = reg | 0x80;
//前面不需要手动设置gpio的cs拉低
spi_write(spi,&data,1);//发送要读取的寄存器地址
spi_read(spi,buf,len);
//后面不需要手动设置gpio的cs拉高

spi_write为一次操作,其软件片选会在结束时拉高,这不符合spi的时序

使用spi_read_then_write函数即可解决。
c
u8 data =0;
data = reg | 0x80;
//使用一个函数完成
spi_write_then_read(spi,&data,1,buf,len);
c
static int icm20608_read_regs(struct icm20608_dev *dev , u8 reg , void *buf , int len){
struct spi_device *spi = dev->private_data;
u8 data =0;
data = reg | 0x80;
// spi_write(spi,&data,1);//发送要读取的寄存器地址
// spi_read(spi,buf,len);
//使用一个函数完成
spi_write_then_read(spi,&data,1,buf,len);
return 0;
}
c
static int icm20608_write_regs(struct icm20608_dev *dev , u8 reg, u8 *buf , int len){
struct spi_device *spi = dev->private_data;
u8 *txdata;
txdata = kzalloc(len+1,GFP_KERNEL);
txdata[0] = reg &~ 0x80;
memcpy(&txdata[1],buf,len);
spi_write(spi,txdata,len+1);
kfree(txdata);
return 0;
}
c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "icm_20608_reg.h"
#define icm20608_CNT 1
#define icm20608_NAME "icm20608"
struct icm20608_dev
{
dev_t devid;
int MAJOR;
int MINOR;
struct cdev cdev;
struct class *class;
struct device *device;
void *private_data;
int cs_gpio;
struct device_node *nd;
signed int gyro_x_adc; /* 陀螺仪X轴原始值 */
signed int gyro_y_adc; /* 陀螺仪Y轴原始值 */
signed int gyro_z_adc; /* 陀螺仪Z轴原始值 */
signed int accel_x_adc; /* 加速度计X轴原始值 */
signed int accel_y_adc; /* 加速度计Y轴原始值 */
signed int accel_z_adc; /* 加速度计Z轴原始值 */
signed int temp_adc; /* 温度原始值 */
};
struct icm20608_dev icm20608;
/*
* @description : 从icm20608读取多个寄存器数据
* @param - dev: icm20608设备
* @param - reg: 要读取的寄存器首地址
* @param - val: 读取到的数据
* @param - len: 要读取的数据长度
* @return : 操作结果
*/
//spi读寄存器
/* static int icm20608_read_regs(struct icm20608_dev *dev , u8 reg , void *buf , int len){
int ret= 0 ;
int value = 0;
unsigned char txdata[len];
struct spi_message m;
struct spi_transfer *t;
struct spi_device *spi = dev->private_data;
t = kzalloc(sizeof(struct spi_transfer),GFP_KERNEL); //向内核申请内存
//第一步、发送要读取的地址
txdata[0] = reg | 0x80 ; //读取时最高位为1 即1000 0000
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); //释放内核中申请的内存
return ret;
} */
static int icm20608_read_regs(struct icm20608_dev *dev , u8 reg , void *buf , int len){
struct spi_device *spi = dev->private_data;
u8 data =0;
data = reg | 0x80;
// spi_write(spi,&data,1);//发送要读取的寄存器地址
// spi_read(spi,buf,len);
//使用一个函数完成
spi_write_then_read(spi,&data,1,buf,len);
return 0;
}
//spi写寄存器
/* static int icm20608_write_regs(struct icm20608_dev *dev , u8 reg, u8 *buf , int len){
int ret= 0;
int value = 0;
unsigned char txdata[len];
struct spi_message m;
struct spi_transfer *t;
struct spi_device *spi = dev->private_data;
//拉低片选
gpio_set_value(dev->cs_gpio,0);
t = kzalloc(sizeof(struct spi_transfer),GFP_KERNEL); //向内核申请内存
spi_write()
//第一步、发送要写的地址
txdata[0] = reg &~ 0x80 ; //读取时最高位为0 即0XXX XXXX
t->tx_buf = txdata;
t->len = 1;
spi_message_init(&m);
spi_message_add_tail(t,&m);
ret = spi_sync(spi,&m);
//第二部,读取数据
t->tx_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);
value = gpio_get_value(dev->cs_gpio);
return ret;
} */
static int icm20608_write_regs(struct icm20608_dev *dev , u8 reg, u8 *buf , int len){
struct spi_device *spi = dev->private_data;
u8 *txdata;
txdata = kzalloc(len+1,GFP_KERNEL);
txdata[0] = reg &~ 0x80;
memcpy(&txdata[1],buf,len);
spi_write(spi,txdata,len+1);
kfree(txdata);
return 0;
}
//读取单个寄存器
static unsigned char icm20608_read_Onereg(struct icm20608_dev *dev , u8 reg){
u8 data = 0;
icm20608_read_regs(dev,reg,&data,1);
return data;
}
//写入单个寄存器
static void icm20608_write_Onereg(struct icm20608_dev *dev , u8 reg , u8 value){
u8 buf = value;
icm20608_write_regs(dev,reg,&buf,1);
}
/*
* @description : 读取ICM20608的数据,读取原始数据,包括三轴陀螺仪、
* : 三轴加速度计和内部温度。
* @param - dev : ICM20608设备
* @return : 无。
*/
void icm20608_readdata(struct icm20608_dev *dev)
{
unsigned char data[14] = { 0 };
icm20608_read_regs(dev, ICM20_ACCEL_XOUT_H, data, 14);
dev->accel_x_adc = (signed short)((data[0] << 8) | data[1]);
dev->accel_y_adc = (signed short)((data[2] << 8) | data[3]);
dev->accel_z_adc = (signed short)((data[4] << 8) | data[5]);
dev->temp_adc = (signed short)((data[6] << 8) | data[7]);
dev->gyro_x_adc = (signed short)((data[8] << 8) | data[9]);
dev->gyro_y_adc = (signed short)((data[10] << 8) | data[11]);
dev->gyro_z_adc = (signed short)((data[12] << 8) | data[13]);
}
//初始化icm20608
void driver_icm20608_init(struct icm20608_dev *dev){
u8 value = 0;
//从睡眠模式唤醒
icm20608_write_Onereg(dev,ICM20_PWR_MGMT_1,0x80); //复位,睡眠模式
mdelay(50);
icm20608_write_Onereg(dev,ICM20_PWR_MGMT_1,0x01); //关闭睡眠模式
mdelay(50);
value = icm20608_read_Onereg(dev,ICM20_WHO_AM_I);
printk("ICM20_WHO_AM_I reg = %#x\r\n",value);
value = icm20608_read_Onereg(dev,ICM20_PWR_MGMT_1);
printk("ICM20_PWR_MGMT_1 reg = %#x\r\n",value);
icm20608_write_Onereg(dev, ICM20_SMPLRT_DIV, 0x00); /* 输出速率是内部采样率 */
icm20608_write_Onereg(dev, ICM20_GYRO_CONFIG, 0x18); /* 陀螺仪±2000dps量程 */
icm20608_write_Onereg(dev, ICM20_ACCEL_CONFIG, 0x18); /* 加速度计±16G量程 */
icm20608_write_Onereg(dev, ICM20_CONFIG, 0x04); /* 陀螺仪低通滤波BW=20Hz */
icm20608_write_Onereg(dev, ICM20_ACCEL_CONFIG2, 0x04); /* 加速度计低通滤波BW=21.2Hz */
icm20608_write_Onereg(dev, ICM20_PWR_MGMT_2, 0x00); /* 打开加速度计和陀螺仪所有轴 */
icm20608_write_Onereg(dev, ICM20_LP_MODE_CFG, 0x00); /* 关闭低功耗 */
icm20608_write_Onereg(dev, ICM20_FIFO_EN, 0x00); /* 关闭FIFO */
}
static int icm20608_open (struct inode *inode, struct file *file){
file->private_data = &icm20608;
return 0;
}
static int icm20608_close(struct inode *inode, struct file *file){
return 0;
}
static ssize_t icm20608_read(struct file *file, char __user *buf, size_t size, loff_t *ppos){
signed int data[7];
long err = 0;
struct icm20608_dev *dev = file->private_data;
//读取icm20608的数据
icm20608_readdata(dev);
data[0] = dev->gyro_x_adc;
data[1] = dev->gyro_y_adc;
data[2] = dev->gyro_z_adc;
data[3] = dev->accel_x_adc;
data[4] = dev->accel_y_adc;
data[5] = dev->accel_z_adc;
data[6] = dev->temp_adc;
err = copy_to_user(buf, data, sizeof(data));
return 0;
return 0;
}
const struct file_operations icm20608_opts={
.owner = THIS_MODULE,
.open = icm20608_open,
.release = icm20608_close,
.read = icm20608_read,
};
static int icm20608_probe(struct spi_device *spi){
int ret = 0;
printk("icm20608_probe\r\n");
//搭建字符设备驱动框架
//注册设备号
icm20608.MAJOR = 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 error\r\n");
goto fail_devid;
}
//注册设备
icm20608.cdev.owner = THIS_MODULE;
cdev_init(&icm20608.cdev,&icm20608_opts);
ret = cdev_add(&icm20608.cdev, icm20608.devid, icm20608_CNT);
if (ret < 0 )
{
goto fail_cdev;
}
//自动创建设备节点
icm20608.class = class_create(THIS_MODULE,icm20608_NAME);
if (IS_ERR(icm20608.class))
{
ret = PTR_ERR(icm20608.class);
goto fail_class;
}
icm20608.device = device_create(icm20608.class,NULL,icm20608.devid,NULL,icm20608_NAME);
if (IS_ERR(icm20608.device ))
{
ret = PTR_ERR(icm20608.device);
goto fail_device;
}
//初始化spi,设置spi的模式
spi->mode = SPI_MODE_0; /*MODE0, CPOL=0, CPHA=0*/
spi_setup(spi);
//设置私有数据
icm20608.private_data = spi;
//初始化icm20608芯片
driver_icm20608_init(&icm20608);
return 0;
fail_gpio:
fail_device:
class_destroy(icm20608.class);
fail_class:
cdev_del(&icm20608.cdev);
fail_cdev:
unregister_chrdev_region(icm20608.devid,icm20608_CNT);
fail_devid:
return ret;
}
static int icm20608_remove(struct spi_device *spi){
cdev_del(&icm20608.cdev);
gpio_free(icm20608.cs_gpio);
unregister_chrdev_region(icm20608.devid,icm20608_CNT);
device_destroy(icm20608.class,icm20608.devid);
class_destroy(icm20608.class);
return 0;
}
//设备树匹配
const struct of_device_id icm20608_of_match[]= {
{ .compatible = "alientek,icm20608"},
{},
};
//传统匹配表
const struct spi_device_id icm20608_id[] = {
{"alientek,icm20608",0},
{},
};
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,
};
static int __init icm20608_init(void){
int ret = 0;
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");
c
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : icm20608App.c
作者 : 左忠凯
版本 : V1.0
描述 : icm20608设备测试APP。
其他 : 无
使用方法 :./icm20608App /dev/icm20608
论坛 : www.openedv.com
日志 : 初版V1.0 2019/9/20 左忠凯创建
***************************************************************/
/*
* @description : main主程序
* @param - argc : argv数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
int fd;
char *filename;
signed int databuf[7];
unsigned char data[14];
signed int gyro_x_adc, gyro_y_adc, gyro_z_adc;
signed int accel_x_adc, accel_y_adc, accel_z_adc;
signed int temp_adc;
float gyro_x_act, gyro_y_act, gyro_z_act;
float accel_x_act, accel_y_act, accel_z_act;
float temp_act;
int ret = 0;
if (argc != 2) {
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if(fd < 0) {
printf("can't open file %s\r\n", filename);
return -1;
}
while (1) {
ret = read(fd, databuf, sizeof(databuf));
if(ret == 0) { /* 数据读取成功 */
gyro_x_adc = databuf[0];
gyro_y_adc = databuf[1];
gyro_z_adc = databuf[2];
accel_x_adc = databuf[3];
accel_y_adc = databuf[4];
accel_z_adc = databuf[5];
temp_adc = databuf[6];
/* 计算实际值 */
gyro_x_act = (float)(gyro_x_adc) / 16.4;
gyro_y_act = (float)(gyro_y_adc) / 16.4;
gyro_z_act = (float)(gyro_z_adc) / 16.4;
accel_x_act = (float)(accel_x_adc) / 2048;
accel_y_act = (float)(accel_y_adc) / 2048;
accel_z_act = (float)(accel_z_adc) / 2048;
temp_act = ((float)(temp_adc) - 25 ) / 326.8 + 25;
printf("\r\n原始值:\r\n");
printf("gx = %d, gy = %d, gz = %d\r\n", gyro_x_adc, gyro_y_adc, gyro_z_adc);
printf("ax = %d, ay = %d, az = %d\r\n", accel_x_adc, accel_y_adc, accel_z_adc);
printf("temp = %d\r\n", temp_adc);
printf("实际值:");
printf("act gx = %.2f°/S, act gy = %.2f°/S, act gz = %.2f°/S\r\n", gyro_x_act, gyro_y_act, gyro_z_act);
printf("act ax = %.2fg, act ay = %.2fg, act az = %.2fg\r\n", accel_x_act, accel_y_act, accel_z_act);
printf("act temp = %.2f°C\r\n", temp_act);
}
usleep(100000); /*100ms */
}
close(fd); /* 关闭文件 */
return 0;
}