STM32Cube code initialization for “blue pill”

 Posted by:   Posted on:   Updated on:  2018-09-18T19:41:29Z

An easy way to generate initial configuration of a development project for the STM32 bluepill using STM32CubeMX software.

Lately I’ve playing around with the STM32F103 development board known as “blue pill”. Developing software for it is not as easy as for Arduino boards. The MCU contains a 32-bit ARM CPU. I have previously tried to write software for this board using HAL library, but I didn’t get the most out of it because I found the programming model rather complicated.

One of the methods to develop software for this MCU is to use ST HAL library (which uses a higher level API than other libraries for this MCU). I chose the Eclipse IDE with a set of plugins for STM32 family. There is an easier way to get the SDK and IDE with the toolchain called System Workbench for STM32 (recommended by ST too). This is the download directory where you can find all releases for the major operating systems. But before creating a blank project in SW4STM32, you should know that there is a tool which can create this project for you. Not quite blank, as you will configure the MCU with a graphical tool in a step-by-step process.

STM32Cube code initialization for “blue pill”
In my opinion, the most difficult initialization procedure for STM32 is the clock configuration. With multiple clock sources, clock prescalers and multipliers, you need to read carefully the documentation to get the right clock frequency. STM32Cube is the graphical initialization tool and it is not limited to clock configuration only. MCU ports and communication interfaces can be configured at this point too. This post will focus mostly on clock configuration though.

The first step is to download and install STM32Cube. You can get it from ST by providing your email address or making an account. Download link is sent to your email address. The software is based on Java, so you have to install it to run STM32Cube. A graphical installer is provided for all operating systems, so there’s nothing to say about the installation procedure. When it’s done launch it.

Install HAL for STM32 in CubeMX
Install HAL for STM32 in CubeMX
Go to Help menu and choose Manage embedded software packages. You must download Package for the MCU used by your board. Since blue pill uses STM32F103, expand STM32F1 and check the latest version. Click Install Now and wait for download.

Select and download HAL for STM32F1
Select and download HAL for STM32F1
STM32Cube is ready for use. Create a new project (File menu, toolbar button or Ctrl+N shortcut). It may download something at this point. Just wait. In the New Project window you must choose the correct MCU. Blue pills use STM32F103C8 or STM32F103CB (the difference is flash size: 64kB for C8 version and 128kB for CB version). Connect the blue pill to ST-Link and use the programming utility (see the Windows and Linux sections here) to check flash size. If it’s 128kB, the MCU is CB version. Select MCU and click Start Project.

Create new project based on STM32F103
Create new project based on STM32F103
You’ll be prompted with the Pinout tab. Here you can either click on a pin and set its function or you can use the left treeview and configure peripherals. Since blue pill uses and 8 MHz crystal, that’s the first thing to configure. Go to Peripherals—RCC and set High Speed Clock (HSE) to Crystal/Ceramic Resonator. It should be noted that the bluepill also contains a 32.768 kHz oscillator, therefore you can set the same for LSE, if you intend to use the RTC peripheral.

Enable HSE clock in STM32Cube
Enable HSE clock in STM32Cube
You can move to Clock Configuration where you will see a flowchart. Look at the screenshot below. I’ll try to match each GUI setting to the line of code you will see (and later edit) in clock configuration function. At the beginning of the main() code entry, clock needs to be configured. This is usually done in a function that is called here.

STM32F103 Clock configuration in CubeMX
STM32F103 Clock configuration in CubeMX
Two HAL calls configure system clock: HAL_RCC_OscConfig (selects physical clock sources) and HAL_RCC_ClockConfig (selects system clock source and configures dividers). A specific, previously declared and configured structure type is passed to these functions.

Looking and the visual configuration, HSE clock (which is 8 MHz—from the crystal) can and will be used as source for both PLL and System Clock muxes. It must be enabled (you just did this in Pinout tab). The result is:
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
To be able to make changes to RTC Clock Mux, enable the RTC in Pinout tab. If you do this, the above OscillatorType will include LSE too.

In the PLL Source Mux, HSE will obviously be used instead of HSI. You can use the full frequency or divide it by a factor of two (HSE Prediv value) [1]. You configure this in the code like this:
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
The PLL has the ability to multiply its input frequency by maximum 9 for this microcontroller [2]. This will result in a system clock of 72 MHz, the maximum available. Choosing PLLCLK as source for System Clock Mux [3], will generate code for enabling PLL. It will be configured with HSE source and chosen multiplier.
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
STM32Cube also configures HSI (internal RC oscillator) as turned on because it is used as Flash memory programming clock. You can turn it off however. When programming flash, the ST-Link will reset MCU to default state where HSI is enabled.
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
At this point we have system clock frequency configuration (SYSCLK [4])! There’s one more clock that derives straight from PLL instead of SYSCLK. That’s the USB clock, but we’ll talk about it later. To apply current clock configuration, you must call HAL_RCC_OscConfig with the address of RCC_OscInitStruct variable.

The next clocks are configured in a different structure. The structure variable can configure one or more clocks. By default, STM32Cube configures all (high-speed clock, system clock, peripheral clock 1 and 2):
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
PLLCLK is the system clock source [3]. Here it is set:
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
Following the diagram, SYSCLK can be applied to AHB (advanced high-perfomance bus) prescaled [5]. AHB prescaler is configured in code:
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
The output from prescaler drives CPU clock (HCLK) and peripheral clocks (via prescalers).

Peripheral clock configuration
Peripheral clock configuration
Only APB1 and APB2 clocks (advanced peripheral buses) are configured in RCC_ClkInitStruct. They can be prescaled with factors of 1/1, 1/2, 1/4, 1/8 and 1/16. APB1 needs to be divided by at least 2 if HCLK is 72 MHz. That’s because PCLK1 can’t be higher than 36 MHz. The remaining code is:
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
Similar to the previous structure variable, the address of this one is passed to HAL_RCC_ClockConfig function.

Specific peripheral clocks are configured with an extension driver. For example USB clock that derives straight from PLL is configured like this:
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB;
PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
Similar to previous ClockConfig function, this one can configure multiple clocks in a single call if you specify clocks in PeriphClockSelection and set their parameters.

STM32Cube can configure CSS (Clock Security System) too. This is a function that switches the main clock source from HSE to HSI if by any chance HSE stops working. This is done with a single call:
HAL_RCC_EnableCSS();
As you can see there are a lot of options to set the clock of STM32 MCU. Luckily, you don’t have to read the manual a lot of times to understand all of them since STM32Cube helps you configure STM32 clocks in a visual way. More than that, if you make a mistake it informs you and can fix the problem for you. Use Project – Generate Code to create the SW4STM32 project. It will ask for a project folder (or you can set these in Project - Settings). If you use a different IDE, set it in Toolchain / IDE combobox.

You can also head over to Configuration tab of STM32Cube and adjust options for enabled subsystems. Generate code and see the programming model in main.c file. The first routine called in main() is HAL initialization function followed by local clock configuration generated based on your settings. The SystemClock_Config function is reusable for any project with similar clock configuration (copy-paste it in your projects and you don’t have to use STM32Cube).

At the end I'm offering you a sample Cube project for bluepill. It configures HSE, LSE, LED at PC13 and USB interface. All clocks are at their maximum values. Download the project, modify it and generate your code.

References

8 comments :

  1. Thank you Cornelius, for the interesting information on STM32Cube and using it to program the Blue Pill. It's a pleasant surprise to me that the graphical tool STM32Cube is free! I need to start playing with it soon!!

    I have two Blue Pill boards with different types of reset switch but both have the same STM32F103C8T6 chip and the same pinout. On checking the boards using ST Link/V2, I find that the flash size is 64K in one while it is 128K in the other. Checked again; the chip is STM32F103C8T6 in both the boards. I understand that F103C8T6 chip comes with 64K or 128K flash.

    Looking forward to more projects with the Blue Pill.

    ReplyDelete
    Replies
    1. Hi Bala,

      I don't know the specific case for the STM32F103C8T6 , but the following is a very common situation in the Silicon industry.

      The cost to design and manufacture chips is made up of two parts:

      First is the design effort and the creating of the master masks/reticles used to manufacture the chips. This is very expensive. For small chips it might be a few million dollars, and for big chips it can be over 100 million dollars. The actual manufacturing is expensive too, ranging from a $500 to $500 per wafer

      http://www.icinsights.com/news/bulletins/LeadingEdge-IC-Foundry-Market-Forecast-To-Increase-72-In-2014/

      https://caly-technologies.com/die-yield-calculator/

      But the wafer cost is divided by the chip size. If you can fit 15,000 chips (2 mm x 2mm) on a 300 mm wafer with 90 nm process, the cost per chip is about $2000/15000 = $0.13 each. So that is my ball park guess for STM32F103C8T6 . Then add packaging cost, test cost, support cost, and maybe some profit.

      Why the long story above? Because you have to sell a hell of a lot of chips that sell for under a dollar to get enough profit to cover the design and mask costs. So what manufacturers do is they design a Master chip that they can sell at a premium, and sell the same chip but with a different label, less declared capabilities, and less testing, at a lower price. Thus the total number of wafers and total sold chips increases for the one initial upfront cost of a few million dollars.

      Since many people (including me) have seen STM32F103C8T6 that seem to have 128 KB of Flash, then the likely scenario is that the master chip is STM32F103CBT6 , and when manufacturing parts that will be labeled STM32F103C8T6 , they don't bother testing the upper half of memory.

      A manufacturer that cared about their reputation, would only use the Flash that ST says the device has and which is tested. If they needed 128K, they would buy the STM32F103CBT6 and pay the extra cost for it being tested to meet the appropriate spec.

      Delete
  2. In STM32CubeMX
    Pinout&Configuration -> RTC -> RTC Mode and Configuration -> RTC OUT set to "No RTC Output"
    Then PC13-TAMPER-RTC output to Pin.
    All work!

    ReplyDelete
    Replies
    1. Hehey, nice tip, couldn't figure out why my BluePill LED was constantly lit. Everything was set up perfectly, yet simple hal_toggle/hal_delay didn't work.

      Checked CubeMX again and I had 'Activate clock source' under RTC checked and while RTC_OUT was set to Disable I noticed Output in bottom window still was still at "Alarm pulse signal on the TAMPER pin"

      Changed RTC_OUT in top window to "No RTC output" and Output in bottom window switched to "No output on the TAMPER pin" - and blinky suddenly started blinking.

      Delete
  3. I have a bunch of BluePill-s from AliExpress, all with wrong R10 resistor. See
    http://amitesh-singh.github.io/stm32/2017/05/27/Overcoming-wrong-pullup-in-blue-pill.html

    //
    // AliExpress BluePills with wrong R10.
    // See http://amitesh-singh.github.io/stm32/2017/05/27/Overcoming-wrong-pullup-in-blue-pill.html
    // Call once from main just after SystemClock_Config(); call.
    //
    void USB_BluePill_Reenumerate()
    {
    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.Pin = GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, 0);
    HAL_Delay(1);
    }

    ReplyDelete
    Replies
    1. BTW, boards work perfectly except (without the piece of code above) right after starting debug session, CDC device shows up nicely in Windows 10 device manager, but trying to open COM port resulted in failure. Had to reset device to be able to use it as serial port.

      With code above, I can sit at CDC breakpoint, open COM port and debug away.

      Delete
  4. I've created a collection of example projects using STM32CubeIDE for the Blue Pill board here...

    https://github.com/miniwinwm/BluePillDemo

    ReplyDelete

Please read the comments policy before publishing your comment.