USB 控制七段数码管(II)

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

发表评论

电子邮件地址不会被公开。 必填项已用*标注