8 Replies Latest reply on Jun 12, 2019 6:58 AM by KTrenholm_1955226

    USBFS - Issues handling connect after disconnect

    KTrenholm_1955226

      Hi all,

       

      I'm coming across a problem with my firmware for a USB Mouse+Keyboard composite device using the USBFS component.  The device is self-powered rather than bus powered.  Long story short, I'm sometimes having problems gracefully handling USB disconnect/reconnect.  I manage to connect normally when I plug to a host PC, but sometimes I am unable to connect/reconnect unless I fully power cycle the PSoC.  Windows will usually just throw a "USB Device has malfunctioned" pop-up at me.  Sometimes it will just show the composite device in the device manager with a little yellow marker on it denoting that "The Device Could Not Start".  

       

      I had thought this code working but something must be wrong that I'm not seeing.  My USB functions are below with descriptions, maybe someone will see something I'm not catching:

       

      I have two variables in my usb source file that serve as flags used in the code below:

      //Connection to host has been established
      volatile static bool USB_IsConnected                = false;
      
      //VUSB has been detected
      static bool USB_VBusPresent                         = false;
      

       

      I call USBFS_Start() only once, when the PSoC first starts. To actually begin exchanging data with the host, I call USB_Init():

      /** USB_Init
       *  - Attempts to begin communication with USB host
       *  - Returns bGetConfiguration status.
       */
      static bool USB_Init (void){
          
          USBFS_InitComponent(0,USBFS_DWR_POWER_OPERATION);
          CyDelay(50);
          if (USBFS_bGetConfiguration()){
              USBFS_LoadInEP(USB_KEYBOARD_ENDPOINT_NUM,(uint8_t *)USB_KeyboardDataBuff,USB_KEYBOARD_DATA_SIZE);
              USBFS_LoadInEP(USB_MOUSE_ENDPOINT_NUM,(uint8_t *)USB_MouseDataBuff,USB_MOUSE_DATA_SIZE);
              return true;
          }
          
          return false;
      }
      

       

      My main USB function is Process_USB():

      /** Process_USB
       *  - Handles USB Connection/Disconnection
       *  - Handles Endpoint data exchange for Mouse+Keyboard
       */
      void Process_USB (void){
          static bool is_connected = false, conn_prev = false;
          static keyboard_led_t led_data_prev;
          
          is_connected = USB_UpdateConnection();
          
          if (is_connected){
              /*We are currently connected*/
              if (USBFS_bGetEPAckState(USB_MOUSE_ENDPOINT_NUM)){
                  /*Mouse Data Updated Here - Snipped*/
              }
              
              if (USBFS_bGetEPAckState(USB_KEYBOARD_ENDPOINT_NUM)){
                  /*Keyboard Data Updated Here - Snipped*/
                  
              }
              
          } else if (conn_prev) {
              /*Connection lost, terminate EPs*/
              USBFS_TerminateEP(USB_KEYBOARD_ENDPOINT_NUM);   
              USBFS_TerminateEP(USB_MOUSE_ENDPOINT_NUM);   
          }
          
          conn_prev = is_connected;
          
          LED_2_Write(is_connected);
          
      }
      

      The main thing I want to point out is the call to USB_UpdateConnection, which governs the behavior of this function.  If we are connected, it will update endpoints and exhange data with the host.  If not, the endpoints are terminated, to be restarted in USB_Init() upon reconnection.

       

      In order to track the state of my USB connection.  I am regularly calling my USB_UpdateConnection() function every time I enter Process_USB().  When VBUS is present, and we are not currently enumerated with the host, this function will call USB_Init() to attempt to enumerate.  If it fails, it will try again on the next time through.  If VBUS is not present, the flags will be set as such to begin calling USB_Init() once VBUS returns.

      /** USB_UpdateConnection
       *  - Handles USB Enumeration when VBUS is detected
       *  - returns:  0 -> No VBUS/Host detected
       *              1 -> Enumerated with host 
       */
      static bool USB_UpdateConnection (void){
          static bool connected = false;
          
          if (USBFS_VBusPresent() == USBFS_VBUS_VALID){
              /*VBUS is present*/
              USB_VBusPresent = true;
              
              if (!connected){
                  /*New connection, Enumerate*/   
                  #ifdef DEBUG_USB
                          DBG_UART_PutStringConst("USB: CONNECTING\r\n");  
                  #endif
                  if (USB_Init()){
                      #ifdef DEBUG_USB
                          DBG_UART_PutStringConst("USB: CONNECTED\r\n");  
                      #endif
                      USB_IsConnected = true;
                      connected = true;
                  }
                  
              }
              
          } else {
              /*No VBUS Present*/
              if (connected){
                  /*Disconnected*/
                  #ifdef DEBUG_USB
                      DBG_UART_PutStringConst("USB: DISCONNECT\r\n");  
                  #endif
                  connected = false;
              }
              
              USB_IsConnected = false;
              USB_VBusPresent = false;
              connected = false;
          }
          
          if (!USB_IsConnected && USB_VBusPresent){
              /*No Data Connection, but VBus still present*/
              #ifdef DEBUG_USB
                  DBG_UART_PutStringConst("USB: NO HOST\r\n");  
              #endif
              connected = false;
          }
          
          return connected;
      }
      

       

      I also have an activity check I am calling every 100mS to keep an eye on USB activity to trigger a reconnect:

      /** USB_Timeout_Tick
       *  - Checks USB activity every 500mS
       *  - Detects when no activity has occurred, denoting a connection problem, 
       *    and sets USB_IsConnected to match.
      */
      void USB_Timeout_Tick (void){
          static uint8_t counter = 0;
          if (USB_IsConnected){
              
              if (++counter >= USB_DATA_TIMEOUT_COUNT){
                  if (!USBFS_CheckActivity()){
                      #ifdef DEBUG_USB
                          DBG_UART_PutStringConst("USB: ACT TIMEOUT\r\n");  
                      #endif
                      USB_IsConnected = false;
                  }
                  counter = 0;
              }
              
          } else {
              counter = 0;
          }
          
      }
      

      This counter will trigger an activity check every 500mS.  If no activity on the bus, it will flag as disconnected, and will attempt to reconnect in USB_UpdateConnection().  In my case, I haven't seen this actually trigger in this instance, but I figured I'd include it for completeness.

       

      For some reason this setup is not 100% reliable to make a USB connection to a host (I've tested both my Windows 7 PC and a Raspberry Pi).  It works most of the time, but will for some reason I can't determine just get stuck calling USB_Init() and USBFS_bGetConfiguration() simply never returns true.  It then spends all eternity attempting to enumerate and not being able to do so.

       

      Does anyone out there maybe have some tips of what I could try here to make connection/reconnection to the host a more reliable process?  I can't seem to figure out the "Trigger" for what's causing it to sometimes just not work.  Even writing this up, this same code is now suddenly working, but it's only a matter of time before I start having problems with it again.

        • 1. Re: USBFS - Issues handling connect after disconnect
          AnkitaS_51

          Hi,

           

          In this Trouble shooting guide for USB it is described how to handle Unplug- Replug events in case of Self powered device :

          Troubleshooting PSoC® 3, PSoC 4 L-Series, and PSoC 5LP USB Designs – KBA210620

           

          I think it will be useful in this case.

          If its not, can you please share the project.

           

          Thanks,

          Ankita Singh

          • 2. Re: USBFS - Issues handling connect after disconnect
            KTrenholm_1955226

            Ankita,

             

                Thank you for the reply, this snippet from that guide:

            uint8 USB_Started_Flag    ;
            for (;;)
            {
                if (VBUS_MONITOR_PIN_Read() == 0)
                {
                      USBFS_Stop();
                      USB_Started_Flag = 0;
                }
                else if (USB_Started_Flag == 0 )
                {
                    USBFS_Start(device, USBFS_3V_OPERATION );
                }
            
            

             

            This is basically what I am doing, detecting VBUS and acting accordingly.  However the difference is that when I lose VBUS I am not calling USBFS_Stop().  Is completely stopping the USBFS component required? 

             

            In my case I am terminating my endpoints upon detecting a disconnect and calling USBFS_InitComponent() upon reconnect.  The idea being that I don't want to shut the whole USBFS component down, but just shut down communication until the host has returned.  Once I detect VBUS again, I call:

            /** USB_Init
             *  - Attempts to begin communication with USB host
             *  - Returns bGetConfiguration status.
             */
            static bool USB_Init (void){
                
                USBFS_InitComponent(0,USBFS_DWR_POWER_OPERATION);
                CyDelay(100);
                if (USBFS_bGetConfiguration()){
                    USBFS_LoadInEP(USB_KEYBOARD_ENDPOINT_NUM,(uint8_t *)USB_KeyboardDataBuff,USB_KEYBOARD_DATA_SIZE);
                    USBFS_LoadInEP(USB_MOUSE_ENDPOINT_NUM,(uint8_t *)USB_MouseDataBuff,USB_MOUSE_DATA_SIZE);
                    return true;
                }
                
                return false;
            }
            

             

            I DID increase the delay between USBFS_InitComponent() and USBFS_bGetConfiguration from 50ms to 100ms, and I haven't run into a problem reconnecting since doing so.  That may have been the issue, but I can't be sure yet.  I might just be getting lucky as this issue seems to pop up and then go away as it pleases.

             

            At the root of it is that once the host VBUS returns and I attempt to re-enumerate, USBFS_bGetConfiguration() never returns 1 and USB_Init() gets called over and over again as it tries to re-enumerate with the host.

             

            Another suggestion by that guide is to use a USB Analyzer to look at the communication between host PC and USB device.  I actually use a Total Phase Beagle USB 12 (which uses the same software as the screenshot in the troubleshooting guide) to attempt to do this.  For some reason my PSoC USB composite HID Mouse and Keyboard do not create any logs on the USB analyzer, as if it isn't even connected.  Very strange since I'm observing the mouse and keyboard working just fine on the host PC, but the analyzer just sits there idle.  Any idea why that might be?

             

            Thanks again for taking the time to respond.

            -Kyle

            • 3. Re: USBFS - Issues handling connect after disconnect
              AnkitaS_51

              Hi Kyle,

               

              Can you change the CyDelay(100); line  to :

               

              /* Wait for device to enumerate */

                  while (0u == USBFS_GetConfiguration())

                  {

                  }

               

              I am attaching the example project which is working fine after multiple plugin and plugout  on Cy8ckit-050

               

              Thanks,

              Ankita

              • 4. Re: USBFS - Issues handling connect after disconnect
                KTrenholm_1955226

                I'd rather not use a while{} in this context, because I don't want to have the potential to sit spinning inside the while loop.

                 

                The code I have currently polls USBFS_GetConfiguration() at regular intervals when VUSB is detected and has the benefit of not potentially lock my code into an infinite loop when the host isn't responding.

                 

                By this method, USB_Init() and by proxy USBFS_GetConfiguration() takes about 2mS to be called again if host communication was not established.

                 

                Regardless, I did try a variation on what you suggested, avoiding the potential to get stuck:

                /** USB_Init
                 *  - Attempts to begin communication with USB host
                 *  - Returns bGetConfiguration status.
                 */
                static bool USB_Init (void){
                    uint8_t timeout = 0;
                    bool connected = true;
                    
                    USBFS_InitComponent(0,USBFS_DWR_POWER_OPERATION);
                    
                    while (!USBFS_bGetConfiguration()){
                        
                        if (++timeout > 20){
                            connected = false;   
                            break;
                        }
                        
                    }
                    
                    if (connected){
                        USBFS_LoadInEP(USB_KEYBOARD_ENDPOINT_NUM,(uint8_t *)USB_KeyboardDataBuff,USB_KEYBOARD_DATA_SIZE);
                        USBFS_LoadInEP(USB_MOUSE_ENDPOINT_NUM,(uint8_t *)USB_MouseDataBuff,USB_MOUSE_DATA_SIZE);
                    }
                    
                    return connected;
                }
                

                 

                However this code does not work and refuses to establish a connection with the host PC at all.  If I add the delay for 100mS back in after USBFS_InitComponent(), it seems to work eventually.  It still takes 8 to 9 calls to USB_Init to actually enumerate, but that's similar to what I saw with my original code.  I imagine this time is just waiting for the host PC to finish enumerating.  The only difference here is I'm calling USBFS_bGetConfiguration() 20 times each call rather than once.

                 

                Why is this delay necessary after USBFS_InitComponent?  I can't find a mention of needing a delay like this in the USBFS Datasheet.

                • 5. Re: USBFS - Issues handling connect after disconnect
                  AnkitaS_51

                  Have you tried using USBFS_Start() instead of USBFS_InitComponent().

                   

                  Can you please share the project.

                  • 6. Re: USBFS - Issues handling connect after disconnect
                    KTrenholm_1955226

                    That's my question.  Should I be using USBFS_Start() instead of USBFS_InitComponent()?  If there is a delay that must be present after the call to USBFS_InitComponent() before polling USBFS_bGetConfiguration(), how long should it be?  So far increasing the delay from 50 to 100 ms is the only thing that has seemed to make a difference.

                     

                    I unfortunately cannot post the complete project, that is why I have provided all the USB handling code on its own.

                    • 7. Re: USBFS - Issues handling connect after disconnect
                      AnkitaS_51

                      I will suggest you to use USBFS_Start() instead of USBFS_InitComponent() because USBFS_Start() routine  is the preferred method to begin component operation.

                      If you check any of the USB based CE available in Creator, this delay by waiting for enumeration has been provided:

                       

                      "USBFS_1_Start(0, USBFS_1_DWR_VDDD_OPERATION);   /* Start USBFS Operation/device 0 and with 5V operation */

                      while(!USBFS_1_bGetConfiguration());      /* Wait for Device to enumerate */"

                       

                      We should wait till the device  gets enumerated by checking USBFS_1_bGetConfiguration().

                       

                      Previously, you terminated the while loop by Break after 20 iterations, may be that delay was not sufficient.

                       

                      Q: If there is a delay that must be present after the call to USBFS_InitComponent() before polling USBFS_bGetConfiguration(), how long should it be?

                      A: That delay depends on host and the number of devices connected to it. Thus , we can't provide a number or a range.

                      • 8. Re: USBFS - Issues handling connect after disconnect
                        KTrenholm_1955226

                        Bear in mind my USB_Init() function is called recurring from main().  I am not just checking USBFS_1_bGetConfiguration() once then never trying again.  I am re-entering USB_Init() again after proceeding through the rest of my main loop.  It normally takes at least 10 calls to enumerate with the host.  This isn't a problem, but the problem was that sometimes it would just sit there forever calling USB_Init() every time through main() and never enumerating with the host PC.  Again, this only seems to have went away when I manually set a delay of 100ms between InitComponent and GetConfiguration.  It still will take 10 or so trips through USB_Init() to enumerate, but it seems to no longer show the issue of never connecting.

                         

                        This works:

                        /** USB_Init 
                         *  - Attempts to begin communication with USB host 
                         *  - Returns bGetConfiguration status. 
                         *  - Called recurringly from main() when VBUS is present and we haven't connected yet
                         */  
                        static bool USB_Init (void){  
                             
                            USBFS_InitComponent(0,USBFS_DWR_POWER_OPERATION);  
                            CyDelay(100);  
                            if (USBFS_bGetConfiguration()){  
                                USBFS_LoadInEP(USB_KEYBOARD_ENDPOINT_NUM,(uint8_t *)USB_KeyboardDataBuff,USB_KEYBOARD_DATA_SIZE); 
                                USBFS_LoadInEP(USB_MOUSE_ENDPOINT_NUM,(uint8_t *)USB_MouseDataBuff,USB_MOUSE_DATA_SIZE); 
                                return true;  
                            } 
                             
                            return false;  
                        } 
                        

                         

                        This does not work:

                        /** USB_Init
                         *  - Attempts to begin communication with USB host
                         *  - Returns bGetConfiguration status.
                         */
                        static bool USB_Init (void){
                            uint8_t timeout = 0;
                            bool connected = true;
                            
                            USBFS_InitComponent(0,USBFS_DWR_POWER_OPERATION);
                            CyDelay(50);
                            
                            while (!USBFS_bGetConfiguration()){
                                
                                if (++timeout > 20){
                                    DBG_UART_PutStringConst("T/O\r\n");  
                                    connected = false;   
                                    break;
                                }
                                
                            }
                            
                            if (connected){
                                USBFS_LoadInEP(USB_KEYBOARD_ENDPOINT_NUM,(uint8_t *)USB_KeyboardDataBuff,USB_KEYBOARD_DATA_SIZE);
                                USBFS_LoadInEP(USB_MOUSE_ENDPOINT_NUM,(uint8_t *)USB_MouseDataBuff,USB_MOUSE_DATA_SIZE);
                            }
                            
                            return connected;
                        }
                        

                         

                         

                        This Works:

                        /** USB_Init
                         *  - Attempts to begin communication with USB host
                         *  - Returns bGetConfiguration status.
                         */
                        static bool USB_Init (void){
                            uint8_t timeout = 0;
                            bool connected = true;
                            
                            USBFS_InitComponent(0,USBFS_DWR_POWER_OPERATION);
                            CyDelay(100);
                            
                            while (!USBFS_bGetConfiguration()){
                                
                                if (++timeout > 20){
                                    DBG_UART_PutStringConst("T/O\r\n");  
                                    connected = false;   
                                    break;
                                }
                                
                            }
                            
                            if (connected){
                                USBFS_LoadInEP(USB_KEYBOARD_ENDPOINT_NUM,(uint8_t *)USB_KeyboardDataBuff,USB_KEYBOARD_DATA_SIZE);
                                USBFS_LoadInEP(USB_MOUSE_ENDPOINT_NUM,(uint8_t *)USB_MouseDataBuff,USB_MOUSE_DATA_SIZE);
                            }
                            
                            return connected;
                        }
                        

                         

                        Remember all of the above were called recurringly from main() every few millisecons.  The argument for CyDelay() seems to really make all the difference.

                         

                        Also note I am not shutting down the component via USBFS_Stop() anywhere in this firmware.

                        USBFS_Start() is being called upon initialization of the firmware before entering main().

                        USBFS_Stop() is never being called in my code.  I am calling USBFS_TerminateEP() for my two endpoints when I lose VBUS or lose the host data connection (see above in my first post, in my Process_USB() function).  USB_Init() is then recurringly called every time through main() until the host re-enumerates.  The idea was to disable and re-enable communication, but not completely shut down and restart the entire USBFS component (I figured disabling and re-enabling communication would be quicker than stopping and starting the whole component)

                         

                        Again, I have not run into a further issue since increasing the delay from 50ms to 100ms.  It still takes a similar number of calls of USB_Init() to connect, but I no longer end up in a situation of the PSoC never enumerating with the host.  I'm just curious as to why this is, because it doesn't jive with the explanation I'm getting.  If it was just a matter of calling USBFS_bGetConfiguration() recurring until the host enumerates, my original code would work, because in both situations I'm calling USBFS_bGetConfiguration() recurringly, the only difference is my manual delay time between the call of USBFS_InitComponent() and USBFS_bGetConfiguration().