Skip to main content

11 posts tagged with "C"

Embedded C

View All Tags

Debugging a HardFault on a Cortex-M: from a mystery to a line of code

· 12 min read

Every embedded developer knows the moment: the firmware has been running, and then it does not. The debugger shows that the program sits in an infinite loop with a name like HardFault_Handler or Default_Handler, and the call stack is a few meaningless frames. That loop is the default handler of the startup code, and it says exactly nothing about what happened. The fault has a cause, and the processor has already written it down: in eight registers on the stack and in four status registers. It is only necessary to read them.

This article shows how to turn the loop into a line of source code, in two real examples that I ran in QEMU (a model of an STM32F4 board) and examined with GDB: a call of a NULL function pointer and a write to an address where nothing is. All outputs are real.

Running the real firmware without the hardware: a simulator in the CI

· 10 min read

In the article about unit testing with Unity and CMock I said that the tests on the PC do not find "the differences between the PC and the MCU". This article is about those differences, and about the tool that finds them: a simulator that runs the real binary. Not the logic compiled for the PC, but the very .elf that the ARM compiler made, with the real startup code and the real linker script, on a model of a processor and its peripherals.

I will show two experiments that I ran: the same test source that passes on the PC and fails on the Cortex-M4, and a firmware that writes to the registers of a USART at the real addresses of an STM32F4 and whose text appears in the terminal. At the end there is an honest list of what a simulator will not tell you.

Designing a peripheral API: eight decisions behind the Gpio module

· 11 min read

The GPIO is the simplest peripheral of a microcontroller: a pin is high or low. That is exactly why it is a good subject for an article about the design of an interface. There is no hardware complexity to hide behind, and every decision is a choice of the designer: how a pin is named, what a function returns, where the polarity of an LED lives. The MCAL module Gpio of the Embedbits BSP is a real example with real answers, and I will go through them one by one, with the alternatives and the price.

The code from the module is quoted from the STM32H5 branch of Bsp-Mcal-Gpio. The examples that use it were compiled and run against the real Gpio_Port.h and Gpio_Types.h, with a small fake of the implementation, so that the API could be tried on a PC.

Doxygen for embedded C: documentation that cannot be forgotten

· 9 min read

Every project has documentation, and every project has documentation that lies. A Word file with the description of the interface was correct in the week when it was written. A comment above the function is more honest, because it is a few lines from the code that it describes, but it is also written by the same people who forget. The way out is not more discipline, it is a tool that reads the comments, builds the documentation from them and fails the build when something is missing. That tool is Doxygen.

This article shows what a documented module looks like in my projects, how it is generated, and how to make the documentation a part of the CI that cannot be skipped. The examples were built with Doxygen 1.9.8 and Graphviz, and the outputs are real.

Interrupts and main(): how to share data safely

· 11 min read

An interrupt handler and the main loop are two programs that run in the same memory and do not know about each other. The C compiler does not know that the interrupt exists, and the CPU does not know that two variables belong together. The programmer is the only one who knows, and the bugs that follow are the worst kind: they appear once in a thousand runs, they disappear when you attach the debugger and they never appear in the code review, because the code looks right.

This article goes through the three problems of the shared data, one by one, with the code that fails, and then shows the patterns that work. The experiments run on a Cortex-M4 (in QEMU, on a model of an STM32F4 board) or on a PC, and the outputs are real.

Finite state machines in practice: a button with debounce and long press

· 11 min read

In the first article about finite state machines I gave you the template and ended with "to be continued". This is the continuation, and the best way to continue a theory is a problem. I chose the one that every embedded project has and that nobody gets right on the first try: a push button.

A button looks trivial: a pin, high or low. But a mechanical contact bounces, so the pin does 1 0 1 1 0 1 before it settles, and the product usually wants two different things from the same button: a short press and a long press. Written with flags and counters in the main loop, it ends as a few ifs that depend on each other in a way that nobody can explain after a month. A state machine solves it in a way that you can explain with a table.

What happens before main(): reset, startup code and linker script

· 12 min read

Every C tutorial starts with int main(void). Nobody explains who calls it. And yet, when you write uint32_t counter = 5; as a global variable and the first line of main() reads 5, a lot of work already happened. When the work is not done, the symptom is a variable with a random value that "worked yesterday".

In this article we follow the microcontroller from the reset to the first line of main(): what the hardware does by itself, what the linker script says and what the startup code has to do. It is the content of two modules of the Embedbits BSP (Linker and Startup), but the principle is the same on every Cortex-M. All code in the article was built with arm-none-eabi-gcc 13.2.1 and run in QEMU, on a model of an STM32F405 board, so the addresses and the outputs are real.

Unit testing embedded C on your PC with Unity and CMock

· 11 min read

"Embedded software cannot be unit tested, you need the hardware." I hear it often, and it is true for exactly one kind of code: the code that touches the registers. All the rest, the state machines, the protocol parsers, the control logic, the conversion of the values, is plain C that compiles on your PC. And on your PC it runs in milliseconds, without a debugger, without a cable and without flashing.

This article shows how to test a module with Unity (the test framework) and CMock (the generator of mocks), from the design that makes it possible to the CMake file that builds it. Everything was built and run, and the output below is the real one.

File organization in embedded C: how a folder becomes a module

· 11 min read

My coding style says that file names start with the module name and that "the complete file organization is described elsewhere". This is the elsewhere.

C has no private, no namespaces and no packages. Once the code is split into files, the files and the build system are the only tools you have to say what belongs together, what is public and what is nobody else's business. So the file organization is not a matter of taste, it is a part of the design. This article describes how I organize an embedded project on three levels: the project, the module and the single file.

MISRA C rules in practice: what they are, why they exist and how to live with them

· 20 min read

Say "MISRA" in a room full of embedded developers and half of them will sigh, and the other half will ask which tool you use. The reputation of the rules is mixed: for some they are the bureaucracy that eats a week before every release, for others the reason why a car does not reboot on the highway. Both groups are partly right.

In this article I will go through the rules that I meet most often, one by one: what the rule asks for, what you get for it, what the bad code looks like, what the good code looks like and who finds the problem for you. All examples are real C code that I compiled and ran.