OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 2990|回复: 2

stem32F407 USB toggle error的分析和解决

[复制链接]

1

主题

1

帖子

0

精华

新手上路

积分
32
金钱
32
注册时间
2019-11-15
在线时间
9 小时
发表于 2019-12-9 13:44:24 | 显示全部楼层 |阅读模式
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的情况。

       










       







最佳答案

查看完整内容[请看2#楼]

非常不错,谢谢分享
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2019-12-9 13:44:25 | 显示全部楼层
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2019-12-10 02:17:09 | 显示全部楼层
自己努力解决一次问题,后续对类似的问题解决会很有帮助,楼主优秀
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-7-6 01:47

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表