Linux驱动开发13 SPI驱动

发布于:2022-12-23 ⋅ 阅读:(1377) ⋅ 点赞:(1)

与IIC驱动类似,适配器不用我们写,我们只写具体设备驱动

        SPI 驱动框架和 I2C 很类似,都分为主机控制器驱动和设备驱动,主机控制器也就是 SOC的 SPI 控制器接口。比如在裸机篇中的《第二十七章 SPI 实验》,我们编写了 bsp_spi.c bsp_spi.h
这两个文件,这两个文件是 I.MX6U SPI 控制器驱动,我们编写好 SPI 控制器驱动以后就可以直接使用了,不管是什么 SPI 设备, SPI 控制器部分的驱动都是一样,我们的重点就落在了种类繁多的 SPI 设备驱动。  
SPI 设备驱动
        spi 设备驱动也和 i2c 设备驱动也很类似, Linux 内核使用 spi_driver 结构体来表示 spi 设备驱动,我们在编写 SPI 设备驱动的时候需要实现 spi_driver。spi_driver 结构体定义在 include/linux/spi/spi.h 文件中

 SPI 设备和驱动匹配过程

        SPI 设备和驱动的匹配过程是由 SPI 总线来完成的,这点和 platform I2C 等驱动一样, SPI 总线为 spi_bus_type ,定义在 drivers/spi/spi.c 文件中

SPI 设备驱动编写流程

SPI 设备信息描述
1 IO pinctrl 子节点创建与修改
        首先肯定是根据所使用的 IO 来创建或修改 pinctrl 子节点,这个没什么好说的,唯独要注意的就是检查相应的 IO 有没有被其他的设备所使用,如果有的话需要将其删除掉!
2 SPI 设备节点的创建与修改
        采用设备树的情况下,SPI 设备信息描述就通过创建相应的设备子节点来完成,我们可以打开 imx6qdl-sabresd.dtsi 这个设备树头文件
308 & ecspi1 {
309 fsl , spi - num - chipselects = < 1 >;
310 cs - gpios = <& gpio4 9 0 >;
311 pinctrl - names = "default" ;
312 pinctrl - 0 = <& pinctrl_ecspi1 >;
313 status = "okay" ;
314
315 flash : m25p80@0 {
316 #address - cells = < 1 >;
317 #size - cells = < 1 >;
318 compatible = "st,m25p32" ;
319 spi - max - frequency = < 20000000 >;
320 reg = < 0 >;
321 };
322 };

SPI 设备数据收发处理流程 (spi_driver)

        SPI 设备驱动的核心是 spi_driver ,这个我们已经在 62.1.2 小节讲过了。当我们向 Linux 内核注册成功 spi_driver 以后就可以使用 SPI 核心层提供的 API 函数来对设备进行读写操作了。 首先是 spi_transfer 结构体,
603 struct spi_transfer {
604 /* it's ok if tx_buf == rx_buf (right?)
605 * for MicroWire, one buffer must be null
606 * buffers must work with dma_*map_single() calls, unless
607 * spi_message.is_dma_mapped reports a pre-existing mapping
608 */
609 const void * tx_buf ;
610 void * rx_buf ;
611 unsigned len ;
612
613 dma_addr_t tx_dma ;
614 dma_addr_t rx_dma ;
615 struct sg_table tx_sg ;
616 struct sg_table rx_sg ;
617
618 unsigned cs_change : 1 ;
619 unsigned tx_nbits : 3 ;
620 unsigned rx_nbits : 3 ;
621 #define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */
622 #define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
623 #define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
624 u8 bits_per_word ;
625 u16 delay_usecs ;
626 u32 speed_hz ;
627
628 struct list_head transfer_list ;
629 };
spi_transfer 中也就没有发送长度和接收长度之分。
spi_transfer 需要组织成 spi_message spi_message 也是一个结构体
在使用 spi_message 之前需要对其进行初始化, spi_message 初始化函数为 spi_message_init
函数原型如下:
void spi_message_init(struct spi_message *m)
函数参数和返回值含义如下:
m 要初始化的 spi_message
返回值:
spi_message 初始化完成以后需要将 spi_transfer 添加到 spi_message 队列中,这里我们要用到 spi_message_add_tail 函数
spi_message 准备好以后既可以进行数据传输了,数据传输分为同步传输和异步传输,同步
传输会阻塞的等待 SPI 数据传输完成,同步传输函数为 spi_sync
异步传输不会阻塞的等到 SPI 数据传输完成,异步传输需要设置 spi_message 中的 complete成员变量,complete 是一个回调函数,当 SPI 异步传输完成以后此函数就会被调用。 SPI 异步传输函数为 spi_async
注册函数 spi_register_driver , 注销 spi_unregister_driver

		pinctrl_ecspi3: icm20608 {
			fsl,pins = < 
				MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20		0x10b0	/* CS */
				MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK	0x10b1	/* SCLK */
				MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO		0x10b1	/* MISO */
				MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI		0x10b1	/* MOSI */
			>;
		};


&ecspi3 {
	fsl,spi-num-chipselects = <1>;
	cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>; /* cant't use cs-gpios! */
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_ecspi3>;
	status = "okay";

	spidev: icm20608@0 {
		compatible = "alientek,icm20608";
		spi-max-frequency = <8000000>;
		reg = <0>;
	};	

C:\Users\Administrator\Desktop\linux-imx-rel_imx_4.1.15_2.1.0_ga\Documentation\devicetree\bindings\spi
NXP官方例程源码中有数据手册

从这节课里我学到了,代码,尤其是linux的驱动源码,官方写的最经典最好用了

#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 "icm20608reg.h"

/***************************************************************

Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.

文件名		: icm20608.c

作者	  	: 左忠凯

版本	   	: V1.0

描述	   	: ICM20608 SPI驱动程序

其他	   	: 无

论坛 	   	: www.openedv.com

日志	   	: 初版V1.0 2019/9/2 左忠凯创建

***************************************************************/

#define ICM20608_CNT	1

#define ICM20608_NAME	"icm20608"



struct icm20608_dev {

	dev_t devid;				/* 设备号 	 */

	struct cdev cdev;			/* cdev 	*/

	struct class *class;		/* 类 		*/

	struct device *device;		/* 设备 	 */

	struct device_node	*nd; 	/* 设备节点 */

	int major;					/* 主设备号 */

	void *private_data;			/* 私有数据 		*/

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

///	

/*

 * @description	: 从icm20608读取多个寄存器数据

 * @param - dev:  icm20608设备

 * @param - reg:  要读取的寄存器首地址

 * @param - val:  读取到的数据

 * @param - len:  要读取的数据长度

 * @return 		: 操作结果

 * 

 * SPI读寄存器

 * 内核也提供了spi_read 和 spi_write

 */

static int icm20608_read_regs(struct icm20608_dev *dev, u8 reg, void *buf, int len)

{



	int ret = -1;

	unsigned char txdata[1];

	unsigned char * rxdata;

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



	/* 构建spi_transfer  */

	t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);	/* 申请内存 */

	if(!t) {

		return -ENOMEM;

	}



	rxdata = kzalloc(sizeof(char) * len, GFP_KERNEL);	/* 申请内存 */

	if(!rxdata) {

		goto out1;

	}



	/* 一共发送len+1个字节的数据,第一个字节为

	寄存器首地址,一共要读取len个字节长度的数据,*/

	/* 第一步,发送要读取的寄存器的地址  */

	txdata[0] = reg | 0x80;		/* 写数据的时候首寄存器地址bit8要置1 */			

	t->tx_buf = txdata;			/* 要发送的数据 */

    t->rx_buf = rxdata;			/* 要读取的数据 */

	t->len = len+1;				/* t->len=发送的长度+读取的长度 */

	spi_message_init(&m);		/* 初始化spi_message */

	spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */

	ret = spi_sync(spi, &m);	/* 同步发送 */

	if(ret) {

		goto out2;

	}

	

    memcpy(buf , rxdata+1, len);  /* 只需要读取的数据 */



	// /* 片选拉高  */

	// gpio_set_value(dev->cs_gpio, 1);



out2:

	kfree(rxdata);					/* 释放内存 */

out1:	

	kfree(t);						/* 释放内存 */

	

	return ret;

}

//============================================================================

// static int icm20608_read_regs(struct icm20608_dev *dev, u8 reg, void *buf, int len)

// {

//  u8 data = 0;

// 	struct spi_device *spi = (struct spi_device *)dev->private_data;

	

// 	/* 片选拉低  */

// 	gpio_set_value(dev->cs_gpio, 0);

// 	data = reg | 0x80;

// 	// spi_write 系统自带的写函数

// 	spi_write(spi,&data, 1);//发送要读取的寄存器地址

// 	spi_read(spi,buf,len);  //读取数据

// 	/* 片选拉高  */

// 	gpio_set_value(dev->cs_gpio, 1);

// }



// static s32 icm20608_write_regs(struct icm20608_dev *dev, u8 reg, u8 *buf, u8 len)

// {

// 	u8 data = 0;

// 	struct spi_device *spi = (struct spi_device *)dev->private_data;



// 	/*片选拉低*/

// 	gpio_set_value(dev->cs_gpio, 0);



// 	data = reg & ~0x80;

// 	spi_write(spi, &data, 1);//发送要写的寄存器地址

// 	spi_write(spi, &data, len);//发送要写的寄存器地址



// 	/*片选拉高*/

// 	gpio_set_value(dev->cs_gpio, 1);

// }

//=================================================================================

/*

 * @description	: 向icm20608多个寄存器写入数据

 * @param - dev:  icm20608设备

 * @param - reg:  要写入的寄存器首地址

 * @param - val:  要写入的数据缓冲区

 * @param - len:  要写入的数据长度

 * @return 	  :   操作结果、

 * 

 * SPI写寄存器

 */

static s32 icm20608_write_regs(struct icm20608_dev *dev, u8 reg, u8 *buf, u8 len)

{

	int ret = -1;

	unsigned char *txdata;

	struct spi_message m;

	struct spi_transfer *t;

	struct spi_device *spi = (struct spi_device *)dev->private_data;

	

	t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);	/* 申请内存 */

	if(!t) {

		return -ENOMEM;

	}

	

	txdata = kzalloc(sizeof(char)+len, GFP_KERNEL);

	if(!txdata) {

		goto out1;

	}

	

	/* 一共发送len+1个字节的数据,第一个字节为

	寄存器首地址,len为要写入的寄存器的集合,*/

	*txdata = reg & ~0x80;	/* 写数据的时候首寄存器地址bit8要清零 */

    memcpy(txdata+1, buf, len);	/* 把len个寄存器拷贝到txdata里,等待发送 */

	t->tx_buf = txdata;			/* 要发送的数据 */

	t->len = len+1;				/* t->len=发送的长度+读取的长度 */

	spi_message_init(&m);		/* 初始化spi_message */

	spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */

	ret = spi_sync(spi, &m);	/* 同步发送 */

    if(ret) {

        goto out2;

    }

	// 这一套是左工一开始以为cs-gpios是硬件片选所以没有用

	// 最后发现cs-gpio是软件片选,所以用了,很好用

out2:

	kfree(txdata);				/* 释放内存 */

out1:

	kfree(t);					/* 释放内存 */

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

}



/*

 * @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]);

}

///	

/*

 * @description		: 打开设备

 * @param - inode 	: 传递给驱动的inode

 * @param - filp 	: 设备文件,file结构体有个叫做pr似有ate_data的成员变量

 * 					  一般在open的时候将private_data似有向设备结构体。

 * @return 			: 0 成功;其他 失败

 */

static int icm20608_open(struct inode *inode, struct file *filp)

{

	filp->private_data = &icm20608dev; /* 设置私有数据 */

	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 *off)

{

	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;

}



/*

 * @description		: 关闭/释放设备

 * @param - filp 	: 要关闭的设备文件(文件描述符)

 * @return 			: 0 成功;其他 失败

 */

static int icm20608_release(struct inode *inode, struct file *filp)

{

	return 0;

}



/* icm20608操作函数 */

static const struct file_operations icm20608_ops = {

	.owner = THIS_MODULE,

	.open = icm20608_open,

	.read = icm20608_read,

	.release = icm20608_release,

};

///	

/*

 * ICM20608内部寄存器初始化函数 (初始化芯片)

 * @param  	: 无

 * @return 	: 无

 * 需要初始化icm20608芯片,然后从里面读取原始数据!

 * 这个过程就要用到如何使用linux内的SPI驱动API来读写 ICM20608

 */

void icm20608_reginit(void)

{

	u8 value = 0;

	

	icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_1, 0x80); //复位。复位后为0x40,睡眠模式

	mdelay(50);

	icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_1, 0x01); //关闭睡眠,自动选择时钟

	mdelay(50);



	value = icm20608_read_onereg(&icm20608dev, ICM20_WHO_AM_I);

	printk("ICM20608 ID = %#X\r\n", value);	



	icm20608_write_onereg(&icm20608dev, ICM20_SMPLRT_DIV, 0x00); 	/* 输出速率是内部采样率					*/

	icm20608_write_onereg(&icm20608dev, ICM20_GYRO_CONFIG, 0x18); 	/* 陀螺仪±2000dps量程 				*/

	icm20608_write_onereg(&icm20608dev, ICM20_ACCEL_CONFIG, 0x18); 	/* 加速度计±16G量程 					*/

	icm20608_write_onereg(&icm20608dev, ICM20_CONFIG, 0x04); 		/* 陀螺仪低通滤波BW=20Hz 				*/

	icm20608_write_onereg(&icm20608dev, ICM20_ACCEL_CONFIG2, 0x04); /* 加速度计低通滤波BW=21.2Hz 			*/

	icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_2, 0x00); 	/* 打开加速度计和陀螺仪所有轴 				*/

	icm20608_write_onereg(&icm20608dev, ICM20_LP_MODE_CFG, 0x00); 	/* 关闭低功耗 						*/

	icm20608_write_onereg(&icm20608dev, ICM20_FIFO_EN, 0x00);		/* 关闭FIFO						*/

}

///	

 /*

  * @description     : spi驱动的probe函数,当驱动与

  *                    设备匹配以后此函数就会执行

  * @param - client  : i2c设备

  * @param - id      : i2c设备ID

  * 

  */	

static int icm20608_probe(struct spi_device *spi)

{

	/* 1、构建设备号 */

	if (icm20608dev.major) {

		icm20608dev.devid = MKDEV(icm20608dev.major, 0);

		register_chrdev_region(icm20608dev.devid, ICM20608_CNT, ICM20608_NAME);

	} else {

		alloc_chrdev_region(&icm20608dev.devid, 0, ICM20608_CNT, ICM20608_NAME);

		icm20608dev.major = MAJOR(icm20608dev.devid);

	}



	/* 2、注册设备 */

	cdev_init(&icm20608dev.cdev, &icm20608_ops);

	cdev_add(&icm20608dev.cdev, icm20608dev.devid, ICM20608_CNT);



	/* 3、创建类 */

	icm20608dev.class = class_create(THIS_MODULE, ICM20608_NAME);

	if (IS_ERR(icm20608dev.class)) {

		return PTR_ERR(icm20608dev.class);

	}



	/* 4、创建设备 */

	icm20608dev.device = device_create(icm20608dev.class, NULL, icm20608dev.devid, NULL, ICM20608_NAME);

	if (IS_ERR(icm20608dev.device)) {

		return PTR_ERR(icm20608dev.device);

	}



	/* 获取片选引脚  */

	/*

	icm20608dev.nd = of_get_parent(spi->dev.of_node);

	icm20608dev.cs_gpio = of_get_named+gpio(icm20608dev.nd,"cs=gpio",0);

	if(icm20608dev.cs_gpio < 0){

		pritnk("can't get cs-gpio\r\n");

		goto fail_gpio;

	}



	ret = gpio_request(icm20608dev.cs_gpio,"cs");

	if(ret < 0)

	{

		printk("cs_gpio request failed!\r\n");

	}



	ret = gpio_direction_output(icm20608dev.cs_gpio, 1); //默认高电平

	*/



	/*初始化spi_device */

	spi->mode = SPI_MODE_0;	/*MODE0,CPOL=0,CPHA=0*/

	spi_setup(spi);

	/* 设置icm20608dev的私有数据 */

	icm20608dev.private_data = spi; 



	/* 初始化ICM20608内部寄存器“芯片” */

	icm20608_reginit();		

	return 0;

}



/*

 * @description     : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行

 * @param - client 	: i2c设备

 * @return          : 0,成功;其他负值,失败

 */

static int icm20608_remove(struct spi_device *spi)

{

	/* 删除设备 */

	cdev_del(&icm20608dev.cdev);

	unregister_chrdev_region(icm20608dev.devid, ICM20608_CNT);



	/* 注销掉类和设备 */

	device_destroy(icm20608dev.class, icm20608dev.devid);

	class_destroy(icm20608dev.class);

	return 0;



	// /* 释放片选  */

	// gpio_free(icm20608dev.cs_gpio);

	// return ret;

}



/* 传统匹配方式ID列表 */

static const struct spi_device_id icm20608_id[] = {

	{"alientek,icm20608", 0},  

	{}

};



/* 设备树匹配列表 */

static const struct of_device_id icm20608_of_match[] = {

	{ .compatible = "alientek,icm20608" },

	{ /* Sentinel */ }

};



/* SPI驱动结构体 */	

static 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,

};

///		   

/*

 * @description	: 驱动入口函数

 * @param 		: 无

 * @return 		: 无

 */

static int __init icm20608_init(void)

{

	return spi_register_driver(&icm20608_driver);

}



/*

 * @description	: 驱动出口函数

 * @param 		: 无

 * @return 		: 无

 */

static void __exit icm20608_exit(void)

{

	spi_unregister_driver(&icm20608_driver);

}



module_init(icm20608_init);

module_exit(icm20608_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("zuozhongkai");
#ifndef ICM20608_H

#define ICM20608_H

/***************************************************************

Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.

文件名		: icm20608reg.h

作者	  	: 左忠凯

版本	   	: V1.0

描述	   	: ICM20608寄存器地址描述头文件

其他	   	: 无

论坛 	   	: www.openedv.com

日志	   	: 初版V1.0 2019/9/2 左忠凯创建

***************************************************************/

#define ICM20608G_ID			0XAF	/* ID值 */

#define ICM20608D_ID			0XAE	/* ID值 */



/* ICM20608寄存器 

 *复位后所有寄存器地址都为0,除了

 *Register 107(0X6B) Power Management 1 	= 0x40

 *Register 117(0X75) WHO_AM_I 				= 0xAF或0xAE

 */

/* 陀螺仪和加速度自测(出产时设置,用于与用户的自检输出值比较) */

#define	ICM20_SELF_TEST_X_GYRO		0x00

#define	ICM20_SELF_TEST_Y_GYRO		0x01

#define	ICM20_SELF_TEST_Z_GYRO		0x02

#define	ICM20_SELF_TEST_X_ACCEL		0x0D

#define	ICM20_SELF_TEST_Y_ACCEL		0x0E

#define	ICM20_SELF_TEST_Z_ACCEL		0x0F



/* 陀螺仪静态偏移 */

#define	ICM20_XG_OFFS_USRH			0x13

#define	ICM20_XG_OFFS_USRL			0x14

#define	ICM20_YG_OFFS_USRH			0x15

#define	ICM20_YG_OFFS_USRL			0x16

#define	ICM20_ZG_OFFS_USRH			0x17

#define	ICM20_ZG_OFFS_USRL			0x18



#define	ICM20_SMPLRT_DIV			0x19

#define	ICM20_CONFIG				0x1A

#define	ICM20_GYRO_CONFIG			0x1B

#define	ICM20_ACCEL_CONFIG			0x1C

#define	ICM20_ACCEL_CONFIG2			0x1D

#define	ICM20_LP_MODE_CFG			0x1E

#define	ICM20_ACCEL_WOM_THR			0x1F

#define	ICM20_FIFO_EN				0x23

#define	ICM20_FSYNC_INT				0x36

#define	ICM20_INT_PIN_CFG			0x37

#define	ICM20_INT_ENABLE			0x38

#define	ICM20_INT_STATUS			0x3A



/* 加速度输出 */

#define	ICM20_ACCEL_XOUT_H			0x3B

#define	ICM20_ACCEL_XOUT_L			0x3C

#define	ICM20_ACCEL_YOUT_H			0x3D

#define	ICM20_ACCEL_YOUT_L			0x3E

#define	ICM20_ACCEL_ZOUT_H			0x3F

#define	ICM20_ACCEL_ZOUT_L			0x40



/* 温度输出 */

#define	ICM20_TEMP_OUT_H			0x41

#define	ICM20_TEMP_OUT_L			0x42



/* 陀螺仪输出 */

#define	ICM20_GYRO_XOUT_H			0x43

#define	ICM20_GYRO_XOUT_L			0x44

#define	ICM20_GYRO_YOUT_H			0x45

#define	ICM20_GYRO_YOUT_L			0x46

#define	ICM20_GYRO_ZOUT_H			0x47

#define	ICM20_GYRO_ZOUT_L			0x48



#define	ICM20_SIGNAL_PATH_RESET		0x68

#define	ICM20_ACCEL_INTEL_CTRL 		0x69

#define	ICM20_USER_CTRL				0x6A

#define	ICM20_PWR_MGMT_1			0x6B

#define	ICM20_PWR_MGMT_2			0x6C

#define	ICM20_FIFO_COUNTH			0x72

#define	ICM20_FIFO_COUNTL			0x73

#define	ICM20_FIFO_R_W				0x74

#define	ICM20_WHO_AM_I 				0x75



/* 加速度静态偏移 */

#define	ICM20_XA_OFFSET_H			0x77

#define	ICM20_XA_OFFSET_L			0x78

#define	ICM20_YA_OFFSET_H			0x7A

#define	ICM20_YA_OFFSET_L			0x7B

#define	ICM20_ZA_OFFSET_H			0x7D

#define	ICM20_ZA_OFFSET_L 			0x7E


#endif
#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;

}

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到