基于STM32F407的SDCard读写操作及USB挂载(HAL库)
本来在上一篇SD卡读写也都OK了,后来想着挂载SD卡做U盘,就去查了下资料……结果基本全是HAL库的,原来没用过HAL库,于是本着好奇的心态去下了,说实话,确实看起来简单多了,不过还是有点不习惯,所以在折腾完以后,又回去把标准库的SD卡挂载也做了。不过正常情况下,HAL库上手肯定比标准库会快一点啦。
关于HAL库的SD卡,一搜一大把,其实没啥特别要说的,非要说只有一些细节,一开始参考的几篇总有或多或少一些小错误,导致前面调试总是不顺,尴尬。
不多废话了,下面是正题:
目录
基于STM32F407的SDCard读写操作及USB挂载(HAL库)
硬件电路
管脚连接(这个和上一篇一样,就不多写了)
STM32CubeMX Untitled图形化配置
1.新建项目(New Project)
2.设置时钟
3.设置SDIO
4.配置文件系统
5.设置USB
6.配置中断优先级
7.配置系统时钟树
8.保存项目文件
9.进行项目生成
软件修改
1.SDCard测试
2.USD挂载U盘
相关下载
硬件电路
管脚连接(这个和上一篇一样,就不多写了)
STM32-GPIO |
STM32-GPIO 复用功能 |
SDCard管脚 |
其他 |
PC8 |
SDIO-D0 |
DATA0 |
|
PC9 |
SDIO-D1 |
DATA1 |
|
PC10 |
SDIO-D2 |
DATA2 |
|
PC11 |
SDIO-D3 |
CD/DATA3 |
|
PC12 |
SDIO-CLK |
CLK |
|
PD2 |
SDIO-CMD |
CMD |
|
STM32CubeMX Untitled图形化配置
HAL库可以采用STM32CubeMX进行自动生成(官网有下载),方法简单,但需要注意配置细节!!
1.新建项目(New Project)
根据自己的方案选择MCU,我用的STM32F407VET6。(可以提前下载,也可以直接在软件里下载,介于我提前下载了,但怎么也导不进去,推荐直接在软件下吧,反正也挺快的!)
2.设置时钟
红色是因为我的截图是配置完成再去的,因为配置SDIO的原因,可以忽略(等配置完SDIO再去看就能看到这个红色了),是提示部分已经被使用。
3.设置SDIO
选择4bit Wide,打开中断,增加SDIO_TX和SDIO_RX DMA,DMA数据类型改为Byte。
切记!GPIO需要改成上拉模式(在左侧菜单栏GPIO配置里SDIO修改,在这个页面改不了?不知道是不是我的软件问题?!)。
如果不修改,SD卡初始化可能可以过,但读写可能卡死,囧,当时卡了好一会,也可能是因为我硬件是偷懒直接飞线没有上拉的原因。
时钟倍数按照自己的时钟来计算,如果初始化有数据,读写无数据,将数值修改(默认是0)。
如果不修改,SD卡初始化可以读出数据,但读、写操作可能出错,我出现的情况的是写操作可以但读操作异常。在后期挂载USB做U盘时,有一次忘记修改,出现过读取乱码或无法打开的情况,更改时钟分频后恢复正常。
如果只是SD卡读写,到这里基本就差不多了(就缺一个中断优先级的配置,即6)。
4.配置文件系统
设置路径中文可识别
SDIO的 DMA
如果只需要文件系统,不需要挂载电脑端读取U盘,到这里基本就差不多了(就缺一个中断优先级的配置,即6)。
5.设置USB
使能中断
设置USBdevice
6.配置中断优先级
前面提到过的优先级,优先级不当会导致设备读写冲突而卡死
中断优先级的大小关系是:
SDIO global interrup >SDIO DMA > USB
数字越小优先级越高(数字随便,满足上面说的就可以)
7.配置系统时钟树
我的板子是外部8M,实际配置按照电路板设计来选择。按照手册SDIO时钟48M
8.保存项目文件
初次创建时,项目文件名称和路径不能有中文(已创建好的project再次打开修改时,可以有中文路径),否则在生成项目时会出错而导致无法生成.s。堆栈适当加大(做文件系统和U盘尽量大一点,只是单纯读写则无所谓)。
Code Generator下的Generated files第一项进行勾选,可以将.c、.h文件单独生成,否则将会全部堆积在main.c文件中。
如果需要挂载做U盘,除了上面说的堆栈需要增加(我最后改成了0x8000)还有USB的MSC_MEDIA_PACKET(默认512,我改成了32768),否则传输文件时速度慢得令人发指,不过在FS模式下,我自己做下来基本存入文件速度在800-900K,最快1M+,导出文件会快点。就只是传输TXT文本数据什么的10~100M+的文件基本上还算可以接受,如果是G以上的还是用HS吧。这几个参数可以在代码修改但建议在配置时修改,不然重新生产配置时候一个刷新,忘了自己没改就尴尬了(别问,问就是这种蠢事不小心发生过了)。
9.进行项目生成
点击GENERATE CODE,忽视WARNINGS,生成后打开Project即可。
生成后打开project
至此,项目的工程文件初步生成结束,HAL库在使用上比较便捷,大多数的初始化已经由系统完成,按照功能在需要的地方加上用户代码即可,注意,用户代码添加在/* USER CODE BEGIN */及/* USER CODE END */之间,更改图形化配置时,重新生成项目文件代码不会被删除,否则将会被删除覆盖。
软件修改
1.SDCard测试
注释MX_USB_DEVICE_Init();先不使用USB,如果没有配置则不用 管。(其实不注释也没事,只是调试一个功能,我就习惯性一步一步来免得万一相互有影响呢,毕竟HAL库我第一次用)
添加SDCard初始化,SD_Driver.disk_initialize(0); 初始化成功后可以读取到SDCard信息,可以在Watch窗口添加hsd.SdCard查看或者使用串口打印输出到电脑查看信息。不同的卡信息不一样,以自己用的卡为准!
测试读写操作(偷懒我就直接测试文件系统读写了,如果一步一步熟悉,可以先试着块读写,然后再看文件系统)
在main()中添加初始化
/* USER CODE BEGIN 0 */
uint8_t readBuf[512];
uint8_t writeBuf[512];
BYTE work[_MAX_SS];
char *fileName = "test.csv";
uint32_t writeLen;
uint32_t readLen;
/* USER CODE END 0 */
加文件读、写操作
可以通过观察readBuf来查看读取是否正确。
文件读、写可以参考上一篇。
举个例子:挂载、文件写、文件读、卸载(用法和上一篇基本一样)
retSD = f_mount(&SDFatFS, SDPath, 0);
retSD = f_open(&SDFile,fileName,FA_OPEN_APPEND|FA_WRITE);
sprintf((char *)writeBuf,"%s","11,44,33\n");
retSD = f_write(&SDFile,writeBuf,strlen((const char *)writeBuf),&writeLen);
retSD = f_close(&SDFile);
retSD = f_open(&SDFile,fileName,FA_OPEN_EXISTING|FA_READ);
retSD = f_read(&SDFile,readBuf,512,&readLen);
f_close(&SDFile);
f_mount(NULL,SDPath,1);
Fatfs文件系统下的读写也就这样了,用HAL库真的方便好多!
2.USD挂载U盘
只是挂载文件,以上设置直接就可以,如果要进行传输,建议增大堆栈、修改USB_Device缓存(前面提过了),可以在STM32Cube配置(前面提过了),也可以在代码中修改(不太推荐,毕竟重新生成配置时候万一忘了改就尴尬了,但是为了测试速度,测试参数时候可以这么干)
//#define MSC_MEDIA_PACKET 512U
#define MSC_MEDIA_PACKET 32768U
Stack_Size EQU 0x8000
Heap_Size EQU 0x8000
STM32Cube在生产项目文件时,已经写好了函数接口,在usbd_storage_if.c中需要的函数添加自己的代码即可。一般来说主要要加东西的是STORAGE_Init_FS, STORAGE_GetCapacity_FS, STORAGE_Read_FS, STORAGE_Write_FS。
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
};
STORAGE_Init_FS我没加代码,可以把SD卡初始化放在里面,不过由于我SD卡会需要单独使用,所以就单独在main()里面初始化了。
STORAGE_GetCapacity_FS
int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
/* USER CODE BEGIN 3 */
HAL_SD_CardInfoTypeDef info;
if(HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_TRANSFER)
{
HAL_SD_GetCardInfo(&hsd, &info);
*block_num = info.LogBlockNbr;
*block_size = info.LogBlockSize;
return USBD_OK;
}
return USBD_FAIL;
/* USER CODE END 3 */
}
STORAGE_Read_FS,注意,如果前面配置用了DMA这里调用的函数就是HAL_SD_ReadBlocks_DMA(),否则调用HAL_SD_ReadBlocks()。
之前调试一直读不到数据,参考的资料配置都用的DMA,但是这里的函数用的HAL_SD_ReadBlocks(),囧,也不知道是不是别的地方改了代码,尽信书不如无书啊!后来直接一条一条跟踪着查……说到底还是第一次用对HAL库不太了解,跟着别人的资料走,然后走到岔路去了。
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_DMA(&hsd, buf, blk_addr, blk_len) == HAL_OK )
//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 */
}
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_DMA(&hsd, buf, blk_addr, blk_len) == HAL_OK )
//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 */
}
上面是非常粗略的,简单的,就把读写调用了下 ,再加上初始化,基本上用是可以用了。其他函数按照自己的需求添加自己需要的功能即可。
注意,初始化SDCard后,才能在电脑看到U盘显示。我是把初始化放在main(),自己可以根据需要自己放。
/* USER CODE BEGIN 2 */
SD_Driver.disk_initialize(0);
/* USER CODE END 2 */
STM32读、写文件时,不要去操作电脑端的U盘。另外写入后,千万、一定、绝对不要忘记close,不然全部白写!
顺便提一句不是吐槽的吐槽,HAL库调来调去的真的好烦啊,跟着他跑可以对着N个文件跑一大圈。其实如果偷懒直接调用BSP_SD_Init();初始化也是可以的。读、写也是一样。
HAL库也有方便的地方,例如不管你用那块芯片,配置完了,上面的函数调用基本就一致了,不用再去纠结寄存器这些了。
——————————————————————————
新增:
相关下载
1.工程文件源码
https://download.csdn.net/download/a390478024/86742278
2.STM32CubeMX安装包
https://download.csdn.net/download/a390478024/86742647