关于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)函数的分析,估计需要的信息较多,我们留在下一节吧!