《rt-thread驅動框架分析》-i2c驅動

2020-11-26 電子芯吧客

驅動分析

I2C設備驅動框架圖:

  1. 我們先RT-Thread的I2C框架圖(這是我自己理解的框架圖,如果不對的地方,請指出):

  • 上圖是我分析的RTT的I2C框架圖。主要分為三層,驅動層-核心層-設備層。如果你分析過Linux的I2C框架,它的層次也是這樣子。所以你了解了RTT的I2C之後再去看Linux的I2C框架,其實問題不大。
  • 驅動層:分為硬體I2C驅動和軟體I2C驅動。
  • 核心層: ①其中bit_ops是RTT為軟體I2C提供的中間層,它的作用:為底層模擬I2C驅動提供回調接口,為核心層提供統一I2C通信接口。②而硬體I2C則直接對接核心層,提供統一I2C通信接口。③RTT在核心層上,也像pin驅動那樣,封裝了一套API(虛線箭頭),供用戶直接使用。④dev是提供RTT設備驅動框架的統一的API(實現箭頭)。⑤注意的是:模擬I2C驅動到核心層,增加了一層中間層。
  • 設備層:設備就是雜七雜八的使用I2C的總線的設備。而這些設備可以選擇使用RTT驅動框架的API,也可以選擇RTT封裝好的API。
  1. 通過上述的描述,可能還沒了解的很清晰。下面我根據兩種不同方式的驅動,兩種不同的API,逐一分析,並且會結合試驗來驗證。

driver 層:

  • RT-Thread的I2C驅動,分為兩種類型:硬體I2C和軟體I2C。在stm32的BSP中提供了軟體I2C的驅動,不過為了全面介紹,硬體I2C的對接,作者也進行簡單的對接和實現。

軟體I2C:

  1. 軟體I2C的層次圖:

  1. drv_soft_i2c層: 主要進行軟體I2C所用到scl引腳,sda引腳初始化。scl引腳和sda引腳的獲取電平和設置電平接口和延時函數(udelay)。並對接bit_opt層提供的操作結構體:struct rt_i2c_bit_ops。並通過rt_i2c_bit_add_bus註冊,提供給bit_opt層回調。
  • struct rt_i2c_bit_ops結構體:

struct rt_i2c_bit_ops{ void *data; void (*set_sda)(void *data, rt_int32_t state); void (*set_scl)(void *data, rt_int32_t state); rt_int32_t (*get_sda)(void *data); rt_int32_t (*get_scl)(void *data); void (*udelay)(rt_uint32_t us); rt_uint32_t delay_us; rt_uint32_t timeout;};

函數指針 功能 void (set_sda)(void data, rt_int32_t state) 設置SDA電平 void (set_scl)(void data, rt_int32_t state); 設置SCL電平 rt_int32_t (get_sda)(void data); 獲取SDA電平 rt_int32_t (get_scl)(void data); 獲取SCL電平 void (*udelay)(rt_uint32_t us); 軟體I2C時序所需要的的延時函數

  • rt_i2c_bit_add_bus接口,主要註冊軟體I2C的引腳操作的回調函數。
  1. bit_opt層:可以歸納為驅動層。其主要實現軟體I2C的時序等邏輯,並提供對應的I2C的收發處理函數,為drv_soft_i2c層提供提供了(struct rt_i2c_bit_ops)註冊接口和(rt_i2c_bit_add_bus)接口,為i2c_core層提供主機模式的數據處理函數。bit_opt層主要對接到i2c_core層提供操作結構體:struct rt_i2c_bus_device_ops以及i2c總線的註冊函數rt_i2c_bus_device_register:
  • struct rt_i2c_bus_device_ops結構體:

struct rt_i2c_bus_device_ops{ rt_size_t (*master_xfer)(struct rt_i2c_bus_device *bus, struct rt_i2c_msg msgs[], rt_uint32_t num); rt_size_t (*slave_xfer)(struct rt_i2c_bus_device *bus, struct rt_i2c_msg msgs[], rt_uint32_t num); rt_err_t (*i2c_bus_control)(struct rt_i2c_bus_device *bus, rt_uint32_t, rt_uint32_t);};

函數指針 功能 master_xfer 主機模式的數據收發 slave_xfer 從機模式的數據收發 i2c_bus_control i2c總線的的操作參數等設置

  • rt_i2c_bus_device_register接口:主要註冊I2C總線和收發數據的回調函數。
  1. 軟體I2C驅動總結:rt-thread的軟體I2C,如果要對接其他平臺,只需要對接好結構體:struct rt_i2c_bit_ops。而軟體I2C的邏輯完全不用理會,全部由bit_opt層管理。

硬體I2C

  1. 硬體I2C的層次圖:

  1. drv_hw_i2c層:沒有軟體I2C的bit_opt層,而是直接對接i2c_core層提供的結構體:struct rt_i2c_bus_device_ops。作者為了簡單說明,寫了個例子(簡單粗暴的例子):

struct rt_i2c_bus_device i2c1_bus;I2C_HandleTypeDef hi2c1;static rt_err_t i2c_hw_init(void){ hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 100000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } return RT_EOK;}static rt_size_t i2c_xfer(struct rt_i2c_bus_device *bus, struct rt_i2c_msg msgs[], rt_uint32_t num){ struct rt_i2c_msg *msg; rt_int32_t i, ret; for(i = 0; i< num; i++) { msg = &msgs[i]; if(msg->flags & RT_I2C_RD) { HAL_I2C_Master_Receive(&hi2c1, msg->addr, msg->buf, msg->len, 100); } else { HAL_I2C_Master_Transmit(&hi2c1, msg->addr, msg->buf, msg->len, 100); } } ret = i; return ret;}static const struct rt_i2c_bus_device_ops i2c_bus_ops ={ i2c_xfer, RT_NULL, RT_NULL};int rt_i2c_hw_init(void){ i2c_hw_init(); i2c1_bus.ops = &i2c_bus_ops; rt_i2c_bus_device_register(&i2c1_bus, "hw_i2c"); return RT_EOK;}INIT_DEVICE_EXPORT(rt_i2c_hw_init);

  1. 以上硬體I2C對接,我只是對接了master_xfer。
  2. 硬體I2C驅動總結:如果你是採用硬體I2C,那麼就不需要去關乎bit_opt層,其實通過上面的描述,你不難理解bit_opt層和drv_hw_i2c層其實對接都是i2c_core層的結構。這也是我為什麼把bit_opt層歸納為驅動層來講解了。

core 層:

  1. i2c_core層為驅動層提供結構體:struct rt_i2c_bus_device_ops。為設備層提供I2C的數據收發處理函數。
  2. 其實i2c_core層主要是封裝了一層API直接提供給用戶層設備調用。

函數 說明 rt_i2c_bus_device_find 查找i2c總線 rt_i2c_transfer 主機模式的i2c數據傳輸 rt_i2c_master_send 主機模式的i2c數據發送 rt_i2c_master_recv 主機模式的i2c數據接受

  1. 其中rt_i2c_master_send函數和rt_i2c_master_recv函數是調用rt_i2c_transfer函數,而rt_i2c_transfer函數是調用master_xfer回調函數。如果你是使用這些接口,那麼device層可以不用理會。

device 層:

  1. i2c_dev層,對接rt-thread設備驅動框架。提供read,write,control函數。並通過函數rt_device_register註冊到設備驅動框架。下面我也會使用這些接口來實現控制OLED。

函數 說明 i2c_bus_device_read I2C讀操作 i2c_bus_device_write I2C寫操作 i2c_bus_device_control I2C總線的控制

軟體I2C設計:

  • 關鍵代碼, 注意軟體I2C的地址需要偏移一位:

struct rt_i2c_bus_device *i2c_bus;#define OLED_I2C_BUS_NAME "i2c1"#define OLED_ADDRESS 0x3cstatic rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t data){ rt_uint8_t buf[2]; struct rt_i2c_msg msgs; buf[0] = reg; buf[1] = data; msgs.addr = OLED_ADDRESS; msgs.flags = RT_I2C_WR; msgs.buf = buf; msgs.len = 2; /@@* 調用I2C設備接口傳輸數據 */ rt_i2c_transfer(bus, &msgs, 1); return RT_EOK;}static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *buf){ struct rt_i2c_msg msgs; msgs.addr = OLED_ADDRESS; msgs.flags = RT_I2C_RD; msgs.buf = buf; msgs.len = len; /@@* 調用I2C設備接口傳輸數據 */ rt_i2c_transfer(bus, &msgs, 1); return RT_EOK;}int oled_init(void){ i2c_bus = rt_i2c_bus_device_find(OLED_I2C_BUS_NAME); if(i2c_bus == RT_NULL) { rt_kprintf("find i2c bus fail!\n"); return RT_ERROR; } rt_kprintf("find i2c bus success!\n"); ......}

  • 效果圖:

硬體I2C設計:

  • 關鍵代碼,注意硬體I2C的地址不需要偏移:

struct rt_i2c_bus_device *i2c_bus;#define OLED_I2C_BUS_NAME "hw_i2c"#define OLED_ADDRESS 0x78static rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t data){ rt_uint8_t buf[2]; struct rt_i2c_msg msgs; buf[0] = reg; buf[1] = data; msgs.addr = OLED_ADDRESS; msgs.flags = RT_I2C_WR; msgs.buf = buf; msgs.len = 2; /@@* 調用I2C設備接口傳輸數據 */ rt_i2c_transfer(bus, &msgs, 1); return RT_EOK;}static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *buf){ struct rt_i2c_msg msgs; msgs.addr = OLED_ADDRESS; msgs.flags = RT_I2C_RD; msgs.buf = buf; msgs.len = len; /@@* 調用I2C設備接口傳輸數據 */ rt_i2c_transfer(bus, &msgs, 1); return RT_EOK;}int oled_init(void){ i2c_bus = rt_i2c_bus_device_find(OLED_I2C_BUS_NAME); if(i2c_bus == RT_NULL) { rt_kprintf("find i2c bus fail!\n"); return RT_ERROR; } rt_kprintf("find i2c bus success!\n"); ......}

使用驅動框架API實現:

  • 關鍵代碼:

#define OLED_I2C_BUS_NAME "hw_i2c"struct rt_device *dev_i2c;#define OLED_ADDRESS 0x78static rt_err_t write_reg(struct rt_device *dev, rt_uint8_t reg, rt_uint8_t data){ rt_uint8_t buf[2]; rt_off_t pos; rt_uint16_t addr = OLED_ADDRESS; rt_uint16_t flags = RT_I2C_WR; buf[0] = reg; buf[1] = data; pos = (flags << 16) | addr; rt_device_write(dev, pos, buf, 2); return RT_EOK;}static rt_err_t read_regs(struct rt_device *dev, rt_uint8_t len, rt_uint8_t *buf){ rt_off_t pos; rt_uint16_t addr = OLED_ADDRESS; rt_uint16_t flags = RT_I2C_WR; pos = (flags << 16) | addr; rt_device_write(dev, pos, buf, 2); return RT_EOK;}int oled_init(void){ dev_i2c = rt_device_find(OLED_I2C_BUS_NAME); if(dev_i2c == RT_NULL) { rt_kprintf("find i2c bus fail!\n"); return RT_ERROR; } rt_kprintf("find i2c bus success!\n"); rt_device_open(dev_i2c, RT_DEVICE_OFLAG_RDWR); .... return RT_EOK;}

總結

  • 有了I2C驅動框架,對於上層來說,不管硬體I2C還是軟體I2C調用接口都是一樣的。
  • rt-thread為了方便,直接在核心層提供了一套API,這樣用戶層調用就更加方便。

相關焦點

  • 「正點原子Linux連載」第六十一章Linux I2C驅動實驗
    第六十一章Linux I2C驅動實驗I2C是很常用的一個串行通信接口,用於連接各種外設、傳感器等器件,在裸機篇已經對I.MX6U的I2C接口做了詳細的講解。本章我們來學習一下如何在Linux下開發I2C接口器件驅動,重點是學習Linux下的I2C驅動框架,按照指定的框架去編寫I2C設備驅動。
  • i2c的設備樹和驅動是如何匹配以及何時調用probe的?
    的設備樹和驅動是如何匹配以及何時調用probe的?,可支持多種不同的i2c控制器驅動。2. i2c架構如何如何管理硬體信息和驅動?不論哪一種總線,一定會維護兩個鍊表,一個是驅動鍊表,一個是硬體信息鍊表。
  • I2C總線驅動程序
    static struct s3c2440_i2c_regs *s3c2440_i2c_regs;static void s3c2440_i2c_start(void){ s3c2440_i2c_xfer_data.state = STATE_START;
  • RT-Thread正式開源了電源管理框架
    RT-Thread正式開源了電源管理框架 李倩 發表於 2018-11-02 15:45:00 PM 組件背景 隨著物聯網(IoT)的興起,產品對低功耗的需求越來越強烈。
  • 國內自主物聯網作業系統 RT-Thread 3.0.1 發布
    在 3.0.1 版本中,USB Stack 得到了極大的增強,在 STM32F4xx 處理器上實現了對應的 USB Device 驅動,以及包括 HID,MSC,甚至是 RNDIS,ECM,WinUSB 等一系列的class driver。
  • rt-smart中的imx6ull用戶態點燈
    rt-smart中的imx6ull用戶態點燈 RTThread物聯網操作 發表於 2021-01-15 13:41:06 rt-thread的rt-smart已經發布有段時間了,其實我之前也寫過幾篇關於rt-smart
  • RT-Thread x STM32 全連接創意創客大賽已開始報名
    其實只要從我們身邊的需求開始就可以啦,比如可以是智能家居類,提升家用設備的智能體驗,做個智能照明調節改善光照;可以是數據採集分析,提升設備維護效率等;可以是醫療類,提高疫情防護效率;鼓勵發揮創意,構建一個解決某個領域某個場景的具體問題的作品。
  • RK3399 探索之旅 / Audio 驅動層速讀
    在 simple-audio-card 已經夠用的情況下,建議優先使用 simple-audio-card 框架,代碼會更加簡潔一些。後面就不再展開繼續分析了,將關注點放在單板相關的部分。Asoc 裡的 Platform driver 一般由 CPU 廠商負責編寫,但是了解其內部實現有有利於我們宏觀把握整個 ASoc 驅動框架。rockchip_i2s.c 核心工作就是對外提供配置和使能 i2s 接口的能力,它最核心的工作如下。
  • Linux regulator子系統分析之四 虛擬regulator device驅動實現
    在前面幾章,我們分析了regulator子系統框架,主要是從數據結構及數據結構間的關聯分析了regulator子系統。為了讓大家對regulator子系統有一個深入的認識,我們本章將實現一個虛擬的regulator device驅動,從而掌握對regulator子系統的開發流程。
  • RT-Thread學習筆記+2.RTT的啟動分析
    摘要本文主要介紹RT-Thread剛剛入手,以及RTT的上電啟動過程分析。然後在rt-thread\bsp\stm32\libraries目錄下,把其他用不到的庫文件也刪掉,只保留了F1的庫文件夾即可。最後強調一下:學習RTT,板子不是關鍵,不論是正點原子、野火、安富萊,還是F1、F4、F7都是可以的,不要糾結板子,最主要的了解RTT的使用方法。
  • 【RT-Thread】線程的基本知識
    (void* parameter){    rt_uint32_t i = 0;    rt_kprintf("This is static thread!\n");        while (1)    {        rt_kprintf("static thread count:%d \r\n", ++i);                rt_thread_delay(500);    }}int main(void){    rt_err_t
  • RT Thread v2.1.0 正式版發布
    下載連結:git打包下載:http://git.oschina.net/rtthread/rt-thread/repository/archive/v2.1.0這個版本經歷的時間比較長,並且原定的一些目標也還未能完成(更全的POSIX支持,包括device fd,socket等在內的select API接口等)。
  • Linux USB總線驅動框架分析
    當USB總線驅動程序識別出設備後,會為其找到該USB設備驅動程序,如鍵盤,滑鼠,U盤。USB通信過程均為主從結構,USB主機發起通信請求,設備進行數據回復,USB設備不具備主動向主機通信的能力。2、USB總線驅動框架如上,我們大致了解了USB的簡單通信過程,那麼什麼是USB總線驅動程序?什麼是USB設備驅動呢?
  • 玩轉RT-Thread之荔枝派Nano(全志F1C100S)一、新手上路
    github倉庫地址:https://github.com/RT-Thread,碼雲倉庫地址:https://gitee.com/rtthread。RT-Thread是率先完成荔枝派Nano移植的嵌入式實時RTOS作業系統,代碼已經合併進RT-Thread官方github倉庫,github地址為:https://github.com/RT-Thread/rt-thread/tree/master/bsp/allwinner_tina。
  • RT-Thread Studio V1.1 新版本發布
    【官網下載地址】https://www.rt-thread.org/page/studio.html(或點擊文末閱讀原文下載) 亮點功能2: RT-Thread配置功能包括框架配置和樹形配置兩種配置方式,在框架配置下可以通過軟體包中心添加軟體包同時可以直接通過點擊組件圖標啟用相關組件
  • 在rt-thread下實現OTA在線固件更新功能
    Bootloader 在線獲取地址: http://iot.rt-thread.com登陸帳號在對固件進行打包操作前,先修改 stm32f407-atk-explorer/applications/main.c 中 APP_VERSION 宏的值為 2.0.0 作為參照,然後重新編譯一遍生成新的 rtthread.bin 文件,修改內容如下圖所示:
  • 是的,RT-Thread 4.0代碼發布了,還有……
    本次發布主要有以下調整:內核:加入SMP支持和64位處理器支持;組件:增加單元測試框架utest、輕型進程實現`components\lwp,並優化部分組件使用體驗;BSP:更新以STM32為基礎的新BSP框架,使得BSP達到了對18個STM32開發板的支持;增加多個BSP:LPC54114-lite、Nuvoton-M487、嘉楠K210 、
  • RT-Thread Studio開發環境搭建
    RT-Thread 最新源碼包簡單易用的工程創建嚮導,快速驗證原型全新圖形化配置系統,同時支持架構圖和樹形圖配置軟體包市場提供豐富的可用軟體包資源豐富的調試功能,快速查看和跟蹤定位代碼問題二、開發環境搭建1 安裝RT-Thread Studio進入rt-thread
  • 黑蘋果必備驅動|觸控滑鼠鍵盤驅動
    還有就是如果你的電腦觸控板不是i2c觸摸板的話,一般都會選擇這個萬能驅動作為首選。那麼如何查看自己的觸控板是否是i2c觸摸板?請大家自行跳轉到關於VoodooI2C.kext的頁面。ApplePS2SmartTouchPad.kext、VoodooI2C.kext和VoodooPS2Controller.kext:三者選擇一個即可,都是驅動鍵盤滑鼠以及觸摸板,三者區別就是適用的類型不一樣,有PS/2、Synaptics、alps、i2c等等,其中Synaptics、alps用ApplePS2SmartTouchPad.kext適配性好一些,VoodooI2C.kext