基于DWC2的USB驱动开发-抽丝剥茧再论SETUP完成标志DOEPINTn.SetUp
前言
https://www.elecfans.com/outside?redirect=https://mp.weixin.qq.com/s/gm5OAutfnYv6H_5Ce8GEqA
一文中我们介绍了控制传输,中断相关寄存器
,
尤其是
DOEPINTn.XferCompl
,
DOEPINTn.SetUp
,
DOEPINTn.StsPhseRcvd
几个状态的组合非常重要,因为由他们可以确定当前控制传输处于什么阶段。前文我们对此进行了介绍,但是个人觉得讲的还不够透彻,原理还没有讲清楚,知其然知其所以然是我们一开始就强调的,所以针对这个几个标志位我们再次更详细的介绍,先来介绍最关键的
DOEPINTn.SetUp
。
DOEPINTn.SetUp
Setup
是
DOEPINTn
寄存器中的一个标志,用于表示
Setup
阶段是否完成。
我们这里可能要问
DIEPINTn
为什么没有
SetUp
标志,因为
Setup
总是
HOST->DEV
的,所以总是
OUT
传输,所以总是对应
OUT
端点的,
IN
端点的寄存器不可能有。这里强调一下我们一定要多问为什么,先思考猜测,然后再从手册规格书中查找答案,只有这样才能加深理解,
USB
开发中这是很重要的,当然任何技术性的工作这一点都重要。
我们从寄存器的描述中也可以看到,该位表示
SetUP
阶段完成,并且只有控制
OUT
端点有。
还有个信息是本次控制传输没有连续的
SETUP
,意味着软件可以开始解析
SETUP
的
8
字节的内容以决定下一步要干嘛了。如果有连续的
SETUP
则是最后一个
SETUP
完成才能进行。
那么究竟
SETUP
完成对应的一个什么状态呢
?
硬件是怎么知道
SETUP
阶段完成的呢?主机和设备判断
SETUP
完成有什么区别吗
?
我们会抛出一连串问题,如果能抛出这些问题说明你很适合做底层和驱动开发
!
后面我们会抽丝剥茧,从原理到实践,从猜测到手册中求证,一层层剖析。
这里我们要回顾下控制传输,
我们从规格书中的拓扑图可以看到,控制传输的
SETUP
阶段过程如下
或者换个表达方式如下
或者从
USB
分析仪抓包数据来看,更形象。
SETUP
阶段对应
3
个包
:SETUP
令牌包;
DATA0
的数据包,始终是
HOST->DEV
;设备的响应
ACK
。
这里顺便提一下,
SETUP
的数据始终使用
DATA0;
而设备要么正常接收并接受
ACK
,要么接收错误或者没接收到不响应,只有这两种情况,不能接收了不接受而
NAK
或者
STALL
等。
这里还顺便提一下接收和接受的概念区别。接收通常指的物理层数据正确接收到了,而接受则指的软件能够处理,或者说愿意处理。
接收了也可能不接受。
这里协议层面我们可以看到
SETUP
阶段完成的标志是,设备
ACK
响应了。
如下所示
仔细思考下,这里只是协议上的定义,也就是对应的总线上的数据,至于
SETUP
完不完成是要由参与者
HOST
和
DEV
去确定的,换句话说总线上的状态并不意味参与者就是这个状态了,因为参与者还要正确接收到总线上的数据才算。
对于主机接收到了设备发送的
ACK
那么主机认为
SETUP
阶段完成,如果设备发了
ACK
但是主机没收到呢?那么主机认为
SETUP
没完成,会认为本次失败,从头发
SETUP
包重来。
同样的对于设备来说将
ACK
发送到总线上去后,设备并不知道
HOST
接收没接收到,设备并不知道主机的状态。所以主机和设备对
SETUP
完成的状态确认存在不同步。
那怎么办呢
?
设备并不知道主机认为
SETUP
完成了没有,因为设备并不知道主机收没收到
ACK
。就好比子非鱼安知鱼之乐,有点绕了。
其实也没什么办法,设备确实现在不知道主机是不是认为
SETUP
完成了,但是设备可以根据主机下一步的行动来确定。因为主机如果接收到了
ACK
,认为
SETUP
完成了那么主机下一步就会发
IN
或者
OUT
令牌进入数据阶段
(
或者状态阶段
)
,否则则会重发
SETUP
重新进入
SETUP
阶段。
设备可以据此来进行判断,如果收到了主机的
IN
或者
OUT
令牌,则确认主机认为
SETUP
已经完成了,于是设备也知道
SETUP
完成了,这里设备和主机判断
SETUP
完成的时间点是不一样的,这就是协议定义和实际判定存在的区别。所以对于协议设计一定要考虑这种区别,协议设计了要考虑如何实现,也就一直强调的理论要结合实践,对于驱动编写更需要考虑从这些细节。
以上恭喜你从头开始推导出了设备判断
SETUP
完成的原理,那么回到上面的标志位
DOEPINTn.SetUp
即表示
SETUP
阶段完成,
DWC2
控制也是这么判断的吗?所谓英雄所见略同,
DWC2
控制器还真是这么判断的。
上面寄存器的描述中只提了该标志的含义,并没提其具体置位的原理和时机,实际编程指导手册中是有说明的。
如下位置,手册就进行了说明当,
SETUP
令牌之后,接收到了
IN
或
OUT
令牌则置位
DOEPINTn.SetU
,和我们推导的完全一致。
为什么是
IN
或
OUT
呢,因为控制传输可能是控制写,控制读,还可能无数据阶段的控制传输,所以后面要不就是
IN
数据或者
OUT
数据或者
IN
状态,如下所示
控制写
控制读
无数据阶段的控制传输
驱动编写注意事项
从上面可以看到设备是延迟才能确认
SETUP
完成的,也就是在
SETUP
后设备收到了主机发的
IN
和
OUT
之后才确认,中断也是在此时产生。
对于控制写,上面设备确认
SETUP
完成产生中断,中断服务函数开始去解析
8
字节的
SETUP
内容,来确定数据阶段要干嘛,后面数据阶段是
IN
还是
OUT
或者还是没有数据阶段直接状态阶段,其实是数据阶段
(
状态阶段
)
已经启动,设备延迟了。
此时中断服务函数还未执行,软件还没根据解析准备
IN
或者
OUT
描述符,所以此时对于这个
IN
或者
OUT
,硬件只能自动
NAK
,如下所描述
:
所以这种情况下,中断服务函数中进行了解析,并知道要准备
OUT
描述进行接收时,设置
EPEna=1
时还要同时设置
CNAK=1
,清除硬件的自动
NAK
状态,让硬件根据
OUT
描述符去接收数据。
对于控制读后面是
IN
数据阶段,或者无数据阶段的控制传输后面是
IN
状态,也是类似的。
总结
以上对
DOEPINTn.SetUp
进行了详细的解析,因为它太重要了,
SETUP
阶段是控制传输的核心,因为只有
SETUP
完成才能去解析
8
字节的内容,才能决定后续干嘛,对于驱动编写接收到
8
字节的
SETUP
数据是第一个关键步。以上也可以看出一定要从原理上去理解,这也是驱动编写的重要原则,必须知其然知其所以然,精确的知道各个时间点的各个时间,以及其条件等,注意精确两个字。