關於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("設置設備遠程喚醒特性\r\n");
					//設置USB狀態爲使能遠程喚醒
					USB_DeviceStatus |= USB_GETSTATUS_REMOTE_WAKEUP;
					/*stm32硬件本身就支持遠程喚醒,這裏就不用設置了
					 *當然,軟件支不支持在於對中斷屏蔽位的設置
					 */
				}	
				else
				{
					printf("清除設備遠程喚醒特性\r\n");
					USB_DeviceStatus &= ~USB_GETSTATUS_REMOTE_WAKEUP;
					/*stm32硬件本身就支持遠程喚醒,這裏就不用設置了
					 *當然,軟件支不支持在於對中斷屏蔽位的設置
					 */
				}
			}
			//不支持的請求
			else
			{
				printf("不支持的請求\r\n");
				return FALSE;
			}
			break;
		//接收端是接口
		case REQUEST_TO_INTERFACE:
			//接口不支持清除特性
			printf("清楚設置接口特徵不被USB2.0支持\r\n");
			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("\r\n");
			//將端點轉化成與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("設置端點遠程喚醒特性\r\n");
						//設置請求的端點爲stall
						USB_SetStallEP(n);
						//更新USB的狀態位
						USB_EndPointHalt |= m;	
					}
					//清楚
					else
					{
						printf("清楚端點遠程喚醒特性\r\n");
						USB_ClrStallEP(n);
						USB_EndPointHalt &= ~m;
					}

				}
				//未定義的請求
				else
				{
					printf("不正確的端點請求\r\n");
					return FALSE;
				}
			}
			//不正確的請求時機
			else
			{
				printf("不正確的請求時機\r\n");
				return	FALSE;
			}
			break;
		//未定義的接收端類型
		default:
				printf("未定義的接收端類型\r\n");
				printf("接收端爲:");
				printhex(SetupPacket.bmRequestType.BM.Recipient);
				printf("\r\n");
				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("獲取設備描述符\r\n");
					EP0Data.pData = (BYTE *)USB_DeviceDescriptor;
					len	= USB_DEVICE_DESC_SIZE;
					break;
				//獲取配置描述符
				case USB_CONFIGURATION_DESCRIPTOR_TYPE:
				  	printf("獲取USB配置描述符\r\n");
					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("獲取字符串描述符\r\n");
					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("請求接收端接口\r\n");
//			switch(SetupPacket.wValue.WB.H)
//			{
//				//Hid 描述符
//				case HID_HID_DESCRIPTOR_TYPE:  
//					printf("請求HID描\r\n");
//					break;
//				case HID_REPORT_DESCRIPTOR_TYPE:
//					printf("請求報告描述符\r\n");
//					//這裏只有一個報告描述符,報告描述符的索引是從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("\r\n");
//					len = HID_ReportDescSize;
//					break;
//				default:
//					printf("未識別的請求!!!###,類型是:");
//					printhex(SetupPacket.wValue.WB.H);
//					printf("\r\n");
//					break;
//			}
//			break;
		default:
			return	FALSE;
 	}

	printf("獲取描述符成功\r\n");
	//將數據發送給主機
	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("設置配置\r\n");
	//配置值不能爲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("設置配置成功\r\n");
		return	TRUE;
	}
	else
	{
		printf("設置配置失敗\r\n");
		return	FALSE;
	}
}

/*功能:用於處理端點0的輸入和輸出請求函數
 *參數: USB_EVT_SETUP 枚舉時上位機發送的數據包
 *      USB_EVT_OUT	  普通傳輸時,上位機發送的數據包
 *		USB_EVT_IN	  普通傳輸時,上位機請求發送數據
 *返回值:無
 */
void USB_EndPoint0 (DWORD event)
{
//	printf("進入端點0處理函數\r\n");
	switch (event)
	{
		//控制傳輸
		case USB_EVT_SETUP:
		//	printf("進入建立階段\r\n");
			//首先讀取主機發來的請求
			USB_SetupStage();
		   	//設置EP0的數據結構
			EP0Data.Count = SetupPacket.wLength;
			//識別是何請求
//			printf("請求是");
//			printhex(SetupPacket.bRequest);
//			printf("    被請求對象:");
//			printhex(SetupPacket.bmRequestType.B);
//			printf("\r\n");
			//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("獲取狀態失敗\r\n");
							goto	stall_i;
						}
						break;
					case USB_REQUEST_CLEAR_FEATURE:
						//如果清除特徵失敗則停止0x81端點
						if(!USB_SetClrFeature(0))
						{
							printf("清楚特徵失敗\r\n");
							goto stall_i;	
						}
						//成功了握一下手OK
						USB_StatusInStage();	
						break;
					case USB_REQUEST_SET_FEATURE:
						//如果狀態失敗則停止0x81端點
						if(!USB_SetClrFeature(1))
						{
							printf("設置特徵失敗\r\n");
							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("設置地址\r\n");
								USB_DeviceAddress = 0x80 | SetupPacket.wValue.WB.L;
								USB_StatusInStage();
						   		break;
							default:
								printf("未識別的接收端,接收端是");
								printhex(SetupPacket.bmRequestType.BM.Recipient);
								printf("\r\n");
								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("請求接口\r\n");
						break;
					default:
						printf("未識別的請求,請求代碼是");
						printhex(SetupPacket.bRequest);
						printf("\r\n");
						break;
					}
				break;
				//類請求
				case REQUEST_CLASS:
					printf("類請求\r\n");
					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("獲取報告描述符\r\n");
										break;
									case HID_REQUEST_SET_REPORT:
										printf("設置報告描述符\r\n");
										break;
									case HID_REQUEST_GET_IDLE:
										printf("獲取空閒率\r\n");
										break;
									case HID_REQUEST_SET_IDLE:
										printf("設置空閒\r\n");
										break;
								    case HID_REQUEST_GET_PROTOCOL:
										printf("查詢報告協議是否在活動\r\n");
										break;				
									case HID_REQUEST_SET_PROTOCOL:
										printf("SetReprot\r\n");
										break;	
									default:
										printf("不支持的HID請求\r\n");
										break;
						   		}
							}
							break;
					}
					break;
				//產商請求
				case REQUEST_VENDOR:
				//未定義請求
				default:
		stall_i:
					USB_SetStallEP(0x80);
					break;
			}
			break;
		//接收成功
		case USB_EVT_OUT:
			printf("USB_EVT_OUT\r\n");
			if(SetupPacket.bmRequestType.BM.Dir ==0)
			{
				printf(" 請求是:");
				printhex(SetupPacket.bRequest);
				printf("\r\n");
			}
			//返回握手
			else
			{
				USB_StatusOutStage();
			}
			break;
		//發送成功
		case USB_EVT_IN:
		//	printf("USB_EVT_IN\r\n");
			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;
	}

}

發佈了43 篇原創文章 · 獲贊 17 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章