新手上路
- 积分
- 32
- 金钱
- 32
- 注册时间
- 2019-11-15
- 在线时间
- 9 小时
|
1金钱
实在很郁闷发个贴。
因工作需要从某个设备上读取USB数据。该设备有windows驱动和应用软件,需要用stm32F407做一个
读取和转发设备。
首先用BusHound在Windows上分析了该设备的USB数据。设备USB类为特殊的0XFF,EndPoint有两个,
一个IN一个OUT Endpoint均是Bulk。主机OUT大约10几个字节的命令,设备会回应一帧(Frame)或多
帧。每帧有一个或多个包(Packet)组成。
因为之前没有做过USB程序,所以使用了例程"USB鼠标键盘(Host)实验"的代码进行改造。正确匹配
了USB的厂家和USB Class信息后,STM32F407可以和设备进行数据传输,主机OUT的数据设备能收到,
设备也能正确回应数据,STM32F407 HOST也能收到设备数据。然而不知道为什么有时应会出现丢包
现象。以为是硬件问题,换了不同型号固障依旧,那没办法需要深入进行分析。
为了分析需要将STM32F407 USB OTG 相关的寄存器通过串打印出来,碰到串口速度慢USB HOST不能
完成设备枚举,又开了一块内存将打印放在内存中,在main循环中发串口。实在很郁闷。终于将
寄存器打印出来了。
结果发现channel中断错误data toggle error。在网上查很少有资料提及这个错误。
深入学习分析后结果:
主机IN BULK(本文仅指BULK类型的传输)数据过程是这样的:
在寄存器HCTSIZ中设置PID。 全速设备PID为DATA0或DATA1,这个值必须和设备发来的数据包PID中的
PID一样,如果不一样就会出data toggle error。一个IN Bulk通道中设备PID会重复DATA0、DATA1。
当设备发来数据时,会触发中断,GINTSTS中RXFLVL: RxFIFO non-empty
寄存器GRXSTSP中PKTSTS状态为IN data packet received, GRXSTSP中的DPID为设备发来数据包的PID。
同时HCTSIZ中PID为DPID的下一个值,应该是硬件自动设置的,即
HCTSIZ.PID = next GRXSTSP.DPID
如果一帧中有多个数据包,那么DPID就反复DATA0 DATA1 DATA0 DATA1....循环,至此工作正常。
但是在一帧(Frame)结束时,会产生另一个中断,寄存器GRXSTSP中PKTSTS状态为IN transfer completed。
在程序usb_hcd_int.c中,函数USB_OTG_USBH_handle_hc_n_In_ISR,
else if (hcint.b.xfercompl)
{
....
if ((hcchar.b.eptype == EP_TYPE_CTRL)||
(hcchar.b.eptype == EP_TYPE_BULK)){
pdev->host.hc[num].toggle_in ^= 1;
}
关键就在这里pdev->host.hc[num].toggle_in ^= 1;将toggle_in翻转了一次。
在下一次接收时,在usb_ioreq.c中,USBH_BulkReceiveData函数中,
if( pdev->host.hc[hc_num].toggle_in == 0)
{
pdev->host.hc[hc_num].data_pid = HC_PID_DATA0;
}
else
{
pdev->host.hc[hc_num].data_pid = HC_PID_DATA1;
}
根据host channle的toggle_in设置了data_pid。
在usb_core.c中,USB_OTG_HC_StartXfer函数,
hctsiz.b.pid = pdev->host.hc[hc_num].data_pid;
USB_OTG_WRITE_REG32(&pdev->regs.HC_REGS[hc_num]->HCTSIZ, hctsiz.d32);
简单说,就是根据channel变量中的toggle_in设置了HCTSIZ.PID。 而HCTSIZ.PID设置错了就会出
data toggle error;
pdev->host.hc[num].toggle_in只有在一帧结束时才设置一次翻转,那么如果一帧有奇数个数据
包,那么就是碰巧正确的。如果一帧有偶数个数据包,pdev->host.hc[num].toggle_in值就是错误
的。
这就是为什么有时对,有时不对。 同一个设备命令,先发送后发送有时对有时出toggle error。
终于找到问题,那么就好解决了:在寄存器GRXSTSP中PKTSTS状态为IN data packet received,因
为硬件已经正确设置了HCTSIZ.PID,就读取这个值,反过来设置toggle_in。
修改USB_OTG_USBH_handle_rx_qlvl_ISR函数,
case GRXSTS_PKTSTS_IN时,
//读取HCTSIZ
hctsiz.d32 = USB_OTG_READ_REG32(&pdev->regs.HC_REGS[channelnum]->HCTSIZ);
if(pdev->host.hc[channelnum].ep_type == 2){
//仅针对Bulk模式,根据PID设置toggle_in
if(hctsiz.b.pid==0){
pdev->host.hc[channelnum].toggle_in=0;
}else{
pdev->host.hc[channelnum].toggle_in=1;
}
}
在传输完成时,如果类型为2(Bulk)不再设置toggle_in值。
if(pdev->host.hc[channelnum].ep_type != 2){
//非BULK才翻转
pdev->host.hc[num].toggle_in ^= 1;
}
到此程序正常了。总结根本原因还是对USB协议不了解。选用了USB鼠标键盘(Host)实验的程序,
鼠标键盘的传输并不是Bulk类型传输,没有考虑到一个帧Frame有多个包Packet的情况。
|
|