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

访问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设备名称为"i2c0"。

  • 返回值

    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,使能例程与外设,步骤如下:

  1. 使能例程
Application Example Config -> Enable I2C Example
  1. 使能外设驱动
Hardware Drivers Config -> On-chip Peripheral Drivers -> Enable I2C

mpu6050简介

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

测试步骤

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

#ifdef APP_EXAMPLE_I2C

#ifndef RT_USING_I2C
#error "Please enable rt-thread i2c device driver"
#endif

#ifndef BSP_USING_I2C
#error "Please enable on-chip peripheral i2c config"
#endif

#include <rtdevice.h>
#include "uc_i2c_app.h"

#define THREAD_STACK_SIZE 512
#define THREAD_PRIORITY 5
#define THREAD_TIMESLICE 5

#define I2C_DEVICE_NAME "i2c0"
#define I2C_BUS_CLOCK 100000 // 100kHz,只在使用硬件i2c时才有效

#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 struct rt_i2c_bus_device *i2c_dev = NULL;

static 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(i2c_dev, &msgs, 1) == 1)
    {
        return RT_EOK;
    }
    else
    {
        return -RT_ERROR;
    }
}

static 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(i2c_dev, msgs, 2) == 2)
    {
        return RT_EOK;
    }
    else
    {
        return -RT_ERROR;
    }
}

static 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;
    }
}

static void mpu6050_read_gyro(int16_t *gyro_x, int16_t *gyro_y, int16_t *gyro_z)
{
    uint8_t buf[6];

    mpu6050_register_read(MPU6050_GYRO_OUT, buf, 6);

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

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

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

static void mpu6050_read_acc(int16_t *acc_x, int16_t *acc_y, int16_t *acc_z)
{
    uint8_t buf[6];

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

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

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

static rt_err_t mpu6050_init()
{
    rt_err_t transfer_succeeded = RT_EOK;

    // 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);

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

    // ACC设置  量程 +-2G s
    transfer_succeeded |= mpu6050_register_write(0x1C, 0x00);

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

    return transfer_succeeded;
}

static int i2c_app_init(void)
{
    rt_err_t ret = RT_EOK;

    i2c_dev = (struct rt_i2c_bus_device *)rt_device_find(I2C_DEVICE_NAME);
    if (!i2c_dev)
    {
        rt_kprintf("find %s failed!\n", I2C_DEVICE_NAME);
        return RT_ERROR;
    }

    ret = rt_i2c_control(i2c_dev, RT_I2C_DEV_CTRL_CLK, I2C_BUS_CLOCK);
    if (ret != RT_EOK)
    {
        rt_kprintf("set bus speed failed!\n");
        return RT_ERROR;
    }

    return ret;
}

static void i2c_thread_entry(void *parameter)
{
    int16_t acc_value[3], gyro_value[3];
    while (1)
    {
        mpu6050_read_acc(&acc_value[0], &acc_value[1], &acc_value[2]);
        mpu6050_read_gyro(&gyro_value[0], &gyro_value[1], &gyro_value[2]);

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

        rt_thread_delay(100);
    }
}

int i2c_app_sample(void)
{
    rt_err_t ret = RT_EOK;
    rt_thread_t thread = RT_NULL;

    rt_kprintf("i2c_app_sample\n");

    ret = i2c_app_init();
    if (ret != RT_EOK)
    {
        rt_kprintf("init i2c failed!\n");
        return -RT_ERROR;
    }

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

    rt_kprintf("mpu6050 init ok\n");

    /* 创建 serial 线程 */
    thread = rt_thread_create("i2c_app",
                              i2c_thread_entry,
                              RT_NULL,
                              THREAD_STACK_SIZE,
                              THREAD_PRIORITY,
                              THREAD_TIMESLICE);
    /* 创建成功则启动线程 */
    if (RT_NULL == thread)
    {
        return -RT_ERROR;
    }
    else
    {
        rt_thread_startup(thread);
    }

    return RT_EOK;
}

#endif // APP_EXAMPLE_I2C
Back to top