IIC总线式驱动开发(mpu6050)

news2024/11/15 12:55:56

目录

一、I2C总线背景知识

二、Exynos4412 I2C收发实现之裸机版

2.1 发送

2.2 接收

三、Linux内核对I2C总线的支持

四、MPU6050

五、应用层直接使用I2C通道

5.1 预备工作:

5.1.1

5.1.2

5.2 应用层直接使用i2c总线的代码实现

5.2.1 调用read、write实现接收、发送

5.2.2 调用ioctl实现接收、发送


一、I2C总线背景知识

SOC芯片平台的外设分为:

1. 一级外设:外设控制器集成在SOC芯片内部

2. 二级外设:外设控制器由另一块芯片负责,通过一些通讯总线与SOC芯片相连

 (二级外设在IIC中有一个身份标识,被称为地址,外设的寄存器也有地址,这个地址是基于外设的。内存也有个地址,这三个地址不是一个,容易弄混)

Inter-Integrated Circuit: 字面意思是用于“集成电路之间”的通信总线,简写:IIC(或者I2C)

i2c传输的要点就是: 传输一个字节 后面必然紧跟一个"响应"信号----应答信号.这个响应信号可能来自主机,或者是从机,具体是谁,就要看传输方向。

传输方向分两种情况(每种情况又有两种可能: A无应答和 B有应答):

1.主机->从机,主机对从机发一个字节之后,主机要读取从机的响应信号(主机读SDA线)

    A) 主机读SDA为高电平,说明从机无应答(意味着从机接收完毕,主机发送停止信号)

    B) 主机读SDA为低电平,说明从机有应答。(可继续发送下一个字节)

2.从机->主机, 主机读取从机一个字节之后,主机要向从机发送一个响应信号(主机写SDA线)

​    A) 主机写SDA为高电平,从机收到主机的无应答信号之后,从机停止传输,等待主机的停止信号。

​    B) 主机写SDA为低电平,从机收到主机的应答信号之后,从机继续输出下一字节

二、Exynos4412 I2C收发实现之裸机版

I2CCON寄存器:控制寄存器

第7位:决定是否允许产生应答信号,无论发送还是接收前,需置1

第6位:传输时时钟线分频,一般选置1

第5位:决定是否开启发送或接收结束时发通知,无论发送还是接收前,需置1

第4位:接收或发送是否完毕可以通过检查此位是否为1,接收或发送完毕后需置0

I2CSTAT寄存器:状态寄存器

 第6、7位:每次传输前需选择传输模式

第5位:置0产生将产生终止信号,传输前置1产生起始信号

第4位:使能数据输出,传输前需置1

I2CDS寄存器:数据寄存器,发送前被发送的数据存放处,接收后结果也从此处读取

2.1 发送

```c

void iic_write (unsigned char slave_addr, unsigned char addr, unsigned char data)

{

    //从设备寻址

    I2C5.I2CDS = slave_addr;

    I2C5.I2CCON = 1<<7 | 1<<6 | 1<<5;/*ENABLE ACK BIT, PRESCALER:512, ,ENABLE RX/TX */

   

    I2C5.I2CSTAT = 0x3 << 6 | 1<<5 | 1<<4;/*Master Trans mode ,START ,ENABLE RX/TX ,*/

    while(!(I2C5.I2CCON & (1<<4)));



    I2C5.I2CDS = addr;

    I2C5.I2CCON &= ~(1<<4); //Clear pending bit to resume.

    while(!(I2C5.I2CCON & (1<<4)));



    //发送数据

    I2C5.I2CDS = data;  // Data

    I2C5.I2CCON &= ~(1<<4); //Clear pending bit to resume.

    while(!(I2C5.I2CCON & (1<<4)));



    I2C5.I2CSTAT = 0xD0; //stop



    I2C5.I2CCON &= ~(1<<4);//Clear pending bit to resume.



    mydelay_ms(10);

}

```


2.2 接收

```c

void iic_read(unsigned char slave_addr, unsigned char addr, unsigned char *data)

{

    //从设备寻址

    I2C5.I2CDS = slave_addr;



    I2C5.I2CCON = 1<<7 | 1<<6 | 1<<5;/*ENABLE ACK BIT, PRESCALER:512, ENABLE RX/TX Interrupt-enable */

    I2C5.I2CSTAT = 0x3 << 6 | 1<<5 | 1<<4;/*Master Trans mode ,START ,ENABLE RX/TX ,*/

    while(!(I2C5.I2CCON & (1<<4))); /*对应位为1表示slave_addr传输完成,线路处于挂起状态*/



    I2C5.I2CDS = addr;

    I2C5.I2CCON &= ~(1<<4); //Clear pending bit to resume. 继续传输

    while(!(I2C5.I2CCON & (1<<4)));

   

   

    I2C5.I2CSTAT = 0xD0; //stop  第5位写0,表示要求产生stop信号



    //接收数据

    I2C5.I2CDS = slave_addr | 0x01; // Read

    I2C5.I2CCON = 1<<7 | 1<<6 | 1<<5;/*ENABLE ACK BIT, PRESCALER:512, ENABLE RX/TX Interrupt-enable */



    I2C5.I2CSTAT = 2<<6 | 1<<5 | 1<<4;/*Master receive mode ,START ,ENABLE RX/TX , 0xB0*/

    while(!(I2C5.I2CCON & (1<<4)));



    I2C5.I2CCON &= ~((1<<7) | (1<<4));/* Resume the operation  & no ack*/

    while(!(I2C5.I2CCON & (1<<4)));



    I2C5.I2CSTAT = 0x90; //stop  第5位写0,表示要求产生stop信号

    I2C5.I2CCON &= ~(1<<4);     /*clean interrupt pending bit  */



    *data = I2C5.I2CDS;

    mydelay_ms(10);

}

```

三、Linux内核对I2C总线的支持

(driver驱动层是IIC从设备驱动,IIC核心层和hardware之间的两块是IIC主设备驱动。主设备驱动一般SOC芯片生产商写。我们只需要写从设备驱动就行。) 

 **I2C设备驱动:**即挂接在I2C总线上的二级外设的驱动,也称客户(client)驱动,实现对二级外设的各种操作,二级外设的几乎所有操作全部依赖于对其自身内部寄存器的读写,对这些二级外设寄存器的读写又依赖于I2C总线的发送和接收

**I2C总线驱动:**即对I2C总线自身控制器的驱动,一般SOC芯片都会提供多个I2C总线控制器,每个I2C总线控制器提供一组I2C总线(SDA一根+SCL一根),每一组被称为一个I2C通道,Linux内核里将I2C总线控制器叫做适配器(adapter),适配器驱动主要工作就是提供通过本组I2C总线与二级外设进行数据传输的接口,每个二级外设驱动里必须能够获得其对应的adapter对象才能实现数据传输

**I2C核心:**承上启下,为I2C设备驱动和I2C总线驱动开发提供接口,为I2C设备驱动层提供管理多个i2c_driver、i2c_client对象的数据结构,为I2C总线驱动层提供多个i2c_algorithm、i2c_adapter对象的数据结构

四大核心对象之间的关系图

i2c二级外设驱动开发涉及到核心结构体及其相关接口函数:


```c

struct i2c_board_info {

    char        type[I2C_NAME_SIZE];

    unsigned short  flags;

    unsigned short  addr;

    void        *platform_data;

    struct dev_archdata *archdata;

    struct device_node *of_node;

    int     irq;

};

/*用来协助创建i2c_client对象

重要成员

type:用来初始化i2c_client结构中的name成员

flags:用来初始化i2c_client结构中的flags成员

addr:用来初始化i2c_client结构中的addr成员

platform_data:用来初始化i2c_client结构中的.dev.platform_data成员

archdata:用来初始化i2c_client结构中的.dev.archdata成员

irq:用来初始化i2c_client结构中的irq成员



关键就是记住该结构和i2c_client结构成员的对应关系。在i2c子系统不直接创建i2c_client结构,只是提供struct i2c_board_info结构信息,让子系统动态创建,并且注册。

*/

```

```c

struct i2c_client {

    unsigned short flags;

    unsigned short addr;

    char name[I2C_NAME_SIZE];

    struct i2c_adapter *adapter;

    struct i2c_driver *driver;

    struct device dev;

    int irq;

    struct list_head detected;

};

/*重要成员:

flags:地址长度,如是10位还是7位地址,默认是7位地址。如果是10位地址器件,则设置为I2C_CLIENT_TEN

addr:具体I2C器件如(at24c02),设备地址,低7位

name:设备名,用于和i2c_driver层匹配使用的,可以和平台模型中的平台设备层platform_driver中的name作用是一样的。

adapter:本设备所绑定的适配器结构(CPU有很多I2C适配器,类似单片机有串口1、串口2等等,在linux中每个适配器都用一个结构描述)

driver:指向匹配的i2c_driver结构,不需要自己填充,匹配上后内核会完成这个赋值操作

dev:内嵌的设备模型,可以使用其中的platform_data成员传递给任何数据给i2c_driver使用。

irq:设备需要使用到中断时,把中断编号传递给i2c_driver进行注册中断,如果没有就不需要填充。(有的I2C器件有中断引脚编号,与CPU相连)

*/



/* 获得/释放 i2c_adapter 路径:i2c-core.c linux-3.5\drivers\i2c */

/*功能:通过i2c总线编号获得内核中的i2c_adapter结构地址,然后用户可以使用这个结构地址就可以给i2c_client结构使用,从而实现i2c_client进行总线绑定,从而增加适配器引用计数。

返回值:

NULL:没有找到指定总线编号适配器结构

非NULL:指定nr的适配器结构内存地址*/

struct i2c_adapter *i2c_get_adapter(int nr);




/*减少引用计数:当使用·i2c_get_adapter·后,需要使用该函数减少引用计数。(如果你的适配器驱动不需要卸载,可以不使用)*/

void i2c_put_adapter(struct i2c_adapter *adap);



/*

功能:根据参数adap,info,addr,addr_list动态创建i2c_client并且进行注册

参数:

adap:i2c_client所依附的适配器结构地址

info:i2c_client基本信息

addt_list: i2c_client的地址(地址定义形式是固定的,一般是定义一个数组,数组必须以I2C_CLIENT_END结束,示例:unsigned short ft5x0x_i2c[]={0x38,I2C_CLIENT_END};

probe:回调函数指针,当创建好i2c_client后,会调用该函数,一般没有什么特殊需求传递NULL。

返回值:

非NULL:创建成功,返回创建好的i2c_client结构地址

NULL:创建失败

*/

struct i2c_client * i2c_new_probed_device

(

 struct i2c_adapter *adap,

 struct i2c_board_info *info,

 unsigned short const *addr_list,

 int (*probe)(struct i2c_adapter *, unsigned short addr)

);

/*示例:

struct i2c_adapter *ad;

struct i2c_board_info info={""};



unsigned short addr_list[]={0x38,0x39,I2C_CLIENT_END};



//假设设备挂在i2c-2总线上

ad=i2c_get_adapter(2);



//自己填充board_info

strcpy(inf.type,"xxxxx");

info.flags=0;

//动态创建i2c_client并且注册

i2c_new_probed_device(ad,&info,addr_list,NULL);



i2c_put_adapter(ad);

*/



/*注销*/

void i2c_unregister_device(struct i2c_client *pclt)




 struct i2c_client * i2c_new_device

 (

     struct i2c_adapter *padap,

     struct i2c_board_info const *pinfo

 );

/*示例:

struct i2c_adapter *ad;

struct i2c_board_info info={

    I2C_BOARD_INFO(name,二级外设地址)

};

//假设设备挂在i2c-2总线上

ad=i2c_get_adapter(2);



//动态创建i2c_client并且注册

i2c_new_device(ad,&info);



i2c_put_adapter(ad);

*/

```

带probe的不需要知道设备号 ,可以探测这些地址找到一个可以进行数据交互的设备

```c

struct i2c_driver {

    unsigned int class;



    /* Standard driver model interfaces */

    int (*probe)(struct i2c_client *, const struct i2c_device_id *);

    int (*remove)(struct i2c_client *);



    /* driver model interfaces that don't relate to enumeration  */

    void (*shutdown)(struct i2c_client *);

    int (*suspend)(struct i2c_client *, pm_message_t mesg);

    int (*resume)(struct i2c_client *);

    void (*alert)(struct i2c_client *, unsigned int data);



    /* a ioctl like command that can be used to perform specific functions

     * with the device.

     */

    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);



    struct device_driver driver;

    const struct i2c_device_id *id_table;



    /* Device detection callback for automatic device creation */

    int (*detect)(struct i2c_client *, struct i2c_board_info *);

    const unsigned short *address_list;

    struct list_head clients;

};

/*重要成员:

probe:在i2c_client与i2c_driver匹配后执行该函数

remove:在取消i2c_client与i2c_driver匹配绑定后后执行该函数

driver:这个成员类型在平台设备驱动层中也有,而且使用其中的name成员来实现平台设备匹配,但是i2c子系统中不使用其中的name进行匹配,这也是i2c设备驱动模型和平台设备模型匹配方法的一点区别

id_table:用来实现i2c_client与i2c_driver匹配绑定,当i2c_client中的name成员和i2c_driver中id_table中name成员相同的时候,就匹配上了。



补充:i2c_client与i2c_driver匹配问题

- i2c_client中的name成员和i2c_driver中id_table中name成员相同的时候

- i2c_client指定的信息在物理上真实存放对应的硬件,并且工作是正常的才会绑定上,并执行其中的probe接口函数这第二点要求和平台模型匹配有区别,平台模型不要求设备层指定信息在物理上真实存在就能匹配

*/



/*功能:向内核注册一个i2c_driver对象

返回值:0成功,负数 失败*/

#define i2c_add_driver(driver)     i2c_register_driver(THIS_MODULE, driver)

int i2c_register_driver(struct module *owner, struct i2c_driver *driver);



/*功能:从内核注销一个i2c_driver对象

返回值:无 */

void i2c_del_driver(struct i2c_driver *driver);

```
```c

struct i2c_msg {

    __u16 addr; /* slave address            */

    __u16 flags;

#define I2C_M_TEN       0x0010  /* this is a ten bit chip address */

#define I2C_M_RD        0x0001  /* read data, from slave to master */

    __u16 len;      /* msg length               */

    __u8 *buf;      /* pointer to msg data          */

};

/* 重要成员:

addr:要读写的二级外设地址

flags:表示地址的长度,读写功能。如果是10位地址必须设置I2C_M_TEN,如果是读操作必须设置有I2C_M_RD······,可以使用或运算合成。

buf:要读写的数据指针。写操作:数据源 读操作:指定存放数据的缓存区

len:读写数据的数据长度

*/



/*i2c收发一体化函数,收还是发由参数msgs的成员flags决定*/

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

/*

功能:根据msgs进行手法控制

参数:

adap:使用哪一个适配器发送信息,一般是取i2c_client结构中的adapter指针作为参数

msgs:具体发送消息指针,一般情况下是一个数组

num:表示前一个参数msgs数组有多少个消息要发送的

返回值:

负数:失败

> 0 表示成功发送i2c_msg数量

*/



/*I2C读取数据函数*/

int i2c_master_recv(const struct i2c_client *client, char *buf, int count)

/*功能:实现标准的I2C读时序,数据可以是N个数据,这个函数调用时候默认已经包含发送从机地址+读方向这一环节了

参数:

client:设备结构

buf:读取数据存放缓冲区

count:读取数据大小 不大于64k

返回值:

失败:负数

成功:成功读取的字节数

*/

   

/*I2C发送数据函数*/

int i2c_master_send(const struct i2c_client *client, const char *buf, int count)

/*功能:实现标准的I2C写时序,数据可以是N个数据,这个函数调用时候默认已经包含发送从机地址+写方向这一环节了

参数:

client:设备结构地址

buf:发送数据存放缓冲区

count:发送数据大小 不大于64k

返回值:

失败:负数

成功:成功发送的字节数

*/



```

四、MPU6050

三轴角速度+三轴加速度+温度传感器

```c

#define SMPLRT_DIV  0x19 //陀螺仪采样率,典型值:0x07(125Hz)

#define CONFIG   0x1A //低通滤波频率,典型值:0x06(5Hz)

#define GYRO_CONFIG  0x1B //陀螺仪自检及测量范围,典型值:0xF8(不自检,+/-2000deg/s)

#define ACCEL_CONFIG 0x1C //加速计自检、测量范围,典型值:0x19(不自检,+/-G)

#define ACCEL_XOUT_H 0x3B

#define ACCEL_XOUT_L 0x3C

#define ACCEL_YOUT_H 0x3D

#define ACCEL_YOUT_L 0x3E

#define ACCEL_ZOUT_H 0x3F

#define ACCEL_ZOUT_L 0x40

#define TEMP_OUT_H  0x41

#define TEMP_OUT_L  0x42

#define GYRO_XOUT_H  0x43

#define GYRO_XOUT_L  0x44

#define GYRO_YOUT_H  0x45

#define GYRO_YOUT_L  0x46

#define GYRO_ZOUT_H  0x47

#define GYRO_ZOUT_L  0x48

#define PWR_MGMT_1  0x6B //电源管理,典型值:0x00(正常启用)

```

五、应用层直接使用I2C通道

5.1 预备工作:

5.1.1

exynos4412平台每个i2c通道的信息是通过设备树提供的,因此需要首先在exynos4412-fs4412.dts中增加5通道的节点:

 不要忘记:

1. 回内核源码顶层目录执行:make  dtbs

2. 将新生成的dtb拷贝到/tftpboot

5.1.2

i2c总线驱动层提供了一个字符设备驱动,以便于应用层可以直接通过它去使用i2c总线通讯去操作二级外设,但需要

内核编译时添加此字符设备驱动代码(i2c-dev.c),因此需要修改make menuconfig的配置:

不要忘记:

1. 回内核源码顶层目录执行:make  uImage

2. 将新生成的uImage拷贝到/tftpboot

5.2 应用层直接使用i2c总线的代码实现

5.2.1 调用read、write实现接收、发送

cp ~/Linux_4412/kernel/linux-3.14/include//uapi/linux/i2c-dev.h .

#ifndef MPU_6050_H
#define MPU_6050_H

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int init_mpu6050(int fd);
int read_accelx(int fd);
int read_accely(int fd);
int read_accelz(int fd);
int read_temp(int fd);
int read_gyrox(int fd);
int read_gyroy(int fd);
int read_gyroz(int fd);

#define SMPLRT_DIV 0x19
#define CONFIG 0x1A
#define GYRO_CONFIG 0x1B
#define ACCEL_CONFIG 0x1C

#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48

#define PWR_MGMT_1  0x6B

#define I2C_SLAVE	0x0703	/* Use this slave address */
#define I2C_TENBIT	0x0704	/* 0 for 7 bit addrs, != 0 for 10 bit */

#endif

#include "mpu6050.h"

int main(int argc,char *argv[])
{
	int fd = -1;
	if(argc < 2)
	{
		printf("Argument is too few\n");
		return 1;
	}

	/*open */
	fd = open(argv[1],O_RDWR);
	if(fd < 0)
	{
		printf("open %s failed\n",argv[1]);
		return 2;
	}
	/*init mpu6050*/
	init_mpu6050(fd);

	while(1)
	{
		sleep(2);
		/*read and print data from 6050*/
		printf("Accel-X:0x%x\n",read_accelx(fd));
		printf("Accel-Y:0x%x\n",read_accely(fd));
		printf("Accel-Z:0x%x\n",read_accelz(fd));
		printf("Temp:0x%x\n",read_temp(fd));
		printf("GYRO-X:0x%x\n",read_gyrox(fd));
		printf("GYRO-Y:0x%x\n",read_gyroy(fd));
		printf("GYRO-z:0x%x\n",read_gyroz(fd));
		printf("\n");
	}
	/*close*/
	close(fd);
	fd = -1;
	return 0;
}

#include "mpu6050.h"

int main(int argc,char *argv[])
{
	int fd = -1;
	if(argc < 2)
	{
		printf("Argument is too few\n");
		return 1;
	}

	/*open */
	fd = open(argv[1],O_RDWR);
	if(fd < 0)
	{
		printf("open %s failed\n",argv[1]);
		return 2;
	}
	/*init mpu6050*/
	init_mpu6050(fd);

	while(1)
	{
		sleep(2);
		/*read and print data from 6050*/
		printf("Accel-X:0x%x\n",read_accelx(fd));
		printf("Accel-Y:0x%x\n",read_accely(fd));
		printf("Accel-Z:0x%x\n",read_accelz(fd));
		printf("Temp:0x%x\n",read_temp(fd));
		printf("GYRO-X:0x%x\n",read_gyrox(fd));
		printf("GYRO-Y:0x%x\n",read_gyroy(fd));
		printf("GYRO-z:0x%x\n",read_gyroz(fd));
		printf("\n");
	}
	/*close*/
	close(fd);
	fd = -1;
	return 0;
}
book@100ask:~/Linux_4412/mydrivercode/i2c/arr_rw$ cat mpu6050_op_rw.c 
#include "mpu6050.h"
static int read_data_from_mpu6050(int fd,unsigned char reg, unsigned char *pdata)
{
	int ret;
	unsigned char buf[1] = {reg};
	ret = write(fd,buf,1);
	if(ret != 1)
	{
		printf("write reg failed, in read_data_from_mpu6050\n");
		return -1;
	}
	buf[0] = 0;
	ret = read(fd, buf, 1);
	if(ret != 1)
	{
		printf("read data failed, in read_data_from_mpu6050\n");
		return -1;
	}

	*pdata = buf[0];
	return 0;
}

static int write_data_to_mpu6050(int fd,unsigned char reg, unsigned char data)
{
	unsigned char buf[2] = {reg,data};
	int ret = 0;
	ret = write(fd, buf, 2);
	if(ret != 2)
	{
		printf("write data failed, in read_data_from_mpu6050\n");
		return -1;
	}
	return 0;
}

int init_mpu6050(int fd)
{
	int ret = 0;
	ret = ioctl(fd,I2C_TENBIT,0);
	if(ret < 0)
	{
		printf("ioctl I2C_TENBIT failed, in init_mpu6050\n");
		return -1;
	}
	ret = ioctl(fd,I2C_SLAVE,0x68);
	if(ret < 0)
	{
		printf("ioctl I2C_TENBIT failed, in init_mpu6050\n");
		return -1;
	}
	ret = write_data_to_mpu6050(fd,PWR_MGMT_1,0x00);
	ret += write_data_to_mpu6050(fd,SMPLRT_DIV,0x07);
	ret += write_data_to_mpu6050(fd,ACCEL_CONFIG,0x19);
	ret += write_data_to_mpu6050(fd,GYRO_CONFIG,0xF8);
	if(ret < 0)
	{
		printf("write init data to mpu6050 failed,in init_mpu6050\n");
		return -1;
	}

	return 0;
}

int read_accelx(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,ACCEL_XOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,ACCEL_XOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read accel x value failed,in read_accelx\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_accely(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,ACCEL_YOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,ACCEL_YOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read accel y value failed,in read_accely\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_accelz(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,ACCEL_ZOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,ACCEL_ZOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read accel z value failed,in read_accelz\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_temp(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,TEMP_OUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,TEMP_OUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read temp value failed,in read_temp\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_gyrox(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,GYRO_XOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,GYRO_XOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read gyro x value failed,in read_gyrox\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_gyroy(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,GYRO_YOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,GYRO_YOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read gyro y value failed,in read_gyroy\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_gyroz(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,GYRO_ZOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,GYRO_ZOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read gyro z value failed,in read_gyroz\n");
		return -1;
	}
	else
	{
		return val;
	}
}

5.2.2 调用ioctl实现接收、发送

#ifndef MPU_6050_H
#define MPU_6050_H

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int init_mpu6050(int fd);
int read_accelx(int fd);
int read_accely(int fd);
int read_accelz(int fd);
int read_temp(int fd);
int read_gyrox(int fd);
int read_gyroy(int fd);
int read_gyroz(int fd);

#define SMPLRT_DIV 0x19
#define CONFIG 0x1A
#define GYRO_CONFIG 0x1B
#define ACCEL_CONFIG 0x1C

#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48

#define PWR_MGMT_1  0x6B

#define I2C_SLAVE	0x0703	/* Use this slave address */
#define I2C_TENBIT	0x0704	/* 0 for 7 bit addrs, != 0 for 10 bit */
#define I2C_RDWR    0x0707  /* Combined R/W transfer (one STOP only) */

struct i2c_msg {
	unsigned short addr;	/* slave address			*/
	unsigned short flags;
#define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
#define I2C_M_RD		0x0001	/* read data, from slave to master */
	unsigned short len;		/* msg length				*/
	unsigned char *buf;		/* pointer to msg data			*/
};

/*This is the structure as used in the I2C_RDWR ioctl call*/
struct i2c_rdwr_ioctl_data{
	struct i2c_msg *msgs; /* pointers to i2c msgs */
	unsigned int nmsgs;         /* number of i2c_msgs */
};

#endif

#include "mpu6050.h"

static int read_data_from_mpu6050(int fd,unsigned char slave,unsigned char reg,unsigned char *pdata)
{
	struct i2c_rdwr_ioctl_data work = {NULL};
	struct i2c_msg msgs[2] = {{0}};
	unsigned char buf1[1] = {reg};
	unsigned char buf2[1] = {0};
	int ret = 0;

	work.msgs = msgs;
	work.nmsgs = 2;

	msgs[0].addr = slave;
	msgs[0].flags = 0;
	msgs[0].buf = buf1;
	msgs[0].len = 1;

	msgs[1].addr = slave;
	msgs[1].flags = I2C_M_RD;
	msgs[1].buf = buf2;
	msgs[1].len = 1;

	ret = ioctl(fd,I2C_RDWR,&work);
	if(ret < 0)
	{
		printf("ioctl I2C_RDWR failed,in read_data_from_mpu6050\n");
		return -1;
	}
	else
	{
		*pdata = buf2[0];
		return 0;
	}
}

static int write_data_to_mpu6050(int fd,unsigned char slave,unsigned char reg,unsigned char data)
{
	struct i2c_rdwr_ioctl_data work = {NULL};
	struct i2c_msg msg = {0};
	unsigned char buf[2] = {reg,data};
	int ret = 0;

	work.msgs = &msg;
	work.nmsgs = 1;

	msg.addr = slave;
	msg.flags = 0;
	msg.buf = buf;
	msg.len = 2;

	ret = ioctl(fd,I2C_RDWR,&work);
	if(ret < 0)
	{
		printf("ioctl I2C_RDWR failed,in write_data_to_mpu6050\n");
		return -1;
	}
	else
	{
		return 0;
	}
}

int init_mpu6050(int fd)
{
	int ret = 0;

	ret = ioctl(fd,I2C_TENBIT,0);
	if(ret < 0)
	{
		printf("ioctl I2C_TENBIT failed,in init_mpu6050\n");
		return -1;
	}

	ret = ioctl(fd,I2C_SLAVE,0x68);
	if(ret < 0)
	{
		printf("ioctl I2C_TENBIT failed,in init_mpu6050\n");
		return -1;
	}

	ret = write_data_to_mpu6050(fd,0x68,PWR_MGMT_1,0x00);
	ret += write_data_to_mpu6050(fd,0x68,SMPLRT_DIV,0x07);
	ret += write_data_to_mpu6050(fd,0x68,ACCEL_CONFIG,0x19);
	ret += write_data_to_mpu6050(fd,0x68,GYRO_CONFIG,0xF8);
	if(ret < 0)
	{
		printf("write init data to mpu6050 failed,in init_mpu6050\n");
		return -1;
	}

	return 0;
}

int read_accelx(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,0x68,ACCEL_XOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,0x68,ACCEL_XOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read accel x value failed,in read_accelx\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_accely(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,0x68,ACCEL_YOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,0x68,ACCEL_YOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read accel y value failed,in read_accely\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_accelz(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,0x68,ACCEL_ZOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,0x68,ACCEL_ZOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read accel z value failed,in read_accelz\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_temp(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,0x68,TEMP_OUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,0x68,TEMP_OUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read temp value failed,in read_temp\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_gyrox(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,0x68,GYRO_XOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,0x68,GYRO_XOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read gyro x value failed,in read_gyrox\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_gyroy(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,0x68,GYRO_YOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,0x68,GYRO_YOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read gyro y value failed,in read_gyroy\n");
		return -1;
	}
	else
	{
		return val;
	}
}

int read_gyroz(int fd)
{
	unsigned short val = 0;
	unsigned char d = 0;
	int ret = 0;

	ret = read_data_from_mpu6050(fd,0x68,GYRO_ZOUT_L,&d);
	val = d;

	ret = read_data_from_mpu6050(fd,0x68,GYRO_ZOUT_H,&d);
	val |= d << 8;

	if(ret < 0)
	{
		printf("read gyro z value failed,in read_gyroz\n");
		return -1;
	}
	else
	{
		return val;
	}
}

缺点:

1. 需要应用程序开发人员查阅原理图和芯片手册,增加了他们的开发负担

2. 开发出的应用程序缺乏可移植性

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/381725.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决“vue“不是内部或外部命令问题

通过vue指令使用脚手架初始化项目时&#xff0c;在cmd中输入vue init webpack vue_project&#xff0c;终端报错&#xff1a; vue 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。解决方案&#xff1a; 1、确定npm是否已正确安装&#xff0c;在cmd中输入如下…

AcWing算法提高课-3.1.3香甜的黄油

宣传一下算法提高课整理 <— CSDN个人主页&#xff1a;更好的阅读体验 <— 题目传送门点这里 题目描述 农夫John发现了做出全威斯康辛州最甜的黄油的方法&#xff1a;糖。 把糖放在一片牧场上&#xff0c;他知道 N 只奶牛会过来舔它&#xff0c;这样就能做出能卖好价…

Dynamics 365 环境搭建 之 SQL Server 2019问题汇总

前言 随着产品的迭代&#xff0c;SQL Server 2016 已经是7-8年前的产品&#xff0c;新环境的搭建必然要随之更新。截至目前&#xff08;2023年3月&#xff09;Dynamics 365 9.1 op版本的软件要求如下。 Windows ServerSQL ServerWindows Server 2019 标准 Windows Server 20…

Vue2与Vue3响应式的详解与比对

目录前言一&#xff0c;响应式的理解1.1 Mvvm模式的复习1.2 什么是响应式二&#xff0c; Vue2中响应式的应用三&#xff0c;Vue2响应式的原理及实现3.1 数据代理3.2 数据代理的原理3.3 数据劫持四&#xff0c;Vue3中响应式的应用3.1 ref与reactive3.2 ref函数的使用讲解3.3 为什…

Anaconda配置Python新版本tensorflow库(CPU、GPU通用)的方法

本文介绍在Anaconda环境中&#xff0c;下载并配置Python中机器学习、深度学习常用的新版tensorflow库的方法。 在之前的两篇文章基于Python TensorFlow Estimator的深度学习回归与分类代码——DNNRegressor&#xff08;https://blog.csdn.net/zhebushibiaoshifu/article/detail…

圣杯布局的实现方式

1.什么是圣杯布局&#xff1f; 左右盒子固定&#xff0c;中间盒子自适应 2.实现方式 &#xff08;1&#xff09;flex布局 思路&#xff1a;左右盒子给固定的宽高&#xff0c;中间盒子flex:1 <!DOCTYPE html> <html lang"en"> <head> <met…

JavaSE学习进阶 day1_01 static关键字和静态代码块的使用

好的现在我们进入进阶部分的学习&#xff0c;看一张版图&#xff1a; 前面我们已经学习完基础班的内容了&#xff0c;现在我们已经来到了第二板块——基础进阶&#xff0c;这部分内容就不是那么容易了。学完第二板块&#xff0c;慢慢就在向java程序员靠拢了。 面向对象进阶部分…

入门力扣自学笔记240 C++ (题目编号:2373)

2373. 矩阵中的局部最大值 题目&#xff1a; 给你一个大小为 n x n 的整数矩阵 grid 。 生成一个大小为 (n - 2) x (n - 2) 的整数矩阵 maxLocal &#xff0c;并满足&#xff1a; maxLocal[i][j] 等于 grid 中以 i 1 行和 j 1 列为中心的 3 x 3 矩阵中的 最大值 。 换句…

学习渗透测试,考CISP-PTE还是考NISP-PT证书呢?

其实两者都可以&#xff0c;但是要看考生的实际需求&#xff01; 为什么说两者都可以&#xff1f; 两个证书都由中国信息安全测评中心颁发&#xff0c;CISP-PTE&#xff08;注册信息安全渗透测试工程师&#xff09;,NISP-PT&#xff08;国家信息安全水平考试渗透测试工程师),…

月薪30k测试岗技术要求,简简单单,你学废了吗?

如果还只会点点点&#xff0c;那么可以往自动化测试的方向发展&#xff0c;然后再往测试开发的方向发展&#xff0c;做一个测试开发的工程师&#xff0c;这样薪资是非常可观的。当然过程中需要学习很多的知识&#xff0c;比如&#xff1a;编程语言&#xff0c;自动化测试框架&a…

2023年工程师中级和高级有什么区别,他们评审的要求有哪些不同?

2023年工程师中级和高级有什么区别&#xff0c;他们评审的要求有哪些不同&#xff1f; 职称主要分为初、中、高三个等级。在大部分地区都是逐级申报的&#xff0c;先初级再中级最后高级。不少人都想直接评中级&#xff0c;这是不可行的的&#xff0c;除少数地区破格来说&#x…

软件测试工程师该怎么做自己的职业规划呢

软件测试需求量不仅稳健&#xff0c;还会加大 疫情前&#xff0c;人们的“吃、穿、住、用、行”方方面面都有对应APP软件。疫情后&#xff0c;复工最快&#xff0c;最迅速的企业也都是通过互联网技术实现。 过去&#xff0c;互联网技术只是让某些企业活的好。未来&#xff0c…

面经-Spring框架相关

面试题 Spring、SpringMVC、SpringBoot的区别 Spring是轻量级的开发框架&#xff0c;主要提供了IOC依赖注入容器和AOP面向切面编程的功能。 SpringMVC是基于Spring的一个用来解决Web开发的问题&#xff0c;主要处理web开发中路径映射和视图渲染等 SpringBoot是融合了Spring…

关于算力的未来,新一代PowerEdge告诉你答案

从ChatGPT等大模型海量参数的训练&#xff0c;自动驾驶领域感知模型的训练与仿真&#xff0c;到蛋白质机构预测、流体力学仿真等AIScience&#xff0c;再到矿山、交通、能源等部署广泛的边缘计算设备……如今&#xff0c;我们愈发确切地认识到&#xff0c;算力在数字经济时代不…

Ubuntu系统设置开机自启

在测试国产操作系统&#xff1a;银河麒麟、UOS统信机器的过程中&#xff0c;发现开机不自启&#xff0c;总结以下几种方式实现自启 一.rc.local rc.local脚本是一个Ubuntu开机后自动执行的脚本&#xff0c;可以在脚本内添加行指令&#xff0c;该脚本位于/etc/路径下&#xff…

【Pytorch】Pytorch深度学习实战教程:超分辨率重建AI与环境搭建

一、基础开发环境搭建 1&#xff09;cuda安装 需要根据自己的显卡的型号选择支持的CUDA版本 显卡驱动查看&#xff1a; 鼠标右键 CUDA安装版本查看&#xff1a;https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html 注意看自己的电脑配置&#xff0c;我的…

基于麻雀算法改进的BP神经网络坑基监测,BP神经网络详细原理,

目标 背影 BP神经网络的原理 BP神经网络的定义 BP神经网络的基本结构 BP神经网络的神经元 BP神经网络的激活函数&#xff0c; BP神经网络的传递函数 麻雀算法原理 麻雀算法主要参数 麻雀算法流程图 麻雀算法优化测试函数代码 基于麻雀算法改进的BP神经网络坑基监测 数据 matlab…

matlab-数据和数据运算

学习视频基本数据类型1.1 整型与浮点型在matlab中同样有8、16、32、64bit的数据大小之分&#xff0c;同时也可以叠加signed(有符号)和unsigned(无符号)的区别&#xff0c;默认数据类型为double(双精度浮点型)参考其他博客的详述1.2 复数还有一些其他常用的函数方法&#xff1a;…

STM32之定时器

定时器软件定时缺点&#xff1a;不精确&#xff0c;占用CPU资源定时器工作原理使用精准的时基&#xff0c;通过硬件的方式&#xff0c;实现定时功能。定时器的核心是计数器。通用定时器框图该框图主要分成四部分&#xff1a;时钟产生器、时基单元、输入捕获、输出比较时钟产生器…

springboot通过aop实现全局日志(是否自定义注解都可以)

内容参考自以下两个链接1、springboot中使用AOP切面完成全局日志_aop全局日志_邹飞鸣的博客-CSDN博客使用AOP记录日志_aop日志_trusause的博客-CSDN博客第一个链接思路很清晰,讲的也很详细,第二个链接讲了自定义注解为了便于自己理解做了以下整理目录 1.aspectj基本概念 2.添加…