STM32 Part 1. How to blink an LED

Greetings!

This article opens a series of articles on programming STM32 microcontrollers for beginners.

Today I will tell you how to write an LED blink in two ways using tools that are hard to find in 2023


Tools: Iron

  1. BlackPill or BluePill Board

  2. Programmer ST-LinkV2

Downloading the Development Environment

To set up clocking and peripherals, we will use ST CubeMX. We will write the code in Keil. Cube MX can be downloaded Hereand Keil Here. To download CubeMX you will need to register on the ST website To download Cube MX, the link to the Telegram post is relevant at the time of writing and has already lived for six months. Installation does not require special knowledge, I will not describe its process, but I can answer questions in the comments. If you have Mac OS or Linux, then Here you can download CubeIDE – Eclipse similar development environment from ST with integrated CubeMX (if you do not live in Russia or know how to use Tor Browser and VPN), but the article will last until the second coming if I describe the whole process for all development environments, so I will just specifically mention the critical points when it is required.

Creating a project in CubeMX

Open CubeMx and see the following window

Rice.  1. CubeMx start screen
Rice. 1. CubeMx start screen

To create a new project, select “Access to MCU selector”. After a short (relative to the period of rotation of the earth around its axis) loading of the components, a new window will open, where we are asked to select the microcontroller we are interested in.

Important note: In the article, here and below, examples will be given for programming on BluePill and BlackPill boards, as the most common among beginners. You can have any other board – NUCLEO, DISCOVERY, but the process of setting up peripherals, clocking and programming will not fundamentally differ from those given in the article.

“Take the black pill…”

Rice.  2. Microcontroller search window.  The first microcontroller for BlackPill
Rice. 2. Microcontroller search window. The first microcontroller for BlackPill

If you have a BlackPill board, you need to choose the STM32F401CC or STM32F411CC option – the laser markings are usually quite clearly engraved on the stone itself.

Fig.3.  Microcontroller search window.  Second microcontroller for BlackPill
Fig.3. Microcontroller search window. Second microcontroller for BlackPill

For BluePill:

Fig.4 Microcontroller search window.  Microcontroller for BluePill
Fig.4 Microcontroller search window. Microcontroller for BluePill

After selecting the chip you need, click “Start Project” in the upper right corner and get into the “Pinout & Configuration” window (Figure 5). Here, in the System Core -> Sys section, in the “Debug” item, select “Serial Wire” and on the right we see two pins of the microcontroller, highlighted in green. These are the pins of the debugging interface, through which the firmware and debugging of programs will take place.

Fig 5. Turning on debugging
Fig 5. Turning on debugging

Now turn on pin 13 of port C in GPIO Output mode – General purpose input in output mode – click on the leg with the mouse, select “GPIO Output” (Figure 6)

Fig.6 GPIO output
Fig.6 GPIO output

For BluePill, the legs are the same – also select pin 13 of port C

Now go to the “Project manager” tab and get into the “Project” menu (Figure 7)

In the “Toolchain / IDE” item, select MDK – ARM for Keil or STM32CubeIDE for the appropriate option. Further I will use Keil, I choose it.

Rice.  7 Project menu
Rice. 7 Project menu

For convenience in further work, open the tab “Code generator” (Figure 8, left column, second button) and check the box next to “Generate peripheral initialization as a pair of ‘.c/.h’ files per peripheral “. What it gives is that each peripheral module of the microcontroller will be initialized not in main.c, but in its own file, which improves readability in large projects.

Fig 8. Separate files
Fig 8. Separate files

Now press the “Generate code” button (Figure 9)

Rice.  9 Launch
Rice. 9 Launch

After successfully creating the project, open it (Figure 10)

Rice.  10 Project successfully created
Rice. 10 Project successfully created

Introduction to the project

Now consider the default project window (Figure 11) 1 – project tree, 2 – compiler output, 3 – main window, which displays the current open document, 4 – project build button (F7), 5 – microcontroller firmware (F8), 6 – Project settings, 7 – switch to debug mode (Ctrl+F5)

Rice.  11 Project Keil
Rice. 11 Project Keil

Let’s move the console under the project tree so as not to take up useful space for code

Rice.  12 Convenient console
Rice. 12 Convenient console

Let’s expand the “Application/User/Core” folder and open the main.c file (Figure 13) Ctrl + wheel allows you to change the font size.

Fig 13 Opening the desired
Fig 13 Opening the desired
Fig.14 The main body of the program
Fig.14 The main body of the program

Consider what is here: first, the main.h file and the gpio.h file are connected, if the separate generation of peripheral files is checked. Next, the prototype of the peripheral configuration function is declared and then we get into the main () function, in which the HAL (Hardware Abstraction Layer) framework and clocking with I/O ports are initialized (Figure 14)

Kodim

In the main() function, after initialization, which occurs once, there is an infinite while (1) loop in which our program will be executed. Pay attention to the USER CODE sections – if you write code inside them, then when you regenerate the project from CubeMX, your code will not be overwritten.

Let’s add to the USER CODE BEGIN 3 section turning on the LED on the GPIOC 13 leg, waiting for 500 ms and turning it off (Figure 15)

Fig. 15 Blinking on the registers
Fig. 15 Blinking on the registers

Line 98: A number equal to

1 << 13;
// или в бинарном выражении
0010 0000 0000 0000

Since the bits are numbered from right to left (and from zero), it turns out that we put a “1” in the 13th bit of the ODR register

Line 99: Called the HAL_Delay() function with argument 500, where “500” is milliseconds, i.e. 500 ms do nothing

Line 100: Recall the number (1<<13) and invert it

 1 << 13;
// или в бинарном выражении
0010 0000 0000 0000
// когда делаем ~(1<<13), мы побитово инвертируем число в скобках
1101 1111 1111 1111

now we multiply the current value of the register bit by bit by the resulting number, i.e. all the bits that were, retain their value, except for the 13th, it was multiplied by zero and reset

0010 0000 0000 0000
// когда делаем ~(1<<13), мы побитово инвертируем число в скобках
1101 1111 1111 1111
//

the LED at this moment lights up, because on the BlackPill board the LED is pulled up by a resistor to 3.3V

Line 101: again waiting 500ms

Now you can press F7 to build the project, it should look like this

Fig.16 Successfully collected
Fig.16 Successfully collected

Now configure the debugger, click “Settings”

Rice.  17 Settings
Rice. 17 Settings

And change the compiler to 6.16 – it is much faster and go to the “Debug” tab (Figure 18)

Rice.  18 Compiler and Debugging
Rice. 18 Compiler and Debugging

In the “Debug” tab, select ST-Link and confirm the choice

Rice.  19 Choosing a debugger
Rice. 19 Choosing a debugger

Now you can connect the programmer to the board, insert it into the USB of the computer and flash it by pressing F8. If it doesn’t work right away, double-check that the contacts of the programmer and the board are not mixed up. After the firmware, you need to press the Reset button on the board so that the LED starts blinking

I really hope that you succeeded, because next we will try to do the same, but by changing a couple of lines of code and delay values ​​from 500 to 100 ms (Figure 20):

Rice.  20 More HALs
Rice. 20 More HALs

Now on line 98 there is a function that turns on pin 13 of the GPIOC port.

On line 99, “500” was changed to “100”.

On line 100, the corresponding pin turns off.

On line 99, “500” was changed to “100”.

We will build the project again using F7 and sew the project by pressing F8, restart the MK by pressing RESET

If everything is done correctly, the LED will blink much more often.

Homework

In order to invert the value of a bit, there is an XOR operand

^=

Try using it for the case with registers to reduce the number of lines of code in while(1) to two.

Thank you all for reading the publication, I look forward to your questions and correction of inaccuracies in the comments or by email mailto:mgostev.it@gmail.com

Similar Posts

Leave a Reply

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