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,使能例程与外设,步骤如下:
- 使能例程
Application Example Config -> Enable I2C Example
- 使能外设驱动
Hardware Drivers Config -> On-chip Peripheral Drivers -> Enable I2C
mpu6050简介
mpu6050 是一个 6 轴运动传感器,可以测量 x,y,z 轴上的重力加速度与角加速度,并可外接磁场传感器实现 9 轴传感,在四轴飞行器中广泛使用。传感器使用 iic 接口与 mcu 通信,支持最高 400k 的速率。
测试步骤
- 根据I2C设备名称,获取I2C设备句柄。
- 通过 iic 总线初始化 mpu6050。
- 读取 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