1 前言 本文总结如何使用CubeMx制作一个U盘。 2 了解硬件平台由于本文将基于STM3240G-EVAL平台,主要用到USB外设和SDIO外设,所以我们主要是看这两部分外围电路。 USB外围电路:
图1 USB外围电路
我们这里USB将作为从设备,因此,只需要关注上图中的下面部分电路即可。
SDIO外围电路:
图2 SDIO外围电路
由上图可知,通过PH13管脚的状态可以得知SD卡的拔插状态。
另外,STM3240G-EVAL评估板使用的是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盘,但不能打开它。
图6 目前还不能打开这个假U盘
由于目前只是实现了USB通讯,但是数据还不能写入或从平台读出,因此还只是一个假的U盘,下面我们将通过SD卡来实现数据的存储。
我们回到CubeMx上,在pinout中为系统增加SDIO外设:
Pinout:
SDIO->Mode:SD 4bits Wide bus
并增加PH13管脚来检测SD卡是否存在,当然设置为输入模式:
图7 PH13管脚用来检测SD是否存在
时钟树保持不变,接下来在配置页面进行参数配置:
在GPIO设置:
图8 GPIO配置
PH13配置为上拉输入模式,这里为其设了一个标签SD_DETECT。
接着为SDIO的读写各自增加DMA,以此增加读写速度:
图9 SDIO使用DMA传输
最后在NVIC中为使用到的中断分配中断优先级:
图10 中断优先级设置
这里的中断优先级设置很关键,顺序设置错了会出现问题,这里有限级必须保持SDIO global interrup >SDIO DMA > USB ,数字越小,优先级越高,数字刚好倒过来,如上图,5<6<7.
就这样再次重新生成代码。 4 代码修改在正式对代码修改前,我们现将一个源码文件bsp_driver_sd.c及其头文件加入工程,此文件是之前一片文章),也是由CubeMx自动生成的代码文件,可能是因为本工程并没有使用到文件系统,且CubeMx并不知道USB MSC与SDIO有明确关联,所以并没有生成SD卡对应的bsp驱动源码,因此,我们挪用下之前的源码。如下图:
图11 工程源码文件
如上图,除了新增的bsp_driver_sd.c源文件,我们主要是针对u***d_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 */ }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
这里使用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 */ }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
同样,这里也是通过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 */ }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
如上代码,我们通过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 */ }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
通过SD卡BSP驱动接口函数BSP_SD_GetStatus获取SD卡状态,并将结果返回。 到此,代码基本修改完成,接下来解释测试验证工作。 5 测试重新编译工程,并烧录到STM3240G-EVAL评估板,将通过micro-u***线连接到PC机后,多出一个移动U盘,双击打开,如下图所示:
图12 PC端打开文件
如上图,可以在PC端通过打开U盘的方式访问SD卡的内容,并创建文件写入内容也是OK的。
6 结论这里的关键是SDIO global interrupt,DMA,就是USB中断优先级的顺序,必须SDIO>DMA>USB,不然会出错,其他的都好处理,使用CubeMx生成的默认配置即可。 嵌入式学习交流群:561213221
|