AnsweredAssumed Answered

How to recover from USB errors, or how to reinitialize USB0 without a full reset?

Question asked by Mark Minich on May 19, 2015
Latest reply on May 28, 2015 by derek lau

Our custom board is using a Kinetis MKL26Z128VFM4 (KL26 sub-family) which connects to a host PC over USB (the Kinetis appears as a "COMx" serial port on the PC). Our development environment is CodeWarrior using ProcessorExpert with USB code by Erich Styger (http://sourceforge.net/projects/mcuoneclipse/files/PEx%20Components/). Our board includes a switching voltage regulator that regulates 20V power to a heater, and with disturbing regularity, sometimes when we turn on/off power to that regulator, the USB communication goes into an errored state from which we have not figured out how to recover without resetting the Kinetis. I need a way to automatically clear the error state in software and/or re-initialize USB0 so that we can resume communications. Ideally this would not result in dropping the "COMx" port connection in the PC.

 

In file CDC1.c, there are two flags: start_app and start_transactions. While USB communications is operating normally, both are TRUE. Sometimes when we turn on/off the heater power, both of these flags go FALSE, after which USB communications is inoperative.

 

I've traced the code trying to figure out how we get into this state, and here's what I see:

 

In function USB_ISR() (in Generated_Code\usb_dci_kinetis.c), we discover a USB error in "if(ERROR_FLAG(intr_stat))". This is checking bit 1 of the USB0_ISTAT register. According to the USB0_ERRSTAT register, the error seems to be either 0x01 (PIDERR) or 0x08 (DFN8). Other errors may also appear; I have only been able to test this 10 times or so because it takes a good bit of time to set things up & cause the error. Function USB_ISR() assigns the USB0_ERRSTAT value (0x01 or 0x08) to event.errors, and then invokes a chain of calls, many through function pointers, which passes that information up higher & higher, but nobody seems to ever care what bits were set in USB0_ERRSTAT. The chain of calls includes...

 

   USB_ISR()

      USB_Device_Call_Service()

         g_usb_CB[USB_SERVICE_ERROR]() --> USB_Error_Service()

            g_class_callback() --> USB_Class_CDC_Event()

               g_cdc_class_callback() --> CDC1_App_Callback()

 

At the end of CDC1_App_Callback(), it sets those flags (start_app and start_transactions) to FALSE and then returns. As mentioned, I don't think any code anywhere actually checks the USB0_ERRSTAT bits to handle any error different from any other error, or to differentiate between different types of errors.

 

In our application, I tried calling the following to see if I could reset the USB communications (which I stole from code in PE_low_level_init() in Cpu.c)...

 

   while (TRUE)

   {

      // Run CDC app task periodically and check for USB connection with host

      byte err = CDC1_App_Task(out, sizeof(out));

      if (err == ERR_OK)

         g_USB_isEnumerated = TRUE;

      else if (err == ERR_BUSOFF)

      {

         if (g_USB_isEnumerated)

         {  // USB *has* been enumerated but has encountered errors; attempt to recover

            USB0_Init();        // same as in PE_low_level_init()

            Tx1_Init();         // same as in PE_low_level_init()

            Rx1_Init();         // same as in PE_low_level_init()

            //(void)USB1_Init();  // same as in PE_low_level_init()

            ++numberOfRecoveryAttempts;

         }

         g_USB_isEnumerated = FALSE;

         .

         .

         .

 

...unfortunately when I do that, execution gets stuck in USB0_Init() somewhere near where it sets USB0_USBTRC0. The USB0 interrupt keeps firing & from then on, that's all we do: service USB0 interrupts.

 

How can I automatically recover from USB0 error conditions without resetting the Kinetis?

 

Thank you,

-Mark Minich

 

======== UPDATE: ========

 

In Generated_Code\CDC1.c, lines I commented out are prefixed with "//CCCC", and lines I added are suffixed by "//NNNN".....

int USB_ignoredErrorCount; // NNNN

void CDC1_App_Callback(byte controller_ID, byte event_type, void *val)

{
.
.
.
  } else if (event_type == USB_APP_ERROR) { /* detach? */

//CCCC    start_app = FALSE;

//CCCC    start_transactions = FALSE;

       ++USB_ignoredErrorCount; // NNNN

  }

}

...and now everything works swimmingly. I've turned heater power on/off over 100 times, and the code has ignored 6 errors, and comm with the PC is still going fine. I have not verified if we dropped bytes or had corrupted bytes on comm link, but at least communications did not terminate due to the errors.

 

Shouldn't the Freescale or ProcessorExpert code be updated to simply track error stats, but not terminate comms?

 

======== ANOTHER UPDATE: ========

 

While that worked great for the SerialPort class in .NET Framework 4 on Windows 7, it does not work at all on .NET Framework 4 on Windows XP. On WinXP, now after several times turning the heater on & off, the Kinetis board thinks it's fine, but the SerialPort class in .NET Framework 4 on Windows XP is all screwed up, complaining with a System::Exception^ about either "A device attached to the system is not functioning" or "The device is not connected".

Outcomes