本文转自公众号系列文章,欢迎关注
基于DWC2的USB驱动开发-USB包详解 (qq.com)
本篇讲解Scatter/Gather DMA模式下控制传输相关的寄存器。控制传输是USB驱动的核心部分,控制传输调通了驱动就完成了一大半,而驱动的核心又是中断的处理。
Scatter/Gather DMA模式的优点是可以提供一个描述符链表,一次处理更多的数据,控制器自动去索引描述符链表,进行相应的DMA处理。而Buffer DMA模式一次只能处理一个DMA操作。
来简单回顾下控制传输,控制传输包括
Setup,Data,Status三个阶段,不是所有情况都需要包括三种阶段,存在无数据阶段的控制传输。
存在三种可能
控制写
控制读
无数据的控制传输,2阶段控制传输。
控制传输需要准备以下几种描述符
为了接收最多可能存在的3个连续的SETUP包,所以需要准备一个描述符链表包括3个描述符。该描述符也可以用于接收控制读传输主机发送的状态包(0长包)。
该描述符链表的前面两个还可以作为ping-pong提高接收效率。
第三个描述符,初始化为空,最终设置为以接收在数据和状态阶段主机又异常发了SETUP。
首先我们来看和中断相关的一些寄存器,控制传输对应的就是端点0所以和其他端点其实是一样的,只不过其多了一些比如SETUP等状态而已。
首先我们来看相关的寄存器
首先全局的:GAHBCFG寄存器的bit0 GlblIntrMsk作为全局中断的总开关,置位使能总中断。
然后寄存器GINTMSK/2的bit19,bit18,OEPIntMsk和IEPIntMsk作为所有的OUT和IN端点的中断使能,对应的状态寄存器是GINTSTS/2。
然后DIEPMSK和DOEPMSK控制所有的OUT和IN端点的中断类型,哪些使能。
对应的状态寄存器是DIEPINTi 和DOEPINTi。
最后DAINTMSK控制某一个端点是否使能中断。对应的状态寄存器是DAINT。
注意
DIEPMSK和DOEPMSK是对所有端点的配置,不能对某一个端点使能某些类型中断,对另外一个端点使能其他类型的中断。
以上形成了金字塔式的总分式,层层控制的中断开关控制。
而中断的处理顺序是层层解析的方式,先通过
DAINT查看是哪个端点产生的中断,然后通过DIEPINTi 和DOEPINTi确认对应端点具体的中断类型。
DAINT的标志的置位和清除是跟着DIEPINTi 和DOEPINTi变化的,该寄存器软件只读。对应DIEPINTi 和DOEPINTi寄存器有标志置位则该寄存器对应位置位,DIEPINTi 和DOEPINTi寄存器所有标志位清除则该寄存器对应位清除。
而GINTSTS.OEPInt和GINTSTS.IEPInt是跟着DAINT的状态走的,软件也是只读的,DAINT对应位有置为则该位置位,DAINT中对应所有寄存器都清零,则该位也清零。
DIEPINTi 和DOEPINTi寄存器的标志都是写1清零。所以对于IN和OUT中断的标志清除,只需要对这两个寄存器对应位写1清零即可。DAINT和GINTSTS.OEPInt和GINTSTS.IEPInt是根据上述寄存器状态硬件清除无需软件手动清。
对比DIEPMSK和DIEPINTi可知,DIEP的NYET,Bble,PktDrp,TxFEmp是不受MASK控制的。
对比DOEPMSK和DOEPINTi可知,DOEP的StupPktRcvd,PktDrpSts是不受MASK控制的。
对比如IN和OUT中断类型有一些相同的,有一些不一样的,比如OUT多了Setup等。
拓扑结构如下
前面提到控制传输对应端点0,和其他端点没什么本质区别。其中断状态主要关注
DIEPINTn和DOEPINTn两个寄存器以确定具体的中断类型。
前面回顾了控制传输有3个阶段,所以对于驱动编写来说重点要知道当前处于什么阶段以决定下一步软件要怎么做,所以软件一般使用状态机记录当前状态然后根据中断标志确定下一个阶段。从上面可以看到DIEPINTn和DOEPINTn两个寄存器有很多的中断类型,并不是所有的都和控制传输的阶段有关的,控制器设计时就考虑到了这一点,尽量精简标志的逻辑,通过尽可能少的标志组合可以确定当前阶段。
手册《10.3.1 Interrupt Handling》中做了一个总结,
软件只需要关心以下标志即可
DIEPINTn.XferCompl 如果描述符中IOC设置,表示IN端点对应的描述符处理完
DIEPINTn.InTknTxfEmp TxFIFO空时收到了主机发的IN令牌,此时设备没有数据可回
DOEPINTn.XferCompl 如果描述符中IOC设置,表示OUT端点对应的描述符处理完
DOEPINTn.SetUp 表示控制器在Setup包后收到了IN和OUT令牌。
DOEPINTn.StsPhseRcvd 控制写传输主机切换到了状态阶段,此时设备需要发状态包(0长包)
当控制器看到需要为OUT端点设置多个中断位时,控制器会对这些中断设置进行一些优化,减少中断的有效组合的数量,以简化应用程序处理。
控制器对中断处理的优先级如下
DOEPINTn.XferCompl>DOEPINTn.StsPhseRcvd(SI)>DOEPINTon.SetUp.
基于此,应用程序只需要解码下表中所示的OUT端点的中断组合:
场景 | StsPhseRcvd (SI) | SetUp (SPD) | XferCompl (IOC) | 说明 |
---|---|---|---|---|
A | 0 | 0 | 1 | 控制器更新了描述符,软件需要查询描述符的SR位以确定数据是SETUP包还是OUT传输的数据。 |
B | 0 | 1 | 0 | 前面收到了SETUP包且Setup阶段完成,即收到了Setup包的内容。 |
C | 0 | 1 | 1 | 控制器更新了描述符,对应Setup包。且Setup阶段完成。 |
D | 1 | 0 | 0 | 控制OUT传输,主机切换到了状态阶段。 |
E | 1 | 0 | 1 | 在A的基础上且主机切换到了控制写的状态阶段,即D+A. |
总结一下就是B和C确定当前处于Setup完成阶段,根据Setup包内容软件决定后面是发数据还是读数据,还是回状态包(无数据)。
D和E确定当前处于控制写的状态阶段,设备需要回0长包。
A需要根据描述符的SR位以确定当前是收到的SETUP包还是OUT数据包。
本篇重点介绍了中断相关寄存器,以及OUT端点相关中断的组合以确定当前处于控制传输的什么阶段。两者都是驱动编写的重点需要理解的内容。下一篇我们继续介绍控制传输,软件的编写。
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !