📄 usb.c
字号:
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 + -