3. I2C通讯#
本章简单介绍使用I2C总线与外部设备的通讯,讲解I2C设备的简单使用
3.1. 本章节的代码仓库#
1#如之前有获取则可跳过
2#获取仓库
3git clone https://gitee.com/LubanCat/lubancat_rk_code_storage.git
4
5#代码所在的位置
6lubancat_rk_code_storage/quick_start/i2c
3.2. i2c#
I2C(Inter-Integrated Circuit)是一种通用的总线协议。它是由Philips(飞利浦)公司, 现NXP(恩智浦)半导体开发的一种简单的双向两线制总线协议标准。
LubanCat-RK系列板卡的 I2C 控制器支持下列功能
兼容 I2C 与 SMBus 总线
仅支持主模式下的 I2C 总线
软件可编程时钟频率支持到 400kbps,最高可达 1000kbps
支持 7 位和 10 位寻址模式
一次中断或轮询至多 32 个字节的数据传输
LubanCat-RK3588系列板子的通用i2c设备有两个(如下图所示), 除此之外其他的GPIO也可以复用成I2C设备,需要自行根据引脚复用图去配置, 比如说LubanCat-4的40pin引脚最大可支持5个i2c接口使用
I2C |
引脚 |
功能 |
|---|---|---|
I2C-SCL |
5,28 |
i2c的时钟信号线 |
I2C-SDA |
3,27 |
i2c的数据线 |
3.3. IIC通信接口复用说明#
在板卡的引脚复用图中,我们可能会看到
I2C5_SDA_M3/I2C5_SCL_M3
I2C5_SDA_M2/I2C5_SCL_M2
可以看到他们之间的区别是后缀名的不同,这个后缀名表示I2C设备的将自己的引脚复用到不同的GPIO引脚的编号, 可以理解成下图的样式
复用出来的多组引脚是不能同时使用的, 如果同时设置则会根据设备节点的加载时间所覆盖,最终只有最后配置的复用引脚可以使用。
所以在使能引脚时需要注意是否为重复复用
3.4. IIC通信接口使能#
LubanCat-RK3588系列板子大多数的IIC接口在默认情况是关闭状态的,需要使能才能使用
注意
LubanCat-4的两个通用I2C接口在默认情况下被设置为I2C的功能且默认是使能状态, 所以对于LubanCat-4的板卡两个通用I2C设备是不需用使能的。
3.4.1. fire-config#
1#进入工具配置
2sudo fire-config
3
4#移动光标到下图的位置
5#按确认键进入配置
选择你要打开的I2C接口,这里以打开I2C-3为例
使用方向键移动光标到
I2C-3按 "空格键" 选中I2C-3(出现 "*" ),如下图
按 "确认键" 进行设置
按 "Esc键" 退出到终端,运行 sudo reboot 进行重启应用
3.4.2. 配置文件#
可以通过修改 /boot/uEnv/uEnv.txt 进行激活,这里以激活 I2C-3 为例,将带有 I2C-3 的那一行的注释符号去掉 如下图:
然后重启激活设备
备注
如果是直接拔电源的方式重启,会有可能出现文件没能做出修改 (原因:文件未能及时从内存同步到存储设备中,解决方法,在终端上输入 "sync" 再拔电关机)
3.5. 检查I2C设备#
可以通过一下命令查看i2c总线有没有开启
1ls /dev/i2c-*
如下图:
可以看到出现了I2C-xxx
3.6. 连接设备#
将mpu6050接入到i2c-3的总线上,如下图所示
1#板卡与mpu6050连接
2
3板子 ------ mpu6050
43.3V(1) ------ VCC
5GND(6) ------ GND
6SCL(5) ------ SCL
7SDA(3) ------ SDA
3.7. i2c-tools测试#
使用i2c-tools工具包提供了一些非常方便的工具来对系统的I2C总线进行调试, 在板卡的终端中可直接执行以下命令进行安装:
1sudo apt -y install i2c-tools
安装后可使用的命令有i2cdetect、i2cdump、i2cset以及i2cget,用于扫描I2C总线上的设备、读写指定设备的寄存器等。
然后查看挂载在i2c-3上的器件情况,输出内容如下所示:
1root@lubancat:~# i2cdetect -a 3
2WARNING! This program can confuse your I2C bus, cause data loss and worse!
3I will probe file /dev/i2c-3.
4I will probe address range 0x00-0x7f.
5Continue? [Y/n] y
6 0 1 2 3 4 5 6 7 8 9 a b c d e f
700: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
810: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
920: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
1030: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
1140: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
1250: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
1360: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
1470: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
15root@lubancat:~# ^C
其中 “68” 是为MPU6050的设备地址,常用的命令还有以下几个。
1#检测当前系统有几组i2c总线
2i2cdetect -l
3
4#查看i2c-0接口上的设备
5i2cdetect -a 3
6
7#读取指定设备的全部寄存器的值。
8i2cdump -f -y 3 0x68
9
10#读取指定IIC设备的某个寄存器的值,如下读取地址为0x68器件中的0x01寄存器值。
11i2cget -f -y 3 0x68 0x01
12
13#写入指定IIC设备的某个寄存器的值,如下设置地址为0x68器件中的0x01寄存器值为0x6f;
14i2cset -f -y 3 0x68 0x01 0x6f
3.8. 读取陀螺仪传感器数据实验#
3.8.1. 实验说明#
本教程将通过IIC接口读取陀螺仪(MPU6050)的原始数据。 本次实验会以i2c-3做为示例,i2c-5的操作和i2c-3的一样, 当然,如果您没有mpu6050模块,可以通过学习操作mpu6050的方式操作您想要操作的i2c设备 在测试程序中大约每一秒读取并显示一次MPU6050的原始数据
查看IIC设备文件,确保IIC 3接口已经使能
1root@lubancat:~# ls /dev/i2c-*
2/dev/i2c-0 /dev/i2c-1 /dev/i2c-3 /dev/i2c-6
3root@lubancat:~#
其中“i2c-3”就是MPU6050使用到的 IIC 3接口总线。
3.8.2. ioctl函数#
在编写应用程序时需要使用ioctl函数设置i2c相关配置,其函数原型如下
1#include <sys/ioctl.h>
2
3int ioctl(int fd, unsigned long request, ...);
其中对于终端request的值常用的有以下几种
I2C_RETRIES |
设置收不到ACK时的重试次数,默认为1 |
I2C_TIMEOUT |
设置超时时限的jiffies |
I2C_SLAVE |
设置从机地址 |
I2C_SLAVE_FORCE |
强制设置从机地址 |
I2C_TENBIT |
选择地址长度0为7位地址,非0为10位 |
3.8.3. 编写应用程序#
根据ioctl相关参数即可编写与i2c相关的接口函数,读取mpu6050原始数据程序如下
1quick_start/i2c/i2c_mpu6050.c
代码较长复制粘贴容易乱序,可以下载我们提供的源码 i2c_mpu6050.c
1#include <stdio.h>
2#include <stdlib.h>
3#include <stdint.h>
4#include <unistd.h>
5#include <sys/types.h>
6#include <sys/stat.h>
7#include <fcntl.h>
8#include <linux/i2c.h>
9#include <linux/i2c-dev.h>
10#include <sys/ioctl.h>
11
12/*寄存器地址*/
13#define SMPLRT_DIV 0x19
14#define PWR_MGMT_1 0x6B
15#define CONFIG 0x1A
16#define ACCEL_CONFIG 0x1C
17
18#define ACCEL_XOUT_H 0x3B
19#define ACCEL_XOUT_L 0x3C
20#define ACCEL_YOUT_H 0x3D
21#define ACCEL_YOUT_L 0x3E
22#define ACCEL_ZOUT_H 0x3F
23#define ACCEL_ZOUT_L 0x40
24#define GYRO_XOUT_H 0x43
25#define GYRO_XOUT_L 0x44
26#define GYRO_YOUT_H 0x45
27#define GYRO_YOUT_L 0x46
28#define GYRO_ZOUT_H 0x47
29#define GYRO_ZOUT_L 0x48
30
31//从机地址 MPU6050地址
32#define Address 0x68
33
34//MPU6050操作相关函数
35static int mpu6050_init(int fd,uint8_t addr);
36static int i2c_write(int fd, uint8_t addr,uint8_t reg,uint8_t val);
37static int i2c_read(int fd, uint8_t addr,uint8_t reg,uint8_t * val);
38static short GetData(int fd,uint8_t addr,unsigned char REG_Address);
39
40int main(int argc,char *argv[] )
41{
42 int fd;
43 fd = I2C_SLAVE;
44
45 if(argc < 2){
46 printf("Wrong use !!!!\n");
47 printf("Usage: %s [dev]\n",argv[0]);
48 return -1;
49 }
50
51 fd = open(argv[1], O_RDWR); // open file and enable read and write
52 if (fd < 0){
53 printf("Can't open %s \n",argv[1]); // open i2c dev file fail
54 exit(1);
55 }
56
57 //初始化MPU6050
58 mpu6050_init(fd,Address);
59 while(1){
60 usleep(1000 * 10);
61 printf("ACCE_X:%6d\n ", GetData(fd,Address,ACCEL_XOUT_H));
62 usleep(1000 * 10);
63 printf("ACCE_Y:%6d\n ", GetData(fd,Address,ACCEL_YOUT_H));
64 usleep(1000 * 10);
65 printf("ACCE_Z:%6d\n ", GetData(fd,Address,ACCEL_ZOUT_H));
66 usleep(1000 * 10);
67 printf("GYRO_X:%6d\n ", GetData(fd,Address,GYRO_XOUT_H));
68 usleep(1000 * 10);
69 printf("GYRO_Y:%6d\n ", GetData(fd,Address,GYRO_YOUT_H));
70 usleep(1000 * 10);
71 printf("GYRO_Z:%6d\n\n ", GetData(fd,Address,GYRO_ZOUT_H));
72 sleep(1);
73 }
74
75 close(fd);
76
77 return 0;
78}
79
80static int mpu6050_init(int fd,uint8_t addr)
81{
82 i2c_write(fd, addr,PWR_MGMT_1,0x00); //配置电源管理,0x00,正常启动
83 i2c_write(fd, addr,SMPLRT_DIV,0x07); //设置MPU6050的输出分频既设置采样
84 i2c_write(fd, addr,CONFIG,0x06); //配置数字低通滤波器和帧同步引脚
85 i2c_write(fd, addr,ACCEL_CONFIG,0x01); //设置量程和 X、Y、Z 轴加速度自检
86
87 return 0;
88}
89
90static int i2c_write(int fd, uint8_t addr,uint8_t reg,uint8_t val)
91{
92 int retries;
93 uint8_t data[2];
94
95 data[0] = reg;
96 data[1] = val;
97
98 //设置地址长度:0为7位地址
99 ioctl(fd,I2C_TENBIT,0);
100
101 //设置从机地址
102 if (ioctl(fd,I2C_SLAVE,addr) < 0){
103 printf("fail to set i2c device slave address!\n");
104 close(fd);
105 return -1;
106 }
107
108 //设置收不到ACK时的重试次数
109 ioctl(fd,I2C_RETRIES,5);
110
111 if (write(fd, data, 2) == 2){
112 return 0;
113 }
114 else{
115 return -1;
116 }
117
118}
119
120static int i2c_read(int fd, uint8_t addr,uint8_t reg,uint8_t * val)
121{
122 int retries;
123
124 //设置地址长度:0为7位地址
125 ioctl(fd,I2C_TENBIT,0);
126
127 //设置从机地址
128 if (ioctl(fd,I2C_SLAVE,addr) < 0){
129 printf("fail to set i2c device slave address!\n");
130 close(fd);
131 return -1;
132 }
133
134 //设置收不到ACK时的重试次数
135 ioctl(fd,I2C_RETRIES,5);
136
137 if (write(fd, ®, 1) == 1){
138 if (read(fd, val, 1) == 1){
139 return 0;
140 }
141 }
142 else{
143 return -1;
144 }
145}
146
147static short GetData(int fd,uint8_t addr,unsigned char REG_Address)
148{
149 char H, L;
150
151 i2c_read(fd, addr,REG_Address, &H);
152 usleep(1000);
153 i2c_read(fd, addr,REG_Address + 1, &L);
154 return (H << 8) +L;
155}
保存退出,接下来再进行编译运行
1gcc i2c_mpu6050.c -o mpu6050
2
3sudo ./mpu6050 /dev/i2c-3
效果如下图所示
三次数据的采集
3.9. 更多#
更多关于rk-i2c的驱动以及GPIO模拟I2C等可以查看: rk开源手册 /Common/I2C
3.10. 参考资料#
本文档主要提供有一定经验的使用者快速入门使用,对于初学者可查看以下文档:
rk开源资料(github): 《开源手册》