换个免驱的方式。前面之所以能免驱动,是因为我走的是USB HID协议。除了这个,更常见的是USB Mass Storage协议,就是我们常见的U盘走的协议。对于这个协议在《圈圈教你玩USB》中有详细介绍。下面就讲一下如何把我这个设备移植到MSD协议上。
首先说一下Firmware设计上的改变。
驱动数码管同样使用之前HID中的那种,用一个Timer不断循环点亮每一个数码管。因此,在Main.c中加入Timer0的初始化和中断服务程序。它会按照zBuf中给出来的依次点亮四个数码管。
volatile uint8 zBuf[]={1,2,3,4}; //用来保存8字节的输出报告。 /******************************************************************** 函数功能:定时器0初始化,用来做键盘扫描。 入口参数:无。 返 回:无。 备 注:无。 ********************************************************************/ void InitTimer0(void) { TMOD&=0xF0; TMOD|=0x01; ET0=1; TR0=1; } /******************************************************************** 函数功能:定时器0中断处理。 入口参数:无。 返 回:无。 备 注:22.1184M晶体约5ms中断一次。 ********************************************************************/ void Timer0Isr(void) interrupt 1 { static i=0; //定时器0重装,定时间隔为5ms,加15是为了修正重装所花费时间 //这个值可以通过软件仿真来确定,在这里设置断点,调整使两次运行 //时间差刚好为5ms即可。 TH0=(65536-Fclk/1000/12*5+15)/256; TL0=(65536-Fclk/1000/12*5+15)%256; // P2=1 << i; P1=zBuf[i]; i++; if (i==4) {i=0;} } /*******************************************************************/
PC对USB Mass Storage 设备传数据,使用的是 WRITE(10) 命令,具体的数据会在批量传输协议中的批量数据阶段传输给设备。具体对应在代码中 SCSI.C 中的 procScsiOutData 中,我们在这里将收到的输局赋值给 zBuf.
/******************************************************************** 函数功能:处理输出数据。 入口参数:无。 返 回:无。 备 注:无。 ********************************************************************/ void ProcScsiOutData(void) { uint8 Len; int i; //读端点2数据 Len=D12ReadEndpointBuffer(4,EP2_SIZE,Ep2Buffer); //LabzDebug_Start zBuf[0]=Ep2Buffer[0]; zBuf[1]=Ep2Buffer[1]; zBuf[2]=Ep2Buffer[2]; zBuf[3]=Ep2Buffer[3]; PrintHex(zBuf[0]); PrintHex(zBuf[1]); PrintHex(zBuf[2]); PrintHex(zBuf[3]); Prints("\r\n"); #ifdef DEBUG1 Prints("收到 \r\n"); for ( i=0; i <4;i++) { PrintHex(*(Ep2Buffer+i)); //如果需要显示调试信息,则显示发送的数据 if(((i+1)%16)==0)Prints("\r\n"); //每16字节换行一次 PrintHex(zBuf[0]); PrintHex(zBuf[1]); PrintHex(zBuf[2]); PrintHex(zBuf[3]); if((Len%16)!=0)Prints("\r\n"); //换行 } #endif //LabZDebug_End Ep2DataLength-=Len; //清除端点缓冲区 D12ClearBuffer(); //由于没有存储器,这里将缓冲区清0模拟数据处理 while(Len) { Ep2Buffer[Len]=0; //缓冲区清0 Len--; } //数据传输完毕,进入到状态阶段 if(Ep2DataLength==0) { //此时Ep2DataLength为0,并且处于数据阶段,调用发送数据函数将返回CSW Ep2SendData(); } }
只要做这一点Change,Firmware即可支持。
其次说一下上位机程序的设计。
因为模拟成一个U盘,因此,按照访问硬盘的方式即可传输数据。先是使用CreateFile打开PhysicalDriverX (特别注意:从PhysicalDriver0开始),之后就用 WriteFile 进行写入,特别注意,写入的最小单位是512字节,不能小于这个值。最后CloseHandle关闭即可。
最初,程序设计出来是这样的
CreateFile(PhysicalDrive3)
while Keypressed=false
{
WriteFile (512 Byte)
}
CloseHandle
结果发现非常奇怪,只有第一次能够顺利写入512Bytes,之后会不断出现 Error 5 ERROR_ACCESS_DENIED 5 (0x5) Access is denied. 的问题。为了防止是我设备不稳定造成的,我还用一个U盘进行试验(特别注意,操作会导致U盘内容不可读!!!)在Windows 7 x64和WindowsXP 32 都有同样的现象。请教天杀,他回复“想起来了,Win7的确有这个问题,可以写分区表,但是不能写后续的分区位置,是会被拒绝的。要写数据必须通过分区去写,或者将分区删除。你可以把设备识别成USB HDD,然后没有分区,这样应该可以全盘去写。如果是没有分区的那种U盘形式,是可能会被禁止写入的。”
这样的话,第一个方案就出来了,每次都要用CreateFile打开硬盘,写入之后再Close。
#include "stdafx.h" #include <windows.h> #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <time.h> int main(int argc, char *argv[]) { HANDLE hFile; BOOL fSuccess; char txchar[512]; DWORD iBytesWritten; byte Seven_Dig[10]={ 0x40, // = 0 0x79, // = 1 0x24, // = 2 0x30, // = 3 0x19, // = 4 0x12, // = 5 0x02, // = 6 0x78, // = 7 0x00, // = 8 0x10, // = 9 }; int n=1234; while (kbhit()==0) { hFile = CreateFile(L"\\\\.\\PhysicalDrive2", GENERIC_WRITE, FILE_SHARE_WRITE, // must be opened with exclusive-access NULL, // no security attributes OPEN_EXISTING, // must use OPEN_EXISTING 0, // not overlapped I/O NULL // hTemplate must be NULL for comm devices ); if (hFile == INVALID_HANDLE_VALUE) { // Handle the error. printf ("CreateFile failed with error %d.\n", GetLastError()); system("PAUSE"); return (1); } txchar[0]=Seven_Dig[n / 1000 % 10]; txchar[1]=Seven_Dig[n / 100 % 10]; txchar[2]=Seven_Dig[n / 10 % 10]; txchar[3]=Seven_Dig[n %10]; for (int i=1;i<512 /4;i++) { txchar[i*4]=txchar[0]; txchar[i*4+1]=txchar[1]; txchar[i*4+2]=txchar[2]; txchar[i*4+3]=txchar[3]; } fSuccess=WriteFile(hFile, &txchar, 512, &iBytesWritten,NULL); printf ("done send %d bytes.Status [%d] \n", iBytesWritten,fSuccess); printf ("Get lasterror %d\n", GetLastError()); printf("%d \n",n); n=(n++)%10000; FlushFileBuffers(hFile); Sleep(1000); CloseHandle(hFile); }//while (kbhit()==0) system("PAUSE"); return (0); }
第二个方案就是每次用WriteFile写入之后,我们再重置指针,再从最开始写入。类似下面的方案。
CreateFile(PhysicalDrive3)
while Keypressed=false
{
SetFilePointer(hFile,0,0,FILE_BEGIN);
WriteFile (512 Byte)
}
CloseHandle
实际的效果和之前的HID的方式相同。
Firmware下载 USB7SegFW
第一种上位机程序下载
exp1
第二种上位机程序下载
exp2