一种嵌入式系统中运行时构建USB复合设备的方法与流程

文档序号:20598884发布日期:2020-05-01 21:27阅读:213来源:国知局
本发明属于相关嵌入式系统
技术领域
:,具体涉及一种嵌入式系统中运行时构建usb复合设备的方法。
背景技术
::在嵌入式系统中,usb设备已经成为比较常见的外设。由于其协议的复杂性,支持的设备class类型比较多,所以usb设备驱动的开发也经常被看作是外设驱动接收处理步骤中比较复杂的一个。现在有很多mcu厂家的产品都支持usb外设,比较常见的是st(意法半导体)公司的stm32系列。这些mcu厂家为了方便用户的使用,一般会开发驱动支持库,这些库有时也会带有usb驱动;另外一些开源的rtos代码中,可能也会带有usb驱动栈,比如zephyr等。不管是以哪种形式提供的usb驱动,研究下来,其功能都相对单一,虽然也支持很多设备类,比如cdc类、massstorage类、hid类、audio类等,但在运行时仅支持一个设备,如果想实现复合设备,需要开发人员手动去修改设备描述符和配置描述符,以及其他描述符。这样的话,就无法再使用原来一个设备的情况,就等同于编译时决定设备是否是复合设备。即便这样,当需要在修改后的复合设备驱动接收处理步骤中,增加一个设备的时候,还要继续手动修改描述符,这个过程非常繁琐,而且容易出错,增加了开发和维护的难度,代码不具有复用性,而且耦合度高。技术实现要素:本发明的目的在于提供一种嵌入式系统中运行时构建usb复合设备的方法,以解决上述
背景技术
:下,不能很好的进行多个usb逻辑设备的扩展,而且方式较为复杂的问题。为实现上述目的,本发明提供如下技术方案:一种嵌入式系统中运行时构建usb复合设备的方法,所述在嵌入式系统中,usb设备驱动栈在运行时构建一个或多个设备的方法,具体包括类对象说明、设备控制块说明、设备接口号和端点号分配、usb设备配置或使用方法、创建类对象流程、usb类初始化流程、类对象控制请求处理流程、类请求如何传递到类对象处理、类对象的数据如何接收和类对象的数据如何发送几个步骤中。优选的,所述而关于类对象的概念,以及这个创建类对象的过程是本专利的重点。所谓类对象,是取自面向对象语言的概念,我们把cdc比喻成一个面向对象语言中的一个类,它可以被实例化成很多个对象。这样的话,比如cdc是一个实现虚拟串口类,一个cdc类对象则表示一个实际的虚拟串口。如果有多个cdc类对象,则表示最终usb设备在usbhost端看到的是多个虚拟串口,而且可以同时使用。a、类对象数据结构每个类对象对应一个usbd_class_object_t结构体,其定义如下:结构体成员说明如下:类对象方法b.1类对象初始化方法该方法在创建一个类对象后使用,用于初始化类对象所属类的类型,以及在该类中的对象编号,如下代码所示:函数参数说明:输入参数obj指向该对象自身的指针输入参数class_type该对象所属的类,比如cdc输入参数index该对象在所属类中的编号返回值错误码,0:成功-1:失败b.2类对象接口添加方法通过该方法将接口添加到类对象的interfacebitmap里,方便以后查找。函数参数说明:b.3类对象端点添加方法将一个端点(endpoint)以bitmap的方式保存到类对象里。函数参数说明:b.4类对象私有信息设置方法设置类对象的私有属性信息,比如cdc类对象中保存的可能是包含中断端点号、数据接收端点号、数据发送端点号等信息的结构体地址。函数参数说明:b.5类对象句柄设置方法暂存该类对象使用的句柄地址,方便以后使用。函数参数说明:b.6根据端点地址查找类索引的方法当有数据从非控制端点接收时,通过该方法可以查找到所属的类。函数参数说明:输入参数head类对象链表头指针输入参数ep_addr非控制端点地址返回值成功:类索引失败:-1b.7根据端点地址查找类对象索引的方法当有数据从非控制端点接收时,通过该方法可以查找到类对象。函数参数说明:b.8根据接口号查找类索引的方法当有来自控制端点类请求时,通过该方法可以查找到对应的类。函数参数说明:输入参数head类对象链表头指针输入参数itf_num接口号返回值成功:类索引失败:-1b.9根据接口号查找类对象索引的方法当有来自控制端点类请求时,通过该方法可以查找到类对象。函数参数说明:输入参数head类对象链表头指针输入参数itf_num接口号返回值成功:类对象索引失败:-1优选的,所述设备控制块说明步骤中,一个usb设备只有一个设备控制块,它对应的结构体定义为usbd_ctrl_block_t,如下所示:优选的,所述设备接口号和端点号分配步骤中分为端点地址转换、申请一个新的接口号、分配一个新的in端点地址和分配一个新的out端点地址四个部分。a.端点地址转换通过下面的in_ep_addr和out_ep_addr分别将端点号转化为in端点地址和out端点地址。#defineep_dir_in(1<<7)#defineep_dir_out(0<<7)#definein_ep_addr(epnum)(ep_dir_in|epnum)#defineout_ep_addr(epnum)(ep_dir_out|epnum)b.申请一个新的接口号该函数比较简单,接口号从0开始递增分配即可。c.分配一个新的in端点地址该函数比较简单,in端点号从1开始递增分配(控制端点0除外)。在实际实现时注意不要超过硬件支持的最大端点。d.分配一个新的out端点地址该函数比较简单,out端点号从1开始递增分配(控制端点0除外)。在实际实现时注意不要超过硬件支持的最大端点。优选的,所述usb设备配置或使用方法步骤中,经过简单的配置,usbstart就可以使该usb设备驱动栈按照既定的方式工作。而对应图1的步骤,详细讲解其配置过程:a.在使用usb时,必然要进行控制器硬件初始化,通过配置相关寄存器,将usb控制器设置在usb设备模式。另外,还要进行usb设备对象链表初始化,也就是设备控制块成员dev_list_head的初始化,它是类对象链表的链表头。b.暂时申请内存大小为一个标准配置描述符的大小(9bytes)的内存空间,并为配置描述符设置初始值,接口数暂时设置为0。c.在软件代码中设置一个flag,暂命名为dev_iscomposite,记录当前usb设备是否为复合设备,如果是复合设备,该值为真,否则为假。d.将cdc类驱动的控制块注册到系统中,将其地址(指针)保存到usb设备控制块g_usbd_ctrl_blk中的class指针数组中,数组下标为对应的类索引。e.应用层接口实现时可以是封装了几个函数指针的结构体,通常包括app_recved,app_send_ok等回调函数,当应用层调用底层cdc驱动接口发送一包数据后,等到底层数据发送完成,可以调用app_send_ok回调函数,指示数据被成功发送。f.添加一个实际的虚拟串口,编号为0。g.再添加一个实际的虚拟串口,编号为1。h.将massstorage类驱动的控制块注册到系统中,将其地址(指针)保存到usb设备控制块中的class指针数组中。i.该操作与e步骤相似,上层给底层提供一些回调函数,当某个事件触发时,回调相应的函数。j.添加一个实际的u盘设备。k.经过前面一系列的配置,最后执行usb设备启动,usb设备就正式工作了。usb正常启动后,usb主机就可以检测并枚举到usb设备了。并根据描述符的配置,在设备管理器里,会显示前面配置好的各个设备,不过如果是第一次安装,虚拟串口则需要安装相应的驱动。优选的,所述创建类对象流程步骤中一个类对象即代表一个实际的设备,并且解释对象详细的创建过程,以及如何被添加到系统中去的。如图2的步骤:a.步骤的具体实现,可以通过本地静态变量来获取,比如:staticusbd_class_object_tg_cdc_objects[cdc_object_num_max];usbd_class_object_t*p_cdc_object=g_cdc_objects+index;b.该步骤对于指针变量p_obj_info的赋值操作,可以通过动态内存的申请实现,比如:cdc_object_info_t*p_obj_info=(cdc_object_info_t*)malloc(sizeof(*p_obj_info));c.对于指针变量p_obj_handler的赋值操作,可以通过动态内存的申请实现,比如:usbd_cdc_handletypedef*p_obj_handler=(usbd_cdc_handletypedef*)malloc(sizeof(*p_obj_handler));d.该步骤申请接口号和端点地址的操作主要是依赖usbd_hs_getnewitfnumber、usbd_hs_getnewinepaddr和usbd_hs_getnewoutepaddr三个函数,它们的使用方式可以是下面这样:p_obj_info->com_itf_num=usbd_hs_getnewitfnumber();p_obj_info>data_itf_num=usbd_hs_getnewitfnumber();p_obj_info->com_ep_addr=usbd_hs_getnewinepaddr();p_obj_info->data_out_ep_addr=usbd_hs_getnewoutepaddr();p_obj_info->data_in_ep_addr=usbd_hs_getnewinepaddr();为端点设置rx/txfifo的方式如下:usbd_settxfifo(pdev,(info->com_ep_addr&0x7f),64);usbd_setrxfifo(pdev,(info->data_out_ep_addr&0x7f),512);usbd_settxfifo(pdev,(info->data_in_ep_addr&0x7f),512);e.1计算cdc类描述符的大小非复合设备:如果在系统配置时,usb设置为非复合设备,也就是只支持某个类的一个设备。这种情况下,cdc类描述符的正常大小为:cdc_desc_size=sizeof(usbd_std_interface_descriptor_t)*2+sizeof(cdc_function_descriptor_t)+sizeof(usbd_std_endpoint_descriptor_t)*3;其中usbd_std_interface_descriptor_t的定义和usb2.0协议table9-12一致。usbd_std_endpoint_descriptor_t的定义和usb2.0协议table9-13一致。而cdc_function_descriptor_t的定义则主要为cdc类的功能描述符,参考cdc协议,这里为了说明,给出了cdc_function_descriptor_t的结构定义,如下:复合设备:如果系统配置usb设备为复合设备,计算类描述符大小时,在前面cdc_desc_size的基础上,追加一个itf_association_descriptor(iad)的大小,它的定义如下:描述符大小:cdc_desc_size+=sizeof(usbd_std_itf_association_descriptor_t);iad描述符为构建复合设备的关键描述符,在复合设备中,一个类设备对应一个iad描述符。e.2为cdc类描述符申请内存并赋值可以通过动态的方式申请:uint8_t*p_cdc_desc=(uint8_t*)malloc(cdc_desc_size);然后按照以下顺序为描述符成员变量赋值:(usbd_std_itf_association_descriptor_t)(iad描述符只在复合设备中存在)usbd_std_interface_descriptor_tcdc_function_descriptor_tusbd_std_endpoint_descriptor_tusbd_std_interface_descriptor_tusbd_std_endpoint_descriptor_tusbd_std_endpoint_descriptor_t赋值完成后,cdc类描述符就已经就绪了。e.3为cdc类更新配置描述符首先,获取配置描述符地址指针p_config_desc(前面系统配置时讲到过);根据cdc类描述符大小cdc_desc_size,重新调整配置描述符内存:p_config_desc=realloc(p_config_desc,current_length+cdc_desc_size);其中current_length为系统配置时的长度为9。然后将前面的cdc类描述符追加到原有配置描述符后面,实现如下:memcpy(p_config_desc+current_length,p_cdc_desc,cdc_desc_size);current_length+=cdc_desc_size;最后,如果系统配置为非复合设备,因为只有一个cdc对象,除了更新配置描述符外,还需要更新设备描述符和设备限定描述符,可以通过如下方式:devicedesc.bdeviceclass=class_code_commu_and_cdc_contrl;devicequalifierdesc.bdeviceclass=class_code_commu_and_cdc_contrl;f.该步骤实现p_cdc_object设置的方式,可以通过类对象说明一节中的方法处理,具体涉及到类型、编号、端点、接口、p_obj_info、p_obj_handler等信息,实现方式如下:class_obj_init(p_cdc_object,index,usb_class_cdc_acm);class_obj_itf_add(p_cdc_object,info->com_itf_num);class_obj_ep_add(p_cdc_object,info->com_ep_addr);class_obj_itf_add(p_cdc_object,info->data_itf_num);class_obj_ep_add(p_cdc_object,info->data_out_ep_addr);class_obj_ep_add(p_cdc_object,info->data_in_ep_addr);class_obj_info_set(p_cdc_object,p_obj_info);class_obj_handler_set(p_cdc_object,p_obj_handler);g.该步骤将新建的类对象p_cdc_object注册进系统,其实就是将p_cdc_object插入系统类对象链表中,等设备启动并运行时,系统会根据不同的情况,遍历该链表,找到相应的设备类以及对应的类对象编号,从而完成不同情况下的操作,实现方式可以是下面这样:list_insert_after(&pdev->dev_list_head,&p_cdc_object->list);h.最后,将临时使用的动态内存空间释放掉,比如p_cdc_desc。free(p_cdc_desc);。优选的,所述usb类初始化流程步骤中,usb开始工作,从而进入到类初始化处理流程中。如图3的步骤过程:a.该过程即为通常所说的usb枚举过程,不是本文重点,不作详述。b.setconfig一般认为是枚举过程的结束,在这里,我们可以分别初始化注册到系统中的每个类。也就是调用每个类的初始化函数,比如cdc_init、ms_init。c.前一步骤所说的类初始化,很大部分都是对类对象的初始化。比如cdc类,在类对象创建后,其实已经知道该类拥有几个类对象,只要对这几个类对象顺序进行初始化即可,也即调用cdc_object_init函数。而在cdc_object_init实现中,一般会根据类对象创建过程中,保存到类对象结构中的一些信息(比如p_obj_info),来使能对应的端点,并为out端点准备接收内存,并做接收准备。优选的,所述类对象控制请求处理流程中,通常为标准请求和类请求,而类请求则需要对应的设备类驱动来处理。如图4步骤:a.假设收到了发给cdc类某个对象的类请求包,如setlinecoding请求。b.根据图5所示,解析该setup包,确定bmrequesttype的recipient字段是否为interface。table9-2.formatofsetupdatac.对于一般的标准请求,不需要转到设备类中去处理,这种情况下,按通常做法处理即可,不做特殊说明。d.如果是某个类请求,通常会带有该类对应的接口号(类描述符中使用),这时我们可以调用find_class_by_itf和find_obj_idx_by_itf从链表上找到类索引和类对象索引e.根据类索引找到请求执行的类,比如为cdc类。然后执行cdc_setup函数,参数为请求的setup数据和类对象索引f.在cdc_setup中,根据参数类对象索引,执行cdc_object_setup对类对象的setup包进行处理g.在cdc_object_setup中,根据请求的setup包,解析具体的类请求,可以知道该请求为setlinecoding,则将对应的波特率等信息提取,然后保存下来。优选的,所述类对象的数据如何接收处理步骤中,根据流程进行数据的接收处理。如图6所示:a.这一步在类对象初始化时已经完成,主要是配置端点、准备好接收的buffer,并开始准备接收数据。前面已经讲过,不作赘述。b.在前一步完成后,类对象相应的接收端点就已经可以接收数据了,这时如果从host侧发来一包数据的话,通常会触发usbout中断。c.在触发usbout中断后,通常会读取usbout端点状态相关的寄存器,从而确定是哪一个端点触发了该中断,这样就可以拿到了触发该中断的out端点的端点号和端点地址。d.拿到接收的out端点后,find_class_by_ep和find_obj_idx_by_ep函数对类对象链表进行遍历就可以确定该端点对应的类索引以及类对象索引。e.通过类索引访问g_usbd_ctrl_blk->pclass数组,即可访问到类的控制块信息,而class_receive函数就在该控制块信息内。比如cdc类对应的class_receive函数为cdc_receive,参数为类对象索引。f.在cdc_rceive函数内部,通过类对象索引调用cdc_object_receive来接收数据。g.在cdc_object_receive函数内部,可以通过读取硬件寄存器或者底层接口的方式获取已接收到的数据的大小,然后可以根据实际情况做相应的处理,比如调用应用程序传下来的函数指针app_recved,转交给应用程序处理。优选的,所述类对象的数据如何发送步骤中,根据usb类对象上数据的发送流程进行处理。如图7所示:a.应用层在发送数据之前肯定要将发送数据准备好,才能发送。假设数据存放在databuffer中,数据长度为datalength,并且应用层知道自己向哪个接口发送数据,也就是知道类对象索引objectindex。b.在cdc类驱动的实现中,数据发送函数可以是cdc_send(databuffer,datalength,objectindex);这样应用层就可以通过现有的参数,调用该接口函数将数据发出。c.在cdc_send函数中,可以根据类对象索引,获取到类对象的handler,类对象的私有属性信息等,通过这些信息可以调用cdc_object_send函数来完成从该类对象上发送数据d.类对象数据的发送最终会从最底层硬件将数据发送出去。这里根据类对象的私有信息p_obj_info拿到的发送端点地址,将数据从底层接口发送出去。与现有技术相比,本发明提供了一种嵌入式系统中运行时构建usb复合设备的方法,具备以下有益效果:本发明嵌入式系统中运行时构建usb复合设备的方法通过抽象usb不同class的相同属性,以面向对象的思想,可以构建不同类下的多个对象,且实现不同对象之间零耦合,不管在设备枚举期间,还是在非控制端点的通信过程中,usb核心都可以通过抽象的属性来找到相应设备类的具体对象,从而实现进一步的class协议层面的处理,这样通过该方法,如果系统想要在原来两个虚拟串口的基础上扩展成三个虚拟串口,只需要调用一个函数添加一个类设备即可,使得该方法与其他usb驱动栈要么仅支持一个设备类,要么以在编译时静态修改的方式实现的复合设备的方式相比,可扩展性更高,且使用方式十分简单、方便。附图说明附图用来提供对本发明的进一步理解,并且构成说明书的一部分,与本发明的实施例一起用于解释本发明,并不构成对本发明的限制,在附图接收处理步骤中:图1为本发明提出的驱动栈使用方法示意图;图2为本发明提出的类对象的添加流程示意图;图3为本发明提出的usb类初始化示意图;图4为本发明提出的类对象控制请求处理示意图;图5为本发明提出的setup包格式示意图;图6为本发明提出的类对象数据接收过程示意图;图7为本发明提出的类对象数据发送流程示意图;具体实施方式下面将结合本发明实施例接收处理步骤中的附图,对本发明实施例接收处理步骤中的技术方案进行清楚、完整地描述,显然,所描述的实施例仅仅是本发明一部分实施例,而不是全部的实施例。基于本发明接收处理步骤中的实施例,本领域普通技术人员在没有做出创造性劳动前提下所获得的所有其他实施例,都属于本发明保护的范围。请参阅图1-7,本发明提供一种技术方案:一种嵌入式系统中运行时构建usb复合设备的方法,在嵌入式系统中,usb设备驱动栈在运行时构建一个或多个设备的方法,具体包括类对象说明、设备控制块说明、设备接口号和端点号分配、usb设备配置或使用方法、创建类对象流程、usb类初始化流程、类对象控制请求处理流程、类请求如何传递到类对象处理、类对象的数据如何接收和类对象的数据如何发送几个步骤。这样通过抽象usb不同class的相同属性,以面向对象的思想,可以构建不同类下的多个对象,且实现不同对象之间零耦合,不管在设备枚举期间,还是在非控制端点的通信过程中,usb核心都可以通过抽象的属性来找到相应设备类的具体对象,从而实现进一步的class协议层面的处理,这样通过该方法,如果系统想要在原来两个虚拟串口的基础上扩展成三个虚拟串口,只需要调用一个函数添加一个类设备即可,使得该方法与其他usb驱动栈要么仅支持一个设备类,要么以在编译时静态修改的方式实现的复合设备的方式相比,可扩展性更高,且使用方式十分简单、方便。进一步,类对象说明步骤中,其中对于usb设备,如果是复合设备的话,可以拥有多个usb设备类,而每个usb设备类一样可以拥有一个或多个设备类对象。所谓类对象,是取自面向对象语言的概念,当把cdc比喻成一个面向对象语言中的一个类,它可以被实例化成很多个对象,比如cdc是一个实现虚拟串口类,一个cdc类对象则表示一个实际的虚拟串口,如果有多个cdc类对象,则表示最终usb设备在usbhost端看到的是多个虚拟串口,而且可以同时使用。进一步,设备控制块说明步骤中,一个usb设备只有一个设备控制块,它对应的结构体定义为usbd_ctrl_block_t。使得可以很好的进行对应,而在设备中,将它对应的变量定义为g_usbd_ctrl_blk,从而便于进行描述讲解,而内部成员分为pclass和dev_list_head,这样可以很好的根据设定进行流程。进一步,设备接口号和端点号分配步骤中分为端点地址转换、申请一个新的接口号、分配一个新的in端点地址和分配一个新的out端点地址四个部分。这样通过四个部分的分别处理,使得整体可以很好的进行使用。进一步,usb设备配置或使用方法步骤中,上层根据实际需要,经过简单的配置,usb正常启动后,usb主机根据正常流程就可以检测并枚举到usb设备了。这样的使用方式,使应用层对驱动栈的使用更加方便。进一步,创建类对象流程步骤中一个类对象即代表一个实际的设备,并且解释类对象是如何被添加到系统中去的。一旦类对象被添加,系统就可以结合操作类对象的方法,在各种情况下找到该类对象。进而使得在后续的处理过程中更加便利。进一步,usb类初始化流程步骤中,usb枚举结束后,从而进入到设备类初始化处理流程中,并且根据usb设备类初始化,使得类对象创建过程中,保存到类对象结构中的一些信息,被用来使能对应的端点,并为out端点准备接收内存,并做接收数据的准备。进一步,类对象控制请求处理流程步骤中,因为在usb设备和主机通信过程中,有很多控制请求,通常为标准请求和类请求,而类请求则需要对应的usb设备类来处理,通过提取接口号,遍历类对象链表的方式,可以快速找到类对象,并进行相应的类请求处理。进一步,类对象的数据如何接收处理步骤中,从而根据流程进行数据的接收处理。通过获知哪个端点接收到数据,进而通过该端点遍历类对象链表,获取到该端点对应的类对象,类对象的接收函数被用来处理接收的数据。这样的方式,使得usb核心层与类驱动层可以有效的进行解耦。进一步,类对象的数据如何发送步骤中,根据usb类对象上数据的发送流程进行处理。上层通过类对象索引,直接通过某个类对象进行数据发送,而且上层也知道发往哪个设备。这样对上层来讲,数据发送接口更加符合使用习惯。本发明的工作原理及使用流程:本发明嵌入式系统中运行时构建usb复合设备的方法通过抽象usb不同class的相同属性,以面向对象的思想,可以构建不同类下的多个对象,且实现不同对象之间零耦合,不管在设备枚举期间,还是在非控制端点的通信过程中,usb核心都可以通过抽象的属性来找到相应设备类的具体对象,从而实现进一步的class协议层面的处理,这样通过该方法,如果系统想要在原来两个虚拟串口的基础上扩展成三个虚拟串口,只需要调用一个函数添加一个类设备即可,使得该方法与其他usb驱动栈要么仅支持一个设备类,要么以在编译时静态修改的方式实现的复合设备的方式相比,可扩展性更高,且使用方式十分简单、方便。尽管已经示出和描述了本发明的实施例,对于本领域的普通技术人员而言,可以理解在不脱离本发明的原理和精神的情况下可以对这些实施例进行多种变化、修改、替换和变型,本发明的范围由所附权利要求及其等同物限定。当前第1页12当前第1页12
当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1