RT-Thread-基于usb_device用SD卡实现U盘功能(读卡器功能)——STM32F407ZG

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


前言

作为新入门的嵌入式选手,基于项目需要最近在学习RT-Thread操作系统,鉴于自己健忘的记性,打算记录下来后面好回顾学习。
今天要总结的是RT-Thread-基于usb device用SD卡实现U盘功能(读卡器功能),参考了很多大神的博文,站在大神的高度进行内容重复和汇总,算是督促自己学习进步的手段之一吧,如有错误请大家及时指出,感谢!


提示:以下是本篇文章正文内容,如有错误请评论或私信指出哈

一、USB概述

USB(Universal Serial Bus)是一种支持热插拔的通用串行总线。在 USB 1.0和 USB 1.1 版本中,只支持 1.5Mb/s 的低速(low-speed)模式和 12Mb/s 的全速(full-speed)模式,在 USB 2.0 中,又加入了480Mb/s 的高速模式,USB 3.0(super speed),传输速率最大5Gbps。

STM32F407 自带的 USB 符合 USB2.0 规范,且探索者 STM32F4 开发板没有外扩高速 PHY 芯片,仅支持 USB OTG FS(FS,即全速,12Mbps)。在主机模式下,OTG FS 支持全速(FS,12Mb/s)和低速(LS,1.5 Mb/s)收发器,而从机模式下则仅支持全速(FS,12 Mb/s)收发器。OTG FS 同时支持 HNP 和 SRP。

标准 USB 共四根线组成,除 VCC/GND 外,另外为 D+和 D-,这两根数据线采用的是差分电压的方式进行数据传输的。在 USB 主机上,D-和 D+都是接了 15K 的电阻到地的,所以在没有设备接入的时候,D+、D-均是低电平。而在 USB 设备中,如果是高速设备,则会在 D+上接一个 1.5K 的电阻到 VCC,而如果是低速设备,则会在 D-上接一个 1.5K 的电阻到 VCC。这样当设备接入主机的时候,主机就可以判断是否有设备接入,并能判断设备是高速设备还是低速设备。

在 USB 体系中又包括 USB Host(主机)和USB Device(设备)

  • USB Host:
    任何USB系统中只有一个主机。 主机系统的USB接口被称为主机控制器。 主机控制器可以以硬件,固件或软件的组合来实现。 根集线器集成在主机系统内以提供一个或多个连接点。
  • USB Device USB Device 可以分为 USB Hub 和 USB Function。
    USB Hub 提供了一种低成本、低复杂度的 USB接口扩展方法。Hub 的上行端口面向 HOST,下行端口面向设备(Hub 或功能设备)。在下行端口上,Hub 提供了设备连接检测和设备移除检测的能力,并给各下行端口供电。Hub 可以单独使能各下行端口。不同端口可以工作在不同的速度等级(高速/全速/低速)。
    USB Function 能够通过总线传输或接收数据或控制信息的设备,在 USB2.0 标准中,别称为 Class。

本文主要是基于正点原子STM32F407ZG开发板,给出了 USB Device 读写 SD卡建立的U 盘的配置和使用示例。


二、STM32F4相关硬件介绍

MiniUSB 接口与 STM32 的连接电路图
USB 座没有直接连接到 STM32F4 上面,而是通过 P11 转接,所以我们
需要通过跳线帽将 PA11 和 PA12 分别连接到 D-和 D+
不过这个 MiniUSB 座和 USB-A 座(USB_HOST)是共用 D+和 D-的,所以他们不能同时使用。这个在使用的时候,要特别注意!!本实验测试时,USB_HOST 不能插入任何 USB 设备!


三、RT-Thread配置usb_device

1.RT-Thread Setting设置

需要打开虚拟文件系统和SDIO,使用SDIO作为USB设备来实现。
组件与服务配置
打开“使用USB设备”,配置使能为大容量存储设备”Enable to use device as Mass Storage device“,需要注意的是“设置MSC类磁盘名”时要对应!!!默认为flash0,改为了sd0。
组件配置

2.board.h的4步实现SDIO配置

board.h配置SDIO

第一步:已经在“1.RT-Thread Setting设置”中实现了。

第二步:定义与sdio相关的宏(如上图):

例如:#define BSP_USING_SDIO——board.h

第三步:将您的sdio初始化函数从stm32cubemx生成的stm32xxxx_hal_msp.c复制到board.c文件的末尾:

(注:打开CubeMx Settings,设置完成后生成代码,在工程中的cubemx文件夹中找到stm32xxxx_hal_msp.c文件,获得相应初始化函数)
cube配置
例如: void HAL_SD_MspInit(SD_HandleTypeDef hsd)

void HAL_SD_MspInit(SD_HandleTypeDef* sdHandle)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(sdHandle->Instance==SDIO)
  {
  /* USER CODE BEGIN SDIO_MspInit 0 */

  /* USER CODE END SDIO_MspInit 0 */
    /* SDIO clock enable */
    __HAL_RCC_SDIO_CLK_ENABLE();

    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    /**SDIO GPIO Configuration
    PC8     ------> SDIO_D0
    PC9     ------> SDIO_D1
    PC10     ------> SDIO_D2
    PC11     ------> SDIO_D3
    PC12     ------> SDIO_CK
    PD2     ------> SDIO_CMD
    */
    GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
                          |GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

    /* SDIO interrupt Init */
    HAL_NVIC_SetPriority(SDIO_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(SDIO_IRQn);
  /* USER CODE BEGIN SDIO_MspInit 1 */

  /* USER CODE END SDIO_MspInit 1 */
  }
}

第四步:修改stm32xxxx_hal_config.h文件以支持sdio外围设备。定义与外围设备相关的宏。

*例如: #define HAL_SD_MODULE_ENABLED
stm32xxxx_hal_config.h

第五步:配置设备文件系统或其他应用程序

文件系统挂载
前2个步骤开启文件系统的时候就已经被调用了。
第3步:如果是使用SDIO,会在程序中自动初始化注册为块设备,则也不需要编写,如果是使用SPI则需要编写注册块设备语句。
第4、5步的程序编写,先格式化,然后再挂载块设备到DFS目录上,之后就可以开始验证工作了。

代码如下(示例):

void mnt_init(void)
{
    rt_thread_mdelay(100);//这段延时必须加上,系统上电过程中存在延时,否则会出现先挂载后注册块设备sd0的情况
    //mkfs("elm","sd0");//挂在前需格式化
    rt_device_t dev=RT_NULL;
    dev=rt_device_find("sd0");
    if(dev != RT_NULL)
    {
        if(dfs_mount("sd0","/","elm",0,0)==0) //挂载文件系统,参数:块设备名称、挂载目录、文件系统类型、读写标志、私有数据0
        {
            rt_kprintf("dfs mount success\r\n");
        }
        else
        {
            rt_kprintf("dfs mount failed\r\n");
            dfs_mkfs("elm","sd0");//若挂载失败,则格式化
        }
    }
}

INIT_ENV_EXPORT(mnt_init);  //自动初始化

3.board.h的5步实现USB_DEVICE配置

board.h配置usb_device

第一步:已经在“1.RT-Thread Setting设置”中实现了。

第二步:定义与usb设备相关的宏:

例如:#define BSP_USING_USBDEVICE——board.h

第三步:将您的usb设备初始化函数从stm32cubemx生成的stm32xxxx_hal_msp.c复制到board.c文件中:

cube

例如: void HAL_PCD_MspInit(PCD_HandleTypeDef hpcd)

void HAL_PCD_MspInit(PCD_HandleTypeDef* pcdHandle)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(pcdHandle->Instance==USB_OTG_FS)
  {
  /* USER CODE BEGIN USB_OTG_FS_MspInit 0 */
  /* USER CODE END USB_OTG_FS_MspInit 0 */

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USB_OTG_FS GPIO Configuration
    PA11     ------> USB_OTG_FS_DM
    PA12     ------> USB_OTG_FS_DP
    */
    GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USB_OTG_FS clock enable */
    __HAL_RCC_USB_OTG_FS_CLK_ENABLE();

    /* USB_OTG_FS interrupt Init */
    HAL_NVIC_SetPriority(OTG_FS_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(OTG_FS_IRQn);
  /* USER CODE BEGIN USB_OTG_FS_MspInit 1 */

  /* USER CODE END USB_OTG_FS_MspInit 1 */
  }
}

第四步:在STM32CubeMX生成的SystemClock_config()中配置usb外设时钟,并替换drv_clk.c中的SystemClock_config()函数。【!!!这里需要注意的是:USB OTG FS需要特定的时钟48MHz(来自于特定 PLL 输出 (PLL48CLK) )】

CubeMX配置时钟
SystemClock_config()函数

第五步:修改您的stm32xxxx_hal_config.h文件以支持usb设备外围设备。定义与外围设备相关的宏

例如:#define HAL_PCD_MODULE_ENABLED
stm32xxxx_hal_config.h文件


四、遇到的问题和解决方法

问题1:disk open error

通过第三部分已完成了配置流程,但在我实际实验中,遇到了报错:“disk open error”。
通过搜索发现问题在mstorage.c文件下rt_device_open(data->disk, RT_DEVICE_OFLAG_RDWR) != RT_EOK出现了报错。
通过一顿查找,发现了和我们有相似问题的博文链接
问题是:块设备的访问问题,只允许被打开一次!!
解决方法参考了上述博文,可具体点上面链接查看。
我采用的方法是usb mstorage.c 的_function_enable函数那里在open前先做判断,如果已经open了,则不需要再open了:

 if ((data->disk->flag & RT_DEVICE_FLAG_STANDALONE) &&(!(data->disk->open_flag & RT_DEVICE_OFLAG_OPEN)))
 {
        if(rt_device_open(data->disk, RT_DEVICE_OFLAG_RDWR) != RT_EOK)
        {
            rt_kprintf("disk open error\n");
            return -RT_ERROR;
        }
 }

问题2:串口打印乱码

问题是CubeMX配置时时钟配错了,我所用的板子的晶振是8M,这类问题的排查可根据打印数据的长度来排查。

问题3:配置成功后一些未定义的错误

需要依据具体情况来补充。

问题4:串口无反应的情况

在使用cubeMX配置后,串口需要重新配置。

问题5:用来ENV工具配置时,遇到的bug是文件操作指令无法运行,list_device无内容

问题在于SD卡第一次未初始化,需要调用:
dfs_mkfs(“elm”,“sd0”);//若挂载失败,则格式化

调试时遇到的问题:

  • 晶振方面
  • RT-Thread版本方面
  • 时钟方面
    如遇到SDIO和USB无法正常运行的情况,可考虑这三个方面的原因。

五、实验结果

编译无错后,烧录程序,连接串口list_device可以看到sd0,usb device。

插入MiniUSB 座到电脑后,此时SD卡能被识别,相当于U盘,类似读卡器的功能就实现了。


总结

其实是一个简单的配置,但还是遇到了很多问题。主要参考了一些博文、rt-thread参考文档和STM32开发指南,算是对自己学习的一种总结吧,希望坚持下去,感谢各位大神们多多指点。

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值