How to program STM32 bluepill in C using System Workbench or Eclipse and HAL library offered by ST. Example LED Blink code.
Unfortunately programming this board is not as easy as programming an Arduino board. There is a project named STM32duino aimed at simplifying things which makes use of Arduino IDE and similar programming language. But, STM32 is a complex CPU with more functions than Arduino language offers. You can program it using Eclipse IDE and a set of libraries offered by ST. These libraries are LL (low level), StdPeriph (standard peripheral library) and HAL (hardware abstraction library). HAL uses high level API which simplify developing an application. This post will show you how to configure the development environment and write the first program with HAL that will blink an LED.
An easier alternative to Eclipse and HAL is PlatformIO and Mbed. Check it out.
The development environment is Eclipse plus a set of plugins. There are two ways of getting it up and running. If you already use Eclipse and have it installed you can just install STM32 plugins. Otherwise AC6 Tools offers a bundled installer that will take care of everything. Instructions below are based on OpenSTM32 documentation.
The Eclipse Way
I'm assuming you got Eclipse installed and running. Go to Help menu and choose Install new software. Click Add button and paste the following URL:http://ac6-tools.com/Eclipse-updates/org.openstm32.system-workbench.update-site-v2Give it any name you want. Click OK and wait. Select all items available in the repository (both external tools and openstm32 tools). Click Next.
Add STM32 tools repository in Eclipse |
If you're on Linux, you must install the 32 bits version of standard C library and NCurses.
sudo apt install libc6:i386 lib32ncurses5
System Workbench
If you don't have Eclipse installed or you want an easier way, OpenSTM32 community and AC6 Tools offer a bundled installer. Click the AC6 Tools link and choose the appropriate version for your operating system. For Linux there is a run file and for Windows there is an exe installer. Choose the right one for the OS architecture.Windows installation is pretty straightforward. Just follow installer instructions.
If you're on 64 bits Linux, you can use the following commands in terminal:
wget -O install_sw4stm32_linux.run http://www.ac6-tools.com/downloads/SW4STM32/install_sw4stm32_linux_64bits-latest.run chmod a+x install_sw4stm32_linux.run ./install_sw4stm32_linux.runYou should run installation in console mode because GUI requires gksudo which is not included and not recommended to use in modern distros. Press 'y' when asked about this. Press 1 repeatedly to continue installation and accept license. You can leave the path default. Follow further instructions (press O, 1, Y, 1). Unpacking will start. Somewhere along the way it will ask you about an automated installation script. Enter 'Y' to create it, because Eclipse will download some tools based on it. When installation finishes, you'll get a Desktop shortcut. Launch it from there.
On first run, it will ask for a workspace directory. Projects will be created here.
The first program
Launch Eclipse or System Workbench for STM32. Go to File - New - Project. Select C Project from C/C++ category and click Next. If everything is installed, you should be able to select Empty Project of Executable type with Ac6 STM32 MCU GCC toolchain. Give it a name and click Next. Select build configurations (debug, release) and go to next screen. Wait a little before CPU selection screen appears.Select target MCU in Eclipse |
On Firmware Configuration screen choose HAL. First time you'll see a warning because target firmware does not exist. Click the Download button, accept license and wait.
Download target firmware for STM32 boards |
C Project with HAL for STM32 |
Code listing
Here is the Arduino blink sketch equivalent. Note that there is an easier way of generating initial configuration using STM32CubeMX, a graphical tool.#include "stm32f1xx.h" void SystemClock_Config(void) { RCC_ClkInitTypeDef clkinitstruct = {0}; RCC_OscInitTypeDef oscinitstruct = {0}; oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; oscinitstruct.HSEState = RCC_HSE_ON; oscinitstruct.HSIState = RCC_HSI_OFF; oscinitstruct.PLL.PLLState = RCC_PLL_ON; oscinitstruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; oscinitstruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&oscinitstruct)!= HAL_OK) { while(1); } clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); clkinitstruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1; clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1; clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2; if (HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2)!= HAL_OK) { while(1); } } int main(void) { HAL_Init(); SystemClock_Config(); __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitTypeDef gpio; gpio.Mode = GPIO_MODE_OUTPUT_PP; gpio.Speed = GPIO_SPEED_FREQ_MEDIUM; gpio.Pin = GPIO_PIN_13; gpio.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOC, &gpio); while (1) { HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); HAL_Delay(500); } }The specific MCU header is included at the beginning of code. Let's analyze main function.
HAL_Init(); SystemClock_Config();These functions perform HAL initialization and configure system clock. The clock configuration function is taken from HAL/LL user manual UM1850 (pg. 54-55) and modified to use HSE (high speed external) clock and set PLL to 9 times HSE (9 * 8 = 72 MHz). System clock configuration enters into endless loops if configuration fails. If you don't call this, by default, MCU will use the internal RC oscillator, which is close to 8 MHz.
__HAL_RCC_GPIOC_CLK_ENABLE();This is a macro that enables system clock for use with GPIO driver on port C. I'm enabling port C because my blue pill has the green LED connected to PC13.
GPIO_InitTypeDef gpio;We declare a GPIO configuration structure that holds input/output type, pins, switching frequency. These are configured individually next.
gpio.Mode = GPIO_MODE_OUTPUT_PP; gpio.Speed = GPIO_SPEED_FREQ_MEDIUM; gpio.Pin = GPIO_PIN_13; gpio.Pull = GPIO_PULLUP;Push-pull output with medium switching frequency on pin 13 only. Multiple pins can be configured at once like this:
gpio.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_13;The configuration is now created.
HAL_GPIO_Init(GPIOC, &gpio);This function initializes HAL for GPIOC with the configuration provided in gpio struct. All this is equivalent to Arduino pinMode.
To blink the LED, in an endless loop, we toggle the pin and add a delay.
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); HAL_Delay(500);Just like above, you can combine pins here too. The delay function will not work properly if system clock is not configured.
Paste the code listing in main.c and go to Project - Build Project. The binary file is in the project directory, subdir Release or Debug depending on selected build configuration and it is named as the project. It has bin extension.
Binary upload
You got a binary file by building the project. Go ahead and connect blue pill to ST-Link. If the board is powered from another source, do not connect the power pin from ST-Link anymore. Plug ST-Link into the USB port.Linux
To be able to upload binary to the blue pill using ST-Link, udev rules must be installed. You can get the rules as well as ST-Link command line tools from Arduino_STM32. You need to download the entire repository, then go to tools/linux64 or tools/linux32. Run ./install.sh for udev rules. Keep stlink folder (you could copy all files from it to /usr/bin because those are ST-Link programming tools that we'll use next). You don't need anything else from this repo.First of all, run st-info --probe. You should see your device detected, the flash and RAM size. Next, write the binary to the flash using:
st-flash --reset write ~/workspace/project/Release/project.bin 0x8000000Adjust paths for the binary file you have. Because you installed udev rules, no root permissions are required.
Windows
Download ST-Link Utility from ST and upload the binary with it. The instructions are the same as uploading the bootloader from here - Windows section.Once it is done, you should see the LED blinking.
All good until flashing
ReplyDeleteLED went dark, stays dark...
Which LED? From the blue pill or from the programmer?
DeleteWorking great now, thanks
DeleteI do have no idea what changed but after the about 10th attempt it justed started working reliably o.O
Thanks for the very quick reply,
Deletethe blue pill's LED.
Not your tutorial's fault, noticed now that the release binary had been created once and then never changed until it just started working, despite being updated and building fine.
Well, it fixed itself, I'm happy.
Thanks for the great, easy to follow tutorial. Still, you might want to move the step on installing the stlink tools higher up, got confused for a bit when you explained the difference between the c8tx and cbtx
Thanks for the quick tutorial. I am not using blue pill but a similar prototype board, STM32F103Cx DEMO BOARD v2.2.I had an issue compiling the code which is at the point of stating that
ReplyDeleteif (HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2)!= HAL_OK)
it was suggesting i should use FLASH_LATENCY_0 which i did and it compile successfully. Can you help with why it is so?
Flash memory is slower than CPU. Therefore, depending on CPU clock, it may be necessary to add some wait states for flash. See the Reference Manual, bottom of page 58. Also note that latency should be chosen by HCLK, not SYSCLK as the manual claims, as noted here. Probably you have configured a slower HCLK, that requires no latency at all (FLASH_LATENCY_0).
Deleteyes! it also works in Arduino IDE too!
ReplyDeleteHello,
ReplyDeleteI am using an STM32F103c8T6 (Blue Pill)
LED at PC13 does not blink after uploading the code and shifting to operating mode (Boot0:0, Boot1:0).
Tried uploading a couple of times now.
Thanks!
Hey there,
ReplyDeletei had the same problem as others that my led would not blink.
I did some debugging and it seemed like i was stuck in an endless loop the debugger called something like WWDG interrupt. This seemed unlikeley enough, having the WWDG not enabled, so i did some googeling and found a post that stated, that the code optimizer was optimizing all endless loops into one, and I could actualy not be stuck in a WWDG interrupt but possibly encountered a hard fault.
Some more googeling revieled that one has to add this function
void SysTick_Handler(void){
HAL_IncTick();
}
when using the HAL_Delay function.
With that the programm run as expected.
This is also documented on page 1184 of "Description of STM32F1 HAL and low-layer drivers":
"3. Add HAL_IncTick() function under SysTick_Handler() ISR function to enable polling process when usingHAL_Delay() function".
So my best estimate what happened is, that SysTick_Handler() is not actually implemented, so I ran into a hard fault, but i didn't really investigate.
I hope that can help some of you also.
Best Michael