#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;
}
}
關於stm32的USB學習筆記之usbcore.c
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.