#include <stm32f10x_lib.h>
#include "usbreg.h"
#include "usbcore.h"
#include "usbuser.h"
#include "usbcfg.h"
#include "usb.h"
#include "usb_hw.h"
#include "usbdesc.h"
#include "hid.h"
#include "hiduser.h"
#define _DEBUG_
#include "debug.h"
#pragma diag_suppress 111,1441
//用来指示USB设备的状态
WORD USB_DeviceStatus;
//用来存储设备的地址
BYTE USB_DeviceAddress;
//用来存储USB当前使用的设备的配置
BYTE USB_Configuration;
//此配置使用端点
DWORD USB_EndPointMask;
//用于标志此端点是否已经被停止0~15依次代表15个端点
DWORD USB_EndPointHalt;
//此配置使用的接口数
BYTE USB_NumInterfaces;
//每个接口可用的当前接口可替换值
BYTE USB_AltSetting[USB_IF_NUM];
/*用于临时存储控制传输时的数据包*/
USB_SETUP_PACKET SetupPacket;
/*用于向主机发送数据的EP0的数据结构*/
USB_EP_DATA EP0Data;
/*EP0Buf用于向USB发送数据时用的缓冲区*/
BYTE EP0Buf[USB_MAX_PACKET0];
/*功能:复位USB的一些数据标志
*参数:无
*返回值:无
*/
void USB_ResetCore(void)
{
//默认为总线供电,因为我们的USB现在不都是插在电脑上才能工作的么&^_^
USB_DeviceStatus = USB_POWER;
//在枚举之初地址当然是0
USB_DeviceAddress = 0;
//配置描述符的标识从1开始,这里也先置为0
USB_Configuration = 0;
//目前使用的是端点0
USB_EndPointMask = 0x00010001;
//没有停止的端点
USB_EndPointHalt = 0x00000000;
}
/*功能:建立阶段,读取建立数据包
*参数:无
*返回值:无
*/
void USB_SetupStage(void)
{
USB_ReadEP(0x00,(BYTE*)&SetupPacket);
}
/*功能:建立阶段,In握手包
*参数:无
*返回值:无
*/
void USB_StatusInStage(void)
{
USB_WriteEP(0x80,NULL,0);
}
/*功能:建立阶段,Out握手包
*参数:无
*返回值:无
*/
void USB_StatusOutStage(void)
{
USB_ReadEP(0x00,EP0Buf);
}
/*功能:数据In阶段
*参数:无
*返回值:无
*/
void USB_DataInStage(void)
{
DWORD cnt;
//先计算引次要发送多少数据
if(EP0Data.Count > USB_MAX_PACKET0)
cnt = USB_MAX_PACKET0;
else
cnt = EP0Data.Count;
//这里写端点却主机读,则将Dir位置1
cnt = USB_WriteEP(0x80,EP0Data.pData,cnt);
EP0Data.pData += cnt;
EP0Data.Count -= cnt;
}
/*功能:数据Out阶段
*参数:无
*返回值:无
*/
void USB_DataOutStage(void)
{
DWORD cnt;
cnt = USB_ReadEP(0x00,EP0Data.pData);
EP0Data.pData+=cnt;
EP0Data.Count-=cnt;
}
/*功能:获取USB设备的状态
*参数:无
*返回值:TRUE --->成功
* FALSE --->错误
*/
__inline BOOL USB_GetStatus(void)
{
DWORD n,m;
switch(SetupPacket.bmRequestType.BM.Recipient)
{
//接收端是设备
case REQUEST_TO_DEVICE:
//返回设备状态给他
EP0Data.pData = (BYTE *)&USB_DeviceStatus;
//将状态信息发送给主机
USB_DataInStage();
break;
//接收端是接口
case REQUEST_TO_INTERFACE:
/*配置描述符的标识从1开始,并且请求的接口号不能大于接口的数目,因为接口数目从0开始
*因为我们接口描述符中的接口号是一个字节所以这里wIndex中的数据中人低字节有效
*/
if((USB_Configuration !=0)&&(SetupPacket.wIndex.WB.L < USB_NumInterfaces))
{
//清0两个字节,因为根据USB协议此处必须返回0
*((__packed WORD *)EP0Buf) = 0;
EP0Data.pData = EP0Buf;
//发送给主机
USB_DataInStage();
}
//此接口出现了错误
else
return FALSE;
break;
case REQUEST_TO_ENDPOINT:
//端点号高1位0方向,低4位为端点号
n = SetupPacket.wIndex.WB.L & 0x8f;
//m的高16位代表in端点的标志,低16位代表out端点的标志
m = (n&0x80)?(1<<16 )<< (n&0x0f) :(1<<n);
//如果配置标识不为0,或配置标识为0,但是是端点0时,才算正常
if((USB_Configuration !=0)||((n&0x0f)==0)&&(USB_EndPointMask & m))
{
//查看此端点是否已经停用
*((__packed WORD *)EP0Buf) = (USB_EndPointHalt &m )?1:0;
EP0Data.pData = EP0Buf;
//将数据发送给主机
USB_DataInStage();
}
//说明配置描述符出了问题
else
return FALSE;
break;
default:
return FALSE;
}
return TRUE;
}
/*功能:设置/清除USB设备的特征
*参数:sc
0----->清除
1----->设置
*返回值:TRUE --->成功
* FALSE --->错误
*/
__inline BOOL USB_SetClrFeature(DWORD sc)
{
DWORD n,m;
switch(SetupPacket.bmRequestType.BM.Recipient)
{
//接收端是设备,则清除或设置设备的特性
case REQUEST_TO_DEVICE:
if(SetupPacket.wValue.W == USB_FEATURE_REMOTE_WAKEUP)
{
if(sc)
{
printf("设置设备远程唤醒特性rn");
//设置USB状态为使能远程唤醒
USB_DeviceStatus |= USB_GETSTATUS_REMOTE_WAKEUP;
/*stm32硬件本身就支持远程唤醒,这里就不用设置了
*当然,软件支不支持在于对中断屏蔽位的设置
*/
}
else
{
printf("清除设备远程唤醒特性rn");
USB_DeviceStatus &= ~USB_GETSTATUS_REMOTE_WAKEUP;
/*stm32硬件本身就支持远程唤醒,这里就不用设置了
*当然,软件支不支持在于对中断屏蔽位的设置
*/
}
}
//不支持的请求
else
{
printf("不支持的请求rn");
return FALSE;
}
break;
//接收端是接口
case REQUEST_TO_INTERFACE:
//接口不支持清除特性
printf("清楚设置接口特征不被USB2.0支持rn");
return FALSE;
//接收端是端点
case REQUEST_TO_ENDPOINT:
//取得端点号
n = SetupPacket.wIndex.WB.L & 0x8F;
// printf("请求的端点是:");
// printhex(n);
// printf("USB_Configuration:");
// printhex(USB_Configuration);
// printf("USB_EndPointMask");
// printhex(USB_EndPointMask);
// printf("rn");
//将端点转化成与EndPointMask一样的格式
m = (n & 0x80)?((1<<16) << (n&0x0f)) : (1<<n);
/*根据USB协议来说,
*地址状态下只有端点0才能使用,
*配置状态下可用
*不知道他这句是不是bug
*原文件引用:
*This request is valid when the device is in the Address state;
*references to interfaces or to endpoints other than endpoint zero
*shall cause the device to respond with a Request Error
*/
/* 此处认为只用配置状态下的非0端点才能清楚特征 ,至于地址状态下没有处理
*
*/
if((USB_Configuration !=0) && ((n&0x0f)!=0) && (USB_EndPointMask & m))
{
//设置/清楚 某个端点stall状态
if(SetupPacket.wValue.W == USB_FEATURE_ENDPOINT_STALL)
{
//设置
if(sc)
{
printf("设置端点远程唤醒特性rn");
//设置请求的端点为stall
USB_SetStallEP(n);
//更新USB的状态位
USB_EndPointHalt |= m;
}
//清楚
else
{
printf("清楚端点远程唤醒特性rn");
USB_ClrStallEP(n);
USB_EndPointHalt &= ~m;
}
}
//未定义的请求
else
{
printf("不正确的端点请求rn");
return FALSE;
}
}
//不正确的请求时机
else
{
printf("不正确的请求时机rn");
return FALSE;
}
break;
//未定义的接收端类型
default:
printf("未定义的接收端类型rn");
printf("接收端为:");
printhex(SetupPacket.bmRequestType.BM.Recipient);
printf("rn");
return FALSE;
}
return TRUE;
}
/*功能:用于向主机发送描述符
*参数:无
*返回值:TRUE --->成功
* FALSE --->发生了错误
*/
__inline BOOL USB_GetDescriptor()
{
BYTE * pD;
DWORD len,n;
switch (SetupPacket.bmRequestType.BM.Recipient)
{
//发给设备的请求
case REQUEST_TO_DEVICE:
switch(SetupPacket.wValue.WB.H)
{
//获取设备描述符
case USB_DEVICE_DESCRIPTOR_TYPE:
printf("获取设备描述符rn");
EP0Data.pData = (BYTE *)USB_DeviceDescriptor;
len = USB_DEVICE_DESC_SIZE;
break;
//获取配置描述符
case USB_CONFIGURATION_DESCRIPTOR_TYPE:
printf("获取USB配置描述符rn");
pD = (BYTE *)USB_ConfigDescriptor;
/* 引用USB协议:The range of values used for a
* descriptor index is from 0 to one less than
* the number of descriptors of that type implemented by the device
* 配置描述符中的标识是从1开始计的,但是,请求中的值是标识值为配置描述符
*/
/*由于可能有几个配置描述符所以可能需要描述一下指针*/
for(n=0;n!= SetupPacket.wValue.WB.L;n++)
{
if(((USB_CONFIGURATION_DESCRIPTOR *)pD)-> bLength != 0)
{
pD+=((USB_CONFIGURATION_DESCRIPTOR *)pD)->wTotalLength;
}
}
//如果请求的描述符为空则返回stall
if(((USB_CONFIGURATION_DESCRIPTOR *)pD)->bLength == 0)
return FALSE;
//设置要传送的指针
EP0Data.pData = pD;
//虽然配置描述符中多了一个字节,但是wTotalLength中没有算上,所以这里不用管,直接用就行了
len = ((USB_CONFIGURATION_DESCRIPTOR*)pD)->wTotalLength;
break;
//字符串描述符
case USB_STRING_DESCRIPTOR_TYPE:
printf("获取字符串描述符rn");
EP0Data.pData = (BYTE *)USB_StringDescriptor + SetupPacket.wValue.WB.L;
len = ((USB_STRING_DESCRIPTOR *)EP0Data.pData)->bLength;
break;
default:
return FALSE;
}
break;
//请求接收端接口
// case REQUEST_TO_INTERFACE:
// printf("请求接收端接口rn");
// switch(SetupPacket.wValue.WB.H)
// {
// //Hid 描述符
// case HID_HID_DESCRIPTOR_TYPE:
// printf("请求HID描rn");
// break;
// case HID_REPORT_DESCRIPTOR_TYPE:
// printf("请求报告描述符rn");
// //这里只有一个报告描述符,报告描述符的索引是从0开始计,跟数组是一样的,如是不是0则返回Stall
// if(SetupPacket.wIndex.WB.L != 0)
// return FALSE;
// EP0Data.pData = (BYTE *)HID_ReportDescriptor;
// printf("报告描述符请求的字节:");
// printhex(EP0Data.Count);
// printf("报告描述符本来的长度:");
// printhex(HID_ReportDescSize);
// printf("rn");
// len = HID_ReportDescSize;
// break;
// default:
// printf("未识别的请求!!!###,类型是:");
// printhex(SetupPacket.wValue.WB.H);
// printf("rn");
// break;
// }
// break;
default:
return FALSE;
}
printf("获取描述符成功rn");
//将数据发送给主机
if(EP0Data.Count > len)
EP0Data.Count = len;
USB_DataInStage();
return TRUE;
}
/*功能:设置配置请求
*参数:无
*返回值:TRUE --- >成功
* FALSE --- >失败
*/
__inline BOOL USB_SetConfiguration(void)
{
USB_COMMON_DESCRIPTOR * pD;
DWORD alt,n,m;
printf("设置配置rn");
//配置值不能为0
if(SetupPacket.wValue.WB.L)
{
pD=(USB_COMMON_DESCRIPTOR *)USB_ConfigDescriptor;
//循环到配置描述符中的最后一个端点描述符
while(pD->bLength)
{
switch(pD->bDescriptorType)
{
//此时是配置描述符
case USB_CONFIGURATION_DESCRIPTOR_TYPE:
//如果此配置描述符是主机要设置的配置描述符
if(((USB_CONFIGURATION_DESCRIPTOR * )pD)->bConfigurationValue == SetupPacket.wValue.WB.L)
{
//保存起此时,设备所用的配置值
USB_Configuration = SetupPacket.wValue.WB.L;
//保存起此时,设备所用的配置的接口数目
USB_NumInterfaces = ((USB_CONFIGURATION_DESCRIPTOR*)pD)->bNumInterfaces;
//清0每个接口可替换设置值
for(n=0;n<USB_IF_NUM;n++)
{
USB_AltSetting[n]=0;
}
//我们将所有的以前可能使用的端点禁用,如果需要,在端点描述符中再使能就行了
//这样如果更改了配置就保证了,不会使用到以前配置使用,而本次配置没有使用的端点
//可能是为了防止干扰,猜的^-^!
for(n=1;n<16;n++)
{
if(USB_EndPointMask & (1<<n))
USB_DisableEP(n);
if(USB_EndPointMask & ((1<<16)<<n))
USB_DisableEP(n|0x80);
}
//还没有枚举到端点因此这里只使用的是端点0
USB_EndPointMask = 0x00010001;
//刚配置,当然没有停止的端点啊,全新的&_^
USB_EndPointHalt = 0x00000000;
/*在复位时,我们默认是总线供电,现在开始正式运行以前要因为主机会根据我们的配
*置描述符进行调整,一旦配置描述符返回后,配置描述符就生效了,所以这里必须来依据实际
*来重新设置
*/
if(((USB_CONFIGURATION_DESCRIPTOR *)pD)->bmAttributes & USB_CONFIG_SELF_POWERED)
USB_DeviceStatus |= USB_GETSTATUS_SELF_POWERED;
else
USB_DeviceStatus &= ~USB_GETSTATUS_SELF_POWERED;
}
//否则略过,下一个配置描述符
else
{
(BYTE *)pD += ((USB_CONFIGURATION_DESCRIPTOR *)pD)->wTotalLength;
continue;
}
break;
//下面是接口描述符
case USB_INTERFACE_DESCRIPTOR_TYPE:
alt = ((USB_INTERFACE_DESCRIPTOR *)pD)->bAlternateSetting;
break;
case USB_ENDPOINT_DESCRIPTOR_TYPE:
//我们仅仅需要处理多个子接口中的第一个就行了,(如果有子接口的话,没有的话刚好也是0)
if(alt == 0)
{
//得到此端点
n = ((USB_ENDPOINT_DESCRIPTOR *)pD)->bEndpointAddress & 0x8f;
//将端点号转化成bit以存储在USB_EndPointMask中去代表我们使用了此端点
m = (n&0x80)?((1<<16)<<(n&0x0f)):(1<<n);
USB_EndPointMask |= m;
//配置端点
USB_ConfigEP((USB_ENDPOINT_DESCRIPTOR *)pD);
//使能端点
USB_EnableEP(n);
//复位端点,使端点处于确定的端点
USB_ResetEP(n);
}
break;
}
(BYTE *)pD += pD->bLength;
}
}
//如果请求的配置为0,则我们将所有的配置恢复到地址状态时的配置
else
{
USB_Configuration = 0;
for(n=1;n<16;n++)
{
//如果上一个配置使用了此端点,则disable it
if(USB_EndPointMask & (1<<n)) //OUT端点
USB_DisableEP(n);
if(USB_EndPointMask & (1<<16)<<n) //IN端点
USB_DisableEP(n | 0x80);
}
//清除使用的全部端点,(当然0端点还是除外的)
USB_EndPointMask = 0x00010001;
USB_EndPointHalt = 0x00000000;
}
//我们根据USB_Configuration的值来查看我们到底时否找到了所需要的值
if(USB_Configuration == SetupPacket.wValue.WB.L)
{
printf("设置配置成功rn");
return TRUE;
}
else
{
printf("设置配置失败rn");
return FALSE;
}
}
/*功能:用于处理端点0的输入和输出请求函数
*参数: USB_EVT_SETUP 枚举时上位机发送的数据包
* USB_EVT_OUT 普通传输时,上位机发送的数据包
* USB_EVT_IN 普通传输时,上位机请求发送数据
*返回值:无
*/
void USB_EndPoint0 (DWORD event)
{
// printf("进入端点0处理函数rn");
switch (event)
{
//控制传输
case USB_EVT_SETUP:
// printf("进入建立阶段rn");
//首先读取主机发来的请求
USB_SetupStage();
//设置EP0的数据结构
EP0Data.Count = SetupPacket.wLength;
//识别是何请求
// printf("请求是");
// printhex(SetupPacket.bRequest);
// printf(" 被请求对象:");
// printhex(SetupPacket.bmRequestType.B);
// printf("rn");
//bRequestType.bit5~bit6代表请求类型
switch(SetupPacket.bmRequestType.BM.Type)
{
//标准请求
case REQUEST_STANDARD:
switch(SetupPacket.bRequest)
{
//获取USB状态
case USB_REQUEST_GET_STATUS:
//如果状态失败则停止0x81端点
if(!USB_GetStatus())
{
printf("获取状态失败rn");
goto stall_i;
}
break;
case USB_REQUEST_CLEAR_FEATURE:
//如果清除特征失败则停止0x81端点
if(!USB_SetClrFeature(0))
{
printf("清楚特征失败rn");
goto stall_i;
}
//成功了握一下手OK
USB_StatusInStage();
break;
case USB_REQUEST_SET_FEATURE:
//如果状态失败则停止0x81端点
if(!USB_SetClrFeature(1))
{
printf("设置特征失败rn");
goto stall_i;
}
//成功了握一下手OK
USB_StatusInStage();
break;
case USB_REQUEST_SET_ADDRESS:
switch(SetupPacket.bmRequestType.BM.Recipient)
{
//只支持向设备设置地址
/*USB协议指出必须在改地址前完成检测并对此信息包进行响应
*即USB的设置地址阶段必须在主机接收到0长度数据包后才能进行
*当主机接收到状态阶段的0数据包时将会产生接收中断
*所以放到了USB_EVT_IN处*/
case REQUEST_TO_DEVICE:
printf("设置地址rn");
USB_DeviceAddress = 0x80 | SetupPacket.wValue.WB.L;
USB_StatusInStage();
break;
default:
printf("未识别的接收端,接收端是");
printhex(SetupPacket.bmRequestType.BM.Recipient);
printf("rn");
goto stall_i;
}
break;
//获取设置描述符
case USB_REQUEST_GET_DESCRIPTOR:
if(!USB_GetDescriptor())
goto stall_i;
break;
//设置配置请求
case USB_REQUEST_SET_CONFIGURATION:
switch(SetupPacket.bmRequestType.BM.Recipient)
{
//对设备
case REQUEST_TO_DEVICE:
if(!USB_SetConfiguration())
{
//如果失败则对主机返回stall
goto stall_i;
}
//完成了请求,握个手OK
USB_StatusInStage();
break;
//不支持对其他类型请求
default:
goto stall_i;
}
break;
case USB_REQUEST_GET_INTERFACE:
printf("请求接口rn");
break;
default:
printf("未识别的请求,请求代码是");
printhex(SetupPacket.bRequest);
printf("rn");
break;
}
break;
//类请求
case REQUEST_CLASS:
printf("类请求rn");
switch (SetupPacket.bmRequestType.BM.Recipient)
{
//对接口请求
case REQUEST_TO_INTERFACE:
if(SetupPacket.wIndex.WB.L == USB_HID_IF_NUM)
{
switch(SetupPacket.bRequest)
{
case HID_REQUEST_GET_REPORT:
printf("获取报告描述符rn");
break;
case HID_REQUEST_SET_REPORT:
printf("设置报告描述符rn");
break;
case HID_REQUEST_GET_IDLE:
printf("获取空闲率rn");
break;
case HID_REQUEST_SET_IDLE:
printf("设置空闲rn");
break;
case HID_REQUEST_GET_PROTOCOL:
printf("查询报告协议是否在活动rn");
break;
case HID_REQUEST_SET_PROTOCOL:
printf("SetReprotrn");
break;
default:
printf("不支持的HID请求rn");
break;
}
}
break;
}
break;
//产商请求
case REQUEST_VENDOR:
//未定义请求
default:
stall_i:
USB_SetStallEP(0x80);
break;
}
break;
//接收成功
case USB_EVT_OUT:
printf("USB_EVT_OUTrn");
if(SetupPacket.bmRequestType.BM.Dir ==0)
{
printf(" 请求是:");
printhex(SetupPacket.bRequest);
printf("rn");
}
//返回握手
else
{
USB_StatusOutStage();
}
break;
//发送成功
case USB_EVT_IN:
// printf("USB_EVT_INrn");
if(SetupPacket.bmRequestType.BM.Dir == 1)
USB_DataInStage();
/*USB协议指出必须在改地址前完成检测并对此信息包进行响应
*即USB的设置地址阶段必须在主机接收到0长度数据包后才能进行
*当主机接收到状态阶段的0数据包时将会产生接收中断
*所以放到了USB_EVT_IN处*/
//原keil库没有加if判断,怀疑为bug,这里判断是否是由于设置地址的状态阶段成功而触发的中断
else// if(SetupPacket.bRequest == USB_REQUEST_SET_ADDRESS)
{
if(USB_DeviceAddress & 0x80)
{
USB_DeviceAddress &= 0x7f;
USB_SetAddress(USB_DeviceAddress);
}
}
break;
}
}
相关