Getting Started with Hardware Debugging

Flashing firmware and watching for an LED to blink confirms that your code runs, but it doesn’t tell you what happens inside your microcontroller. When your firmware crashes or a variable holds an unexpected value, you need to see the internal state of the microcontroller. A hardware debugger lets you pause execution, inspect memory, and step through your source code line by line.

In this guide, you will learn how to use a hardware debugger with the Texas Instruments CC2340 series and Nordic Semiconductor 54 L series development boards.

Why Hardware Debugging Beats printf

Many developers rely on UART logging to debug their systems. While getting your first printf logs is an essential skill, logging has its limitations. It changes the timing of your firmware and requires you to recompile and reflash every time you want to track a new variable.

Hardware debugging uses a dedicated physical interface, typically Serial Wire Debug (SWD), to give you direct access to the CPU. With a debugger, you can:

  • Set Breakpoints: Pause the CPU at a specific line of code.
  • Step Through Execution: Move through your code one instruction at a time.
  • Inspect Variables: View and modify the contents of RAM and peripheral registers in real-time.
  • Examine the Call Stack: See the sequence of function calls that led to the current state.

In the following sections, you’ll see how hardware debugging works on both the TI CC2340R5 and Nordic nRF54L15.

TI CC2340R5 Example

The TI CC2340R5 LaunchPad uses the XDS110 board as its debugger. It attaches to the main board and links it to the data-capable USB cable that connects to your PC.

To get started with hardware debugging, follow the instructions from a previous tutorial to set up the gpiointerrupt project in Code Composer Studio.

In gpiointerrupt.c, find the mainThread() function, and set a breakpoint right inside its body (see example below).

/*
 *  ======== mainThread ========
 */
void *mainThread(void *arg0)
{
    // Breakpoint
    /* Call driver init functions */
    GPIO_init();

Press F11 to start debugging. You should see your window automatically stop at your breakpoint. Step through each line of code in mainThread() until you reach GPIO_setConfig(). Then, step into that function, and you should see something like the image below.

TI Code Composer Studio debug window showing call stack, variables, breakpoint, and current execution point

Notice that you can see the call stack, a list of variables, your breakpoint, and your debugger’s current place in the code all at once. Whether this is your first time using the hardware debugger or your millionth, everything you need to debug your firmware successfully is right here in this window.

Debugging the Nordic nRF54L15: Inspecting State

The Nordic nRF54L15 board has a built-in onboard SEGGER J-Link debugger. This means that as soon as you connect the board to your PC using a data-capable USB cable, you’re ready to start debugging.

For this example, follow the instructions from a previous tutorial to set up the Button application in nRF Connect.

In the main() function, set a breakpoint right inside its body (see example below).

int main(void)
{
    // Breakpoint
    int ret;

Start debugging. You should see your window automatically stop at your breakpoint. Step through several lines of code in main(). You should see something like the image below.

nRF Connect VS Code debug window showing call stack, variables, CPU registers, and current execution point

Notice that you can see the call stack, a list of variables, your breakpoint, and your debugger’s current place in the code all at once. While the order of these panels is a little different from TI’s interface, you have access to the same features here. In fact, you have a few additional features as well, including a CPU registers viewer and memory explorer.

Common Debugging Pitfalls

Debugging hardware differs from debugging desktop software. Keep these factors in mind:

  • Watchdog Timers: If your hardware has a watchdog timer enabled, it may reset the board while the CPU is paused. Most debuggers attempt to suspend the watchdog during a halt, but you should verify this in your project settings.
  • Optimized Code (Release Build): Compilers often rearrange or remove code to improve performance, especially if you do a release build. If you try to step through code compiled with these optimizations, the debugger may jump around unpredictably. For the best experience, flash a debug build before using your hardware debugger.
  • Real-Time Interrupts: Pausing the CPU stops all software-based timing. If your board is communicating with an external sensor via I2C or SPI, the sensor might timeout while you are paused at a breakpoint.

Wrapping Up

Hardware debuggers turn a “black box” microcontroller into a transparent system. By getting comfortable with breakpoints and stepping, you can find bugs in minutes that might otherwise take hours if you only used logs. Then, you would be ready to move from simply observing your application running to taking full control over the code execution and internal logic.


Ready to connect your devices anywhere on Earth? Get started with Hubble Network for free →