stem32F407 USB data toggle error的分析和解决

因工作需要从某个设备上读取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一样,如果不一样就会出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。

终于找到问题,那么就好解决了:既然HOST IN Channel,BULK模式下,HCTSIZ.PID本来就是对的,
犯不着画蛇添足写入,于是修改usb_core.c,USB_OTG_HC_StartXfer函数:
读现在的值
oldhctsiz.d32 = USB_OTG_READ_REG32(&pdev->regs.HC_REGS[hc_num]->HCTSIZ);
if(pdev->host.hc[hc_num].ep_is_in && pdev->host.hc[hc_num].ep_type == 2){
//如果是IN Bulk,就用现在寄存器中的PID
hctsiz.b.pid = oldhctsiz.b.pid;
}else{
//其它情况仍使用host.hc中的data_pid
hctsiz.b.pid = pdev->host.hc[hc_num].data_pid;
}
//写入
USB_OTG_WRITE_REG32(&pdev->regs.HC_REGS[hc_num]->HCTSIZ, hctsiz.d32);

到此程序正常了。总结根本原因原来程序没有考虑到一个帧Frame有多个包Packet的情况。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值