Other Parts Discussed in Thread: SYSCONFIG
Tool/software:
Hello TI Team,
I'm working on a bare-metal C++ project for the AM261x device using TinyUSB CDC class integrated via CMake, not using SysConfig or TI SDK dependencies. The USB stack is initialized using a custom sequence similar to CCS examples, and the USB CDC communication works initially.
void UsbDevice::Start()
{
DRIVEROPEN::Drivers_open();
usb_monitor_task();
}
/////////////////////////////////////////////////////////////////////////////
/// @brief Stop
/// This function initialize the use device with RHPORT = 0;
///
/// @param[in,out] None
///
/// @return None
/// @retval None
/////////////////////////////////////////////////////////////////////////////
void UsbDevice::Stop()
{
DRIVEROPEN::Drivers_close();
}
/////////////////////////////////////////////////////////////////////////////
/// @brief UsbDeviceTask
/// This function start the usb task.
///
/// @param[in,out] None
///
/// @return None
/// @retval None
/////////////////////////////////////////////////////////////////////////////
void UsbDevice::UsbDeviceTask()
{
#if defined(SOC_AM261X)
USB_dwcTask(); // TI-specific USB low-level task
#endif
tud_task(); // TinyUSB device task
}
/////////////////////////////////////////////////////////////////////////////
/// @brief USB device CDC connected
/// This function return whether the usb cdc device is connected or not
///
/// @param[in,out] None
///
/// @return None
/// @retval None
/// @return True or False
/////////////////////////////////////////////////////////////////////////////
bool UsbDevice::CdcConnected()
{
return tud_cdc_n_connected(0);
}
/////////////////////////////////////////////////////////////////////////////
/// @brief USB Device Interrupt handler
/// This function handles the usb interrupt.
///
/// @param[in,out] None
///
/// @return None
/// @retval None
/////////////////////////////////////////////////////////////////////////////
void UsbDevice::UsbDeviceInterruptHandler()
{
tud_int_handler(0);
}
/////////////////////////////////////////////////////////////////////////////
/// @brief Flush
/// This function flush data after write operation.
/// @param[in] itf
/// @return flush count
/////////////////////////////////////////////////////////////////////////////
uint32_t UsbDevice::Flush(uint8_t itf)
{
return tud_cdc_n_write_flush(itf);
}
/////////////////////////////////////////////////////////////////////////////
/// @brief Write bytes to the interface
///
/// @param[in ] buffer: pointer from where data is to be copied
/// @param[in] bufsize: Number of bytes to write
///
/// @return number of bytes of data written
///
/////////////////////////////////////////////////////////////////////////////
uint32_t UsbDevice::Write(const uint8_t *buffer, uint32_t bufsize)
{
uint32_t remain = bufsize;
while(remain && tud_cdc_connected())
{
uint32_t wrcount = tud_cdc_write(buffer,remain);
remain -= wrcount;
buffer += wrcount;
}
return bufsize - remain;
}
/////////////////////////////////////////////////////////////////////////////
/// @brief Read received data for the interface
///
/// @param[out ] buffer: pointer to where data is to be copied
/// @param[out] bufsize: Number of bytes to read
///
/// @return number of bytes of data available to read
///
/////////////////////////////////////////////////////////////////////////////
uint32_t UsbDevice::Read(uint8_t *buffer, uint32_t bufsize)
{
return tud_cdc_read(buffer, bufsize);
}
void DRIVEROPEN::Drivers_open(void)
{
Drivers_i2cOpen();
USB0_enableClock();
Clock::ClockP_usleep(5000);
Drivers_usbOpen();
}
void DRIVEROPEN::Drivers_close(void)
{
Drivers_i2cClose();
Drivers_usbClose();
}
void DRIVEROPEN::Drivers_usbOpen(void)
{
/* Force USB Core reset */
// USB0_resetControl();
// Clock::ClockP_usleep(5000);
/* initialize USB HW for TI SOC */
USB_init();
/* tiny USB init */
tusb_init();
return;
}
void DRIVEROPEN::Drivers_usbClose(void)
{
USB_deinit();
return;
}
int main()
{
SystemInitialization::System_init();
UsbDevice::Start();
uint32_t tick = 0;
int messageId = 1;
int intendedCount = 0;
int sentCount = 0;
// while (!UsbDevice::CdcConnected())
// {
// printf("Waiting for CDC connection...\n");
// Clock::ClockP_usleep(100000); // Wait 100ms
// }
while (true)
{
UsbDevice::UsbDeviceTask(); // Placeholder, no FreeRTOS
usb_monitor_task();
Clock::ClockP_usleep(50000);
tick++;
if (tick % 100000 == 0)
{
char msg[64];
snprintf(msg, sizeof(msg), "Hello World %d\r\n", messageId++);
intendedCount++;
if (UsbDevice::CdcConnected())
{
UsbDevice::Write(reinterpret_cast<const uint8_t*>(msg), strlen(msg));
UsbDevice::Flush(0);
sentCount++;
}
// Report data loss if any
int lost = intendedCount - sentCount;
if (lost > 0 && UsbDevice::CdcConnected())
{
char lossMsg[64];
snprintf(lossMsg, sizeof(lossMsg), "Lost %d messages\r\n", lost);
UsbDevice::Write(reinterpret_cast<const uint8_t*>(lossMsg), strlen(lossMsg));
UsbDevice::Flush(0);
}
}
}
UsbDevice::Stop();
return 0;
}
void usb_monitor_task()
{
static bool usb_was_connected = false;
bool usb_now_connected = tud_connected();
if (usb_now_connected && !usb_was_connected)
{
printf("USB Device Mounted\n");
}
else if (!usb_now_connected && usb_was_connected)
{
printf("USB Device Unmounted\n");
// Attempt to reinitialize TinyUSB stack
printf("Reinitializing USB stack...\n");
tusb_init();
}
usb_was_connected = usb_now_connected;
}
Here’s a brief overview of my setup:
- USB initialization includes:
USB0_enableClock()
USB_init()
for PHY and controller setuptusb_init()
for TinyUSB stack
- USB tasks are handled in a loop via:
USB_dwcTask()
(TI-specific)tud_task()
(TinyUSB)
- CDC communication is managed using
tud_cdc_write()
,tud_cdc_read()
, andtud_cdc_n_connected()
- I monitor USB mount/unmount events using
tud_connected()
and attempt to reinitialize withtusb_init()
on disconnect
Issue: When the USB cable is disconnected and reconnected after a few seconds, the device sometimes shows up as "Unknown USB Device" on the host. It doesn’t enumerate again properly, and CDC communication fails. This behavior is inconsistent and seems related to timing or PHY reset.
Questions:
- Is there a recommended way to handle USB reconnects on AM261x using TinyUSB?
- Should I explicitly reset the USB controller or PHY on disconnect?
- Is there a known issue with USB VBUS or ID detection on AM261x that affects reconnect behavior?
- Are there any TI-specific registers or sequences I should apply during reconnect to ensure proper enumeration?
Any guidance or reference to working examples would be greatly appreciated.
Thanks
Manjunatha HR