基于STM32F407的SDCard读写操作及USB挂载(HAL库)

基于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

  • 12
    点赞
  • 74
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值