关于stm32的USB学习笔记之usbcore.c

#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;
	}

}

发表评论

82 − eighty =