7
\$\begingroup\$

Using STM32F4 USB Mass Storage Controller example in Device Mode, how do you detect connections and disconnections with a host controller?

I tried polling the VBUS pin state yet it could be high from a wall adapter or high without any communication with the host.

Is there a register to check? I noticed DSTS (status I guess?) in the USB library structs but couldn't find its documentation nor any useful comments in the code.

\$\endgroup\$
2
  • \$\begingroup\$ take this question to the STM forum, email their tech support. Is this a custom PCB with STM32 on it, or a discovery/other dev board? \$\endgroup\$
    – KyranF
    Oct 13, 2014 at 22:32
  • \$\begingroup\$ Seeing USB bus activity would be a strong clue. \$\endgroup\$ Jan 20, 2015 at 17:27

9 Answers 9

5
\$\begingroup\$

You could detect the connection and disconnection from this file:

usbd_core.c

and the API for it is this

USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef  *pdev)
USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef  *pdev)

I am not sure about USB Mass Storage Class but in the CDC class, the 2 APIs above detect the USB connection and disconnection, maybe this helps

Things to note:

  • Connection > when the physical USB cable is plugged into the USB port
  • Disconnection > when the physical USB cable is unplugged from the USB port

I used STMCubeMX to generate the USB CDC class.

\$\endgroup\$
6
  • \$\begingroup\$ usbd_core.c has USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef *pdev) { return USBD_OK; } ...How does this work? \$\endgroup\$
    – cp.engr
    Dec 2, 2016 at 21:36
  • \$\begingroup\$ OP is asking how to detect the connection, not what to do after it's detected. \$\endgroup\$
    – cp.engr
    Dec 2, 2016 at 21:37
  • \$\begingroup\$ This seems the right answer, but AFAICS that code is only active if USB is OTG. \$\endgroup\$ Sep 6, 2017 at 15:14
  • \$\begingroup\$ But these callbacks are already implemented in usbd_core.c and you can't add user code there. So how can the event be handled by the user? \$\endgroup\$
    – Alaa M.
    Oct 4, 2018 at 6:52
  • \$\begingroup\$ I mean if you're using CubeMx it will overwrite your code every time you hit the generate button. \$\endgroup\$
    – Alaa M.
    Oct 4, 2018 at 7:27
2
\$\begingroup\$

For the people who want to use the "User Code" parts made by CubeMX, This callback is called when USB is plugged into system.

void HAL_PCD_ResumeCallback(PCD_HandleTypeDef *hpcd)

And this one is called when USB is unplugged from the system.

void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd)

Both functions are inside usbd_conf.c

\$\endgroup\$
1
\$\begingroup\$
void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd)
{
  /* Inform USB library that core enters in suspend Mode. */
  USBD_LL_Suspend((USBD_HandleTypeDef*)hpcd->pData);
......

in usbd_conf.c

Function calls on usb cable disconnecting

(tested on Mass Storage device, CubeMX)

\$\endgroup\$
1
\$\begingroup\$

The best way that fits in with ST's software stack is to implement these functions:

void HAL_PCD_ConnectCallback(PCD_HandleTypeDef *hpcd);
void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd);

These are declared with weak linkage in the HAL stack so your implementations will override the HAL do-nothing stubs.

However, if you're using Cube then you'll find these already implemented in usbd_conf.c and for reasons unknown there are no /* USER CODE BEGIN */ sections in these functions so cube will overwrite your code next time you save it. Until ST fixes this you either have to not use Cube (I use it to bootstrap a project then stop using it and take over control) or you can modify the Cube template for this file.

In the [CUBE-INSTALL-DIR]/db/templates directory locate the usbdconf_XX.ftl file for your MCU. e.g. for the f4 it will be usbdconf_f4_c.ftl. Now edit that file to include unique USER CODE BEGIN and USER CODE END comment blocks. Next time you generate source code you'll have a section where you can place your code.

\$\endgroup\$
1
\$\begingroup\$

In my project, I am using the callbacks that are initialized / registered in the following structure:

typedef struct _USBD_CDC_Itf
{
  int8_t (* Init)(void);   // <- triggered on connection
  int8_t (* DeInit)(void); // <- triggered on disconnection

  int8_t (* Control)(uint8_t cmd, uint8_t *pbuf, uint16_t length);
  int8_t (* Receive)(uint8_t *Buf, uint32_t *Len);
  int8_t (* TransmitCplt)(uint8_t *Buf, uint32_t *Len, uint8_t epnum);
} USBD_CDC_ItfTypeDef;
\$\endgroup\$
1
\$\begingroup\$

Per the chip reference manual, you can use the BSVLD (B-session valid) bit in the Global OTG Control and Status Register. I first tried the other solutions here, but the generated code doesn't allow you to register PCD callbacks before USBD_Start() is called. This means that you can't detect if USB is connected on startup. I only need to check once on startup, so this simpler bit test is easier.

The F4xx reference manual states the following (34.16.2 OTG_FS global registers):

Bit 19 BSVLD: B-session valid  
      Indicates the device mode transceiver status.  
      0: B-session is not valid.  
      1: B-session is valid.  
      In OTG mode, you can use this bit to determine if the device is
      connected or disconnected. 
      Note: Only accessible in device mode

Code:

#include "main.h"
...
// Defined in usbd_conf.c:
extern PCD_HandleTypeDef hpcd_USB_OTG_FS;
...
bool usbIsConnected = (bool)(hpcd_USB_OTG_FS.Instance->GOTGCTL & USB_OTG_GOTGCTL_BSESVLD);
\$\endgroup\$
0
\$\begingroup\$

I am using STM32F767ZI and was facing same issue of not detecting USB as Virtual com port so I added Minimum Heap stack 2000 and Maximum heap size from 4000 and boom!! It started working.

\$\endgroup\$
0
\$\begingroup\$

Yes it's strange that there isn't a simple check function or couple of callbacks for this. What I did (using an STM32G0B1) was add a function to usb_device.c to read the dev_state of the USB peripheral. In usb_device.c there is already the handle hUsbDeviceFS and there are blocks where user code can be added, so our code won't be removed next time we edit the .ioc file.

In usb_device.h add a prototype:

/*
 * -- Insert functions declaration here --
 */
/* USER CODE BEGIN FD */
USB_CABLE_e usb_device_check_plug_state(void);
/* USER CODE END FD */

And we'll need an enum for state tracking, I put this in the variables section of the same header file:

/*
 * -- Insert your variables declaration here --
 */
/* USER CODE BEGIN VARIABLES */
 typedef enum
 {
   USB_CABLE_UNPLUGGED = 0U,
   USB_CABLE_PLUGGED,
 } USB_CABLE_e;
/* USER CODE END VARIABLES */

Then add something like this to usb_device.c:

/*
 * -- Insert your variables declaration here --
 */
/* USER CODE BEGIN 0 */
static USB_CABLE_e lastState = USB_CABLE_UNPLUGGED;
/* USER CODE END 0 */

/*
 * -- Insert your external function declaration here --
 */
/* USER CODE BEGIN 1 */
USB_CABLE_e usb_device_check_plug_state(void)
{
   uint8_t stateNow = hUsbDeviceFS.dev_state;

   if((stateNow == USBD_STATE_CONFIGURED) && (lastState == USB_CABLE_UNPLUGGED))
   {
      lastState = USB_CABLE_PLUGGED;
   }
   else if((stateNow == USBD_STATE_SUSPENDED) && (lastState == USB_CABLE_PLUGGED))
   {
      lastState = USB_CABLE_UNPLUGGED;
   }

   return lastState;
}
/* USER CODE END 1 */

Then you can use this function in your main loop and do what you want with the returned state. I have a global to track the USB port state:

 usbPortState = usb_device_check_plug_state();

 if(usbPortState == USB_CABLE_PLUGGED)
 {
    HAL_GPIO_WritePin(DEBUG_LED2_GPIO_Port, DEBUG_LED2_Pin, GPIO_PIN_SET);
 }
 else
 {
    HAL_GPIO_WritePin(DEBUG_LED2_GPIO_Port, DEBUG_LED2_Pin, GPIO_PIN_RESET);
 }

Unfortunately it requires polling but using the current state / last state logic we can detect when the cable has just been unplugged or plugged in.

This works because when there is a connection/disconnection event interrupt, a chain of calls happens ending at USBD_LL_Suspend() and USBD_LL_Resume() which set the dev_state appropriately.

If there are any suggestions for improvement I am all ears! This is just what I've found to work.

\$\endgroup\$
-2
\$\begingroup\$

The answer is very simple. Just like you tried VBUS, try DSTS, and see what happens! Good luck.

\$\endgroup\$
3
  • 4
    \$\begingroup\$ This is scarcely an answer. The question states that DSTS is not clearly documented. Do you have any insight to add? Or are you suggesting just logging the value to see what happens to happen, without bothering to understand what it represents? \$\endgroup\$ Oct 21, 2014 at 18:36
  • \$\begingroup\$ Presumably @Guill is talking about this: "Bit0 SUSPSTS:Suspendstatus In device mode, this bit is set as long as a Suspend condition is detected on the USB. The core enters the Suspended state when there is no activity on the USB data lines for a period of 3 ms. The core comes out of the suspend: – When there is an activity on the USB data lines" \$\endgroup\$
    – Daniel
    Apr 4, 2016 at 3:54
  • \$\begingroup\$ Agreed, though, as a pro struggling with this awful USB core and associated documentation, this is unnecessarily teasing. \$\endgroup\$
    – Daniel
    Apr 4, 2016 at 3:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.