使用CubeMx制作一个SD卡作为存储空间的U盘

1 前言

本文总结如何使用CubeMx制作一个U盘。

2 了解硬件平台

由于本文将基于STM3240G-EVAL平台,主要用到USB外设和SDIO外设,所以我们主要是看这两部分外围电路。

USB外围电路:

USB外围电路

图1 USB外围电路

我们这里USB将作为从设备,因此,只需要关注上图中的下面部分电路即可。

SDIO外围电路:

SDIO外围电路

图2 SDIO外围电路

由上图可知,通过PH13管脚的状态可以得知SD卡的拔插状态。

另外,STM3240G-EVAL评估板使用的是25M外部晶振:

使用25M外部晶振

图3 使用25M外部晶振

这样,知道这些,我们就可以开始制作CubeMx工程了。

3 制作CubeMx工程

首先我们只做一个USB MSC工程,先不急着使用SDIO外设。
使用CubeMx创建一个STM32F407IHx工程:
Pinout:
RCC->Crystal/Ceramic Resonator
SYS->Serial Wire
USB_OTG_FS->Mode:Device_Only
USB_DEVICE->Class For FS IP:Mass Storage Class

Clock Configuration:

时钟树设置

图4 时钟树设置

Configuration:
先不做任何设置,USB使用默认设置即可。

Project Settings:
堆栈设置:

堆栈设置

图5 堆栈设置

生成代码后编译,运行后将USB连接到PC机,PC电脑能够识别到这个U盘,但不能打开它。
这里写图片描述
目前还不能打开这个假U盘

图6 目前还不能打开这个假U盘

由于目前只是实现了USB通讯,但是数据还不能写入或从平台读出,因此还只是一个假的U盘,下面我们将通过SD卡来实现数据的存储。

我们回到CubeMx上,在pinout中为系统增加SDIO外设:
Pinout:
SDIO->Mode:SD 4bits Wide bus
并增加PH13管脚来检测SD卡是否存在,当然设置为输入模式:

PH13管脚用来检测SD是否存在

图7 PH13管脚用来检测SD是否存在

时钟树保持不变,接下来在配置页面进行参数配置:
在GPIO设置:
GPIO配置

图8 GPIO配置

PH13配置为上拉输入模式,这里为其设了一个标签SD_DETECT。
接着为SDIO的读写各自增加DMA,以此增加读写速度:
SDIO使用DMA传输

图9 SDIO使用DMA传输

最后在NVIC中为使用到的中断分配中断优先级:
中断优先级设置

图10 中断优先级设置

这里的中断优先级设置很关键,顺序设置错了会出现问题,这里有限级必须保持SDIO global interrup >SDIO DMA > USB ,数字越小,优先级越高,数字刚好倒过来,如上图,5<6<7.

就这样再次重新生成代码。

4 代码修改

在正式对代码修改前,我们现将一个源码文件bsp_driver_sd.c及其头文件加入工程,此文件是之前一片文章(如何制作一个读取U盘文件系统的工程: http://blog.csdn.net/flydream0/article/details/53789548) ,也是由CubeMx自动生成的代码文件,可能是因为本工程并没有使用到文件系统,且CubeMx并不知道USB MSC与SDIO有明确关联,所以并没有生成SD卡对应的bsp驱动源码,因此,我们挪用下之前的源码。如下图:

工程源码文件

图11 工程源码文件

如上图,除了新增的bsp_driver_sd.c源文件,我们主要是针对usbd_storage_if.c这个USB device MSC与系统的接口文件:
初始化代码 :

int8_t STORAGE_Init_FS (uint8_t lun)
{
  /* USER CODE BEGIN 2 */ 
BSP_SD_Init();
  return (USBD_OK);
  /* USER CODE END 2 */ 
}

接口初始化,主要是对SD卡的初始化,调用bsp_driver_sd.c的初始化函数BSP_SD_Init来实现。

读U盘 代码:

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 = -1;

  if(BSP_SD_IsDetected() != SD_NOT_PRESENT)
  {
    BSP_SD_ReadBlocks_DMA((uint32_t *)buf, blk_addr * STORAGE_BLK_SIZ, STORAGE_BLK_SIZ, blk_len);
    ret = 0;
  }
  return ret;
  /* USER CODE END 6 */ 
}

这里使用SD卡的DMA方式来读取SD内容。

写U盘:

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 = -1;

  if(BSP_SD_IsDetected() != SD_NOT_PRESENT)
  {
    BSP_SD_WriteBlocks_DMA((uint32_t *)buf, blk_addr * STORAGE_BLK_SIZ, STORAGE_BLK_SIZ, blk_len);
    ret = 0;
  }
  return ret;
  /* USER CODE END 7 */ 
}

同样,这里也是通过SD的DMA方式来写SD卡数据。

获取U盘容量信息:

int8_t STORAGE_GetCapacity_FS (uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
  /* USER CODE BEGIN 3 */   
  HAL_SD_CardInfoTypedef info;
  int8_t ret = -1;

  if(BSP_SD_IsDetected() != SD_NOT_PRESENT)
  {
    BSP_SD_GetCardInfo(&info);

    *block_num = (info.CardCapacity)/STORAGE_BLK_SIZ  - 1;
    *block_size = STORAGE_BLK_SIZ;
    ret = 0;
  }
  return ret;
  /* USER CODE END 3 */ 
}

如上代码,我们通过SD卡的BSP驱动接口函数BSP_SD_GetCardInfo来获取SD卡信息,并将数据返回。

获取U盘状态:

int8_t  STORAGE_IsReady_FS (uint8_t lun)
{
  /* USER CODE BEGIN 4 */ 
  static int8_t prev_status = 0;
  int8_t ret = -1;

  if(BSP_SD_IsDetected() != SD_NOT_PRESENT)
  {
    if(prev_status < 0)
    {
      BSP_SD_Init();
      prev_status = 0;

    }
    if(BSP_SD_GetStatus() == SD_TRANSFER_OK)
    {
      ret = 0;
    }
  }
  else if(prev_status == 0)
  {
    prev_status = -1;
  }
  return ret;
  /* USER CODE END 4 */ 
}

通过SD卡BSP驱动接口函数BSP_SD_GetStatus获取SD卡状态,并将结果返回。

到此,代码基本修改完成,接下来解释测试验证工作。

5 测试

重新编译工程,并烧录到STM3240G-EVAL评估板,将通过micro-usb线连接到PC机后,多出一个移动U盘,双击打开,如下图所示:

PC端打开文件

图12 PC端打开文件

如上图,可以在PC端通过打开U盘的方式访问SD卡的内容,并创建文件写入内容也是OK的。

6 结论

这里的关键是SDIO global interrupt,DMA,就是USB中断优先级的顺序,必须SDIO>DMA>USB,不然会出错,其他的都好处理,使用CubeMx生成的默认配置即可。

另附上源码:http://download.csdn.net/detail/flydream0/9729021

猜你喜欢

转载自blog.csdn.net/flydream0/article/details/54023812