设备驱动在AWbus-lite中驱动设备正常工作

电子说

1.2w人已加入

描述

本文导读

一个硬件设备正常工作的前提是系统中存在对应的驱动。AWorks提供了大量常用硬件设备的驱动,用户通常不需要开发驱动。为了使读者对设备驱动有一定的理解,本文介绍了设备驱动相关的基础概念,展示了设备驱动在AWbus-lite中驱动设备正常工作的原理。

本文为《面向AWorks框架和接口的编程(上)》第三部分软件篇——第13章——第3小节:设备驱动。

13.3  设备驱动

上面讨论了如何通过Method机制获得具体设备提供的LED服务,系统能够从某一设备获得LED服务的前提是,该硬件具有提供LED服务的能力。一个硬件设备相关的功能,需要通过设备驱动才能体现出来,进而为系统服务。为此,在LED设备驱动中,需要实现一个LED服务,以供上层获取。

13.3.1  基础驱动信息

AWBus-lite对设备驱动进行了高度的抽象,定义了驱动的基本结构,一个设备驱动相关的信息统一使用一个结构体常量进行描述。其中包含了诸多信息,比如:驱动名、驱动初始化入口、驱动提供的Method对象等。开发驱动的核心即完成一个结构体常量的定义。

不同总线下的设备驱动需要提供的驱动信息可能不同,对应的驱动信息类型也就不同。但无论什么总线下的设备驱动,其驱动信息类型均是从基础驱动信息类型派生而来的,也就是说,无论什么设备驱动,都需要提供AWBus-lite定义的基础驱动信息,即使需要扩展其它驱动信息,也只能在基础驱动信息的基础上进行扩展。

由于所有设备驱动均会提供基础驱动信息,因此,AWBus-lite可以方便的对所有驱动进行统一的管理。基础驱动信息的类型为struct awbl_drvinfo,具体定义详见程序清单13.19。

程序清单13.19 struct awbl_drvinfo类型定义(awbl_lite.h)

致远电子

要实现一个驱动,需要定义一个该类型结构体,并完成各个成员的赋值。下面,首先对各个成员的含义作简要介绍。

1.  AWBus-lite版本号

awb_ver表示该驱动支持的AWBus-lite版本号,当前AWBus-lite的版本号为:AWBL_VER_1,其在awbus_lite.h文件中定义如下:

致远电子

在新开发驱动时,将awb_ver设置为AWBL_VER_1即可。

2.  总线ID

bus_id表示总线ID,该值由总线类型和设备类型两部分组成。总线类型与设备描述中的总线类型概念一致,其表示了该驱动所驱动的设备挂在何种总线上,常见总线类型详见表12.1。实际中,在进行驱动和设备的匹配操作时,只有当驱动信息中的总线类型与设备描述中的总线类型一致时,才会继续判定设备名和驱动名是否一致,只有当两者完全相同时,驱动和设备才会判定为匹配。设备类型表明该驱动对应的设备是普通设备还是特殊设备(总线控制器),若是总线控制器,则其驱动的设备又会扩展出另外一条总线。设备类型可能的取值详见表13.4。

表13.4 设备类型宏定义

致远电子

bus_id的值为总线类型和设备类型的或值(C语言的“|”运算符)。特别地,若一个设备是普通设备,则设备类型可以省略,即

AWBL_DEVID_DEVICE宏可以被省略。

例如,对于使用GPIO直接控制的LED设备驱动,GPIO是一种片内外设,当前并没有将GPIO视为一种总线,在AWBus-lite中,由GPIO直接驱动的设备也视为挂在PLB上的一种设备。因而总线类型为:AWBL_BUSID_PLB,同时,LED设备只是一个普通设备,并非总线控制器,因而设备类型为AWBL_DEVID_DEVICE。bus_id的值即为:

AWBL_BUSID_PLB | AWBL_DEVID_DEVICE

(或省略AWBL_DEVID_DEVICE,直接设定为

AWBL_BUSID_PLB)。

对于i.MX28x的I²C驱动,其驱动的设备是片内外设,挂在PLB总线上,因而总线类型为:AWBL_BUSID_PLB。同时,i.MX28x 中的I²C设备又是一种总线控制器,可以扩展出一条I²C总线,因而设备类型为:

AWBL_DEVID_BUSCTRL。bus_id的值即为:

AWBL_BUSID_PLB | AWBL_DEVID_BUSCTRL。

对于PCF85063设备驱动,其驱动的设备挂在I²C总线上,因而总线类型为:AWBL_BUSID_I²C。同时,PCF85063是一个普通设备,并非总线控制器,因而设备类型为AWBL_DEVID_DEVICE。bus_id的值即为:

AWBL_BUSID_I²C | AWBL_DEVID_DEVICE

(或省略AWBL_DEVID_DEVICE,直接设定为

AWBL_BUSID_I²C)。

3.  驱动名

p_drvname表示该驱动的名字。在设备描述中,使用了“设备名”用来描述设备的名字,在系统启动时,会为每个设备寻找合适的驱动,当驱动的总线类型和设备描述中的总线类型一致时,将会把“驱动名”与“设备名”进行比对,完全一致时,将视为驱动与设备匹配,进而将该驱动和对应的设备进行绑定。在AWBus-lite中,当一个驱动和设备匹配后,驱动名和设备名势必是完全一致的,因而在匹配后可以直接将驱动名作为设备名。

对于使用GPIO控制的LED设备驱动,其驱动名可以定义为:

致远电子

4.  驱动入口点

设备在使用前,需要完成必要的初始化操作,例如,对于使用GPIO控制的LED设备,需要在初始时将引脚配置为输出模式。作为设备驱动,必须提供相关的初始化函数以完成设备的初始化。驱动入口点p_busfuncs即用于提供初始化函数的入口,其为指向struct awbl_drvfuncs类型结构体常量的指针,struct awbl_drvfuncs类型定义详见程序清单13.20。

程序清单13.20 struct awbl_drvfuncs类型定义(awbus_lite.h)

致远电子

其中,包含了3个函数指针,分别对应了AWBus-lite中三个阶段的初始化动作。由此可见,为了完成一个设备的初始化,驱动需要提供三个初始化函数,分别用于完成设备不同阶段的初始化,各阶段对应的初始化函数将在系统启动过程中被依次调用。各阶段初始化函数的类型是完全一样的,均只有一个指向设备实例的指针作为形参,且均无返回值。即:

致远电子

例如,对于使用GPIO控制的LED设备驱动,为了完成设备的初始化,需要提供3个初始化函数,结构性范例程序详见程序清单13.21。

程序清单13.21 驱动初始化函数结构性范例程序

致远电子

其中,__g_awbl_drvfuncs_led_gpio的地址即可作为驱动入口点p_busfuncs的值。各初始化函数的实现将在后文进行详细介绍。

在系统启动时,将根据驱动信息中提供的驱动入口点信息,依次调用各阶段对应的初始化函数,进而完成一个设备的初始化。在调用各初始化函数时,传入形参p_dev的值为设备描述中p_dev的值,其本质上指向了静态定义的设备实例。

在AWBus-lite中,将设备的初始化分为了三个阶段:第一阶段、第二阶段、第三阶段。这样的划分有着极其重要的意义,各阶段对应的初始化函数被系统调用的时机并不相同。

  • 第一阶段

第一阶段为设备初始化过程中最先进入的阶段,在该阶段中,系统总中断被关闭,OS内核服务(如多任务管理)尚未提供,调试串口也尚未准备就绪。作为设备驱动,只能处理一些设备相关的最基本、最简单的操作,不可在本阶段中使用常见的其它服务,比如:执行连接中断、申请信号量、打印调试信息等操作。对于绝大部分普通设备驱动,该阶段对应的函数设置为空,不执行任何操作。

  • 第二阶段

第二阶段为设备初始化的主要阶段,设备相关的绝大部分初始化操作均在该阶段中完成,在第二阶段中,系统相关的服务均已准备就绪,比如:中断、调试串口、信号量、多任务等。可以在第二阶段中使用这些服务。

  • 第三阶段

第三阶段作为第二阶段后的一个阶段,系统相关的服务同样已准备就绪,比如:中断、调试串口、信号量、多任务等。第三阶段主要用于完成比较耗时的初始化操作,第三阶段相关的操作将在一个单独的任务中执行,不会影响系统的整体启动过程。

例如,在某一设备的初始化过程中,有一个特殊的操作需要一分钟才能完成,为了不影响系统的启动效率,可以将该耗时较长的操作放在第三阶段中完成,如此一来,系统同样可以快速启动,进而运行至应用程序入口,即aw_main()。否则,若将该操作放在第二阶段中,则系统必须在第二阶段初始化完成后才能启动完成,接着才能运行至应用程序入口处,导致系统的整个启动过程变慢。

系统启动速度的快慢将直接影响用户体验,以数字示波器为例,传统示波器的开机时间几乎都在30秒甚至1分钟以上,对于现场测试工程师来说,有可能会遗漏稍纵即逝的异常信号,几乎所有的示波器厂商都对这个需求熟视无睹,而广州致远电子有限公司的设计理念却与众不同,使用AWorks助力ZDS系列示波器,使用户从按下电源到开始使用,整个过程仅需要十余秒,其开机时间击败了所有其他品牌的示波器。让用户从按下电源的那一刻起,就能感受到极致的体验。

在AWBus-lite中,巧妙的将一个设备的初始化过程分为了三个阶段,可以使系统的启动速度从结构上得到优化,在大多数中小系统中,系统启动时间都小于1秒。

特别地,在一些设备的初始化过程中,可能并没有耗时较长的操作,此时,可以将第三阶段对应的函数设置为空,不执行任何操作。

5.  Method对象列表

每个设备都是为系统提供某种服务而存在的,其提供了相应的服务,才能被系统、用户所使用。如LED设备可以为系统提供LED服务,为了使系统能够获取到设备提供的LED服务,需要提供相应的Method对象,以指定获取LED服务对应的入口函数。定义一个Method对象的范例详见程序清单13.22。

程序清单13.22 定义Method对象范例程序

致远电子

一些特殊设备,可能可以为系统提供多种服务,这时,对应驱动中将需要定义多个Method对象,每个Method对象用于获取某一种服务。

为此,AWBus-lite中,将一个驱动定义的所有Method对象存放在一个列表中,并以AWBL_METHOD_END表示列表的结束。驱动信息p_methods即用于指向Method对象列表,范例详见程序清单13.23。

程序清单13.23 定义Method对象列表范例程序

致远电子

其中,__g_led_gpio_dev_methods即可作为驱动信息中p_methods的值。Method对象的具体定义将在后文详细介绍。

6.  驱动探测函数

在基础驱动信息中,pfunc_drv_probe是一个函数指针,用于指向一个探测函数,用于探测驱动是否支持该设备。其类型为:

致远电子

由此可见,其指向的函数是具有一个p_dev形参,返回值为布尔类型的函数。

p_dev是指向设备实例的指针,用于指定探测的设备。返回值为布尔类型,返回AW_TRUE时,探测成功;否则,探测失败。

前面提到过系统是如何判定设备与驱动是否匹配的。设备与驱动匹配的首要条件是设备描述中的总线类型与驱动信息中的总线类型一致。在总线类型一致的情况下,可以增加两种额外的判定条件。

一种条件是针对该总线类型的,该总线类型下的设备和驱动,都必须满足该条件。不同总线类型对该条件的定义可能不同,但对于绝大部分总线来说,其判定条件都是:驱动名和设备名是否相同。这也是前面提到系统中使用驱动名和设备名进行匹配判定的原因。需要用户注意的是,这种条件是与具体总线类型相关的,虽然绝大部分总线类型的判定条件都是驱动名和设备名是否相同,但也不排除可能出现某一类型的总线,其不要求设备名和驱动名一致,这种情况极为罕见,如果出现,应该对该类总线作出非常重要的特殊说明。

一种条件是针对某一特定驱动的,这种条件通过驱动提供探测函数来实现。若驱动不需要进行额外的判断,则将pfunc_drv_probe的值设置为NULL。若需要进行额外的判断,则应提供一个有效的探测函数,在探测函数的实现中,若判定驱动和设备匹配,能够支持相应的设备,则应返回AW_TRUE,以告知AWBus-lite系统,驱动和设备是匹配的,进而将设备和驱动进行绑定;若判定驱动和设备不匹配,驱动不支持该设备,则应该返回AW_FALSE,以告知AWBus-lite系统,驱动和设备不匹配。

通常情况下,对于绝大部分驱动而言,并不需要进行的探测,此时,需将pfunc_drv_probe的值设置为NULL。基于此,通常情况下,只要驱动与设备的总线类型和名字一致,均可视为驱动和设备匹配。

以上仅仅对基础驱动信息中各成员的含义进行了简要的介绍,其中的初始化函数,Method对象列表等均还未实际实现,后文将以开发LED设备驱动为例,进一步介绍设备类型的定义、设备信息类型的定义、初始化函数的实现,Method对象的具体实现等。

13.3.2  实际驱动信息

在开发具体的设备驱动时,需要明确该驱动所对应的设备挂在何种总线上,不同总线下的设备对应驱动可能需要提供不同的驱动信息,此时,它们对应的驱动信息类型也是不同的。无论何种总线下的设备驱动,它们的驱动信息类型都是从基础驱动信息派生而来的,以便在基础驱动信息的基础上,扩展一些特殊的总线相关的成员。但在实际中,常见的大多数总线下的设备驱动信息并没有扩展更多的成员,例如,PLB总线下的设备驱动,其对应的驱动信息类型为awbl_plb_drvinfo_t,其定义详见程序清单13.24。

程序清单13.24 awbl_plb_drvinfo_t类型定义(awbl_plb.h

致远电子

对于I²C总线下的设备驱动,其对应的驱动信息类型为awbl_i2c_drvinfo_t,其定义详见程序清单13.25。

程序清单13.25 awbl_i2c_drvinfo_t类型定义(awbl_i2cbus.h)

致远电子

由此可见,这些总线下的设备驱动,并没有扩展额外的成员,均是对基础驱动信息的简单继承。即使如此,出于结构性考虑,为了便于后续扩展,每种总线都单独定义了相应的驱动信息类型。用户仅需了解到,开发不同总线下的设备驱动时,它们对应的驱动信息类型可能是不同的,需要查看总线对应的实际驱动信息类型,以判断在开发驱动时,除了提供基础驱动信息外,是否还需要提供其它额外的信息。

13.3.3  定义设备类型

通过前面对硬件设备列表的介绍可知,在硬件设备的描述中,需要使用驱动定义的具体设备类型定义一个设备实例,用于为设备分配必要的内存空间,设备相关的状态、变量、属性等相关数据都可以存放在该设备实例中。

在AWBus-lite中,所有具体设备类型均是从基础设备类型struct awbl_dev派生而来的,在定义设备类型时,基础设备类型的成员应该作为设备类型的第一个成员,基于此,可以定义LED设备类型为:

致远电子

显然,要完成LED设备类型的定义,重点是考虑需要定义哪些其它成员。LED设备的核心功能是为系统提供LED服务,回顾LED服务的具体类型定义,详见程序清单13.26。

程序清单13.26 LED服务类型定义(awbl_led.h)

致远电子

LED服务类型作为一个结构体类型,显然,需要占用一定的内存空间,由于每个LED设备均能提供LED服务,因此,可以将LED服务作为LED设备类型的一个成员。当需要为系统上层提供LED服务时,只需要将LED服务中的各个成员正确赋值,提交给上层即可。基于此,可以更新LED设备类型的定义,详见程序清单13.27。

程序清单13.27 LED设备类型的定义

致远电子

当前仅仅从LED的主要功能出发,完成了LED设备类型的定义,若在开发过程中,发现需要在设备类型中增加新的成员,可以随时动态添加。在设备描述中,如需定义一个设备实例,直接使用该类型定义一个设备实例即可,例如:

致远电子

13.3.4  定义设备信息类型

通过前面对硬件设备列表的介绍可知,在硬件设备的描述中,需要提供硬件设备信息。硬件设备信息的具体类型同样由相应的驱动定义。

在使用LED设备时,往往需要用户提供一些必要的信息,例如,在使用GPIO控制LED时,需要知道各个LED对应的引脚信息,即使用哪些GPIO引脚控制相应的LED。同时,对于不同的硬件电路,点亮LED对应的GPIO输出电平可能是不同的,可能是输出低电平点亮LED,也可能是输出高电平点亮LED,此外,不同硬件设备中,LED的数目也可能存在差异,这些都是与具体硬件相关的。

为了便于对这些信息进行修改、配置,可以定义一个信息结构体类型,以包含所有需要由用户提供的信息,即:

致远电子

特别的,在部分平台中,使用GPIO前,可能需要一些特殊的平台相关的操作,比如:使能时钟、申请GPIO的使用权、配置GPIO的特殊模式等。由于具体平台相关的操作当前并不能确定,为此,可以由用户提供一个平台初始化函数,以在必要时,通过该函数完成平台相关的初始化操作。基于此,在设备信息类型中新增一个函数指针成员,用以指向用户提供的平台初始化函数,即:

致远电子

新增的pfn_plfm_init是一个函数指针,指向的函数是无参数、无返回值的函数,用于完成在使用LED前需要完成的平台相关的初始化操作。在一些情况下,可能不需要执行任何平台相关的初始化操作,则可以将其值设置为NULL。

上面主要基于硬件层面定义了设备信息中的各个成员,此外,LED主要的功能是提供LED服务,在LED服务中,需要提供的一个重要信息是LED服务信息,其主要包含了LED设备中各个LED的编号信息,LED服务信息的定义详见程序清单13.28。

程序清单13.28 LED服务信息类型定义

致远电子

由此可见,LED服务信息中包含了起始编号和结束编号,用户通过设定起始编号和结束编号,就可以为设备中的每个LED分配一个唯一ID。为了存放用户分配的ID信息,可以在设备信息类型中新增一个LED服务信息成员,设备信息完整的定义详见程序清单13.29。

程序清单13.29 LED设备信息类型完整定义

致远电子

在EPC-AW280开发套件中,板载了两个LED,标识分别为RUN、Error,对应的引脚分别为PIO2_6、PIO2_5,等效原理图详见图13.2,由此可见,当GPIO输出低电平时,对应的LED点亮,GPIO输出高电平时,对应的LED熄灭。若为两个LED分配的ID号分别为0、1,则在设备描述中,LED设备信息的定义范例详见程序清单13.30。

致远电子

图13.2 板载LED电路

程序清单13.30 LED设备信息定义范例

致远电子

13.3.5  实现三个阶段的初始化函数

在一个设备使用前,需要完成设备相关的初始化操作,例如,对于LED驱动,可能需要将GPIO设置为输出模式等。在基础驱动信息中,使用了驱动入口点指定驱动提供的初始化函数,其结构性代码详见程序清单13.21,共需实现3个初始化函数,分别对应3个阶段。

需要特别注意的是,虽然各阶段初始化函数的形参类型均为awbl_dev_t *,但实际上,在系统调用各初始化函数时,传入形参p_dev的值为设备描述中p_dev的值,其本质上指向了静态定义的设备实例。对于GPIO控制型LED设备,其设备实例的实际类型为struct awbl_led_gpio_dev(具体定义详见程序清单13.27),在使用p_dev时,可以将其强制转换为指向实际设备实例的指针,以访问设备实例中的各个成员。范例程序详见程序清单13.31。

程序清单13.31 将p_dev转换为指向实际设备实例的指针

致远电子

在初始化时,往往还需要获得用户为设备提供的相关信息,AWBus-lite提供了通过p_dev获取硬件设备描述的宏:AWBL_DEVHCF_GET(),其返回值即为const struct awbl_devhcf *类型的指向硬件设备描述的指针,struct awbl_devhcf类型的定义详见程序清单12.2,其中包含了设备名、设备单元号、所处总线、设备信息等常见的信息,使用范例详见程序清单13.32。

程序清单13.32 AWBL_DEVHCF_GET()宏的使用范例程序

致远电子

程序中,只要获得了设备描述,即可通过指针获得设备描述中的其它成员信息,但通常情况下,可能只需要获得设备描述中某一个成员的信息,此时,为了简化获取步骤,AWBus-lite提供了直接获取设备描述中某一成员的辅助宏,详见表13.5。

表13.5 获取设备相关信息的辅助宏(awbus_lite.h)

致远电子

特别地,在设备描述中,设备实例信息的类型为const void *,而实际上,p_devinfo指向的是具体设备信息,对于GPIO控制型LED设备,其设备信息的实际类型为

struct awbl_led_gpio_param(具体定义详见程序清单13.29),因此,在使用设备实例信息时,可以将其强制转换为指向实际设备信息的指针,以便访问实际设备信息中的成员。例如:

致远电子

1.  第一阶段初始化函数实现

第一阶段通常无需作任何操作,该阶段对应的始化函数往往为空,详见程序清单13.33。

程序清单13.33 第一阶段初始化函数实现范例

致远电子

2.  第二阶段初始化函数实现

第二阶段为初始化设备的主要阶段,可以在该阶段中完成GPIO模式设置、初始电平设置等初始化相关操作,范例程序详见程序清单13.34。

程序清单13.34 第二阶段初始化函数实现范例

致远电子

程序中,将所有LED对应的引脚设置为了输出模式,并将初始电平设置为了设备信息中active_low的值,以使LED初始处于熄灭状态。例如,active_low的值为1,则表示引脚输出低电平时点亮LED,而初始时,将输出电平设置为了

active_low的值,即高电平,从而熄灭了LED。为了更清楚的理解这个关系,可以列举出active_low的值和GPIO输出电平对LED状态的影响,详见表13.6。由此可见,当active_low的值和GPIO输出电平相同时,LED熄灭,否则,LED点亮。因此,初始时,将GPIO输出电平设置为active_low的值,确保了LED初始处于熄灭状态。

表13.6 LED状态的影响因素

致远电子

3.  第三阶段初始化函数实现

第三阶段通常用于耗时较长的初始化操作,由于LED设备初始化中,并没有任何比较复杂、耗时的操作,因此,第三阶段无需作任务处理,设定为空即可,详见程序清单13.35。

程序清单13.35 第三阶段初始化函数实现范例

致远电子

实现了各阶段初始化函数后,可以定义一个struct awbl_drvfuncs类型的常量,以将所有初始化函数整合在一起,作为基础驱动信息中驱动入口点的值。详见程序清单13.36。

程序清单13.36  定义struct awbl_drvfuncs类型的常量

致远电子

其中,__g_awbl_drvfuncs_led_gpio即可作为驱动入口点p_busfuncs的值。

为了简化程序,可以在定义

struct awbl_drvfuncs类型的常量时,将空函数对应的指针直接设定为NULL。例如,在程序清单13.36中,由于第一阶段和第三阶段对应的初始化函数是空函数,没有作任何操作,因此,可以将pfunc_dev_init1和pfunc_dev_connect的值设置为NULL,更新后的struct awbl_drvfuncs类型常量定义详见程序清单13.37。

程序清单13.37 更新struct awbl_drvfuncs类型的常量定义

致远电子

13.3.6  实现LED服务

LED设备的主要功能是为系统提供LED服务,在向系统提供LED服务前,需要实现一个LED服务,回顾LED服务类型的定义,详见程序清单13.38。

程序清单13.38 LED服务类型定义(awbl_led.h)

致远电子

在LED设备实例中,具有一个

struct awbl_led_service 类型的led_serv成员(详见程序清单13.27)。实现LED服务的核心工作就是完成led_serv中各成员的赋值。

1.  p_next成员赋值

在LED服务中,p_next用于系统组织多个LED服务,使它们以链表的形式串接起来,便于统一管理。对于单个LED设备来讲,其仅能提供一个LED服务,p_next的值设置应设置为NULL。设置范例详见程序清单13.39。

程序清单13.39 LED服务中p_next成员赋值范例

致远电子

2.  p_servinfo成员赋值

在LED服务中,p_servinfo用于指向LED服务信息,通过LED设备信息类型的定义可知,LED服务信息由用户提供,因此,只需将p_servinfo指向设备信息中的LED服务信息,设置范例详见程序清单13.40。

程序清单13.40 LED服务中p_servinfo成员赋值范例

致远电子

3.  p_servfuncs成员赋值

为了屏蔽底层硬件的差异性,系统为LED设备定义了两个抽象方法,回顾

struct awbl_led_servfuncs类型的定义,详见程序清单13.41。

程序清单13.41 struct awbl_led_servfuncs类型的定义(awbl_led.h)

致远电子

在LED服务中,p_servfuncs即为指向各抽象方法的具体实现列表。为了完成p_servfuncs成员的赋值,首先需要实现操作LED设备的的两个抽象方法,然后将它们整合到一个

struct awbl_led_servfuncs类型的结构体常量中,抽象方法的实现详见程序清单13.42。

程序清单13.42 LED抽象方法的实现

致远电子

致远电子

在各个抽象方法的实现中,都将参数p_cookie直接视为了指向设备的指针,为了便于使用,将p_cookie的类型从void *强制转换为了struct awbl_led_gpio_dev *。实际中,p_cookie的值是由驱动自身决定的,在下一小节p_cookie成员的赋值中将作进一步介绍。

各函数完成的主要功能是根据需要控制LED对应引脚的输出电平,主要分为两个步骤:根据LED的ID得到引脚索引;通过引脚索引,控制相应引脚的输出电平。

LED对应的引脚在设备信息的引脚数组中,数组的起始索引为0,但LED的ID是从LED服务信息中指定的起始编号开始的,为了通过LED的ID获得其对应引脚在数组中的索引,应使用ID号减去起始编号,即:

致远电子

在__led_gpio_set()函数的实现中,其需要根据参数on的值决定是否点亮LED,点亮LED的电平与设备信息中的active_low有关, 为了更清楚的理解这个关系,可以列举出参数on和active_low的值对GPIO输出电平的影响,详见表13.7。例如,当active_low为0时,表示GPIO输出高电平时点亮LED,此时,若on为1,表示需要点亮LED,则GPIO应输出高电平;若on为0,表示需要熄灭LED,则GPIO应输出低电平。

表13.7 GPIO输出与active_low和on值的关系

致远电子

由此可见,当active_low和on相同时,GPIO应该输出“0”,而当active_low和on值不同时,GPIO应输出“1”。即:“相同为0,相异为1”,这恰好是一种异或关系,因此,在设置GPIO输出电平时,直接将active_low和on的异或值作为GPIO的输出电平,即:

致远电子

在__led_gpio_toggle()函数的实现中,仅需翻转GPIO输出电平接口,与设备信息中active_low的值无关,即:

致远电子

至此,实现了LED服务中的两个抽象方法,并存放在了__g_led_servfuncs常量中,该常量的地址即可直接作为LED服务中p_servfuncs的值,详见程序清单13.43。

程序清单13.43 p_servfuncs成员的赋值

致远电子

4.  p_cookie成员赋值

在LED服务中,p_cookie用于系统在调用设备实现的抽象方法时,“原封不动”的传递给各个抽象方法的p_cookie参数。这样一来,传入抽象方法中的p_cookie与LED服务中的p_cookie是完全相同的,换句话说,驱动为LED服务中的p_cookie设置了什么值,那么,在系统通过LED服务调用驱动实现的抽象方法时,传入p_cookie参数的值也为该值。

通常情况下,p_cookie都起到一个p_this的作用,用于指向设备自身,为此,直接将LED服务中p_cookie的设置为p_this,详见程序清单13.44。

程序清单13.44 p_cookie成员的赋值

致远电子

也正因为如此,在程序清单13.42所示的LED抽象方法的实现中,可以直接将p_cookie强制转换为指向设备自身的指针。

至此,清楚了LED服务中各成员应该设置的具体值,可以选择在初始化函数中完成各成员的赋值,比如将相关的赋值语句添加到第二阶段初始化函数中。也可以选择在系统获取LED服务时,再进行相关成员的赋值。显然,在系统获取LED服务时再进行相关成员的赋值,这种方式更优,因为若系统不获取LED服务,那么就不会进行相关成员的赋值,避免了不必要的操作,下面将对这种方法作进一步介绍。

13.3.7  定义Method对象

通过前面对Method机制的介绍可知,为了使LED设备可以向系统提供LED服务,需要定义Method对象,一个Method对象由Method类型标识和一个入口函数构成。已知获取LED服务的Method类型为:awbl_ledserv_get。因此,定义Method对象的关键在于实现一个用于系统获取LED服务的入口函数,范例程序详见程序清单13.45。

程序清单13.45 获取LED服务的入口函数实现范例

致远电子

基于此,可以完成一个Method对象的定义,即:

致远电子

为了便于管理,一个驱动提供的所有Method对象应该存放在一个列表中,由于LED设备仅能提供LED服务,因此,Method对象列表中仅包含一个用于获取LED服务的Method对象,详见程序清单13.46。

程序清单13.46 LED设备驱动Method对象列表定义

致远电子

其中,__g_led_gpio_dev_methods即可作为基础驱动信息中p_methods的值。

13.3.8  注册驱动

通过前面的介绍,LED设备驱动相关的函数均已实现,驱动已经基本开发完成,基于此,可以按照AWbus-lite的定义,使用相应驱动信息结构体类型完成一个驱动信息的定义,用于完整的描述LED驱动。

由GPIO直接控制的LED设备挂在PLB总线上,PLB总线上的所有设备驱动对应的信息结构体类型为awbl_plb_drvinfo_t,其是直接从基础驱动信息类型派生而来的,其定义详见程序清单13.47。

程序清单13.47 awbl_plb_drvinfo_t类型定义(awbl_plb.h)

致远电子

由此可见,其并未扩展任何其它新的成员,和基础驱动信息是完全一样的,可以定义用于描述LED驱动的信息常量,详见程序清单13.48。

程序清单13.48 定义描述LED驱动的信息常量

致远电子

完成描述驱动的信息常量定义后,还需要将驱动注册到系统中,以便被系统中相应的设备所使用,AWBus-lite提供了驱动注册函数,用于向系统中注册一个驱动,其函数原型为:

致远电子

其中,p_drvinfo指向待注册的驱动信息,返回值为标准的错误号,若返回AW_OK,则表示驱动注册成功;若返回-AW_ENOSPC,则表示内存空间不足,驱动注册失败;若返回-AW_ENOTSUP,则表示AWbus-lite不支持该驱动的版本,往往是由于驱动信息中的awb_ver版本号设置有误引起的。例如,注册LED驱动的范例程序详见程序清单13.49。

程序清单13.49 注册LED驱动范例程序

致远电子

由于__g_drvinfo_led_gpio的类型是从基础驱动信息类型派生而来的,两者类型并不完全相同,为了避免警告,可以将__g_drvinfo_led_gpio的地址转换为struct awbl_drvinfo *类型。

显然,是否注册驱动应该是由用户决定的,只有当需要使用某一设备时,才应将相应的驱动注册到系统中。为了使用户可以在需要使用LED驱动时再注册驱动,比较容易想到的方法可能是将__g_drvinfo_led_gpio作为一个对外开放的全局变量,引出到驱动文件外部,当用户需要使用LED驱动时,再使用程序清单13.49所示的程序进行注册。但是,__g_drvinfo_led_gpio作为一个结构体常量,可以看作驱动的一个数据,在面向对象的编程中,作为一种良好的编程习惯,应该尽可能避免将数据直接引出到对象外部供其它模块使用,对数据的操作都应该通过相关的接口实现。将过多的数据作为全局变量引出到文件外部,将严重破坏系统的可维护性。同时,对于用户来说,其并不需要访问驱动信息中的相关成员,将整个驱动信息开放给用户也是不必要的。

由于驱动信息仅用于在注册驱动时使用,为此,可以提供一个用于注册LED驱动的专用函数,其实现详见程序清单13.50。

程序清单13.50 注册LED驱动的专用函数

致远电子

如此一来,驱动信息仅在内部被访问,做到了很好的“封装”。当用户需要使用LED驱动时,直接调用awbl_led_gpio_drv_register()函数即可。

通常情况下,并不需要由用户手动调用该函数,而是将该函数的调用放在模板工程下的

aw_prj_config.c文件中,具体位于

awbl_group_init()函数中,该函数在系统启动时会被自动调用,从而使系统在启动时自动调用awbl_led_gpio_drv_register()函数完成驱动的注册,详见程序清单13.51。

程序清单13.51 系统启动时自动注册驱动的原理

致远电子

由此可见,用户可以通过是否定义

AW_DRV_AWBL_GPIO_LED宏来确定是否注册LED驱动,在aw_prj_params.h工程配置文件中,只要使能了LED设备,即定义了AW_DEV_GPIO_LED宏,则表示要使用GPIO驱动型LED,此时,将自动完成

AW_DRV_AWBL_GPIO_LED的定义,以此确保当使用LED时,相应驱动会在系统启动过程中被自动注册。核心的原理性程序详见程序清单13.52。

程序清单13.52 自动定义AW_DRV_AWBL_GPIO_LED宏的原理(aw_prj_params.h)

致远电子

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分