电子说
本文导读
在使用AWBus-lite对设备进行管理时,无论设备处于 AWBus-lite拓扑结构中的哪个位置,只要其能够提供某种标准服务,就可以使用相应的通用接口对其进行操作。本文将从接口的定义和实现两个方面,深入理解AWbus-lite工作的原理。
本文为《面向AWorks框架和接口的编程(上)》第三部分软件篇——第13章——第1~2小节:通用接口的定义和接口的实现。本章导读
在基于AWBus-lite总线拓扑结构的设备管理框架中,无论一个设备处于AWBus-lite总线拓扑结构中的哪个位置,只要其能够提供某种标准服务,就可以使用相应的通用接口对其进行访问。那么,这究竟是怎样实现的呢?本章将继续深入探讨AWBus-lite,为您揭开AWBus-lite的神秘面纱,使您对AWBus-lite有更加深入的了解,在具有这些足够的了解后,你将有能力独立开发一些设备的驱动,当后续遇到一些AWorks暂不支持的设备时,可以自行开发设备相应的驱动。
13.1 通用接口的定义
合理的接口应该是简洁的、易阅读的、职责明确的,为了便于维护,通用接口由广州致远电子有限公司进行统一的定义,用户通常不需要自行定义通用接口。目前,常用的功能都已经被标准化,定义了相应的通用接口。
作为一种了解,下面以LED为例,从接口的命名、参数和返回值三个方面阐述在AWorks中定义接口的一般方法。
13.1.1 接口命名
在AWorks中,所有通用接口均以“aw_”开头,紧接着是操作对象的名字,对于LED控制接口来说,所有接口都应该以“aw_led_”作为前缀。
在接口的前缀定义好之后,应该考虑需要定义哪些功能性接口,然后根据功能完善接口名。对于LED来说,核心的操作是控制LED的状态,点亮或熄灭LED,为此,可以定义一个设置(set)LED状态的函数,比如:“aw_led_set”。
使用该接口可以设置LED的状态,显然,为了区分是点亮还是熄灭LED,需要通过一个额外的参数来指定具体的操作。
每次开灯或关灯都需要传递额外的参数给aw_led_set()接口,显得比较繁琐。为了简化操作,可以为常用的开灯和关灯操作定义专用的接口,这样就不需要额外的参数来对开灯和关灯操作进行具体的区分了。比如,使用on和off分别表示开灯和关灯,则可以定义开灯的接口名为:“aw_led_on”,关灯的接口名为“aw_led_off”。
在一些特殊的应用场合中,比如,LED闪烁,用户可能并不关心具体的操作是开灯还是关灯,它仅仅需要LED的状态发生翻转。此时,可以定义一个用于翻转(toggle)LED状态的专用接口,比如:“aw_led_toggle”
13.1.2 接口参数
在AWorks中,通用接口的第一个参数往往用于表示要操作的具体对象。显然,在一个系统中,可能存在多个LED,为了区分各个LED,可以为每个LED分配一个唯一编号,即ID号。ID号是一个从0开始的整数,例如,系统中有两个LED,则编号分别为0、1。基于此,为了指定要操作的具体LED,通用接口的第一个参数可以设定为int类型的id。
对于aw_led_set接口,其除了使用id确定需要控制的LED外,还需要使用一个参数来区分是点亮LED还是熄灭LED,这是一个二选一的操作,对应参数的类型可以使用布尔类型: aw_bool_t。当值为真(AW_TRUE)时,则点亮LED;当值为假(AW_FALSE)时,则熄灭LED。基于此,可以定义aw_led_set()接口的原型为(还未定义返回值):
对于aw_led_on、aw_led_off和aw_led_toggle接口来说,它们的职责单一,仅仅需要指定控制的LED,即可完成点亮、熄灭或翻转操作,无需其它额外的参数。对于这类接口,参数仅仅需要id,这些接口的原型可以定义如下(还未定义返回值):
13.1.3 返回值
对于用户来说,调用通用接口后,应该可以获取到本次执行的结果,是执行成功还是执行失败,或是一些其它的有用信息。比如,在调用接口时,如果指定的id超过了有效范围,由于没有与无效id对应的LED设备,操作必定会失败,此时必须返回通过返回值告知用户操作失败,且操作失败的原因是id不在有效范围内,无与之对应的LED设备。
在AWorks中,接口通常返回一个aw_err_t类型的返回值来表示接口执行的结果,返回值的含义已被标准化:若返回值为AW_OK,则表示操作成功;若返回值为负数,则表示操作失败,此时,用户可根据具体的返回值,查找aw_errno.h文件中定义的宏,根据宏的含义确定失败的原因;若返回值为正数,其含义与具体接口相关,由具体接口定义,无特殊说明则表示不会返回正数。AW_OK是在aw_common.h文件中定义的宏,其定义如下:
错误号在aw_errno.h文件中定义,几个常见错误号的定义详见表6.2。比如,在调用LED通用接口时,若id不在有效范围内,则该id没有对应的LED设备,此时接口应该返回-AW_ENODEV。注意:AW_ENODEV的前面有一个负号,以表示负值。
基于此,将所有LED通用接口的返回值类型定义为aw_err_t,LED控制接口的完整定义详见表13.1,其对应的类图详见图13.1。
表13.1 LED通用接口(aw_led.h)
图13.1 LED接口类图
这些接口都已经在aw_led.h文件中完成了定义,无需用户再自行定义。上述从接口命名、参数和返回值三个方面详细阐述了一套接口定义的方法,旨在让用户了解接口的由来,加深对接口的理解。实际中,定义接口并不是一件容易的事情,需要尽可能考虑到所有的情况,接口作为与用户交互的途径,一旦定义完成,如非必要,都应该避免再对接口进行修改。否则,所有依赖于该接口的应用程序都将受到影响。因此,当前并不建议用户自定义通用接口,通用接口的定义应由广州致远电子有限公司统一规划、定义、维护和管理。
13.2 接口的实现
作为一种范例,下面以实现LED接口为例,详细介绍AWorks中实现接口的一般方法。
13.2.1 实现接口初探
在AWorks中,硬件设备和驱动统一由AWBus-lite进行管理,因此,对于LED这一类硬件设备,其实现是属于AWBus-lite的一部分。LED有4个通用接口函数,其中的aw_led_on()和aw_led_off()接口可以直接基于aw_led_set()接口实现,详见程序清单13.1。
程序清单13.1 aw_led_on()和aw_led_off()接口的实现
实现接口的核心是实现aw_led_set()和aw_led_toggle()这两个接口。对于不同的底层硬件设备,LED实际控制方式是不同的,比如,最常见的,通过GPIO直接控制一个LED,简单范例详见程序清单13.2。
程序清单13.2 aw_led_set()的实现(GPIO控制LED)
也有可能是通过串口控制一个LED设备。例如,通过发送字符串命令控制LED的状态,命令格式为:set
程序清单13.3 aw_led_set()的实现(UART控制LED)
总之,底层硬件设备是多种多样的,不同类型的LED设备对应的控制方式也会不同。
定义通用接口的目的在于屏蔽底层硬件的差异性,即无论底层硬件如何变化,用户都可以使用通用接口控制LED。显然,如果直接类似程序清单13.2和程序清单13.3这样实现一个通用接口,那么随着LED设备种类的增加,同一个接口的实现代码将有越来越多不同的版本。
在一个应用程序中,一个接口不能同时具有多种不同的实现,因此,这样的做法有着非常明显的缺点:多个不同种类的LED设备不能在一个应用中共存,更换硬件设备,就必须更换通用接口的实现,使用何种设备就加入相应设备的控制代码进行编译。 例如,系统中有几个直接通过GPIO控制的LED,同时也存在几个通过UART控制的LED,那么,类似程序清单13.2和程序清单13.3这样直接实现通用接口的方法,就无法组织代码了,因为不可能同时将程序清单13.2和程序清单13.3所示的代码加入工程编译。显然,需要更好的办法来解决这个问题。
13.2.2 LED抽象方法
在使用几种控制方式不同的LED硬件设备时,虽然它们对应的aw_led_set()和aw_led_toggle()接口的具体实现方法并不相同,但它们要实现的功能却是一样的,这是它们的共性:均要实现设置LED状态和翻转LED状态的功能。由于一个接口的实现代码只能有一份,因此,这些功能的实现不能直接作为通用接口的实现代码。为此,可以在通用接口和具体实现之间增加一个抽象层,以对共性进行抽象,将两种功能的实现抽象为如下两个方法:
相对于通用接口来说,抽象方法多了一个p_cookie参数。在面向对象的编程语言中(如C++),对象中的方法都能通过隐式指针p_this访问对象自身,引用自身的一些私有数据。而在C语言中则需要显式的声明,这里的p_cookie就有类似的作用,当前设置为void *类型主要是由于具体对象的类型还并不确定。
为了节省内存空间,同时方便管理,可以将所有抽象方法放在一个结构体中,形成一张虚函数表,比如:
由于LED的实现属于AWBus-lite的一部分,因此,命名使用"awbl_"作为前缀。这里定义了一个虚函数表,包含了两个方法,分别用于设置LED的状态和翻转LED。针对不同的硬件设备,都可以根据自身特性实现这两个方法。GPIO控制LED的伪代码详见程序清单13.4,UART控制LED的伪代码详见程序清单13.5。
程序清单13.4 抽象方法的实现(GPIO控制LED)
程序清单13.5 抽象方法的实现(UART控制LED)
显然,__g_led_gpio_drv_funcs和__g_led_uart_drv_funcs分别是使用GPIO和UART控制LED的具体实现,它们在形式上是两个不同的结构体常量,在同一系统中是可以共存的。
13.2.3 抽象的LED服务
当对不同的LED硬件设备抽象了相同的pfn_led_set和pfn_led_toggle方法后,在通用接口aw_led_set()和aw_led_toggle()的实现中,就无需再处理与硬件相关的具体事务,只需要找到相应设备提供的相应方法,然后调用这些具体硬件设备提供的方法即可。
在调用设备提供的方法时,由于方法的第一个参数为p_cookie,p_cookie代表了具体的设备对象,调用方法时,需要传递一个p_cookie给下层,用于在具体实现时,访问设备对象中的具体成员,起到“p_this”的作用。由于在aw_led_set()接口的实现中,并不需要直接操作具体设备,因此,无需知道p_cookie的具体类型,仅需起到一个传递的作用:在调用相应的方法时,传递给下层驱动使用。
既然要传递p_cookie,那么,必然要获得一个p_cookie并保存下来,显然,p_cookie代表了具体设备,只能由具体设备提供,同时,抽象方法也是由具体设备提供的,为此,可以将抽象方法和p_cookie定义在一起,形成一个新的类型,以便在具体设备提供的抽象方法的实现时,将p_cookie一并提供,即:
为了便于描述,将其称之为 “LED服务”。其中包含了由驱动实现的抽象方法和传递给驱动函数的p_cookie。其中,p_servfuncs为指向驱动虚函数表的指针,比如,指向__g_led_gpio_drv_funcs或__g_led_uart_drv_funcs,p_cookie为指向具体设备的指针,即传递给驱动函数的第一个参数。此时,在aw_led_set()接口的实现中,无需再完成底层硬件相关的操作,仅需调用LED服务中包含的pfn_led_set方法即可,其范例程序详见程序清单13.6。
程序清单13.6 aw_led_set()实现(1)
程序中,使用了一个全局变量__gp_led_serv指向当前一个有效的LED服务,显然,其只有在赋值后才能使用,暂不考虑其如何赋值,仅用以展示pfn_led_set方法的调用形式。
实际中,具体的LED设备往往不止一个。比如,使用GPIO控制的LED设备和使用UART控制的LED设备,它们都可以向系统提供LED服务,这就需要系统具有管理多个LED服务的能力。由于LED设备的数目无法确定,可能动态变化,因此,选用单向链表进行动态管理。为此,在struct awbl_led_service中增加一个p_next成员,用于指向下一个LED服务。即:
此时,系统中的多个LED服务使用链表的形式进行管理。那么,在通用接口的实现中,如何确定具体该使用哪个LED服务呢?在定义通用接口时,使用了ID号区分不同的LED,ID号是唯一的,显然,某一ID的LED必然属于某一确定的硬件设备,若一个硬件设备在提供LED服务时,包含了该硬件设备中所有LED的ID号信息,那么,在通用接口的实现中,就可以根据ID找到对应的LED服务,然后使用驱动中提供的方法完成LED的操作。为此,可以在LED服务中再增加一个ID信息,以表示对应硬件设备中所有LED的ID号。
在一个LED硬件设备中,可能包含多个LED,例如,MiniPort-LED扩展板,包含了8个LED,但一般来讲,一个LED硬件设备中所有LED的编号是连续分配的,基于此,仅需知道一个硬件设备中LED的起始编号和结束编号,就可以获得该硬件设备中所有LED对应的编号,例如,一个硬件设备中LED的起始编号为2、结束编号为9,则表示在该硬件设备中,各个LED的编号分别为:2、3、4、5、6、7、8、9,共计8个LED。为此,可以定义一个LED服务信息结构体类型,专门用于提供LED服务相关的信息,例如:
由此可见,在LED服务信息中,当前仅包含起始编号和结束编号两个信息。LED服务信息是与具体设备相关的,因此,可以在LED服务中新增一个指向LED服务信息的指针,以便在具体设备在提供p_cookie和抽象方法的实现时,将设备支持的LED编号信息一并提供,LED服务的完整定义详见程序清单13.7。
程序清单13.7 完整的LED服务类型定义(awbl_led.h)
由于在LED服务中新增了LED编号信息,那么,在通用接口的实现中,就可以根据设备提供的LED编号信息,找到id对应的LED服务。基于此,aw_led_set()函数的实现详见程序清单13.8。
程序清单13.8 aw_led_set()实现(2)
程序中,调用了__led_id_to_serv()函数以获取ID号对应的LED服务。__led_id_to_serv()函数通过遍历LED服务链表,将id与各个LED服务中的ID信息进行对比,直到找到id对应的LED服务(即id处于该LED服务的起始编号和结束编号之间),具体实现范例详见程序清单13.9。
程序清单13.9 获取ID号对应的LED服务
其中,__gp_led_serv_head是一个模块内部使用的全局变量,初始值为NULL,表示初始时系统中无任何有效的LED服务。同理,可实现aw_led_toggle()接口,详见程序清单13.10。
程序清单13.10 aw_led_toggle()实现
至此,实现了所有通用接口。显然,由于当前并不存在任何有效的LED服务,因此,__gp_led_serv_head的值为NULL,致使__led_id_to_serv()的返回值为NULL,最终导致通用接口的返回值始终为-AM_ENODEV。
13.2.4 Method机制
为了使通用接口能够操作到具体有效的LED,必须通过某种方法,将当前系统中所有LED设备提供的LED服务加入到以__gp_led_serv_head为头的LED服务链表中。
AWbus-lite提供了一种特殊的“Method机制”,其是一种系统上层和硬件底层相互“交流”的一种方式,可用于系统发现各个硬件设备提供的服务。AWBus-lite提供了Method相关的宏,详见表13.2。
表13.2 Method相关的宏(awbus_lite.h)
1. 定义一个Method类型
AWBL_METHOD_DEF()用于定义一个Method类型,其原型为:
其中,method为定义的Method类型名,string为一个描述字符串,描述字符串仅在定义时对method类型进行描述,可以是任意字符串,其它地方不会被使用到。
Method类型具有唯一性,不可定义两个相同的Method类型。Method类型可以看作一个“唯一标识”,用于标记某一类操作,在底层驱动的实现中,凡是能够完成该类操作的设备,都可以使用该“唯一标识”标记一个入口函数,该入口函数即用于完成相应的操作,这样一来,系统就可以通过该“唯一标识”查找所有被标记的入口函数,并依次调用它们,完成某种特定功能。例如,对于LED,为了获取当前系统中所有设备提供的LED服务,可以定义一个获取LED服务的Method类型,范例程序详见程序清单13.11。
程序清单13.11 定义Method类型范例程序
如此一来,即定义了一个名为awbl_ledserv_get的Method类型,该Method类型即表示了一类操作:获取LED服务。后续在底层设备驱动的实现中,只要一个设备能够提供LED服务,则表示系统可以从中获取到一个LED服务,此时,该驱动就可以提供一个入口函数,用于获取LED服务,并使用该Method类型对入口函数进行标记。这样,在系统启动时,就可以通过查找系统中所有被该Method类型标记的入口函数,然后一一调用它们,以此获得所有设备提供的LED服务。
2. 定义一个具体的Method对象
定义Method对象的本质就是使用Method类型标记一个入口函数,使系统知道该入口函数可以完成某种特定的功能,以便系统在合适的时机调用。为便于描述,在AWBus-lite中,将使用Method类型标记的入口函数称之为一个Method对象。AWBus-lite提供了定义Method对象的辅助宏,其原型为:
其中,name为使用AWBL_METHOD_DEF()定义的Method类型名,handler为用于完成某种特定功能的入口函数。例如,定义awbl_ledserv_get类型的目的是获取LED服务,因此,该类型的Method对象对应的handler应该能够获取到一个LED服务。handler的类型为awbl_method_handler_t,其定义如下(awbus_lite.h):
由此可见,awbl_method_handler_t是一个函数指针类型,其指向的函数有两个形参:p_dev和p_arg,返回值为标准的错误号。p_dev指向设备自身,同样起到一个p_this的作用,该handler运行在哪个设备上,传入p_dev的值就应该为指向该设备的指针;p_arg为void *类型的参数,其具体类型与该入口函数需要完成的功能相关,如果功能为获取一个LED服务,p_arg的实际类型就为struct awbl_led_service **,即一个指向LED服务的指针的地址,以便在函数内部改变指向LED服务的指针的值,使其指向设备提供的LED服务,从而完成LED服务的获取。例如,在GPIO控制LED的设备中,其能够提供LED服务,则应在对应的驱动中,定义一个具体的Method对象,便于系统得到LED服务。范例详见程序清单13.12。
程序清单13.12 定义Method对象范例程序
其中,__g_led_gpio_dev_methods为该设备能够提供的Method对象列表,一个设备提供的Method对象统一存放在一个列表中。该列表的具体使用以及入口函数的具体实现,将在LED驱动的实现中进一步介绍。这里仅用于展示系统上层获取LED服务的原理。
3. Method对象列表结束标记
在程序清单13.12中,将Method对象定义在了一个列表中,AWBL_METHOD_END用于定义一个特殊的标志,表示Method对象列表的结束。其原型为:
该宏无需传入任何参数,其仅用于标识一个Method对象列表的结束。例如,GPIO控制的LED设备只能提供一个Method对象,用于系统上层获取LED服务,那么,在该对象之后,应该使用AWBL_METHOD_END表示列表结束。范例程序详见程序清单13.13。
程序清单13.13 AWBL_METHOD_END使用范例程序
4. 导入(声明)一个在外部定义的Method类型
当需要使用一个已定义的Method类型时,若Method类型是在其它文件中定义的,此时就需要在使用前对该Method类型进行声明,类似于C语言中的extern。其原型为:
其中,name为使用AWBL_METHOD_DEF()定义的Method类型名。例如,在程序清单13.13中,定义了Method对象列表,其中使用到了Method类型:awbl_ledserv_get。在使用前,需要对该Method类型进行声明,范例程序详见程序清单13.14。
程序清单13.14 AWBL_METHOD_IMPORT()使用范例程序
5. 得到一个已定义的Method类型的ID
Method类型相当于一个“唯一标识”,有时候,需要判断某一Method对象是否为指定的Method类型。为了便于比较判断,或将“Method类型”作为参数传递给其它接口函数,可以通过该宏获得一个Method类型对应的ID,ID作为一个常量,可以用于比较或参数传递。获取Method类型的ID对应的宏原型为:
其中,method为使用AWBL_METHOD_DEF()定义的Method类型名,返回值为该Method类型的ID,其类型为:awbl_method_id_t,该类型的具体定义用户无需关心,ID可以用作比较,只要两个Method类型的ID相同,就表示两个Method类型是相同的,ID也可以作为参数传递给其它接口,以指定一种Method类型。
例如,在AWbus-lite中,提供了一个工具函数,用于查找设备中是否存在指定类型的Method对象,其函数原型为(awbus_lite.h):
其中,p_dev指定了要查找的设备,即在该设备中查找是否存在指定类型的Method对象,method用于指定Method类型的ID。若查找到该ID对应的method对象,则返回该method对象的入口函数,否则,返回NULL。
例如,有一个p_dev指向的设备,要查找其是否能够提供LED服务,则可以通过该接口实现,范例程序详见程序清单13.15。
程序清单13.15 AWBL_METHOD_ CALL()使用范例程序
程序中,使用AWBL_METHOD_ CALL()得到Method类型awbl_ledserv_get的ID,然后传递给awbl_dev_method_get()函数以查找设备中是否存在相应的Method对象,若存在,则得到了一个有效的入口函数,最后使用该入口函数即可得到一个LED服务。
13.2.5 LED服务链表的初始化
通过程序清单13.15所示的一个流程,若一个设备能够提供LED服务,则可以从中获取到相应的LED服务。若对每个设备都执行一遍上述流程,并在获得一个设备提供的LED服务后,就将其添加到以__gp_led_serv_head为头的LED服务链表中,则可以收集到系统中所有的LED服务,完成LED服务链表的初始构建。为了便于对所有设备执行某一操作,AWBus-lite提供了一个用于遍历所有设备的接口,其函数原型为:
其中,pfunc_action表示要在每个设备上执行的操作,p_arg为传递给pfunc_action的附加参数,flags为遍历设备的标志,返回值为标准错误号。
pfunc_action的类型为awbl_iterate_func_t,该类型的具体定义如下(awbus_lite.h):
由此可见,awbl_iterate_func_t是一个函数指针类型,其指向的函数有两个形参:p_dev和p_arg,返回值为标准的错误号。p_dev指向当前遍历到的设备,同样起到一个p_this的作用,当前遍历到哪个设备,在哪个设备上运行pfunc_action函数,传入的p_dev就为指向该设备的指针;p_arg为遍历时提供的附加参数,其值与调用awbl_dev_iterate()函数时传入的p_arg参数相同。
flags为遍历设备的标志,其决定了需要遍历哪些设备,以及pfunc_action函数的返回值是否能够终止遍历过程。可用标志详见表13.3。
表13.3 遍历设备标志宏(awbus_lite.h)
其中,AWBL_ITERATE_INSTANCES和AWBL_ITERATE_ORPHANS标志用于指定需要遍历的设备,即在哪些设备上执行pfunc_action函数。在硬件设备列表中,定义了当前系统中所有的硬件设备,当设备具有与之匹配的驱动时,才能够变成一个实例,被系统正常使用,而如果一个设备没有与之匹配的驱动,该设备将变成一个孤儿设备,由于没有与之匹配的驱动,因此系统暂时无法使用孤儿设备。AWBL_ITERATE_INSTANCES表示表示需要遍历所有实例,即具有相应驱动的设备。AWBL_ITERATE_ORPHANS表示需要遍历所有的孤儿设备。若要遍历所有的实例和孤儿设备,可以使用或运算同时设定这两个标志。
AWBL_ITERATE_INTRABLE标志用于指定pfunc_action 的返回值是否可以终止遍历,若设定了该标志,则在某一设备上运行pfunc_action函数的返回值将可以决定是否终止整个遍历过程:返回值为AW_OK时,不终止遍历,继续遍历其它设备;否则,遍历过程被终止,不会再继续遍历其它设备。若未设定该标志,则遍历过程不受返回值影响,直到遍历完所有需要遍历的设备后结束。
为了获得所有设备提供的LED服务,需要遍历所有实例,并在pfunc_action中执行如程序清单13.15所示的流程,并将各个设备提供的LED服务添加到以__gp_led_serv_head为头的LED服务链表中。完整的范例程序详见程序清单13.16。
程序清单13.16 获取所有设备提供的LED服务
程序中,由于孤儿设备没有对应的驱动,无法正常使用,显然无法提供LED服务,因此仅需遍历所有实例,遍历标志设定为了:AWBL_ITERATE_INSTANCES。该程序的目的是完成LED服务链表的初始构建,是一种初始化操作,因此,将其入口函数命名为了awbl_led_init(),若需使用LED,则应确保在系统启动时,该函数被自动调用。通常情况下,并不需要由用户手动调用该函数,而是将该函数的调用放在模板工程下的aw_prj_config.c文件中,具体位于aw_prj_early_init()函数中,该函数在系统启动时会被自动调用,从而使系统在启动时自动调用awbl_led_init()函数,详见详见程序清单13.17。
程序清单13.17 awbl_led_init()在系统启动时被自动调用的原理
在aw_prj_params.h工程配置文件中,只要使能了某一LED设备,比如,AW_DEV_GPIO_LED,则表示要使用LED,此时,将自动完成AW_COM_AWBL_LED的定义,以此确保当使用LED时,awbl_led_init()会在系统启动过程中被自动调用。核心的原理性程序详见程序清单13.18。
程序清单13.18 自动定义AW_COM_AWBL_LED宏的原理(aw_prj_params.h)
至此,完成了LED服务链表的初始化,若系统中存在LED服务,则链表将不为空,在LED通用接口的实现中,只要传入的id正确,__led_id_to_serv()的返回值就不为NULL,进而返回一个有效的LED服务,接着,通过LED服务中实现的抽象方法,即可完成LED相关的操作,例如,设置LED状态、翻转LED状态等。
全部0条评论
快来发表一下你的评论吧 !