三年前的文章之:PIC单片机和PDIUSBD12开发USB传输器备忘录

2019-04-15 13:53发布

选用的原因
为什么要选用PIC?居做硬件的来说,我们要接Flash,51引脚不够,所以挑了PIC。
方案的缺点
用PIC和D12作为搭配来开发USB数据传输器并不是个好注意,因为PIC对外部操作是通过I/O口来进行的,这样就要用软件来模拟51和D12的通信时序。而软件的执行是顺序的,如果晶振接4M,那么主频就是1M ,每条单周期指令要花费1us,而对D12如果是采用复合总线通信的话,一个outportb就要8条指令,约8us,是C51的两倍。
其次,由于D12的INT脚在上电后就是低电平,这时候main函数还没有启动,而PIC是边沿触发中断,所以第一个中断就无法捕捉到,为了保证程序正确性,就采用了轮询的方式。这也是影响速度的愿意之一。
字节顺序
有个小小的注意点,PIC是little-endian,C51是big-endian,USB是little-endian所以如果用周立功的程序来改的话,SWAP就不需要了。
PIC和D12的接口方式
A0是用来区别总线上是数据还是命令的,但是如果是复合总线的话,是由ALE来判别数据和命令的,所以可以把A0接高电平,但是我做了跳线,接低电平也可以,看了周立功的原理图,发现他们是接地的,只要接固定电平就可以了。而ALE则要接在MCU的I/O out上。
在开发中,由于PCB人员的马虎以及PIC引脚不是按照顺序排列的,头板把PIC PORT的引脚顺序和D12数据线的顺序接乱了,事后我才知道,比较胸闷,这样每次通信前,我都要把bit移位,要浪费不少时间,后来重新制板才纠正了。大家千万不要犯这样的错误。
对总线复位中断和suspend的处理
我看周立功的代码里,这两个处理都是空的,所以我干脆就删除了,因为我做成轮询的,太多判别会影响速度,后来我把endpoint 1 in/out的判别也都扔掉了
上电后的第一次中断处理
我跟踪了下,发现D12在上电后,如果在main里已经正确发送了reconnect的命令的话,D12会产生3个中断
第一个:bur reset中断,只要读D12中断寄存器,就可以清这个中断
第二个:suspend中断,不必去理会它,同上处理
第三个:endpoint 0 IN中断,是上位机要求D12传送device descriptor数据结构的请求
因为上位机在得到端口电平变化的信息后,就会发送这个请求,如果有应答,上位
机才真正认为有设备插入。
上位机 USB总线枚举的一些问题
只要在应答getdescriptor这个请求后,传送了数据的话,上位机就会提示安装驱动的向导,也就是在bushound中如果能看到getdescriptor后面有DI数据的话16byte,那么就应该有安装提示。至于是否能正确安装,就要看你的固件的PID和UID是否与驱动的inf文件中匹配。
值得注意的是,我听到经常有人说在枚举时候,出了问题。其中一位老兄的症状是:在设备插入后,goodlink的led灯亮了3下就不亮了。我也遇到了这种错误,后来我好好想了下,结果发现是我把字节顺序给搞混了,我把PIC当作big-endian了,所以无法正确识别请求类型。而goodlink的灯闪3下,是上位机发了3次getdescriptor,没有应答,它就认为不是真正有设备插入。
其次,在安装驱动时候,也经常有人出问题。驱动安装不上!我看了一位老兄的代码,他是自己发现自己的问题,问题似乎是point如果不指向rom的话,好象向上位机传送的数据就有错误。那么PID,UID肯定就不匹配了。于是那位老兄在传送前都指定了rom point。居他自己说是改正了。我是没碰到他类似的问题,我就是按照普通C语言中的指针来用,就可以正确安装驱动的。我在这里提出来,是希望大家如果以后遇到类似问题,可以试试这中方法。
MCU对D12操作时序
这个,如果光看D12的Datasheet是不够的,我觉得那里面说的不全面。因为我是从一点不懂开始做起的,所以一开始被D12的时序问题搞了一把。
后来,我看了赵明兄使用的MCU(51)的datasheet中的时序图才搞明白,咳,费了点时间摸索的。程序如下:注意,CS选通可别忘记了,PORT C默认要接成output,WR,RD接成高电
void outportb(unsigned char port,unsigned char val)

ALE = 0;//ALE low
ALE = 1;//ALE up
PORTC = port;//出地址
ALE = 0;//锁存地址

PORTC = val;//写数据
ASSERT_WR = 0;
ASSERT_WR = 1;

ASSERT_RD = 1;//RD = 0 active
}

unsigned char inportb(unsigned char port)
{
unsigned char bData;
TRISC = 0x00;
ALE = 0;//ALE low
ALE = 1;//ALE up
PORTC = 0x02;
ALE = 0;//ALE low

TRISC = 0xFF;
ASSERT_RD = 0;//RD = 0 active
bData = PORTC;
ASSERT_RD = 1;

TRISC = 0x00;
ASSERT_WR = 1;
return(bData);
}
大家看到了吧,用I/O模拟时序,要那么多指令,编译成汇编还不知道要多少呢,而51的话,大概在2个指令周期就能完成。到这里,我才真正理解为什么说vhdl里很多是并发的而软件程序是顺序执行的。
上位机程序
读一个文件,每64个字节为一个数据包,扔给驱动。这样做不如把数据扔给驱动去分割来的快,不过我想X86那么快,也无所谓了吧。。。在驱动里,每次提交URB也都是按照endpoint2 out的最大缓冲来发送的。已经是尽全力了。不过,应用程序最好每次提交的数据等于64字节,小于这个的话,总的传输时间会加长,超过了的话,就会丢包,除非驱动设计成能分割大于64字节的功能。
还有,我的驱动是用driverstudio写的,不是直接用DDK,不知道速度有多少差异。。。

最终数据传输速度
在PIC上,接了4M晶振,这样测试了下,传输83k字节的文件,大概用了5秒不到的
时间,也就是17k/s左右--------大家别笑了,我自己都觉得不好意思了,人家写USB协议的人那么辛苦,我就做出这么个鸟东西。。。。
我请教了做过的人,也是PIC+D12的,凭他的经验,最快可以达到100k/s,不过他使用的是8M晶振,我写这篇文章的时候,还是使用的4M,我明天去试下,如果各位有什么好的建议,也请告诉我。不过我觉得要达到200K/s是无论如何也不可能的。
我已经试过10M的晶振了,速度可以达到40K/s,看来的确是I/O 速度的问题