基于STM32F103的USB学习笔记16 - 分组缓冲存储区

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个字节的数据的意义是定义好的

基于STM32F103的USB学习笔记16 - 分组缓冲存储区_第1张图片

在程序设计时并不需要处理物理地址,只要注意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:设置存储区块的个数。

基于STM32F103的USB学习笔记16 - 分组缓冲存储区_第2张图片

#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表示读入数据的长度。

 

你可能感兴趣的:(MCU编程,USB)