搜索
bottom↓
回复: 25
打印 上一主题 下一主题

lpcopen中一个很隐蔽usb lib错误,解决过程分享

[复制链接]

出0入0汤圆

跳转到指定楼层
1
发表于 2019-1-21 17:39:36 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 jjj 于 2019-1-21 17:52 编辑

        最近调试LPC4357的USB接口,出现一个很棘手的问题,拷贝文件时,莫名其妙的拷贝失败,这个问题如果不解决的话,即使将来用在产品上,肯定会有问题的,早晚会暴发,于是我就好好的研究了一下这个USB协议栈,LPC4357官方例程LPCOPEN使用的是Dean Camera开发的LUFA(The Lightweight USB Framework for AVRs),应该是在这上面修改而成的,令我吃惊的LUFA的作者Dean Camera,这个家伙居然很年轻,还没有我大,因为我之前常用的比如LWIP、FATFS都是一些老家伙(老教授)开发的,没想到这么年轻开发的软件居然入了大公司NXP的法眼,于是NXP在此基础上专门给LPCXXXX系列单片机做了修改,终于2012年成了“Copyright(C) NXP Semiconductors, 2012”
        膜拜也好、惊叹也好,问题还得解决,LPC4357片子用的是通用的EHCI主机,这与我熟悉的STM32单片机不一样,我本来是不打算深究的,但是我简单查了一下EHCI控制器相关的资料,发展EHCI规范是INTER制定的,这个USB主机才是主流的,通用的,好多单片机、ARM的USB外设都是兼容EHCI的,而STM32的USB控制器我也没有找到来源,估计是ST公司自己设计的吧,很显然,研究EHCI的工作原理要比STM32的USB更有前景。
        于是我从网上找了很多EHCI的资料,有中文的、有准备的英文规范,《USB2.0与OTG规范及开发指南》这是一本系统介绍USB的书籍,但是我不可能有时间会去慢慢回味,我要的是速战速决。《ehci-specification-for-usb》是英文原版的EHCI规范,这对我来说只是工具书,遇到中文资料有含糊不清的地方时我才会打开英文原版资料来确认。〈Host控制器之EHCI〉这是个无名大神的类学习笔记,在我学习EHCI、解决问题的过程中起了重要作用。
        EHCI与我之前熟悉的那些外设如SPI、UART等最大的不同是USB2.0是标准,EHCI直接定义了USB20的一种实现方式,定义的非常之细,不仅定义了的寄存器,而定义了的数据结构体,更甚一步这些数据结构体在一定程度上还起着寄存器的作用,但这是数据结构体是定义在用户RAM区的,大多这些结构体要求是4096对齐的,(太浪费空间了,STM32不用是不是因为RAM太小)。因为我这次要解决的是LPC4357访问U盘,因此我只是详细看了,QH(queue header)、qTD(queue transfer descriptors),因为大容量存储设备大概只用了这两个结构体,还有一个iTD这是同步传输才使用的,我只是简单看了下。QH对应 一个pipe,一个端点,下面挂了一串qTD,所谓的发送接收USB数据包,就是填充这些结构体,然后启动发送就行了,然后EHCI控制器会自动的访问这些定义的QH、qTD来传送数据,数据传输完毕后在中断程序中结束发送。
        有了这些基本的知识,再调试起LPC4357的USB来就不会一头雾水了,至少我大概能弄清数据的流向,如果你不弄清这些东西,即使单步调试到错误的地方,你还是不知道哪里错了。经过好多天学习-〉调试-〉学习-〉调试-〉学习-〉调试-〉反复过程,终于慢慢定位到真正的错误地方;期间我还找来了公司的USB ANALYST 总线分析仪,这个仪器对我定位问题起到决定性作用。  NXP提供的这个USB协议栈有个很隐蔽的问题,在EHCI.c文件中QueueQTDs()函数,
static HCD_STATUS QueueQTDs (uint8_t HostID,
                                                         uint32_t* pTdIdx,
                                                         uint8_t* dataBuff,
                                                         uint32_t xferLen,
                                                         HCD_TRANSFER_DIR PIDCode,
                                                         uint8_t DataToggle)
{
        uint32_t TailTdIdx=0xFFFFFFFF;

        while (xferLen > 0)
        {
                uint32_t TdLen;
                uint32_t MaxTDLen = QTD_MAX_XFER_LENGTH - Offset4k((uint32_t)dataBuff);

                if(PipeStreaming[HostID].PacketSize > 0)
                        TdLen = MIN(xferLen, PipeStreaming[HostID].PacketSize);
                else
                        TdLen = MIN(xferLen, MaxTDLen);
                xferLen -= TdLen;

                if (TailTdIdx == 0xFFFFFFFF)
                {
                        ASSERT_STATUS_OK ( AllocQTD(HostID, pTdIdx, dataBuff, TdLen, PIDCode, DataToggle, (xferLen==0) ? 1 : 0) );
                        TailTdIdx = *pTdIdx;
                }
                else
                {
                        uint32_t NewTdIDx;
                        if(HCD_STATUS_OK == AllocQTD(HostID, &NewTdIDx, dataBuff, TdLen, PIDCode, DataToggle, (xferLen==0) ? 1 : 0))
                        {
                                HcdQTD(HostID,TailTdIdx)->NextQtd = Align32((uint32_t) HcdQTD(HostID,NewTdIDx));
                                TailTdIdx = NewTdIDx;
                        }
                        else
                        {
                                PipeStreaming[HostID].BufferAddress = (uint32_t)dataBuff;
                                PipeStreaming[HostID].RemainBytes = xferLen + TdLen;
                                PipeStreaming[HostID].DataToggle = DataToggle;
                                HcdQTD(HostID,HCD_MAX_QTD - 1)->IntOnComplete = 1;
                                break;
                        }
                }
                if(DataToggle == 1) DataToggle = 0;
                else DataToggle = 1;
                dataBuff += TdLen;
        }
        if(xferLen == 0)
        {
                memset(&PipeStreaming[HostID], 0, sizeof(Pipe_Stream_Handle_T));
        }
        return HCD_STATUS_OK;
}
即使不懂USB协议栈,完全分析这个函数,也能看出点问题的,传输长度xferLen在循环分配的过程中,先减去了将要分配的长度xferLen -= TdLen;再去分配QTD,如果分配失败的话,xferLen已经减过了,而在函数后面if(xferLen == 0)(),却依然使用了xferLen 这个错误的值,造成最后一包数据丢失。只需在这个地方这样修改就可以了。
                                xferLen = xferLen + TdLen;                //willow add
                                PipeStreaming[HostID].RemainBytes = xferLen /*+ TdLen*/;
经过测试,确实是这的问题,测试通过。
这个问题之所以隐蔽,是因为不是所有的应用都会暴露出来的,只有当传送的数据包为(8*n)+1包时,才会出现。所以有的文件可以拷贝成功,有的文件不能成功,且与文件大小关系也不大

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出50入58汤圆

2
发表于 2019-1-21 17:44:30 来自手机 | 只看该作者
感觉在这个年代能潜下心分析协议栈的都是牛人。。

出0入0汤圆

3
 楼主| 发表于 2019-1-21 17:49:20 | 只看该作者
鲜衣怒马 发表于 2019-1-21 17:44
感觉在这个年代能潜下心分析协议栈的都是牛人。。

如果这些貌似有点用的分享过程 再也找不到个合适的地方来分享, 那这个年代……
让我们守护这片净土。

出0入0汤圆

4
发表于 2019-1-21 17:57:13 | 只看该作者
类似的调试分享,作用很大

出0入0汤圆

5
发表于 2019-1-21 20:46:56 | 只看该作者
佩服静心研究协议的牛人,^_^,我是mark党。

出0入0汤圆

6
发表于 2019-1-21 20:59:10 | 只看该作者
STm32的host+otg的控制器,用的应该是synopsys提供的IP。这也是soc上比较常见的一种。

出0入0汤圆

7
发表于 2019-1-21 21:03:18 | 只看该作者
做个记号,谢谢楼主

出0入0汤圆

8
发表于 2019-1-21 21:33:31 | 只看该作者
putty 发表于 2019-1-21 20:59
STm32的host+otg的控制器,用的应该是synopsys提供的IP。这也是soc上比较常见的一种。 ...

它的eth,一样是用的新思的ip,其实都比nxp自己开发的好,实话实说

出0入0汤圆

9
发表于 2019-1-22 08:02:48 来自手机 | 只看该作者
nxp的usb是很快的,读写u盘很快。

出0入0汤圆

10
 楼主| 发表于 2019-1-22 08:09:56 | 只看该作者
XA144F 发表于 2019-1-22 08:02
nxp的usb是很快的,读写u盘很快。

就是用这个片子的高速USB2.0功能, 测试写U盘速度10M/S,
实际应用是从以太网上取数据再保存到U盘,能稳定在3MB/S,
STM32的USB不加PHY的话,超不过1MB/S,

出0入0汤圆

11
发表于 2019-1-22 08:33:18 | 只看该作者
jjj 发表于 2019-1-21 17:49
如果这些貌似有点用的分享过程 再也找不到个合适的地方来分享, 那这个年代……
让我们守护这片净土。 ...

就屏这句话就应该为你点赞!!!

出0入0汤圆

12
发表于 2019-1-22 08:39:03 | 只看该作者
我们也在用4357,但是感觉NXP现在都不推这个片子了,双核功能很强大,用起来很复杂。。没有ST的用起来简单

出0入0汤圆

13
发表于 2019-1-22 08:40:07 | 只看该作者
本帖最后由 bblythe2017 于 2019-1-22 08:41 编辑

这个年代,找出USB协议中的这个bug,需要技术、智慧、情怀的结合。

找出这个BUG的难度有多大,只有做过这个程序的人才知道。我写类似的程序,需要花好几天的时间来构思、来写,就怕出现楼主发现的这种BUG。

但是非常遗憾,我花了好几天时间写的类似程序,现在有些客户使用也出现了不明原因的问题。

出0入0汤圆

14
发表于 2019-1-22 08:44:36 来自手机 | 只看该作者
多谢分享。保留备用

出0入42汤圆

15
发表于 2019-1-22 08:48:13 来自手机 | 只看该作者
非常感谢楼主无私分享

出0入0汤圆

16
发表于 2019-1-22 09:10:10 | 只看该作者
看到你有个usb总线分析仪,就知道你很幸运。我当年只能靠bus hound抓包分析,如果总线发送不成功,那个debug真是痛苦。

出0入0汤圆

17
 楼主| 发表于 2019-1-22 09:22:44 | 只看该作者
lkm_unication 发表于 2019-1-22 09:10
看到你有个usb总线分析仪,就知道你很幸运。我当年只能靠bus hound抓包分析,如果总线发送不成功,那个debu ...

单片机做USB HOST,怎么用BUSHOUND ?

出0入0汤圆

18
发表于 2019-1-22 09:24:35 | 只看该作者
jjj 发表于 2019-1-22 09:22
单片机做USB HOST,怎么用BUSHOUND ?

不会意思,让你误解了,我是调usb device。

出0入0汤圆

19
发表于 2019-1-22 09:31:41 | 只看该作者
顶楼主,这种能深入研究协议的都是公司的栋梁之才。

出0入8汤圆

20
发表于 2019-1-22 09:49:21 | 只看该作者
jjj 发表于 2019-1-22 08:09
就是用这个片子的高速USB2.0功能, 测试写U盘速度10M/S,
实际应用是从以太网上取数据再保存到U盘,能稳 ...

ST的芯片只有F723和F730有内置的高速phy,其它芯片没有内置高速phy,所以速度上不去

出0入0汤圆

21
 楼主| 发表于 2019-1-22 10:03:03 | 只看该作者
本帖最后由 jjj 于 2019-1-22 10:04 编辑
canspider 发表于 2019-1-22 09:49
ST的芯片只有F723和F730有内置的高速phy,其它芯片没有内置高速phy,所以速度上不去 ...


STM32有内置USB PHY的全部没有网口,这个项目不方便用

出0入0汤圆

22
发表于 2019-1-22 12:30:20 | 只看该作者
给LZ点个赞,厉害了,这种坑很难查的。可以去Kernel中瞅瞅。

出0入0汤圆

23
发表于 2019-2-17 21:01:42 | 只看该作者
感谢楼主分享!巨牛无比!

出0入0汤圆

24
发表于 2019-2-18 10:11:22 来自手机 | 只看该作者
jjj 发表于 2019-1-21 17:49
如果这些貌似有点用的分享过程 再也找不到个合适的地方来分享, 那这个年代……
让我们守护这片净土。 ...

楼主有高速USB协议分析仪?这高级货多少钱

出0入0汤圆

25
发表于 2019-2-18 10:21:23 | 只看该作者
佩服楼主, 现在能潜心坐下来好好分析问题的工程师越来越少了,

出0入0汤圆

26
 楼主| 发表于 2019-2-22 10:42:12 | 只看该作者
huangqi412 发表于 2019-2-18 10:11
楼主有高速USB协议分析仪?这高级货多少钱

公司的,不知道,看着像ZLG早期产品,现在官网上找不到这个型号了
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-26 22:11

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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