Stm32f3-discovery USB Virtual Com Port

Intro

Well, after having a complete build and debug environment set up, the first thing that I usually want to do is to attach the device to a PC. In the past I always had a USART port, which I connect through a USB-to-serial adaptor, or a FTDI or equivalent chip on the board. This means that the coding on the device is really straightforward. For the first time I have a microcontroller with integrated USB capabilities and it happen that for me it doesn’t fulfils my requirements out of the box (no demo software for a VCOM on that particular board). This is frustrating, because serial communication is what I consider a basic thing, but in the end like any problem it force you and allows you to dig into the problem and learn a lot of useful and interesting thing about a new world.

References

Online intro to USB

Traffic monitoring

USB sniffing on Linux.

Some blogs suggest to do the following:

mount -t debugfs / /sys/kernel/debug

As stated in the wireshark doc this is only needed for old kernels:

For versions of the kernel prior to 2.6.21, the only USB traffic capture mechanism available is a text-based mechanism that limits the total amount of data captured for each raw USB block to about 30 bytes. There is no way to change this without patching the kernel.

Don’t do it with newer kernel, because you will end up with an unstable system and you will have to reboot.

With newer kernels you only need to mount the usb monitor driver:

modprobe usbmon

Example of a mouse left click and release with a mouse:

pezzino@samarcanda:~$ lsusb 
Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 003: ID 1bcf:288a Sunplus Innovation Technology Inc. 
Bus 002 Device 003: ID 046d:c06c Logitech, Inc. Optical Mouse
pezzino@samarcanda:~$ sudo cat /sys/kernel/debug/usb/usbmon/2u
ffff88010dd140c0 1341942814 C Ii:2:003:1 0:8 4 = 01000000
ffff88010dd140c0 1341942885 S Ii:2:003:1 -115:8 4 <
ffff88010dd140c0 1341966814 C Ii:2:003:1 0:8 4 = 00000000
ffff88010dd140c0 1341966870 S Ii:2:003:1 -115:8 4 <

Accessing network data without root privileges.

sudo dpkg-reconfigure wireshark-common 
sudo adduser <yourUser> wireshark

Accessing USB data without root privileges.

“Runtime” hack:

chmod o=rw /dev/usbmon*

Definitive hack:

sudo setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip CAP_DAC_OVERRIDE+eip' /usr/bin/dumpcap

USB

Excerpt stolen from USB Complete: The Developer’s Guide.


Each endpoint has a number, a direction, and a maximum number of data bytes the endpoint can send or receive in a transaction.
Each USB transfer consists of one or more transactions that can carry data to or from an endpoint. A USB 2.0 transaction begins when the host sends a token packet on the bus. The token packet contains the target endpoint number and direction. An IN token packet requests a data packet from the endpoint. An OUT token packet precedes a data packet from the host. In addition to data, each data packet contains error-checking bits and a Packet ID (PID) with a data-sequencing value. Many transactions also have a handshake packet where the receiver of the data reports success or failure of the transaction.


USB supports four transfer types: control, bulk, interrupt, and isochronous. In a control transfer, the host sends a defined request to the device. On device attachment, the host uses control transfers to request a series of data structures called descriptors from the device. The descriptors provide information about the device’s capabilities and help the host decide what driver to assign to the device. A class specification or vendor can also define requests.

Every device must have endpoint zero configured as a control endpoint.

USB set up on stm32f3

ST USB library

STM32_USB-FS-Device_Driver
├── inc
│   ├── usb_core.h
│   ├── usb_def.h
│   ├── usb_init.h
│   ├── usb_int.h
│   ├── usb_lib.h
│   ├── usb_mem.h
│   ├── usb_regs.h
│   ├── usb_sil.h
│   └── usb_type.h
└── src
    ├── usb_core.c
    ├── usb_init.c
    ├── usb_int.c
    ├── usb_mem.c
    ├── usb_regs.c
    └── usb_sil.c

The project

I’ve started with the demo that ships with the board and tried to adapt it from an HID device to a virtual com port. From STM32_USB-FS-Device_Lib_V4.0.0 VirtualComport_Loopback example project I’ve stolen some files.

  • usb_desc.h
  • usb_desc.c
  • usb_prop.h
  • usb_prop.c
  • usb_endp.c

These files are used to define the device as virtual com port to the host and to define the callback functions.

The main looks like:

  while (1)
  {
    if (bDeviceState == CONFIGURED)
    {
      CDC_Receive_DATA();
      /*Check to see if we have data yet */
      if (Receive_length  != 0)
      {
        if (packet_sent == 1)
          CDC_Send_DATA ((unsigned char*)Receive_Buffer,Receive_length);
        Receive_length = 0;
      }
    }
  }

I’ve also added this two functions from the same demo project to the hw_config:

uint32_t CDC_Send_DATA (uint8_t *ptrBuffer, uint8_t Send_length)
{
  /*if max buffer is Not reached*/
  if(Send_length < VIRTUAL_COM_PORT_DATA_SIZE)
  {
    /*Sent flag*/
    packet_sent = 0;
    /* send  packet to PMA*/
    UserToPMABufferCopy((unsigned char*)ptrBuffer, ENDP1_TXADDR, Send_length);
    SetEPTxCount(ENDP1, Send_length);
    SetEPTxValid(ENDP1);
  }
  else
  {
    return 0;
  }
  return 1;
}

uint32_t CDC_Receive_DATA(void)
{
  /*Receive flag*/
  packet_receive = 0;
  SetEPRxValid(ENDP3);
  return 1 ;
}

First test

Well, using any terminal program (I use cutecom), you should be able to connect to the device selecting /dev/ttyACM0 with 115200 8 1. If the device works as planned you should see your text echoed back.

Well, fine. The only problem is that using wireshark to monitor the traffic I can see that the host receives a malformed packet!

wireshark-malformedpacket

It seems that we send garbage when the host ask for the device qualifier.

Well, after hours of checks it seems that the device structures with the USB parameters are correct. I will still investigate in order to understand better all the USB initialization phase and communication, in order to understand if there’s a problem or not.

Stupid modems…

Well, after some debugging I’ve discovered that the device keeps receiving “AT+xxx”. This are AT commands used in modem devices and are sent from my Linux host. A simple research shows that the offending service is the ModemManager, and can be disabled for specifics device with the following commands:

sudo vi /etc/udev/rules.d/10-local.rules

Add the following line:

ATTRS{idVendor}=="0483", ATTRS{idProduct}=="5710", ENV{ID_MM_DEVICE_IGNORE}="1"

Restart udev:

sudo udevadm control --reload-rules

I’ve checked again with wireshark and eventually everything works as expected! There is always something to learn…

Source code

Eclipse USB VCOM echo project.

13 thoughts on “Stm32f3-discovery USB Virtual Com Port

  1. Ciao,

    sono arrivato qui al tuo stesso punto dopo aver giocherellato coi dati raccolti dai sensori, utilizzando i led come IMU.

    Io , come scritto nel firmware, uso hyperterminal e senza settare i baud e nient’altro torna al terminale ciò che viene inputato da tastiera.

    Vorrei mandare dei dati attraverso la porta usb in maniera mirata, puoi aiutarmi?
    Sarei felice se usassi la mail!

    grazie

    Marco

  2. Hi,

    I am curious whether you are able to debug this program using OpenOCD?

    I use the very same bard but I do not know how to debug a program which uses USB user interface. I always need to plug USB user cable to PC (to enumerate USB) after the program is written to the device which corrupts the debug session.

    Thanks for answer and nice articles.
    Jan

    1. Hi,

      I’ve always have the two USB cables plugged.

      I reprogram the device with this openocd arguments:

      -f /usr/local/share/openocd/scripts/board/stm32f3discovery.cfg -c init -c"reset init" -c "flash write_image erase ${project_loc}/Debug/${project_name}.elf" -c reset -c shutdown
      

      Then I unplug and plug again the user USB cable as you said.

      After that I can start the debug session.

      I never changed this procedure, so I don’t know if any change can lead to problems…

      pezzino

      1. Hi,

        I use two USB cables as well and the same command to (re)program the device.

        But before starting the debug session I need to invoke following command which resets the device. I invoke it using “External Tools” in Eclipse.
        openocd -f /usr/local/share/openocd/scripts/board/stm32f3discovery.cfg -c init -c"reset init" -c"poll"
        Do you use it similarly or in another way?

        Then after starting the debug session and stepping to following condition which is not fulfilled, I reckon, as result of reset.
        if (bDeviceState == CONFIGURED)

        Thank you for answer.
        Jan

        1. ok, this is the working sequence:

          • two USB cables plugged
          • start openocd with your command (as you said this will reset the device)
          • the system is now in reset state at address 0x0, press resume (be sure that the system is running, and you’re not stopped at any breakpoint)
          • now the system is running and looping around if (bDeviceState == CONFIGURED) which is not fulfilled
          • unplug and plug the USB communication cable (while the system is running)
          • you’re done!
  3. I am working on STM32F3 Discovery on Linux platform using arm-none-eabi toolchain.I have successfully completed compilation of code on STM32F3 and loaded into flash also.But I’m not getting why /tty/USB0 device not created.I have done this project on STM32f4 Discovery.But for Stm32f3 its not working.

  4. Hey ppezzio,

    I hope this site is still up to date. I try to run your project but the only answer is “Info: Nothing to build for stm32f3-usb-vcom-echo”.

    I believe the problem is that I don´t use the right toolchain. I´m currently useing “ARM Linux GCC (Sourcery G++ Lite)” and my current builder is “CDT Internal Builder”.
    In my old porjects I´m using “Cross ARM GCC” and the builder is “Gnu Make Builder”. By the way I´m using Windows 7 and not Linux.

    Do u have any site where I get some help for the config or can u help me?

    Best regards from germany and good job

    1. Hi David, I hadn’t much time lately to work on this blog..

      Usually this is a message from make command, it means that the source files vs. object files are up to date. You could try with “make clean” command.

      There is nothing wrong with code sourcery toolchain.

      Check the gnuarm eclipse plugin site (http://gnuarmeclipse.github.io/) it as a lot of useful info.

      Grüsse aus der Schweiz!

      1. Hey ppezzino

        Ich hab es jetzt geschafft, dass dein Programm soweit läuft und keine Fehler ausspuckt. Allerdings weiß ich nicht wie ich das Programm testen soll. Ich benutzte Putty und bis jetzt hatte ich immer die folgenden Einstellungen: Serial Line: COM4 ; Speed: 9600;

        Hast du erneut einen Denkanstoß oder eine Seite, die mir weiter helfen kann?

        Vielen Dank

        David

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.