USB-Serial on STM32F4

About

Based on this, I have written a small program for the STM32F4 Discovery that uses the USB-CDC class to show up as an virtual serial port. I’m using the USB stack provided by ST, however, there is also another project that uses libopencm3 that can be found here. I have routed the write system call to use this port, so the stdio printf function will print directly to this serial port.

How to use it

If you don’t have the necessary toolchain to build and upload programs to the STM32F4, you can have a look at this post. Otherwise, the following steps should be sufficient:

  1. Download the source code complete with all libraries and makefiles here.
  2. Unpack the program and run make from its root directory. The binary file that can be uploaded should appear in the build directory.
  3. Upload the program to the STM32F4 discovery (again, this post explains how) and plug in a micro-USB cable to the port next to the audio jack.
  4. The serial port should show up as /dev/ttyACM0 on most GNU/Linux distributions, such as Ubuntu.
  5. Start a serial port terminal, such as gtkterm (sudo apt-get install gtkterm on Debian/Ubuntu), and open ttyACM0. The baudrate does not matter as it currently is ignored by the program (see usbd_cdc_vcp.c).

The current program runs some floating point operations and prints the elapsed time on the USB-CDC serial port. The float parameters in the makefile can be changed to see how the FPU affects the speed (and there is a lot of difference). Remember to run clean after changing the float parameters, as the object files are not compatible between hardfloat and softfloat builds.

27 thoughts on “USB-Serial on STM32F4

  1. Hello Benjamin

    Have you tried using this with Windows 7? I built your project (with the IAR toolchain) and it worked as described with Linux (both native and with a VM on a Win 7 host) and with Win XP (VM on a Win 7 host) but I can’t seem to get it working with Win 7. Here’s what happens for me on Win 7:

    Two devices (with the same name) show up in Device Manger for some reason. I can start one device with the usbser.sys driver (using a custom INF file, same as what worked with Win XP) but the other fails to start (code 10). I can open a connection to the Virtual COM port (using putty) provided by the first ‘working’ device but I don’t receive any data and when I type a character to send, it (putty) crashes. Any ideas?

    Thanks for posting this.

    • Hello Ben,

      I would like to do exactly what you’ve done. Build the project of Benjamin on Win7 in IAR toolchain. And then use it in Linux (Fedora), like Benjamin does. I will use Python (PySerial) on Linux to communicate with the STM. But I can’t manage to build the project on Win7 in IAR, could you give me the steps you followed quickly so I can build it on windows?

  2. How can i use this for accepting data from the computer? I would like to use read() and write() for sending and receiving raw data (or whatever suitable alternative). write() to file descriptor 1 seems to work as i would expect, but read from file descriptor 0 only gets one byte per function call. Also, fcntl(0, F_SETFL, O_NONBLOCK); makes everything stop working (though compiling is successful). I’m doing my dev in ubuntu 12.04 virtual machine with summon-arm-toolchain, but testing the USB_CDC directly on the Mac 10.6.8 host, if that’s relevant at all.

    • I only made very minimalistic implementations in syscalls.c, and the one I made for read does almost not work at all. I suggest that you have a look in syscalls.c and update the read implementation. You could also use VCP_get_char or VCP_get_string directly to read from the usb-serial-port.

    • Hello Andrew ,
      I am working on STM32F4 with USB serial communication .In my case printf print data on serial port (gtkterm) i.e it works fine but scanf doesn’t work i.e it will not take input from user .I have gone through syscall.c and gone through _read and _write function but can’t got exact solution.so plz guide me regarding that
      int _read(int file, char *ptr, int len) {
      if (file != 0) {
      return 0;
      }

      // Use USB CDC Port for stdin
      while(!VCP_get_char((uint8_t*)ptr)){};

      // Echo typed characters
      VCP_put_char((uint8_t)*ptr);

      return 1;
      }

      int _write(int file, char *ptr, int len) {
      VCP_send_buffer((uint8_t*)ptr, len);
      return len;
      }

  3. Thanks, I was able to make a nice blocking/nonblocking read function using VCP_get_char, which was straightforward once you told me where to look.

    Do you have any idea how to make a new VCP_send_buffer that will block if there is not enough room in the output buffer (APP_Rx_Buffer), returning as soon as the data has all been written to the buffer? It seems that the current code has a buffer of 512, and if i do something like for i=1:BIGNUM send(i), then the buffer just gets cyclically overwritten.

    • Andrew, could you please share that read function?
      I’m working on that for a few hours, but I can’t get it working.

  4. Hi Benjamin,

    Could I ask what is the maximum transter rate in this method and is /dev/ttyACM0 a Virtual Com port?

    Sorry if my questions are very basic, I am a student and I’m trying to learn how to make fast usb communication beween a C program on the board and a linux box.

    • Hi,

      I haven’t made any benchmarks, but the speed is surely limited by USB1.1. Yes, it will show up as a virtual com port. Note that the baudrate you choose doesn’t matter or makes any difference since there is no actual UART.

      No problem if the question is a bit basic, everyone has to start somewhere 🙂

  5. Hello Benjamin,
    I am working on STM32F4 with USB serial communication .In my case printf print data on serial port (gtkterm) i.e it works fine but scanf doesn’t work i.e it will not take input from user .I have gon through syscall.c and gone through _read and _write function but can got get exact solution.so plz guide me regarding that
    int _read(int file, char *ptr, int len) {
    if (file != 0) {
    return 0;
    }

    // Use USB CDC Port for stdin
    while(!VCP_get_char((uint8_t*)ptr)){};

    // Echo typed characters
    VCP_put_char((uint8_t)*ptr);

    return 1;
    }

    int _write(int file, char *ptr, int len) {
    VCP_send_buffer((uint8_t*)ptr, len);
    return len;
    }

  6. Thanks. Since 2 days I’m fighting with all STM32F4 VCP examples on the net, trying to get one to work to try adding a VCom to an existing project (based on FreddieChopin’s linker .ld and startup assembler).
    Tried to compile this example. Always get an error, because “make” calls the compiler like this: ~/sat/bin/arm-none-eabi-gcc ………
    And it’s not there.
    Edited the Makefile. Cleared the word “sat” from it. Set BINPATH = “/usr/bin”. Did a reboot. But like in black magic the “~/sat/bin/” persists every time “make” is issued…
    Did close examination of STM/CMSIS library sub folders and wow! – there are multiple Makefile-s which create compiled libraries with .a extension. I have to edit ’em all.
    Why was that necessary ?

    • Splitting this into separate makefiles is not necessary and probably not the best way to do it, but it is one way to do it. Putting everything in the same makefile is also quite simple and works. If I would rewrite the same example again I would probably do that, but I almost always use ChibiOS when working with the stm32f4 now.

    • O.K. anyway it works – I’m very thankful, though I didn’t show it.
      I didn’t try to use an OS, because I’m affraid that it may:
      -mess with the interrupts (NVIC priorities, etc) so my handlers won’t execute on time;
      -make the code larger, resp. difficult to debug or fit in the flash;
      -increase the RAM usage;
      -inflict the portability, e.t. STM32F4Dis. has lots of resources, but it’s an expensive MCU (~10..13 EUR) so I think of it as a development platform, then port to 1.5EUR M0+ cores (when possible) with 10x less flash, less RAM and probably not OS-supported.

  7. Hello Benjamin!
    thanks for your project!
    and can i user this project and send data from my android device to STM (in host mode), i tried, but it didn’t work, it only work with computer.
    thanks!

  8. Hi Ben, when I upload the program to the dicovery board and plug in the micro usb cable, the serial terminal does not appear in my /dev directory. When I open gtkterm, it says ‘ no open port ‘ what could be causing this?

  9. I would like to use some of your code from this post and your Makefile organization in an open-source project of mine (Digitabulum). So I have two questions:
    A) May I?
    and if so…
    B) How would you like to be credited?

    • Sure, no problem. You don’t have to give me any credit, I’m just glad that my project was helpful. Nice project by the way 🙂

  10. Hi Benjamin,

    Thanks for posting this code, it got me a serial link running no time.

    One thing is not working. The values of a are not printing correctly. I the letter “f” instead. Same if I change to use a %g format string I get a “g” in the output. Integers print correctly.

    It may be related to this.

    https://answers.launchpad.net/gcc-arm-embedded/+question/245658

    It seems you are including the Atollic TrueSTUDIO startup files too.

    I’m compiling from command using your Makefiles. GCC under Linux.
    arm-none-eabi-gcc (crosstool-NG 1.18.0) 4.7.2

  11. Another oddity I have found is that this works reliably on Gentoo but on of Fedora 20 about every 6th 3 line packet gets lost. There’s a pause and then it prints the subsequeny 3line block.

    I have not had time to look at that yet.

  12. Hi all,

    i am trying to use this test application on a STM32F401-disco, the difference should only be on the MCU which is an STM32F401VCT instead of the STM32F401VG of the STM32F4 discovery.

    I am able to compile, flash and run the application. However i don’t see any string written to the serial console. I am sure that the code is calling the appropriate VCP read and write functions.

    I have noticed that dmesg gives the following error when attaching the usb connector:

    [26822.814642] usb 1-1.2: new full-speed USB device number 74 using ehci-pci
    [26822.886872] usb 1-1.2: device descriptor read/64, error -32
    [26823.062944] usb 1-1.2: device descriptor read/64, error -32

    Hevae you ever seen it? or daes anyone have a clue on how to debug it??

    Any help is really appreciated.

    Thanks,

    Christian

  13. Hello Benjamin.
    Thanks a lot for
    We are working on STM32F4 with USB serial communication but have problem with /dev/ttyACM0 port’s because it doesn’t known. So we run dmesg and don’t see what happens when we plug in the usb cable although /dev/ttyACM0 doesn’t known in ubuntu 12.04 terminal.
    Could you help us please.

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.