Skip to content

I2C设备

I2C简介

I2C(Inter Integrated Circuit)总线是 PHILIPS 公司开发的一种半双工、双向二线制同步串行总线。I2C 总线传输数据时只需两根信号线,一根是双向数据线 SDA(serial data),另一根是双向时钟线 SCL(serial clock)。SPI 总线有两根线分别用于主从设备之间接收数据和发送数据,而 I2C 总线只使用一根线进行数据收发。

I2C又称IIC,更详细介绍说明请查看RT-Thread官网:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/i2c

驱动支持

访问I2C设备前,需要保证相关驱动配置已选中。

Windows环境:利用RT-Thread官方env工具直接在代码根目录下执行menuconfig进入配置。

Linux环境:在代码根目录下执行scons --menuconfig进入配置。

I2C驱动所在路径如下:

RT-Thread Components -> Device Drivers -> Using I2C device drivers

打开驱动配置

上述配置选中后,相关的GPIO配置也需要配置,打开源码文件:board/board.h搜索"BSP_USING_I2C1",取消对应注释,这里选用软件模拟的I2C,修改如下:

#define BSP_USING_I2C1
#define BSP_I2C1_SCL_PIN    GET_PIN(A, 2)
#define BSP_I2C1_SDA_PIN    GET_PIN(A, 3)

访问I2C设备

访问I2C设备,库中提供了以下接口:

函数 描述
rt_device_find() 根据I2C名称查找设备获取设备句柄
rt_i2c_transfer() I2C读写数据

查找I2C设备

查找I2C设备,函数原型如下所示:

rt_device_t rt_device_find(const char* name);
  • 参数

    [IN]name:I2C设备名称,目前注册到的系统中的I2C设备名称为"i2c1"。

  • 返回值

    RT_NULL:失败,设备不存在。

    非RT_NULL:成功,该返回值是操作I2C设备的句柄,在后续的接口中作为函数入参。

I2C读写数据

打开I2C设备并进行读写操作,函数原型如下所示:

rt_size_t rt_i2c_transfer(struct rt_i2c_bus_device* bus,
                          struct rt_i2c_msg         msgs[],
                          rt_uint32_t               num);
  • 参数

    [IN]bus:I2C设备句柄,查找I2C设备函数的返回值。

    [INOUT]msgs:待读写的消息数组指针。

    // 结构体定义头文件所在路径:
    // rt-thread/components/drivers/include/drivers/i2c.h
    struct rt_i2c_msg
    {
        rt_uint16_t addr;
        rt_uint16_t flags;
        rt_uint16_t len;
        rt_uint8_t*  buf;
    };
    

    [IN]num:消息数组的元素个数。

  • 返回值

    成功,返回消息数组的元素个数。

    失败,返回错误码。

I2C使用示例

这里提供I2C测试实例中以使用 mpu6050 作为例子。

使用示例需要配置menuconfig打开配置(默认关闭),路径如下:

wiota APP -> open i2c, default close(_IIC_APP_)

mpu6050简介

mpu6050 是一个 6 轴运动传感器,可以测量 x,y,z 轴上的重力加速度与角加速度,并可外接磁场传感器实现 9 轴传感,在四轴飞行器中广泛使用。传感器使用 iic 接口与 mcu 通信,支持最高 400k 的速率。

测试步骤

  1. 根据I2C设备名称,获取I2C设备句柄。
  2. 通过 iic 总线初始化 mpu6050。
  3. 读取 mpu6050 回传的传感器数据。
#include <rtthread.h>
#ifdef _IIC_APP_
#include <rtdevice.h>
#include "uc_iic_app.h"
#include "drivers/i2c.h"

#define IIC_DEVICE_NAME    "i2c1"
#define MPU6050_ADDR 0x68

#define MPU6050_GYRO_OUT        0x43
#define MPU6050_ACC_OUT         0x3B

#define ADDRESS_WHO_AM_I (0x75U)          // !< WHO_AM_I register identifies the device. Expected value is 0x68.
#define ADDRESS_SIGNAL_PATH_RESET (0x68U) // !<

static const uint8_t expected_who_am_i = 0x68U; // !< Expected value to get from WHO_AM_I register.

static rt_device_t iic_dev = NULL;

rt_err_t mpu6050_register_write(uint8_t register_address, uint8_t value)
{
    rt_uint8_t buf[2];

    struct rt_i2c_msg msgs;

    buf[0] = register_address;
    buf[1] = value;

    msgs.addr = MPU6050_ADDR;
    msgs.flags = RT_I2C_WR;
    msgs.buf = buf;
    msgs.len = 2;

    if (rt_i2c_transfer((struct rt_i2c_bus_device*)iic_dev, &msgs, 1) == 1)
    {
        return RT_EOK;
    }
    else
    {
        return -RT_ERROR;
    }
}

rt_err_t mpu6050_register_read(uint8_t register_address, uint8_t *destination, uint8_t number_of_bytes)
{
    struct rt_i2c_msg msgs[2];

    rt_uint8_t buf[1];
    buf[0] = register_address;

    /* 写要读取的寄存器地址 */
    msgs[0].addr = MPU6050_ADDR;
    msgs[0].flags = RT_I2C_WR;
    msgs[0].buf = buf;
    msgs[0].len = 1;

    /* 读取寄存器数据 */
    msgs[1].addr = MPU6050_ADDR;
    msgs[1].flags = RT_I2C_RD;
    msgs[1].buf = destination;
    msgs[1].len = number_of_bytes;

    if (rt_i2c_transfer((struct rt_i2c_bus_device*)iic_dev, msgs, 2) == 2)
    {
        return RT_EOK;
    }
    else
    {
        return -RT_ERROR;
    }
}

rt_err_t mpu6050_verify_product_id(void)
{
    uint8_t who_am_i;

    if (mpu6050_register_read(ADDRESS_WHO_AM_I, &who_am_i, 1) == RT_EOK)
    {
        if (who_am_i != expected_who_am_i)
        {
            return -RT_ERROR;
        }
        else
        {
            return RT_EOK;
        }
    }
    else
    {
        return -RT_ERROR;
    }
}

void MPU6050_ReadGyro(int16_t *pGYRO_X, int16_t *pGYRO_Y, int16_t *pGYRO_Z)
{
    uint8_t buf[6];

    mpu6050_register_read(MPU6050_GYRO_OUT, buf, 6);

    *pGYRO_X = (buf[0] << 8) | buf[1];
    if (*pGYRO_X & 0x8000)
        *pGYRO_X -= 65536;

    *pGYRO_Y = (buf[2] << 8) | buf[3];
    if (*pGYRO_Y & 0x8000)
        *pGYRO_Y -= 65536;

    *pGYRO_Z = (buf[4] << 8) | buf[5];
    if (*pGYRO_Z & 0x8000)
        *pGYRO_Z -= 65536;
}

void MPU6050_ReadAcc(int16_t *pACC_X, int16_t *pACC_Y, int16_t *pACC_Z)
{

    uint8_t buf[6];

    mpu6050_register_read(MPU6050_ACC_OUT, buf, 6);
    *pACC_X = (buf[0] << 8) | buf[1];
    if (*pACC_X & 0x8000)
        *pACC_X -= 65536;

    *pACC_Y = (buf[2] << 8) | buf[3];
    if (*pACC_Y & 0x8000)
        *pACC_Y -= 65536;

    *pACC_Z = (buf[4] << 8) | buf[5];
    if (*pACC_Z & 0x8000)
        *pACC_Z -= 65536;
}

rt_err_t mpu6050_init()
{
    rt_err_t transfer_succeeded = RT_EOK;

    uint8_t inData[7] = {0x00,  // 0x19
                         0x00,  // 0x1A
                         0x03,  // 0x6B
                         0x10,  // 0x1B
                         0x00,  // 0x6A
                         0x32,  // 0x37
                         0x01}; // 0x38
    uint8_t acc = 0x00;

    // Do a reset on signal paths
    uint8_t reset_value = 0x04U | 0x02U | 0x01U; // Resets gyro, accelerometer and temperature sensor signal paths
    transfer_succeeded &= mpu6050_register_write(ADDRESS_SIGNAL_PATH_RESET, reset_value);

    // 设置GYRO
    mpu6050_register_write(0x19, inData[0]); // 设置采样率    -- SMPLRT_DIV = 0  Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)
    mpu6050_register_write(0x1A, inData[1]); // CONFIG        -- EXT_SYNC_SET 0 (禁用晶振输入脚) ; default DLPF_CFG = 0 => (低通滤波)ACC bandwidth = 260Hz  GYRO bandwidth = 256Hz)
    mpu6050_register_write(0x6B, inData[2]); // PWR_MGMT_1    -- SLEEP 0; CYCLE 0; TEMP_DIS 0; CLKSEL 3 (PLL with Z Gyro reference)
    mpu6050_register_write(0x1B, inData[3]); // gyro配置 量程  0-1000度每秒
    mpu6050_register_write(0x6A, inData[4]); // 0x6A的 I2C_MST_EN  设置成0  默认为0 6050  使能主IIC
    // 0x37的 推挽输出,高电平中断,一直输出高电平直到中断清除,任何读取操作都清除中断 使能 pass through 功能 直接在6050 读取5883数据
    mpu6050_register_write(0x37, inData[5]);
    mpu6050_register_write(0x38, inData[6]); // 使能data ready 引脚

    // 设置 ACC
    mpu6050_register_write(0x1C, acc); // ACC设置  量程 +-2G s

    // Read and verify product ID
    transfer_succeeded &= mpu6050_verify_product_id();

    return transfer_succeeded;
}

int iic_app_init(void)
{
    rt_err_t ret = RT_EOK;

    iic_dev = rt_device_find(IIC_DEVICE_NAME);
    if (!iic_dev)
    {
        rt_kprintf("find %s failed!\n", IIC_DEVICE_NAME);
        return RT_ERROR;
    }

    return ret;
}

void iic_app_sample(void)
{
    rt_err_t ret = RT_EOK;

    rt_kprintf("iic test demo.\r\n");

    ret = iic_app_init();
    if(ret != RT_EOK)
    {
        rt_kprintf("init iic failed!\n");
        return;
    }

    ret = mpu6050_init();
    if (ret != RT_EOK) {
        rt_kprintf("mpu6050 init error\n");    
    }

    uint8_t id;
    int16_t AccValue[3],GyroValue[3];

    rt_kprintf("mpu6050 init ok\n");
    mpu6050_register_read(0x75U, &id, 1);
    rt_kprintf("mpu6050 id is %d \n",id);

    while (1) {
        MPU6050_ReadAcc( &AccValue[0], &AccValue[1] , &AccValue[2] );
        MPU6050_ReadGyro(&GyroValue[0] , &GyroValue[1] , &GyroValue[2] );

        rt_kprintf("ACC:  %d    %d  %d  \n",AccValue[0],AccValue[1],AccValue[2]);
        rt_kprintf("GYRO: %d    %d  %d  \n",GyroValue[0],GyroValue[1],GyroValue[2]);

        rt_thread_delay(100);
    }
}

#endif
Back to top