62 spi驱动

一、spi驱动框架

1、裸机spi驱动框架

首先编写 spi 控制器驱动,bsp_spi.c 和 bsp_spi.h。向外提供读写api:spich0_readwrite_bye

spi 具体芯片驱动:ICM20608

2、linux下的 spi 驱动框架

主机控制器驱动:soc 的 spi 外设驱动,此驱动是半导体原厂编写好的,为 spi-imx.c

当 spi 控制器的设备和驱动匹配以后, spi_imx_probe 函数就会执行,完善 imx6ul 的 spi控制器驱动。

spi控制器驱动的核心就是 struct spi_master 的构建, struct spi_master 里面就有如何通过 spi控制器 和 spi外设 进行通信的函数。此函数是原厂编写并提供给我们(spi_master->transfer 函数 或者 spi_master->transfer_one_message 函数)

在这里插入图片描述
看原理图外设,找设备树节点,根据节点的compatible属性值找对应的驱动文件。

struct spi_imx_data 结构体,nxp用来表示 spi 外设设备结构体。定义在 spi-imx.c 中。重点关注其成员结构体 struct bitbang,成员函数 tx 和 rx。
结构体 struct bitbang 重点关注其成员结构体 struct spi_master,成员函数 setup_transfer。

struct spi_imx_data -> struct bitbang -> struct spi_master -> setup_transfer
														   -> txrx_bufs

核心是 spi_master 结构体,
此结构体通过 spi_alloc_master 函数来申请,附带把 struct spi_imx_data 的内存空间一起申请了。
初始化,
注册, spi_register_master
注销, spi_unregister_master
释放结构体通过 spi_master_put 函数

分析 spi_imx_probe 函数,定义在 spi-imx.c
若设备树中使用属性名 cs-gpios ,表示默认使用硬件片选,本此实验使用软件片选。因此设备树中不能用这个属性名。

函数 spi_imx_setupxfer 中设置 struct spi_imx_data 的 tx,rx成员函数。

spi_imx->rx = spi_imx_buf_rx_u8; /*最终的spi接收函数*/
spi_imx->tx = spi_imx_buf_tx_u8; /*最终的spi发送函数*/

spi_imx->devtype_data->config(spi_imx, &config); 对应的函数时 mx51_ecspi_config,显然是配置 ecspi 即 6ull 的 spi控制器的寄存器。

由 spi_imx->devtype_data = of_id ? of_id->data : (struct spi_imx_devtype_data *) pdev->id_entry->driver_data; 设置而来。

接着看看 spi_imx->bitbang 下面的 成员函数 txrx_bufs ,即 spi_imx_transfer函数
spi_imx_transfer
-> spi_imx_pio_transfer
-> spi_imx_push
-> spi_imx->tx 来完成发送
没有接收是因为开发板上的spi接收是通过中断来完成的。

probe函数接下来会获取中断号,申请中断,注册中断处理函数(spi_imx_isr)。

spi_imx_isr
spi_imx->rx 来完成接收

dma 本次实验不使用

最终 spi_bitbang_start函数(注意bitbang结构体里面有个成员变量就是spi核心结构体spi_master)

spi_bitbang_start
-> 设置 master->transfer_one_message = spi_bitbang_transfer_one;
-> spi_register_master(向系统注册 spi_master 核心结构体)

下面再来看看 spi_bitbang_transfer_one 函数:里面通过 bitbang->txrx_bufs 来发送数据。上面由此函数的分析。最终还是调用 spi_imx->tx 即 spi_imx_buf_tx_u8。

总结:设置 spi_master 的 transfer_one_message 为 spi_bitbang_transfer_one

4、spi设备驱动

spi设备驱动就是具体的spi芯片驱动,比如 icm20608。

spi_device:定义在 spi.h ,此结构体下都有一个 struct spi_master。每个 spi 设备都挂载在一个 spi 控制器上。如 icm20608 挂载到了 6ull 的 ecspi3 接口上。
因为采用了设备树,不需要我们自己去创建 spi_device,在设备树里完成解析。

因此重点就是 spi_driver。类似于 i2c_driver。
申请或者定义一个 spi_driver;
初始化 spi_driver 中的各个成员变量;
初始化 spi_driver 成功以后需要向内核注册,使用函数:spi_register_driver
需要注销 spi_driver 时使用函数:spi_unregister_driver
当 spi 设备和驱动匹配以后,spi_driver 下的 probe 函数会执行。

二、驱动编写

1、修改设备树

1、添加 io 相关信息,参考原理图,使用 2,3,5,4 这四个 io。
在这里插入图片描述
整理如下:
ECSPI3_SS0 — UART2_TXD
ECSPI3_SCLK — UART2_RXD
ECSPI3_MOSI — UART2_CTS
ECSPI3_MISO — UART2_RTS

片选信号不作为硬件片选,而是作为普通的gpio,在程序中自行控制片选引脚。
添加 pinctrl 节点时要注意防止冲突(重复)

在 iomuxc 节点的子节点 imx6ul-evk 下 添加一个子节点:

pinctrl_ecspi3: ecspi3grp {
			fsl,pins = <
				/*pinxuanyingjiao caiyong ruanjiankongzhi,yinci cichu zuowei gpio*/
				MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x10b0 
				MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x10b1
				MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x10b1
				MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x10b1
			>;
};

2、在 ecspi3节点下追加一些属性,并在 ecspi3节点下创建 icm20608 子节点。参考绑定文档:fsl-imx-cspi.txt,获取属性和值的含义

&ecspi3 {
	/*pianxuan xinhao de shuliang wei 1*/
	fsl,spi-num-chipselects = <1>;
	/*zhiding de shi yingjian panxuan yinjiao*/
	/* cs-gpios = <&gpio4 9 0>;*/
	/*womenshiyong ruanjian kongzhi pianxuan,gaigemingzi
	  shezhi ruanjian pianxuan yinjiao, didianping youxiao*/
	cs-gpio = <&gpio1 20 GPIO_ACTIVE_LOW>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_ecspi3>;
	status = "okay";

	/* guazai zai ecspi3 shang de shebei zijiedian
	  cankao bangding wendang spi-bus.c 
	  @ houmian de 0 biaoshi ci spi xinpian jiedao nage yingjian pianxuan shang
	  0 jiushi jiedao pianxuan 0,ruoyouduoge shebei guazaizai ci kongzhiqi xia
	  xiayige pianxuandizhi yinggai wei 1,2,3 ...
	  dan benci shiyong de shi ruan jian pianxuan ,suoyi yongbudao '@0'*/
	spidev0: icm20608@0 {
		/*xiamian lianghang zuoyong yu ziji de zijiedian*/ 
		#address-cells = <1>;
		#size-cells = <1>;
		reg = <0>;
		compatible = "alientek,icm20608";
		/*spi shizhong pinlv*/
		spi-max-frequency = <8000000>;
	};
};

重启开发板,以新的设备树启动:

/sys/bus/spi # 
/sys/bus/spi # 
/sys/bus/spi # cd devices/spi2.0/
/sys/devices/platform/soc/2000000.aips-bus/2000000.spba-bus/2010000.ecspi/spi_master/spi2/spi2.0 # 
/sys/devices/platform/soc/2000000.aips-bus/2000000.spba-bus/2010000.ecspi/spi_master/spi2/spi2.0 # 
/sys/devices/platform/soc/2000000.aips-bus/2000000.spba-bus/2010000.ecspi/spi_master/spi2/spi2.0 # 
/sys/devices/platform/soc/2000000.aips-bus/2000000.spba-bus/2010000.ecspi/spi_master/spi2/spi2.0 # 
/sys/devices/platform/soc/2000000.aips-bus/2000000.spba-bus/2010000.ecspi/spi_master/spi2/spi2.0 # cd of_node/
/sys/firmware/devicetree/base/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000/icm20608@0 # 

如下列表中的属性与新设备树中的节点内容一致
/sys/firmware/devicetree/base/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000/icm20608@0 # ls
#address-cells     compatible         reg
#size-cells        name               spi-max-frequency
/sys/firmware/devicetree/base/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000/icm20608@0 # 
如下 02010000 与 dtsi 中 ecspi节点的 控制器地址 一致
/sys/firmware/devicetree/base/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000/icm20608@0 # 
/sys/firmware/devicetree/base/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000/icm20608@0 # cat name 
icm20608/sys/firmware/devicetree/base/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000/icm20608@0 # 
/sys/firmware/devicetree/base/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000/icm20608@0 # cat compatible 
alientek,icm20608/sys/firmware/devicetree/base/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000/icm20608@0 # 

3、需要初始化 icm20608芯片,然后从里面读取原始数据。
这个过程需要用到如何使用 linux kernel 的 spi驱动 api 来读取 icm20608 。
用到两个重要的结构体:spi_transferspi_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/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include <linux/fs.h>
#include<linux/slab.h>
#include<linux/io.h>
#include<linux/uaccess.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/of.h>
#include<linux/of_address.h>
#include<linux/of_irq.h>
#include<linux/gpio.h>
#include<linux/of_gpio.h>
#include<linux/atomic.h>
#include<linux/timer.h>
#include<linux/string.h>
#include<linux/jiffies.h>
#include<linux/irq.h>
#include<asm/mach/map.h>
#include<asm/uaccess.h>
#include<asm/io.h>
#include<linux/interrupt.h>

#include<linux/delay.h>
#include<linux/spi/spi.h>
#include"icm20608reg.h"


#define ICM20608_CNT 1
#define ICM20608_NAME    "icm20608"
#define USE_KERNEL_API

#ifdef USE_KERNEL_API

static inline int
spi_read(struct spi_device *spi, void *buf, size_t len);
static inline int
spi_write(struct spi_device *spi, const void *buf, size_t len);

#endif

//#define READ_USE_KERNEL_API

static int icm20608_probe(struct spi_device *spi);
static int icm20608_remove(struct spi_device *spi);
static int icm20608_open(struct inode *inode, struct file *filp);
static ssize_t icm20608_read(struct file *filp, 
                            char __user *buf, 
                            size_t cnt, 
                            loff_t *offt);
static int icm20608_release(struct inode *inode, struct file *filp);



/*chuantongde pipeibiao*/
static struct spi_device_id icm20608_id[] = {
    {"alientek,icm20608", 0}, /*0biaoshi shenme yisi?*/
    {},
};

/*shebeishu pipei */
static struct of_device_id icm20608_of_match[] = {
    {.compatible = "alientek,icm20608"},
    {},
};

/* i2c_driver jiegouti*/
static struct spi_driver icm20608_driver = {
    .probe = icm20608_probe,
    .remove = icm20608_remove,
    .driver = {
        .name = "icm20608",
        .owner = THIS_MODULE,
        .of_match_table = icm20608_of_match,
    },
    .id_table = icm20608_id,
};

static struct file_operations icm20608_fops = {
	.owner = THIS_MODULE,
	.open = icm20608_open,
	.read = icm20608_read,
	.release = 	icm20608_release,
};


/*zhuyi zheli meiyou static*/
struct icm20608_dev {
    int major;
    int minor;
    dev_t devid;
    struct cdev cdev;
    /*zhuyi zheliangge shi zhizhen*/
    struct class *class;
    struct device *device;
    /*gaoyige siyouzhizhen*/
    void *private_data;         /* 私有数据 		*/
    struct device_node *nd;
    int cs_gpio;                /* 片选所使用的GPIO编号		*/
    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;		/* 温度原始值 			*/
};

static struct icm20608_dev icm20608;

static int icm20608_read_regs(struct icm20608_dev *dev, 
                                u8 reg, 
                                u8 *buf, 
                                int len);
static int icm20608_write_regs(struct icm20608_dev *dev,
                                u8 reg,
                                u8 *buf,
                                int len);
static u8 icm20608_read_reg(struct icm20608_dev *dev, u8 reg);
static void icm20608_write_reg(struct icm20608_dev *dev,u8 reg, u8 val);
void icm20608_dev_init(struct icm20608_dev *dev);
void icm20608_readdata(struct icm20608_dev *dev);

static int icm20608_probe(struct spi_device *spi) {
    int ret = 0;
    printk("%s(%d):\n", __FILE__, __LINE__);

    /*you xitong lai fenpei zhushebeihao*/
    icm20608.major = 0;

    if (icm20608.major) {		/*  定义了设备号 */
		icm20608.devid = MKDEV(icm20608.major, 0);
		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("%s(%d):error\n", __FILE__, __LINE__);
        goto fail_devid;
    }
    
	printk("icm20608 major=%d,minor=%d\r\n",icm20608.major, icm20608.minor);	
	
	/* 2、初始化cdev */
	icm20608.cdev.owner = THIS_MODULE;
	cdev_init(&icm20608.cdev, &icm20608_fops);
	ret = cdev_add(&icm20608.cdev, icm20608.devid, ICM20608_CNT);
    if(ret < 0) {
        printk("%s(%d):error\n", __FILE__, __LINE__);
        goto fail_cdev;
    }

	/* 3、创建类 */
	icm20608.class = class_create(THIS_MODULE, ICM20608_NAME);
	if (IS_ERR(icm20608.class)) {
        ret = PTR_ERR(icm20608.class);
		printk("%s(%d):error\n", __FILE__, __LINE__);
        goto fail_class;
	}

	/* 5、创建设备 */
	icm20608.device = device_create(icm20608.class, NULL, icm20608.devid, NULL, ICM20608_NAME);
	if (IS_ERR(icm20608.device)) {
        ret = PTR_ERR(icm20608.device);
		printk("%s(%d):error\n", __FILE__, __LINE__);
        goto fail_device;
	}

    /* huoqu pianxuan yinjiao 
     * keyi cong benhanshu de chuanrucanshu huoqu
     * yaoqueding cijiedian duiying shebeishu zhong 
     * nage jiedian
     * yao youcijiedian huoqu qi fujiedian
     */
    icm20608.nd = of_get_parent(spi->dev.of_node);
    /*huouq pianxuan shuxing */
    icm20608.cs_gpio = of_get_named_gpio(icm20608.nd, "cs-gpio", 0);
    if(icm20608.cs_gpio < 0) {
        printk("%s(%d):error\n", __FILE__, __LINE__);
        ret = -1;
        goto fail_gpio;
    }
    /*shengqing gpio*/
    ret = gpio_request(icm20608.cs_gpio, "cs");/*yao ziji jiashang panduan chenggong yufou*/
    if(ret < 0) {
        printk("%s(%d):error\n", __FILE__, __LINE__);
    }
    /*moren gei ge gaodianping*/
    ret = gpio_direction_output(icm20608.cs_gpio, 1);
    if(ret < 0) {
        printk("%s(%d):error\n", __FILE__, __LINE__);
    }

    /*chushihua spi_device jiegouti*/
    spi->mode = SPI_MODE_0;//shezhi spi gongzuo moshi
    spi_setup(spi);

    /*shei zhi jiegouti de siyoushuju wei probe hanshu de chuanru canshu */    
    icm20608.private_data = spi;

    icm20608_dev_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) {
    printk("%s(%d):\n", __FILE__, __LINE__);
    /* 注销字符设备驱动,删除cdev */
	cdev_del(&icm20608.cdev);
	/* 注销设备号 */
	unregister_chrdev_region(icm20608.devid, ICM20608_CNT); 

	device_destroy(icm20608.class, icm20608.devid);
	class_destroy(icm20608.class);
    //shifang gpio
    gpio_free(icm20608.cs_gpio);
    return 0;
}

/*zai cihanshu zhong chushihua icm20608*/
static int icm20608_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &icm20608; /* 设置私有数据 */
	return 0;
}


/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t icm20608_read(struct file *filp, 
                            char __user *buf, 
                            size_t cnt, 
                            loff_t *offt)
{
    
    signed int data[7];
	long err = 0;
	struct icm20608_dev *dev = (struct icm20608_dev *)filp->private_data;

	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;
}

static int icm20608_release(struct inode *inode, struct file *filp)
{
    //struct icm20608_dev *dev = (struct icm20608_dev* )filp->private_data;
    printk("%s(%d):\n", __FILE__, __LINE__);
	return 0;
}

/* icm 20608 chushihua
 * ruhetongguo spi lai caozuo 
 * waibuspi xinpian
 * xuyao spi dujici qi he 
 * spi xiejicunqi caozuo hanshu 
 */

void icm20608_dev_init(struct icm20608_dev *dev) {
    u8 val = 0;
    icm20608_write_reg(dev, ICM20_PWR_MGMT_1, 0x80);		/* 复位,复位后为0x40,睡眠模式 			*/
	mdelay(50);
	icm20608_write_reg(dev, ICM20_PWR_MGMT_1, 0x01);		/* 关闭睡眠,自动选择时钟 					*/
	mdelay(50);

	val = icm20608_read_reg(dev, ICM20_WHO_AM_I);
	printk("icm20608 id = %#X\r\n", val);
	if(val != ICM20608G_ID && val != ICM20608D_ID){
        printk("%s(%d):error.\n", __FILE__, __LINE__);
    }
    val = icm20608_read_reg(dev, ICM20_PWR_MGMT_1);
    printk("ICM20_PWR_MGMT_1 = %#X\r\n", val);

    icm20608_write_reg(&icm20608, ICM20_SMPLRT_DIV, 0x00); 	/* 输出速率是内部采样率					*/
	icm20608_write_reg(&icm20608, ICM20_GYRO_CONFIG, 0x18); 	/* 陀螺仪±2000dps量程 				*/
	icm20608_write_reg(&icm20608, ICM20_ACCEL_CONFIG, 0x18); 	/* 加速度计±16G量程 					*/
	icm20608_write_reg(&icm20608, ICM20_CONFIG, 0x04); 		/* 陀螺仪低通滤波BW=20Hz 				*/
	icm20608_write_reg(&icm20608, ICM20_ACCEL_CONFIG2, 0x04); /* 加速度计低通滤波BW=21.2Hz 			*/
	icm20608_write_reg(&icm20608, ICM20_PWR_MGMT_2, 0x00); 	/* 打开加速度计和陀螺仪所有轴 				*/
	icm20608_write_reg(&icm20608, ICM20_LP_MODE_CFG, 0x00); 	/* 关闭低功耗 						*/
	icm20608_write_reg(&icm20608, ICM20_FIFO_EN, 0x00);		/* 关闭FIFO						*/
}	


#ifdef USE_KERNEL_API

static int icm20608_read_regs(struct icm20608_dev *dev, 
                                u8 reg, 
                                u8 *buf, 
                                int len) {
    u8 data = 0;
    int ret = 0;
    /*spi shi shebeishu jiexi erlai ,zainaliguanlian?*/
    struct spi_device *spi = dev->private_data;
    /*pianxuan ladi*/
    gpio_set_value(dev->cs_gpio, 0);
    
    //1 fasong yaoduqu de jicunqi dizhi,qie dizhi zuigaowei yao zhi 1
    data = reg | 0x80;
    spi_write(spi, &data, 1);
    //2
    ret = spi_read(spi, buf, len);
    /*pianxuan lagao*/
    gpio_set_value(dev->cs_gpio, 1);

    return ret;
}

static int icm20608_write_regs(struct icm20608_dev *dev,
                                u8 reg,
                                u8 *buf,
                                int len) {
    u8 data = 0;
    int ret = 0;
    struct spi_device *spi = dev->private_data;
    gpio_set_value(dev->cs_gpio, 0);

    data = reg & (~(1 << 15));
    ret = spi_write(spi, &data, 1); //jiarupanduan
    ret = spi_write(spi, buf, len); // jiarupanduan 
    gpio_set_value(dev->cs_gpio, 1);

    return ret;
}

#else

/* spi du jicunqi */
static int icm20608_read_regs(struct icm20608_dev *dev, 
                                u8 reg, 
                                u8 *buf, 
                                int len) {
    int ret = 0;
    u8 txdata[len];
    struct spi_message m;
    struct spi_transfer *t;
    struct spi_device *spi = (struct spi_device* )dev->private_data;

    /*pianxuan ladi, kaishicaozuo*/
    gpio_set_value(dev->cs_gpio, 0);

    /*goujian spi_transfer*/
    t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
    //1.fasong yaoduqudejicunqidedizhi
    txdata[0] = reg | 0x80;// yaodu zuigaowei bixu zhi 1?
    t->tx_buf = txdata;
    t->len = 1;
    //spi_message
    spi_message_init(&m);
    spi_message_add_tail(t, &m);
    //lingwai xiamiandiyigecanshu spi_device biaoshi shenme
    ret = spi_sync(spi, &m); //tongbufasong

    //2 duqushufu ,fangru buf
    txdata[0] = 0xff;
    // dudao de shuju fangdao buf limian
    t->rx_buf = buf;
    t->len = len;
    spi_message_init(&m);
    spi_message_add_tail(t, &m);
    ret = spi_sync(spi, &m);

    kfree(t);

    /*caozuowancheng, pianxuanlagao*/
    gpio_set_value(dev->cs_gpio, 1);
    return ret;
}

/* spi xie jicunqi */
static int icm20608_write_regs(struct icm20608_dev *dev,
                                u8 reg,
                                u8 *buf,
                                int len) {
    int ret = 0;
    u8 txdata[len];
    struct spi_message m;
    struct spi_transfer *t;
    struct spi_device *spi = (struct spi_device* )dev->private_data;

    /*pianxuan ladi, kaishicaozuo*/
    gpio_set_value(dev->cs_gpio, 0);

    /*goujian spi_transfer*/
    t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
    //1.fasong yaoxiedejicunqidedizhi
    txdata[0] = reg & (~(1 << 15));// yaodu zuigaowei bixu zhi 1?
    t->tx_buf = txdata;
    t->len = 1;
    //spi_message
    spi_message_init(&m);
    spi_message_add_tail(t, &m);
    //lingwai xiamiandiyigecanshu spi_device biaoshi shenme
    ret = spi_sync(spi, &m); //tongbufasong

    //2 jixu fa shufu ,fangru buf
    txdata[0] = 0xff;
    t->tx_buf = buf;
    t->len = len;
    spi_message_init(&m);
    spi_message_add_tail(t, &m);
    ret = spi_sync(spi, &m);

    
    kfree(t);
    
    /*caozuowancheng, pianxuanlagao*/
    gpio_set_value(dev->cs_gpio, 1);
    return ret;
}

#endif

// duqu dange jicunqi api
static u8 icm20608_read_reg(struct icm20608_dev *dev, u8 reg) {
    u8 buf;
    icm20608_read_regs(dev, reg, &buf, 1);

    return buf;
}

// xie dange zhi dao jicunqi
static void icm20608_write_reg(struct icm20608_dev *dev,u8 reg, u8 val) {
    u8 buf = val;
    icm20608_write_regs(dev, reg, &buf, 1);
}

/*
 * @description	: 读取ICM20608的数据,读取原始数据,包括三轴陀螺仪、
 * 				: 三轴加速度计和内部温度。
 * @param - dev	: ICM20608设备
 * @return 		: 无。
 */
void icm20608_readdata(struct icm20608_dev *dev)
{
	unsigned char data[14];
	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]);
}

static int __init icm20608_init(void) {
    int ret = 0;
    
    ret = spi_register_driver(&icm20608_driver);
    return ret;
}

static void __exit icm20608_exit(void) {
    /*wu fanhuizhi*/
    spi_unregister_driver(&icm20608_driver);
}

module_init(icm20608_init);
module_exit(icm20608_exit);
MODULE_LICENSE("GPL");

/*ziding zifushebei jiegouti he spi_driver bingcun ,jiexi shebei shu huode spi_device? */

读取ID,读写测试正常。

lib/modules/4.1.15 # 
/lib/modules/4.1.15 # 
/lib/modules/4.1.15 # modprobe icm20608.ko 
/home/jl/linux/imx6ull/linux_driver/23_spi/icm20608.c(109):
icm20608 major=249,minor=0
icm20608 id = 0XAE
ICM20_PWR_MGMT_1 = 0X1
/lib/modules/4.1.15 # 

还可以使用内核提供的api:spi_read,spi_write。而这最终也是调用 spi_sync。定义在 spi.h

补充:设备树dts文件中的ecspi追加节点下的 cs-gpios 也可以作为软件片选,不必改名再用。但是 io 复用一定要复用为 gpio。
此时不需要我们去手动片选了。
设备树 gpio 配置为 硬件片选:MX6UL_PAD_UART2_TX_DATA__ECSPI3_SS0 0x10b1
软件片选:MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x10b1

没用硬件片选是教程也不知道怎么用。厂商也使用的软件片选,因此只提供了软件片选方法。

修正后

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include <linux/fs.h>
#include<linux/slab.h>
#include<linux/io.h>
#include<linux/uaccess.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/of.h>
#include<linux/of_address.h>
#include<linux/of_irq.h>
#include<linux/gpio.h>
#include<linux/of_gpio.h>
#include<linux/atomic.h>
#include<linux/timer.h>
#include<linux/string.h>
#include<linux/jiffies.h>
#include<linux/irq.h>
#include<asm/mach/map.h>
#include<asm/uaccess.h>
#include<asm/io.h>
#include<linux/interrupt.h>

#include<linux/delay.h>
#include<linux/spi/spi.h>
#include"icm20608reg.h"


#define ICM20608_CNT 1
#define ICM20608_NAME    "icm20608"
#define USE_KERNEL_API

#ifdef USE_KERNEL_API

static inline int
spi_read(struct spi_device *spi, void *buf, size_t len);
static inline int
spi_write(struct spi_device *spi, const void *buf, size_t len);

#endif

//#define READ_USE_KERNEL_API

static int icm20608_probe(struct spi_device *spi);
static int icm20608_remove(struct spi_device *spi);
static int icm20608_open(struct inode *inode, struct file *filp);
static ssize_t icm20608_read(struct file *filp, 
                            char __user *buf, 
                            size_t cnt, 
                            loff_t *offt);
static int icm20608_release(struct inode *inode, struct file *filp);



/*chuantongde pipeibiao*/
static struct spi_device_id icm20608_id[] = {
    {"alientek,icm20608", 0}, /*0biaoshi shenme yisi?*/
    {},
};

/*shebeishu pipei */
static struct of_device_id icm20608_of_match[] = {
    {.compatible = "alientek,icm20608"},
    {},
};

/* i2c_driver jiegouti*/
static struct spi_driver icm20608_driver = {
    .probe = icm20608_probe,
    .remove = icm20608_remove,
    .driver = {
        .name = "icm20608",
        .owner = THIS_MODULE,
        .of_match_table = icm20608_of_match,
    },
    .id_table = icm20608_id,
};

static struct file_operations icm20608_fops = {
	.owner = THIS_MODULE,
	.open = icm20608_open,
	.read = icm20608_read,
	.release = 	icm20608_release,
};


/*zhuyi zheli meiyou static*/
struct icm20608_dev {
    int major;
    int minor;
    dev_t devid;
    struct cdev cdev;
    /*zhuyi zheliangge shi zhizhen*/
    struct class *class;
    struct device *device;
    /*gaoyige siyouzhizhen*/
    void *private_data;         /* 私有数据 		*/
    struct device_node *nd;
    int cs_gpio;                /* 片选所使用的GPIO编号		*/
    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;		/* 温度原始值 			*/
};

static struct icm20608_dev icm20608;

static int icm20608_read_regs(struct icm20608_dev *dev, 
                                u8 reg, 
                                u8 *buf, 
                                int len);
static int icm20608_write_regs(struct icm20608_dev *dev,
                                u8 reg,
                                u8 *buf,
                                int len);
static u8 icm20608_read_reg(struct icm20608_dev *dev, u8 reg);
static void icm20608_write_reg(struct icm20608_dev *dev,u8 reg, u8 val);
void icm20608_dev_init(struct icm20608_dev *dev);
void icm20608_readdata(struct icm20608_dev *dev);

static int icm20608_probe(struct spi_device *spi) {
    int ret = 0;
    printk("%s(%d):\n", __FILE__, __LINE__);

    /*you xitong lai fenpei zhushebeihao*/
    icm20608.major = 0;

    if (icm20608.major) {		/*  定义了设备号 */
		icm20608.devid = MKDEV(icm20608.major, 0);
		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("%s(%d):error\n", __FILE__, __LINE__);
        goto fail_devid;
    }
    
	printk("icm20608 major=%d,minor=%d\r\n",icm20608.major, icm20608.minor);	
	
	/* 2、初始化cdev */
	icm20608.cdev.owner = THIS_MODULE;
	cdev_init(&icm20608.cdev, &icm20608_fops);
	ret = cdev_add(&icm20608.cdev, icm20608.devid, ICM20608_CNT);
    if(ret < 0) {
        printk("%s(%d):error\n", __FILE__, __LINE__);
        goto fail_cdev;
    }

	/* 3、创建类 */
	icm20608.class = class_create(THIS_MODULE, ICM20608_NAME);
	if (IS_ERR(icm20608.class)) {
        ret = PTR_ERR(icm20608.class);
		printk("%s(%d):error\n", __FILE__, __LINE__);
        goto fail_class;
	}

	/* 5、创建设备 */
	icm20608.device = device_create(icm20608.class, NULL, icm20608.devid, NULL, ICM20608_NAME);
	if (IS_ERR(icm20608.device)) {
        ret = PTR_ERR(icm20608.device);
		printk("%s(%d):error\n", __FILE__, __LINE__);
        goto fail_device;
	}

#if 0 //buzaixuyaole
    /* huoqu pianxuan yinjiao 
     * keyi cong benhanshu de chuanrucanshu huoqu
     * yaoqueding cijiedian duiying shebeishu zhong 
     * nage jiedian
     * yao youcijiedian huoqu qi fujiedian
     */
    icm20608.nd = of_get_parent(spi->dev.of_node);
    /*huouq pianxuan shuxing */
    icm20608.cs_gpio = of_get_named_gpio(icm20608.nd, "cs-gpio", 0);
    if(icm20608.cs_gpio < 0) {
        printk("%s(%d):error\n", __FILE__, __LINE__);
        ret = -1;
        goto fail_gpio;
    }
    /*shengqing gpio*/
    ret = gpio_request(icm20608.cs_gpio, "cs");/*yao ziji jiashang panduan chenggong yufou*/
    if(ret < 0) {
        printk("%s(%d):error\n", __FILE__, __LINE__);
    }
    /*moren gei ge gaodianping*/
    ret = gpio_direction_output(icm20608.cs_gpio, 1);
    if(ret < 0) {
        printk("%s(%d):error\n", __FILE__, __LINE__);
    }
#endif
    /*chushihua spi_device jiegouti*/
    spi->mode = SPI_MODE_0;//shezhi spi gongzuo moshi
    spi_setup(spi);

    /*shei zhi jiegouti de siyoushuju wei probe hanshu de chuanru canshu */    
    icm20608.private_data = spi;

    icm20608_dev_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) {
    printk("%s(%d):\n", __FILE__, __LINE__);
    /* 注销字符设备驱动,删除cdev */
	cdev_del(&icm20608.cdev);
	/* 注销设备号 */
	unregister_chrdev_region(icm20608.devid, ICM20608_CNT); 

	device_destroy(icm20608.class, icm20608.devid);
	class_destroy(icm20608.class);
    //shifang gpio
    gpio_free(icm20608.cs_gpio);
    return 0;
}

/*zai cihanshu zhong chushihua icm20608*/
static int icm20608_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &icm20608; /* 设置私有数据 */
	return 0;
}


/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t icm20608_read(struct file *filp, 
                            char __user *buf, 
                            size_t cnt, 
                            loff_t *offt)
{
    
    signed int data[7];
	long err = 0;
	struct icm20608_dev *dev = (struct icm20608_dev *)filp->private_data;

	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;
}

static int icm20608_release(struct inode *inode, struct file *filp)
{
    //struct icm20608_dev *dev = (struct icm20608_dev* )filp->private_data;
    printk("%s(%d):\n", __FILE__, __LINE__);
	return 0;
}

/* icm 20608 chushihua
 * ruhetongguo spi lai caozuo 
 * waibuspi xinpian
 * xuyao spi dujici qi he 
 * spi xiejicunqi caozuo hanshu 
 */

void icm20608_dev_init(struct icm20608_dev *dev) {
    u8 val = 0;
    icm20608_write_reg(dev, ICM20_PWR_MGMT_1, 0x80);		/* 复位,复位后为0x40,睡眠模式 			*/
	mdelay(50);
	icm20608_write_reg(dev, ICM20_PWR_MGMT_1, 0x01);		/* 关闭睡眠,自动选择时钟 					*/
	mdelay(50);

	val = icm20608_read_reg(dev, ICM20_WHO_AM_I);
	printk("icm20608 id = %#X\r\n", val);
	if(val != ICM20608G_ID && val != ICM20608D_ID){
        printk("%s(%d):error.\n", __FILE__, __LINE__);
    }
    val = icm20608_read_reg(dev, ICM20_PWR_MGMT_1);
    printk("ICM20_PWR_MGMT_1 = %#X\r\n", val);

    icm20608_write_reg(&icm20608, ICM20_SMPLRT_DIV, 0x00); 	/* 输出速率是内部采样率					*/
	icm20608_write_reg(&icm20608, ICM20_GYRO_CONFIG, 0x18); 	/* 陀螺仪±2000dps量程 				*/
	icm20608_write_reg(&icm20608, ICM20_ACCEL_CONFIG, 0x18); 	/* 加速度计±16G量程 					*/
	icm20608_write_reg(&icm20608, ICM20_CONFIG, 0x04); 		/* 陀螺仪低通滤波BW=20Hz 				*/
	icm20608_write_reg(&icm20608, ICM20_ACCEL_CONFIG2, 0x04); /* 加速度计低通滤波BW=21.2Hz 			*/
	icm20608_write_reg(&icm20608, ICM20_PWR_MGMT_2, 0x00); 	/* 打开加速度计和陀螺仪所有轴 				*/
	icm20608_write_reg(&icm20608, ICM20_LP_MODE_CFG, 0x00); 	/* 关闭低功耗 						*/
	icm20608_write_reg(&icm20608, ICM20_FIFO_EN, 0x00);		/* 关闭FIFO						*/
}	


#ifdef USE_KERNEL_API

// len biaoshi jieshou buf de changdu
static int icm20608_read_regs(struct icm20608_dev *dev, 
                                u8 reg, 
                                u8 *buf, 
                                int len) {
    u8 data = 0;
    int ret = 0;
    /*spi shi shebeishu jiexi erlai ,zainaliguanlian?*/
    struct spi_device *spi = dev->private_data;
    /*pianxuan ladi*/
    //gpio_set_value(dev->cs_gpio, 0);
    
    //1 fasong yaoduqu de jicunqi dizhi,qie dizhi zuigaowei yao zhi 1
    data = reg | 0x80;
    //spi_write(spi, &data, 1);
    //2
    //ret = spi_read(spi, buf, len);

    spi_write_then_read(spi, &data, 1, buf, len);

    /*pianxuan lagao*/
    //gpio_set_value(dev->cs_gpio, 1);

    return ret;
}

static int icm20608_write_regs(struct icm20608_dev *dev,
                                u8 reg,
                                u8 *buf,
                                int len) {
    u8 data = 0;
    int ret = 0;
    struct spi_device *spi = dev->private_data;
    //gpio_set_value(dev->cs_gpio, 0);

    data = reg & (~(1 << 15));
#if 0 //liangci xie yeshibaile cs-gpios xiangguan
    ret = spi_write(spi, &data, 1); //jiarupanduan
    ret = spi_write(spi, buf, len); // jiarupanduan 
#else
    // jiejue fangfa erheyi yiqifa
    u8 *txdata;
    txdata = kzalloc(len+1, GFP_KERNEL);

    txdata[0] = reg & (~(1 << 15));
    memcpy(&txdata[1], buf, len);
    spi_write(spi, txdata, len+1);

    kfree(txdata);
#endif
    //gpio_set_value(dev->cs_gpio, 1);

    return ret;
}

#else

/* spi du jicunqi */
static int icm20608_read_regs(struct icm20608_dev *dev, 
                                u8 reg, 
                                u8 *buf, 
                                int len) {
    int ret = 0;
    u8 txdata[len];
    struct spi_message m;
    struct spi_transfer *t;
    struct spi_device *spi = (struct spi_device* )dev->private_data;

    /*pianxuan ladi, kaishicaozuo*/
    //gpio_set_value(dev->cs_gpio, 0);

    /*goujian spi_transfer*/
    t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
    //1.fasong yaoduqudejicunqidedizhi
    txdata[0] = reg | 0x80;// yaodu zuigaowei bixu zhi 1?
    t->tx_buf = txdata;
    t->len = 1;
    //spi_message
    spi_message_init(&m);
    spi_message_add_tail(t, &m);
    //lingwai xiamiandiyigecanshu spi_device biaoshi shenme
    ret = spi_sync(spi, &m); //tongbufasong

    //2 duqushufu ,fangru buf
    txdata[0] = 0xff;
    // dudao de shuju fangdao buf limian
    t->rx_buf = buf;
    t->len = len;
    spi_message_init(&m);
    spi_message_add_tail(t, &m);
    ret = spi_sync(spi, &m);

    kfree(t);

    /*caozuowancheng, pianxuanlagao*/
    //gpio_set_value(dev->cs_gpio, 1);
    return ret;
}

/* spi xie jicunqi */
static int icm20608_write_regs(struct icm20608_dev *dev,
                                u8 reg,
                                u8 *buf,
                                int len) {
    int ret = 0;
    u8 txdata[len];
    struct spi_message m;
    struct spi_transfer *t;
    struct spi_device *spi = (struct spi_device* )dev->private_data;

    /*pianxuan ladi, kaishicaozuo*/
    //gpio_set_value(dev->cs_gpio, 0);

    /*goujian spi_transfer*/
    t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
    //1.fasong yaoxiedejicunqidedizhi
    txdata[0] = reg & (~(1 << 15));// yaodu zuigaowei bixu zhi 1?
    t->tx_buf = txdata;
    t->len = 1;
    //spi_message
    spi_message_init(&m);
    spi_message_add_tail(t, &m);
    //lingwai xiamiandiyigecanshu spi_device biaoshi shenme
    ret = spi_sync(spi, &m); //tongbufasong

    //2 jixu fa shufu ,fangru buf
    txdata[0] = 0xff;
    t->tx_buf = buf;
    t->len = len;
    spi_message_init(&m);
    spi_message_add_tail(t, &m);
    ret = spi_sync(spi, &m);

    
    kfree(t);
    
    /*caozuowancheng, pianxuanlagao*/
    //gpio_set_value(dev->cs_gpio, 1);
    return ret;
}

#endif

// duqu dange jicunqi api
static u8 icm20608_read_reg(struct icm20608_dev *dev, u8 reg) {
    u8 buf;
    icm20608_read_regs(dev, reg, &buf, 1);

    return buf;
}

// xie dange zhi dao jicunqi
static void icm20608_write_reg(struct icm20608_dev *dev,u8 reg, u8 val) {
    u8 buf = val;
    icm20608_write_regs(dev, reg, &buf, 1);
}

/*
 * @description	: 读取ICM20608的数据,读取原始数据,包括三轴陀螺仪、
 * 				: 三轴加速度计和内部温度。
 * @param - dev	: ICM20608设备
 * @return 		: 无。
 */
void icm20608_readdata(struct icm20608_dev *dev)
{
	unsigned char data[14];
	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]);
}

static int __init icm20608_init(void) {
    int ret = 0;
    
    ret = spi_register_driver(&icm20608_driver);
    return ret;
}

static void __exit icm20608_exit(void) {
    /*wu fanhuizhi*/
    spi_unregister_driver(&icm20608_driver);
}

module_init(icm20608_init);
module_exit(icm20608_exit);
MODULE_LICENSE("GPL");

/*ziding zifushebei jiegouti he spi_driver bingcun */

三、测试 APP

#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(1000000); /*100ms */
	}
	close(fd);	/* 关闭文件 */	
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值