1. BTABLE(缓冲区描述表)
STM32F103有一组分组缓冲存储区(简称PMA),大小为512字节,首地址是0x40006000,实际寻址空间是1K字节,因为USB IP是16bit寻址,而APB1总线是32bit寻址,所以每32bit数据中只使用16bit作为有效数据。这段空间是STM32F103专门用来给USB或CAN(不能同时)使用缓冲数据的,对于USB来说是用于端点接收或发送的缓冲,而缓冲大小不是固定的,STM32F103用一组缓冲区描述表来描述端点分组这512字节(冲区描述表也使用这512字节空间,所以实际上端点能使用的缓冲区大小要小于512字节)。这样用户可以根据实际项目的情况自主分配每个端点使用到的buffer大小。
USB_BTABLE是分组缓冲区描述表地址寄存器,记录分组缓冲区描述表在512字节中的起始地址,必须8字节对齐,这个寄存器是16bit大小,低3bit必须为0。一般情况下这个寄存器定义为0,即使用512字节的最前面作为描述表空间。
#define BTABLE_ADDRESS (0x00)
SetBTABLE(BTABLE_ADDRESS);
每个端点都是双向设计的,一个方向上的分组分别要定义其起始偏移地址和长度,而这些值都是16位的,所以一个端点需要占用16bit x 2(偏移地址和长度) x 2(IN和OUT方向) = 64bit = 8字节作为描述表。这8个字节的数据的意义是定义好的
在程序设计时并不需要处理物理地址,只要注意PMA地址,另外,程序可能并不会设置端点的4个配置,因为端点可能只会被设置为单向,所以只需要设置该方向上的2个配置就可以了,但是它还是要占8个字节空间。
2. 设置EP分组缓冲区有4个API函数,分别为SetEPTxAddr, SetEPTxCount,SetEPRxAddr,SetEPRxCount。
a) SetEPTxAddr,只是对应下面的宏定义
#define _SetEPTxAddr(bEpNum,wAddr) (*_pEPTxAddr(bEpNum) = ((wAddr >> 1) << 1))
((wAddr >> 1) << 1))是为了保证地址的bit 0为0,即16bit对齐。
#define _pEPTxAddr(bEpNum) ((uint32_t *)((_GetBTABLE()+bEpNum*8 )*2 + PMAAddr))
PMAAddr即0x40006000;_GetBTABLE()是获取USB_BTABLE寄存器的值;bEpNum*8即每个EP占用8字节空间;(_GetBTABLE()+bEpNum*8 )*2是将PMA地址转换为物理地址。
b) SetEPTxCount,对应下面的宏定义
#define _SetEPTxCount(bEpNum,wCount) (*_pEPTxCount(bEpNum) = wCount)
#define _pEPTxCount(bEpNum) ((uint32_t *)((_GetBTABLE()+bEpNum*8+2)*2 + PMAAddr))
+2即意味是在偏移2字节的位置。
c) SetEPRxAddr
#define _SetEPRxAddr(bEpNum,wAddr) (*_pEPRxAddr(bEpNum) = ((wAddr >> 1) << 1))
#define _pEPRxAddr(bEpNum) ((uint32_t *)((_GetBTABLE()+bEpNum*8+4)*2 + PMAAddr))
d)SetEPRxCount,这里的设置会比较特别
上图是接收数据字节数寄存器 n(USB_COUNTn_RX)的结构
高6位定义了接收分组缓冲区的大小,以便USB模块检测缓冲区的溢出。低10位则用于USB模块记录实际接收到的字节数,注意只读属性。
BLSIZE:设置存储区块的大小,与NUM_BLOCK组合定义存储区的大小
NUM_BLOCK:设置存储区块的个数。
#define _SetEPRxCount(bEpNum,wCount) {\
uint32_t *pdwReg = _pEPRxCount(bEpNum); \
_SetEPCountRxReg(pdwReg, wCount);\
}
#define _SetEPCountRxReg(dwReg,wCount) {\
uint16_t wNBlocks;\
if(wCount > 62){_BlocksOf32(dwReg,wCount,wNBlocks);}\
else {_BlocksOf2(dwReg,wCount,wNBlocks);}\
}/* _SetEPCountRxReg */
如果设置的EPCount > 62,则Block的大小为32,否则为2。 其中_BlocksOf32的宏定义如下:
#define _BlocksOf32(dwReg,wCount,wNBlocks) {\
wNBlocks = wCount >> 5;\
if((wCount & 0x1f) == 0)\
wNBlocks--;\
*pdwReg = (uint32_t)((wNBlocks << 10) | 0x8000);\
}/* _BlocksOf32 */
wNBlocks = wCount >> 5; //Block的大小为32,除以32得到block数。
if((wCount & 0x1f) == 0) //正好是32的倍数,block数要减一
wNBlocks--;
*pdwReg = (uint32_t)((wNBlocks << 10) | 0x8000);设置的是USB_COUNTn_RX的BLSIZE和NUM_BLOCK。
_BlocksOf2的宏定义如下:
#define _BlocksOf2(dwReg,wCount,wNBlocks) {\
wNBlocks = wCount >> 1;\
if((wCount & 0x1) != 0)\
wNBlocks++;\
*pdwReg = (uint16_t)(wNBlocks << 10);\
}/* _BlocksOf2 */
含义与_BlocksOf32类似。
在文件usb_mem.c中有2个操作PMA的API函数:
1. UserToPMABufferCopy,将用户数据拷贝到PMA中。
void UserToPMABufferCopy(uint8_t *pbUsrBuf, uint16_t wPMABufAddr, uint16_t wNBytes)
参数pbUsrBuf是用户数据的buffer指针;wPMABufAddr是PMA的地址;wNBytes表示写入数据的长度。
2. PMAToUserBufferCopy,将PMA中的数据拷贝到用户指定buffer中。
void PMAToUserBufferCopy(uint8_t *pbUsrBuf, uint16_t wPMABufAddr, uint16_t wNBytes)
参数pbUsrBuf是用户数据的buffer指针;wPMABufAddr是PMA的地址;wNBytes表示读入数据的长度。