一.初始化
USB進行枚舉前的初始化流程圖
- 從main()開始
int main(void) { Set_System();//初始化時鍾系統、使能相關的外圍設備電源; Set_USBClock();//配置和使能USB時鍾 USB_Interrupts_Config();//主要功能是配置USB所用到的中斷;USB低優先級中斷和喚醒中斷 USB_Init();//USB系統初始化 while (1) { } }
- 開始USB系統初始化USB_Init()
void USB_Init(void) { /* 初始化三個全局指針,指向DEVICE_INFO、USER_STANDARD_REQUESTS 和 DEVICE_PROP結構體。 后面兩個是函數指針結構體,里面都是USB請求實現、功能實現的函數指針。 */ pInformation = &Device_Info; //USB設備信息 pInformation->ControlState = 2; //設置控制狀態為IN_DATA pProperty = &Device_Property; //設備本身支持的屬性和方法 pUser_Standard_Requests = &User_Standard_Requests; //初始化主機標准請求 /* Initialize devices one by one */ pProperty->Init(); //初始化 /* 調用pProperty->Init()(實質上是執行Virtual_Com_Port_init()函數)完成設備的初始化。 上層函數調用下層函數是常規操作。而下層函數調用上層文件函數稱之為回調。 回調函數的意義在於給同一種操作模式,提供不同的回調函數即可實現不同的功能。 回調函數的實現方法是函數指針數組,這是指針的高級應用。 */ }
2.1 USB設備信息DEVICE_INFO
//USB內核將主機發送過來的用於實現USB設備的設置包保存在設備信息結構表中 typedef struct _DEVICE_INFO { uint8_t USBbmRequestType; /* bmRequestType */ uint8_t USBbRequest; /* bRequest */ uint16_t_uint8_t USBwValues; /* wValue */ uint16_t_uint8_t USBwIndexs; /* wIndex */ uint16_t_uint8_t USBwLengths; /* wLength */ uint8_t ControlState; /* 控制狀態 */ uint8_t Current_Feature; /* 當前功能 */ uint8_t Current_Configuration; /* 當前篇日志 */ uint8_t Current_Interface; /* 選擇當前配置接口 */ uint8_t Current_AlternateSetting;/* 選擇當前接口備選設置 */ ENDPOINT_INFO Ctrl_Info; }DEVICE_INFO;
2.2 USB控制狀態
typedef enum _CONTROL_STATE { WAIT_SETUP, /* 0 */ SETTING_UP, /* 1 */ IN_DATA, /* 2 */ OUT_DATA, /* 3 */ LAST_IN_DATA, /* 4 */ LAST_OUT_DATA, /* 5 */ WAIT_STATUS_IN, /* 7 */ WAIT_STATUS_OUT, /* 8 */ STALLED, /* 9 */ PAUSE /* 10 */ } CONTROL_STATE; /* The state machine states of a control pipe*/
2.3 USB功能實現Device_Property
DEVICE_PROP Device_Property = { Virtual_Com_Port_init, Virtual_Com_Port_Reset, Virtual_Com_Port_Status_In, Virtual_Com_Port_Status_Out, Virtual_Com_Port_Data_Setup, Virtual_Com_Port_NoData_Setup, Virtual_Com_Port_Get_Interface_Setting, Virtual_Com_Port_GetDeviceDescriptor, Virtual_Com_Port_GetConfigDescriptor, Virtual_Com_Port_GetStringDescriptor, 0, 0x40 /*MAX PACKET SIZE*/ };
2.4 USB標准請求USER_STANDARD_REQUESTS
//標准請求的處理函數,非USB寄存器的操作,而是用戶自己設定的寄存器操作 好像都是設置當前的設備狀態 USER_STANDARD_REQUESTS User_Standard_Requests = { Virtual_Com_Port_GetConfiguration,//獲得配置 Virtual_Com_Port_SetConfiguration,//設置配置 Virtual_Com_Port_GetInterface,//獲得接口 Virtual_Com_Port_SetInterface,//設置接口 Virtual_Com_Port_GetStatus,//得到狀態 Virtual_Com_Port_ClearFeature,//清除特性 Virtual_Com_Port_SetEndPointFeature,//設置端點特性 Virtual_Com_Port_SetDeviceFeature,//設置設備特性 Virtual_Com_Port_SetDeviceAddress//設置設備地址 };
- 設備的初始化Virtual_Com_Port_init()
void Virtual_Com_Port_init(void) { Get_SerialNum();//獲取設備序列號 pInformation->Current_Configuration = 0;//使當前配置為0,說明還沒進行枚舉。 PowerOn();//將USB上電 連接設備 //主要是CNTR寄存器的初始化 開啟所有必須處理的中斷(除DMA溢出中斷) USB_SIL_Init(); USART_Config_Default();//將USART配置為默認設置 bDeviceState = UNCONNECTED; //設備狀態標志 當前狀態未連接 }
3.1獲取設備序列號Get_SerialNum()
void Get_SerialNum(void) {//獲取設備版本號,將其存入到版本號字符串。 uint32_t Device_Serial0, Device_Serial1, Device_Serial2; Device_Serial0 = *(uint32_t*)ID1; Device_Serial1 = *(uint32_t*)ID2; Device_Serial2 = *(uint32_t*)ID3; Device_Serial0 += Device_Serial2; if (Device_Serial0 != 0) { IntToUnicode (Device_Serial0, &Virtual_Com_Port_StringSerial[2] , 8); IntToUnicode (Device_Serial1, &Virtual_Com_Port_StringSerial[18], 4); } }
3.2 USB上電PowerOn()
RESULT PowerOn(void) { uint16_t wRegVal; /** 在PowerOn函數使能了復位中斷以后,將進入到USB的復位中斷里面去。 然后再執行函數USB_SIL_Init 將所有的USB中斷都打開。 這個函數首先把D+的上拉電阻上電;這樣電腦就可以檢測到設備了。 */ #if !defined (USE_NUCLEO) /*** cable plugged-in ? ***/ USB_Cable_Config(ENABLE); //上電連接,實質上就是將USB控制線置為低電平,便於主機檢測 #endif /*** CNTR_PWDN = 0 ***/ //USB 收發器內部參照電壓 //這句話雖然將強制復位USB模塊,但由於復位中斷允許位沒有使能,不會引起復位中斷 wRegVal = CNTR_FRES; //強制復位 _SetCNTR(wRegVal); /* The following sequence is recommended:建議遵循以下順序 1- FRES = 0 2- Wait until RESET flag = 1 (polling) 3- clear ISTR register */ /*** CNTR_FRES = 0 ***///清除復位信號 wInterrupt_Mask = 0; _SetCNTR(wInterrupt_Mask); /* Wait until RESET flag = 1 (polling) */ //等待復位標志置一 while((_GetISTR()&ISTR_RESET) == 1); /*** Clear pending interrupts ***/ //清除掛起標志 SetISTR(0); /*** Set interrupt mask ***/ //復位中斷屏蔽位 掛起中斷屏蔽位 喚醒中斷屏蔽位使能 wInterrupt_Mask = CNTR_RESETM | CNTR_SUSPM | CNTR_WKUPM; _SetCNTR(wInterrupt_Mask); /* 后面的一個語句執行后,復位中斷已經被允許,而此時集線器多半已經開始復位端口了。或者說稍微有限 延遲,設備固件還能繼續初始化一些部件,但已經不會影響整個工作流程了。 所以,接下來直接進入復位中斷。 */ return USB_SUCCESS; }
在PowerOn函數使能了復位中斷以后,將進入到USB的復位中斷里面去。然后再執行函數USB_SIL_Init 將所有的USB中斷都打開。
3.4 ISTR事件中斷服務程序USB_Istr()
#if (IMR_MSK & ISTR_RESET)//初始化時第一次進入 //USB復位請求中斷 if (wIstr & ISTR_RESET & wInterrupt_Mask) { _SetISTR((uint16_t)CLR_RESET); //清除復位中斷標志 Device_Property.Reset(); //進入到復位中斷 //實際上是調用Virtual_Com_Port_Reset()函數進行處理
3.5 實現對端點的復位Virtual_Com_Port_Reset()
void Virtual_Com_Port_Reset(void) { /* Set Virtual_Com_Port DEVICE as not configured 還沒被配置*/ pInformation->Current_Configuration = 0; //當前配置為0 /* Current Feature initialization */ pInformation->Current_Feature = Virtual_Com_Port_ConfigDescriptor[7]; //需要總線供電 /* Set Virtual_Com_Port DEVICE with the default Interface*/ pInformation->Current_Interface = 0; //當前接口為0 SetBTABLE(BTABLE_ADDRESS); //設置包緩沖區地址
/* Set this device to response on default address */ SetDeviceAddress(0); //設置設備地址默認為0 bDeviceState = ATTACHED; //設置當前狀態為連接狀態
復位中斷執行完后,開發板的USB接口能夠以默認的地址對主機來的數據包進行響應了。這個階段的分析結束,下面正式分析代碼實現的枚舉過程。
3.6 CNTR寄存器的初始化USB_SIL_Init()
uint32_t USB_SIL_Init(void) { //USB中斷初始化 /* USB interrupts initialization */ /* clear pending interrupts */ _SetISTR(0); //清除掛起標志 wInterrupt_Mask = IMR_MSK; /* set interrupts mask */ //開啟所有必須處理的中斷(除DMA溢出中斷) _SetCNTR(wInterrupt_Mask); return 0; }
3.7 串口初始化USART_Config_Default()