Codebeat
Codebeat

Reputation: 6610

AVR-C: USB connection can be detected successfully, not an USB disconnect

It works fine on the ATMega32U4 MCU however..... when battery powered, it cannot detect a disconnect from USB. It can detect a reconnect (false) and after this a true state, but no disconnect.

For example:

bool TEnjoyPad::isUSBConnected()
{
#if defined(UDADDR) && defined(_BV) && defined(ADDEN)

  //setDelay( 1000 );
  Serial1.println( "--------" );
  Serial1.println( UDADDR & _BV(ADDEN), HEX );
  //Serial1.println( UDADDR, HEX );     // 97 or 98 hex
  //Serial1.println( _BV(ADDEN), HEX ); 

  return (UDADDR & _BV(ADDEN));
#else
  return false;
#endif
} 

When you take a look at the line:

Serial1.println( UDADDR & _BV(ADDEN), HEX );

It prints:

Connected: 0x80 (function result = true)
Not connected: 0x00 (function result = false)

It can detect the change from NOT connected to connected but not connected to NOT connected (disconnected).

Is there a simple solution to this?


EDIT 13 okt 2017:

Just figure out this, it works however not always. Found it here: http://forum.arduino.cc/index.php?topic=28567.0

It is the UDINT register, it reports 16 decimal (0x10 hex) when connected.

Modified version of the first version of this function above:

bool TEnjoyPad::isUSBConnected()
{
#if defined(UDADDR) && defined(_BV) && defined(ADDEN) && defined(UDINT) && defined(USBCON) 
  return ( (UDADDR & _BV(ADDEN)) && (UDINT) );
#else
  return false;
#endif
}

It works however only when these conditions are met:

- Device is connected to computer, phone etc
- Device is disconnected from computer, phone etc

If you use an USB batterypack for example, it reports true and with some batterypacks the state is still true when disconnected. Bus is confused? When you connect again to the computer, phone etc it is reporting false and then true and when disconnected it reports false (as it should).

Any ideas? Has something to do with the construction USB datalines?

In the example, they use this line. It doesn't make a difference to me when I added it:

USBCON = USBCON | B00010000;

EDIT 14 okt 2017:

Thanks to @ralph htp, see my posted answer below, maybe it can help others too.

Upvotes: 1

Views: 1132

Answers (2)

Codebeat
Codebeat

Reputation: 6610

Finally, it works flawless, thanks to "@ralf htp". He is right the USBSTA register changes to 1 when connected to USB and 0 when not connected. You have to include pins_arduino.h otherwise the register is unknown. So this makes it very easy to detect USB Power status.

However, when you want to know either the status of the USB bus, the datalines are connected, a connection is established, it is a little more complicated. You need to know an USB connection is established when the MCU needs to operate as an USB HID device.


I don't know it is a bug or just the way USB works, the address of the USB device is kept by the MCU even when the device is disconnected or attached to an USB batterypack. Some registers keep their values even at 'invalid' state.

However, the address changes when a connection is established. So it is possible to figure out there is a real connection, a connection is made, the MCU is recognized by the USB host.


In my following code i can detect:

  • It is connected to external USB power;
  • A data connection is established, the device is recognized or not;
  • With a combination of these things it is easy to figure out how it is powered and/or connected.

Now you can decide a device function is possible or not, or report to the user the status of the device.


My renewed code looks like this:

// enjoypad.h
#include <pins_arduino.h> // Required to access some register defines
..........
..........
..........

 // Via pins_arduino.h => defined in avr/iom32u4.h
#ifdef USBSTA
 #define TEP_USB_VBUS_CONNECTED    (USBSTA == 3) 
 #define TEP_USB_VBUS_DISCONNECTED (USBSTA == 2) 
#endif

#ifdef UDADDR
 #define TEP_USB_ADDRESS (isOnUSBPower()?UDADDR:0)
#endif

#if defined(ADDEN) && defined(UDINT)   
 #define TEP_USB_ADDRESS_ESTABLISHED (_BV(ADDEN)== 0x80 && UDINT > 0 )
#endif

..........
..........
..........

// enjoypad.cpp

uint8_t TEnjoyPad::getUSBaddress()
{ 
  #ifdef TEP_USB_ADDRESS
   return TEP_USB_ADDRESS; 
  #else 
   return 0;
  #endif 
}

bool TEnjoyPad::isOnUSBPower()
{
  #ifdef TEP_USB_VBUS_CONNECTED   
    return TEP_USB_VBUS_CONNECTED;
  #else 
   return false;
  #endif 
}

bool TEnjoyPad::isUSBDataEstablished()
{
 #ifdef TEP_USB_ADDRESS_ESTABLISHED
  static uint8_t iLastAddress = 0;
  static uint8_t bUSBDataEstablished = false;

  // The address changes from 0 to something (byte) or increases 
  // with one each time you plugin the device, when this is an USB host. 
  // If the VBUS not connected, it returns always 0 (zero) 
  uint8_t iAddress = getUSBaddress();
  if( iAddress > 0 ) 
  {
    //Serial1.println( _BV(ADDEN), HEX );
    //Serial1.println( UDINT, HEX );

    if( TEP_USB_ADDRESS_ESTABLISHED )
    {
      //Serial1.println( iAddress );
      // Need update? On USB Battery for example, address stays the same
      // so it never perform an update and never reports there is a 
      // data connection established.
      if( iAddress != iLastAddress )
      {
        bUSBDataEstablished = true;
        iLastAddress = iAddress;
      }
    }  
  }
  else { bUSBDataEstablished = false; }

  return bUSBDataEstablished; 
 #else
  return false;
 #endif
}

bool TEnjoyPad::isOnUSBPowerBattery() // Or something else providing power
{ return ( isOnUSBPower() && !isUSBDataEstablished() ); }

bool TEnjoyPad::isOnBatteryPower()
{ return !isOnUSBPower(); }

The code in action :-) :

Code in action

Upvotes: 1

ralf htp
ralf htp

Reputation: 9422

in page 261 and 279 of the datasheet is said that there is VBUSTI interrupt for USB insert an remove. There is an implementation in the LUFA stack ( EVENT_USB_Host_DeviceUnattached() if in host mode and EVENT_USB_Device_Disconnect() if in device mode )

source code is also in http://caves.org/section/commelect/DUSI/openmag/src/myusb/MyUSB/MyUSB/Drivers/USB/HighLevel/USBInterrupt.h

edit

only valid in USB device mode :

if you want to detect if a pure voltage source like a battery block is attached or detached you can use the VBUS ( voltage bus ) pad. its state can be accessed in the USBSTA ( USB status ) in the VBUS bit / flag, this is in the data sheet on page 268.

to do this catch the VBUSTI interrupt and whenever the interrupt occurs check the VBUS bit / flag in USBSTA register. if VBUS is high (1) the battery is attached ( voltage on VBUS pad is > 1.4 V, datasheet page 265 ) if it is low battery is not attached ( voltage on VBUS pad is < 1.4 V )

Upvotes: 0

Related Questions