9、STM32HAL_USB_DEVICE库硬件相关

 

目录

1、STM32_USB硬件模块简介

2、STM32_USB硬件功能框图

3、STM32_USB硬件中断

3、STM32_USB端点相关寄存器 


1、STM32_USB硬件模块简介

首先对STM32系列MCU自带的USB模块有一个概念性的认知,官方提供的培训PPT写的也很清晰,如下.

我们下面所说的全部都是F103系列的USB_FS.

在STM32F10X参考手册里面有如下两段话.

 

 除了对USB硬件模块有了一个最基本的说明外,需要注意的就是有以下三点.

 ●    F103系列的MCU里面CAN和USB模块不能同时使用(F105,F107不受该影响)

 ●    USB的ACK包的发送和ACK包的处理,令牌包分组的检测,数据的发送和接收,包括USB里面的CRC校验,全部都由硬件自动做完了.

●     一共8个双向端点,可以作为16个单向端点使用

除此之外,USB提供低功耗模式(不产生任何静态电流,USB时钟减慢或停止).

2、STM32_USB硬件功能框图

各个功能框图的描述其实在ST官方的培训文档也说的很详细了,如下所示.


 

3、STM32_USB硬件中断

USB模块的寄存器主要分为三大类,在手册里面也说了.

先看一下通用寄存器里面最简单的两个寄存器.这部分基本没啥说的,和STM32其他模块大同小异.

寄存器功能描述
BTABLE寄存器里面存放了缓冲区描述表的起始地址
DADDR寄存器 

1、USB模块的总开关

2、记录了HOST设置的设备地址

下面的两个寄存器都是和中断相关的.

●    CNTR寄存器

 ●    ISTR寄存器

 可以很清晰的看到CNTR里面存放着USB中断的使能位,在这里我们只需要关注ISTR的CTR、WKUP、SUSP和RESET这4个bit即可.

中端标志位描述
CTR每次传输完成后都会触发这个标志位
WKUP只有在挂起的时候检测到总线的复位信号才会触发
SUSP总线无信号后,硬件自动挂起
RESET

检测到复位信号后触发,触发后USB模块的发送和接收将会被禁止,直至此为被清除

4、STM32_USB端点相关寄存器 

端点寄存器的数量由USB模块所支持的端点数目决定。每个端点都有与之对应的USB_EpnR寄存器,用于存储该端点的各种状态信息。比如F1系列一共有8个双向端点, 则0≤n≤7.具体寄存器内容如下.

这个寄存器中有 4 类标志,分别是只能清零( rc_w0),写1 翻转(t),只读( r),读写( rw).

  • rc_w0                    写1无效,写0清0
  • t                             写1翻转,写0无变化

主要关注下表的几个寄存器.

位名称含义
CTR_RX正确接收到OUT或SETUP分组时由硬件置位,如果CTRM被置位,产生对应中断,以NAK或者STALL结束的分组和出错不会导致该位置位.
STAT_RX[1:0]

指示端点当前状态

EP_TYPE[1:0]
CTR_TX正确传输一个IN分组时由硬件置位,如果CTRM被置位,产生对应中断.IN分组传输完成后,如果主机响应NAK或STALL,则不会被置位.
STAT_TX[1:0]

指示发送数据的状态位

DEVICE库

4、PMA读写

在开始的时候就提到过PC和USB的交互是通过一段专属的数据缓冲区进行的,该数据缓冲区在硬件功能框图里面又被叫做Packet Buffer Memory,该区域在ST官方的USB库里面又被叫做Packet Memory Area,简称PMA.那么我们如何和这段内存进行交互呢?

首先从上面的信息里面可以知道,PMA的大小是512字节,可以通过APB1总线/USB控制器访问,且APB1的访问权限要高于USB.且软件部分通过Packet buffer interface访问(管理)PMA的存储空间,我们想使用的packet buffer的位置和大小可以随意配置,由buffer描述表指定.

buffer描述表,这个东西实在难理解,首先要明白,它本身也是占用内存的,且占用的内存也在PMA区域里面,但是这块内存的地址是由USB_BTABLE寄存器指定的!!!如下所示.

可以看到这个寄存器的功能只有一个,那就是保存了buffer描述表的起始地址.


通过上面的几段话,可以明白,想要操作这段内存空间(Packet Buffer Memory)就要对buffer描述表进行操作,但buffer描述表本身就处于这段内存中.那该如何操作这个描述表呢?这个描述表的大小又如何确定?

4.1、操作描述表

首先来看如何操作这个描述表,下面看官方提供的一段描述.

简单点说,这个描述表本身可以看成寄存器,使用操作寄存器的收发去操作.下面看一段官方的代码.

#define PCD_SET_EP_TX_ADDRESS(USBx, bEpNum, wAddr) do { \
  register __IO uint16_t *_wRegVal; \
  register uint32_t _wRegBase = (uint32_t)USBx; \
  \
  _wRegBase += (uint32_t)(USBx)->BTABLE; \
  _wRegVal = (__IO uint16_t *)(_wRegBase + 0x400U + (((uint32_t)(bEpNum) * 8U) * PMA_ACCESS)); \
  *_wRegVal = ((wAddr) >> 1) << 1; \
} while(0) /* PCD_SET_EP_TX_ADDRESS */

#define PCD_SET_EP_RX_ADDRESS(USBx, bEpNum, wAddr) do { \
  register __IO uint16_t *_wRegVal; \
  register uint32_t _wRegBase = (uint32_t)USBx; \
  \
  _wRegBase += (uint32_t)(USBx)->BTABLE; \
  _wRegVal = (__IO uint16_t *)(_wRegBase + 0x400U + ((((uint32_t)(bEpNum) * 8U) + 4U) * PMA_ACCESS)); \
  *_wRegVal = ((wAddr) >> 1) << 1; \
} while(0) /* PCD_SET_EP_RX_ADDRESS */

可以很清楚的看到,_wRegVal其实就是你要操作的ADDR_TX/RX的地址,这两段代码就是设置TX/RX缓冲区的起始地址.

这个时候已经设置好了数据存放的地址点了,但是数据交互的长度还没有设置,同理,如下所示.

#define PCD_SET_EP_TX_CNT(USBx, bEpNum, wCount) do { \
    register uint32_t _wRegBase = (uint32_t)(USBx); \
    register __IO uint16_t *_wRegVal; \
    \
    _wRegBase += (uint32_t)(USBx)->BTABLE; \
    _wRegVal = (__IO uint16_t *)(_wRegBase + 0x400U + ((((uint32_t)(bEpNum) * 8U) + 2U) * PMA_ACCESS)); \
    *_wRegVal = (uint16_t)(wCount); \
} while(0)

#define PCD_SET_EP_RX_CNT(USBx, bEpNum, wCount) do { \
    register uint32_t _wRegBase = (uint32_t)(USBx); \
    register __IO uint16_t *_wRegVal; \
    \
    _wRegBase += (uint32_t)(USBx)->BTABLE; \
    _wRegVal = (__IO uint16_t *)(_wRegBase + 0x400U + ((((uint32_t)(bEpNum) * 8U) + 6U) * PMA_ACCESS)); \
    PCD_SET_EP_CNT_RX_REG(_wRegVal, (wCount)); \
} while(0)

 这个和上面的设定地址的代码其实是一样的,只是地址不一样,且在接受的时候多了一个宏,该宏的实际展开如下.

#define PCD_SET_EP_CNT_RX_REG(pdwReg, wCount)  do { \
    uint32_t wNBlocks; \
    if ((wCount) == 0U) \
    { \
      *(pdwReg) &= (uint16_t)~USB_CNTRX_NBLK_MSK; \
      *(pdwReg) |= USB_CNTRX_BLSIZE; \
    } \
    else if((wCount) <= 62U) \
    { \
      PCD_CALC_BLK2((pdwReg), (wCount), wNBlocks); \
    } \
    else \
    { \
      PCD_CALC_BLK32((pdwReg), (wCount), wNBlocks); \
    } \
  } while(0) /* PCD_SET_EP_CNT_RX_REG */

其实也没什么好说的,就是根据数据量的不同,对寄存器进行设定,只是运算方法比较巧妙,有兴趣可以自己琢磨琢磨.

4.2、描述表大小的确定

上面说完了如何操作描述表,让我们可以自由的设定发送和接收的地址缓冲区和长度了,但如何确定你要使用的缓冲区地址呢,毕竟我要用的缓冲区和描述表是出于同一段内存空间的.下面看一张图.

有了上面的知识,应该已经知道能大概理解这张图的含义了,可以看出每个端点所使用的描述表大的大小为8个字节,简单点理解

描述表大小 =  MAN_EP_NUM*8

USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
{
    /*省略.....*/
  HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, 0x18);
  HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x58);
  HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_IN_EP , PCD_SNG_BUF, 0x98);
  HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_OUT_EP , PCD_SNG_BUF, 0xD8);
  HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , MSC_EPIN_ADDR , PCD_SNG_BUF, 0x118);
  HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , MSC_EPOUT_ADDR , PCD_SNG_BUF, 0x158);
  HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_CMD_EP   , PCD_SNG_BUF, 0x198);
    /*省略.....*/
}

结合例子,可以看到端点0的地址设定的是0X18(24),意思就是前24个字节用于缓冲区描述符,后续的各项间隔为0X40,其实就是端点描述符中的wMaxPacketSize.

其实我挺好奇的,复合设备的端点用的是0、1、2的双向端点,3的单向端点,其实按道理来说这地方应该是0X20,但是依旧能正常使用,后来经过实际测试,发现在通讯过程的并没有CDC_CMD_EP的传输数据.所以只使用了0,1,2三个端点.所以这个地方还是可以正常使用的.

建议使用端点的时候最好使用连续的端点,如果使用的是0,1,3.计算的时候还是需要按照4个端点进行计算的,端点2虽然没有使用,但是在实际的内存空间里面还是有端点2的描述表的.这样的话就会有空间浪费.

关于更详细的的解释可以看一下这篇文章,挺详细的.


下面说一些我整了半天才整明白的概念,可能有点简单,但是我确实很迷.

简单说一下,USB_EPnR寄存器的低四位是专门存储端点地址的,比如我们在EP1R的寄存器里面写入0X0A,那么我们代码里面使用的EP1的实际地址就是0X0A,那么如果你的实际端点号其实是0X0A,但是在代码层面8个端点被抽象成为了IN_ep[8],调用的时候使用IN_ep[1]操作的依然是0X0A端点,实现了对底层的抽象.

还有就是一直说STM32有8个双向端点,可以作为16个单向端点使用,我被这句话迷惑了好久,明明只有8个EPnrR寄存器,为什么可以使用16个单向端点,后来在上述图片内看到这句话,才明白,USB_IP最多只能使用8个端点,也就是说,我们不能同时使用15个IN/OUT端点.

他说的这16个单向端点受EPnR控制,说的就是受上图红框内的寄存器位控制.

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值