Electronic – STM32F4 USB VCP, PC receive all 0x00

microcontrollerstm32stm32f4usbusb device

I'm a beginner in STM32F4 and I'm learning how to use the USB Virtual COM Port ("VCP") to transmit data from the microcontroller to a PC.

The virtual port is detected as "STMicroelectronics Virtual COM Port", and I use serial monitoring software to see the data received by the PC.

The PC is receiving all 0x00 no matter what I send in my code.

I called VCP_DataTx() function to send data.

Any ideas about what's wrong?

p.s. in my main() function, I only called USBD_Init() to initialize USB Device, is that enough? Did I miss anything that should be configured? I see someone say USART when talk about VCP, is there any relationship between them?

Thanks.

This is my main.cpp. I did nothing more but initialize USB device and send data.

#include "usbd_cdc_core.h"
#include "usbd_usr.h"
#include "usb_conf.h"
#include "usbd_desc.h"

#include "usbd_cdc_vcp.h"

USB_OTG_CORE_HANDLE    USB_OTG_dev;
uint8_t Buf[6] = "A";

int main(void)
{
  __IO uint32_t i = 0;  

  USBD_Init(&USB_OTG_dev,         
            USB_OTG_FS_CORE_ID,
            &USR_desc, 
            &USBD_CDC_cb, 
            &USR_cb);

    VCP_DataTx(Buf, 6);

  /* Main loop */
  while (1)
  {
      VCP_DataTx(Buf, 6);
      while (i < 0x77ffff)
      {
          i++;
      }
  }
} 

In order to call VCP_DataTx() in main, I deleted static keyword in usbd_cdc_vcp.h and usbd_cdc.vcp.c

p.p.s. I didn't realize usbd_cdc_vcp.c is not in STM USB library. It comes from the VCP example in STM32_USB-Host-Device_Lib_V2.1.0, and the function VCP_DataTx() looks like:

uint16_t VCP_DataTx (uint8_t* Buf, uint32_t Len)
{
  if (linecoding.datatype == 7)
  {
    APP_Rx_Buffer[APP_Rx_ptr_in] = USART_ReceiveData(EVAL_COM1) & 0x7F;
  }
  else if (linecoding.datatype == 8)
  {
    APP_Rx_Buffer[APP_Rx_ptr_in] = USART_ReceiveData(EVAL_COM1);
  }

  APP_Rx_ptr_in++;

  /* To avoid buffer overflow */
  if(APP_Rx_ptr_in == APP_RX_DATA_SIZE)
  {
    APP_Rx_ptr_in = 0;
  }  

  return USBD_OK;
}

Best Answer

I am using the ST USB Library for a different ST ARM microcontroller (STM32F105), but the structure looks the same. I, too, have based my code off of the VCP example.

First, there is some additional initialization required for the USB to work. Following is a list of things to verify. You may new to adjust some of these examples to work with your microcontroller:

  1. You need to have enabled the OTG_FS_IRQHandler interrupt:

    NVIC_InitStructure.NVIC_IRQChannel = OTG_FS_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    

    The following is in my interrupt vector file:

    #include "usb_core.h"
    #include "usbd_core.h"
    #include "usbd_cdc_core.h"
    
    extern USB_OTG_CORE_HANDLE USB_OTG_dev;
    extern uint32_t USBD_OTG_ISR_Handler (USB_OTG_CORE_HANDLE *pdev);
    
    void OTG_FS_IRQHandler(void)
    {
        USBD_OTG_ISR_Handler (&USB_OTG_dev);
    } // end OTG_FS_IRQHandler()
    
  2. You must select and enable a 48 MHz clock source for the OTG/USB model before calling USBD_Init. In my case, this was derived by dividing the 144 MHz PLLVCO by 3. The clock tree can be tricky; make sure you understand that section of the Reference Manual.

    // Configure USB Clock Source
    RCC_OTGFSCLKConfig(RCC_OTGFSCLKSource_PLLVCO_Div3);
    
    // Enable USB Clock
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_OTG_FS, ENABLE);
    
  3. In the USB Library is the usb_conf.h file. This is a tangle of conditional defines. Make sure that these are actually getting defined (if they are appropriate to your application):

    #define USE_USB_OTG_FS
    #define USB_OTG_FS_CORE
    #define USE_EMBEDDED_PHY
    #define VBUS_SENSING_ENABLED
    #define USE_DEVICE_MODE
    

Next, as @Tut pointed out in the comments, your VCP_DataTX function needs modification. It is currently using the USART peripheral as the source of the USB data. You want to change it so that it uses Buf and Len. Even though the function call includes the Buf and Len arguments, these aren't actually used in the function.

The VCP_DataTx stuffs data, one byte at a time, into the APP_Rx_Buffer array. Every time it writes a byte to the array it also increments the APP_Rx_ptr_in value. This buffer is regularly checked and processed by the OTG/USB library code.

My function looks like this:

uint16_t VCP_DataTx(uint8_t *Buf, uint32_t Len)
{
    uint32_t i;

    // Put the data into the buffer. It will be processed by the USB stack.
    for (i=0; i<Len; i++)
    {
        // Add a byte to the buffer
        APP_Rx_Buffer[APP_Rx_ptr_in] = Buf[i];

        // Update the circular buffer index
        APP_Rx_ptr_in++;

        // Loop the index if necessary
        if (APP_Rx_ptr_in == APP_RX_DATA_SIZE)
        {
            APP_Rx_ptr_in = 0;
        }
    }
    return USB_SUCCESS;
}

Finally, there are a few other issues in your code. I expect you are aware of some of these, but I'll list them just in case:

  1. The statement uint8_t Buf[6] = "A"; only initializes the first value to 'A'. The remaining five values are '\0'. See here for more information.

  2. When the counter variable i exceeds your delay limit, it should be cleared back to zero. This could be a primary problem. Currently, when the delay finishes up the first time, it never delays again. The outer while loop starts calling VCP_DataTx(Buf, 6); continuously. This will overflow the USB buffer and lead to all sorts of problems.

  3. Once you fix the delay problem you will still get two calls to VCP_DataTx(Buf, 6); back-to-back with no delays. Once before the delay loop, and immediately again before the first delay. This shouldn't cause a problem but I wanted to point it out.


FYI, if you are only trying to send data back and forth to a PC via USB then you don't need to enable any USART functionality in the microcontroller. The VCP example uses the USART as a data source (and destination), but you aren't trying to do that. Your data source is your Buf[] array.