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 的速率。
测试步骤
- 根据I2C设备名称,获取I2C设备句柄。
- 通过 iic 总线初始化 mpu6050。
- 读取 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