開篇分割線,驅(qū)動開發(fā)和應(yīng)用是完全兩條路子,系統(tǒng)里面應(yīng)用程序想要通過名稱或者句柄找到驅(qū)動的必要條件就是,驅(qū)動已經(jīng)被注冊到了系統(tǒng)里了,那么我們的第一篇就聊聊他是如何被注冊到系統(tǒng)里面的,直接上圖看下其調(diào)用關(guān)系:
首先我們看到系統(tǒng)初始化過程中,需要對板載硬件進(jìn)行初始化,調(diào)用rt_hw_board_init,而內(nèi)部使用大量的預(yù)編譯宏進(jìn)行開關(guān)控制各個硬件部分是不是需要進(jìn)行初始化,接下來看下函數(shù)rt_hw_usart_init:
int rt_hw_usart_init(void)
{
rt_size_t obj_num = sizeof(uart_obj) / sizeof(struct stm32_uart);
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
rt_err_t result = 0;
stm32_uart_get_dma_config();
for (int i = 0; i < obj_num; i++)
{
uart_obj[i].config = &uart_config[i];
uart_obj[i].serial.ops = &stm32_uart_ops;
uart_obj[i].serial.config = config;
/* register UART device */
result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].config->name,
RT_DEVICE_FLAG_RDWR
| RT_DEVICE_FLAG_INT_RX
| RT_DEVICE_FLAG_INT_TX
| uart_obj[i].uart_dma_flag
, NULL);
RT_ASSERT(result == RT_EOK);
}
return result;
}
這里我們先不分析代碼里面的大部分內(nèi)容,而是迎來了系統(tǒng)為我們提供的第一個注冊函數(shù)rt_hw_serial_register,它是你理解注冊的第一步接下來我們看下這個函數(shù)的原形:
/*
* serial register
*/
rt_err_t rt_hw_serial_register(rt_device_t device, const char* name, rt_uint32_t flag, struct serial_device *serial)
{
RT_ASSERT(device != RT_NULL);
device->type = RT_Device_Class_Char;
device->rx_indicate = RT_NULL;
device->tx_complete = RT_NULL;
device->init = rt_serial_init;
device->open = rt_serial_open;
device->close = rt_serial_close;
device->read = rt_serial_read;
device->write = rt_serial_write;
device->control = rt_serial_control;
device->user_data = serial;
/* register a character device */
return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR | flag);
}
我們注意下它的輸入?yún)?shù),這非常重要,第一個就是一個對象實體(當(dāng)然C語言中就是一個結(jié)構(gòu)體變量),第二個參數(shù)我們得給這個對象起個名字,這樣方便我們的應(yīng)用更好的找到我們的對象,第三個參數(shù)是對象的人設(shè)(舉個例子它可以進(jìn)行那些操作的合集,比如或者讀or寫),四個參數(shù)則是這個對象的私有數(shù)據(jù)(user_data,也就是我和別人不一樣的點),這個函數(shù)的主要任務(wù)就是,定義設(shè)備的類型:RT_Device_Class_Char,并且設(shè)備的動作賦真實可操作的值:初始化/打開/關(guān)閉/讀/寫/控制操作。
最后的最后它需要調(diào)用系統(tǒng)提供的rt_device_register完成最終的注冊,原型如下:
最后的最后我們倒序的方式回憶一下,設(shè)備注冊都做了什么:
rt_device_register:設(shè)備對象dev 與設(shè)備name 建立聯(lián)系,并賦值掙得操作flag類型。
rt_hw_serial_register:設(shè)備對象dev的操作方法具體賦值,私有數(shù)據(jù)賦值和類型賦值,豐富dev對象,然后調(diào)用rt_device_register。
rt_hw_usart_init:傳入真實的設(shè)備操作方法,比如我們用的是stm32芯片,這里就需要將真實的硬件驅(qū)動操作方法,串口配置參數(shù)傳入到serial結(jié)構(gòu)體中,下一章我們會基于這個API講講設(shè)備驅(qū)動的詳細(xì)實現(xiàn)細(xì)節(jié)。