Flashing is not just “copying a file” — it is a controlled sequence of memory erase, program, verify, and reset operations that interact directly with the microcontroller’s internal flash controller. Debugging, on the other hand, allows you to halt, inspect, and control the CPU while it executes instructions, giving you deep visibility into registers, memory, stack, peripherals, and timing behavior.
Before tools and cables, understand the architecture.
An MCU system looks like this internally:
+-----------------------+
| CPU Core |
| (ARM Cortex-M) |
+-----------+-----------+
|
|
+-----------v-----------+
| Bus Matrix |
+-----+-----------+-----+
| |
+-------------v--+ +----v-------------+
| Flash | | SRAM |
| (Non-volatile) | | (Volatile) |
+----------------+ +------------------+
- Flashing = Writing machine code into Flash memory.
- Debugging = Controlling the CPU while it executes from Flash.
Flashing modifies memory contents.
Debugging controls CPU execution state.
These are two completely different layers.
In professional embedded development, flashing and debugging are tightly connected but technically different layers. Flashing modifies non-volatile memory. Debugging controls the processor execution state.
To understand this properly, we must examine the hardware path from the PC to the CPU.
PC (IDE / CLI Tool)
|
v
Debug Probe (J-Link / ST-Link / CMSIS-DAP)
|
v
Debug Interface (JTAG / SWD / UART)
|
v
MCU Debug Logic / Bootloader
|
v
Flash Controller
|
v
Internal Flash Memory
Flash Firmware
Flashing firmware means transferring a compiled binary image (.bin, .hex, .elf) into non-volatile memory (Flash) of the microcontroller so that it executes after reset.
Flashing can happen at different system layers:
- Via Debug Interface (JTAG/SWD)
- Via Bootloader (UART, USB, CAN)
- Via Network (OTA)
- Via External Programmer (Mass production)
The difference is who controls the flash controller.
JTAG (Joint Test Action Group):
is a standard that was originally developed for boundary-scan testing of digital circuits, JTAG typically uses a set of pins such as :
TDI (data input)
TDO (data output)
TMS (test mode select)
TCK (clock),
TRST (reset).
It provides full access to the microcontroller's internal resources and allows for sophisticated debugging, you typically need a JTAG programmer (such as a J-Link or ST-Link) connected to your development system and the target device.
The programmer allows you to access the microcontroller’s memory and load the firmware using appropriate software tools, such as OpenOCD or SEGGER J-Link tools.
SWD (Serial Wire Debug):
is an ARM-specific two-wire debugging protocol used to interact with ARM Cortex-M microcontrollers, SWD uses just two main data lines:
SWDIO (data input/output)
SWCLK (clock)—along
with optional RESET and GND lines.
Flashing with SWD is similar to JTAG but requires an SWD-compatible debug probe.
For ARM Cortex-M devices, tools like ST-Link, J-Link, or CMSIS-DAP can be used to interact with the SWD interface.
Most IDEs, such as Keil, Eclipse, or Visual Studio Code (with extensions like PlatformIO), support SWD for programming and debugging.
Let us examine each method deeply.
Flashing via Debug Interface (JTAG / SWD)
This is the most direct method during development.
PC (IDE)
|
v
Debug Probe (J-Link / ST-Link / CMSIS-DAP)
|
v
SWD / JTAG
|
v
Debug Access Port (DAP)
|
v
Flash Controller
|
v
Internal Flash
When you press “Flash” in:
- Visual Studio Code
- Keil MDK
- OpenOCD
The debugger:
- Halts CPU
- Unlocks flash controller
- Erases sectors
- Writes words to flash
- Verifies CRC
- Resets CPU
Advantages:
- Full memory access
- Fast flashing
- Debug immediately after flash
- No bootloader required
Disadvantages
- Requires debug pins exposed
- Not ideal for field updates
- Debug port must be enabled (security risk)
Flashing via UART Bootloader
UART (Universal Asynchronous Receiver-Transmitter)
is a serial communication protocol primarily used for data transfer between devices, UART is useful for bootloader communication (where a bootloader over UART allows you to flash firmware) and for sending debug information from the MCU to an external terminal.
UART-based flashing is often performed using a bootloader that listens for a firmware file via a serial connection.
Many microcontrollers, particularly those from the ESP32 or STM32 families, support bootloaders that facilitate flashing over UART.
Tools like esptool.py for ESP32 and STM32CubeProgrammer for STM32 devices allow you to send a firmware image over a serial port and write it to flash. For devices like the ESP32, the flashing process involves putting the device into bootloader mode by manipulating the
IO0 or GPIO0 pins,
after which you can use a UART-to-USB adapter to send the firmware.
UART flashing is bootloader-level flashing.
The MCU contains:
- ROM Bootloader (factory programmed)
- Custom Bootloader in flash
OR
Example devices:
- ESP32
- STM32F4
Boot Flow
Power ON
|
v
ROM Bootloader
|
+-- Check Boot Pins
|
+-- If Boot Mode → Listen on UART
|
+-- Receive Firmware
|
+-- Program Flash
|
+-- Reset
UART Flash Architecture
PC (esptool.py / STM32CubeProgrammer)
|
| TX/RX Serial
|
+----------------------+
| Bootloader |
| Flash Controller |
+----------+-----------+
|
v
Flash
Tools:
- esptool.py
- STM32CubeProgrammer
Characteristics
- Slower than SWD
- Does not allow debugging
- Simple wiring (TX, RX, GND)
- Good for production flashing
USB Bootloader Flashing
Some MCUs include USB DFU (Device Firmware Upgrade).
Example:
- STM32 DFU mode
Architecture:
PC
|
| USB
|
+------------------+
| USB Bootloader |
| Flash Controller |
+--------+---------+
|
v
Flash
Advantages:
- Faster than UART
- No external debugger required
- User-friendly update
OTA (Over-The-Air) Flashing
OTA is modern firmware updating through network.
Used in:
- IoT devices
- Industrial gateways
- Wi-Fi modules
- BLE devices
Example:
- ESP32
OTA Architecture
Cloud Server
|
| HTTPS
|
WiFi Router
|
v
Device (Running Firmware)
|
+-- Download New Image
+-- Store in Secondary Partition
+-- Verify Signature
+-- Swap Boot Partition
+-- Reset
Dual Partition Model
+--------------------+
| Bootloader |
+--------------------+
| App Partition A | <-- Running
+--------------------+
| App Partition B | <-- New image
+--------------------+
OTA Steps:
- Download firmware
- Validate checksum
- Validate signature
- Mark new partition active
- Reset
- Rollback if failure
Advantages
- Remote update
- No physical access required
- Essential for IoT SaaS
Disadvantages
- Requires secure boot
- Needs partition planning
- Power failure risk must be handled
External Production Programmer
Used in mass manufacturing.
Architecture:
Gang Programmer
|
v
Multiple PCBs
|
v
Flash via SWD / JTAG / UART
Used when:
- High production volume
- Need speed optimization
- Automated test fixtures
DEBUGGING TOOLS
Debugging is about controlling and observing the CPU while running firmware.
Debugging tools are divided into:
- Hardware Debuggers
- Software Debuggers
Hardware Debugging Tools
These physically connect to MCU debug pins.
Examples:
- J-Link
- ST-Link
- CMSIS-DAP
Architecture:
PC
|
| USB
|
Debug Probe
|
| SWD / JTAG
|
MCU Debug Access Port
|
CPU
Capabilities
- Halt CPU
- Read/Write memory
- Breakpoints
- Watchpoints
- Step execution
- Reset control
- Flash programming
Internal Debug Units (Cortex-M)
- FPB (Flash Patch & Breakpoint)
- DWT (Data Watchpoint & Trace)
- ITM (Instrumentation Trace Macrocell)
- ETM (Embedded Trace Macrocell)
Software Debugging Tools
Software tools interpret debug data and control execution.
Examples:
- GDB
- Visual Studio Code
- Eclipse IDE
- Keil MDK
IDE
|
v
GDB
|
v
OpenOCD
|
v
SWD
|
v
MCU
Types of Debugging
Breakpoint Debugging
- Stops CPU at specific instruction
- Hardware or software breakpoint
if(x == 5) <-- Breakpoint here
Watchpoint Debugging
- Trigger when memory address changes
Watch variable: temperature
Break when modified
Trace Debugging
- Records instruction flow
- Used for timing analysis
- Requires ETM support
Serial Debug (printf Debugging)
Using UART:
printf("Value = %d\n", x);
Simple but:
- Adds timing overhead
- Not real-time safe
- Cannot inspect registers
| Feature | JTAG | SWD | UART |
|---|---|---|---|
| Purpose | Debugging, testing, programming | Debugging, programming ARM Cortex-M | Serial communication between devices |
| Pins | 10-20 pins | 4 pins (SWDIO, SWCLK, GND, RESET) | 2 main pins (TX, RX), optional GND/VCC |
| Speed | High speed, but more complex | Faster than UART, simpler than JTAG | Lower speed, best for basic data transfer |
| Complexity | High (requires specific hardware and setup) | Low to moderate (designed for ARM Cortex-M) | Very low, simple communication |
| Use Case | Low-level access to MCU for debugging and testing | Efficient debugging and programming for ARM devices | Communication for peripherals, logging, or control |