This first collection of examples and exercises is focused on LEDs. This article is mainly related to the article about STM32 GPIO peripheral. To understand this article you should be able to create, build and run a custom project for STM32 development board using ChibiOS. In what follows I will not add downloadable projects because putting examples to work is actually part of the exercise.
The following concepts could be easily applied to every STM32 development board. If you have any question ask support commenting this article or subscribing to our forum.
Create a single thread project which blinks a LED using an STM32 development board.
From task to solution
The default demo already already blinks the LED but here we are not talking about a generic blinker: the goal is to create the simplest LED blinker. The solution is a single thread application and we can do this using only the main() thread.
The suggestion is to start duplicating a default demo like shown in the Chapter 5 of the article “A close look to ChibiOS demos for STM32”. As quick reference here a video which shows how to do that:
So let’s create a new project giving it a proper name, for instance, RT-STM32F401RE-NUCLEO64-Simple_blinker. After editing properly the makefile and debug configuration we can start to edit the main.c:
- remove the inclusion of ch_test.h;
- remove the sdStart (we are not going to use the test suite nor the serial driver);
- remove the chThdCreateStatic (we are not going to create another thread);
- replace the loop of main with the loop of Thread1;
- delete the declaration of waThread1 and Thread1;
- edit the comment within the code (not necessary but is a good habit).
For a STM32 Nucleo-64 the main.c would look like
This code is valid for each STM32 Nucleo-64 because all these board have a Green LED which line is named (LINE_LED_GREEN). This code could be also used on other development board by editing this line properly.
If you are not aware about what a “Line” is you should read again Using STM32’s GPIO with ChibiOS’ PAL Driver. As quick reference, remember that all the lines related to your board are declared in board.h file which is easily reachable through the linked folder inside your project as shown in Fig.1
The code we have written here blinks the LED with a period of 1 second and duty cycle of 50%. This because the palToggleLine() inverts the logic state of the line where the LED is connected and the chThdSleepMilliseconds() adds a pause between each toggle. The next figure is the timing diagram of the main thread as is.
We have learned an important lesson here: how to blink a LED. Usually in every project I made there is always a blinking LED because it is a cheap probe inside my firmware: if LED stops to blink the firmware has crashed!
We can still optimise our code: with our modification we have deleted all the code related to Serial Driver and Test suite. This means we could completely disable all the code related to these parts.
It is possible to disable the Serial Driver acting on act on halconf.h:
To exclude the Test Suite we have to act on the makefile commenting the entry related to it. Considering that in makefile a comment begins with hashkey that a look to the last line of the following code:
How to double the LED’s blinking speed?
From task to solution
Looking back to previous example we can figure out that a easy solution could be to copy the previous code and act on thread sleep: to double the frequency we just have to half the sleep time.
For a generic STM32 Nucleo-64 the implementation would be
This code blinks the LED with a period of half second and duty cycle of 50% and the next figure is the timing diagram of the main thread as is now.
To conclude this exercise lets point out something about timing. In all the timing diagrams we have stated that the turn-on/turn-off of the the Green LED is instantaneous. In reality the change of the status of a digital output PIN involves some logic circuitry and this means that palTogglePad() requires a non-zero time to be put in practice. The execution time is nonetheless mush smaller as it in the order of hundreds of nanoseconds. Thus, we can confidently say that the turn-on/turn-off of the LED is almost instantaneous.
In the blinker thread what beats the time are the thread sleeps. Except in case of extreme CPU load or tight scheduling the timing provided by a sleep is extremely reliable: if the blinker thread is the thread with highest priority and sleeps are in the order of few milliseconds, even in case of extreme CPU load its schedule is reliable.
If you are interested in this argument you should absolutely walk through this further reading about scheduling and multi-threading.
Changing the duty cycle!
How is it possible to change the duty cycle to the 25%?
From task to solution
The duty cycle is a concept which is specific of square waves. In the common sense, the duty cycle is a percentage representing the fraction one period in which the signal is active. In our case since the LED is active on the high side the duty cycle is
This means that if we want to keep the period to one second then the LED should be turned on for 200 ms and turned off for 800 ms. In this way the duty would be
To match the requirement we have to slightly change the code in the main loop. For a generic STM32 Nucleo-64 it would be
This code works as expected only if LED is connected as active high or, in other words, the LED stands lighted during the high phase of the signal.
Looking at figure 4 we can see that the LD2 which is the green LED has its cathode connected to the ground and its anode connected in series with a 510Ω resistor to the PA5 (alias Arduino D13) and that means if PA5 is set as high (which is 3.3V) the current flow through the LED and it turns and this is the definition of active high.
What follows is the timing diagram of the main loop for this example.
The following circuit could better explain the difference between an active high (left) and active low (right) circuit. Note that the round symbol which switches between 1 and 0 represent the GPIO of an STM32 and that this circuit works with 0-5V logic (differently from STM32 which works with 0-3.3V logic).
Connect an external LED to your board as active high and make it blinks
Hint: a GPIO must be properly configured to drive a LED as explained here.
Blink the on board LED and the external LED alternatively (e.g. when one is on the other is off)
Reduce the LED bright using only the PAL Driver
Hint: duty cycle, persistence of view, chThdSleepMicroseconds().