高 培
(福建對外經濟貿易職業技術學院 信息技術系,福州 350016)
?
RT-Thread的I2C總線驅動結構分析、移植及應用
高 培
(福建對外經濟貿易職業技術學院 信息技術系,福州 350016)
針對RT-Thread操作系統下I2C總線驅動展開研究,對驅動結構進行介紹與分析。以STM32F407VG上的軟件I2C驅動移植為例介紹了驅動移植步驟,并通過STM32F407VG讀寫24LC02B的實例,詳細介紹I2C編程步驟及技術細節。最后通過實驗驗證I2C驅動移植及編程的有效性。實驗結果表明,RT-Thread操作系統下的I2C總線驅動結構簡潔,且易于移植及進行程序設計。
RT-Thread;I2C;驅動結構;移植;應用
RT-Thread嵌入式實時操作系統(Real-Time Operation System,RTOS)是國人自創的開源實時操作系統,借鑒了VxWorks、μc/os、RTXC(Real-Time eXecutive in C)等成熟的RTOS的優點,又具有自己的特點,在功能及性能方面不遜色于以上系統[1]。當前RT-Thread嵌入式實時操作系統在許多嵌入式設備上特別是物聯網方面得到了廣泛應用[2-5]。I2C(Inter-Integrated Circuit)總線是一種由Philips公司開發的兩線式串行總線(一根時鐘線,一根數據線),I2C總線可以使具有I2C總線接口的設備相互連接,它采用主從訪問模式,主設備通過總線訪問從設備。I2C總線具有接口線少,控制方式簡單,器件封裝形式小,降低了成本。因此,在嵌入式系統中有著廣泛的應用[6-7]。
RT-Thread下的I2C總線驅動設計類似于Linux,但比Linux更加簡潔。本文對RT-Thread(1.2.0版本)下的I2C總線驅動結構進行分析,介紹了I2C總線驅動的工作原理和移植方法。最后,以STM32F407VG連接2K位串行CMOS EEPROM(24LC02B)為例[8-9],介紹了RT-Thread及I2C總線驅動移植及應用程序設計。
RT-Thread下的I2C總線驅動架構分為(如圖1所示):設備層(i2c_dev.c,位于DeviceDrivers目錄下)、總線核心層(i2c_core.c,位于DeviceDrivers目錄下)、軟件方式I2C接口層(i2c-bit-ops.c,位于DeviceDrivers目錄下)、I2C硬件驅動層(移植目標,本項目中為stm32_i2c.c,位于Drivers目錄下)、I2C外設驅動層或應用程序。

圖1 RT-Thread下I2C總線驅動架構
1.1 I2C總線設備層(i2c_dev.c)
設備層(i2c_dev.c)是操作系統與總線核心層間的接口,同時規范了I2C總線設備的讀寫接口,一般用于I2C總線設備驅動。
設備層(i2c_dev.c)將I2C總線的操作抽象為基本操作接口(初始化、讀操作、寫操作、控制等),主要函數如表1所列。

表1 i2c_dev.c主要函數
常用的接口函數如下所示:
static rt_err_t i2c_bus_device_control(rt_device_t dev,rt_uint8_t cmd,void *args); // 控制函數
static rt_size_t i2c_bus_device_write(rt_device_t dev,rt_off_t pos,const void *buffer,rt_size_t count); //寫操作函數
static rt_size_t i2c_bus_device_read(rt_device_t dev,rt_off_t pos, void *buffer, rt_size_t count); //讀操作函數
其中,dev為I2C總線設備rt_device_t類型結構體指針,cmd為操作類型,args為操作類型所對應的數據(詳見i2c_dev.c),pos包含操作方式與操作地址(分別對應pos的高十六位和低十六位),buffer為操作緩存首地址,count為緩存數據長度。
1.2 I2C總線核心層(i2c_core.c)
總線核心層(i2c_core.c)可作為應用程序或者I2C外設驅動的接口,是操作系統完成初始化及配置I2C驅動及應用程序或者I2C外設驅動實現I2C配置及通信的媒介。該層所涉及的主要函數見表2。

表2 總線核心層(i2c_core.c)主要函數
需要說明的是,應用程序或者驅動向系統申請指定I2C的控制權需要通過總線核心層的rt_i2c_bus_device_find函數,其具體形式如下所示:
struct rt_i2c_bus_device *rt_i2c_bus_device_find (const char *bus_name);
其中,bus_name為所需申請的I2C總線設備在系統注冊時所使用的名字。
此外,總線核心層提供了三種I2C總線操作方式:傳輸(rt_i2c_transfer)、發送(rt_i2c_master_send)、接收(rt_i2c_master_recv)。其中rt_i2c_transfer是最常用的接口,具有讀及寫的功能,為驅動或應用程序開發提供了便利。其函數接口如下:rt_size_t rt_i2c_transfer(struct rt_i2c_bus_device *bus,struct rt_i2c_msg msgs[],rt_uint32_t num);
其中,bus為所申請到的總線設備的rt_i2c_bus_device類型結構體指針,msgs為需要進行讀寫操作的rt_i2c_msg類型(需要根據讀寫時序來安排),num為msgs的維數。rt_i2c_msg結構體具體如下所示:
struct rt_i2c_msg{
rt_uint16_t addr; //設備地址
rt_uint16_t flags; //操作方式
rt_uint16_t len; //讀寫數據長度
rt_uint8_t *buf; //讀寫數據的起始地址
};
1.3 軟件方式I2C總線接口層(i2c-bit-ops.c)
軟件方式I2C總線接口層(i2c-bit-ops.c)提供了使用GPIO來實現I2C總線通信的軟件方式接口函數,將I2C總線通信過程中的開始、停止、發送、接收等具體操作規范化。用戶移植驅動程序時,無需再設計這些具體操作,只需要完成對應GPIO配置及操作函數(rt_i2c_bit_ops結構體中,詳見i2c-bit-ops.h文件)即可,極大地規范并方便了驅動移植。其主要函數見表3。

表3 i2c-bit-ops.c主要函數
1.4 I2C總線硬件驅動層
I2C總線硬件驅動層是I2C總線驅動與硬件間的接口,是I2C總線驅動的具體實現,也是驅動移植時必須完成的主要部分。RT-Thread將具體I2C總線操作抽象并封裝為I2C總線設備操作(rt_i2c_bus_device_ops),主要包括三個基本操作:主機傳輸(master_xfer)、從機傳輸(slave_xfer)、總線控制(i2c_bus_control),因此對于用戶來說,只需要完成所需基本操作的驅動編寫即可完成驅動的移植工作。以下分別介紹硬件I2C總線及軟件I2C總線的硬件驅動層。
1.4.1 硬件I2C
對于硬件I2C,其總線的讀寫等具體操作都是通過寄存器的讀寫來完成的,因此驅動移植時,只需要按照所需操作的接口格式,對照寄存器手冊,完成程序編寫[10]。對于本文所使用的STM32F407VG,其I2C硬件驅動層主要函數如表4所列。

表4 硬件驅動層(硬件I2C)主要函數
其中,函數rt_hw_i2c_init的功能是初始化I2C總線硬件及驅動,其主要任務為:初始化I2C總線所使用的硬件資源(I2C控制器所涉及的寄存器),并將移植完成的操作函數入口地址等信息通過rt_i2c_bus_device_register函數向系統進行注冊。
1.4.2 軟件I2C
軟件I2C是依據I2C通信時序標準,通過控制GPIO口來實現通信。由于RT-Thread驅動框架中已經將軟件方式I2C進行規范,提供標準接口層(i2c-bit-ops.c),因此,軟件I2C的移植也得到了簡化。與硬件I2C不同,軟件I2C的基本操作已經被封裝在標準接口層中(i2c-bit-ops.c),驅動移植時無需再重復這些工作,只需要提供標準接口層的具體實現函數(具體形式詳見i2c_bit_ops.h文件中的rt_i2c_bit_ops結構體形式),對于本文所使用的STM32F407VG,其I2C硬件驅動層需要移植的主要函數如表5所列。

表5 硬件驅動層(軟件I2C)主要函數
此外,對于軟件I2C硬件驅動層中的初始化函數(rt_hw_i2c_init),其主要編程工作為:①對I2C接口所使用的兩個I/O口進行初始化;②將set_sda等函數指針及延時時間等參數封裝入rt_i2c_bit_ops結構中;③調用rt_i2c_bit_add_bus函數,借助rt_i2c_bit_ops結構向標準接口層進行注冊,進一步由標準接口層向系統完成總線設備驅動的注冊工作。
本節將詳細介紹RT-Thread下的I2C總線驅動移植,并以24LC02B的讀寫為例,介紹I2C總線驅動應用。本文使用STM32F407VG的PB6和PB7引腳分別作為軟件I2C總線的SCL和SDA,以主模式連接24LC02B模塊。
2.1 I2C總線驅動移植
本文所使用的RT-Thread 1.2.0版本提供了簡單的I2C驅動支持。為了使用該模塊,需要將i2c_dev.c、i2c_core.c、i2c-bit-ops.c三個文件添加到工程項目中。之后,在rtconfig.h添加宏定義:
#define RT_USING_I2C
#define RT_USING_I2C_BITOPS
I2C總線驅動移植主要工作在I2C硬件驅動層,該部分主要函數介紹見第1.4.2節。移植工作主要分為: I2C總線驅動硬件初始化及操作方法和延時子程序。
2.1.1 I2C總線驅動硬件初始化及操作方法
I2C總線驅動硬件初始化主要完成I2C總線所涉及的兩個I/O引腳的硬件初始化及輸入輸出操作。對于STM32F407VG,I2C總線驅動硬件初始化函數需要完成的操作如圖2所示:①GPIO引腳外設時鐘開啟;②GPIO引腳模式初始化;③rt_i2c_bus_device和rt_i2c_bit_ops結構體初始化;④借助標準的接口層向系統注冊I2C總線(rt_i2c_bit_add_bus)。在系統初始化時(如rt_init_thread_entry中)調用該函數,即可完成I2C總線驅動的硬件及系統初始化。

圖2 I2C總線驅動硬件初始化流程圖
操作方法主要完成set_sda、set_scl、get_sda、get_scl四種基本操作的移植,用戶可以根據處理器手冊和庫函數編程實現。最后需要將這4個函數的入口地址通過rt_i2c_bit_ops類型結構體形式傳遞給操作系統。
需要說明的是,向系統注冊的I2C總線接口函數如下:
rt_err_t rt_i2c_bit_add_bus(struct rt_i2c_bus_device *bus, const char *bus_name);
其中,指針bus是指向包含硬件驅動層信息的rt_i2c_
bus_device類型結構體,name為向系統注冊的該I2C總線名,用戶可以通過name向系統申請和訪問該I2C總線。
2.1.2 延時子程序
延時子程序主要用于實現I2C通信時序,其默認單位為μs,準確的延時子程序有助于正確設置I2C通信速率。對于使用while(i)形式的延時子程序,用戶需要根據處理器的頻率進行有針對性的調整。
2.2 I2C總線設備驅動應用
24LC02B是一款串行CMOS EEPROM芯片,該芯片使用I2C總線進行讀寫等控制。其單字節寫入時序、隨機讀取時序分別如圖3和圖4所示[9]。本節將以在指定位置寫入數據,并讀取該指定位置數據為例,來詳細介紹RT-Thread下I2C總線設備驅動的總線核心層接口函數應用,并驗證驅動移植的有效性(I2C設備層一般用于驅動編寫,相對復雜,由于篇幅所限,本文使用簡潔的總線核心層)。

圖3 24LC02B字節寫入時序

圖4 24LC02B隨機讀取時序
總線核心層接口函數如第1.2節中介紹,相比總線設備層接口函數,其接口函數格式更簡單、直觀,方便讀寫時序的實現。使用總線核心層接口實現24LC02B讀寫的程序流程圖如圖5所示。

圖5 24LC02B讀寫程序流程
首先,調用rt_i2c_bus_device_find函數向系統申請所需的總線設備(如圖中“i2c1”);正確申請到總線設備后,系統會將該I2C總線設備的rt_i2c_bus_device類型結構體指針返回(如圖中存儲在dev中),利用該指針和rt_i2c_transfer函數(詳見第1.2節)即可實現24LC02B的字節寫入與讀取操作,其關鍵是根據讀寫時序合理設置rt_i2c_msg結構體。
對于指定位置寫入數據操作,其寫入順序為:寫控制字、寫入指定位置地址、寫入數據。因此,只需要一個rt_i2c_msg結構體(本程序中使用msg_wr),其具體內容為:
msg_wr.addr =24LC02B;
//24LC02B地址
msg_wr.buf = msg_buf_wr;
msg_wr.flags = RT_I2C_WR; //寫操作
msg_buf_wr[0] = 23; //指定寫入地址
msg_buf_wr[1]++;
msg_wr.len = 2; //操作數據長度
len = rt_i2c_transfer(dev,&msg_wr,1); //返回寫入字符數
對于讀取指定位置數據操作,其寫入順序為:寫控制字、寫入指定位置地址、寫控制字、讀取數據。注意:兩次寫控制字間不能有STOP時序。因此不能使用兩次rt_i2c_transfer來實現該操作,應該使用兩個rt_i2c_msg結構體來實現(本程序中記為使用msg_rd[2]),其具體內容為:
// msg_rd[0]初始化
msg_rd[0].addr = 24LC02B;
//24LC02B地址
msg_rd[0].buf = msg_buf_wr;
msg_rd[0].flags = RT_I2C_WR; //寫操作
msg_buf_wr[0] = 23; //指定讀取地址
msg_rd[0].len = 1; //操作數據長度
// msg_rd[1]初始化
msg_rd[1].addr = 24LC02B; //24LC02B地址
msg_rd[1].buf = msg_buf_rd;
msg_rd[1].flags = RT_I2C_RD; //讀操作
msg_buf_wr[0] = 23; //指定讀取地址
msg_rd[0].len = 1; //操作數據長度
len = rt_i2c_transfer(dev,msg_rd,2);
//返回讀取到的字節數
由圖6所示實驗結果可知,程序正確申請到I2C總線設備后,在地址為23處,從0開始寫入字節數據,隨后讀取該地址上的字節數據,通過對比可以看出,讀取到的數據與寫入數據完全相同。綜上所述,本次I2C總線設備的驅動移植是成功的,并且利用I2C總線核心層接口進行24LC02B的讀寫操作應用也是成功的。


圖6 實驗結果
[1] 曹成.嵌入式實時操作系統RT-Thread原理分析與應用[D].青島:山東科技大學,2011.
[2] 宋天楹,張紅梅,馮歡.CAN-RS232轉換器在實時操作系統RT-Thread上的實現[J].自動化儀表,2012,33(4):70-72.
[3] 蘇憲利,鄭一麟.基于RT-thread的機床物聯網系統設計與實現[J].組合機床與自動化加工技術,2014(6):69-72.
[4] 張麗彪,駱東佳,張艦航,等.基于RT-Thread和Yeelink的物聯網平臺開發的應用設計[J].電子技術與軟件工程,2015(16):70-70.
[5] 李云紅,田冀達,陳航.RT-Thread操作系統的電池管理系統設計[J].單片機與嵌入式系統應用,2015(7):14-17.
[6] 李祥兵,鄭扣根.Linux中I2C總線驅動程序的開發[J].計算機工程與設計,2005,26(1):41-43.
[7] 朱南皓,李正祥.嵌入式Linux中I2C設備驅動程序的研究與實現[J].微計算機信息,2010,26(11):67-69.
[8] RT-Thread開發組.RT-Thread編程指南,2014.
[9] Microchip Technology Inc.24LC01B/02B 1K/2K 2.5V CMOS EEPROMs,1995.
[10] STMicroelectronics.RM0090 Reference manual STM32 F405xx, STM32F407xx,STM32F415xx and STM32 F417xx advanced ARM-based 32-bit MCUs,2012.
[4] 邵長彬,李洪亮.用Busybox制作嵌入式Linux根文件系統[J].微計算機信息,2007,23(10-2):48-50.
[5] 查啟鵬.基于嵌入式Linux的Flash驅動與文件系統的研究與實現[D].南京:東南大學,2008.
[6] 王學龍.嵌入式Linux系統設計與應用[M].北京:清華大學出版社,2001.
[7] 彭浩,龔杰,秦建敏.基于S3C2440的嵌入式Linux根文件系統構建[J].電子設計工程,2010,18(6):20-22.
[8] 李桂香,常赟杰.嵌入式Linux文件系統研究與應用[J].電腦開發與應用,2010,23(5):5-7.
陳選育(工程師),研究方向為光通信技術、嵌入式軟件開發。
(責任編輯:薛士然 收稿日期:2016-06-23)
Structure Analysis,Migration and Application of I2C Bus Driver for RT-Thread
Gao Pei
(Department of Information Technology,Fujian International Business&Economic College,Fuzhou 350016,China)
Aiming at the I2C bus driver of RT-Thread operating system,the structure of I2C bus driver is presented and analyzed.The I2C driver migration process is introduced with the example of the I2C drive migration for STM32F407VG,and by the read/write 24LC02B through STM32F407VG,the I2C programming steps and technical details are introduced.Finally,an experiment is taken to verify the effectiveness of the migration and programming of I2C driver.The experiment results show that the I2C bus driver structure of RT-Thread is simple and easy for migration.
RT-Thread;I2C;driver structure;migration;application
TP316.2
A
?士然
2016-06-17)