USB转串口的实现过程

                                          USB转串口的实现过程

 

       前面我已经说到了USB转串口过程中USB是怎么跟串口联系起来的,如何挂上去的。接下来对于程序的深入理解想写一下关于USB转串口的整个数据的收和发的过程,一达到理清思路解决遗留问题的目的。

 

关于初始化的部分不用详细说明,主要是USB转串口的获得USB设备属性,枚举接口,挂上串口的过程。现在主要是根据串口的API来说下数据流的过程,COM_OPEN,COM_READ,COM_WRITE,COM_CLOSE.

 

COM_OPEN是一个相当重要的函数,这个函数自然石不需要做任何修改这是属于微软MDD的函数,其对应的两个文件夹COM_MDD2,SERPDDCM,是基本上都不需要修改的。但是其流程需要好好的分析。

 

COM_INIT给的返回值是PHW_INDEP_INFO,这里面的pHWOBJ是联系COM_MDD2到SERPDDCM的纽带。

 

DCB是DATA Control Block的缩写,DCB的结构成员全部都是串口数据流传输的控制参数,这些参数都是属于硬件属性范畴,是所有串口的特性,当然我们现在的这个设备并不会去采用流控。OPEN初始化两个重要的结构体PHW_INDEP_INFO,PHW_OPEN_INFO,

HWSetCommTimeouts读写操作的超时的参数设置,HWOpen具体的做了SetDefaultConfiguration,InitLine,InitReceive,InitXmit。

 

SetDefaultConfiguration这个函数是设置系统默认的结构体的设置。流控,波特率,校验位等等。主要是InitReceive,InitXmit。

 

InitReceive会对应到serialDataIn这个类InitReceive,ResetTransferQueue主要是去将正在传输队列中的Transfer都清空,这些Transfer都是在init的时候new出来的,ResetPipe根据MSDN的说法是清除USB的halt状态位,复位endpoint的数据到DATA0,他不会复位stall状态,(所以后面就加了一个clearfeature来清除这个stall feature,但是这个feature会使得AT回复阻塞),之后就会让整个USB处于等待USB接收数据的状态。它其实给了USB 4个设备缓冲,即USB接收到数据后就是通过这4个缓冲来把数据移到串口的缓冲中。IssueBulkTransfer是这个函数最重要的函数,在理解USB协议的时候再来分析。

 

一个重要的问题就是线程函数是在初始化的时候如何就进入等待状态的,而CMiniThread这个类里就有ThreadStart()这个成员函数,作用是清除挂起状态(用到了这个函数ResumeThread)。构造函数创建了线程CMiniThread::ThreadProc,并且处于挂起状态,ThreadProc调用了ThreadRun()这个成员函数(是个纯虚函数,在CPddXXXUart里有实现,而这个实现就是IST)。当这个函数执行后那么整个初始化基本上就完成了其最后的结果就是让这个线程等着USB那边的数据过来。

 

InitXmit只是做了ResetPipe清除了相对应Pipe的一些标志位。

 

 

其实所有的动力源都是COM_WRITE这个函数做的。只有向USB设备发送了请求或者写入字节数就能收到USB设备相对应的返回。才能触动整个流程。

DoTxData这个函数通过HWTxIntrHandler这个中断处理函数来用bulk方式来把想要发的数发给USB设备。

 

对于IssueBulkTransfer这个函数中带有一个回调函数地址,当flag中带有USB_NO_WAIT的时候就会函数一执行就会直接返回,但是会用这个回调函数来等着USB设备给报告是否发送完成,这就是一个典型的异步模式,之后SerialDataOut这个线程会等到发送完成后收到USB的回调消息触发串口MDD层的NotifyPDDInterrupt,然后由SerialEventHandle这个函数来处理这个事件最后再由DoTxData来发送剩下的字节,知道发送完所有的字节。

而当

 

还记得前面有个InitReceive这个函数吗?当一有数据从USB设备返回的时候就会触动它的回调函数(我的理解是,返回的概念肯定有一个超时的限制,比如说我一次来了一个数,但是很长时间不来了,只能算一次回调,应该是在两个字符之间有多长时间的超时就会算上一次回调,当然得预先有个回调在那里等着)。当接收的回调函数执行后会又回到NotifyPddInterupt这个函数当中来判别这个貌似中断的类型是接收中断还是发送中断,之后会跟传输部分一样在SerialEventHandle这个函数里面来处理HWRxIntrHandler这个函数就是用来处理回调后来把收到的字符存储到串口缓存里面,

每一个Transfer对应着一个m_dwClientInfo,只有在CloseTransfer的时候才会吧每一次传输的句柄置零,每个USBPIPE就会去有一个自己的USBTransfer,所以很多个pipe就会有一个transfer List,

 

突然间又不整个枚举设备的过程重新的理了一下,从Attach()开始,在attach()中实际上回去做几件事情

1, 创建了一个interface的链条。说是个链表也不像,就是通过类的构造函数的强大讲所有interface的指针串成了一条线,

2, New出了所有的interface对象。

3, 对于interface进行了初始化,初始化得实际事情就是对于每个interface中的Endpoint进行初始化

4, 在EndPoint的构造中实际上就是将每个interface的Endpoint串成链。

5, 一个没有实际意义的Init.

在做完attach后就会在注册表的Active下去根据USB下的键值去创建相应的键值。这主要是通过一个函数GetSerialObject,这个函数是非常关键,至关重要的函数。它主要做的几件事情。

1, 构造一个Driver类。与前面的对应关系是一个client类对应一个driver类。

2, 做这个driver的初始化。在初始化的过程中可以对你想要的接口进行初始化,在这里面初始化的一些跟USB设备属性无关的都是跟USB相关的具体操作类,比如说PIPE,UsbTransfer.

3, 在这个初始化的过程中会先根据一个接口的属性来,多少个EndPoint,每个EndPoint的out或者In的属性来创建CreateBulkIn,CreateBulkOut。

4, CreateBulkIn在这个函数里面回去构造SerialDataIn这个类。在这个SerialDataIn类里面会去启动上面说的那个CminiThread这个线程,让ThreadRun起来,同时也会调用UsbAsyncClassPipe这个的构造函数,实质上是UsbClassPipe的构造。这个构造实际上就是根据EndPoint来创建相应的PIPE.那么就能得出这样的一个结论,就是一个USB对应着多个Interface,每个Interface对应着几个EndPoint,每个EndPoint对应着一个Pipe.

5, 接下来就是SerialDataIn的初始化了。这个初始化就会为各个PIPE来创建各自的缓存,这个缓存就是后来处于接收状态的时候USB的缓存。从程序来看每个PIPE都会有4个同样大小的缓存对应在里面。这四个缓存就是专门用于USB部分的接收。

6, 现在其实可以看出在COM_OPEN里面在InitReceive里面去初始化用IssueBulkTransfer来注册回调的过程中,就是有每个m_hUsbTransfer就对应着一个缓存。当这个m_hUsbTransfer这个句柄没有被清零就说明这次的传输就没有结束。转过头来看看InitReceive这个函数里面的4个注册回调的函数IssueBulkTransfer发出去后等着回调的过程也就是接收数据的过程也是一个传输。直到将这次回调收到得数全部填入串口缓存。然后就会调用CloseTransfer去讲m_hUsbTransfer置零就是一次Transfer的一次真正的结束。所以在串口真的处理接收字符的时候是传输一次并没有结束的时候,所以此时IsTransferEmpty肯定是个FALSE,所以按照上面的总结我想对USB数据的处理过程做一个分析。

 

每个In Pipe有4个Transfer,这4个Transfer组成一个队列,m_lpArmedTranfser在Pipe初始化的时候被赋予链表的首地址,IncTransfer这个函数就是遍历这4个Transfer组成的链表。这就不难理解每次去GetClientInfo,只要这个Trnasfer结束,我就会去用下一个Transfer每一个Transfer有自己对应的ClientInfo,前面也讲到了只有当把这个Transfer的USB部分的缓存得到的数据全部存入串口的缓存中,这次Transfer才算结束,才回去close,这个Transfer的句柄才会被清0,那么就会在GetClientInfo的时候m_lpArmedTranfser指向下一个,同时也会得到相应的clientinfo.这样也不会让数据乱序,搬完第一个buffer就会去搬运下一个buffer,知道把数据搬完。当然会是一次USB的回调才会去搬运一次,GetTransferStatus这个USBd的函数能告诉我们一次Transfer会有多少的数据。

 

其实感觉只要弄清逻辑关系写一个USB的驱动程序也不算是那么的难。

 

 

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: USB串口是一种电子设备,它可以将USB接口转换成串行通信接口,通常用于将计算机和其他设备(如Arduino)连接起来。Arduino是一款非常流行的开源电子平台,可以用于制作各种创意和交互式项目。 通过将USB串口模块连接至Arduino,我们可以在计算机上编写代码,并通过串口与Arduino进行通信。这样,我们可以通过计算机来控制Arduino以执行特定的任务。例如,我们可以使用Arduino来控制电机、传感器、LED等,完成各种自动化或互动项目。 使用USB串口模块连接Arduino可以带来许多优势。首先,USB接口在大多数计算机上都可以找到,这使得连接过程非常简单。其次,USB串口模块通常具有稳定的传输速度和可靠的数据传输能力,确保数据的准确性和高效性。 USB串口模块通常带有USB A和串口连接器(如DB9或DB25),可以将它们连接到计算机和Arduino上相应的接口。一旦建立了连接,我们便可以使用Arduino IDE(集成开发环境)来编写代码,并将其上传到Arduino中。 总之,通过使用USB串口模块连接Arduino,我们可以实现计算机与Arduino之间的通信,从而控制和操作各种硬件设备和传感器。这为我们进行电子创作和交互式项目提供了更多的灵活性和创造力。 ### 回答2: USB串口是一种常用的连接方式,用于将电脑的USB接口转换串口接口,以便连接和通信与使用串口接口的设备,像Arduino开发板等。 通常,电脑的USB接口较多,而串口接口较少,因此使用USB串口适配器可以扩展串口数量,方便与多个串口设备进行通信。 为了将USB接口和串口接口连接起来,我们需要使用USB串口适配器。这种适配器通常具有一个连接USB接口的一端和一个连接串口设备的另一端。当我们将适配器插入电脑的USB接口后,电脑会自动识别适配器,并为其分配一个COM口号,该COM口号用于在程序中与串口设备进行通信。 在Arduino开发中,我们经常使用USB串口连接Arduino开发板与电脑进行通信。通过该连接,我们可以使用Arduino IDE编写代码并上传到开发板上运行,还可以通过串口监视器来查看开发板返回的数据。 总结来说,USB串口是一种常用的连接方式,通过将USB接口转换串口接口,可以方便地连接和通信与使用串口接口的设备,如Arduino开发板等。这种连接方式在电脑和设备之间建立了一条数据通路,使得我们可以进行数据传输和控制。 ### 回答3: USB串口是指通过USB接口将电脑与串口设备(如Arduino)进行连接的一种转换方式。Arduino是一种开源的电子原型平台,通过编写程序进行控制,通常需要与电脑进行通信。然而,大多数现代电脑已经不再配备传统的串口接口,而是采用了更方便的USB接口。 为了能够将电脑与Arduino连接起来,我们可以使用一种叫做USB串口的设备。这种设备可以将USB接口转换串口接口,从而实现电脑与Arduino之间的通信。 在使用USB串口时,首先需要将Arduino通过USB线连接到电脑上,然后再将USB串口设备插入电脑的USB接口。接下来,在电脑上安装好相应的驱动程序,以确保电脑能够正常识别USB串口设备。安装完驱动程序后,电脑就可以识别USB串口设备,并将其作为一个串口设备来进行操作。 一旦电脑能够正常识别USB串口设备,我们就可以通过编写程序来与Arduino进行通信。通过串口通信,我们可以向Arduino发送指令或数据,以实现对Arduino的控制或监测。Arduino也可以通过串口将数据传输回电脑,方便我们对数据进行处理或显示。 总结来说,USB串口是一种连接电脑与串口设备的方式,通过这种方式可以方便地实现电脑与Arduino的通信,使得我们可以轻松地控制和监测Arduino的运行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值