本應用筆記以驅動I2C接口的6軸傳感器MPU6050為例,說明了如何使用I2C設備驅動接口開發(fā)應用程序,并詳細講解了RT-Thread I2C設備驅動框架及相關函數。
1 本文的目的和結構
1.1 本文的目的和背景
I2C(或寫作i2c、IIC、iic)總線是由Philips公司開發(fā)的一種簡單、雙向二線制(時鐘SCL、數據SDA)同步串行總線。它只需要兩根線即可在連接于總線上的器件之間傳送信息,是半導體芯片使用最為廣泛的通信接口之一。RT-Thread中引入了I2C設備驅動框架,I2C設備驅動框架提供了基于GPIO模擬和硬件控制器的2種底層硬件接口。
1.2 本文的結構
本文首先描述了RT-Thread I2C設備驅動框架的基本情況,然后詳細描述了I2C設備驅動接口,并使用I2C設備驅動接口編寫MPU6050的驅動程序,并給出了在正點原子STM32F4探索者開發(fā)板上驗證的代碼示例。
2 I2C設備驅動框架簡介
在使用MCU進行項目開發(fā)的時候,往往需要用到I2C總線。一般來說,MCU帶有I2C控制器(硬件I2C),也可以使用MCU的2個GPIO自行編寫程序模擬I2C總線協議實現同樣的功能。
RT-Thread提供了一套I/O設備管理框架,它把I/O設備分成了三層進行處理:應用層、I/O設備管理層、底層驅動。I/O設備管理框架給上層應用提供了統(tǒng)一的設備操作接口和I2C設備驅動接口,給下層提供的是底層驅動接口。應用程序通過I/O設備模塊提供的標準接口訪問底層設備,底層設備的變更不會對上層應用產生影響,這種方式使得應用程序具有很好的可移植性,應用程序可以很方便的從一個MCU移植到另外一個MCU。
本文以6軸慣性傳感器MPU6050為例,使用RT-Thread I2C設備驅動框架提供的GPIO模擬I2C控制器的方式,闡述了應用程序如何使用I2C設備驅動接口訪問I2C設備。
圖2-1 RT-Thread I2C設備驅動框架
3 運行I2C設備驅動示例代碼
3.1 示例代碼軟硬件平臺
正點原子STM32F4探索者開發(fā)板
GY-521 MPU-6050模塊
MDK5
RT-Thread 源碼
正點原子探索者STM32F4 開發(fā)板的MCU是STM32F407ZGT6,本示例使用USB串口(USART1)發(fā)送數據及供電,使用SEGGER JLINK連接JTAG調試。
本次實驗用的GY521模塊是一款6軸慣性傳感器模塊,板載MPU6050。我們使用開發(fā)板的PD6(SCL)、PD7(SDA)作為模擬I2C管腳,用杜邦線將GY521模塊的SCL硬件連接到PD6、SDA連接到PD7、GND連接到開發(fā)板的GND、VCC連接到3.3V。
圖3.1-1 正點原子開發(fā)板
圖3.1-2 GY521模塊
本文基于正點原子STM32F4探索者開發(fā)板,給出了底層I2C驅動(GPIO模擬方式)的添加方法和I2C設備的具體應用示例代碼(以驅動MPU6050為例),包含寄存器讀、寫操作方法。由于RT-Thread上層應用API的通用性,因此這些代碼不局限于具體的硬件平臺,用戶可以輕松將它移植到其它平臺上。
3.2 啟用I2C設備驅動
使用env工具命令行進入 rt-threadspstm32f4xx-HAL 目錄,然后輸入menuconfig命令進入配置界面。
配置shell使用串口1:選中Using UART1,進入RT-Thread Kernel ---> Kernel Device Object菜單,修改the device name for console為uart1。
進入RT-Thread Components ---> Device Drivers菜單,選中 Using I2C device drivers,本示例使用GPIO模擬I2C,因此還要開啟 Use GPIO to simulate I2C。
圖3.2-1 使用menuconfig開啟i2c
退出menuconfig配置界面并保存配置,在env命令行輸入scons --target=mdk5 -s命令生成mdk5工程,新工程名為project。使用MDK5打開工程,修改MCU型號為STM32F407ZGTx,修改調試選項為J-LINK。
圖3.2-2 修改MCU
圖3.2-3 修改調試選項
2.編譯工程后下載程序至開發(fā)板運行。在終端PuTTY(打開對應端口,波特率配置為115200)輸入list_device命令可以看到名為i2c2的設備,設備類型是I2C Bus,說明I2C設備驅動添加成功了。如圖所示:
圖3.2-4使用list_device命令查看i2c總線
3.3 運行示例代碼
將I2C示例代碼(請回復AN0003,下載示例代碼)里的main.c拷貝到 t-threadspstm32f4xx-HALapplications目錄,替換原有的main.c。drv_mpu6050.c、drv_mpu6050.h拷貝到 t-threadspstm32f4xx-HALdrivers目錄,并將它們添加到工程中對應分組。如圖所示:
圖3.3-1 添加驅動
本例使用GPIO PD6作為SCL、GPIO PD7作為SDA,I2C總線名字是i2c2,讀者可根據需要修改drv_i2c.c件中如下參數以適配自己的板卡,確保drv_mpu6050.c中定義的宏MPU6050_I2C_BUS_NAME與drv_i2c.c中的宏I2C_BUS_NAME相同。本示例需要將drv_i2c.c默認驅動端口GPIOB改為GPIOD,如下圖所示:
圖3.3-2 drv_i2c.c中的i2c板級配置
連接好MPU6050模塊和開發(fā)板,編譯工程并下載程序至開發(fā)板,復位MCU,終端PuTTY會打印出讀取到的MPU6050傳感器數據,依次是溫度,三軸加速度,三軸角速度:
圖3.3-3 終端打印信息
4 I2C設備驅動接口詳解
按照前文的步驟,相信讀者能很快的將RT-Thread I2C設備驅動運行起來,那么如何使用I2C設備驅動接口開發(fā)應用程序呢?
RT-Thread I2C設備驅動目前只支持主機模式,使用RT-Thread I2C設備驅動需要使用menuconfig工具開啟宏RT_USING_DEVICE和RT_USING_I2C,如果要使用GPIO模擬I2C還需開啟宏RT_USING_I2C_BITOPS。
使用I2C設備驅動的大致流程如下:
用戶可以在msh shell輸入list_device命令查看已有的I2C設備,確定I2C設備名稱。
查找設備使用rt_i2c_bus_device_find()或者rt_device_find(),傳入I2C設備名稱獲取i2c總線設備句柄。
使用rt_i2c_transfer()即可以發(fā)送數據也可以接收數據,如果主機只發(fā)送數據可以使用rt_i2c_master_send(),如果主機只接收數據可以使用rt_i2c_master_recv()。
接下來本章將詳細講解I2C設備驅動接口的使用。
4.1 查找設備
應用程序要使用已經由操作系統(tǒng)管理的I2C設備需要調用查找設備函數,找到I2C設備后才可以對該設備進行信息傳送。
函數原型:struct rt_i2c_bus_device *rt_i2c_bus_device_find(const char *bus_name)
參數 | 描述 |
---|---|
bus_name | I2C設備名稱 |
函數返回:I2C設備存在則返回I2C設備句柄,否則返回RT_NULL。
本文示例代碼底層驅動drv_mpu6050.c中mpu6050_hw_init()查找設備源碼如下:
#define MPU6050_I2CBUS_NAME "i2c2" /* I2C設備名稱,必須和drv_i2c.c注冊的I2C設備名稱一致 */ static struct rt_i2c_bus_device *mpu6050_i2c_bus; /* I2C設備句柄 */ ... ... ... ... int mpu6050_hw_init(void) { rt_uint8_t res; mpu6050_i2c_bus = rt_i2c_bus_device_find(MPU6050_I2CBUS_NAME); /*查找I2C設備*/ if (mpu6050_i2c_bus == RT_NULL) { MPUDEBUG("can't find mpu6050 %s device ",MPU6050_I2CBUS_NAME); return -RT_ERROR; } ... ... ... ... }
4.2 數據傳輸
RT-Thread I2C設備驅動的核心API是rt_i2c_transfer(),它傳遞的消息是鏈式結構的。可以通過消息鏈,實現調用一次完成多次數據的收發(fā),此函數既可以用于發(fā)送數據,也可以用于接收數據。
函數原型:
rt_size_t rt_i2c_transfer(struct rt_i2c_bus_device *bus, struct rt_i2c_msg msgs[], rt_uint32_t num)
參數 | 描述 |
---|---|
bus | I2C總線設備句柄 |
msgs[] | I2C消息數組 |
num | 消息數組的數量 |
函數返回: 成功傳輸的消息數組的數量
消息數組msgs[]類型為
struct rt_i2c_msg { rt_uint16_t addr; //從機地址 rt_uint16_t flags; //標志,讀、寫等 rt_uint16_t len; //讀寫數據字節(jié)數 rt_uint8_t *buf; //讀寫數據指針 }
addr從機地址支持7位和10位二進制地址(flags |= RT_I2C_ADDR_10BIT)。RT-Thread的I2C設備驅動接口使用的從機地址均為不包含讀寫位的地址,讀寫位對應修改flags。
flags標志可選值為i2c.h文件中定義的宏,發(fā)送數據賦值 RT_I2C_WR,接收數據賦值RT_I2C_RD,根據需要可以與其他宏使用位運算“|”組合起來使用。
#define RT_I2C_WR 0x0000 #define RT_I2C_RD (1u << 0) #define RT_I2C_ADDR_10BIT ?(1u << 2) /* this is a ten bit chip address */ #define RT_I2C_NO_START ? ?(1u << 4) #define RT_I2C_IGNORE_NACK (1u << 5) #define RT_I2C_NO_READ_ACK (1u << 6) /* when I2C reading, we do not ACK */
4.2.1 發(fā)送數據
用戶可以調用I2C設備驅動接口rt_i2c_master_send()或者rt_i2c_transfer()發(fā)送數據。函數調用關系如下:
圖4.2.1-1 發(fā)送數據函數調用關系
drv_mpu6050.c中的mpu6050_write_reg()函數是MCU向mpu6050寄存器寫數據。此函數的實現共有2種,分別調用了I2C設備驅動接口rt_i2c_transfer()和rt_i2c_master_send()實現。
本文示例使用的MPU6050數據手冊中提到7位從機地址是110100X,X由芯片的AD0管腳決定,GY521模塊的AD0連接到了GND,因此MPU6050作為從機時地址是1101000,16進制形式是0x68。寫MPU6050某個寄存器,主機首先發(fā)送從機地址MPU6050_ADDR、讀寫標志 R/W 為 RT_I2C_WR(0 為寫,1 為讀),然 后主機發(fā)送從機寄存器地址reg及數據data。
使用rt_i2c_transfer()發(fā)送數據
本文示例代碼底層驅動drv_mpu6050.c發(fā)送數據源碼如下:
#define MPU6050_ADDR 0X68 //寫mpu6050單個寄存器 //reg:寄存器地址 //data:數據 //返回值: 0,正常 / -1,錯誤代碼 rt_err_t mpu6050_write_reg(rt_uint8_t reg, rt_uint8_t data) { struct rt_i2c_msg msgs; rt_uint8_t buf[2] = {reg, data}; msgs.addr = MPU6050_ADDR; /* 從機地址 */ msgs.flags = RT_I2C_WR; /* 寫標志 */ msgs.buf = buf; /* 發(fā)送數據指針 */ msgs.len = 2; if (rt_i2c_transfer(mpu6050_i2c_bus, &msgs, 1) == 1) { return RT_EOK; } else { return -RT_ERROR; } }
以本文示例代碼其中一次調用rt_i2c_transfer()發(fā)送數據為例,從機MPU6050地址16進制值為0X68,寄存器地址reg 16進制值為0X6B,發(fā)送的數據data 16進制值為0X80。示例波形如下圖所示,第一個發(fā)送的數據是0XD0,第一個數據的高7位是從機地址,最低位是讀寫位為寫(值為0),所以第一個數據為:0X68 << 1|0 = 0XD0,然后依次發(fā)送寄存器地址0X6B和數據0X80。
圖4.2.1-2 I2C發(fā)送數據波形示例
使用rt_i2c_master_send()發(fā)送數據
函數原型:
rt_size_t rt_i2c_master_send(struct rt_i2c_bus_device *bus, rt_uint16_t addr, rt_uint16_t flags, const rt_uint8_t *buf, rt_uint32_t count)
參數 | 描述 |
---|---|
bus | I2C總線設備句柄 |
addr | 從機地址,不包含讀寫位 |
flags | 標志,讀寫標志為寫。只支持10位地址選擇RT_I2C_ADDR_10BIT |
buf | 指向發(fā)送數據的指針 |
count | 發(fā)送數據字節(jié)數 |
函數返回: 成功發(fā)送的數據字節(jié)數。
此函是對rt_i2c_transfer()的簡單封裝。
本文示例代碼底層驅動drv_mpu6050.c發(fā)送數據源碼如下:
#define MPU6050_ADDR 0X68 //寫mpu6050單個寄存器 //reg:寄存器地址 //data:數據 //返回值: 0,正常 / -1,錯誤代碼 rt_err_t mpu6050_write_reg(rt_uint8_t reg, rt_uint8_t data) { rt_uint8_t buf[2]; buf[0] = reg; buf[1] = data; if (rt_i2c_master_send(mpu6050_i2c_bus, MPU6050_ADDR, 0, buf ,2) == 2) { return RT_EOK; } else { return -RT_ERROR; } }
4.2.2 接收數據
用戶可以調用I2C設備驅動接口rt_i2c_master_recv()或者rt_i2c_transfer()接受數據。函數調用關系如下:
圖4.2.2-1 接收數據函數調用關系
本文示例代碼drv_mpu6050.c中的mpu6050_read_reg()函數是MCU從MPU6050寄存器讀取數據,此函數的實現同樣有2種方式,分別調用了I2C設備驅動接口rt_i2c_transfer()和rt_i2c_master_recv()實現。
讀MPU6050某個寄存器,主機首先發(fā)送從機地址MPU6050_ADDR、讀寫標志 R/W 為 RT_I2C_WR(0 為寫,1 為讀)、從機寄存器地址reg之后才能開始讀設備。然后發(fā)送從機地址MPU6050_ADDR、讀寫標志 R/W 為 RT_I2C_RD(0 為寫,1 為讀)、保存讀取數據指針。
使用rt_i2c_transfer()接收數據
本文示例代碼底層驅動drv_mpu6050.c接收數據源碼如下:
#define MPU6050_ADDR 0X68 //讀取寄存器數據 //reg:要讀取的寄存器地址 //len:要讀取的數據字節(jié)數 //buf:讀取到的數據存儲區(qū) //返回值: 0,正常 / -1,錯誤代碼 rt_err_t mpu6050_read_reg(rt_uint8_t reg, rt_uint8_t len, rt_uint8_t *buf) { struct rt_i2c_msg msgs[2]; msgs[0].addr = MPU6050_ADDR; /* 從機地址 */ msgs[0].flags = RT_I2C_WR; /* 寫標志 */ msgs[0].buf = ® /* 從機寄存器地址 */ msgs[0].len = 1; /* 發(fā)送數據字節(jié)數 */ msgs[1].addr = MPU6050_ADDR; /* 從機地址 */ msgs[1].flags = RT_I2C_RD; /* 讀標志 */ msgs[1].buf = buf; /* 讀取數據指針 */ msgs[1].len = len; /* 讀取數據字節(jié)數 */ if (rt_i2c_transfer(mpu6050_i2c_bus, msgs, 2) == 2) { return RT_EOK; } else { return -RT_ERROR; } }
以本文示例代碼其中一次調用rt_i2c_transfer()接收數據為例,從機MPU6050地址16進制值為0X68,寄存器地址reg 16進制值為0X75。示例波形如下圖所示,第一個發(fā)送的數據是0XD0,第一個數據的高7位是從機地址,最低位是讀寫位是寫(值為0),所以第一個數據值為:0X68 << 1|0 = 0XD0,然后發(fā)送寄存器地址0X75。第二次發(fā)送的第一個數據為0XD1,讀寫位是讀(值為1),值為:0X68 << 1 | 1 = 0XD1,然后收到讀取到的數據0X68。
圖4.2.2-2 I2C發(fā)送數據波形示例
使用 rt_i2c_master_recv()接收數據
函數原型:
rt_size_t rt_i2c_master_recv(struct rt_i2c_bus_device *bus, rt_uint16_t addr, rt_uint16_t flags, rt_uint8_t *buf, rt_uint32_t count)
參數 | 描述 |
---|---|
bus | I2C總線設備句柄 |
addr | 從機地址,不包含讀寫位 |
flags | 標志,讀寫標志為讀,只支持10位地址選擇RT_I2C_ADDR_10BIT |
buf | 接受數據指針 |
count | 接收數據字節(jié)數 |
函數返回: 成功接收的數據字節(jié)數。
此函數是對rt_i2c_transfer()的簡單封裝,只能讀取數據(接收數據)。
本文示例代碼底層驅動drv_mpu6050.c接收數據源碼如下:
#define MPU6050_ADDR 0X68 //讀取寄存器數據 //reg:要讀取的寄存器地址 //len:要讀取的數據字節(jié)數 //buf:讀取到的數據存儲區(qū) //返回值: 0,正常 / -1,錯誤代碼 rt_err_t mpu6050_read_reg(rt_uint8_t reg, rt_uint8_t len, rt_uint8_t *buf) { if (rt_i2c_master_send(mpu6050_i2c_bus, MPU6050_ADDR, 0, ®, 1) == 1) { if (rt_i2c_master_recv(mpu6050_i2c_bus, MPU6050_ADDR, 0, buf, len) == len) { return RT_EOK; } else { return -RT_ERROR; } } else { return -RT_ERROR; } }
4.3 I2C設備驅動應用
通常I2C接口芯片的只讀寄存器分為2種情況,一種是單一功能寄存器,另一種是地址連續(xù),功能相近的寄存器。例如MPU6050的寄存器0X3B、0X3C、0X3D、0X3E、0X3F、0X40依次存放的是三軸加速度X、Y、Z軸的高8位、低8位數據。
本文示例代碼底層驅動drv_mpu6050.c使用mpu6050_read_reg()函數讀取MPU6050的3軸加速度數據:
#define MPU_ACCEL_XOUTH_REG 0X3B //加速度值,X軸高8位寄存器 //得到加速度值(原始值) //gx,gy,gz:陀螺儀x,y,z軸的原始讀數(帶符號) //返回值:0,成功/ -1,錯誤代碼 rt_err_t mpu6050_accelerometer_get(rt_int16_t *ax, rt_int16_t *ay, rt_int16_t *az) { rt_uint8_t buf[6], ret; ret = mpu6050_read_reg(MPU_ACCEL_XOUTH_REG, 6, buf); if (ret == 0) { *ax = ((rt_uint16_t)buf[0] << 8) | buf[1]; ? ? ? ? ?*ay = ((rt_uint16_t)buf[2] << 8) | buf[3]; ? ? ? ? ?*az = ((rt_uint16_t)buf[4] << 8) | buf[5]; ? ? ? ? ? ? ? ?return RT_EOK; ? ?} ? ?else ? ?{ ? ? ? ?return -RT_ERROR; ? ?} ? ? ? }
5 參考
本文所有相關的API
API | 頭文件 |
---|---|
rt_i2c_transfer() | rt-threadcomponentsdriversincludedriversi2c.h |
rt_i2c_master_send() | rt-threadcomponentsdriversincludedriversi2c.h |
rt_i2c_master_recv() | rt-threadcomponentsdriversincludedriversi2c.h |
mpu6050_hw_init() | drv_mpu6050.h |
mpu6050_write_reg() | drv_mpu6050.h |
mpu6050_read_reg() | drv_mpu6050.h |
mpu6050 _temperature_get() | drv_mpu6050.h |
mpu6050 _gyroscope_get() | drv_mpu6050.h |
mpu6050 _accelerometer_get() | drv_mpu6050.h |
-
I2C
+關注
關注
28文章
1487瀏覽量
123754 -
函數
+關注
關注
3文章
4331瀏覽量
62618 -
設備驅動
+關注
關注
0文章
68瀏覽量
10888
原文標題:【應用筆記】小白也能玩轉RT-Thread之I2C設備
文章出處:【微信號:RTThread,微信公眾號:RTThread物聯網操作系統(tǒng)】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論