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.

[参考译文] EK-TM4C123GXL:USB 主机-大容量存储示例:意外中断

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

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/598230/ek-tm4c123gxl-usb-host---mass-storage-example-unexpected-interrupt

器件型号:EK-TM4C123GXL

您好!  

我正在尝试使 https://github.com/krithik/tiva-usb-host-msc 中提供的代码 正常工作。  

我已经更改了硬件、代码构建时没有任何错误或警告。

当我调试代码时、它会进入  IntDefaultHandler  循环。 我怀疑它与  USB0HostIntHandler 有关(在 USB0下的中断矢量表中是比较的)。

无论是否连接了 USB-Stick、都会发生这种情况。 但是、当逐步运行代码时、它不会进入  IntDefaultHandler、但也不会识别 USB 器件。  

有什么想法吗?

亚得里亚诺

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

    我发现了其中一个问题的根源。 SysTickHandler 不在中断矢量表中。

    尽管如此、我仍然无法识别 USB 设备。 我使用的是 FAT32 2Gb USB 记忆棒、安装了 R25和 R29、并将 H18连接到 H19。  

    下面是我要使用的代码:  

    #include 
    #include 
    #include 
    #include 
    
    ///----------------------
    // TivaWare 头文件
    //---------------
    include "inc/hw_memblumap.h"
    #include "inc/hw_types.h"
    #include "driverlib/fpu.h"
    #include "driverlib/sib/interrupts.h"
    
    #include "driverlib/usblu.h"
    
    
    
    
    
    
    
    
    
    
    #include "driverlib/usb.utils/usb/include "driverlib.usb/usb.包含#drivers/usb.utils/#include "#drivers/usb/usb.usb.usb/usb.utils/us.usb/#include "#include "#include "drivers/usb.usb.usb.utils/usb.us.us.us.us.us.us.us.us.us.us.dlintrins"#include "#include "#include "#include "#include "#include "#include "#包含"#drivers/usb.utils/usb.utils/usb.utils/us.us.us.usb.usb.us.usb.us.us.us.us.us.us.us.us.us.us.us.us
    
    
    
    
    
    
    路径、或
    来自 USB 磁盘的临时//数据。 分配了两个具有此大小的缓冲区。
    //缓冲区大小必须足够大,以容纳预期的 int32_test
    //完整路径名,包括文件名和后缀空字符。
    ////
    *****************
    #define PATH_BUF_SIZE 80
    
    //*********
    //
    //定义呼叫以检查连接的设备是否
    就绪的次数//。
    ////
    *****************
    #define USBMSC_DRIVE_RETRY 4
    
    //*************
    //
    //此缓冲区保存到当前工作目录的完整路径。
    //最初它是根("/")。
    ////
    *****************
    静态 char g_cCwdBuf[path_BUF_size]="/";
    
    //*********
    //
    //下面是 FatFs 使用的数据结构。
    ////
    *****************
    静态 FATFS g_sFatFs;
    静态 DIR g_sDirObject;
    静态 FILINFO g_sFileInfo;
    
    //*********
    //
    //保持 FRESULT 数字代码、
    //和字符串表示之间映射的结构。 从 FatFs
    // FAT 文件系统驱动程序返回 FRESULT 代码。
    ////
    *****************
    
    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))
    
    //*********
    //
    // ChangeToDirectory()返回的错误原因。
    ////
    *****************
    #define NAME_TOW_LON_ERROR 1
    #define opendir_error 2
    
    //*********
    //
    //每秒的 SysTick 节拍数。
    ////
    *****************
    #define TICKS_PER_second 100
    #define MS_PER_SysTick (1000 / TICKS_PER_second)
    
    //*********
    //
    //系统时钟节拍计数器,用于简单定时。
    ////
    *****************
    静态 uint32_t g_ui32SysTickCount;
    
    //*************
    //
    //保持系统的全局标志。
    ////
    *****************
    静态 uint32_t g_ui32Flags = 0;
    
    
    //*********
    //
    //存储文件名。
    ////
    *****************
    #define NUM_LIST_STRINS 48
    const char * g_ppcDirListStrings[NUM_LIST_STRINTS];
    
    //*********
    //
    //存储当前目录中的文件名。 文件名
    //存储为目录的"(D) filename.ext"格式或
    文件的"(F) filename.ext"//格式。
    ////
    *****************
    #define MAX_FILENAME_STRING_LEN (4 + 8 + 1 + 3 + 1)
    char g_pcFilename[NUM_LIST_STRING][MAX_FILENAME_STRIN_LEN];
    
    //*********
    //
    //存储显示在
    //显示底部状态框中的字符串。
    ////
    *************
    #define NUM_STATUS_STRINS 6
    #define MAX_STATUS_STRIN_LEN (36 + 1)
    char g_pcStatus[NUM_STATUS_STRINS][MAX_STATUS_STRIN_LEN];
    
    
    //*********
    //
    //标志表示已连接某些 USB 设备。
    ////
    *****************
    #define flags_device_present 0x00000001
    
    //*********
    //
    //保持应用程序的当前状态。
    ////
    *****************
    volatile enum
    {
    //
    //不存在设备。
    //
    State_no_device、
    
    //
    //正在枚举大容量存储设备。
    //
    State_device_enum、
    
    //
    //大容量存储设备就绪。
    //
    State_DEVICE_READY、
    
    //
    //连接了不受支持的设备。
    //
    State_unknown_device、
    
    //
    //已连接大容量存储设备,但未能随时报告。
    //
    State_Timeout_device、
    
    //
    //发生电源故障。
    //
    State_Power_FAULT
    }
    g_Estate;
    
    //*********
    //
    //主机控制器内存池的大小(以字节为单位)。
    ////
    *****************
    #define HCD_MEMORY_SIZE 128
    
    //*************
    //
    //提供给主机控制器驱动程序的内存池。
    ////
    *****************
    uint8_t g_pHCPool [hcd_memory_size];
    
    
    //*********
    //
    // MSC 驱动程序的实例数据。
    ////
    *****************
    tUSBHMSCInstance * g_psMSCInstance = 0;
    
    //*********
    //
    //声明 USB 事件驱动程序接口。
    ////
    *****************
    Declate_event_driver (g_sUSBEventDriver、0、0、USBHCEDents);
    
    //***************
    //
    //包含应用程序中使用的所有主机驱动程序的全局变量。
    //在本例中,仅加载 MSC 类。
    ////
    *****************
    静态 tUSBHostClassDriver const * const g_ppHostClassDriver[]=
    {
    &g_sUSBHostMSCClassDriver、
    &g_sUSBEventDriver
    };
    
    //*********
    //
    //此全局变量保存 g_ppHostClassDrivers
    //列表中的类驱动程序数。
    ////
    *****************
    静态常量 uint32_t g_ui32NumHostClassDrivers =
    sizeof (g_ppHostClassDrivers)/ sizeof (tUSBHostClassDriver *);
    
    //*********
    //
    // uDMA 控制器使用的控制表。 此表必须与
    1024字节边界对齐//。 在此应用中、UDMA 仅用于 USB、
    //因此只需要前6个通道。
    ////
    *****************
    #if defined (ewarm)
    #pragma DATA_alignment=1024
    tDMAControlTable g_psDMAControlTable[6];
    #Elif Defined (CCS)
    #pragma DATA_align (g_psDMAControlTable、1024)
    tDMAControlTable g_psDMAControlTable[6];
    #else tDMAControlTable_aligned
    (g_pDMAControlTable、1024) tDMAControlTable_h (_dMAControlTable_h)
    
    
    (_dMA_h)(_dMA_dMAControlTable_h)(_h)(_h)(h)(h)(h)__g_g
    //
    //定义一对用于保存路径信息的缓冲区。
    //缓冲区大小必须足够大,以容纳预期的最长
    //完整路径名,包括文件名和尾随的空字符。
    //初始路径设置为根"/"。
    ////
    *****************
    #define PATH_BUF_SIZE 80
    
    //*********
    //
    //定义可在任何目录级别显示的最大文件数。
    //这用于为保存文件信息分配空间。
    //定义子目录的最大深度,也用于为
    目录结构分配空间//。
    //定义允许为文件存储的最大字符数
    //名称。
    ////
    *****************
    #define MAX_FILES _PER_MENU 64
    #define MAX_SUBDIR_DEPTH 32
    
    
    
    //---------------
    //原型
    ///----------------
    void hardware_init (void);
    static bool FileInit (void);
    void SysTickHandler (void);
    static const char * StringFromFresult(FRESULT fresult);
    static void MSCCallback (tUSBHMSCInstance * ps32Instance,Uprint32_t ui32Event,void * pvData);
    static---------(static/--/--/--/--/--/--)
    
    
    // main()//-----------------
    
    void main (void)
    {
    uint32_t ui32DriveTimeout、ui32SysClock;
    
    //
    //将主系统时钟设置为以50MHz 的频率从 PLL 运行
    //处理器时钟的计算方法为(PLL/2)/4 ->(400/2)/4 = 50
    //注:对于 USB 操作,它至少应为20MHz
    //
    SysCtlClockSet (SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHz);
    
    //
    //获取系统时钟速率
    // 50 MHz
    //
    ui32SysClock = SysCtlClockGet ();
    
    //启用 USB0
    //
    // PB0 --- USB ID --> GND (硬件)
    // PB1 --- USB VBUS
    // PD4 --- USB D+
    // PD5 --- USB D-
    //
    SysCtlPeripheralEnable (SYSCTL_Periph_USB0);
    SysCtlUSBPLLEnable();
    
    SysCtlPeripheralEnable (SYSCTL_Periph_GPIOB);
    GPIOPinTypeUSBAnalog (GPIO_PORTB_BASE、GPIO_PIN_0 | GPIO_PIN_1);
    
    SysCtlPeripheralEnable (SYSCTL_Periph_GPIOD);
    GPIOPinTypeUSBAnalog (GPIO_PORTD_BASE、GPIO_PIN_4 | GPIO_PIN_5);
    
    //
    //仅为主机模式初始化 USB 堆栈。
    //
    USBStackModeSet (0、eUSBModeForceHost、0);
    
    //
    //注册主机类驱动程序。
    //
    USBHCDRegisterDrivers (0、g_ppHostClassDrivers、g_ui32NumHostClassDrivers);
    
    //
    //为100Hz 中断配置 SysTick。
    // SysTick 周期= 5000000 / 100 -> 500000
    //
    SysTickPeriodSet (SysCtlClockGet ()/ ticks_per_second);
    SysTickEnable();
    SysTickIntEnable();
    
    //
    //启用 uDMA 控制器并设置控制表基址。
    // USB 库使用 uDMA 控制器。
    //
    SysCtlPeripheralEnable (SYSCTL_Periph_UDMA);
    uDMAEnable();
    uDMAControlBaseSet (g_psDMAControlTable);
    
    //初始化 UART0 (通过调试 USB 端口呈现到控制台)
    // RX -- PA0
    // TX -- PA1
    //注:使用 UARTstdio 实用程序
    //
    SysCtlPeripheralEnable (SYSCTL_Periph_UART0);
    SysCtlPeripheralEnable (SYSCTL_Periph_GPIOA);
    GPIOPinConfigure (GPIO_PA0_U0RX);
    GPIOPinConfigure (GPIO_PA1_U0TX);
    GPIOPinTypeUART (GPIO_Porta_base、GPIO_PIN_0 | GPIO_PIN_1);
    UARTClockSourceSet (UART0_BASE、(UART_CLOCK_SYSTEM));
    UARTStdioConfig (0、115200、SysCtlClockGet ());
    IntEnable (INT_UART0);
    UARTIntEnable (UART0_BASE、UART_INT_RX | UART_INT_RT);
    
    //启用所有中断。
    IntMasterEnable();
    
    UARTprintf ("硬件已初始化\r\n");
    
    //
    //最初等待设备连接。
    //
    g_Estate = State_no_device;
    
    //
    //打开海量存储类驱动程序的实例。
    //
    G_psMSCInstance = USBHMSCDriveOpen (0、MSCCallback);
    
    //
    //初始化驱动器超时。
    //
    ui32DriveTimeout = USBMSC_DRIVE_RETRY;
    
    //
    //初始化 USB 控制器以进行主机操作。
    //
    USBHCDInit (0、g_pHCPool、HCD_MEMORY_SIZE);
    
    //
    //初始化 FAT 文件系统。
    //
    FileInit();
    
    UARTprintf ("FAT 文件系统模块已初始化\r\n);
    
    //
    //输入一个(几乎)无限循环来读取和处理命令
    //用户。
    //
    while (1)
    {
    //
    //调用 USB 堆栈使其保持运行。
    //
    USBHCDMain();
    
    switch (g_Estate)
    {
    实例 State_device_enum:
    {
    //
    //如果大容量存储设备速度慢,请在该设备上轻松操作
    //连接后启动。
    //
    if (USBHMSCDriveReady (g_psMSCInstance)!= 0)
    {
    //
    //在尝试检查是否为之前等待大约500ms
    //设备已就绪。
    //
    // 1个机器循环需要(1/50*10^6)秒
    // SysCtlDelay 使用3个机器周期,因此它将是3*(1/50*10^6)秒
    //总延迟->(3个机器周期所需的时间)*计数值
    //
    //因此,[(3/50*10^6)*(50*10^6/(3*2)]=1/2秒
    //
    //
    SysCtlDelay (ui32SysClock /(3 * 2));
    
    //
    //减少重试计数。
    //
    ui32DriveTimeout --;
    
    //
    //如果超时被按下,则转至
    // State_Timeout_device 状态。
    //
    if (ui32DriveTimeout = 0)
    {
    g_Estate = State_Timeout_device;
    }
    中断;
    }
    
    UARTprintf ("USB 大容量存储设备就绪\r\n");
    
    //
    //到达此处意味着设备已准备就绪。
    //将 CWD 重置为根目录。
    //
    G_cCwdBuf[0]='/';
    G_cCwdBuf[1]= 0;
    
    //
    //使用找到的文件和目录填充列表框。
    //
    if (!printFileStructure())
    {
    //
    //如果没有报告错误,我们就可以了
    // MSC 操作。
    //
    g_Estate = State_device_ready;
    }
    
    //
    //设置设备存在标志。
    //
    G_ui32Flags = flags_device_present;
    中断;
    }
    
    //
    //如果没有设备,则只需等待一个。
    //
    案例 State_no_device:
    {
    if (g_ui32Flags = flags_device_present)
    {
    //
    //清除设备存在标志。
    //
    G_ui32Flags &&~flags_device_present;
    }
    中断;
    }
    
    //
    //连接了未知设备。
    //
    案例 State_UNKNOWN_DEVICE:
    {
    //
    //如果这是新设备,则更改状态。
    //
    if ((g_ui32Flags & flags_device_present)=0)
    {
    //指示存在未知设备。
    UARTprintf ("未知设备已连接\r\n);
    }
    
    //
    //设置设备存在标志。
    //
    G_ui32Flags = flags_device_present;
    中断;
    }
    
    //
    //连接的大容量存储设备未准备好报告。
    //
    案例 State_Timeout_device:
    {
    //
    //如果这是第一次处于此状态,则打印 A
    //消息。
    //
    if ((g_ui32Flags & flags_device_present)=0)
    {
    //指示尝试连接时的超时
    UARTprintf ("未知设备\r\n");
    
    }
    
    //
    //设置设备存在标志。
    //
    G_ui32Flags = flags_device_present;
    中断;
    }
    
    //
    //某些情况导致了电源故障。
    //
    案例 State_Power_FAULT:
    {
    中断;
    }
    默认值:
    {
    中断;
    }
    }
    
    }
    
    
    //*********
    //
    //初始化文件系统模块。
    //
    //\param 无。
    //
    //此函数初始化第三方 FAT 实现。
    //
    //返回成功时返回\e true,失败时返回\e false。
    ////
    *****************
    静态 bool 文件初始化(void){
    //
    //使用逻辑磁盘0装入文件系统。
    //
    if (f_mount (0、&g_sFatfs)!= FR_OK)
    {
    return (false);
    }
    return (true);
    }
    
    //*********
    //
    //这是该 SysTick 中断的处理程序。 它只是增加
    一个用于计时的//计数器。
    ////
    *****************
    void SysTickHandler (void){
    //
    //更新我们的 tick 计数器。
    //
    G_ui32SysTickCount++;
    }
    
    //*********
    //
    //此函数返回错误代码//的字符串表示
    ,该错误代码是从函数调用返回到 FatFs 的。 它可用于
    //打印人类可读的错误消息。
    ////
    *****************
    static 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);
    }
    }
    
    //
    //此时未找到匹配的代码,因此返回 a
    //指示未知错误的字符串。
    //
    return ("未知 ERR");
    }
    
    //*********
    //
    //这是 MSC 驱动程序的回调。
    //
    //\param ui32Instance 是
    与驱动程序进行通信时所需的驱动程序实例//。
    //\param ui32event 是驱动程序定义的事件之一。
    //\param pvData 是传递到寄存器初始调用
    中的数据的指针//回调。
    //
    //此函数处理 MSC 驱动程序的回调事件。
    当前处理的唯一事件//是 MSC_EVENT_OPEN 和 MSC_EVENT_CLOSE。 这使
    //主例程能够知道何时检测到 MSC 设备并
    枚举//以及何时从系统中删除 MSC 设备。
    //
    //返回无
    //
    ////*********
    静态空 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;
    
    //
    //重新初始化文件系统。
    //
    FileInit();
    
    中断;
    }
    
    默认值:
    {
    中断;
    }
    }
    
    
    
    //*********
    //
    //这是来自主机堆栈的通用回调。
    //
    //// pvData 实际上是 tEventInfo 结构的指针。
    //
    //将调用此函数,以便在发生与
    大容量存储设备相关的 USB 事件之外的//时通知应用程序。 此时
    //此选项用于检测插入和删除的不受支持的设备。
    //它还用于在发生电源故障时通知应用程序。
    //
    将 g_USBGenericEventDriver 包含在//主机控制器驱动程序数组中时,需要此函
    数,该数将传递到// USBHCDRegisterDrivers()函数。
    ////
    *****************
    void USBHCEDEvents (void *pvData){
    tEventInfo *pEventInfo;
    
    //
    //将此指针投射到其实际类型。
    //
    pEventInfo =(tEventInfo *) pvData;
    
    //
    //处理每种事件
    //
    switch (pEventInfo->ui32Event)
    {
    //
    //已连接未知设备。
    //
    案例 USB_EVENT_UNKNOWN_Connected:
    {
    //
    //检测到未知设备。
    //
    g_Estate = State_unknown_device;
    中断;
    }
    
    //
    //已拔下未知设备。
    //
    案例 USB_EVENT_DISCONNECTED:
    {
    //
    //已删除未知设备。
    //
    g_Estate = State_no_device;
    中断;
    }
    
    //
    //检测到总线电源故障。
    //
    案例 USB_EVENT_POWER_FAULT:
    {
    //
    //不能通电表示没有设备。
    //
    G_ARRAT = State_Power_FAULT;
    中断;
    }
    
    默认值:
    {
    中断;
    }
    }
    
    
    
    //*********
    //在 UART 上打印文件结构。
    //
    静态 int printFileStructure (void){
    
    uint32_t ui32ItemCount;
    FRESULT 结果;
    
    //
    //打开当前目录进行访问。
    //
    fresult = f_opendir (&g_sDirObject、g_cCwdBuf);
    
    //
    //检查错误,如果有问题,则返回。
    //
    if (fresult!= FR_OK)
    {
    //
    //确保报告错误。
    //
    UARTprintf ("USB 盘出错:\r\n");
    UARTprintf ((char *) StringFromFresult (fresult));
    UARTprintf ("\r\n");
    return (fresult);
    }
    
    ui32ItemCount = 0;
    
    //
    //输入循环以枚举所有目录条目。
    //
    for (;;)
    {
    //
    //从目录中读取条目。
    //
    fresult = f_readdir (&g_sDirObject、&g_sFileInfo);
    
    //
    //检查错误,如果有问题,则返回。
    //
    if (fresult!= FR_OK)
    {
    UARTprintf ("USB 盘出错:\r\n");
    UARTprintf ((char *) StringFromFresult (fresult));
    UARTprintf ("\r\n");
    return (fresult);
    }
    
    //
    //如果文件名为空,则这是的结尾
    //列表。
    //
    if (!g_sFileInfo.fname[0])
    {
    中断;
    }
    
    //
    //在控制台上添加信息
    //
    if (ui32ItemCount < NUM_LIST_STRES)
    {
    snprintf (g_pcFilename[ui32ItemCount]、MAX_filename_string_LEN、
    "(%c)%s"、(g_sFileInfo.fattrib 和 am_DIR)? 'D':'F'、
    g_sFileInfo.fname);
    UARTprintf (g_pcFilames[ui32ItemCount]);
    UARTprintf ("\r\n");
    }
    
    //
    //移动到我们用于填充的项目数组中的下一个条目
    //列表框。
    //
    ui32ItemCount++;
    }
    
    //
    //将其添加到这里,返回时没有错误。
    //
    返回(0);
    }