This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] TM4C1294NCPDT:如何正确处理 USB 中的阻塞 R/W?

Guru**** 2473270 points
Other Parts Discussed in Thread: EK-TM4C1294XL

请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/676949/tm4c1294ncpdt-how-to-deal-with-blocking-r-w-in-usb-properly

器件型号:TM4C1294NCPDT
Thread 中讨论的其他器件:EK-TM4C1294XL

我正在使用 TM4C1242的 USB 主机模式来访问拇指驱动器、除了在 USB R/W 期间移除驱动器时、它将挂起到 USBHCPpeWrite 等阻塞 API 中、因为 R/W 将由于移除拇指驱动器而无法完成。

是否有人已经解决了此问题? 我希望 USB 堆栈会使用错误代码停止 R/W、并等待 USB 拇指驱动器重新连接

谢谢!

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    尊敬的 David:

    这是我第一次听说在移除器件时 USBHCCDPipeWrite 挂起、但过去有报告称、由于 USBHCCDPIPeWrite 发送的字节超过64字节、它挂起。 悬挂是否可能与悬挂相关?

    您采取了哪些步骤来确保仅在 R/W 期间移除是根本原因、您是否可以在 TI EVM 硬件上复制它们、以便我可以尝试采取相同的步骤来观察问题并在我的末尾调试堆栈? 如果我不能在最后的调试中看到这个问题、我就很难提供很多建设性的建议

    最后、您运行的是哪个版本的 TivaWare?
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    从 USB_host_msc 开始
    添加代码以在 cmd_cat 中复制文件(请参阅下面的代码)
    如果我不使用 f_sync、它将正常工作、当我拔下拇指时、它将显示错误并等待下一个拇指。 如果我在 f_write 后使用 f_sync、并在写入时移除拇指、很可能会锁定

    _________________________________ 已修改 USB_host_msc.c _________________________

    //
    //
    // USB_host_msc.c - USB 海量存储主机应用程序。
    //
    //版权所有(c) 2013-2016 Texas Instruments Incorporated。 保留所有权利。
    //软件许可协议
    //
    //德州仪器(TI)仅提供和使用此软件
    //专门用于 TI 的微控制器产品。 该软件归其所有
    // TI 和/或其供应商、受适用版权保护
    //法律。 您不能将此软件与"病毒"开源软件结合使用
    //软件,以便形成一个更大的程序。
    //
    //此软件按“原样”提供,且存在所有故障。
    //不作任何明示、暗示或法定的保证,包括但
    //不限于对适销性和适用性的暗示保证
    //此软件的特定用途。 TI 不得以任何方式进行
    //情况,对特殊、偶然或从属事件负责
    //任何原因造成的损害。
    //
    //这是 EK-TM4C1294XL 固件包版本2.1.3.156的一部分。
    //
    //

    #include
    #include
    #include
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "driverlib/gpio.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "driverlib/udma.h"
    #include "usblib/usblib.h"
    #include "usblib/usbmsc.h"
    #include "usblib/host/usbhost.h"
    #include "usblib/host/usbhmsc.h"
    #include "utils/cmdline.h"
    #include "utils/uartstdio.h"
    #include "fatfs/src/ff.h"
    #include "drivers/pinout.h"

    //
    //
    //! \addtogroup example_list
    //!

    USB 海量存储类主机(USB_host_msc)


    //!
    //! 此示例应用演示了如何从 USB 大容量读取文件系统
    //! 存储类设备。 它使用 FatFs,一种 FAT 文件系统驱动程序。 它
    //! 通过 UART 提供一个简单的命令控制台、用于发出要查看的命令
    //! 并在大容量存储设备上导航文件系统。
    //!
    //! 第一个 UART、连接到 Tiva C 系列虚拟串行端口
    //! 在评估板上、配置为每秒115、200位、和
    //! 8-N-1模式。 程序启动后、将向打印一条消息
    //! 终端。 键入``help ''可获得命令帮助。
    //!
    //! 有关 FatFs 的更多详细信息,请参见以下站点:
    //! elm-chan.org/.../00index_e.html
    //
    //

    //
    //
    //每秒的 SysTick 节拍数。
    //
    //
    #define tICKS_PER_second 100
    #define MS_PER_SysTick (1000 / ticks_per_second)

    //
    //
    //我们运行的系统节拍计数器和用于确定时间的全局变量
    //自上次调用 GetTickms()以来已过。
    //
    //
    uint32_t g_ui32SysTickCount;
    uint32_t g_ui32LastTick;

    //
    //
    //定义保存路径的缓冲区的大小,或来自的临时数据
    //存储卡。 分配了两个具有此大小的缓冲区。 缓冲器
    //大小必须足够大,以容纳预期的最长完整路径名称,
    //包括文件名和后缀空字符。
    //
    //
    #define PATH_BUF_SIZE 80

    //
    //
    //定义保存命令行的缓冲区的大小。
    //
    //
    #define CMD_BUF_SIZE 64

    //
    //
    //定义呼叫以检查连接的设备是否为的次数
    //准备就绪。
    //
    //
    #define USBMSC_DRIVE_RETRY 4.

    //
    //
    //此缓冲区保存到当前工作目录的完整路径。 进行了比较
    //它是根("/")。
    //
    //
    静态 char g_cCwdBuf[path_BUF_size]="/";

    //
    //
    //操作文件路径或读取数据时使用的临时数据缓冲区
    //从存储卡。
    //
    //
    静态 char g_cTmpBuf[path_BUF_size];
    静态 char g_cTmpBuf2[path_BUF_size];
    静态 char g_cTmpBuf3[2048];

    //
    //
    //保存命令行的缓冲区。
    //
    //
    静态 char g_cmdBuf[CMD_BUF_SIZE];

    //
    //
    //当前 FAT FS 状态。
    //
    //
    静态 FATFS g_sFatFs;
    静态 DIR g_sDirObject;
    静态 FILINFO g_sFileInfo;
    静态 FIL g_sFileObject;
    静态 FIL g_sFileObject2;

    //
    //
    //保存 FRESULT 数字代码之间映射的结构,
    //和字符串表示。 从 FatFs 返回 FRESULT 代码
    // FAT 文件系统驱动程序。
    //
    //
    typedef 结构

    FRESULT 结果;
    char *pcResultStr;

    tFresultString;

    //
    //
    //一个宏,用于轻松地将结果代码添加到表中。
    //
    //
    #define FRESULT_Entry (f) {(f)、(#f)}

    //
    //
    //保存数字 FRESULT 代码与之间映射的表
    //它的名称是字符串。 这用于查找的错误代码
    //打印到控制台。
    //
    //
    tFresultString g_sFresultStrings[]=

    FRESULT_Entry (FR_OK)、
    FRESULT_Entry (FR_DISK_ERR)、
    FRESULT_Entry (FR_INT_ERR)、
    FRESULT_Entry (FR_NOT _READY)、
    FRESULT_Entry (FR_NO_FILE)、
    FRESULT_Entry (FR_NO_PATH)、
    FRESULT_Entry (FR_INVALID_NAME)、
    FRESULT_Entry (FR_Denied)、
    FRESULT_Entry (FR_Exists)、
    FRESULT_Entry (FR_INVALID_OBJECT)、
    FRESULT_Entry (FR_WRITE_PROTECTED)、
    FRESULT_Entry (FR_INVALID_DRIVE)、
    FRESULT_Entry (FR_NOT _ENABLED)、
    FRESULT_Entry (FR_NO_filesystem)、
    FRESULT_Entry (FR_mkfs_aborted)、
    FRESULT_Entry (FR_TIMEOUT)、
    FRESULT_Entry (FR_LOCKED)、
    FRESULT_Entry (FR_NOT 足够的内核)、
    FRESULT_Entry (FR_TO_MOUSE_OPEN_FILES)、
    FRESULT_Entry (FR_INVALID_PARAMETER)
    };

    //
    //
    //保存结果代码数的宏。
    //
    //
    #define NUM_FRESULT_CODES (sizeof (g_sFresultStrings)/ sizeof (tFresultString))

    //
    //
    //主机控制器内存池的大小(以字节为单位)。
    //
    //
    #define HCD_MEMORY_SIZE 128

    //
    //
    //提供给主机控制器驱动程序的内存池。
    //
    //
    uint8_t g_pHCDPool[hcd_memory_size];

    //
    //
    // MSC 驱动程序的实例数据。
    //
    //
    tUSBHMSCInstance * g_psMSCInstance = 0;

    //
    //
    //声明 USB 事件驱动程序接口。
    //
    //
    Declate_event_driver (g_sUSBEventDriver、0、0、USBHCEEvents);

    //
    //
    //保存应用程序中使用的所有主机驱动程序的全局变量。
    //在本例中,仅加载 MSC 类。
    //
    //
    静态 tUSBHostClassDriver const * const g_ppHostClassDrivers []=

    &g_sUSBHostMSCClassDriver、
    &g_sUSBEventDriver
    };

    //
    //
    //保持应用程序的当前状态。
    //
    //
    typedef 枚举

    //
    //不存在设备。
    //
    State_no_device、

    //
    //正在枚举大容量存储设备。
    //
    State_device_enum、

    //
    //大容量存储设备就绪。
    //
    State_DEVICE_READY、

    //
    //连接了不受支持的设备。
    //
    State_unknown_device、

    //
    //已连接大容量存储设备,但未能随时报告。
    //
    State_Timeout_device、

    //
    //发生电源故障。
    //
    State_Power_FAULT

    tState;

    易失性 tState g_Estate;
    volatile tState g_eUIState;

    //
    //
    //此全局变量保存 g_pHostClassDrivers 中的类驱动程序数
    //列表。
    //
    //
    静态常量 uint32_t g_ui32NumHostClassDrivers =
    sizeof (g_ppHostClassDrivers)/ sizeof (tUSBHostClassDriver *);

    //
    //
    // UDMA 控制器使用的控制表。 此表必须对齐
    //到1024字节边界。 在此应用中、UDMA 仅用于 USB、
    //因此只需要前6个通道。
    //
    //
    #IF 定义(ewarm)
    #pragma DATA_alignment=1024
    tDMAControlTable g_sDMAControlTable[6];
    #Elif Defined (CCS)
    #pragma DATA_ALIGN (g_sDMAControlTable、1024)
    tDMAControlTable g_sDMAControlTable[6];
    其他
    tDMAControlTable g_sDMAControlTable[6]__attribute__(aligned (1024)));
    #endif

    //
    //
    //当前 USB 操作模式-主机、设备或未知。
    //
    //
    tUSBMode g_eCurrentUSBMode;

    //
    //
    //未知设备的类别。
    //
    //
    uint32_t g_ui32UnknownClass;

    //
    //
    //驱动程序库遇到错误时调用的错误例程。
    //
    //
    #ifdef 调试
    无效
    _error__(char * pcFilename、uint32_t ui32Line)


    #endif

    //
    //
    //这是该 SysTick 中断的处理程序。
    //
    //
    无效
    SysTickIntHandler (空)

    //
    //更新我们的 tick 计数器。
    //
    G_ui32SysTickCount++;


    //
    //
    //此函数返回自上次此函数以来的节拍数
    //被调用。
    //
    //
    uint32_t
    GetTickms (空)

    uint32_t ui32RetVal;
    uint32_t ui32Saved;

    ui32RetVal = g_ui32SysTickCount;
    ui32Saved = ui32RetVal;

    if (ui32Saved > g_ui32LastTick)

    ui32RetVal = ui32Saved - g_ui32LastTick;

    其他

    ui32RetVal = g_ui32LastTick - ui32Saved;


    //
    //这可能会错过几毫秒,但这里的计时是在 A 上
    //比例大得多。
    //
    G_ui32LastTick = ui32Saved;

    //
    //返回自上次调用此函数以来的毫秒数。
    //
    return (ui32RetVal * MS_PER_SysTick);


    //
    //
    //此函数返回错误代码的字符串表示
    //从函数调用返回到 FatFs。 它可用于打印人机
    //可读错误消息。
    //
    //
    const char *
    StringFromFresult (FRESULT fresult)

    uint32_t ui32Idx;

    //
    //输入循环,在错误代码表中搜索匹配的错误代码。
    //
    对于(ui32Idx = 0;ui32Idx < NUM_FRESULT_CODES;ui32Idx++)

    //
    //如果找到匹配项,则返回错误代码的字符串名称。
    //
    if (g_sFresultStrings[ui32Idx].fresult=fresults)

    return (g_sFresultStrings[ui32Idx].pcResultStr);



    //
    //此时未找到匹配的代码,因此返回一个字符串,指示
    //未知错误。
    //
    return ("未知错误代码");


    //
    //
    //此函数实现"ls"命令。 它将打开当前目录
    //并枚举内容,并为其每个项目打印一行
    //找到。 它显示了文件属性、时间和日期以及等详细信息
    //文件大小以及名称。 它在末尾显示了文件大小的摘要
    //以及可用空间。
    //
    //
    内部
    cmd_ls (int argc、char * argv[])

    uint32_t ui32 TotalSize;
    uint32_t ui32FileCount;
    uint32_t ui32DirCount;
    FRESULT 结果;
    FATFS * pFatFs;

    //
    //如果未连接驱动器,请勿尝试执行任何操作。
    //
    if (g_Estate!= State_device_ready)

    返回(FR_NOT _READY);


    //
    //打开当前目录进行访问。
    //
    fresult = f_opendir (&g_sDirObject、g_cCwdBuf);

    //
    //检查错误,如果有问题,则返回。
    //
    if (fresult!= FR_OK)

    返回(fresult);


    ui32 TotalSize = 0;
    ui32FileCount = 0;
    ui32DirCount = 0;

    //
    //输入循环以枚举所有目录条目。
    //
    while (1)

    //
    //从目录中读取条目。
    //
    fresult = f_readdir (&g_sDirObject、&g_sFileInfo);

    //
    //检查错误,如果有问题,则返回。
    //
    if (fresult!= FR_OK)

    返回(fresult);


    //
    //如果文件名为空,则这是列表的末尾。
    //
    if (!g_sFileInfo.fname[0])

    中断;


    //
    //如果属性是目录,则增加目录计数。
    //
    if (g_sFileInfo.fattrib 和 am_DIR)

    ui32DirCount++;


    //
    //否则,它是一个文件。 递增文件计数、并添加
    //将文件大小设置为总计。
    //
    其他

    ui32FileCount++;
    ui32TotalSize += g_sFileInfo.fsize;


    //
    //将条目信息打印在一行上,并显示格式
    //属性、日期、时间、大小和名称。
    //
    UARTprintf ("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9U %s\n"、
    (G_sFileInfo.fattrib & AM_DIR)? 'D':'-'、
    (G_sFileInfo.fattrib 和 AM_RDO)? 'R':'-'、
    (G_sFileInfo.fattrib 和 AM_HID)? 'h':'-'、
    (G_sFileInfo.fattrib 和 AM_SYS)? S':'-'、
    (G_sFileInfo.fattrib & AM_ARC)? 'A':'-'、
    (G_sFileInfo.fdate >> 9)+ 1980、
    (g_sFileInfo.fdate >> 5)和15、
    G_sFileInfo.fdate 和31、
    (g_sFileInfo.ftime >> 11)、
    (G_sFileInfo.ftime >> 5)和63、
    G_sFileInfo.fsize、
    g_sFileInfo.fname);


    //
    //打印显示文件、目录和大小总计的摘要行。
    //
    UARTprintf ("\n%4U 文件、总共%10U 字节\n%4U 目录"、
    ui32FileCount、ui32TotalSize、ui32DirCount);

    //
    //获得可用空间。
    //
    fresult = f_getfree ("/"、&ui32TotalSize、&pFatFs);

    //
    //检查错误,如果有问题,则返回。
    //
    if (fresult!= FR_OK)

    返回(fresult);


    //
    //显示计算出的可用空间量。
    //
    UARTprintf ("、%10uK 字节免费\n"、ui32TotalSize * pFatFs->csize / 2);

    //
    //将其添加到这里,返回时没有错误。
    //
    返回(0);


    //
    //
    //此函数实现“cd”命令。 它采用这样的参数
    //指定使当前工作目录的目录。 路径
    //分隔符必须使用正斜杠"/"。 CD 的参数可以是其中之一
    //以下内容:
    //
    //* root ("/")
    //*完整指定的路径(“/my/path/to/mydir”)
    //*当前目录中的单个目录名("mydir")
    //*父目录("..")
    //
    //它不理解相对路径,所以不要尝试这样的操作:
    //("../my/new/path)
    //
    //指定新目录后,将尝试打开该目录
    //确保它存在。 如果新路径已成功打开、则为
    //当前工作目录(cwd)更改为新路径。
    //
    //
    内部
    CMD_CD (int argc、char * argv[])

    uint32_t ui32Idx;
    FRESULT 结果;

    //
    //如果未连接驱动器,请勿尝试执行任何操作。
    //
    if (g_Estate!= State_device_ready)

    返回(FR_NOT _READY);


    //
    //将当前工作路径复制到临时缓冲区中,这样就可以了
    //操作。
    //
    strcpy (g_cTmpBuf、g_cCwdBuf);

    //
    //如果第一个字符是/,则这是完全指定的路径,然后是
    //应按原样使用。
    //
    if (argv[1][0]='/')

    //
    //确保新路径不大于 cwd 缓冲区。
    //
    if (strlen (argv[1])+ 1 > sizeof (g_cCwdBuf))

    UARTprintf ("生成的路径名太长\n"\n);
    返回(0);


    //
    //如果新路径名(在 argv[1]中)不是太长,则复制该路径名
    //进入临时缓冲区,以便可以对其进行检查。
    //
    其他

    strncpy (g_cTmpBuf、argv[1]、sizeof (g_cTmpBuf));



    //
    //如果参数是. 然后尝试删除上的最低级别
    // CWD。
    //
    否则、如果(!strcmp (argv[1]、".."))

    //
    //获取当前路径中最后一个字符的索引。
    //
    ui32Idx = strlen (g_cTmpBuf)- 1;

    //
    //从路径名末尾备份,直到分隔符(/)
    //找到,或者直到我们到达路径的开头。
    //
    while ((g_cTmpBuf[ui32Idx]!='/')&&(ui32Idx > 1))

    //
    //备份一个字符。
    //
    ui32Idx--;


    //
    //现在我们要么处于当前路径中的最低级别分隔符,
    //或字符串开头(根)。 因此、请设置新的结束
    //字符串,有效地删除路径的最后一部分。
    //
    G_cTmpBuf[ui32Idx]= 0;


    //
    //否则,这只是当前目录中的常规路径名,
    //并且需要将其附加到当前路径。
    //
    其他

    //
    //测试以确保在添加新的附加路径时
    //当前路径,缓冲区中有空间用于完整的新路径。
    //它需要包含一个新的分隔符和一个尾随的空字符。
    //
    if (strlen (g_cTmpBuf)+ strlen (argv[1])+ 1 + 1 > sizeof (g_cCwdBuf))

    UARTprintf ("生成的路径名太长\n"\n);
    返回(0);


    //
    //新路径是可以的,所以添加分隔符,然后附加新路径
    //目录到路径。
    //
    其他

    //
    //如果尚未位于根级别,则追加/
    //
    if (strcmp (g_cTmpBuf、"/"))

    strcat (g_cTmpBuf、"/");


    //
    //将新目录附加到路径。
    //
    strcat (g_cTmpBuf、argv[1]);



    //
    //此时,候选新目录路径位于 chTmpBuf 中。 尝试
    //打开它以确保它有效。
    //
    fresult = f_opendir (&g_sDirObject、g_cTmpBuf);

    //
    //如果无法打开,则是一条错误的路径。 通知用户并返回。
    //
    if (fresult!= FR_OK)

    UARTprintf ("CD:%s\n"、g_cTmpBuf);
    返回(fresult);


    //
    //否则,它是一个有效的新路径,所以将其复制到 CWD 中。
    //
    其他

    strncpy (g_cCwdBuf、g_cTmpBuf、sizeof (g_cCwdBuf));


    //
    //返回成功。
    //
    返回(0);


    //
    //
    //此函数实现"pwd"命令。 它只是打印电流
    //工作目录。
    //
    //
    内部
    cmd_pwd (int argc、char * argv[])

    //
    //如果未连接驱动器,请勿尝试执行任何操作。
    //
    if (g_Estate!= State_device_ready)

    返回(FR_NOT _READY);


    //
    //将 CWD 打印到控制台。
    //
    UARTprintf ("%s\n"、g_cCwdBuf);

    //
    //返回成功。
    //
    返回(0);


    //
    //
    //此函数实现"cat"命令。 它读取文件的内容
    //并将其打印到控制台。 这只能用于文本文件。 如果
    //它被用在二进制文件上,那么可能会打印一堆垃圾
    //控制台。
    //
    //
    内部
    cmd_cat (int argc、char * argv[])

    FRESULT 结果;
    uint32_t ui32字节 Read;

    //
    //如果未连接驱动器,请勿尝试执行任何操作。
    //
    if (g_Estate!= State_device_ready)

    返回(FR_NOT _READY);


    //
    //首先,检查以确保当前路径(CWD)加上文件
    //名称加上分隔符和尾随空,都适合临时
    //将用于保存文件名的缓冲区。 文件名必须为
    //完整指定,带路径,到 FatFs。
    //
    if (strlen (g_cCwdBuf)+ strlen (argv[1])+ 1 + 1 > sizeof (g_cTmpBuf))

    UARTprintf ("生成的路径名太长\n"\n);
    返回(0);


    //
    //将当前路径复制到临时缓冲区,以便对其进行操作。
    //
    strcpy (g_cTmpBuf、g_cCwdBuf);

    //
    //如果尚未位于根级别,则追加分隔符。
    //
    if (strcmp ("/"、g_cCwdBuf))

    strcat (g_cTmpBuf、"/");


    //
    //最后,附加文件名以生成完全指定的文件。
    //
    strcat (g_cTmpBuf、argv[1]);
    strcpy (g_cTmpBuf2、g_cTmpBuf);
    g_cTmpBuf2[strlen (g_cTmpBuf)-2]='y';

    //
    //打开文件进行读取。
    //
    fresult = f_open (&g_sFileObject、g_ctmpBuf、fa_read);

    fresult = f_open (&g_sFileObject2、g_cTmpBuf2、fa_write|fa_create_always);

    //
    //如果打开文件时出现问题,则返回错误。
    //
    if (fresult!= FR_OK)

    返回(fresult);


    //
    //输入循环从文件中重复读取数据并显示数据,直到
    //文件的末尾已到达。
    //
    操作

    //
    //从文件中读取数据块。 读取尽可能多的数据
    //临时缓冲区,包括尾部空的空间。
    //
    fresult = f_read (&g_sFileObject、g_cTmpBuf3、sizeof (g_cTmpBuf3)、
    (UINT *)\ui32BytesRead);
    fresult = f_write (&g_sFileObject2、g_cTmpBuf3、sizeof (g_cTmpBuf3)、
    (UINT *)\ui32BytesRead);
    f_sync (&g_sFileObject2);

    //
    //如果读取错误,则打印换行符并返回
    //用户错误。
    //
    if (fresult!= FR_OK)

    UARTprintf ("Error\n");
    返回(fresult);


    //
    // Null 终止最后一个被读取的块,使其成为空
    //可与 printf 一起使用的终止字符串。
    //
    //g_cTmpBuf[ui32BytesRad]= 0;

    //
    //打印接收到的文件的最后一个块。
    //
    //UARTprintf ("%s"、g_cTmpBuf);
    UARTprintf (".");

    //
    //继续读取,直到读取的字节数小于完整的字节数。
    //这意味着已到达缓冲区的末尾。
    //

    while (ui32BytesRead = sizeof (g_cTmpBuf3));

    //
    //返回成功。
    //
    返回(0);


    //
    //
    //此函数实现“帮助”命令。 它打印的简单列表
    //可用命令,带有简要说明。
    //
    //
    内部
    CMD_help (int argc、char * argv[])

    tCmdLineEntry *pentrry;

    //
    //打印一些标题文本。
    //
    UARTprintf ("\n 可用命令\n");
    UARTprintf ("------------------ \n");

    //
    //指向命令表的开头。
    //
    pentrry =&g_psCmdTable[0];

    //
    //输入循环以从命令表中读取每个条目。 结束时
    //命令名称为 NULL 时已到达表。
    //
    while (preme->pcCmd)

    //
    //打印命令名称和简短说明。
    //
    UARTprintf ("%s%s%s\n"、pentry -> pcCmd、pentry -> pcHelp);

    //
    //前进到表中的下一个条目。
    //
    pentre++;


    //
    //返回成功。
    //
    返回(0);


    //
    //
    //这是保存命令名称、实现函数和的表
    //简要说明。
    //
    //
    tCmdLineEntry g_psCmdTable[]=

    {"help"、Cmd_help、 ":显示命令列表"},
    {"h"、 CMD_help、":帮助别名"}、
    {"?"、 CMD_help、":帮助别名"}、
    {"ls"、 CMD_ls、 ":显示文件列表"},
    {"chdir"、cmd_cd、 ":更改目录"}、
    {"CD"、 CMD_CD、 “: chdir 的别名”},
    {"pwd"、Cmd_pwd、 ":显示当前工作目录"},
    {"cat"、cmd_cat、 ":显示文本文件的内容"},
    { 0、0、0}
    };

    //
    //
    //这是 MSC 驱动程序的回调。
    //
    //\param ui32Instance 是需要在何时使用的驱动程序实例
    //与驱动程序通信。
    //\param ui32event 是驱动程序定义的事件之一。
    //\param pvData 是传递到对寄存器的初始调用中的数据的指针
    //回调。
    //
    //此函数处理 MSC 驱动程序的回调事件。 唯一的事件
    //当前处理的是 MSC_EVENT_OPEN 和 MSC_EVENT_CLOSE。 这是允许的
    //知道何时检测到 MSC 设备的主例程和
    //枚举,以及从系统中删除 MSC 设备的时间。
    //
    //返回成功时返回\n true,失败时返回\n false。
    //
    //
    无效
    MSCCallback (tUSBHMSCInstance * ps32Instance、uint32_t ui32Event、void * pvData)

    //
    //确定事件。
    //
    switch (ui32event)

    //
    //设备驱动程序已成功枚举 MSC 时调用
    //器件。
    //
    案例 MSC_EVENT_OPEN:

    //
    //继续到枚举状态。
    //
    g_Estate = State_device_enum;
    中断;


    //
    //由于错误或而卸载设备驱动程序时调用
    //设备不再存在。
    //
    案例 MSC_EVENT_CLOSE:

    //
    //返回到“无设备”状态并等待新连接。
    //
    g_Estate = State_no_device;

    中断;


    默认值:

    中断;




    //
    //
    //这是来自主机栈的通用回调。
    //
    //\param pvData 实际上是 tEventInfo 结构的指针。
    //
    //将调用此函数以在 USB 事件发生时通知应用程序
    //发生在与大容量存储设备相关的范围之外。 进行了比较
    //点此选项用于检测插入和删除的不受支持的设备。
    //它还用于在发生电源故障时通知应用程序。
    //在中包含 g_USBGenericEventDriver 时需要此函数
    //传入的主机控制器驱动程序数组
    // USBHCDRegisterDrivers()函数。
    //
    //\返回无。
    //
    //
    无效
    USBHCEDents (void *pvData)

    tEventInfo *pEventInfo;

    //
    //将此指针投射到其实际类型。
    //
    pEventInfo =(tEventInfo *) pvData;

    switch (pEventInfo->ui32Event)

    //
    //检测到未知设备。
    //
    案例 USB_EVENT_UNKNOWN_Connected:

    //
    //保存未知类。
    //
    G_ui32UnknownClass = pEventInfo->ui32Instance;

    //
    //检测到未知设备。
    //
    g_Estate = State_unknown_device;

    中断;


    //
    //键盘已拔下。
    //
    案例 USB_EVENT_DISCONNECTED:

    //
    //已删除未知设备。
    //
    g_Estate = State_no_device;

    中断;


    案例 USB_EVENT_POWER_FAULT:

    //
    //不能通电表示没有设备。
    //
    G_ARRAT = State_Power_FAULT;

    中断;


    默认值:

    中断;




    //
    //
    //此函数从 UART 控制台读取一行文本。 USB 主机主设备
    在整个过程中调用//函数以使 USB 保持活动状态并保持正常。
    //
    //
    无效
    readline (空)

    uint32_t ui32Idx、ui32Prompt;
    uint8_t ui8Char;
    tState eStateCopy;
    uint32_t ui32DriveTimeout;

    //
    //在命令缓冲区的开头开始读取并打印提示符。
    //
    G_cCmdBuf[0]='\0';
    ui32Idx = 0;
    ui32Prompt = 1;

    //
    //初始化驱动器超时。
    //
    ui32DriveTimeout = USBMSC_DRIVE_RETRY;

    //
    //永久循环。 该循环将在何时明确地断开
    //已完全读取。
    //
    while (1)

    //
    //查看是否已枚举大容量存储设备。
    //
    if (g_Estate = State_device_enum)

    //
    //如果大容量存储设备速度慢,请在该设备上轻松操作
    //连接后启动。
    //
    if (USBHMSCDriveReady (g_psMSCInstance)!= 0)

    //
    //在尝试检查是否为之前等待大约500ms
    //设备已就绪。
    //
    SysCtlDelay (SysCtlClockGet ()/(3*2));

    //
    //减少重试计数。
    //
    ui32DriveTimeout --;

    //
    //如果超时被按下,则转至
    // State_Timeout_device 状态。
    //
    if (ui32DriveTimeout = 0)

    g_Estate = State_Timeout_device;


    中断;


    //
    //将工作目录重置为 root。
    //
    G_cCwdBuf[0]='/';
    G_cCwdBuf[1]='\0';

    //
    //尝试打开目录。 某些驱动器需要更长的时间
    //比其他人启动,这可能会失败(即使是 USB
    //设备已枚举)。
    //
    f_mount (0、&g_sFatFs);
    if (f_opendir (&g_sDirObject、g_cCwdBuf)== FR_OK)

    //
    //驱动器已完全就绪,因此进入该状态。
    //
    G_ARRAT = STATE_DEVICE_READY;



    //
    //查看状态是否已更改。 我们将 g_eUIState 的副本复制到
    //防止编译器警告未定义的易失性顺序
    //访问。
    //
    eStateCopy = g_eUIState;
    if (g_Estate!= eStateCopy)

    //
    //确定新状态。
    //
    switch (g_Estate)

    //
    //先前连接的设备已断开连接。
    //
    案例 State_no_device:

    if (g_eUIState = State_unknown_device)

    UARTprintf ("\n 未知设备已断开连接。\n");

    其他

    UARTprintf ("\n 大容量存储设备已断开连接。\n");

    ui32Prompt = 1;
    中断;


    //
    //正在枚举大容量存储设备。
    //
    实例 State_device_enum:

    中断;


    //
    //已枚举并初始化大容量存储设备。
    //
    案例 State_device_ready:

    UARTprintf ("\n 已连接大容量存储设备。\n");
    ui32Prompt = 1;
    中断;


    //
    //已连接未知设备。
    //
    案例 State_UNKNOWN_DEVICE:

    UARTprintf ("未知设备类(0x%02x)已连接。\n"、
    G_ui32UnknownClass);
    ui32Prompt = 1;
    中断;


    //
    //连接的大容量存储设备未准备好报告。
    //
    案例 State_Timeout_device:

    //
    //如果这是第一次处于此状态,则打印 A
    //消息。
    //
    UARTprintf ("设备超时");
    ui32Prompt = 1;

    中断;


    //
    //发生电源故障。
    //
    案例 State_Power_FAULT:

    UARTprintf ("\n 电源故障。\n"\});
    ui32Prompt = 1;
    中断;



    //
    //保存当前状态。
    //
    G_eUIState = g_Estate;


    //
    //如有必要,打印提示。
    //
    if (ui32Prompt)

    //
    //根据当前状态打印提示。
    //
    if (g_Estate = State_device_ready)

    UARTprintf ("%s>%s"、g_cCwdBuf、g_cCmdBuf);

    否则、如果(g_Estate = State_unknown_device)

    UARTprintf ("未知>%s"、g_cCmdBuf);

    其他

    UARTprintf ("nodev>%s"、g_cCmdbf);


    //
    //不再需要打印提示。
    //
    ui32Prompt = 0;


    //
    //循环,此时已从接收到字符
    // UART。
    //
    while (ROM_UARTCharsAvail (UART0_BASE))

    //
    //从 UART 读取下一个字符。
    //
    ui8Char = UARTgetc();

    //
    //查看此字符是否为退格,并且至少有一个
    //输入行中的字符。
    //
    if ((ui8Char ='\b')&&(ui32Idx!= 0))

    //
    //从输入行擦除 lsat 字符。
    //
    UARTprintf ("\b\b\b");
    ui32Idx--;
    g_cCmdBuf[ui32Idx]='\0';


    //
    //查看此字符是否为换行符。
    //
    否则、如果((ui8Char ='\r\n')||(ui8Char ='\n'))

    //
    //返回给呼叫者。
    //
    UARTprintf ("\n");
    返回;


    //
    //查看此字符是转义还是 Ctrl-U
    //
    否则、如果((ui8Char = 0x1b)||(ui8Char = 0x15))

    //
    //擦除输入缓冲区中的所有字符。
    //
    while (ui32Idx)

    UARTprintf ("\b\b\b");
    ui32Idx--;

    G_cCmdBuf[0]='\0';


    //
    //查看这是否是可打印的 ASCII 字符。
    //
    否则、如果((ui8Char >=')&&(ui8Char <='~')&&
    (ui32Idx <(sizeof (g_cCmdBuf)- 1)))

    //
    //将此字符添加到输入缓冲区。
    //
    g_cCmdBuf[ui32Idx++]= ui8Char;
    g_cCmdBuf[ui32Idx]='\0';
    UARTprintf ("%c"、ui8Char);



    //
    //告诉 OTG 状态机经过了多少时间
    //自上次调用以来的毫秒数。
    //
    USBOTGMain (GetTickms());



    //
    //
    //这是运行应用程序的主循环。
    //
    //
    内部
    main (空)

    Int iStatus;
    uint32_t ui32SysClock、ui32PLLRate;

    //
    //最初等待设备连接。
    //
    g_Estate = State_no_device;
    G_eUIState = State_no_device;

    //
    //从 PLL 以120MHz 运行。
    //
    ui32SysClock = MAP_SysCtlClockFreqSet ((SYSCTL_XTAL_25MHz |
    SYSCTL_OSC_MAIN |
    SYSCTL_USE_PLL |
    SYSCTL_CFG_VCO_480)、120000000);

    //
    //配置器件引脚。
    //
    PinoutSet (false、true);

    //
    //启用 UART 并打印简短消息。
    //
    UARTStdioConfig (0、115200、ui32SysClock);
    UARTprintf ("\033[2J\033[H");
    UARTprintf ("USB 大容量存储主机程序\n");
    UARTprintf ("键入\'help\'以获取帮助。\n\n");

    //
    //为100Hz 中断配置 SysTick。
    //
    ROM_SysTickPeriodSet (ui32SysClock/ticks_per_sond);
    ROM_SysTickEnable();
    ROM_SysTickIntEnable();

    //
    //启用 uDMA 控制器并设置控制表基址。
    //
    ROM_SysCtlPeripheralEnable (SYSCTL_Periph_UDMA);
    ROM_uDMAEnable();
    ROM_uDMAControlBaseSet (g_sDMAControlTable);

    //
    //初始化 USB 堆栈模式并传入模式回调。
    //
    USBStackModeSet (0、eUSBModeHost、0);

    //
    //注册主机类驱动程序。
    //
    USBHCDRegisterDrivers (0、g_ppHostClassDrivers、g_ui32NumHostClassDrivers);

    //
    //打开海量存储类驱动程序的实例。
    //
    G_psMSCInstance = USBHMSCDriveOpen (0、MSCCallback);

    //
    //初始化电源配置。 这将设置电源使能信号
    //为高电平有效且不使能电源故障。
    //
    USBHCDPowerConfigInit (0、USBHCD_VBUS_AUTO_HIGH | USBHCD_VBUS_FILTER);

    //
    //将 CPU 时钟和 PLL 频率告知 USB 库。 这是一个
    // TM4C129器件的新要求。
    //
    SysCtlVCOGet (SYSCTL_XTAL_25MHz、\ui32PLLRate);
    USBHCDFeatureSet (0、USBLIB_Feature_CPUCLK、&ui32SysClock);
    USBHCDFeatureSet (0、USBLIB_Feature_USBPLL、&ui32PLLRate);

    //
    //初始化 USB 控制器以进行主机操作。
    //
    USBHCDInit (0、g_pHCPool、HCD_MEMORY_SIZE);

    //
    //初始化文件系统。
    //
    f_mount (0、&g_sFatFs);

    //
    //输入一个无限循环来读取和处理来自的命令
    //用户。
    //
    while (1)

    //
    //从用户获取一行文本。
    //
    readline();
    if (g_cCmdBuf[0]='\0')

    继续;


    //
    //将线路从用户传递到命令处理器。
    //将对其进行解析并执行有效的命令。
    //
    iStatus = CmdLineProcess (g_cCmdBuf);

    //
    //处理命令错误的情况。
    //
    if (iStatus = CMDLINE_BAD_CMD)

    UARTprintf ("命令错误!\n");


    //
    //处理过多参数的情况。
    //
    否则、如果(iStatus = CMDLINE_TOW_LOAD_ARGs)

    UARTprintf ("命令处理器的参数太多!\n");


    //
    //否则命令被执行。 打印错误
    //如果返回一个,则编码。
    //
    否则(iStatus!= 0)

    UARTprintf ("命令返回错误代码%s\n"、
    StringFromResult(((FRESULT)iStatus));


  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 David:

    这看起来 仍然与 USBHCSDPipeWrite API 相关、但与我之前提到的问题无关。 我从 fatfs 第三方库跟踪了根本原因、其中 far_usbmsc.c 中的 disk_write API 使用对 USBHMSCBlockWrite 的调用。 通过 USB 堆栈进行的跟踪表明、它调用 USBHSCSIWrite10、然后调用 USBHSCSISendCommand、 最后调用 USBHCSDPIPEWrite。

     在 USBHCPDPIPEWrite 内部、对断开事件进行了检查、如下所示:

    if (g_sUSBHCD.ui32IntEvents &(INT_EVENT_DISCONNECT |)
    INT_EVENT_VBUS_ERR |
    INT_EVENT_POWER_FAULT))
    

    当接收到 INT_EVENT_DISCONNECT 和 INT_EVENT_SOF 的结果等于0x14时、此检查由于某种原因失败。

    它看起来可能与 将 g_sUSBHCD 结构声明为静态相关。 我发现的另一篇文章建议将 g_sUSBHCD 变为易失性文件、这样可以解决用户问题。 不幸的是、在我的结尾、似乎没有这样做、但我希望您尝试一下、如果您在这方面有任何改进、我会向您报告。

    我将在明天继续对此进行调查。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    不、它没有解决问题
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    尊敬的 David:

    这仍然是我的最后一个未决问题 我正在等待来自不同产品组的另一位工程师来帮助我调试和找到解决此问题的解决方案、因为我已经用尽了我的所有想法 但是、我将在下周的部分时间进行商务旅行、并且我将无法与他同步、直到星期四才能够尽早同步。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    非常感谢您的跟进!

    周末愉快!

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 David:

    很抱歉耽误你的时间。

    看起来问题实际上是由编译器而不是源代码引起的。 您使用的是哪个版本的 CCS + ARM 编译器?

    当我将示例和 usblib 移入 CCS V8并使用18.1.0.LTS 编译器在 V8内构建时(请注意、您必须为此重新链接库)、问题不再存在。

    我们不知道编译器导致此问题的原因、但随着最新的 CCS+ARM 编译器解决了此问题、我们目前不打算与 CCS 团队进一步探讨此问题。 更新最新的 CCS+ARM 编译器是否适合您的情况?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    我使用的是 CCS 6.2

    如果我下载并安装 CCS 6。 我无法只导入当前项目并继续吗?
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    尊敬的 David:

    我尚未在 CCS v6.2上尝试过此操作、如果您给我一天左右的时间、我可以尝试 V6.2并查看该版本的行为是什么?

    关于您的问题、我不确定我是否完全理解? 您是指 CCS 8吗? 如果是、只要您在安装 CCS V8的情况下对仿真器提供 TM4C 支持和支持、那么您应该能够顺利导入和运行项目。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    不要在 CCS v6.2上浪费时间、因为这是我首次发现问题的地方

    我的理解是、一旦我升级到 CCSv8、这个问题将不再存在(在我当前的项目中、没有进行任何修改)、对吧?
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    尊敬的 David:

    是的、CCSv8并确保您拥有18.1.0.LTS (或更高版本)编译器-使用该组合、我和另一位工程师所做的所有测试不再存在问题(因此、到目前为止、2台单独的 PC ... 希望您的将会是第三名!)
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    刚刚安装 了 CCSv8并尝试了我发送给您的同一程序、几次尝试后、它被锁定在同一个位置。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    我安装了 CCSv8并使用了我在这里发布的测试程序、以复制一个大文件、并在复制数据时拔下 USB 闪存盘

    几次尝试后、它锁定在同一个 USBHCPDPIPEWrite

    您必须再尝试几次测试、我敢打赌您会发现同样的问题
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    尊敬的 David:

    我在多个电路板上尝试了二十多次。 但是、我没有使用您在这里发布的项目、我使用了我们最新 TivaWare 中的 TivaWare USB_host_msc 示例。 我将尝试您在今天或明天晚些时候发布的项目、并从那里进行评估。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    尊敬的 David:
    还使用了哪个 ARM 编译器?
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    USB_host_msc 的问题是缺少写入和同步、因此您不会遇到问题。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    默认安装的任何内容
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    尊敬的 David:

    再次观察问题、仍在调查。 乍一看、我认为内部处理程序没有正确设置标志。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    尊敬的 David:

    我一直在进一步调查此问题、但在解决方案方面没有取得很大进展。 我已确认 USB 驱动程序正在正确检测中断处理程序中的断开事件(因此标志设置正确、与我最初的想法不同)、 但是、当管道写入函数必须处理标志中的更改时、它无法正确输入断开事件的条件。 此时、我认为这是 C 代码和/或编译器问题。 我将尝试与 TM4C 小组之外的一些人同步、了解有关如何解决此问题的选项。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    感谢您的更新!