關於GPIO的內部基準時鐘已經打開,下一步就是給GPIO安裝操作函數了,只有安裝了操作函數,才能夠利用的標準的接口訪問GPIO.
1、文件:Io_gpio.c (source\io\gpio)中
/*FUNCTION*-------------------------------------------------------------------
*
* Function Name : _io_gpio_install
* Returned Value : _mqx_uint a task error code or MQX_OK
* Comments :
* Install a gpio driver.
*
*END*----------------------------------------------------------------------*/
_mqx_uint _io_gpio_install
(
/* [IN] A string that identifies the device for fopen */
/* input values are those identifiers defined in io_gpio.h file */
char_ptr identifier
)
{ /* Body */
if (IO_OK == gpio_cpu_init())
return _io_dev_install(identifier, _io_gpio_open, _io_gpio_close, _io_gpio_read, _io_gpio_write, gpio_cpu_ioctl, NULL);
return (_mqx_uint)IO_ERROR;
} /* Endbody */
分析:
gpio_cpu_init()函數,我們已經知道是爲了實現硬件層gpio操作時鐘的使能控制的。那麼_io_dev_install(xxxx)函數,就是給gpio模塊安裝驅動函數的,驅動函數類似於linux方式,包括:打開、關閉、讀、寫、控制。這些控制函數都在本文件內實現,當我們調用fopen、fclose、fread、fwrite、ioctl等系統統一接口時,mqx會在內部通過映射表,調用gpio模塊對應的功能函數。例如:fopen -> _io_gpio_open。
2、文件:Io_inst.c (source\io)中
/*FUNCTION*-------------------------------------------------------------------
*
* Function Name : _io_dev_install
* Returned Value : _mqx_uint a task error code or MQX_OK
* Comments :
* Install a device dynamically, so tasks can fopen to it.
*
*END*----------------------------------------------------------------------*/
_mqx_uint _io_dev_install
(
/* [IN] A string that identifies the device for fopen */
char_ptr identifier,
/* [IN] The I/O open function */
_mqx_int (_CODE_PTR_ io_open)(MQX_FILE_PTR, char _PTR_, char _PTR_),
/* [IN] The I/O close function */
_mqx_int (_CODE_PTR_ io_close)(MQX_FILE_PTR),
/* [IN] The I/O read function */
_mqx_int (_CODE_PTR_ io_read)(MQX_FILE_PTR, char _PTR_, _mqx_int),
/* [IN] The I/O write function */
_mqx_int (_CODE_PTR_ io_write)(MQX_FILE_PTR, char _PTR_, _mqx_int),
/* [IN] The I/O ioctl function */
_mqx_int (_CODE_PTR_ io_ioctl)(MQX_FILE_PTR, _mqx_uint, pointer),
/* [IN] The I/O initialization data */
pointer io_init_data_ptr
)
{ /* Body */
return (_io_dev_install_ext(identifier, io_open, io_close, io_read, io_write, io_ioctl, (_mqx_int (_CODE_PTR_)(IO_DEVICE_STRUCT_PTR))NULL, io_init_data_ptr));
} /* Endbody */
分析:
GPIO的設備驅動安裝函數,該函數接收5個函數指針和一個void型指針。調用帶函數指針形參的函數時,我們只需要把對應函數實參的名字傳遞過去就可以了,不用對函數名稱取地址&,因爲在C語言內部,函數名稱指的就是函數的入口地址,即:該函數運行的首地址,關於這個不再多說。最後一個void*指針,是給該函數傳遞初始化數據的,這裏傳遞了一個NULL。
該函數經過了一層封裝,調用了_io_dev_install_ext(xxx)函數,進行具體的安裝工作,與外層函數相比,它多了一個形參:
(_mqx_int (_CODE_PTR_) (IO_DEVICE_STRUCT_PTR)) NULL。主體是一個NULL,前面的是一個強制類型轉換,轉換成了什麼,轉換成了一個函數指針,該函數返回_mqx_int數值,形參IO_DEVICE_STRUCT_PTR。詳細分析進入該函數即可看到。
可能有人會問:傳遞一個空數據,幹嘛還要再進行一次函數調用?這其中就涉及到函數維護的知識了,可能_io_dev_install_ext()函數出現較早,而那個參數隨着版本升級,不再需要,但是爲了保持一致性,又不能輕易修改已經成型的函數,這時候怎麼辦。辦法就是再增加一層函數,利用這層函數屏蔽掉這個變化,使上層看不到無效數據。以後,當維護函數數據較多時,肯定會用到這個方法。
對_io_dev_install_ext(xx)函數的分析,估計需要的信息較多,我們留在下一節吧!