LV10-11-I2C驱动-02-应用层使用I2C驱动

本文主要是Linux中应用层直接使用I2C驱动的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows windows11
Ubuntu Ubuntu16.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
SecureCRT Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日
Linux开发板 华清远见 底板: FS4412_DEV_V5 核心板: FS4412 V2
u-boot 2013.01
点击查看本文参考资料
参考方向参考原文
------
点击查看相关文件下载
文件下载链接
------

在linux内核中为我们提供了一个I2C字符设备驱动,我们直接在linux内核中配置使用就可以了。

一、linux配置

1. 设备树

exynos4412平台每个i2c通道的信息是通过设备树提供的,因此需要首先在exynos4412-fs4412.dts中增加5通道(MPU6050就挂载在这个通道上)的节点。关于I2C设备树的书写实例,我们可以参考linux内核源码中的这个文档:

1
Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt

然后我们在linux内核源码的设备树中添加以下内容:

1
2
3
4
5
6
7
8
9
i2c@138B0000{
#address-cells = <1>;
#size-cells = <0>;
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus-freq = <20000>;
pinctrl-0 = <&i2c5_bus>;
pinctrl-names = "default";
status = "okay";
};

2. 内核配置

另外我们还需要在内核中开启I2C字符设备驱动:

1
2
3
Device Drivers  --->
-*- I2C support --->
<*> I2C device interface
image-20220927162633308

3. 编译测试

然后我们就可以重新编译内核和设备树文件,我们在linux内核目录下执行:

1
2
make dtbs    # 编译设备树
make uImage # 编译内核

编译完成后,我们将内核和设备树文件拷贝到相应的目录中,然后重启开发板,重启之后我们将会在 /dev/目录下看到i2c开头的设备节点:

1
2
[root@farsight ]# ls /dev/i2c*
i2c-0 i2c-5

当我们读写i2c设备的时候就是通过这个设备节点进行的。

二、read和write读写I2C

我们可以直接通过read和write函数读写I2C设备,此处以MPU6050为例。

1. 使用步骤

01

2. mpu6050.c

2.1 read_data_from_mpu6050()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* @Function : read_data_from_mpu6050
* @brief : 从MPU6050指定寄存器读一个字节数据
* @param fd : int 类型,文件描述符,代表设备
* @param reg : unsigned char 类型,寄存器地址
* @param pdata: unsigned char * 类型,读取到的数据存放内存空间的首地址
* @return : int类型,成功返回0,失败返回-1
* @Description: 内核中会自己完成 I2C 器件寻址,以及读写的判断,所以这里我们就直接发送寄存器地址即可
* 初始化MPU6050的时候将从机地址设置到 fd 关联的 I2C_dev上
*/
static int read_data_from_mpu6050(int fd, unsigned char reg, unsigned char *pdata)
{
/* 0.相关变量定义 */
int ret = 0;
unsigned char buf[1] = {reg};
/* 1.向从机发送要读的寄存器 */
ret = write(fd, buf, 1); /* */
if (ret != 1)
{
printf("write reg failed,in read_data_from_mpu6050!\n");
return -1;
}
/* 3.读取数据 */
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;
}

2.2 write_data_to_mpu6050()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* @Function : write_data_to_mpu6050
* @brief : 向MPU6050指定寄存器写一个字节数据
* @param fd : int 类型,文件描述符,代表设备
* @param reg : unsigned char 类型,寄存器地址
* @param data : unsigned char 类型,要写入的数据
* @return : int类型,成功返回0,失败返回-1
* @Description:
*/
static int write_data_to_mpu6050(int fd, unsigned char reg, unsigned char data)
{
/* 0.相关变量定义 */
unsigned char buf[2] = {reg, data};
int ret = 0;
/* 1.向MPU6050发送要写入的寄存器地址和要写入的数据 */
ret = write(fd, buf, 2);
if (ret != 2)
{
printf("write data failed,in write_data_to_mpu6050\n");
return -1;
}

return 0;
}

2.3 init_mpu6050()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* @Function : init_mpu6050
* @brief : 初始化MPU6050
* @param fd : int 类型,文件描述符,代表设备
* @return : int类型,成功返回0,失败返回-1
* @Description:
*/
int init_mpu6050(int fd)
{
/* 0.相关变量定义 */
int ret = 0;
/* 1.设置I2C使用7位地址 */
ret = ioctl(fd, I2C_TENBIT, 0); /* 0表示不使用10位地址,使用7位地址 */
if (ret < 0)
{
printf("ioctl I2C_TENBIT failed,in init_mpu6050\n");
return -1;
}
/* 2.将从设备地址关联到I2C */
ret = ioctl(fd, I2C_SLAVE, 0x68);
if (ret < 0)
{
printf("ioctl I2C_TENBIT failed,in init_mpu6050\n");
return -1;
}
/* 3.设置MPU6050 */
ret = write_data_to_mpu6050(fd, PWR_MGMT_1, 0x00); /* 电源管理,典型值:0x00(正常启用) */
ret += write_data_to_mpu6050(fd, SMPLRT_DIV, 0x07); /* 陀螺仪采样率 */
ret += write_data_to_mpu6050(fd, ACCEL_CONFIG, 0x19); /* 加速计自检、测量范围及高通滤波频率,加速计自检、测量范围,典型值:0x19(不自检,+/-G) */
ret += write_data_to_mpu6050(fd, GYRO_CONFIG, 0xF8); /* 陀螺仪自检及测量范围,典型值:0xF8(不自检,+/-2000deg/s) */
if (ret < 0)
{
printf("write init data to mpu6050 failed,in init_mpu6050\n");
return -1;
}

return 0;
}

2.4 read_accelx()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @Function : read_accelx
* @brief : 读取x方向加速度值
* @param fd : int 类型,文件描述符,代表设备
* @return : int类型,读取到的数据
* @Description:
*/
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;
}
}

2.5 read_accely()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @Function : read_accely
* @brief : 读取y方向加速度值
* @param fd : int 类型,文件描述符,代表设备
* @return : int类型,读取到的数据
* @Description:
*/
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;
}
}

2.6 read_accelz()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @Function : read_accelz
* @brief : 读取z方向加速度值
* @param fd : int 类型,文件描述符,代表设备
* @return : int类型,读取到的数据
* @Description:
*/
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;
}
}

2.7 read_temp()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @Function : read_temp
* @brief : 读取温度值
* @param fd : int 类型,文件描述符,代表设备
* @return : int类型,读取到的数据
* @Description:
*/
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;
}
}

2.8 read_gyrox()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @Function : read_gyrox
* @brief : 读取x方向角速度值
* @param fd : int 类型,文件描述符,代表设备
* @return : int类型,读取到的数据
* @Description:
*/
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;
}
}

2.9 read_gyroy()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @Function : read_gyroy
* @brief : 读取y方向角速度值
* @param fd : int 类型,文件描述符,代表设备
* @return : int类型,读取到的数据
* @Description:
*/
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;
}
}

2.10 read_gyroz()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @Function : read_gyroz
* @brief : 读取z方向角速度值
* @param fd : int 类型,文件描述符,代表设备
* @return : int类型,读取到的数据
* @Description:
*/
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;
}
}

3. mpu6050.h

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#ifndef __MPU6050_H__
#define __MPU6050_H__

/* 头文件 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

/* 宏定义 */
#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(正常启用) */

/* 原来定义在内核源码的 include/uapi/linux/i2c/i2c-dev.h */
#define I2C_SLAVE 0x0703 /* Use this slave address */
#define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */

/* printk和printf 打印输出的颜色定义 */
/* 前景色(字体颜色) */
#define CLS "\033[0m" /* 清除所有颜色 */
#define BLACK "\033[1;30m" /* 黑色加粗字体 */
#define RED "\033[1;31m" /* 红色加粗字体 */
#define GREEN "\033[1;32m" /* 绿色加粗字体 */
#define YELLOW "\033[1;33m" /* 黄色加粗字体 */
#define BLUE "\033[1;34m" /* 蓝色加粗字体 */
#define PURPLE "\033[1;35m" /* 紫色加粗字体 */
#define CYAN "\033[1;36m" /* 青色加粗字体 */
#define WHITE "\033[1;37m" /* 白色加粗字体 */
#define BOLD "\033[1m" /* 加粗字体 */

/* 函数声明 */
int init_mpu6050(int fd); /* 初始化 MPU6050 */
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);

#endif

4. mpu6050_app.c

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>     /* printf */
#include <sys/types.h> /* open */
#include <sys/stat.h> /* open */
#include <fcntl.h> /* open */
#include <unistd.h> /* close sleep */
#include <sys/ioctl.h> /* ioctl */
#include "mpu6050.h"
int main(int argc, char *argv[])
{
/* 0.相关变量定义 */
int fd = -1; /* 文件描述符 */
/* 1.判断参数个数是否合法 */
/* 1.1判断参数数量 */
if (argc < 2)
{
printf("\nusage:\n");
printf("%s /dev/dev_name\n", argv[0]);
return -1;
}
/* 2.打开字符设备 */
if ((fd = open(argv[1], O_RDWR)) < 0) /* 默认是阻塞的 */
{
printf("open %s failed!\n", argv[1]);
return -1;
}
/* 3.初始化 MPU6050 */
init_mpu6050(fd);
/* 4.循环读取并打印数据 */
while (1)
{
sleep(2);
/* read and print data from MPU6050 */
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");
}
/* 4.关闭字符设备 */
close(fd);
fd = -1;

return 0;
}

5. 编译测试

编译的时候注意使用交叉编译工具链,测试的时候,我们使用的设备节点是在系统启动的时候就创建好了的,使用的设备节点是:

1
/dev/i2c-5

三、ioctl读写I2C

我们可以通过ioctl一个函数读写I2C设备,此处以MPU6050为例。

1. 相关结构体和宏

只使用ioctl来读写i2c设备的话,我们需要用到3个宏和两个结构体,不过为了方便,这几个会在mpu6050.h中重新实现。

1.1 三个宏

这里需要用到三个宏定义,它们定义在linux内核源码的这个目录下:

1
include/uapi/linux/i2c-dev.h

我们可以看到这三个宏定义如下:

1
2
3
#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) */

后边为了方便使用,将这三个宏定义定义到 mpu6050.h 文件中去。

1.2 struct i2c_rdwr_ioctl_data

这个结构体定义在linux内核源码的这个目录下:

1
include/uapi/linux/i2c-dev.h

我们打开这个文件,会看到结构体定义如下:

1
2
3
4
5
/* This is the structure as used in the I2C_RDWR ioctl call */
struct i2c_rdwr_ioctl_data {
struct i2c_msg __user *msgs; /* pointers to i2c_msgs */
__u32 nmsgs; /* number of i2c_msgs */
};

【成员说明】

  • msgs : struct i2c_msg类型指针变量,指向要发送给i2c设备的消息的首地址。
  • nmsgs : __u32 类型,表示有几个消息需要发送。

1.3 struct i2c_msg

这个结构体定义在linux内核中的这个目录下:

1
include/uapi/linux/i2c.h

我们打开这个文件,该结构体定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 */
#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};

【成员介绍】

  • addr :从机的地址
  • flags : 一个标志,它表示从机设备地址位数,值为0的话表示使用7位从设备地址。
  • buf :我们要发送或者接收数据的内存空间首地址
  • len :表示buf中有几个数据需要发送或者接收,一般来说向i2c设备写数据的话,这个值一般会是2,一个表示寄存器地址,一个表示要写入的值,从i2c设备读数据的话,一般会是1。

2. 使用步骤

02

3. mpu6050.c

3.1 read_data_from_mpu6050()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* @Function : read_data_from_mpu6050
* @brief : 从MPU6050指定寄存器读一个字节数据
* @param fd : int 类型,文件描述符,代表设备
* @param slave: unsigned char 类型,从机地址
* @param reg : unsigned char 类型,寄存器地址
* @param pdata: unsigned char * 类型,读取到的数据存放内存空间的首地址
* @return : int类型,成功返回0,失败返回-1
* @Description:
*/
static int read_data_from_mpu6050(int fd, unsigned char slave, unsigned char reg, unsigned char *pdata)
{
/* 0.相关变量定义 */
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;
/* 1.变量赋值 */
work.msgs = msgs;
work.nmsgs = 2;
/* msgs[0]负责寻址,完成发送从设备地址和要读取的寄存器的地址 */
msgs[0].addr = slave;
msgs[0].flags = 0; /* 表示使用7位从设备地址 */
msgs[0].buf = buf1;
msgs[0].len = 1;
/* msgs[0]负责接收读到的数据,完成 slave_addr + 1,并读取相应寄存器数据 */
msgs[1].addr = slave;
msgs[1].flags = I2C_M_RD;
msgs[1].buf = buf2;
msgs[1].len = 1;
/* 3.读取数据 */
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;
}
}

3.2 write_data_to_mpu6050()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
* @Function : write_data_to_mpu6050
* @brief : 向MPU6050指定寄存器写一个字节数据
* @param fd : int 类型,文件描述符,代表设备
* @param slave: unsigned char 类型,从机地址
* @param reg : unsigned char 类型,寄存器地址
* @param data : unsigned char 类型,要写入的数据
* @return : int类型,成功返回0,失败返回-1
* @Description:
*/
static int write_data_to_mpu6050(int fd, unsigned char slave, unsigned char reg, unsigned char data)
{
/* 0.相关变量定义 */
struct i2c_rdwr_ioctl_data work = {NULL};
struct i2c_msg msg = {0};
unsigned char buf[2] = {reg, data};
int ret = 0;
/* 1.变量赋值 */
work.msgs = &msg;
work.nmsgs = 1;

msg.addr = slave;
msg.flags = 0;
msg.buf = buf;
msg.len = 2;
/* 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;
}
}

3.3 init_mpu6050()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* @Function : init_mpu6050
* @brief : 初始化MPU6050
* @param fd : int 类型,文件描述符,代表设备
* @return : int类型,成功返回0,失败返回-1
* @Description:
*/
int init_mpu6050(int fd)
{
/* 0.相关变量定义 */
int ret = 0;
/* 1.设置I2C使用7位地址 */
ret = ioctl(fd, I2C_TENBIT, 0); /* 0表示不使用10位地址,使用7位地址 */
if (ret < 0)
{
printf("ioctl I2C_TENBIT failed,in init_mpu6050\n");
return -1;
}
/* 2.将从设备地址关联到I2C */
ret = ioctl(fd, I2C_SLAVE, SLAVE_ADDRESS);
if (ret < 0)
{
printf("ioctl I2C_TENBIT failed,in init_mpu6050\n");
return -1;
}
/* 3.设置MPU6050 */
ret = write_data_to_mpu6050(fd, SLAVE_ADDRESS, PWR_MGMT_1, 0x00); /* 电源管理,典型值:0x00(正常启用) */
ret += write_data_to_mpu6050(fd, SLAVE_ADDRESS, SMPLRT_DIV, 0x07); /* 陀螺仪采样率 */
ret += write_data_to_mpu6050(fd, SLAVE_ADDRESS, ACCEL_CONFIG, 0x19); /* 加速计自检、测量范围及高通滤波频率,加速计自检、测量范围,典型值:0x19(不自检,+/-G) */
ret += write_data_to_mpu6050(fd, SLAVE_ADDRESS, GYRO_CONFIG, 0xF8); /* 陀螺仪自检及测量范围,典型值:0xF8(不自检,+/-2000deg/s) */
if (ret < 0)
{
printf("write init data to mpu6050 failed,in init_mpu6050\n");
return -1;
}

return 0;
}

3.4 read_accelx()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @Function : read_accelx
* @brief : 读取x方向加速度值
* @param fd : int 类型,文件描述符,代表设备
* @return : int类型,读取到的数据
* @Description:
*/
int read_accelx(int fd)
{
unsigned short val = 0;
unsigned char d = 0;
int ret = 0;

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

ret = read_data_from_mpu6050(fd, SLAVE_ADDRESS, 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;
}
}

3.5 read_accely()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @Function : read_accely
* @brief : 读取y方向加速度值
* @param fd : int 类型,文件描述符,代表设备
* @return : int类型,读取到的数据
* @Description:
*/
int read_accely(int fd)
{
unsigned short val = 0;
unsigned char d = 0;
int ret = 0;

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

ret = read_data_from_mpu6050(fd, SLAVE_ADDRESS, 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;
}
}

3.6 read_accelz()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @Function : read_accelz
* @brief : 读取z方向加速度值
* @param fd : int 类型,文件描述符,代表设备
* @return : int类型,读取到的数据
* @Description:
*/
int read_accelz(int fd)
{
unsigned short val = 0;
unsigned char d = 0;
int ret = 0;

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

ret = read_data_from_mpu6050(fd, SLAVE_ADDRESS, 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;
}
}

3.7 read_temp()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @Function : read_temp
* @brief : 读取温度值
* @param fd : int 类型,文件描述符,代表设备
* @return : int类型,读取到的数据
* @Description:
*/
int read_temp(int fd)
{
unsigned short val = 0;
unsigned char d = 0;
int ret = 0;

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

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

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

3.8 read_gyrox()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @Function : read_gyrox
* @brief : 读取x方向角速度值
* @param fd : int 类型,文件描述符,代表设备
* @return : int类型,读取到的数据
* @Description:
*/
int read_gyrox(int fd)
{
unsigned short val = 0;
unsigned char d = 0;
int ret = 0;

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

ret = read_data_from_mpu6050(fd, SLAVE_ADDRESS, 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;
}
}

3.9 read_gyroy()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @Function : read_gyroy
* @brief : 读取y方向角速度值
* @param fd : int 类型,文件描述符,代表设备
* @return : int类型,读取到的数据
* @Description:
*/
int read_gyroy(int fd)
{
unsigned short val = 0;
unsigned char d = 0;
int ret = 0;

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

ret = read_data_from_mpu6050(fd, SLAVE_ADDRESS, 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;
}
}

3.10 read_gyroz()

点击查看函数详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @Function : read_gyroz
* @brief : 读取z方向角速度值
* @param fd : int 类型,文件描述符,代表设备
* @return : int类型,读取到的数据
* @Description:
*/
int read_gyroz(int fd)
{
unsigned short val = 0;
unsigned char d = 0;
int ret = 0;

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

ret = read_data_from_mpu6050(fd, SLAVE_ADDRESS, 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;
}
}

4. mpu6050.h

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#ifndef __MPU6050_H__
#define __MPU6050_H__

/* 头文件 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

/* 宏定义 */
#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 SLAVE_ADDRESS 0x68
#define PWR_MGMT_1 0x6B /* 电源管理,典型值:0x00(正常启用) */
/* 原来定义在内核源码的 include/uapi/linux/i2c-dev.h */
#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 (buf的大小)*/
unsigned char *buf; /* pointer to msg data(要发送的数据和要接收的数据都存储在这里)*/
};

/** 原来定义在内核源码的 include/uapi/linux/i2c-dev.h
* 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 */
};

/* 函数声明 */
int init_mpu6050(int fd); /* 初始化 MPU6050 */
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);

#endif

5. mpu6050_app.c

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>     /* printf */
#include <sys/types.h> /* open */
#include <sys/stat.h> /* open */
#include <fcntl.h> /* open */
#include <unistd.h> /* close sleep */
#include <sys/ioctl.h> /* ioctl */
#include "mpu6050.h"
int main(int argc, char *argv[])
{
/* 0.相关变量定义 */
int fd = -1; /* 文件描述符 */
/* 1.判断参数个数是否合法 */
/* 1.1判断参数数量 */
if (argc < 2)
{
printf("\nusage:\n");
printf("%s /dev/dev_name\n", argv[0]);
return -1;
}
/* 2.打开字符设备 */
if ((fd = open(argv[1], O_RDWR)) < 0) /* 默认是阻塞的 */
{
printf("open %s failed!\n", argv[1]);
return -1;
}
/* 3.初始化 MPU6050 */
init_mpu6050(fd);
/* 4.循环读取并打印数据 */
while (1)
{
sleep(2);
/* read and print data from MPU6050 */
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");
}
/* 4.关闭字符设备 */
close(fd);
fd = -1;

return 0;
}

6. 编译测试

编译的时候注意使用交叉编译工具链,测试的时候,我们使用的设备节点是在系统启动的时候就创建好了的,使用的设备节点是:

1
/dev/i2c-5