RT-Thread论坛
直播中

王利祥

8年用户 1520经验值
私信 关注
[问答]

CherryUSB怎样实现U盘动态加载?

现已在AT32F403A上实现了文件系统挂载,将FAL分区filesys挂载为elm文件系统;同时也移植了CherryUSB并实现了U盘功能。但是文件系统和U盘使用的是基于同一FAL分区创建的块设备,致使二者不能同时工作。使用CherryUSB怎样实现U盘动态挂载和卸载呢?即在不插USB线时,挂载文件系统,应用可正常使用文件系统,当插入USB线时,动态卸载文件系统并挂载U盘,当弹出U盘或插出USB线时,再动态地卸载U盘并挂载文件系统。自己偿试在CherryUSB的事件回调usbd_event_handler中完成以上操作,但没成功,相关代码执行不到。是CherryUSB不支持动态加载吗?


  • /* assume the block device is 512M */
  • #define BLOCK_DEV_NAME      "filesys"
  • #define BLOCK_SIZE          4096
  • #define BLOCK_COUNT         (7 * 1024 * 1024 / BLOCK_SIZE)
  • static rt_device_t blk_dev = RT_NULL;

  • static void usbd_event_handler(uint8_t busid, uint8_t event)
  • {
  •     switch (event) {
  •         case USBD_EVENT_RESET:
  •             break;
  •         case USBD_EVENT_CONNECTED:
  •             //卸载文件系统
  •             //挂载U盘
  •             break;
  •         case USBD_EVENT_DISCONNECTED:
  •             //卸载U盘
  •             //挂载文件系统
  •             break;
  •         case USBD_EVENT_RESUME:
  •             break;
  •         case USBD_EVENT_SUSPEND:
  •             break;
  •         case USBD_EVENT_CONFIGURED:
  •             break;
  •         case USBD_EVENT_SET_REMOTE_WAKEUP:
  •             break;
  •         case USBD_EVENT_CLR_REMOTE_WAKEUP:
  •             break;

  •         default:
  •             break;
  •     }
  • }




回帖(1)

陈静

2025-10-24 17:58:45

在AT32F403A上实现文件系统与CherryUSB U盘动态切换需解决关键问题:同一块设备无法同时被文件系统和U盘驱动访问。以下是分步解决方案:




核心方案:动态挂载/卸载文件系统 + USB事件驱动


在CherryUSB的USB连接/断开事件中动态切换文件系统和U盘模式:


#include "fal.h"
#include "elm_fatfs.h"
#include "usbd_msc.h"

// 定义块设备及挂载点
static struct fal_blk_device *blk_dev; // FAL块设备指针
const char *mount_point = "/";        // 文件系统挂载点

// 挂载文件系统
int mount_filesystem(void) {
    FRESULT res = f_mount(&blk_dev->fatfs, mount_point, 1);
    return (res == FR_OK) ? 0 : -1;
}

// 卸载文件系统
void unmount_filesystem(void) {
    f_mount(NULL, mount_point, 0); // 卸载
    fatfs_device_unlock();         // 解锁块设备 (见下文关键点)
}

// CherryUSB事件回调
void usbd_event_handler(uint8_t event, void *arg) {
    switch (event) {
    case USBD_EVENT_CONNECTED:   // USB插入
        unmount_filesystem();    // 卸载文件系统
        usbd_msc_init();         // 初始化MSC设备 (需关联blk_dev)
        break;
    case USBD_EVENT_DISCONNECTED: // USB拔出
        usbd_msc_deinit();        // 反初始化MSC
        mount_filesystem();       // 重新挂载文件系统
        break;
    }
}



关键实现细节




  1. 块设备互斥访问



    • 问题:文件系统(Elm-FatFS)和CherryUSB MSC会同时访问同一块设备。

    • 解决:在卸载文件系统后解锁设备,挂载前加锁
      // 在FAL驱动中实现设备锁
      void fal_blk_device_lock(struct fal_blk_device *dev) {
      rt_mutex_take(&dev->lock, RT_WAITING_FOREVER);
      }
      void fal_blk_device_unlock(struct fal_blk_device *dev) {
      rt_mutex_release(&dev->lock);
      }




  2. CherryUSB事件触发时机



    • 确保USB事件能正确触发:

      • 检查USB初始化代码中已注册事件回调:
        usbd_desc_register(usbd_descriptor); // 描述符
        usbd_msc_class_register();           // MSC类
        usbd_event_handler_register(usbd_event_handler); // !!! 关键注册 !!!
        usbd_initialize();                   // 初始化USB





  3. FatFS与MSC共享块设备



    • 在MSC初始化时传入FAL设备:
      void usbd_msc_init(void) {
      // 将FAL块设备绑定到MSC
      usbd_msc_storage_set(0, (struct msc_block_dev *)blk_dev);
      }

    • 实现CherryUSB所需的msc_block_ops接口(对接FAL):
      const struct msc_block_ops fal_block_ops = {
      .read_block  = fal_blk_read,  // 从FAL读取扇区
      .write_block = fal_blk_write, // 写入FAL扇区
      };






操作流程




  1. 初始状态(无USB连接)  



    • 系统启动 → 挂载文件系统 → 应用访问文件。




  2. USB插入事件  



    • 触发USBD_EVENT_CONNECTED

      卸载文件系统 → 初始化MSC设备 →

      PC识别U盘并操作文件。




  3. USB拔出事件  



    • 触发USBD_EVENT_DISCONNECTED

      反初始化MSC → 重新挂载文件系统 →

      应用恢复文件访问。






调试技巧




  1. 确认事件触发

    usbd_event_handler中加入调试输出:


    printf("[USB] Event: %dn", event);

    观察插入/拔出时是否打印事件。




  2. 检查卸载/挂载返回值  


    FRESULT res = f_mount(NULL, mount_point, 0);
    if (res != FR_OK) {
       printf("Unmount failed: %dn", res);
    }



  3. 确保USB中断使能

    board_init()中确认USB时钟和中断已配置:


    at32_msp_init(AT32_USB_DEVICE); // AT32库的USB初始化
    nvic_irq_enable(USB_LP_CAN1_RX0_IRQn, 0, 0); // 使能中断






为何之前回调不执行?

最常见原因:未正确注册事件回调(usbd_event_handler_register),或USB配置/中断问题。参考CherryUSB设备模式示例检查初始化流程。



通过动态卸载/挂载文件系统和USB事件驱动,即可实现无缝切换。最终效果:

无USB → 文件系统工作 | 插入USB → U盘工作 | 拔出USB → 文件系统恢复

举报

更多回帖

发帖
×
20
完善资料,
赚取积分