3. I2C communication

Video introduction to this chapter:

../../../_images/video.png

“31-Use I2C interface on Lubancat”

https://www.bilibili.com/video/BV1Th411T7bd/

This chapter introduces the communication between the I2C bus and external devices in the application layer, and explains the application of the Linux system bus type device driver architecture.

Code repository for this chapter

1
2
3
4
5
6
#You can skip it if you have obtained it before
#get warehouse
git clone https://gitee.com/LubanCat/lubancat_rk_code_storage.git

#location of the code
lubancat_rk_code_storage/quick_start/i2c

This chapter mainly revolves around the LubanCat-RK series boards with 40 pins. The models of the boards are as follow

  • LubanCat-Zero-W

  • LubanCat-Zero-N

  • LubanCat-1

  • LubanCat-1N

  • LubanCat-2

  • LubanCat-2N

3.1. i2c pin

LubanCat-RK series boards have 2 I2C peripherals, namely i2c-3 and i2c-5.

I2C

pin

function

I2C3-SCL

5

i2c3 clock signal line

I2C3-SDA

3

i2c3 data line

I2C5-SCL

28

Clock signal line of i2c5

I2C5-SDA

27

i2c5 data line

As shown below :

未找到图片

Corresponding to the physical 40pin interface

3.2. Enable IIC communication interface

The IIC interface is disabled by default and needs to be enabled before it can be used

3.2.1. Method 1

1
2
3
4
5
#Enter tool configuration
sudo fire-config

#Move the cursor to the position in the picture below
#Press the enter key to enter the configuration
未找到图片

Select the I2C interface you want to open, here is an example to open I2C-3

  1. Use the arrow keys to move the cursor to I2C-3

  2. Press “Space” to select I2C-3 (appears “*” ), as shown below

  3. Press “Enter” to make settings

  4. Press “Esc” to exit to the terminal, run sudo reboot to restart the application

未找到图片

3.2.2. Method 2

Comparison table of each model

Board

Device Tree Plugin Configuration File

LubanCat-Zero-W

uEnvLubanCatZW.txt

LubanCat-Zero-N

uEnvLubanCatZN.txt

LubanCat-1

uEnvLubanCat1.txt

LubanCat-1N

uEnvLubanCat-1N.txt

LubanCat-2

uEnvLubanCat2.txt

New version LubanCat-2

uEnvLubanCat2-V1.txt

LubanCat-2N

uEnvLubanCat2N.txt

You can open /boot/uEnv/board.txt (board is the name of the board you are using) to check whether the i2c-related device device tree plugin is enabled.

Here, take activating I2C-3 as an example, remove the comment mark of the line with I2C-3, as shown in the figure below:

未找到图片

Then reboot to activate the device

注解

If it is restarted by directly unplugging the power supply, there may be files that cannot be modified. ( Reason: The file failed to be synchronized from the internal memory to the storage device in time. The solution is to enter “sync” on the terminal and then unplug the power to shut down )

3.2.3. Check IIC device

You can check whether the i2c bus is turned on with the following command

1
ls /dev/i2c-*

As shown below :

未找到图片

3.3. Connect device

Connect the mpu6050 to the i2c-3 bus, as shown in the figure below

1
2
3
4
5
6
7
#The board is connected to the mpu6050

Board  ------  mpu6050
3.3V(1)  ------  VCC
GND(6)   ------  GND
SCL(5)   ------  SCL
SDA(3)   ------  SDA
未找到图片

3.4. IIC third-party tools - i2c-tools

Use the i2c-tools toolkit to provide some very convenient tools to debug the I2C bus of the system. You can directly execute the following commands in the terminal of the board to install:

1
sudo apt install i2c-tools wget gcc -y

The commands that can be used after installation are i2cdetect, i2cdump, i2cset and i2cget, which are used to scan devices on the I2C bus, read and write registers of specified devices, etc.

Then check the device mounted on i2c-3, the output is as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
root@lubancat:~# i2cdetect -a 3
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-3.
I will probe address range 0x00-0x7f.
Continue? [Y/n] y
        0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
root@lubancat:~# ^C

Among them, “68” is the device address of MPU6050, and the commonly used commands are as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#Detect several sets of i2c buses in the current system.
i2cdetect -l

#View devices on the i2c-0 interface.
i2cdetect -a 3

#Read the values of all registers of the specified device.
i2cdump  -f -y 3 0x68

#Read the value of a register of the specified IIC device,
as follows to read the value of the 0x01 register in the device whose address is 0x68.
i2cget -f -y 3 0x68 0x01

#Write the value of a certain register of the specified IIC device,
as follows to set the value of the 0x01 register in the device whose address is 0x68 to 0x6f;
i2cset -f -y 3 0x68 0x01 0x6f

3.5. Read gyroscope sensor data experiment

3.5.1. Experiment Description

This tutorial will read the raw data of the gyroscope (MPU6050) through the IIC interface. This experiment will take i2c-3 as an example. The operation of i2c-5 is the same as that of i2c-3. Of course, if you don’t have the mpu6050 module, you can operate the i2c device you want to operate by learning how to operate the mpu6050. Read and display the raw data of the MPU6050 approximately every second in the test program.

Check the IIC device file to ensure that the IIC 3 interface is enabled

1
2
3
root@lubancat:~# ls /dev/i2c-*
/dev/i2c-0  /dev/i2c-1  /dev/i2c-3  /dev/i2c-6
root@lubancat:~#

Among them, “i2c-3” is the IIC 3 interface bus used by MPU6050.

3.5.2. ioctl function

When writing an application, you need to use the ioctl function to set the i2c related configuration. The function prototype is as follows

1
2
3
#include <sys/ioctl.h>

int ioctl(int fd, unsigned long request, ...);

Among them, the following are commonly used values for terminal request:

I2C_RETRIES

Set the number of retries when the ACK is not received, the default is 1

I2C_TIMEOUT

Set jiffies with timeout

I2C_SLAVE

Set slave address

I2C_SLAVE_FORCE

Mandatory setting of slave address

I2C_TENBIT

Select address length 0 as 7-bit address, non-zero as 10-bit

3.5.3. Write application

According to the relevant parameters of ioctl, the interface function related to i2c can be written, and the program to read the original data of mpu6050 is as follows

code location
1
quick_start/i2c/i2c_mpu6050.c

The code is long and easy to copy and paste out of order, you can download the source code we provide i2c_mpu6050.c

quick_start/i2c/i2c_mpu6050.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
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>

/*Register address*/
#define SMPLRT_DIV      0x19
#define PWR_MGMT_1      0x6B
#define CONFIG          0x1A
#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 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

//Slave address MPU6050 address
#define Address         0x68

//MPU6050 operation related functions
static int mpu6050_init(int fd,uint8_t addr);
static int i2c_write(int fd, uint8_t addr,uint8_t reg,uint8_t val);
static int i2c_read(int fd, uint8_t addr,uint8_t reg,uint8_t * val);
static short GetData(int fd,uint8_t addr,unsigned char REG_Address);

int main(int argc,char *argv[] )
{
    int  fd;
    fd = I2C_SLAVE;

    if(argc < 2){
        printf("Wrong use !!!!\n");
        printf("Usage: %s [dev]\n",argv[0]);
        return -1;
    }

    fd = open(argv[1], O_RDWR); // open file and enable read and  write
    if (fd < 0){
        printf("Can't open %s \n",argv[1]); // open i2c dev file fail
        exit(1);
    }

    //Initialize MPU6050
    mpu6050_init(fd,Address);
    while(1){
        usleep(1000 * 10);
        printf("ACCE_X:%6d\n ", GetData(fd,Address,ACCEL_XOUT_H));
        usleep(1000 * 10);
        printf("ACCE_Y:%6d\n ", GetData(fd,Address,ACCEL_YOUT_H));
        usleep(1000 * 10);
        printf("ACCE_Z:%6d\n ", GetData(fd,Address,ACCEL_ZOUT_H));
        usleep(1000 * 10);
        printf("GYRO_X:%6d\n ", GetData(fd,Address,GYRO_XOUT_H));
        usleep(1000 * 10);
        printf("GYRO_Y:%6d\n ", GetData(fd,Address,GYRO_YOUT_H));
        usleep(1000 * 10);
        printf("GYRO_Z:%6d\n\n ", GetData(fd,Address,GYRO_ZOUT_H));
        sleep(1);
    }

    close(fd);

    return 0;
}

static int mpu6050_init(int fd,uint8_t addr)
{
    i2c_write(fd, addr,PWR_MGMT_1,0x00);  //Configure power management, 0x00, start normally
    i2c_write(fd, addr,SMPLRT_DIV,0x07);  //Set the output frequency division of MPU6050 and set the sampling
    i2c_write(fd, addr,CONFIG,0x06);  //Configure the digital low-pass filter and frame sync pin
    i2c_write(fd, addr,ACCEL_CONFIG,0x01);  //Set range and X, Y, Z axis acceleration self-test

    return 0;
}

static int i2c_write(int fd, uint8_t addr,uint8_t reg,uint8_t val)
{
    int retries;
    uint8_t data[2];

    data[0] = reg;
    data[1] = val;

    //Set address length: 0 is a 7-bit address
    ioctl(fd,I2C_TENBIT,0);

    //Set slave address
    if (ioctl(fd,I2C_SLAVE,addr) < 0){
        printf("fail to set i2c device slave address!\n");
        close(fd);
        return -1;
    }

    //Set the number of retries when no ACK is received
    ioctl(fd,I2C_RETRIES,5);

    if (write(fd, data, 2) == 2){
        return 0;
    }
    else{
        return -1;
    }

}

static int i2c_read(int fd, uint8_t addr,uint8_t reg,uint8_t * val)
{
    int retries;

    //Set address length: 0 is a 7-bit address
    ioctl(fd,I2C_TENBIT,0);

    //Set slave address
    if (ioctl(fd,I2C_SLAVE,addr) < 0){
        printf("fail to set i2c device slave address!\n");
        close(fd);
        return -1;
    }

    //Set the number of retries when no ACK is received
    ioctl(fd,I2C_RETRIES,5);

    if (write(fd, &reg, 1) == 1){
        if (read(fd, val, 1) == 1){
                return 0;
        }
    }
    else{
        return -1;
    }
}

static short GetData(int fd,uint8_t addr,unsigned char REG_Address)
{
    char H, L;

    i2c_read(fd, addr,REG_Address, &H);
    usleep(1000);
    i2c_read(fd, addr,REG_Address + 1, &L);
    return (H << 8) +L;
}

Save and exit, then compile and run

1
2
3
gcc i2c_mpu6050.c -o mpu6050

sudo ./mpu6050 /dev/i2c-3

Results as shown below:

未找到图片show

Three data collection

3.6. More

For more information about rk-i2c drivers and GPIO analog I2C, please check: rk open source manual /Common/I2C

3.7. References

This document mainly provides a quick start for users with certain experience. For beginners, you can view the following documents:

《I2C communication》

rk open source information (github): 《open source handbook》