1 前言
本文总结如何使用CubeMx制作一个U盘。
2 了解硬件平台
由于本文将基于STM3240G-EVAL平台,主要用到USB外设和SDIO外设,所以我们主要是看这两部分外围电路。
USB外围电路:
我们这里USB将作为从设备,因此,只需要关注上图中的下面部分电路即可。
SDIO外围电路:
由上图可知,通过PH13管脚的状态可以得知SD卡的拔插状态。
另外,STM3240G-EVAL评估板使用的是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:
Configuration:
先不做任何设置,USB使用默认设置即可。
Project Settings:
堆栈设置:
生成代码后编译,运行后将USB连接到PC机,PC电脑能够识别到这个U盘,但不能打开它。
由于目前只是实现了USB通讯,但是数据还不能写入或从平台读出,因此还只是一个假的U盘,下面我们将通过SD卡来实现数据的存储。
我们回到CubeMx上,在pinout中为系统增加SDIO外设:
Pinout:
SDIO->Mode:SD 4bits Wide bus
并增加PH13管脚来检测SD卡是否存在,当然设置为输入模式:
时钟树保持不变,接下来在配置页面进行参数配置:
在GPIO设置:
PH13配置为上拉输入模式,这里为其设了一个标签SD_DETECT。
接着为SDIO的读写各自增加DMA,以此增加读写速度:
最后在NVIC中为使用到的中断分配中断优先级:
这里的中断优先级设置很关键,顺序设置错了会出现问题,这里有限级必须保持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驱动源码,因此,我们挪用下之前的源码。如下图:
如上图,除了新增的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端通过打开U盘的方式访问SD卡的内容,并创建文件写入内容也是OK的。
6 结论
这里的关键是SDIO global interrupt,DMA,就是USB中断优先级的顺序,必须SDIO>DMA>USB,不然会出错,其他的都好处理,使用CubeMx生成的默认配置即可。