使用STM32的USB模塊中后對USB緩沖區(qū)的認(rèn)識
最近在使用STM32的USB模塊開發(fā)個項目,還以為挺簡單,結(jié)果搞了快兩天才把USB的包緩沖區(qū)的訪問搞定,在此做個小總結(jié)吧。
STM32的USB模塊包緩沖區(qū)有512B,但是在STM32的參考手冊中的存儲器映像中卻表明0x40006000-0x400063ff,整整多了512B,怎么會這樣呢,同時在嘗試著編程時也遇到了一個問題:
在usb_core.c文件的Setup0_Process(void)這個函數(shù)中,有這么一段:
uint16_t offset = 1;
if (pInformation->ControlState != PAUSE)
{
pInformation->USBbmRequestType = *pBuf.b++; /* bmRequestType */
pInformation->USBbRequest = *pBuf.b++; /* bRequest */
pBuf.w += offset;/* word not accessed because of 32 bits addressing */
pInformation->USBwValue = ByteSwap(*pBuf.w++); /* wValue */
pBuf.w += offset;/* word not accessed because of 32 bits addressing */
pInformation->USBwIndex= ByteSwap(*pBuf.w++); /* wIndex */
pBuf.w += offset;/* word not accessed because of 32 bits addressing */
pInformation->USBwLength = *pBuf.w; /* wLength */
}
這其中又不太明白為什么需要pBuf.w += offset;而且后面的解釋也不太懂 /* word not accessed because of 32 bits addressing */
,我于是在這段之前加入調(diào)試以顯示收到什么數(shù)
#ifdef DEBUG
UARTSend_String("***端點0收到SETUP數(shù)據(jù)***rn");
for(offset=0;offset<16;offset++)
{
UARTSend_Hex(*pBuf.b++);
}
#endif
結(jié)果串口調(diào)試顯示如下:
***USB總線復(fù)位***
***USB總線CTR置位***
***進(jìn)入端點0***
***端點0收到SETUP包***
***端點0收到SETUP數(shù)據(jù)***
0x80 0x06 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x40 0x00 0x00 0x00
本來應(yīng)該顯示0x80 0x06 0x00 0x01 0x00 0x000x40 0x00才對,不太明白怎么0x80 0x06和0x00 0x01 后面多了兩個 0x00 ,難道USB模塊還會將收到的數(shù)據(jù)跳著放嗎?
后來反復(fù)看參考手冊,問了些人,才知道原來STM32的USB緩沖區(qū)是一個雙端口的RAM,CPU一端需要使用32位方式訪問,但USB模塊一端使用16位方式訪問。也就是說每個USB模塊中的地址*2才能對應(yīng)到控制器中的實際地址,這樣每四個字節(jié)地址空間后兩個字節(jié)地址空間是空的。所以上面串口調(diào)試顯示的數(shù)據(jù)每正確兩個字節(jié)就會多出兩個字節(jié)的0x00。
這里也對STM32的USB庫函數(shù)中對緩沖區(qū)的操作函數(shù)做個說明:
在usb_men.c文件有這么一個函數(shù)
void PMAToUserBufferCopy(uint8_t *pbUsrBuf, uint16_t wPMABufAddr, uint16_t wNBytes)
uint32_t n = (wNBytes + 1) >> 1;/* /2*/
uint32_t i;
uint32_t *pdwVal;
pdwVal = (uint32_t *)(wPMABufAddr * 2 + PMAAddr);
for (i = n; i != 0; i--)
{
*(uint16_t*)pbUsrBuf++ = *pdwVal++;
pbUsrBuf++;
}
它的作用是將緩沖區(qū)中的數(shù)據(jù)拷貝到你所定義的uint8_t Receive_Buffer[];數(shù)組中。其它地方都好理解,這里說說
pdwVal = (uint32_t *)(wPMABufAddr * 2 + PMAAddr);
for (i = n; i != 0; i--)
{
*(uint16_t*)pbUsrBuf++ = *pdwVal++;
pbUsrBuf++;
}
首先是pdwVal = (uint32_t *)(wPMABufAddr * 2 + PMAAddr);這里wPMABufAddr * 2之所以要*2就是前面所述的USB模塊中的地址*2才能對應(yīng)到控制器中的實際地址,在取得對應(yīng)端點的緩沖區(qū)首地址后,將其(uint32_t*)強(qiáng)制指向uint32_t型,這樣每次*pdwVal++,pdwVal的地址都增加4個字節(jié),并且每次都會有四個字節(jié)的數(shù)據(jù)讀出。
在for循環(huán)中,*(uint16_t*)pbUsrBuf++ = *pdwVal++;這句重點說說,++與指針*同優(yōu)先級,結(jié)合順序為至右向左結(jié)合,因此相當(dāng)于*((uint16_t*)pbUsrBuf++),先*(uint16_t*)pbUsrBuf賦值只取*pdwVal++的32位數(shù)據(jù)的前16位,然后再將pbUsrBuf加上1個字節(jié)。到這里,前面定義的uint8_tReceive_Buffer[];數(shù)組中就有兩個數(shù)組變量被賦值了,但這時地址還指增加一個字節(jié),因此還需要pbUsrBuf++;讓其指向Receive_Buffer[];的下下個數(shù)組變量。一次循環(huán),知道讀出所有數(shù)據(jù)。