换个免驱的方式。前面之所以能免驱动,是因为我走的是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