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.

LP-AM261: USB can‘t operate stable!

Part Number: LP-AM261


Tool/software:

Test environment:  LP AM261X  + cdc_echo_am261x-lp_r5fss0-0_nortos_ti-arm-clang in  mcu_plus_sdk_am261x_10_02_00_15.

After I downloaded the .out file to the LP AM261X board and ran the program, I used a bat file to execute the Python script in a loop 200 times. However, when it ran to more than 100 times, the USB device could no longer be found. 

The USB_test.py  is:

import serial
import serial.tools.list_ports

# List all serial port devices
ports = serial.tools.list_ports.comports()
usb_port = None

# Enumerate all serial port devices
for port in sorted(ports):
    if "USB" in port.description:
        print(f"Found USB device on port: {port.device}")
        usb_port = port.device
        break  # Exit the loop after finding the first USB device

# Exit directly if no USB device is found
if usb_port is None:
    print("Error: No USB device found. Exiting...")
    exit()

baudrate = 19200  # Baud rate, modify according to actual requirements
timeout = 2  # Timeout period (seconds)

# Define the data to be sent
data_to_send = [0xF8, 0x03, 0x01, 0x02, 0x00, 0x02, 0x70, 0x5E]
data_to_send_hex = ' '.join(f'{byte:02X}' for byte in data_to_send)
print(f"USB Sent data: {data_to_send_hex}")

try:
     # Open the serial port
    ser = serial.Serial(usb_port, baudrate, timeout=timeout)
    
    # Send data
    ser.write(data_to_send)

    # Read the received data
    received_data = ser.read(20)
    
    # Close the serial port
    ser.close()

    # Print the received data
    received_data_hex = ' '.join(f'{byte:02X}' for byte in received_data)
    print(f"USB Received data: {received_data_hex}")

except serial.SerialException as e:
    print(f"Serial communication error: {e}")
except Exception as e:
    print(f"Unexpected error: {e}")

The bat file is:

@echo off

set /a count=0

:loop
set /a count+=1
echo The test times: %count%

python USB_test.py
timeout /t 2 /nobreak >nul

if %count% lss 200 goto loop

Test Result:

  • Hi,

    Is this out-of-box application using 2 COM ports or a custom application using only 1 COM Port?

    Regards,
    Shaunak

  • This test result is using 2 COM ports.  Whether it's or 2 COM port, the problem remains the same.  In our servo application, the USB test results are even worse.

  • Hi Xiaoxue, 

    This test result is using 2 COM ports.  Whether it's or 2 COM port, the problem remains the same.

    I'll try to reproduce this issue on my end and debug further.

    the USB test results are even worse.

    Do you mind sharing some more details about this?

    Regards,
    Shaunak

  • 1. I run cdc_echo_am261x-lp_r5fss0-0_nortos_ti-arm-clang in  mcu_plus_sdk_am261x_10_02_00_15 without any modifications on our servo control board, it only can run USB_test.py  20+ times.  It seems that our board performs worse than LP AM261X.

    2. If I run the whole servo program,  the USB_test.py may run less than 10 times. The servo program is large and the optimization is "fast". And the main PWM interrupt is designed specially, refer to https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1348817/am2634-does-am263-support-self-interrupt-nesting/5158659#5158659. Moreover, the load of the main interrupt may be a bit  high.

    Generally speaking, I think there could be issues with the USB driver, the hardware of the servo board, and our servo application program. I don't think the USB driver is robust. I have sent a servo control board to India, and you could test the USB on that board.

    Looking forward to your reply.

  • Hi,

    I am able to reproduce the issue, with some changes locally, I might have the fix ready (testing it properly before sharing). We have made some changes regarding memory allocation and the event handling queue. 

    I'll share my updated driver code in a while, I'm trying to test across different OS (Windows, Linux) to make sure the fix is working well.

    Regards,
    Shaunak

  • Hi,

    I am attaching my "synp" folder which you can place and unzip in the mcu_plus_sdk/source/usb/ folder. This is my copy of the USB driver folder which includes some fixes done after the v10.02 SDK release. Can you please use the updated driver files, re-build your USB driver using the following commands and try?

    Commands to clean and re-build USB libs in the SDK:

    gmake -sj -f makefile.am261x usbd_synp_nortos_r5f.ti-arm-clang_clean
    gmake -sj -f makefile.am261x usbd_tusb_cdc_nortos_r5f.ti-arm-clang_clean
    
    gmake -sj -f makefile.am261x usbd_synp_nortos_r5f.ti-arm-clang
    gmake -sj -f makefile.am261x usbd_tusb_cdc_nortos_r5f

    synp.zip

    Regards,
    Shaunak

  •   I tried it and it didn't work. Have you passed the tests? If you pass the test, you can send me the .out file and I will test it.

  • Hi,

    Please find attached my .out file as well as the the result log for a test of 1500 iterations (i tested for single com port as well since I remember a similar issue being raised a few months back, post that we have fixed a couple of things in the driver, which has been provided in my previous reply). We tested on both Windows as well as Linux

    usb_1000_iteration_cdc_test.txt

    https://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/908/cdc_5F00_echo.release.out

    Regards,
    Shaunak

  • I loaded the .out file to the LP AM261X board and the test can only be iterated over 150 times. And CCS shows that "Cortex_R5_0: Error: (Error -1170 @ 0x0) Unable to access the DAP...." :

    I tried this twice and got the same result as above.

  • Hi Xiaoxue,

    Seems like there are some hardware issues with the board or power supplies, clock or reset signals or even the XDS Firmware issues.

    The DAP access errors indicate that we are losing the connection with AM261x and not exactly issues with the USB. I found some similar issues reported here: https://e2e.ti.com/support/processors-group/processors/f/processors-forum/275907/unable-to-access-the-dap

    Regards,
    Shaunak

  • But when I run an another .out file, it worked fine. Could you give me a .appimage file ? I will let the program load from Flash.

  • Hi,

    Please find attached the appimage.

    Also, you see the DAP access errors on both AM261x-LP and your custom board? (i do know that usb reliability issues are higher on custom board than am261x is your observation, wanted to know the same about DAP errors).

    https://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/908/cdc_5F00_echo.release.appimage

    Regards,
    Shaunak

  • Hi,

    Also, for the CS DAP error in CCS, please use the AM261x in OSPI boot mode. Please use OSPI boot mode and flash SBL Null on the AM261x instead of using the DEV Boot mode.

    Since we have the E1 launchpad, flash the SBL Null from the AM261x v10.00.01.10 SDK (https://www.ti.com/tool/download/MCU-PLUS-SDK-AM261X/10.00.01.10)

    If you have the E2 launchpad, use the latest SDK (v10.02)

    Please use the SDK Flashing tools (software-dl.ti.com/.../TOOLS_FLASH.html) to flash the sbl_null tiimage to the board, then switch to OSPI boot mode so you can run the long tests.

    Regards,
    Shaunak

  • Hi Shaunak,

       In my control board, I loaded the cdc_echo.release.appimage and it can run 1000 times. Then I modified the CFG_TUD_CDC(to 1), usb_descriptors.c and rebuild the usb lib  to enum 1 port, and it also can run 1000 times. But when porting to my servo application program, I found it performed worse than before.

       I conducted some tests in the past week and found that adding a PWM interrupt would cause the tests to fail. In my control board, I loaded my appimage to the flash and it only could run 20+ times.

       The test codes:

       

    /*
     *  Copyright (C) 2021-2023 Texas Instruments Incorporated
     *
     *  Redistribution and use in source and binary forms, with or without
     *  modification, are permitted provided that the following conditions
     *  are met:
     *
     *    Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     *    Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the
     *    distribution.
     *
     *    Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    /* Adapted by TI for running on its platform and SDK */
    
    
    #include <ctype.h>
    #include <string.h>
    #include "hwl_usb.h"
    #include "test.h"
    
    #include "ti_drivers_config.h"
    #include "ti_drivers_open_close.h"
    #include "ti_board_open_close.h"
    #include <kernel/dpl/HwiP.h>
    #include <kernel/nortos/dpl/r5/HwiP_armv7r_vim.h>
    
    vcom_handle_t vcomhandle_test;
    uint16_t vcom_initHandle(vcom_handle_t *handle, size_t bufferSize){
      /* Zero the handle. */
      (void)memset(handle, 0, sizeof(vcom_handle_t));
      /* Set the state. */
      handle->state = kVCOM_state_Receive;
      /* allocate the buffer */
      handle->buffer = malloc(bufferSize);
      if(handle->buffer){
        (void)memset(handle->buffer, 0, bufferSize);
        handle->bufferSize = bufferSize;
        return 1;
      }
      return 0;
    }
    
    
    
    
    void vcom_resetHandle(vcom_handle_t *handle){
      /* reset members to default value. */
      handle->state = kVCOM_state_Receive;
      handle->errorStatus = 0;
      handle->txDataFrameSize = 0;
      handle->txDataSize = 0;
      handle->rxDataSize = 0;
    }
    
    void usb_test(void)
    {
    
        if(vcomhandle_test.state == kVCOM_state_Receive)
        {
            if (usb_vcom_receive(vcomhandle_test.buffer,256, &vcomhandle_test.rxDataSize)>0)
            {
                vcomhandle_test.state = kVCOM_state_Decode;
            }
    
        }
    
        if( vcomhandle_test.state == kVCOM_state_Decode)
        {
    
            vcomhandle_test.state = kVCOM_state_Send;
        }
    
        if(vcomhandle_test.state == kVCOM_state_Send)
        {
            vcomhandle_test.txDataSize = vcomhandle_test.rxDataSize;
            usb_vcom_send(vcomhandle_test.buffer,vcomhandle_test.txDataSize);
            vcomhandle_test.txDataSize = 0;
            vcomhandle_test.state = kVCOM_state_Receive;
    
        }
    
    }
    
    #define EPWM_INT_BASE_ADDR (CSL_CONTROLSS_G0_EPWM0_U_BASE)
    
    void pwm_sched_init_test(void) //62.5us interrupt
    {
    
        uint16_t u16EpwmIntPrd = (uint16_t)((125 * 250) >> 1);
        /* Time Base */
        EPWM_setEmulationMode(EPWM_INT_BASE_ADDR, EPWM_EMULATION_FREE_RUN);
        EPWM_setClockPrescaler(EPWM_INT_BASE_ADDR, EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_1);
        EPWM_setTimeBasePeriod(EPWM_INT_BASE_ADDR, u16EpwmIntPrd - 1);
        EPWM_setPeriodLoadMode(EPWM_INT_BASE_ADDR, EPWM_PERIOD_SHADOW_LOAD);
        EPWM_selectPeriodLoadEvent(EPWM_INT_BASE_ADDR, EPWM_SHADOW_LOAD_MODE_COUNTER_ZERO);
        EPWM_setTimeBaseCounter(EPWM_INT_BASE_ADDR, 0);
        EPWM_setTimeBaseCounterMode(EPWM_INT_BASE_ADDR, EPWM_COUNTER_MODE_UP);
        EPWM_setCountModeAfterSync(EPWM_INT_BASE_ADDR, EPWM_COUNT_MODE_UP_AFTER_SYNC);
        EPWM_enablePhaseShiftLoad(EPWM_INT_BASE_ADDR);
        EPWM_enableSyncOutPulseSource(EPWM_INT_BASE_ADDR, EPWM_SYNC_OUT_PULSE_ON_CNTR_ZERO);
        EPWM_setSyncInPulseSource(EPWM_INT_BASE_ADDR, EPWM_SYNC_IN_PULSE_SRC_SYNCOUT_EPWM2);
    
        /* Counter Compare */
        EPWM_setCounterCompareValue(EPWM_INT_BASE_ADDR, EPWM_COUNTER_COMPARE_A, (u16EpwmIntPrd >> 1));
        EPWM_disableGlobalLoadRegisters(EPWM_INT_BASE_ADDR, EPWM_GL_REGISTER_CMPA_CMPAHR);
        EPWM_setCounterCompareShadowLoadMode(EPWM_INT_BASE_ADDR, EPWM_COUNTER_COMPARE_A, EPWM_COMP_LOAD_ON_CNTR_ZERO);
    
        /* Counter Compare */
        EPWM_setCounterCompareValue(EPWM_INT_BASE_ADDR, EPWM_COUNTER_COMPARE_C, 13321 - 1);
        EPWM_disableGlobalLoadRegisters(EPWM_INT_BASE_ADDR, EPWM_GL_REGISTER_CMPC);
        EPWM_setCounterCompareShadowLoadMode(EPWM_INT_BASE_ADDR, EPWM_COUNTER_COMPARE_C, EPWM_COMP_LOAD_ON_CNTR_ZERO);
    
    
        /* Event Trigger */
        EPWM_disableInterrupt(EPWM_INT_BASE_ADDR);
        EPWM_setInterruptSource(EPWM_INT_BASE_ADDR, EPWM_INT_TBCTR_ZERO, 0);
        EPWM_setInterruptEventCount(EPWM_INT_BASE_ADDR, 1);
        EPWM_setInterruptEventCountInitValue(EPWM_INT_BASE_ADDR, 0);
    
        //use SOCA to trigger SAR-ADC
        EPWM_enableADCTrigger(EPWM_INT_BASE_ADDR, EPWM_SOC_A);
        EPWM_setADCTriggerSource(EPWM_INT_BASE_ADDR, EPWM_SOC_A, EPWM_SOC_TBCTR_U_CMPA, 0);
        EPWM_setADCTriggerEventPrescale(EPWM_INT_BASE_ADDR, EPWM_SOC_A, 1);
        EPWM_disableADCTriggerEventCountInit(EPWM_INT_BASE_ADDR, EPWM_SOC_A);
        EPWM_setADCTriggerEventCountInitValue(EPWM_INT_BASE_ADDR, EPWM_SOC_A, 0);
    
        //use SOCB to trigger Sigma-delta
        EPWM_enableADCTrigger(EPWM_INT_BASE_ADDR, EPWM_SOC_B);
        EPWM_setADCTriggerSource(EPWM_INT_BASE_ADDR, EPWM_SOC_B, EPWM_SOC_TBCTR_U_CMPC, 0);
        EPWM_setADCTriggerEventPrescale(EPWM_INT_BASE_ADDR, EPWM_SOC_B, 1);
        EPWM_disableADCTriggerEventCountInit(EPWM_INT_BASE_ADDR, EPWM_SOC_B);
        EPWM_setADCTriggerEventCountInitValue(EPWM_INT_BASE_ADDR, EPWM_SOC_B, 0);
    
    }
    
    void App_EPWM_ISR_test(void* handle)
    {
    
        EPWM_clearEventTriggerInterruptFlag(EPWM_INT_BASE_ADDR);
        wait_us(10);
    
    }
    
    HwiP_Params  hwiPrms1;
    
    void configHosInt_test()
    {
        /* Interrupt XBAR configuration */
        SOC_xbarSelectInterruptXBarInputSource_ext(CSL_CONTROLSS_INTXBAR_U_BASE, 0, ( INT_XBAR_EPWM0_INT ), 0, 0, 0, 0, 0, 0, 0, 0, 0);
    
        /* Register & enable interrupt */
        HwiP_Params_init(&hwiPrms1);
    
        hwiPrms1.intNum      = CSLR_R5FSS0_CORE0_CONTROLSS_INTRXBAR0_OUT_0;
        hwiPrms1.priority    = 0x01;
        hwiPrms1.isFIQ       = FALSE;
        hwiPrms1.isPulse     = TRUE;
    
        (void) HwiP_disableInt(hwiPrms1.intNum);
        HwiP_clearInt(hwiPrms1.intNum);
        HwiP_setAsFIQ(hwiPrms1.intNum, hwiPrms1.isFIQ);
        HwiP_setPri(hwiPrms1.intNum, hwiPrms1.priority);
        HwiP_setAsPulse(hwiPrms1.intNum, hwiPrms1.isPulse);
    
    
         HwiP_setVecAddr(hwiPrms1.intNum, (uintptr_t)HwiP_irq_handler);
         hwiPrms1.callback    = &App_EPWM_ISR_test;
         gHwiCtrl.isr[hwiPrms1.intNum] = hwiPrms1.callback;
         gHwiCtrl.isrArgs[hwiPrms1.intNum] = hwiPrms1.args;
    
    
        HwiP_enableInt(hwiPrms1.intNum);
    
        EPWM_clearEventTriggerInterruptFlag(EPWM_INT_BASE_ADDR);
        EPWM_enableInterrupt(EPWM_INT_BASE_ADDR);
    }
    
    
    void usb_cdc_echo_test(void)
    {
    
        usb_vcom_init();
        vcom_initHandle(&vcomhandle_test,256);
    
    #if 1
        SOC_setMultipleEpwmTbClk(0xFF,FALSE);
        pwm_sched_init_test();
        SOC_setMultipleEpwmTbClk(0xFF,TRUE);
        configHosInt_test();
    #endif
    
        while (1)
        {
            usb_vcom_task();
            usb_test();
        }
    
    }
    
    
    
    

       hwl_usb_vcom.c:

       

    /*
     * Copyright (c) 2015 - 2016, Freescale Semiconductor, Inc.
     * Copyright 2016 - 2017, 2019 NXP
     * All rights reserved.
     *
     * SPDX-License-Identifier: BSD-3-Clause
     */
    #include "hwl_usb.h"
    #include <string.h>
    
    #include <usb/synp/soc/usb_init.h>
    #include "tusb.h"
    
    #include "ti_drivers_config.h"
    #include "ti_drivers_open_close.h"
    #include "ti_board_open_close.h"
    
    
    
    /*******************************************************************************
     * Definitions
     ******************************************************************************/
    
    
    /*!
     * @brief Application initialization function.
     *
     * This function initializes the application.
     *
     * @return None.
     */
    void usb_vcom_init(void) // Drivers_usbOpen();
    {
        /* initialize USB HW for TI SOC */
        USB_init();
        /* tiny USB init */
        tusb_init();
    
        return;
    
    }
    
    uint32_t usb_vcom_receive(uint8_t *volatile rcvBuf, size_t bufSize, size_t *recvSize)
    {
        uint32_t currRecvLen = 0;
        if ( tud_cdc_n_available(0) )
        {
            uint8_t currRecvBuf[512];
            currRecvLen = tud_cdc_n_read(0, currRecvBuf, sizeof(currRecvBuf));
            if( (bufSize>= currRecvLen) && (currRecvLen>0) )
            {
                memcpy(rcvBuf, currRecvBuf, currRecvLen);
                *recvSize = currRecvLen;
                return currRecvLen;
            }
    
        }
        return 0;
    }
    
    void usb_vcom_send(const uint8_t *txData, size_t dataSize)
    {
    
        tud_cdc_n_write(0,txData,dataSize);
        tud_cdc_n_write_flush(0);
    
    }
    /*!
     * @brief Application task function.
     *
     * This function runs the task for application.
     *
     * @return None.
     */
    void usb_vcom_task(void)
    {
        USB_dwcTask(); /* Synopsis DWC task */
        tud_task(); /* tinyusb device task */
    }
    
    
    
    
    

     hwl_usb.h:

      

    #ifndef _HWL_USB_H_
    #define _HWL_USB_H_ 1
    
    #include "ctarget.h"
    
    
    typedef enum _vcom_state
    {
        kVCOM_state_Receive, /*!< ready to reveive data form usb vcom port. */
        kVCOM_state_Send, /*!< ready to send data to usb vcom port. */
        kVCOM_state_Decode,  /*!< data frame received, ready to decode. */
        kVCOM_state_Error  /*!< usb vcom error. */
    } vcom_state_t;
    
    
    
    
    typedef struct _vcom_handle
    {
        uint32_t errorStatus;                /*!< error Status. */
        vcom_state_t state;             /*!< State of the USB VCOM. */  
        uint8_t *buffer;              /*!< Start address of the receiver buffer. */
        size_t bufferSize;            /*!< Size of the buffer. */
        size_t txDataFrameSize;           /*!< Size of the data to send out. */
        size_t txDataSize;     /*!< Size of the data already sent. */
        size_t rxDataSize;   /*!< Size of the data already received. */
    
    } vcom_handle_t;
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    void usb_vcom_init(void);
    
    void usb_vcom_task(void); //run in the mainloop
    
    uint32_t usb_vcom_receive(uint8_t *volatile rcvBuf, size_t bufSize, size_t *recvSize);
    
    void usb_vcom_send(const uint8_t *txData, size_t dataSize);
    
    
    #ifdef __cplusplus
    }
    #endif
    #endif /* _HWL_USB_H_ */
    

       You can refer to my test code and add a PWM interrupt to reproduce the program:

       

      

  • I tested two control boards and the code is different only in this part(One is "#if 0" and the other is "#if 1").

  • Hi,

    Shaunak is on leave until next week, Please expect a response by mid next week.

  • Hi ,

    Looks like the USB task here is blocking CPU resources and not letting you run PWM tasks. This is further confirmed by you since you are able to run long tests on standalone USB application. One possible solution here is to Switch to OS based scheduling approach. So that USB is the default task running, but when high priority PWM interrupt comes in, we service that and return to USB task. 

    Regards,
    Shaunak

  • Can this USB problem be solved without using OS? Our servo application is not suitable for OS.

  • Hi Xiaoxue,

    I tried the following experiment: On AM261x, I enabled a periodic timer interrupt to trigger every 100us and the ISR to run for about 40us, while running the USB CDC echo test.

    With the updated changes (shared as a zip) I was able to tests successfully without any failures.

    7411.synp.zip

    I tried enabling PWM looking at the source code shared above but I was running into some issues with PWM, so I enabled and tested with a higher priority Timer interrupt.

    Regards,
    Shaunak

  • I tried it and the USB could run 1000 times iterations. I think this resolved my issue.

    I have a question. Will this solution affect my interrupt structure? 

  • Hi ,

    Really glad to know the USB issues are resolved now.

    The updated driver shared above does not change any interrupt architecture. It fixes a lot of run-time dynamic memory allocation which was causing issues due to limited memory. We changed a lot of memory architecture and handling in the driver to make it more reliable at run-time for event handling like disconnect events.

    Regards,
    Shaunak