RT-Thread的I/O设备模块及其驱动实现步骤
生活随笔
收集整理的這篇文章主要介紹了
RT-Thread的I/O设备模块及其驱动实现步骤
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、I/O設備控制塊
1、I/O設備控制塊
struct rt_device
{struct rt_object parent;/* 設備類型 */enum rt_device_class_type type;/* 設備參數及打開參數 */rt_uint16_t flag, open_flag;/* 提供給上層應用的回調函數 */rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);rt_err_t (*tx_complete)(rt_device_t dev, void* buffer);/* 公共的設備接口(由驅動程序提供) */rt_err_t (*init) (rt_device_t dev);rt_err_t (*open) (rt_device_t dev, rt_uint16_t oflag);rt_err_t (*close)(rt_device_t dev);rt_size_t (*read) (rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);rt_size_t (*write)(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);rt_err_t (*control)(rt_device_t dev, rt_uint8_t cmd, void *args);/* 用于支持電源管理的函數接口 */
#ifdef RT_USING_DEVICE_SUSPENDrt_err_t (*suspend) (rt_device_t dev);rt_err_t (*resumed) (rt_device_t dev);
#endif
/* 設備的私有數據 */void* user_data;
};typedef struct rt_device* rt_device_t;當前RT-Thread支持的設備類型包括:
enum rt_device_class_type
{RT_Device_Class_Char = 0, /* 字符設備 */RT_Device_Class_Block, /* 塊設備 */RT_Device_Class_NetIf, /* 網絡接口 */RT_Device_Class_MTD, /* 內存設備 */RT_Device_Class_CAN, /* CAN設備 */RT_Device_Class_RTC, /* RTC設備 */RT_Device_Class_Sound, /* 聲音設備 */RT_Device_Class_Display, /* 顯示設備 */RT_Device_Class_Unknown /* 未知設備 */
};注:uspend、resume回調函數只會在RT_USING_DEVICE_SUSPEND宏使能的情況下才
會有效。
從設備控制塊,我們可以看到,每個設備對象都會在內核中維護一個設備控制塊結構,
這種結構是使設備對象繼承rt_object基類,然后形成rt_device設備類型。2、注冊設備
一個設備能夠被上層應用訪問前,需要先把這個設備注冊到系統中,并添加一些相應的
一些屬性。這些注冊的設備均可以通過設備名,采用“查找設備接口”的方式從系統中查找
到,從而獲得該設備控制塊(或設備句柄)。注冊設備的函數接口如下:
rt_err_t rt_device_register(rt_device_t dev, const char* name, rt_uint8_t flags);函數參數:
dev 設備句柄;
name 設備名稱;
flag 設備模式標志:
flags參數支持下列參數(可以采用或的方式支持多種參數):#define RT_DEVICE_FLAG_DEACTIVATE 0x000 /* 未初始化設備 */
#define RT_DEVICE_FLAG_RDONLY 0x001 /* 只讀設備 */
#define RT_DEVICE_FLAG_WRONLY 0x002 /* 只寫設備 */
#define RT_DEVICE_FLAG_RDWR 0x003 /* 讀寫設備 */
#define RT_DEVICE_FLAG_REMOVABLE 0x004 /* 可移除設備 */
#define RT_DEVICE_FLAG_STANDALONE 0x008 /* 獨立設備 */
#define RT_DEVICE_FLAG_ACTIVATED 0x010 /* 已激活設備 */
#define RT_DEVICE_FLAG_SUSPENDED 0x020 /* 掛起設備 */
#define RT_DEVICE_FLAG_STREAM 0x040 /* 設備處于流模式 */
#define RT_DEVICE_FLAG_INT_RX 0x100 /* 設備處于中斷接收模式*/
#define RT_DEVICE_FLAG_DMA_RX 0x200 /* 設備處于DMA接收模式 */
#define RT_DEVICE_FLAG_INT_TX 0x400 /* 設備處于中斷發送模式*/
#define RT_DEVICE_FLAG_DMA_TX 0x800 /* 設備處于DMA發送模式 */設備流模式RT_DEVICE_FLAG_STREAM參數用于向串口終端輸出字符串:當輸出的字符
是“\n”時,自動在前面補一個“\r”做分行。
函數返回
返回RT_EOK
警告:應當避免重復注冊已經注冊的設備,以及注冊相同名字的設備。3、移除設備
將設備從設備系統中移除,被卸載的設備將不能再通過“查找設備接口”被查找到。卸
載設備的函數接口如下所示:
rt_err_t rt_device_unregister(rt_device_t dev)函數參數
參數 描述
dev 設備句柄;
函數返回
返回RT_EOK
注:卸載設備并不會釋放設備控制塊所占用的內存4、初始化所有設備
初始化所有注冊到設備對象管理器中的未初始化的設備,可以通過如下函數接口完成:rt_err_t rt_device_init_all(void)函數參數
無
函數返回
返回RT_EO
? 注:此函數將逐漸廢棄,不推薦在應用程序中調用。當一個設備初始化完成后它
的flags域中的RT_DEVICE_FLAG_ACTIVATED應該被置位。如果設備的flags域已經是
RT_DEVICE_FLAG_ACTIVATED,調用這個接口將不再重復做初始化。5、查找設備
根據指定的設備名稱查找設備,可以通過如下接口完成:rt_device_t rt_device_find(const char* name)使用這個函數接口時,系統會在設備對象類型所對應的對象容器中遍歷尋找設備對象,
然后返回該設備的句柄,如果沒有找到相應的設備對象,則返回RT_NULL。
函數參數
參數 描述
name 設備名稱。
函數返回
查找到對應設備將返回相應的設備句柄;否則返回RT_NULL。6、打開設備
根據設備控制塊來打開設備,可以通過如下函數接口完成:rt_err_t rt_device_open (rt_device_t dev, rt_uint16_t oflags);函數參數
參數 描述
dev 設備句柄;
oflags 訪問模式。其中oflags支持以下列表中的參數:#define RT_DEVICE_OFLAG_CLOSE 0x000 /* 設備已經關閉(內部使用) */
#define RT_DEVICE_OFLAG_RDONLY 0x001 /* 以只讀方式打開設備 */
#define RT_DEVICE_OFLAG_WRONLY 0x002 /* 以只寫方式打開設備 */
#define RT_DEVICE_OFLAG_RDWR 0x003 /* 以讀寫方式打開設備 */
#define RT_DEVICE_OFLAG_OPEN 0x008 /* 設備已經打開(內部使用) */函數返回
返回驅動的open函數返回值
注:如果設備注冊時指定的參數中包括RT_DEVICE_FLAG_STANDALONE參數,此設備將
不允許重復打開,返回-RT_EBUSY。7、關閉設備
根據設備控制塊來關閉設備,可以通過如下函數接口完成:rt_err_t rt_device_close(rt_device_t dev)函數參數
參數 描述
dev 設備句柄;函數返回
返回驅動的close函數返回值8、讀設備
從設備中讀取,或獲得數據,可以通過如下函數接口完成:rt_size_t rt_device_read (rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)調用這個函數,會從設備dev中獲得數據,并存放在buffer緩沖區中。這個緩沖區的最
大長度是size。pos根據不同的設備類別存在不同的意義。
函數參數
參數 描述
dev 設備句柄;
pos 讀取數據偏移量;
buffer 內存緩沖區指針,讀取的數據將會被保存在緩沖區中;
size 讀取數據的大小。
函數返回返回讀到數據的實際大小(如果是字符設備,返回大小以字節為單位;如果是塊設備,
返回的大小以塊為單位);如果返回0,則需要讀取當前線程的errno來判斷錯誤狀態。9、寫設備
向設備中寫入數據,可以通過如下函數接口完成:rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)調用這個函數,會把緩沖區buffer中的數據寫入到設備dev中。寫入數據的最大長度是
size。pos根據不同的設備類別存在不同的意義。
函數參數參數 描述
dev 設備句柄;
pos 讀取數據偏移量;
buffer 內存緩沖區指針,放置要寫入的數據;
size 寫入數據的大小。函數返回
返回寫入數據的實際大小(如果是字符設備,返回大小以字節為單位;如果是塊設備,
返回的大小以塊為單位);如果返回0,則需要讀取當前線程的errno來判斷錯誤狀態
? 注:在RT-Thread的塊設備中,從1.0.0版本開始, rt_device_read()/rt_device_write()接
口的pos、size參數按照以塊為單位。0.3.x以前的版本則按字節為單位。10、控制設備
根據設備控制塊來控制設備,可以通過下面的函數接口完成:rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);函數參數
參數 描述
dev 設備句柄;
cmd 命令控制字,這個參數通常與設備驅動程序相關;
arg 控制的參數函數返回
返回驅動控制接口的返回值。11、設置數據接收指示
設置一個回調函數,當硬件設備收到數據時回調以通知用程序有數據到達。可以通過如
下函數接口完成設置接收指示:
rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind );
(rt_device_t dev,rt_size_t size));在調用這個函數時,回調函數rx_ind由調用者提供。當硬件設備接收到數據時,會回調
這個函數并把收到的數據長度放在size參數中傳遞給上層應用。上層應用線程應在收到指示
后,立刻從設備中讀取數據。
函數參數
參數 描述
dev 設備句柄;
rx_ind 接收回調函數。函數返回
返回RT_EOK12、設置發送完成指示
在上層應用調用rt_device_write寫入數據時,如果底層硬件能夠支持自動發送,那么上層應用可以設置一個回調函數。這個回調函數會在底層硬件給出的發送完成后(例如DMA傳送完成或FIFO已經寫入完畢產生完成中斷時)被調用。可以通過如下函數接口設置設備發送完成指示:
rt_err_t rt_device_set_tx_complete(rt_device_t dev, rt_err_t (*tx_done)(rt_device_t dev,void *buffer))
調用這個函數時,回調函數tx_done參數由調用者提供,當硬件設備發送完數據時,由驅動程序回調這個函數并把發送完成的數據塊地址buffer做為參數傳遞給上層應用。上層應用(線程)在收到指示時應根據發送buffer的情況,釋放buffer內存塊或將其做為下一個寫數據的緩存。
函數參數
參數 描述
dev 設備句柄;
tx_done 發送回調函數。函數返回
返回RT_EOK二、設備驅動設備驅動必須實現的接口
在10.1節中提及了RT-Thread設備接口類,我們著重看看其中包含的一套公共設備接口
(類似上節說的設備訪問接口,但面向的層次已經不一樣,這里是面向底層驅動):/* 公共的設備接口(由驅動程序提供) */
rt_err_t (*init) (rt_device_t dev);
rt_err_t (*open) (rt_device_t dev, rt_uint16_t oflag);
rt_err_t (*close)(rt_device_t dev);
rt_size_t (*read) (rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
rt_size_t (*write)(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
rt_err_t (*control)(rt_device_t dev, rt_uint8_t cmd, void *args);/* 用于支持電源管理的函數接口 */#ifdef RT_USING_DEVICE_SUSPENDrt_err_t (*suspend) (rt_device_t dev);rt_err_t (*resumed) (rt_device_t dev);
#endif這些接口也是上層應用通過RT-Thread設備接口進行訪問的實際底層接口(如設備操作接口與設備驅動程序接口的映射 ):即這些驅動實現的底層接口是上層應用最終訪問的落腳點,例如上層應用調用rt_device_read接口進行設備讀取數據操作,上層應先調用rt_device_find獲得相對應的設備句柄,而在調用rt_device_read時,就是使用這個設備句柄所對應驅動的driver_read。上述的接口是一一對應關系。
I/O設備模塊提供的這六個接口(rt_device_init/open/read/write/control),對應到設備驅動程序的六個接口(driver_init/open/read/write/control等),可以認為是底層設備驅動必須提供的接口:1、init
設備的初始化。設備初始化完成后,設備控制塊的flag會被置 成已激活狀態(RT_DEVICE_FLAG_ACTIVATED)。如果設備控制塊 的flag
不是已激活狀態,那么在設備框架調用 rt_device_init_all接口時將調用此設備驅動的init接口進行 設備初始化;如果設備控制塊中的flag標志已經設置成激活狀 態,那么再運行初始化接口時,會立刻返回,而不會重新進行 初始化。
2、open
打開設備。有些設備并不是系統一啟動就已經打開開始運行; 或者設備需要進行數據接收,但如果上層應用還未準備好,設 備也
不應默認已經使能并開始接收數據。所以建議在寫底層驅 動程序時,應在調用open接口時才使能設備。3、close
關閉設備。建議在打開設備時,設備驅動自行維護一個打開計數,在打開設備時進行+1操作,在關閉設備時進行-1操作, 當計數
器變為0時,進行真正的關閉操作。4、read
從設備中讀取數據。參數pos指出讀取數據的偏移量,但是有些 設備并不一定需要指定偏移量,例如串口設備,設備驅動應忽 略這
個參數。而對于塊設備來說,pos以及size都是以塊設備的 數據塊大小做為單位的。例如塊設備的數據塊大小是512,而參 數中pos= 10, size = 2,那么驅動應該返回設備中第10個塊 (從第0個塊做為起始),共計2個塊的數據。這個接口返回的 類型rt_size_t,即讀到的字節數或塊數目。正常情況下應 該會返回參數中size的數值,如果返回零請設置對應的errno值。
5、write
向設備中寫入數據。參數pos指出寫入數據的偏移量。與讀操作 類似,對于塊設備來說,pos以及size都是以塊設備的數據塊 大小做
為單位的。這個接口返回的類型是rt_size_t,即真實寫 入數據的字節數或塊數目。正常情況下應該會返回參數中size 的數值,如果返回零請設置對應的errno值。6、control
根據不同的cmd命令控制設備。命令往往是由底層各類設備驅 動自定義實現。例如參數RT_DEVICE_CTRL_BLK_GETGEOME,意思 是獲取塊設備的大小信息。三、設備驅動實現的步驟在實現一個RT-Thread設備時,可以按照如下的步驟進行(對于一些復雜的設備驅動,例如以太網接口驅動、圖形設備驅動,請參看網絡組件、GUI部分章節):
? 按照RT-Thread的對象模型,擴展一個對象有兩種方式:
? 定義自己的私有數據結構,然后賦值到RT-Thread設備控制塊的user_data指針上;
? 從struct rt_device結構中進行派生。
? 實現RT-Thread I/O設備模塊中定義的6個公共設備接口,開始可以是空函數(返回類型是rt_err_t的可默認返回RT_EOK);
? 根據自己的設備類型定義自己的私有數據域。特別是在可能有多個相類似設備的情況下(例如串口1、2),設備接口可以共用同一套接口,不同的只是各自 的數據域(例如寄存器基地址);
? 根據設備的類型,注冊到RT-Thread設備框架中。
總結
以上是生活随笔為你收集整理的RT-Thread的I/O设备模块及其驱动实现步骤的全部內容,希望文章能夠幫你解決所遇到的問題。