Cortex-M3 USB的“JoyStickMouse”例程結(jié)構(gòu)分析(一)
一、USB的“JoyStickMouse”例程結(jié)構(gòu)分析
1、例程的結(jié)構(gòu)
(1)底層結(jié)構(gòu)
包括5個文件:usb_core.c(USB總線數(shù)據(jù)處理的核心文件),usb_init.c,usb_int.c(用于端點數(shù)據(jù)輸入輸入中斷處理),usb_mem.c(用于緩沖區(qū)操作),usb_regs.c(用于寄存器操作)。它們都包含了頭文件“usb_lib.h”。在這個頭文件中,又有以下定義:
#include"usb_type.h"
#include"usb_regs.h"
#include"usb_def.h"
#include"usb_core.h"
#include"usb_init.h"
#include"usb_mem.h"
#include"usb_int.h"
usb_lib.h中又包含了七個頭文件,其中usb_type.h中主要是用typedef為stm32支持的數(shù)據(jù)類型取一些新的名稱。usb_def.h中主要是定義一些相關(guān)的數(shù)據(jù)類型。
還有一個未包含在usb_lib.h中的頭文件,usb_conf.h用于USB設(shè)備的配置。
(2)上層結(jié)構(gòu)
上層結(jié)構(gòu)總共5個文件:hw_config.c(用于USB硬件配置)、usb_pwr.c(用于USB連接、斷開操作)、usb_istr.c(直接處理USB中斷)、usb_prop.c(用于上層協(xié)議處理,比如HID協(xié)議,大容量存儲設(shè)備協(xié)議)、usb_desc.c(具體設(shè)備的相關(guān)描述符定義和處理)。
可見,ST的USB操作庫結(jié)構(gòu)十分清晰明了,我先不準(zhǔn)備直接閱讀源代碼。而是先利用MDK的軟件模擬器仿真執(zhí)行,先了解一下設(shè)備初始化的流程。
2、設(shè)備初始化所做的工作
(1)Set_System(void)
這個是main函數(shù)中首先調(diào)用的函數(shù),它位于hw_config.c文件中。它的主要功能是初始化時鐘系統(tǒng)、使能相關(guān)的外圍設(shè)備電源。
配置了JoyStickMouse所用到的5個按鍵,并且配置了兩個EXTI中斷,一個是用于把USB從掛起模式喚醒,還有一個用途未知。
(2)USB_Interrupts_Config();
這個是main函數(shù)中調(diào)用的第二個函數(shù),它也位于hw_config.c文件中。主要功能是配置USB所用到的中斷。
跟蹤到代碼中,主要設(shè)配置了USB低優(yōu)先級中斷和喚醒中斷,又有一個EXTI中斷功能未知。
(3)Set_USBClock()
這個是main函數(shù)中調(diào)用的第三個函數(shù),它也位于hw_config.c文件中。它的功能是配置和使能USB時鐘。
(4)USB_Init(void)
這個是main函數(shù)中調(diào)用的第四個函數(shù),它也位于usb_init.c文件中。它初始化了三個全局指針,指向DEVICE_INFO、USER_STANDARD_REQUESTS和DEVICE_PROP結(jié)構(gòu)體。
后面兩個是函數(shù)指針結(jié)構(gòu)體,里面都是USB請求實現(xiàn)、功能實現(xiàn)的函數(shù)指針。
voidUSB_Init(void)
{
pInformation=&Device_Info;
pInformation->ControlState=2;
pProperty=&Device_Property;
pUser_Standard_Requests=&User_Standard_Requests;
pProperty->Init();
}
這三個結(jié)構(gòu)體都是與具體設(shè)備枚舉和功能實現(xiàn)相關(guān)的,定義在usb_prop.c和usb_desc.c文件中。
DEVICE_PROPDevice_Property=
{
Joystick_init,
Joystick_Reset,
Joystick_Status_In,
Joystick_Status_Out,
Joystick_Data_Setup,
Joystick_NoData_Setup,
Joystick_Get_Interface_Setting,
Joystick_GetDeviceDescriptor,
Joystick_GetConfigDescriptor,
Joystick_GetStringDescriptor,
0,
0x40
};
USER_STANDARD_REQUESTSUser_Standard_Requests=
{
Joystick_GetConfiguration,
Joystick_SetConfiguration,
Joystick_GetInterface,
Joystick_SetInterface,
Joystick_GetStatus,
Joystick_ClearFeature,
Joystick_SetEndPointFeature,
Joystick_SetDeviceFeature,
Joystick_SetDeviceAddress
};
Usb_init()函數(shù)調(diào)用pProperty->Init()(實質(zhì)上就是Joystick_init)完成設(shè)備的初始化。
上層程序調(diào)用下次函數(shù)是常規(guī)性的操作。而下層函數(shù)(usb_init相對于usb_prop是輸入底層操作文件)調(diào)用上層文件函數(shù)我們稱之為回調(diào)。
回調(diào)函數(shù)的意義在于同一種操作模式、提供不同的回調(diào)函數(shù)則可以實現(xiàn)不同的功能。Windows中處理消息,好像也用到了這種模式。
回調(diào)函數(shù)的實現(xiàn)方法是函數(shù)指針數(shù)組。這是指針的高級應(yīng)用。
這是函數(shù)的代碼:
voidJoystick_init(void)
{
Get_SerialNum();
//獲取設(shè)備序列號,轉(zhuǎn)變?yōu)閡nicode字符串
pInformation->Current_Configuration=0;
PowerOn();
//連接USB設(shè)備,實質(zhì)是能讓主機檢測到了。
_SetISTR(0);
wInterrupt_Mask=IMR_MSK;
_SetCNTR(wInterrupt_Mask);
bDeviceState=UNCONNECTED;
}
實質(zhì)上,代碼執(zhí)行到這里,開發(fā)板已經(jīng)可以響應(yīng)主機發(fā)來的數(shù)據(jù)了。但我還是先把main()函數(shù)的代碼看完吧。
(5)SysTick_Config();
這個函數(shù)調(diào)用主要是為程序中用到的精確延時作配置。
3、進入主循環(huán)
進入主循環(huán)的工作就兩個:
Joystick_Send(JoyState())。
JoyState()用來獲取按鍵的狀態(tài)。
Joystick_Send(JoyState())用來把按鍵狀態(tài)發(fā)到主機。當(dāng)然這里真正的發(fā)送工作并不是由該代碼完成的。它的工作只是將數(shù)據(jù)寫入IN端點緩沖區(qū),主機的IN令牌包來的時候,SIE負(fù)責(zé)把它返回給主機。
主要代碼如下:
UserToPMABufferCopy(Mouse_Buffer,GetEPTxAddr(ENDP1),4);
//從用戶復(fù)制四個字節(jié)到端點1緩沖區(qū),控制端點的輸入緩沖區(qū)。
SetEPTxValid(ENDP1);
4、中斷處理過程大致理解
(1)usb_istr()函數(shù)中的中斷處理簡單分析
有用的代碼大概以下幾段,首先是處理復(fù)位的代碼,調(diào)用設(shè)備結(jié)構(gòu)中的復(fù)位處理函數(shù)。
wIstr=_GetISTR();
if(wIstr&ISTR_RESET&wInterrupt_Mask)
{
_SetISTR((u16)CLR_RESET);//清復(fù)位中斷
Device_Property.Reset();
}
處理喚醒的代碼:
if(wIstr&ISTR_WKUP&wInterrupt_Mask)
{
_SetISTR((u16)CLR_WKUP);
Resume(RESUME_EXTERNAL);
}
處理總線掛起的代碼:
if(wIstr&ISTR_SUSP&wInterrupt_Mask)
{
if(fSuspendEnabled)
{
Suspend();
}
else
{
Resume(RESUME_LATER);
}
_SetISTR((u16)CLR_SUSP);
}
處理端點傳輸完成的代碼,這段是最重要的,它調(diào)用底層usb_int.c()文件中的CTR_LP()函數(shù)來處理端點數(shù)據(jù)傳輸完成中斷。
if(wIstr&ISTR_CTR&wInterrupt_Mask)
{
CTR_LP();
}
二、STM32處理器的USB接口
1、接口模塊的內(nèi)部結(jié)構(gòu)
在書上有一個很好的USB內(nèi)部接口模塊內(nèi)部結(jié)構(gòu)圖,比較好的解釋了各個模塊之間的關(guān)系,我這里試著用我自己的理解闡述一下吧。
首先在總線端(與D+、D-相連的那一端),通過模擬收發(fā)器與SIE連接。SIE使用48MHz的專用時鐘。
與SIE相關(guān)的的有三大塊:CPU內(nèi)部控制、中斷和端點控制寄存器,掛起定時器(這個好像是USB協(xié)議的要求,總線在一定時間內(nèi)沒有活動,SIE模塊能夠進入SUSPEND狀態(tài)以節(jié)約電能),還有包緩沖區(qū)接口模塊。
說到包緩沖區(qū)接口模塊,這個對應(yīng)的含義是,USB設(shè)備應(yīng)該提供USB包緩沖區(qū)。這塊緩沖區(qū)同時受到SIE和CPU核心的控制,用于CPU與SIE共享達(dá)到數(shù)據(jù)傳輸?shù)哪康摹?/p>
所以CPU通過APB1總線接口訪問,SIE通過包緩沖區(qū)接口模塊訪問,中間通過Arbiter來協(xié)調(diào)訪問。
當(dāng)然我們關(guān)注的中心點是控制、中斷和端點控制寄存器。我們通過這些寄存器來獲取總線傳輸?shù)臓顟B(tài),控制各個端點的狀態(tài),并可以產(chǎn)生中斷來讓CPU處理當(dāng)前的USB事件。
CPU可以通過APB1總線接口來訪問這些寄存器。它們使用的都是PCLK1時鐘。
2、USB模塊的寄存器認(rèn)識
(1)
控制寄存器CNTR
傳輸完成中斷允許位。CTRM,1有效,如果SIE置位傳輸完成標(biāo)志,則相應(yīng)的數(shù)據(jù)傳輸完成中斷發(fā)生。
第15位
包緩沖區(qū)溢出中斷允許位
錯誤中斷允許位
喚醒中斷允許位。WKUPM。1有效,如果喚醒請求標(biāo)志位置位,則產(chǎn)生喚醒中斷。
掛起中斷允許位。SUSPM,1有效,當(dāng)總線掛起標(biāo)志置位時,發(fā)生掛起中斷。
復(fù)位中斷允許位。RESETM。1有效,軟件強制復(fù)位和總線復(fù)位信號,都能觸發(fā)復(fù)位中斷。
幀首中斷允許位
期望幀首中斷允許位。ESOFM。它的含義是沒有收到幀首信號,允許發(fā)生中斷。
第8位
向主機發(fā)送的喚醒請求,RESUME。1有效,主機收到該信號,將喚醒設(shè)備。這個由軟件置位。
第4位
強制掛起控制,F(xiàn)SUSP。1有效。與由于總線無活動引起掛起的效果相同。
低功耗模式。前提是先進入掛起狀態(tài)。由軟件設(shè)置,一般又硬件復(fù)位(被喚醒后自動清零)。
斷電模式控制位。PDWN。此位為1時,USB模塊關(guān)閉。
強制復(fù)位控制。FRES。與總線上的復(fù)位信號產(chǎn)生相同的效果。也能產(chǎn)生復(fù)位中斷.
第0位。
(2)
中斷狀態(tài)寄存器ISTR
這個寄存器主要是反映USB模塊當(dāng)前的狀態(tài)的。第15-8為與控制寄存器的中斷允許是意義對應(yīng)的。相應(yīng)的標(biāo)志位置位,且中斷未屏蔽,則向CPU發(fā)出對應(yīng)的中斷。
CTR標(biāo)志,數(shù)據(jù)傳輸完成后硬件置1.
PMAOVR標(biāo)志
ERR標(biāo)志
WKUP請求,總線檢測到主機喚醒請求時由硬件置位。
SUSP請求標(biāo)志位。
RESET請求標(biāo)志位。
SOF幀首標(biāo)志
ESOF,期待幀首標(biāo)志。
DIR傳輸方向,此位由硬件控制。IN時為0,OUT為1.
第4位。
發(fā)生數(shù)據(jù)傳輸?shù)亩它c的地址。
(3)USB設(shè)備地址寄存器
第7位,EF,USB模塊允許位。如果EF=0,則USB模塊將停止工作。
第6-0位。USB當(dāng)前使用的地址。復(fù)位時為0.
(4)
端點狀態(tài)和配置寄存器,8個寄存器,支持8個雙向端點和16個單向端點。
CTR_RX,正確接收標(biāo)志位。
第15位。
DTOG_RX,用于檢測的數(shù)據(jù)翻轉(zhuǎn)位。一般由硬件自動設(shè)置,軟件寫1可使其手動翻轉(zhuǎn)。
STAT_RX,占據(jù)兩位。
00表示該端點不可用,無回應(yīng)。
01表示響應(yīng)STALL
10響應(yīng)NAK
11表示端點有效,可接收數(shù)據(jù)。
SETUP標(biāo)志。收到SETUP令牌包時置位。用戶收到數(shù)據(jù)后需檢查次位。
第11位。
EP_TYPE,兩位,表示端點類型。
00表示批量端點。
01表示控制端點
10表示等時端點。
11表示中斷端點。
EP_KIND,端點特殊類型。在EP_TYP