从零实现 USB_SLAVE读卡器 USB_MSC+FATFS+SD/SPI_FLASH/NANDFLASH

欢迎大家留言交流~

 

要实现USB读卡器,其核心就是F429作为从设备用USB_MSC与主机通讯,利用FATFS从SD卡、SPI_FLASH、NANDFLASH中读写数据。

什么是OTG?OTGOn-The-Go的缩写,是近年发展起来的技术。20011218日由USB Implementers Forum公布,主要应用于各种不同的设备或移动设备间的联接,进行数据交换。

产生背景

USB技术的发展,使得PC和周边设备能够通过简单的方式、适度的制造成本,将各种数据传输速度的设备连接在一起。上述我们提到的应用,都是通过USB连接到PC,并在PC的控制下进行数据交换。

但这种方便的交换方式,一旦离开了PC,各设备间无法利用USB口进行操作,因为没有一个设备能够像PC一样充当主机。

作用

OTG技术就是在没有Host的情况下,实现设备间的数据传送。

 

USB模式为三种:1.OTG/dual_role_device 具备一根OTG_ID线用来判断为主设备或时从设备的,作为OTG设备的时候使用。也就是可以根据外接的来判断对面是主还是从,自己进行主从转换。

2.host_only,仅作为主设备。OTG_ID就用不到了。

3.device_only,本次实验是要我们的设备做一个大容量存储设备,就是处于这个模式。

 

F429实验

由于USB要用48MHZ,而主频HCLK之前是180M,无法正数分频至48M。原子采用倍频HCLK到192MHZ,再分频USB时钟频率至48MHZ。

但是F429的HCLK不是最大只能到180MHZ吗?我用cubemx也无法设置到192MHZ啊。原子说,超频的不多不影响使用,那我觉得还是不要这么做比较好,不正规。

输入USB时钟频率为48MHZ让cubemx自己去算,算出来HCLK是120MHZ,太低了。

上网查找,HCLK为168MHZ时可以。我设置下去,果然可行。那么就要注意了,如果用到了USB,之前其他功能的时钟肯定就改变了。

所以,以后如果要用到USB,直接将HCLK设置为168MHZ。

 

VBUS管脚不用选,原子原理图上的USB接口上的VBUS也没有接单片机,直接通往5V就好了。

 

FATFS支持USB DISK也就是U盘,但是必须是在HOST模式,支持MSC的情况下。

F103实验

选上USB,看来F103只支持从设备啊。选项都默认。

配置中间层,选择MSC大容量存储设备。其余选项默认

 

 

修改中断,让USB的中断最高。生成代码。

 

与原子不同,他用的USB驱动是2013年版的,cubemx生成的是2020年版的。所以有一定差异。主要表现在USB调用SD卡的函数不同,他是在mass_mal.c(F103)usbd_storage_msd.c(F429)中,我的是在usbd_storage_if.c(F103)中,但是作用是一样的,主机操作USB_slave读写,但是USB驱动不知道该去读写哪的扇区,这种文件就是供用户修改,可以连接至SD卡的读写函数。

下载验证

程序编写完毕后,编译下载,计算机第一次识别不出来,磁盘驱动器能识别出来是STM 的USB设备,但是在计算机中找不到磁盘,原来是SD卡没插入。那肯定不行啊。。。

接着插入SD卡,重启开发板,真的识别出来了。

 

这个“U盘”的名字是我在格式化tf卡的时候,起的名字。他存储在tf卡的启动扇区,扇区0的FAT32格式下。

打开U盘,只有一个文件,这个文件就是上个FATFS实验生成的。

打开这个文件

尝试修改一下,看用开发板的FATFS读出的一致么。我看到网上有人提问如果开发板没有做FATFS,这种USB读卡器还能用么。到此,我们知道,USB驱动调用的都是直接调用SD卡的读写函数,跟FATFS一样,都是中间层。同为中间层不存在调用关系。

修改完后,操作开发板挂载SD卡,然后读这个文件。一致!

 

用开发板创建一个文件,创建成功,关闭失败,读取失败

 

怀疑是文件名称问题,改回原有名称,尝试创建。

还是关闭文件失败。那是不是有可能是FATFS的创建文件功能收到了影响呢?

用上一个实验的工程实验,没有问题,一切正常。

因为我看到我尝试把USB中断优先级比TIM1低

 

我以前调试过USB,USB的优先级应该最高。把USB中断优先级设为1,TIM设为2

结果还是不行。但是USB优先级调高肯定没问题。

 

我现在怀疑是否栈的空间不够了,调至0x2000看看效果。

还是不行啊。在写完文件后关闭返回的是1.

而且读出的数据也不对

 

因为在FATFS实验中给的栈是0x1000,那么是不是还得增加栈的大小啊,设置为0x2000。

还是不行!

 

实在不知道了,上网求助!顺带吐槽一下原子哥,不是帮顶就是说好好检查下代码。大哥,我给你跪了。

还是STM社区给力,大神真多。

还真有可能是FATFS和USB同时操作SD卡造成的影响。我去看原子例程的USB读卡器实验,发现都没有FATFS。也就是没有同时实现USB、FATFS同时操控SD卡。

那么我的代码中USB是用的封装过的函数,即SD卡实验中的函数。

在读写中关闭了中断。也就是说其他中断打扰不了USB.

而FATFS调用的是自动生成的

 

 

 

 

 

bsp_driver_sd.c是自动生成的SD卡操作底层文件。

然后被自动生成的sd_diskio.c中间层调用。

FATFS调用->SD_read调用->BSP_SD_ReadBlocks..

我们看到整个过程,没有关闭中断保护。那么就有可能FATFS对SD卡的操作被其他中断打断。那么增加关闭中断,试试效果

不接USB线实验下FATFS。

成了。。。

接上USB线,等待计算机识别出来SD卡,再实验FATFS

计算机识别出“U盘”,并且文件内容正确

而且FATFS也可以成功操作。

那么我们试试创建新文件“TanCheng.txt”和内容,成功

 

但是呢,在计算机中刷新了很多遍,还是看不见“TanCheng.txt”

重新插拔usb线,相当于重新枚举下设备,文件才出来。

 

那么我尝试从计算机修改“TanCheng.txt”内容,看FATFS能否成功。成功!

 

那么我们之前设置的优先级为1,还是不够高,设置为0怎么样?

 

不成功,能识别出来是USB,但是操作失败。所以还是设置回优先级为1吧。

 

得了,测试完毕,完成了USB读卡器的功能。也就是相当于U盘了。


总结:

刚开始配置完USB发现不行,真是吓一跳,因为USB驱动比FATFS难太多了。fatfs都那么费劲,这USB不得弄上个一个星期。赶紧回顾下以前的USB笔记,回顾了一下USB的枚举、设备描述符、管道等知识点。发现这么学实在是太费时间了。还是通过不断调试,上网查找资料解决实际问题吧。没想到调着调着看到大神的几句话就豁然开朗了。

说到这里还真的很奇怪,之前FATFS使用时不加开关中断也好使,加上USB就不好使了。可能USB的操作还是太频繁了吧。

  1. F103创建完代码后,去usbd_storage_if.c增加SD卡操作的函数。
  2. USB读卡器和FATFS的核心都是中间层,应用层通过这两个中间层去操作SD卡的底层,也就是读写。归根结底操作的都只是SD卡的读写函数而已。
  3. 读写SD卡期间一定要关中断,不然会造成正在读写SD卡时被打断,从而读写失败。但是cubemx重新生成代码又会把sd_diskio.c和bsp_driver_sd.c的文件重新创建,开关中断代码就被擦除了,虽然__weak uint8_t BSP_SD_ReadBlocks(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout)前面有weak,但是在整个fatfs中间层和底层都找不到合适的地方去重新定义这个函数,所以只好在每次cubemx之后记得加上
  4. __weak uint8_t BSP_SD_ReadBlocks(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout)
    {
      uint8_t sd_state = MSD_OK;
        __disable_irq();   // 关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!)
      if (HAL_SD_ReadBlocks(&hsd, (uint8_t *)pData, ReadAddr, NumOfBlocks, Timeout) != HAL_OK)
      {
        sd_state = MSD_ERROR;
      }
        __enable_irq();    // 开启总中断
      return sd_state;  
    }
  5. __weak uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout)
    {
      uint8_t sd_state = MSD_OK;
        __disable_irq();   // 关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!)
      if (HAL_SD_WriteBlocks(&hsd, (uint8_t *)pData, WriteAddr, NumOfBlocks, Timeout) != HAL_OK) 
      {
        sd_state = MSD_ERROR;
      }
        __enable_irq();    // 开启总中断
      return sd_state;  
    }
  6. 计算机无法实时看到SD卡上新增的文件,需要插拔一下,重新枚举。FATFS可以实时看到。
  7. 枚举的过程不要打断,比如debug什么的,全速运行。如果枚举不上,重新插拔试试。
  8. USB的优先级要设置高,但是不要到0!!

拓展阅读:

STM32的USB模块可以产生三种中断:USB唤醒中断、USB高优先级中断和USB低优先级中断,在STM32的参考手册中没有详细说明这三种中断对应哪些事件,现说明如下:
1)USB唤醒中断:在中断向量表中的位置是42。这个中断在USB设备从暂停模式唤醒时产生,唤醒事件由USB_ISTR寄存器的WKUP位标识。
2)USB高优先级中断:在中断向量表中的位置是19。这个中断仅由USB同步(Isochronous)模式传输或双缓冲块(Bulk)传输模式下的正确传输事件产生,正确传输事件由USB_ISTR寄存器的CTR位标识。
3)USB低优先级中断:在中断向量表中的位置是20。这个中断由所有其它的USB事件产生,例如正确传输(不包括同步模式和双缓冲块模式)、USB复位等,事件标志位在USB_ISTR寄存器中。
在STM32的USB开发包的例子中包含了上述中断的处理,例如在USB扬声器的例子中,CTR_HP函数处理USB高优先级中断;在所有例子中都有USB_Istr()函数处理USB低优先级中断。

 

USB如何区分低速、全速和高速设备:对于全速和高速设备,上拉电阻是接在D+上;而低速设备则是上拉电阻接在D-上。

USB枚举过程:

(1)       USB主机检测到USB设备插入后,对设备复位。

(2)       USB主机对设备发送获取设备描述符的标准请求。

(3)       设备收到该请求后,在数据过程将设备描述符返回给主机。

(4)       主机在成功获取到一个数据包的设备描述符后并且确认无错误后,返回一个0长度的状态数据包给设备。

(5)       主机再对设备复位一下,接下来进入到设置地址阶段。

(6)       USB主机发出一个设置地址的请求,地址包含在建立包中。

(7)       USB设备在收到地址后,返回0长度的状态包。

(8)       主机收到0长度的状态包之后,返回一个ACK。

(9)       设备在收到这个ACK之后,就可以启用新的地址了。

USB一直进中断的原因:主机每1ms就给设备发送一个sof,3ms没反应主机就挂起。所以一直进中断属于正常现象。

USB虚拟串口使用时应注意:

(1)       用于上拉的控制位使能时,主机能检测到USB设备。

(2)       设备插入时,地址为0,后由主机分配地址。

(3)       枚举结束前(即设备被识别前),不能中断程序,否则设备不能被识别。

(4)       设备发送usb_SendDataToHost("\r\n$ERRCMD#\r\n", 10)注意数据长度为10,而不为16.(不知道是不是“\$#这样的符号不算,求大神赐教”)

主机发送“WHO’S DEMO”这类有空格的字符串时,空格会被省略,在设备里计算长度时应为9,而不为10.


 

  • 6
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值