整理:MilerShao 關(guān)鍵詞:STM32、USB、DEVICE、PACKET BUFFER 、PMA 、ENDPOINT、緩沖描述表、包緩沖、端點(diǎn) STM32系列MCU大多具有USB外設(shè),其中一部分具有USB FS模塊,作為DEVICE使用。另外一部分具備OTG模塊,可以實(shí)現(xiàn)HOST/DEVICE雙重角色的功能。這里聊聊關(guān)于STM32F1/F3/L1系列的USB FS 模塊中的包數(shù)據(jù)緩沖話題,即Packet Buffer Memory。STM32F1/F3/L1這三個(gè)系列的USB FS模塊基本具有相同的結(jié)構(gòu),這里不妨以STM32F1系列的非互聯(lián)型芯片的USB FS 模塊為例來(lái)介紹下Packet Buffer Memory Area,后面簡(jiǎn)稱(chēng)PMA.
這個(gè)PMA的作用就是USB設(shè)備模塊用來(lái)實(shí)現(xiàn)MCU與主機(jī)進(jìn)行數(shù)據(jù)通信的一個(gè)專(zhuān)門(mén)的數(shù)據(jù)緩沖區(qū),我們稱(chēng)之為USB硬件緩沖區(qū)。說(shuō)得具體點(diǎn)就是USB模塊把來(lái)自主機(jī)的數(shù)據(jù)接收進(jìn)來(lái)后先放到PMA,然后再被拷貝到用戶(hù)數(shù)據(jù)緩存區(qū);或者M(jìn)CU要發(fā)送到主機(jī)的數(shù)據(jù),先從用戶(hù)數(shù)據(jù)緩存區(qū)拷貝進(jìn)PMA,再通過(guò)USB模塊負(fù)責(zé)發(fā)送給主機(jī)。 不少人在利用ST官方提供的參考工程庫(kù)文件來(lái)開(kāi)發(fā)自己的USB應(yīng)用時(shí),圍繞PMA的配置和使用的地方有時(shí)會(huì)出錯(cuò)卡殼,或者說(shuō)即使做完了,即使關(guān)于PMA可能還有些疑惑。這里一起聊聊PMA話題。不妨先看看STM32F1系列的非互聯(lián)型芯片的USB FS 模塊功能結(jié)構(gòu)圖。
上圖中的紅色方框部分就是Packet Buffer Memory,該系列芯片的的最大容量為512字節(jié),在USB模塊內(nèi)部按半字訪問(wèn),即256個(gè)半字。該存儲(chǔ)區(qū)域既可以被USB內(nèi)核通過(guò)Packer buffer Interface【包緩沖接口】尋址訪問(wèn),亦可以被CPU通過(guò)APB1總線訪問(wèn),某時(shí)刻具體誰(shuí)去訪問(wèn)它由圖中的Arbiter【仲裁器】決定,APB1總線具有更高優(yōu)先級(jí)。要注意的是,USB內(nèi)核訪問(wèn)該區(qū)域是16位數(shù)據(jù)寬度對(duì)齊,而APB1總線訪問(wèn)它是以32位數(shù)據(jù)寬度對(duì)齊。另外,USB模塊中的端點(diǎn)相關(guān)寄存器的訪問(wèn)跟PMA一樣,也可以分別被USB內(nèi)核或APB1總線訪問(wèn),同樣存在兩種訪問(wèn)對(duì)齊的問(wèn)題。
好,繼續(xù)聊聊PMA的具體內(nèi)部結(jié)構(gòu)和用法。我們知道,【如果不知道就假設(shè)知道吧,其實(shí)無(wú)數(shù)的理論都是從假設(shè)或約定開(kāi)始的:-)】每個(gè)雙向端點(diǎn)對(duì)應(yīng)2個(gè)Packet Buffer,分別用作發(fā)送數(shù)據(jù)包的緩沖和接收數(shù)據(jù)包的緩沖【雙緩沖端點(diǎn)可視為兩個(gè)單向端點(diǎn)輪流跟HOST固定地做IN或OUT方向的單向通信,輪換使用2個(gè)同向緩沖區(qū)】。這些Packet buffer的大小和位置在PMA內(nèi)可以配置,具體由緩沖描述表【buffer description table】來(lái)指定,該描述表也放在PMA里面。它的起始地址由USB_BTABLE寄存器指定。即整個(gè)PMA放置的內(nèi)容就是緩沖描述表和該表所指定的各端點(diǎn)相應(yīng)的接收或發(fā)送緩沖區(qū)。 現(xiàn)在問(wèn)題來(lái)了,既然緩沖描述表負(fù)責(zé)指定使用到的各端點(diǎn)的包緩沖地址及大小,那緩沖描述表自身在PMA中所占存儲(chǔ)空間的大小怎么定的呢?
下面就是PMA的內(nèi)容框架結(jié)構(gòu)示意圖。PMA內(nèi)存放著USB應(yīng)用中用到的各端點(diǎn)包緩沖【PACKET BUFFER】和指定這些PACKET BUFFER的地址和大小的緩沖描述表【buffer description table】兩部分內(nèi)容。  從上面PMA的內(nèi)部框架圖可以比較直觀地看出緩沖描述表是由各端點(diǎn)兩個(gè)方向的緩沖區(qū)起始地址及相關(guān)數(shù)據(jù)長(zhǎng)度之內(nèi)容所占據(jù),每個(gè)雙向端點(diǎn)占4個(gè)連續(xù)的半字,在PMA內(nèi)部按照地址從低往高的順序依次存放各端點(diǎn)的發(fā)送緩沖區(qū)的地址、待發(fā)送數(shù)據(jù)的長(zhǎng)度、接收緩沖區(qū)的地址、接收緩沖區(qū)的長(zhǎng)度。這4個(gè)連續(xù)的半字組成緩沖描述表的一個(gè)表項(xiàng)[ENTRY]。各表項(xiàng)又按照端點(diǎn)序號(hào)依次從小往大的朝地址遞增方向連續(xù)存放。
前面說(shuō)過(guò)緩沖描述表在PMA中的起始位置的偏移量由USB_BTABLE寄存器決定。如無(wú)特別需要,一般把它設(shè)置為零,即從PMA的硬件起始地址開(kāi)始存放包緩沖描述表,確切地說(shuō),從PMA的起始地址開(kāi)始沿著端點(diǎn)序號(hào)由小到大的順序依次存放或預(yù)留各端點(diǎn)表項(xiàng),直到程序代碼中指定的最大端點(diǎn)號(hào)為止。每個(gè)表項(xiàng)占4個(gè)半字,即8個(gè)字節(jié)。 比方說(shuō),如果你用到端點(diǎn)0,1,3 共三個(gè)端點(diǎn),那緩沖描述表里除了安排端點(diǎn)0,1,3的表項(xiàng)外,其中2號(hào)端點(diǎn)的表項(xiàng)空間【8個(gè)字節(jié)】依然會(huì)預(yù)留在表項(xiàng)1與表項(xiàng)3之間的存儲(chǔ)空間。也就是說(shuō)這種情況下,緩沖描述表自身還是占4*8=32字節(jié)的空間,其中表項(xiàng)2的8字節(jié)空間可以說(shuō)是浪費(fèi)了【其實(shí)也可以用,只是使用起來(lái)不太方便】。
當(dāng)然,我這里只是舉個(gè)例子。一般來(lái)講,比方中的端點(diǎn)安排完全可以調(diào)整為 0,1,2,那3號(hào)端點(diǎn)就用不著了。這樣的話緩沖描述表自身所占的PMA空間為 3*8=24 字節(jié),節(jié)省出8個(gè)字節(jié)可以給后面的包收發(fā)緩沖區(qū)之用。 我們已經(jīng)知道,PMA里面的各端點(diǎn)的包緩沖地址及大小是由緩沖描述表指定的,而緩沖描述表同時(shí)又跟這些包緩沖共同占據(jù)整個(gè)PMA。那么,如果緩沖描述表自身在什么位置、占多少空間不清的話,而去給端點(diǎn)指定緩沖地址及大小難免有點(diǎn)抓瞎的味道。有人在做STM32 USB DEVICE開(kāi)發(fā)應(yīng)用時(shí),正是在對(duì)緩沖描述表本身位置及自身用到多少PMA存儲(chǔ)空間不清楚的情況下而去給用到的端點(diǎn)指派緩沖地址及大小,經(jīng)常出現(xiàn)調(diào)試或通信異常。
經(jīng)過(guò)上面的介紹,了解了緩沖描述表的結(jié)構(gòu)和存儲(chǔ)模式后,對(duì)于不同端點(diǎn)需求的情況下,緩沖描述表自身占多少空間就不難算出了,知道了起始地址,結(jié)尾地址自然就知道了。下面一起來(lái)看看大家常見(jiàn)的一個(gè)STM32 USB DEVICE 應(yīng)用實(shí)例。
顯然,這里用到了0,1,2三個(gè)端點(diǎn)。具體是0號(hào)雙向端點(diǎn),1號(hào)IN端點(diǎn),2號(hào)OUT端點(diǎn)。配置代碼里有5個(gè)數(shù)據(jù),其中第一行的數(shù)據(jù)0x00告知緩沖描述表的起始位置位于PMA的起始位置,另外4個(gè)數(shù)據(jù)用來(lái)明確給出應(yīng)用中用到的各端點(diǎn)數(shù)據(jù)緩沖區(qū)起始地址【或發(fā)或收】。順便提下,USB硬件不會(huì)把本端點(diǎn)的數(shù)據(jù)溢出到相鄰端點(diǎn)的緩沖區(qū)。從這幾句代碼可以看出使用到的各端點(diǎn)數(shù)據(jù)緩沖區(qū)的起始地址及大致范圍,但似乎并不能明確看出前面提到的緩沖描述表所占的地址空間及尾地址。尤其ENDP0_TXADDR的起始地址0X18怎么定出來(lái)的呢?是否還可以變動(dòng)? 結(jié)合上面關(guān)于緩沖描述表的介紹,一起來(lái)分析下。這里用到3個(gè)端點(diǎn),而且是三個(gè)編號(hào)連續(xù)的端點(diǎn),對(duì)應(yīng)3個(gè)表項(xiàng),那么該緩沖描述表自身所占空間長(zhǎng)度為 3*8=24,16進(jìn)制是0x18; 因?yàn)锽TABLE_ADDRESS=0,所以緩沖描述表在PMA里存放位置就是0x00-0x17這段存儲(chǔ)空間,從0x18開(kāi)始以上的空間就可用作包緩沖區(qū)了。為了充分利用PMA空間,所以0號(hào)端點(diǎn)的發(fā)送緩沖區(qū)起始地址ENDP0_TXADDR就從0x18開(kāi)始了。其它緩沖區(qū)的地址位置和大小也是在結(jié)合各自端點(diǎn)傳輸特性和相鄰包緩沖大小的基礎(chǔ)上擬定,避免相鄰緩沖區(qū)的交叉重疊。
那這個(gè)起始地址0X18是否可以變動(dòng)呢?如果在端點(diǎn)安排不變的前提下,這個(gè)0X18是否可以變動(dòng),答案是肯定的。只要不放進(jìn)緩沖描述表地址段里面就好,完全可以弄成0x20,0x28,0x48等不小于0x17的偶數(shù),因?yàn)閁SB模塊對(duì)PMA存儲(chǔ)區(qū)的訪問(wèn)是雙字節(jié)對(duì)齊。當(dāng)然,如果你把0x18調(diào)整后,其它相鄰的包緩沖地址往往需要做相應(yīng)的調(diào)整。
那假如在上面圖示的基礎(chǔ)上,因?yàn)轫?xiàng)目功能需求調(diào)整,減少1個(gè)端點(diǎn),比如把2號(hào)OUT端點(diǎn)取消,那上面的PMA地址配置需要怎樣調(diào)整呢?【其它相關(guān)問(wèn)題這里就不延伸了】 如果取消OUT 2端點(diǎn)的話,上面有關(guān)PMA地址的配置基本可以不動(dòng),屏蔽掉關(guān)于OUT2端點(diǎn)接收緩沖地址定義的就行。當(dāng)然,這里的端點(diǎn)數(shù)由之前的3個(gè)變少到2個(gè),緩沖描述表自身的長(zhǎng)度實(shí)際上變少為 2*8=16 字節(jié)了,也就是說(shuō)從0x10開(kāi)始以上的空間都可以用來(lái)做包緩沖區(qū),如果包緩沖區(qū)繼續(xù)保持在0x18開(kāi)始的地方也無(wú)妨。
假如就在上面圖示基礎(chǔ)上,因?yàn)楣δ艿男枨笳{(diào)整,增加1個(gè)單向端點(diǎn),比如增加3號(hào)IN端點(diǎn),那上面的PMA地址配置需要怎樣調(diào)整呢? 這里的端點(diǎn)數(shù)由之前的3個(gè)變成4個(gè),緩沖描述表自身的長(zhǎng)度就變多為 4*8=32字節(jié)了,即0x20以下的空間都給緩沖描述表占了,換句話說(shuō),只有從0x20開(kāi)始以上的空間才能用作包緩沖區(qū)。那端點(diǎn)0發(fā)送緩沖地址【ENP0_TXADDR】如果還保持0x18的話肯定不行,這里不調(diào)整的話可能就亂大了。經(jīng)常有人在以ST官方參考庫(kù)的基礎(chǔ)上增加端點(diǎn)后,只是添加新增端點(diǎn)的緩沖區(qū)地址和大小及相應(yīng)描述配置等,卻沒(méi)有顧及到這個(gè)包緩沖的起始地址的問(wèn)題而遇到調(diào)試障礙。具體到這里,ENP0_TXADDR緩沖區(qū)的地址不得小于0x20,其它緩沖地址往往需要適當(dāng)調(diào)整移動(dòng)。當(dāng)然,如果覺(jué)得剩余PMA空間夠用的話,你也完全可以在既不影響其它端點(diǎn)緩沖大小又不影響自身緩沖需要的前提下,大刀闊斧的把ENP0_TXADDR緩沖起始地址換到一個(gè)全新的地址,不一定非得圍著0x18或0x20轉(zhuǎn)來(lái)轉(zhuǎn)去,比方換到0x138。 ......Have a rest..... 還是接著上面的實(shí)例繼續(xù)聊,看看根據(jù)上面的初始化配置,相關(guān)緩沖描述表的表項(xiàng)內(nèi)容在PMA中是如何存放的。
顯然,整個(gè)緩沖描述表用到3個(gè)端點(diǎn),共占空間 3*8=24個(gè)字節(jié)。 因?yàn)镺UT 1端點(diǎn)和IN 2端點(diǎn)沒(méi)有用到,但他們?cè)诒眄?xiàng)中的橢圓形圓圈圈出來(lái)的灰色部分空間還預(yù)留出來(lái)了,一共8個(gè)字節(jié)夾在中間基本算是浪費(fèi)了。如果把端點(diǎn) OUT 2 換到 OUT 1,那整個(gè)緩沖描述表只用到2個(gè)端點(diǎn),所需空間就變?yōu)?16字節(jié)了。多余出來(lái)的8字節(jié)就可以方便的留給包緩沖用。另外,表格中的有些數(shù)據(jù)可能需要結(jié)合參考手冊(cè)的USB寄存器部分看看。那個(gè)XXXX/YYYY表示初始化時(shí)不定的,準(zhǔn)備做數(shù)據(jù)傳輸時(shí)才確定。地址0x40006000是PMA的起始地址。 小結(jié)下,緩沖描述表的起始地址和所占空間都是可以明確指定和計(jì)算出來(lái)的,包緩沖地址的安排往往跟它有關(guān)聯(lián)。只有明確知道了緩沖描述表的起始地址和自身所需存儲(chǔ)空間長(zhǎng)度后,才能放心地用剩余的PMA給用到的端點(diǎn)分別指定緩沖地址及大小。 緩沖描述表里的表項(xiàng)是根據(jù)端點(diǎn)號(hào)在PMA里按序填充的,合理安排端點(diǎn)可以減少緩沖描述表的PMA空間使用量,而留下更多空間給端點(diǎn)包緩沖區(qū)。 最后,注意每次傳輸實(shí)際收到的數(shù)據(jù)包長(zhǎng)度不會(huì)超過(guò)各端點(diǎn)接收緩沖區(qū)容量的限制,長(zhǎng)了會(huì)被截掉。 |