Search code examples
cusbstm32stm32cubeide

STM32Cube USB VCP becomes inoperative after microcontroller reset


I'm trying to figure out how to make the USB VCP functionality on my STM32F103-based Blue Pill board operate after a microcontroller reset, e.g. after uploading new code.

As I have it right now, the VCP operates normally at first, but then PuTTY ceases to report any new lines from the VCP after a hardware reset. The FTDI adapter connected to UART1 continues to work, though. Here is a minified version of main.c:

#include "main.h"
#include "usb_device.h"
#include <string.h>
#include <usbd_cdc_if.h> // Necessary to avoid "warning: implicit declaration of function" for CDC_Transmit_FS()

int main(void) {
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_USB_DEVICE_Init();

  char msg[50];
  HAL_StatusTypeDef ret_status = HAL_OK;
  sprintf(msg, "Reset!\n");
  ret_status = HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
  uint8_t state = 0;

  while (1) {
      HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, state ? GPIO_PIN_RESET : GPIO_PIN_SET);
      sprintf(msg, "Hello World! LED State: %d\n", state);
      ret_status = HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
      ret_status = CDC_Transmit_FS((uint8_t*)msg, strlen(msg));

      HAL_Delay(500);
      state = state == 1 ? 0 : 1;
  }
}

In reading this SO Q/A, I learned that the issue is likely due to the host not recognizing that the client device was reset (and thus needs to be reinitialized) because the D+ line was never pulled down. However, I don't understand where/how to apply a fix for this. I tried inserting a pair of HAL_GPIO_WritePin and HAL_Delay instructions into the MX_USB_DEVICE_Init() function like so:

void MX_USB_DEVICE_Init(void)
{
  /* USER CODE BEGIN USB_DEVICE_Init_PreTreatment */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET);
  HAL_Delay(100);
  /* USER CODE END USB_DEVICE_Init_PreTreatment */

  /* Init Device Library, add supported class and start the library. */
  if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK)
  {
    Error_Handler();
  }
  if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_CDC) != USBD_OK)
  {
    Error_Handler();
  }
  if (USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS) != USBD_OK)
  {
    Error_Handler();
  }
  if (USBD_Start(&hUsbDeviceFS) != USBD_OK)
  {
    Error_Handler();
  }

  /* USER CODE BEGIN USB_DEVICE_Init_PostTreatment */

  /* USER CODE END USB_DEVICE_Init_PostTreatment */
}

but there was no effect. I also tried changing the instruction to pull the pin to GPIO_PIN_RESET in case I'd misunderstood which constant corresponded to a logic low, but it also didn't have any effect. As far as I can reason, this should have the effect of applying the fix described in 0___________'s answer, but I must have misunderstood the problem. Does anyone know how to address this?

Solution (Oct. 22, 2021)

In accordance with @Flexz's answer below, I added code to my modification of the MX_USB_DEVICE_Init() function to be the following:

void MX_USB_DEVICE_Init(void)
{
  /* USER CODE BEGIN USB_DEVICE_Init_PreTreatment */
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);

  /*Configure GPIO pin : PA12 */
  GPIO_InitStruct.Pin = GPIO_PIN_12;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);


  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);
  HAL_Delay(100); // Actually unnecessary (from my testing) and can be removed without consequence.
  /* USER CODE END USB_DEVICE_Init_PreTreatment */

  /* Init Device Library, add supported class and start the library. */
  if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK)
  {
    Error_Handler();
  }
  if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_CDC) != USBD_OK)
  {
    Error_Handler();
  }
  if (USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS) != USBD_OK)
  {
    Error_Handler();
  }
  if (USBD_Start(&hUsbDeviceFS) != USBD_OK)
  {
    Error_Handler();
  }

  /* USER CODE BEGIN USB_DEVICE_Init_PostTreatment */

  /* USER CODE END USB_DEVICE_Init_PostTreatment */
}

I adapted this from code generated by STM32CubeMX to configure the Blue Pill's green SMD LED (PC13). At least for me, this makes the VCP function again after an MCU reset, although PuTTY still complains and I have to reinit it manually. :)


Solution

  • Your MX_GPIO_Init probably initializes PA11/PA12 pins in alternate function mode for use with USB controller. Thus pulling PA12 low with WritePin will have no effect.

    To force active zero on D+ first configure PA12 as regular GPIO output before (or at the beginning of) MX_GPIO_Init, and use HAL_GPIO_WritePin as written in your code. Than restore PA12 function as USB's alternate function.