
A close look to ChibiOS demos for STM32
Important notice
Please note that the information contained in this article may no longer be accurate or useful due to changes in the ChibiOS codebase and ChibiStudio. However, we have published a new series of articles to provide you with a comprehensive guide on how to get started with ChibiOS and ChibiStudio.
To gain a better understanding of ChibiOS demos, we invite you to read Mastering multithreading with ChibiOS: a beginner’s guide. Additionally, The simplest project ever with ChibiOS can help you take your first steps when dealing with ChibiOS projects, and Parametric threads with ChibiOS demonstrates how to create a slightly more complex project than the first one, giving you more confidence with the tool. Please keep yourself updated by reading the latest articles.
Thank you for your interest in PLAY Embedded.
Introduction
In this article, we are going to take a deep look to ChibiOS default demos explaining how they work. We will also see how to create a new project and how to modify it in in order to create our own applications.
ChibiOS projects’ anatomy

A ChibiOS’ default demo is usually composed of some different folders and files. For example, in figure, we can see the resources of the default demo for STM32 Nucleo F401RE. In general, all the ChibiOS’ projects are characterized by similar anatomy.
All demos have some folders, some configuration headers, a source file named main.c and a makefile. Additional notes about the demo are usually reported in a “read me” file which is actually a plain text file (readme.txt).
Linked folders
Some of the folders contained in our project are actually links. You can notice they are because there is a small arrow on their icons in the bottom right corner.
These folders are usually shared between different demos and projects. This means that editing a file within these folders we will affect more than a project. In general:
It is not a good idea to edit the linked folders and their content because changes will affect all the demos.
Let’s make an example to clarify this concept. The os folder is linked to the folder C:\ChibiStudio\chibios182\os. In our case, it links to ChibiOS191 because, during the previous article (Developing on STM32: introducing ChibiStudio), we have imported demos from this release. Anyway, it contains the ChibiOS’s sources: if we edit this code we can potentially break the code and, thus, every project linked to this folder.
If you broke ChibiOS accidentally editing its code you can always restore it downloading the latest relase from Sourceforge and manually replacing the broken folder with the new one taking care to preserve the folder name and its hierarchy.
The debug folder
The debug folder instead is a real folder. It is specifically related to our project because it contains a .launch file. We already have used this file before: do you remember the steps required by flash and run? Just after the start-up of OpenOCD, we have to select the Launch Configuration from the Debug Menu. This .launch file represents the Launch Configuration.
The Launch Configuration contains some information about the flash and run procedure required from the debugger. It is actually an XML file contained in the debug folder.
Configuration headers
Each project comes with some configuration headers. All the configurations are actually pre-processor directives (a series of constant definitions and macros). To re-configure a project, we usually have to edit these files, save them and recompile the entire project.
Working with embedded systems,it is extremely usual to deal with textual configuration headers. From here and out, we will often talk about pre-processor switches. A switch is just a boolean constant definition which includes/excludes a piece of code from the compilation process. For example, what follows are two switches: the first one is enabled and includes all the code related to Serial Driver, the second one is disabled and excludes all the code related to PWM Driver.
/** * @brief Enables the SERIAL subsystem. */ #if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__) #define HAL_USE_SERIAL TRUE #endif /** * @brief Enables the PWM subsystem. */ #if !defined(HAL_USE_PWM) || defined(__DOXYGEN__) #define HAL_USE_PWM FALSE #endif
These switches are related to HAL drivers and are often called Driver Switches. In general pre-processor switches are very important because they allow removing entire sub-module from our binary when we do not need them.
If our aim is to create a marketable product based on embedded systems we need to pay attention to resource usage. If our embedded software uses fewer resources, we can use the smallest MCU, and this means that our product will require smaller memories, lower clock frequencies and most likely will require less energy: this means it will be cheaper, smaller, thinner and thus more competitive.
The less resources are used the more cheap the hardware will be. Cheaper hardware means lower final price and higher profit. The software is a key point in this philosophy!
ChibiOS offers a lot of mechanisms to keep your code nimble and performance-oriented but there aren’t magic bullets: the user application shall be designed properly to ensure optimization.
Kernel configuration header: chconf.h
The chconf.h contains mainly setting related to the kernel: there are some settings related to system timer and kernel parameters, a lot of switches to enable kernel features and some interesting hooks. This file is configured to suit a generic case of use. Except for debugging features, we will rarely edit it, especially in these basic tutorials.
Note that even if the kernel configuration header for ChibiOS/RT and ChibiOS/NIL is named at the same way, configurations are extremely different.
ChibiOS/RT comes with a series of debugging features which are very useful during the development phase. By default, these features are disabled because when enabled kernel executes a big number of additional check: this introduces a reduction of performances in exchange of debugging aid. Usually, these features should be enabled when needed and disabled before to deploy our binary. We will take a deeper look to this part later.
Here reported a piece of chconf.h containing a series of switch related to debugging options.
/*===========================================================================*/ /** * @name Debug options * @{ */ /*===========================================================================*/ /** * @brief Debug option, kernel statistics. * * @note The default is @p FALSE. */ #define CH_DBG_STATISTICS FALSE /** * @brief Debug option, system state check. * @details If enabled the correct call protocol for system APIs is checked * at runtime. * * @note The default is @p FALSE. */ #define CH_DBG_SYSTEM_STATE_CHECK FALSE /** * @brief Debug option, parameters checks. * @details If enabled then the checks on the API functions input * parameters are activated. * * @note The default is @p FALSE. */ #define CH_DBG_ENABLE_CHECKS FALSE /** * @brief Debug option, consistency checks. * @details If enabled then all the assertions in the kernel code are * activated. This includes consistency checks inside the kernel, * runtime anomalies and port-defined checks. * * @note The default is @p FALSE. */ #define CH_DBG_ENABLE_ASSERTS FALSE /** * @brief Debug option, trace buffer. * @details If enabled then the trace buffer is activated. * * @note The default is @p CH_DBG_TRACE_MASK_DISABLED. */ #define CH_DBG_TRACE_MASK CH_DBG_TRACE_MASK_DISABLED /** * @brief Trace buffer entries. * @note The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is * different from @p CH_DBG_TRACE_MASK_DISABLED. */ #define CH_DBG_TRACE_BUFFER_SIZE 128 /** * @brief Debug option, stack checks. * @details If enabled then a runtime stack check is performed. * * @note The default is @p FALSE. * @note The stack check is performed in a architecture/port dependent way. * It may not be implemented or some ports. * @note The default failure mode is to halt the system with the global * @p panic_msg variable set to @p NULL. */ #define CH_DBG_ENABLE_STACK_CHECK FALSE /** * @brief Debug option, stacks initialization. * @details If enabled then the threads working area is filled with a byte * value when a thread is created. This can be useful for the * runtime measurement of the used stack. * * @note The default is @p FALSE. */ #define CH_DBG_FILL_THREADS FALSE /** * @brief Debug option, threads profiling. * @details If enabled then a field is added to the @p thread_t structure that * counts the system ticks occurred while executing the thread. * * @note The default is @p FALSE. * @note This debug option is not currently compatible with the * tickless mode. */ #define CH_DBG_THREADS_PROFILING FALSE /** @} */
HAL configuration header: halconf.h
This halconf.h contains all the configuration related to ChibiOS/HAL drivers. Acting on this file, it is possible to enable/disable entire drivers: this usually reduces the size of the final firmware and thus the size of flash memory occupancy.
Remember to disable unused driver in halconf.h before to deploy your firmware.
We will often act on halconf.h to enable/disable driver. Note that disabling a driver which is actually used the build procedure will fail with a lot of error: this because when a driver is disabled, it is completely excluded from compilation and all its APIs becomes unavailable.
What follows is a piece of halconf.h. In this example, PAL Driver and CAN Driver are enabled, ADC Driver and DAC Driver are disabled.
/** * @brief Enables the PAL subsystem. */ #if !defined(HAL_USE_PAL) || defined(__DOXYGEN__) #define HAL_USE_PAL TRUE #endif /** * @brief Enables the ADC subsystem. */ #if !defined(HAL_USE_ADC) || defined(__DOXYGEN__) #define HAL_USE_ADC FALSE #endif /** * @brief Enables the CAN subsystem. */ #if !defined(HAL_USE_CAN) || defined(__DOXYGEN__) #define HAL_USE_CAN TRUE #endif /** * @brief Enables the DAC subsystem. */ #if !defined(HAL_USE_DAC) || defined(__DOXYGEN__) #define HAL_USE_DAC FALSE #endif
MCU configuration header: mcuconf.h
The MCU configuration header contains all the configuration strictly related to our MCU. This file is extremely hardware dependent: it is very usual that STM32 belonging to the same sub-family have a different mcuconf.h.
This file contains configurations for the whole clock tree, plus a series of configuration related to DMA, IRQ priorities, peripheral assignments and much more.
We will often act on peripheral assignments: ChibiOS/HAL drivers rely on hardware peripherals and often different drivers rely on the same kind of hardware, thus
To use a ChibiOS/HAL driver, usually we have to enable it in halconf.h and assign it an hardware peripheral in mcuconf.h
Don’t worry if this concept is not completely clear right now: we will clarify those statement in a later moment when we will properly introduce ChibiOS/HAL drivers.
The Makefile
We have already introduced the makefile: it is a script file which contains a series of directives used by GCC make to build our code. It has a syntax which is completely documented on the GNU make manual. For our purposes we will edit it only partially so do not worry too much about that: we will face this problem at the proper moment.
The main.c
The most important part of our project is definitely the main.c source file. In this source file, there is the application entry point. We will spend most of our development phase on this file.
Just a note, the main.c file contains a namesake function int main(). In what follows I will refer to the whole file as main.c and to the contained function like main().
A more detailed look to default demo
In the previous article, we have already got a glimpse of ChibiOS demos for STM32. We have used both STM32 Nucleo F401RE and STM32F3 Discovery kit, and related projects which are RT-STM32F401RE-NUCLEO64 and RT-STM32F303-DISCOVERY. After the flash and run we have resumed the code execution and the one or more LEDs started to blink on our development board.
Actually, the default demo is able to do more than blink a LED. Indeed, that demos executes also a test suite which prints data on a serial port. We can try this demo relaunching the demo. In brief, steps are:
- Execute again the flash and run of the default demo for your development board;
- In the Debug perspective press the Resume button to run the code execution: one or more LEDs will start to blink;
- In the device manager, under LPT and COM Ports check the COM port assigned to the STMicroelectronics STLink Virtual COM port;
- In the Debug perspective, in the bottom part, open the terminal tab and connect to that COM port with default configurations (Baudrate 38400, Data bits 8, Stop bit 1, no parity, no hardware flow control);
- Press the User Button and the test suite will be executed printing test results on the terminal.
Let’s take a look at this short video in which we are using STM32 Nucleo F401RE:
If the Terminal is not visible in the Debug View click on the Window drop down menu and choose Show view, then Other; in the newly opened windows click on Terminal to add it to current perspective.
A brief explanation
What we have just seen in the previous video is the ChibiOS test suite which is executed and prints its result over a virtual COM port: this is possible because the ST-Link V2-1 bridges one of the UARTs of the STM32 to the PC emulating a COM port over the USB: this is known as STLink Virtual COM port.
In the first article of this series, we have seen that STM32 is equipped with a lot of peripherals. A very common peripheral is the Universal synchronous/asynchronous receiver/transmitter also known as USART, a peripheral capable to implement a standard serial protocol communication known as RS232. This standard was largely used in old Computers and is commonly known as COM port.
Thanks to the bridge, it is possible to exchange characters between a terminal and our STM32. In a later article, we will see how to use this mechanism to print escaped strings on the terminal like we usually do with the printf function in C.
Something about the multi-threading
This demo performs two different tasks apparently at the same moment:
- It blinks the Green LED with a precise cadence;
- It constantly checks the User button status and if pressed starts the ChibiOS’ Test Suite printing on a UART.
The management of more tasks apparently executed at the same time is easily achievable by the user with an RTOS. One of the most important features of ChibiOS/RT is the multi-threading. Oversimplifying, a thread could be imagined like a sequence of instructions and multi-threading means kernel can manage more than a sequence of instructions independently, executing them in a parallel fashion even if actually the kernel is running on a single core processor. This is possible because the RTOS plans an operation sequence: this task is commonly called scheduling.
In ChibiOS/RT a thread is actually a function. When it is started, the thread is associated with a priority level and to a working area. The working area is a piece of memory dedicated to our thread and a priority level is a relative number which we can use to influence the scheduling. Priority follows a simple rule:
Among all the threads ready for execution, the one with the highest priority is the one being executed, no exceptions to this rule.
I have also written a detailed article about ChibiOS/RT multi-threading in case you are interested in this topic. If we are designing a complex application performance-oriented it is important to understand the working principle of ChibiOS/RT’ scheduling. Anyway, at this level, our MCU is so underutilized that we can almost ignore these details and we can choose priority levels applying this simple rule:
Every thread must have a sleep or a suspending function inside its loop. In this way the CPU ownership is switched among threads and the scheduling can proceed.
ChibiOS/RT offers different sleep functions but the one suitable in many scenarios certainly is
chThdSleepMilliseconds(<xxx>);
This function suspends a thread for <xxx> milliseconds, where the argument is an unsigned integer.
Into the code
In what follow we will refer to the demo RT-STM32F401RE-NUCLEO64 but what the following concepts are applicable to every demos for STM32 which are available under the folder \demos\STM32.
Browsing main.c we can easily see that it is composed by two main pieces:
The blinker thread
To conclude this brief introduction to multi-threading this piece of code is a piece of the main.c of the demo RT-STM32F401RE-NUCLEO64.
/* * Green LED blinker thread, times are in milliseconds. */ static THD_WORKING_AREA(waThread1, 128); static THD_FUNCTION(Thread1, arg) { (void)arg; chRegSetThreadName("blinker"); while (true) { palClearPad(GPIOA, GPIOA_LED_GREEN); chThdSleepMilliseconds(500); palSetPad(GPIOA, GPIOA_LED_GREEN); chThdSleepMilliseconds(500); } }
In this case, we are declaring a working area named waThread1 having size 128 bytes. We are also declaring a function named Thread1 which in its loop:
- turns off the green LED,
- sleeps for 500 ms,
- turns on the green LED,
- sleeps for 500 ms,
- turns off the green LED,
- sleeps for 500 ms,
- turns on the green LED,
- … (continues indefinitely)
Note that the function
chRegSetThreadName("blinker");
is executed only once before the loop and has debugging purposes: with this function, the thread assigns itself an identification.
The macro:
static THD_WORKING_AREA(<name>, <size>);
allocates a working area where <name> is its identifier and <size> its size expressed in Bytes.
The macro:
static THD_FUNCTION(<name>, <args>) { <code> }
declares a function where <name> is its identifier <args> is a argument passed to the function and <code> its implementation.
The main
Right below the thread function declaration, there is the main() which is the application entry point: we could consider this as the starting point of our code execution.
/* * Application entry point. */ int main(void) { /* * System initializations. * - HAL initialization, this also initializes the configured device drivers * and performs the board-specific initializations. * - Kernel initialization, the main() function becomes a thread and the * RTOS is active. */ halInit(); chSysInit(); ... }
When the application starts the two functions (halInit() and chSysInit()) perform the system initialization: while halInit() is an API of ChibiOS/HAL and initializes the HAL subsystem, chSysInit() is an API of ChibiOS/RT and initializes the kernel. Note that every application based on ChibiOS/RT and ChibiOS/HAL begins in the same way.
After after chSysInit() the main() becomes a thread itself and another thread named idle is created: idle is a dummy thread which runs when all the other threads are not ready to run.
It is important that halInit() and chSysInit() are executed at the beginning of main(). Using any ChibiOS API before them will have an unwanted behavior probably causing a system crash.
Considering the RT-STM32F401RE-NUCLEO64 demo, right after the initialization we have:
/* * Activates the serial driver 2 using the driver default configuration. */ sdStart(&SD2, NULL); /* * Creates the blinker thread. */ chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO + 1, Thread1, NULL); /* * Normal main() thread activity, in this demo it does nothing except * sleeping in a loop and check the button state. */ while (true) { if (!palReadPad(GPIOC, GPIOC_BUTTON)) test_execute((BaseSequentialStream *)&SD2); chThdSleepMilliseconds(500); }
This code:
- initializes the Serial Driver 2 with a default configuration;
- creates the blinker thread (Thread1);
- enters in the main() loop. Here it checks the User Button: when it is pressed it launches the ChibiOS’ Test Suite over Serial Driver 2.
As we can the blinker thread is actually created inside the main using the API chThdCreateStatic()
chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO + 1, Thread1, NULL);
This API is commonly used and requires 5 arguments:
- A pointer to the working area (waThread1, the one we have declared before),
- The size of the working area,
- The priority of thread,
- A function to execute as a thread (Thread1, which has been declared before),
- A parameter which will be passed to the Thread1 function (in this case we are passing nothing).
After the execution of this code, our kernel has to manage three threads: main, Thread1 and idle.
Comparing two different demo
To conclude this code walkthrough, let’s take a look to the code of the demo RT-STM32F303-DISCOVERY
#include "ch.h" #include "hal.h" #include "ch_test.h" /* * Blinker thread #1. */ THD_WORKING_AREA(waThread1, 128); THD_FUNCTION(Thread1, arg) { (void)arg; chRegSetThreadName("blinker 1"); while (true) { palToggleLine(LINE_LED3_RED); chThdSleepMilliseconds(100); palToggleLine(LINE_LED7_GREEN); chThdSleepMilliseconds(100); palToggleLine(LINE_LED10_RED); chThdSleepMilliseconds(100); palToggleLine(LINE_LED6_GREEN); chThdSleepMilliseconds(100); } } /* * Blinker thread #2. */ THD_WORKING_AREA(waThread2, 128); THD_FUNCTION(Thread2, arg) { (void)arg; chRegSetThreadName("blinker 2"); while (true) { chThdSleepMilliseconds(50); palToggleLine(LINE_LED5_ORANGE); chThdSleepMilliseconds(100); palToggleLine(LINE_LED9_BLUE); chThdSleepMilliseconds(100); palToggleLine(LINE_LED8_ORANGE); chThdSleepMilliseconds(100); palToggleLine(LINE_LED4_BLUE); chThdSleepMilliseconds(50); } } /* * Application entry point. */ int main(void) { /* * System initializations. * - HAL initialization, this also initializes the configured device drivers * and performs the board-specific initializations. * - Kernel initialization, the main() function becomes a thread and the * RTOS is active. */ halInit(); chSysInit(); /* * Activates the serial driver 1 using the driver default configuration. */ sdStart(&SD1, NULL); /* * Creates the example threads. */ chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO+1, Thread1, NULL); chThdCreateStatic(waThread2, sizeof(waThread2), NORMALPRIO+1, Thread2, NULL); /* * Normal main() thread activity, in this demo it does nothing except * sleeping in a loop and check the button state, when the button is * pressed the test procedure is launched. */ while (true) { if (palReadPad(GPIOA, GPIOA_BUTTON)) test_execute((BaseSequentialStream *)&SD1); chThdSleepMilliseconds(500); } }
The main difference is that the Discovery has 8 LEDs which here are managed through 2 threads. The interesting part is that each thread manages 4 LEDs and nonetheless the game light remains in sync along time. This is due to the reliability of hard real-time scheduling. ChibiOS/RT’s is considered a hard real-time operating system: this means that its code execution is deterministic and predictable.
Creating our first project
Until now we have seen how to import existing demos and how to flash and run them. The development process requires actually the creation of new projects. The best way to do that is to duplicate the default demo for your development board.
This can be done opening the demo, copy and paste it (CTRL + C and CTRL + V) and choosing a new name. To complete the procedure we have also to change a relative path in the makefile otherwise the new project will not compile.
The short explanation is: in the makefile, you have to change this
# Imported source files and paths CHIBIOS = ../../..
to this
# Imported source files and paths CHIBIOS = ../../chibios191
The long explanation is that CHIBIOS is a relative path. A relative path is a way to specify the location of a directory relative to another directory. Talking of relative paths the symbol “.” (dot) means this folder, the symbol “..” (double dots) means the parent folder.
In this case, CHIBIOS represent the path of the ChibiOS main directory (C:\ChibiStudio\chibios191) reported to the project path (C:\ChibiStudio\chibios182\demos\STM32\RT-STM32F401RE-NUCLEO64) hence in this case the absolute path is equal to three steps up (../../..).
After the copy, the newly created project will be placed in our workspace (C:\ChibiStudio\workspace_user\<name of the new project>\) hence the new relative path will be two steps back then enter in the folder chibios191 (../../chibios191).
Duplicating a default project we have to edit makefile and fix the CHIBIOS relative path otherwise the new project will not compile.
After this modification, we will be able to compile our new project but we will not be able to flash and run it without editing the Launch Configuration. Duplicating the project we have also duplicated the folder named debug which contains the .launch file: unfortunately this file is still associated with the old project. To fix this we have to open the launch configuration file, search the name of the old project and replace it with the name of the new project: it should replace three times.
Remember to rename the Launch Configuration to avoid confusion between projects.
Duplicating a default project we have to edit the Launch Configuration to make it point to our new project. It is also a good idea to rename it using the name of the new project to avoid confusion.
Let us resume the steps required by this procedure:
- Open the default demo for your development board;
- Copy and paste the demo choosing a name for the newly created demo;
- Open the makefile, and fix the CHIBIOS relative path;
- Rename the debug configuration using the name of the new project instead of the starting one;
- Open the debug configuration, search and replace the name of the old project with the new one: it should replace three times.
The following video will dissipate all doubts
Important notes about Flash and Run

In the previous article, we already talked about launching OpenOCD and the flash and run procedure. I would like to add something: when we develop a new project, every time we edit our code, we need to re-flash our microcontroller with the newer version of the firmware. If we follow each time the flash and run procedure largely described we would attempt to launch OpenOCD more than one but this is not possible.
We cannot even relaunch the flash and run if we do not terminate the previous instance. So what is the right way?
You can notice that in the Debug windows you have two instances: the OpenOCD and the current debugging section (has the name of launch configuration used to start debugging).
You do not need to terminate OpenOCD unless:
- You have disconnected your board (in this case you will see OpenOCD which looks for the target continuously polling it and a lot of red lines in the Console windows).
- You have to change the board.
- You have powered off the USB (e.g. sleep or hibernate your PC)
You have to terminate the current debugging section: to do this right click on the previous instance of your debug section in the Debug Window and select Terminate and Remove (as in Fig. 2).
Now you can launch a new Debug Configuration.
Previous and next
This article is part of a series of articles which are meant to be tutorials. I have composed them to be read in sequence. Here the previous and next article of this series:
hello,
Sir while creating the project I am not able to find the option of rename. Even if I double click on it it is getting open only.
Please Reply. Thank you
Hello yourself.
Rename a default project is not a good idea. The suggested way is to duplicate a default demo. Take a look to chapter 5. There is also a small video about that. While duplicating one of the steps is to choose a new name for the project.
I don’t know if you talk about board.h later, but I miss some information related with that file necessary to adapt the pins mapping/setup to a particular platform. That was the hardest part to start working with my STM32 platform. Is there any article related? I would like to read about it to deeper with that. Thanks for sharing all this information! 🙂
Sorry for the noise, just the next post is what I was looking for. I want to read it in depth. Very happy to have that nice information available!! 🙂
Yes, the article about GPIO and PAL driver answers your question in deep.
Hello Author. I’ve got STM32F4 Discovery kit, and I’m a starter about ChibiStudio. Your article is very helpful to me!! Just one thing is the video on ‘Section 3. A more detailed look to default demo’.
The problem is that I couldn’t check the test result on the terminal when I pressed the User Button. I’ve checked your article and did tutorial from first article to this one. I think that I’m missing something.
So, please give me advice about the problem. For example, setting or anything… Thank you.
Hey there,
thanks for commenting and the nice words. Well it depends on your kit. Is an STM32F407 based discovery kit I guess. Look at the revision board because on old boards the ST-Link VCP is not physically connected to the MCU. Plus even depends on the code you are running.
Thanks for replying. For more detail, my kit is ‘STM32F407G-DISC1’. One of the kit’s features is on-board ST-LINK/V2-A. I thought that I would test like articles then I could get same result. But the result is different even using demo code. The cause is ST-LINK/V2-A maybe?
One more thing, after execute the flash and run using demo code, I’ve got below message from console.
------------------
Open On-Chip Debugger 0.10.0+dev-01135-gdca1c6ca1-dirty (2020-03-17-11:50)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
srst_only separate srst_nogate srst_open_drain connect_deassert_srst
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : clock speed 2000 kHz
Info : STLINK V2J37M26 (API v2) VID:PID 0483:374B
Info : Target voltage: 2.889073
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : Listening on port 3333 for gdb connections
Info : accepting 'gdb' connection on tcp/3333
Info : device id = 0x10076413
Info : flash size = 1024 kbytes
Info : flash size = 512 bytes
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x080002b8 msp: 0x20000400
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x080002b8 msp: 0x20000400
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x080002b8 msp: 0x20000400
------------------
I think upper message is error… even if my kit’s LED is blinking after press the Resume button.
Please give me some hints to solve my problems.
Thank you. And Sorry for too long comment.
Hey,
you need to flash the demo for the discovery kit that is RT-STM32F407-DISCOVERY
Unfortunately you are going to see nothing on the serial as the ST-Link v2-A do support the Virtual COM port but the RX TX lines are not actually connected on the board.

I did it !!! Actually, I success article “Printing strings on a Virtual COM port with an STM32 and ChibiOS”.
I connected USB to UART cable between PC and PA2, PA3 pins.
I Checked the string ‘Hello PLAY embedded’ from Other terminal program.
Thanks for your help.I will keep learning through articles.
Have a nice day 🙂
Very cool articles. Nicely written as well.
Got to know Chibios from the Axoloti Audio Dsp project.
Thanks
Thanks Tim, there are some newer articles actually that you may want to look into