简单记录一下USB的DP与DM状态。
RESET,DP、DM都拉低,并维持20MS以上。
SUSPEND,处于IDLE状态DP为高,DM为低,即连续三次没有等到SOF。
RESUME,DP为低、DM为高,并维持20MS以上。
在做USB的Host时,有几个地方需要注意:
1、在做插拔简测时,检测到DP没有上拉时间,判定设备掉线的时间一定要远远大于RESET的时间。因为在RESET的时候,DP可能拉低很长一段时间,会造成拔出误判;
2、在整个USB的枚举及通信过程,不要去随意修改DP、DM的上下拉电阻,可能会对设备制造一些RESUME误判等。
3、在检测到设备插入时,最好能第一时间对设备进行RESET。
4、在发送第一条命令之前,确认发送足够的SOF,以便部分芯片内部的初始化。
5、SCSI命令的超时时间,建议采取Windows的标准,也就是10s。
IAR嵌汇编真是有些危险,尤其是关于堆栈的操作,经常就被“优化”了。
案例一:多中断程序的入口处有压栈保护操作,结果被优化生子程序来进行调用;
案例二:出口的压出栈操作不一样的,也被优化到一起去了。
IAR中要嵌汇编,千万要把优先级调低!
在KeilC的选项中,C51这一项里有一项叫做“Don't use absolute register accesses”。我们已经因为这一项出了三次Bug了。
那么这一项有什么作用呢?
在KeilC默认中,这一项并没有选上,表示KeilC将使用寄存器的绝对定位来优化程序。什么意思呢?我们知道,51中有四组通用寄存器。在寄存器组0的时候,R0-R7实际上就映射到了0x01-0x07这段地址。也就是说有时候为了优化,KeilC将会使用0x01-0x07这样的绝对地址。举个简单的例子吧,比如说我们需要将寄存器R5的值存入R7,正常我们会这样写:
MOV A, R5
MOV R7, A
但是如果使用寄存器的绝对定位呢?我们就可以这样写了:
MOV R7, 0x05
节省了一条指令的执行时间!
当然,正常时候这样使用当然没有任何问题。然而,如果有一个中断的服务程序,我们使用了寄存器组1呢?这时候,R5就不再是0x05了,而是0x0D!因此,Bug就出来了。
当然,我们可以在这个中断服务程序上所调用的所有子程序中加在using 1的关键字来解决这个问题。不过这样还是带来了一些不便,更有一些时候,一些子函数是多处调用了。因此,有时我们一般都直接勾选了这个选项来取消KeilC的这项优化。
在C51中,KeilC与IAR在函数参数的传递及返回值的处理上,有着较大的不同。本文主要小结不同,并稍稍发表Hanny个人对这些方式的优缺点的看法。
首先,我们对数据类型进行分类。根据数据类型的长度,我们可以将数据简单分为:bit, u8, u16, u32。其中,bit表示位变量;u8主要为char、signed char、unsigned char型变量;u16主要为short、usigned short、int、unsigned int、point型变量;u32主要为long、unsigned long、float型变量。
然后,先介绍一下函数参数的传入。
首先是bit,KeilC采用的是位寻址区的变量来进行bit型参数的传入。而IAR采用的是B寄存器来传入。
接着是u8,KeilC主要采用R7、R5、R3来进行参数的传入。也就是说:当函数的参数为一个时使用R7,两个时使用R7和R5,三个时就使用R7、R5及R3。同样的,IAR主要采用R1、R2、R3、R4、R5来进行u8型参数的传递。
紧接着是u16,KeilC主要采用R6:R7、R4:R5、R2:R3来进行传入,而IAR采用R3:R2、R5:R4来进行传入。
再接着就是u32,KeilC主要采用R4:R7来进行传入,IAR采用R5:R2来进行传入。
最后,KeilC还支持一种叫做通用数据指针的数据类型。主要采用R2:R1、R3来传入。其中,R2:R1存储指针地址,R3存储内存类型。
然后再说说返回值。
返回值为bit时,KeilC与IAR都用C来传出。
返回值为u8时,Keil采用R7,而IAR采用R1。
返回值为u16时,Keil采用R6:R7,而IAR采用R3:R2。
返回值为u32时,Keil采用R4:R7,而IAR采用R5:R2。
下面是Hanny的个人小结:
KeilC的函数传入传出都是秉承KeilC的大端思想,主要用到的寄存器为R7-R2;IAR的函数传入传出是秉承小端思想,主要用到的寄存器为R1-R5。
KeilC函数参数传递的优点是:在从u8向u16、u32扩展时,由于低位所处的寄存器位置不变,扩展时对代码的修改较小。
IAR的优点是:在传入的参数位数为u8时,更紧凑的方式能够传入更多的参数。
相比之下,Hanny更喜欢KeilC这种方式。