⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usb.c

📁 PDIUSB12+51做 U盘程序,可以将自己的系统单做U盘跟 电脑通信
💻 C
📖 第 1 页 / 共 4 页
字号:
	Delay(100);
}

void Connect_Usb(void)
{
	Write_Usb_CMD(0XF3);		//设置USB—D12的模式命令;连续写两个字节。第一个为配置信息,第二个为分频系数。
    Write_A_Usb_DAT(0x1E);		//7-6=00:模式0,非同步模式;5=0:保留,写0;4=1;连接;3=1:所有都报告,2=1;1=0;0=0。
	Write_A_Usb_DAT(0x47);		//分频系数
}

void Init_Usb()
{
	Set_Usb_Addr(0);	//设置USB地址0。
	Set_Endp_Enable();	//默认端点使能。
}

/***********************************************************************************]
以下是一些关于USB协议操作中的函数,也就是说,如果要进行USB通讯,就必须有以下这些函数
*************************************************************************************/


/*************************************************************************************
USB总线复位处理,如果读中断寄存器之后,标志中的RESET=1,则说明主机发出的要求是要求总线复位,
那么此时就可以调用次函数来进行总线复位
*************************************************************************************/
void Usb_Bus_Reset(void)
{
	usb_flags.Register=0;
	Set_Endp_Enable();
	SCSI.Status.Command=1;	//SCSI命令状态为1
	SCSI.Status.Data=0;		//SCSI数据状态为0
 	csw.dCSWSignature=0x55534253;	
}

void Usb_Bus_Suspend(void)                       //USB总线挂起处理
{
}


void Set_Usb_Address(void)		//设置USB设备地址
{
	 usb_flags.flags.set_addr=1;  //如果调用了此函数,则说明地址已经设置,那么次标志为置1。
	 while(Chioce_Endp(1)&0x01);//如果缓冲区满,则循环,等待缓冲区空,如果缓冲区空,则继续执行
	 Write_Endp_Buffer(1,0,0);		//写端点0输出缓冲区,数据长度为0,为什么????可以先注销了试试
	 Set_Usb_Addr(Control_Data.DeviceRequest.wValue); //设置地址,该地址有主机分配,通过单片机读取D12的端点0输出缓冲区得到,然后配置到D12的地址寄存器中
	
	 OutputS("设备地址为: ");
	 OutputH(Get_MSB(Control_Data.DeviceRequest.wValue));
	 OutputH(Get_LSB(Control_Data.DeviceRequest.wValue));
	 OutputS("\n\r");
	
	 usb_flags.flags.usb_endp0_in=0;	   //端点0输入标志清0,
	 usb_flags.flags.setup_packet_out=0; 	//端点0输出标志清0	
}

void Get_Status(unsigned char receiver)	 //获取状态,根据bmRequestType的不同(主机发出的),需要提供的状态可能是:设备的、接口的或者端点的
{
 	unsigned char status[2];		  //定义一个状态数组。
 	switch (receiver)
  	{
   		case 0:                        //获取设备状态
   	 	status[0]=0x00;
   		status[1]=0x00;
    	break;
   		case 1:                        //获取接口状态
    	status[0]=0x00;
    	status[1]=0x00;
    	break;
  	 	case 2:                        //获取端点状态
    	status[0]=0x00;
    	status[1]=0x00;
    	break;
   }
  	Write_Endp_Buffer(1,2,status);	//发送返回数据到端点0输出缓冲区,即给主机返回它所需要的状态
  	usb_flags.flags.usb_endp0_in=0;	//清除端点0输入标志
}

void Clear_Feature(unsigned char receiver)	//清除特性,主机不需要设备返回任何数据,也就是对端点0输入缓冲区写0个字节就可以了
{
 	receiver=0;					   
 	Write_Endp_Buffer(1,0,0);		 	//写0字节
 	usb_flags.flags.usb_endp0_in=0;	 	//清除端点0输入标志
 	usb_flags.flags.setup_packet_out=0;	//清除端点0输出标志
}

void Set_Feature(unsigned char receiver)   //设置特性,注释:略
{
 	receiver=0;					   
 	Write_Endp_Buffer(1,0,0);	 	//写0字节
 	usb_flags.flags.usb_endp0_in=0;	 	//清除端点0输入标志
 	usb_flags.flags.setup_packet_out=0;	//清除端点0输出标志
}

void Set_Descriptor(void) //设置描述符请求,在这里暂时不用,以后慢慢深入研究,但是需要清楚标志
{
	usb_flags.flags.usb_endp0_in=0;	 	//清除端点0输入标志
	usb_flags.flags.setup_packet_out=0;	//清除端点0输出标志	
}

void Set_Configuration(void)	//
{
	Write_Endp_Buffer(1,0,0);		 	//写0字节
 	usb_flags.flags.usb_endp0_in=0;	 	//清除端点0输入标志
 	usb_flags.flags.setup_packet_out=0;	//清除端点0输出标志
}

void Set_Interface(void)	//
{
	Write_Endp_Buffer(1,0,0);		 	//写0字节
 	usb_flags.flags.usb_endp0_in=0;	 	//清除端点0输入标志
 	usb_flags.flags.setup_packet_out=0;	//清除端点0输出标志
}																															  
void Get_Configuration(void)		 //主机通过此函数可以得到设备当前的配置值
{
	Write_Endp_Buffer(1,1,&con_int_endp_descriptor.configuration_descriptor.bConfigurationValue);
	usb_flags.flags.usb_endp0_in=0;	
}

void Get_Interface(void)	 //主机通过此函数可以得到当前的某个接口的接口描述符编号
{
	Write_Endp_Buffer(1,1,&con_int_endp_descriptor.interface_descritor.bAlternateSetting);
	usb_flags.flags.usb_endp0_in=0;	
}

void Get_Max_LUN(void)                   //获取磁盘最大逻辑单元号
{
 unsigned char max_LUN=MAX_LUN;          //只有一个逻辑单元,这里返回给主机,说明只有一个盘,如果有多个,则可以定义为1、2、3...
 Write_Endp_Buffer(1,1,&(max_LUN));
 usb_flags.flags.usb_endp0_in=0; 
}

void Mass_Storage_Reset(void)            //USB大容量存储设备复位
{
 Write_Endp_Buffer(1,0,0);
 usb_flags.flags.usb_endp0_in=0;
 usb_flags.flags.setup_packet_out=0;
 SCSI.Status.Command=1;
 SCSI.Status.Data=0;
}
void Get_Descriptor(void)
{
	if(!usb_flags.flags.not_end)
	{	
		switch(Get_MSB(Control_Data.DeviceRequest.wValue))	//DeviceRequest.wValue的高位中放着主机需要得到的描述符类型编号
		{
			case	DEVICE_DESCRIPTOR:	Control_Data.wCount=sizeof(DEVICE_DESCRIPTOR_STRUCT);	 //Control_Data.wCount存放设备描述符结构体的数据大小
 	        							Control_Data.pData=(unsigned char *)(&device_descriptor); //Control_Data.pData指向设备描述符首地址
										OutputS("得到设备描述符\n\r");
			break;
			case	CONFIGURATION_DESCRIPTOR:	Control_Data.wCount=SWAP(con_int_endp_descriptor.configuration_descriptor.wTotalLength);	 //应该是所有(配置、接口、端点)描述符大小的总和
												Control_Data.pData=	(unsigned char *)(&con_int_endp_descriptor);//指向配置、接口、端点描述符集合的首地址
												if(Control_Data.wLength<Control_Data.wCount)
													Control_Data.wCount=Control_Data.wLength;//如果主机需要返回的数据长度小于整个长度,则只返回主机需要的长度的数据就可以了
												  OutputS("得到配置描述符\n\r");
			break;
			case	STRING_DESCRIPTOR: if(Get_LSB(Control_Data.DeviceRequest.wValue)==0)//需要告清楚
			  							{
			   								Control_Data.wCount=LANGUAGE_ID[0];
               								Control_Data.pData=LANGUAGE_ID;
			  							}
		     							if(Get_LSB(Control_Data.DeviceRequest.wValue)==2)  //需要告清楚
			  							{
			   								Control_Data.wCount=device_serial_number[0];
			   								Control_Data.pData=device_serial_number;
			  							}
										OutputS("得到字符串描述符\n\r");
			break;
			
	   	}

      if(Control_Data.wLength<Control_Data.wCount)Control_Data.wCount=Control_Data.wLength;		//主机需要多少数据,就发送多少数据到端点0输入缓冲区中。多余的数据不管
	 		
	}
		 //这里需要搞清楚如果主机需要的数据大于16字节,分多次发送的具体过程是怎么样的?
	 if(Control_Data.wCount>=MAX_CONTROL_DATA_SIZE)			 //由于D12的端点缓冲区为16字节,所以一次只能发送16字节数据,如果数据大于16字节,则分多次发送
     {
	  	Write_Endp_Buffer(1,MAX_CONTROL_DATA_SIZE,Control_Data.pData);
	  	Control_Data.pData+=MAX_CONTROL_DATA_SIZE;	   //指针指向第17字节
      	Control_Data.wCount-=MAX_CONTROL_DATA_SIZE;	  //需要发送的数据已经发送了16字节,所以这里带发送的数据字节数要减掉16
	  	if(usb_flags.flags.set_addr)usb_flags.flags.not_end=1;
	   	else usb_flags.flags.usb_endp0_in=0;
	  	return;
	 }
    else
     {
	  	Write_Endp_Buffer(1,Control_Data.wCount,Control_Data.pData);
      	usb_flags.flags.setup_packet_in=0;
	  	usb_flags.flags.usb_endp0_in=0;
	  	return;
	 }   	
}


void Endp0_Out(void)                            //端点0输出(主机到设备)中断处理 ,也就是单片机需要接收来自主机的信息并设置标志位
{
//  unsigned char i;
 Last_Status.Register=Read_Endp_Last_Status(0); 		//先读取D12的端点0输出最后状态寄存器,并将数据放入单片机内部定义的最后状态寄存器Last_Status.Register中,以下的操作基于此
 //OutputC(Last_Status.Register);	 //状态显示:串口
 //OutputS("\n\r");	 
 if(Last_Status.Status.setup_packet)			//如果D12最后成功接收的信息有一个SETUP包,则进行下面一系列处理
  {
   OutputS("端点0最后成功接收的信息有SETUP包\n\r");
   Control_Data.wLength=0;						//数据长度为0,即无数据
   Control_Data.wCount=0;						//???
  
   if(Read_Endp_Buffer(0,sizeof(Control_Data.DeviceRequest),(unsigned char *)(&(Control_Data.DeviceRequest)))!=sizeof(REQUESTCMD)) //读取端点0输入寄存器,如果读取失败(返回长度与参数中的长度不相等),则继续,否则跳过
    {	 //如果读取失败
	 Set_Endp_Status(0,0);	//停止端点0输入
	 Set_Endp_Status(1,0);	//停止端点0输出	,这两个停止,当D12接收到一个SETUP包时,自动解除停止状态
	 OutputS("读取数据包失败\n\r");
	 return;					//如果失败则直接返回,进入主程序循环
	}
	OutputS("读取数据包成功\n\r");
	//如果读取成功,则对读取的内容进行调整。
//	OutputC(Control_Data.DeviceRequest.bmRequestType);
 // OutputC(Control_Data.DeviceRequest.bRequest);
  Control_Data.DeviceRequest.wValue=SWAP(Control_Data.DeviceRequest.wValue);
  Control_Data.DeviceRequest.wIndex=SWAP(Control_Data.DeviceRequest.wIndex);
  Control_Data.DeviceRequest.wLength=SWAP(Control_Data.DeviceRequest.wLength);
//  i=Get_LSB(Control_Data.DeviceRequest.wLength);
  //	OutputC(i);
  //同时建立应答包	
  Ack_Setup(0);	//对端点0输入建立
  Ack_Setup(1);	//对端点0输出建立
  Control_Data.wLength=Control_Data.DeviceRequest.wLength; //将需要传输的数据大小附值给Control_Data.wLength,有可能为0,即不需要传输数据
  usb_flags.flags.not_end=0; //	   未结束标志;如果此位为1,表示此次操作结束,如果为0,表示没有结束。
  usb_flags.flags.usb_endp0_in=1;  //说明主机有输入请求,此时单片机内部标志寄存器该为置1,且需要根据请求类型对端点0输入缓冲区写相应数据(如果Control_Data.wLength=0,则写0字节)
  usb_flags.flags.setup_packet_in=0; //由于此时还不知道是什么类型的请求,所以输入输出标志均为0,并在接下来的语句中做判断
  usb_flags.flags.setup_packet_out=0;
  if(Control_Data.DeviceRequest.bmRequestType&0x80){usb_flags.flags.setup_packet_in=1; return;}//如果bmRequestType[D7]=1,说明接下来是一个输入包(从设备到主机),并将该为置1,在输入事务中需要用到次标志
  else {usb_flags.flags.setup_packet_out=1;return;}	//否则就是一个输出包(主机到设备)
 }
 else	  //如果D12最后成功接收的信息没有SETUP包,则选择端点0输入,清缓冲区(与该函数中第一个IF对应)
  {
   OutputS("端点0最后成功接收的信息没有SETUP包\n\r");
   Chioce_Endp(0);
   Clear_Buffer();
  }
}


void Endp0_In(void)
{
	Read_Endp_Last_Status(1);
	if(usb_flags.flags.setup_packet_in||usb_flags.flags.not_end)	  //如果是从设备到主机的输入事务,则继续执行下面的
	{
		OutputS("设备到主机事务\n\r");
		if((Control_Data.DeviceRequest.bmRequestType==0xA1)&&(Control_Data.DeviceRequest.bRequest==0xFE))//这句不明白?????
    	{
			Get_Max_LUN();	  //得到最大逻辑号?
			OutputS("得到最大逻辑号\n\r");
		}
		switch(Control_Data.DeviceRequest.bmRequestType&0x7B)//检测第一位和最后两位(原程序为0X7B),即检测从外设到主机的输入事务
		{
			case	0:	switch(Control_Data.DeviceRequest.bRequest)		 //这里为0表示:bmRequestType=10000000,即数据从设备到主机,接收着为设备(不是接口和端点),即在这种情况下,所有有关设备的都应该列举出来
						{
							case	0:	Get_Status(0);OutputS("得到设备状态\n\r");break;//这里的参数0表示发送设备的状态(参数0就表示设备)
							case	6:	Get_Descriptor();break;//得到描述符  OutputS("得到描述符\n\r");
							case	8:	Get_Configuration();OutputS("得到配置\n\r");break;//得到配置
							default:break;		
						}
			case	1:	switch(Control_Data.DeviceRequest.bRequest)//这里为1表示:bmRequestType=10000001,即数据从设备的接口到主机,接收着为接口(不是设备和端点)
						{
							case	0:	Get_Status(1);OutputS("得到接口状态\n\r");break;//这里的参数1表示发送的是接口的状态	
							case	10:	Get_Interface();OutputS("得到接口\n\r");break;
							default:break;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -