Wednesday, June 21, 2017

How To: STM32F103C8T6 As An USB Device (Virtual Serial Port / CDC)


Cheap STM32F103C8T6 development board 

Blue STM32F103C8T6 development boards, also known as "BluePill", are cheap way to get started with 32bit ARM microcontrollers. The STM32 development board can sometimes be bought for less than $2 and ST-LinkV2 compatible programmer and debugger doesn't cost much more than that either.
The STM32F103C8T6 has nice amount of flash and RAM, runs at 72MHz and best of all: It has built-in USB. It is possible to program these STM32 boards to act as an USB devices, without "FTDI chip". In this post and in the embedded video I will teach step by step how to use the STM32F103C8T6 as an USB device, in particular a virtual serial port.

Before we start

It shall be assumed that one following this tutorial has successfully installed STM32CubeMX and SystemWorkbench For STM32 (SW4STM32), knows how to connect and configure the debugger/programmer and how to flash the project to the STM32 microcontroller from the SW4STM32. STM32CubeMX is ST's tool for creating a project "skeleton" for STM32 microcontrollers, with all the user-selected peripherals configured properly. SW4STM32 is an IDE for STM32 and includes tools needed to write, compile, flash and debug the code. This tutorial will not go into steps needed to install and set up these tools.

Configuring USB enabled project, using STM32CubeMX

When creating a new project with STM32CubeMX, new window pops up and one needs to select correct microcontroller. In this case, correct one is "STM32F103C8Tx". After selecting it, the configuration can begin. First tab that shows up is called "Pinout", where different peripherals can be enabled and pinouts can be configured.
Pin's function can be selected by clicking the pin on the right part of the screen and selecting one function from the menu that opens. Peripherals can be enabled and configured by using the dropdown menus and checkboxes on the left side of the screen.
Following things should be configured. Apart from the USB and USB_DEVICE, these are recommended for any project that uses the $2 STM32F103C8T6 development board.
  • Function of the pin PC13: GPIO-Output
  • RCC->High Speed Clock (HSE): Crystal/Ceramic Resonator
  • Sys->Debug: Serial Wire
  • USB: Device (FS)
  • USB_Device->Class For FS IP: Communication Device Class (Virtual Serial Port)
At this point required peripherals are enabled, but things won't work because clock configuration has some errors. They can be automatically resolved by going to the Clock Configuration -tab and allowing the automatic clock issues solver to solve the issues. You don't need to manually change any prescalers, PLL multipliers or other things in this tab.
Next thing is optional and only affects the default state of the LED on the BluePill. Default state of the pin can be changed in configuration -tab by clicking "GPIO", then selecting the wanted GPIO pin and changing the GPIO output level -option. The built-in LED on the BluePill is active low, thus it is recommended to select default state "High", so the LED won't light up automatically.
Before generating the project, correct IDE and project location should be set in the Project -> Settings... -menu. In this case Toolchain / IDE should be SW4STM32 and project location and name can be choosen freely. Last thing to do in the STM32CubeMX is generating the code, by selecting Project-> Generate Code. Once the code has been generated, project can be opened in the IDE by selecting "Open Project".

Using the USB / virtual serial port project in the SW4STM32

Project should build correctly and it can be flashed to the STM32F103C8T6 using ST-LinkV2, or other compatible programmer. Like said in the "Before we start", you should know how to configure the IDE and make it work with the programmer you have. It's good idea to build and flash the project at this point, just to make sure any possible errors caused by the SW4STM32 or the STM32CubeMX (there weren't any while writing this post) gets fixed right away.
Receiving data through the virtual serial port happens in the file Src/usbd_cdc_if.c. The specific function where the received data can be handled is "CDC_Receive_FS". It's a function that gets automatically called when the data is received. It's not a function that you call to read data... It has two arguments: Buf and Len. Buf is buffer of data that has been received and Len is number of bytes received. Handle the received data in the beginning of this function.
Sending data through the virtual serial port happens in the same file, with the function "CDC_Transmit_FS". It also has two arguments: Buf and Len. Data gets sent through the virtual serial port by calling this function and giving data to be send as first argument, and length of that data as a second argument. To send text, create character array, for example char data[] = "Hello world!";, give it as first argument and "strlen(data)" as second array.

The verdict

Getting the virtual serial port to work for the first time took a long time, but once all the tricks were learned, the process became much faster and easier. Bottom line is, don't give up if something seems hard... Try, try again and try again. When you figure out how to make it, share the knowledge and help others. Write a blog post, make a video or answer questions in the forums. It takes just a short while to do, but can be a time saver for others.