STM32第二十一课(USB SLAVE, HAL)

STM32F407 的 USB OTG FS 是一款双角色设备 (DRD) 控制器,同时支持从机功能和主机
功能,完全符合 USB 2.0 规范的 On-The-Go 补充标准。此外,该控制器也可配置为“仅主机”
模式或“仅从机” 模式,完全符合 USB 2.0 规范。
从机模式下则仅支持全速(FS, 12 Mb/s)收发器。

ST 提供了我们一个完整的 USB OTG 驱动库(包括主机和设备),通过这个库,
我们可以很方便的实现我们所要的功能,

对于USB OTG FS功能模块, STM32F4通过AHB总线访问(AHB频率必须大于14.2Mhz),
其中 48Mhz 的 USB 时钟,是来自时钟树图里面的 PLL48CK(和 SDIO 共用)。

++++++++++++++++++++++++++++++++++
在cubemx中,配置好SD之后,再配置USB_OTG_FS。
设置mode为 device_only,
不使能sof,
不使能vbus,
使能NVIC,

添加middelware,
选中usb device,
设置class for fs,为mass storage class,
设置usbd max num of interface,为1
设置usbd max num of configuration,为1
设置usbd max string desc size ,为512
设置self power,为enable
设置msc io buffer size ,为512

设置clock tree,
HSE经过8分频,变成1MHZ,
为了保证PLLQ能够生成48MHZ,那么PLL的VCO,必须是48的整倍,这里设置为6倍,即288MHZ,
PLLP经过2分频,变成144MHZ,即SYSCLK ,
SYSCLK 经过1分频,成为HCLK,即144MHZ。
所以,HCLK,并没有跑满168MHZ,而是144MHZ。

++++++++++++++++++++++++++++++++++++++++++++++
来看看cubemx如何为我们生成了USBD的代码。

在usb device/target/usbd_conf.h文件中,存放的是一些在cubemx中配置的参数。

通过修改 usbd_conf.h 里面的 MSC_MEDIA_PACKET 定义值大小,可以一定程度提高
USB 读写速度(越大越快),设置 512。

来看usb device/target/usbd_conf.c文件。

HAL_PCD_MspInit函数,是硬件初始化函数。
主要是设置GPIO和NVIC。
它被usb_device/app/usb_device.c文件中的
MX_USB_DEVICE_Init函数调用。

还提供用户应用层接口函数,即 USB 设备类的一些回调函数,当 USB 状态机处理
完不同事务的时候,会调用这些回调函数,我们通过这些回调函数,就可以知道 USB 当前状态,
比如:是否枚举成功了?是否连接上了?是否断开了?等,根据这些状态,用户应用程序可以
执行不同操作,完成特定功能。

HAL_PCD_DataOutStageCallback函数,会调用USBD_LL_DataOutStage函数,
HAL_PCD_DataInStageCallback函数,会调用USBD_LL_DataInStage函数。

来看看usb_device/app/usbd_desc.c文件。

USBD_DescriptorsTypeDef FS_Desc =
{
  USBD_FS_DeviceDescriptor
, USBD_FS_LangIDStrDescriptor
, USBD_FS_ManufacturerStrDescriptor
, USBD_FS_ProductStrDescriptor
, USBD_FS_SerialStrDescriptor
, USBD_FS_ConfigStrDescriptor
, USBD_FS_InterfaceStrDescriptor
#if (USBD_LPM_ENABLED == 1)
, USBD_FS_USR_BOSDescriptor
#endif /* (USBD_LPM_ENABLED == 1) */
};

这里,定义了一系列的FOPS,并存放在一个fops结构体中。

来看看usb_device/app/usbd_storage_if.c文件。

USBD_StorageTypeDef USBD_Storage_Interface_fops_FS =
{
  STORAGE_Init_FS,
  STORAGE_GetCapacity_FS,
  STORAGE_IsReady_FS,
  STORAGE_IsWriteProtected_FS,
  STORAGE_Read_FS,
  STORAGE_Write_FS,
  STORAGE_GetMaxLun_FS,
  (int8_t *)STORAGE_Inquirydata_FS
};

这里,定义了一系列的FOPS,并存放在一个fops结构体中。
提供一些磁盘操作函数,包括支持的磁盘个数,以及每个磁盘的初始化
和读写等函数。

移植时需要修改的函数大多数就在这个usbd_storage_if.c文件中。

+++++++++++++++++++++++++++++++++++
usbd_storage_if为应用层文件,里面为USB大容量存储设备类底层操作,包括获取存储器容量,块读写等操作。实际上usbd_storage_if里面这些操作都是空的,需要我们移植底层。由于SD的操作是由电脑同过USB镜像操作的,故开发板程序是不需要移植文件系统的,我们只需在usbd_storage_if文件中添加SD卡的底层操作既可。
打开usbd_storage_if文件,我们只需修改三个函数既可,第一个是获取存储器容量大小函数,返回块大小,已经块数目。
++++++++++++++++++++++++++++++++++++++
STORAGE_GetCapacity_FS
我们需要获取sd卡的信息,HAL_SD_GetCardInfo用来获取sd卡信息,并存放在一个sdcardinfo变量中,通常设置为全局变量即可。

HAL_SD_GetCardInfo(&hsd, &SDCardInfo);

我们可以在初始化的时候,调用这个函数,填充sdcardinfo这个全局变量,
这样,后面就可以直接使用这个结构体对象中的数据了。

int8_t STORAGE_Init_FS(uint8_t lun)
{
  /* USER CODE BEGIN 2 */
  HAL_SD_GetCardInfo(&hsd, &SDCardInfo);
  return (USBD_OK);
  /* USER CODE END 2 */
}

int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
  /* USER CODE BEGIN 3 */
  //*block_num  = STORAGE_BLK_NBR;
  //*block_size = STORAGE_BLK_SIZ;
    *block_size=STORAGE_BLK_SIZ;  
	*block_num=((uint32_t)(SDCardInfo.LogBlockNbr)); 
  return (USBD_OK);
  /* USER CODE END 3 */
}

+++++++++++++++++++++++++++++++++++++
STORAGE_Read_FS
HAL库提供的SD卡的读取函数,如下:

HAL_StatusTypeDef HAL_SD_ReadBlocks(SD_HandleTypeDef *hsd, 
							uint8_t *pData, 
							uint32_t BlockAdd, uint32_t NumberOfBlocks, 
							uint32_t Timeout)

我们需要在STORAGE_Read_FS中调用这个函数,完成SD卡的读取。

HAL库提供的读取卡状态的函数,如下:

HAL_SD_StateTypeDef HAL_SD_GetState(SD_HandleTypeDef *hsd)
{
  return hsd->State;
}

这个函数,从SD卡的句柄中,读取当前SD卡句柄的状态。

HAL库提供的读取卡状态的函数,如下:

HAL_SD_CardStateTypeDef HAL_SD_GetCardState(SD_HandleTypeDef *hsd)

我们需要在STORAGE_Read_FS中调用这个函数,完成SD卡状态的读取。

实现的STORAGE_Read_FS函数如下:

int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
  /* USER CODE BEGIN 6 */
    int8_t ret=USBD_FAIL;
    if( HAL_SD_ReadBlocks(&hsd, buf, blk_addr,  blk_len, HAL_MAX_DELAY) == HAL_OK )
    {
    	ret = USBD_OK;
 
        while(HAL_SD_GetState(&hsd) == HAL_SD_STATE_BUSY);
        while( HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER );
    }
 
    return ret;

  /* USER CODE END 6 */
}

这里使用
while();
形式的语句块,实现自旋等待。
当条件为TRUE时,CPU自旋,直到条件为FALSE时,CPU跳出自旋状态,开始执行后续的语句块。

这里,我们首先针对SD卡的句柄执行自旋,然后针对SD卡的卡状态执行自旋,
两个自旋块都跳出后,我们认为本次读取操作可以安全的退出了。

在USB接入电脑上后,PC会调用STORAGE_Read_FS函数,读取MSC的sector,获取U盘的FATFS的目录数据。

++++++++++++++++++++++++++++++++++++
STORAGE_Write_FS
HAL库提供的写SD卡的函数如下:

HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, 
								uint8_t *pData, 
								uint32_t BlockAdd, uint32_t NumberOfBlocks, 
								uint32_t Timeout)

我们需要在STORAGE_Write_FS中,调用这个函数,完成SD卡的写入。
类似的,我们实现的STORAGE_Write_FS函数如下:

int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
  /* USER CODE BEGIN 7 */
    int8_t ret = USBD_FAIL;
    if( HAL_SD_WriteBlocks(&hsd, buf, blk_addr, blk_len, HAL_MAX_DELAY) == HAL_OK )
    {
    	ret = USBD_OK;
 
        while(HAL_SD_GetState(&hsd) == HAL_SD_STATE_BUSY);
        while( HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER );
    }
 
    return ret;
  /* USER CODE END 7 */
}

在USB接入电脑上后,PC会调用STORAGE_Write_FS函数,写入MSC的sector,修改U盘的FATFS的目录数据。
例如,我们需要修改LABEL,当我们在PC上点击rename后,PC会调用STORAGE_Write_FS函数,写入MSC的sector。
++++++++++++++++++++++++++++++++++
修改U盘的各项信息,需要修改

const int8_t STORAGE_Inquirydata_FS[] = {/* 36 */

  /* LUN 0 */
  0x00,
  0x80,
  0x02,
  0x02,
  (STANDARD_INQUIRY_DATA_LEN - 5),
  0x00,
  0x00,
  0x00,
   'L', 'Z', ' ', ' ', ' ', ' ', ' ', ' ', /* Manufacturer : 8 bytes */
  'H', 'U', 'S', 'K', 'A', 'R', ' ', ' ', /* Product      : 16 Bytes */
  ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
  '0', '.', '0' ,'1'                      /* Version      : 4 Bytes */
};

其中包括8个char的manufacturer信息,16个char的product信息,4个char的version信息。
注意用space字符来填充PAD。
我们改成自己想要的字符串。

+++++++++++++++++++++++++++++++++++
在main中,并不需要专门为USBD_MSC专门设置代码,
当USB被触发时,需要执行的工作由FOPS完成。

+++++++++++++++++++++++++++++++++++
SD可以开启DMA读取或者单纯的SD的中断,但是其中优先级一定要为SD > SD DMA Rx/Tx > USB,不然当SD卡在读写的时候被其他中断打断,会直接导致U盘掉盘,中途卡顿。

全速FS,USB通信速率最高为12Mbps,所以理论上的读写速度是位于1.5MB/s的。实际上,经过测试,读速度在700KB/s左右,写速度在400KB/s左右。
如果需要提高读写速度,则可以把MSC_MEDIA_PACKET 设置大一点,并且将STM32的heap空间设置的更大一点,这样可以尽可能的接近理论速率。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值