Hello again!
In our first article, we built a clean, fully-controlled Zephyr RTOS development environment from scratch. Now it's time to put it to work.
In this guide we will use our new environment to build and flash a project onto a real microcontroller - the popular STM32F103C8T6 "Blue Pill".
To do this, we will use a minimal, production-ready template that is pre-configured for a seamless experience in Visual Studio Code and solves common hardware issues out of the box.
1. What You'll NeedBefore we begin, make sure you have everything set up. The hardware is simple, and the software should already be configured if you followed our first guide.
Hardware:
- STM32F103C8T6 "Blue Pill" board
- ST-Link V2 programmer
- Connecting wires (Jumper wires)
Software:
- A fully configured Zephyr RTOS environment (Python, Git, West, SDK, etc.).
- Visual Studio Code with the recommended extensions (including C/C++, CMake Tools, and Task Explorer).
π‘ Just starting out? If you haven't prepared your development environment yet, please follow my detailed guide first: Manual Zephyr RTOS Installation on Windows. This project template assumes your environment is ready to go.2. Quick Start: From Zero to Blinking
β οΈ Important: Make sure your Zephyr environment is activated! Runzephyr-env.cmd- your terminal should show(.venv)at the prompt.
2.1. Get the Project Template
Clone the repository to your local machine and open it in Visual Studio Code:
git clone https://github.com/gkiryaziev/Zephyr_STM32F103C8T6.git
cd Zephyr_STM32F103C8T6
code .π¦ Project Repository: https://github.com/gkiryaziev/Zephyr_STM32F103C8T6
2.2. Connect Your Hardware
Connect ST-Link to Blue Pill via SWD interface:
- SWDIO β SWDIO
- SWCLK β SWCLK
- GND β GND
- 3.3V β 3.3V
Then plug ST-Link into your computer's USB port.
2.3. Build the Project
We'll use VS Code tasks for a streamlined workflow. You have two options:
Option A: Command Palette
- Press
Ctrl+Shift+Bor go toTerminal β Run Task... - Select Build from the dropdown
Option B: Task Explorer(recommended)
- Open the Task Explorer panel (side bar)
- Click the βΆοΈ play button next to the Build task
β Wait for the build to complete. You should see Build succeeded in the terminal.
2.4. Flash the Board
Run the Flash (64k) task using the same method.
Run the Flash (128k).
π‘ Note: If flashing fails with invalid flash size error, your chip likely has 64KB flash. Use the Flash (64k) task instead.
2.5. Celebrate! π
The LED on PC13 should now blink once per second. You've just run your first Zephyr application!
3. Under the Hood: What Makes This Template Smart3.1. Hardware Configuration (bluepill_f103c8.overlay)
The overlay file tells Zephyr exactly how our board is wired:
/ {
aliases {
my-led = &pc_13; // LED alias for easy reference
};
leds {
compatible = "gpio-leds";
pc_13: pc-13 {
gpios = <&gpioc 13 GPIO_ACTIVE_LOW>; // LED is active-low
};
};
};
&pinctrl {
swj-cfg = "jtag-disable"; // Free up PA15, PB3, PB4 for GPIO
};Key features:
- LED configured as
ACTIVE_LOW(lights when pin is LOW) - JTAG disabled, SWD enabled for debugging
- Pins PA15, PB3, PB4 now available for your application
3.2. Project Configuration (prj.conf)
CONFIG_DEBUG=y # Enable debug features for automatic resetThis single line enables OpenOCD to automatically halt and reset the microcontroller during flashing - no more manual RESET button pressing!
3.3. The Application Code (src/main.c)
Let's look at the actual blinking code:
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#define LED_NODE DT_ALIAS(my_led)
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED_NODE, gpios);
int main(void)
{
if (!gpio_is_ready_dt(&led)) {
return -1;
}
gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
while (1) {
gpio_pin_toggle_dt(&led);
k_msleep(1000); // 1 second delay
}
return 0;
}What's happening here:
1. We get LED configuration from device tree using the my-led alias
2. Check if GPIO port is ready
3. Configure pin as output
4. Toggle LED every second in an infinite loop
Notice how we never hardcode pin numbers - everything comes from the device tree. This makes the code portable and maintainable.
3.4. The VS Code Workflow
tasks.json: Defines theBuild,Flash, andCleanscripts. It also includes aFlash (128k)task, because some C8T6 boards secretly contain a chip with 128 KB of flash. If the standard flash fails, this alternative task often solves the problem.c_cpp_properties.json: This file configures IntelliSense (code completion). It points to acompile_commands.jsonfile that Zephyr's build system generates automatically. Important: You must run theBuildtask at least once for code completion to work perfectly!
β "AP write error, reset will not halt"
When it happens: First flash after build clean or project clone. This can only happen once, before the next deletion of the build folder.
Why: OpenOCD can't halt the running microcontroller.
Solution:
1. Press and hold RESET button on Blue Pill
2. Run Flash task in VS Code
3. Release RESET when "Downloading..." message appears
Prevention: Ensure CONFIG_DEBUG=y is set in prj.conf (already included).
β "Flash write failed" / "Invalid flash size"
Why: Your chip has 64KB flash, not 128KB.
Solution: Use Flash (64k) task instead of Flash (128k).
β "west: command not found"
Why: Zephyr environment not activated.
Solution: Run zephyr-env.cmd (Windows) or source your environment script.
Your prompt should show (.venv).
Congratulations! You've successfully built and flashed your first Zephyr project.
You can now focus on writing your application code, knowing that the underlying project structure is sound and optimized for a smooth development workflow in VS Code.
Feel free to modify this template, experiment, and build something amazing.
To be continued!











_SPmZ63wPgQ.png?auto=compress%2Cformat&w=40&h=40&fit=fillmax&bg=fff&dpr=2)
Comments