Writing embedded systems is indeed an interesting task. Unlike regular application development, we have to deal directly with the hardware and also worry about memory usage and real-time performance. As a veteran in embedded systems for over a decade, I find that developing embedded systems with C++ is quite cool. It allows us to utilize object-oriented features without the excessive overhead that Java brings.
1
Characteristics of Embedded Development
When it comes to embedded development, the biggest characteristic is resource constraints. The RAM of an MCU might only be a few K to a few hundred K, and the ROM is similarly limited. When writing code, one has to be very economical, and operations like new have to be used cautiously.
Here’s a simple LED blinking code:
class LED {
private:
volatile uint32_t* const gpio_reg;
const uint8_t pin;
public:
LED(volatile uint32_t* reg, uint8_t p) : gpio_reg(reg), pin(p) {
// Configure GPIO as output mode
*(gpio_reg + 1) |= (1 << pin);
}
void toggle() {
*gpio_reg ^= (1 << pin);
}
};
Tip: The volatile keyword cannot be omitted; otherwise, compiler optimizations may lead to unexpected results.
2
Choosing a Real-Time Operating System
Speaking of real-time systems, FreeRTOS is definitely a hot choice. It is compact, the source code is clear, and most importantly, it’s free. However, wrapping it in C++ can make it even more user-friendly:
class Task {
protected:
TaskHandle_t handle;
public:
template<typename F>
Task(F&& func, const char* name, uint32_t stack_size) {
xTaskCreate(
[](void* param) {
auto fp = static_cast<F*>(param);
(*fp)();
delete fp;
},
name,
stack_size,
new F(std::forward<F>(func)),
tskIDLE_PRIORITY,
&handle
);
}
};
3
Designing Hardware Abstraction Layers
The Hardware Abstraction Layer (HAL) is a technical task. A good HAL allows upper-level applications to be written easily and facilitates portability. Personally, I prefer using the CRTP pattern:
template<typename Impl>
class SPIDevice {
public:
void transfer(uint8_t* tx, uint8_t* rx, size_t len) {
static_cast<Impl*>(this)->transfer_impl(tx, rx, len);
}
};
class STM32SPI : public SPIDevice<STM32SPI> {
friend class SPIDevice<STM32SPI>;
protected:
void transfer_impl(uint8_t* tx, uint8_t* rx, size_t len) {
// STM32 specific implementation
}
};
4
Memory Management
In embedded systems, memory management is a big deal. Dynamic memory allocation is not impossible, but it requires some thought. I recommend using a memory pool:
template<typename T, size_t N>
class MemoryPool {
union Block {
T data;
Block* next;
};
Block pool[N];
Block* free_list;
public:
MemoryPool() {
free_list = &pool[0];
for(size_t i = 0; i < N-1; ++i) {
pool[i].next = &pool[i+1];
}
pool[N-1].next = nullptr;
}
T* allocate() {
if(!free_list) return nullptr;
Block* block = free_list;
free_list = free_list->next;
return &(block->data);
}
};
Tip: Don’t think about using STL’s allocator; it doesn’t work well in embedded environments.
5
Interrupt Handling
Interrupt handling is a technical task, and writing it in C++ can be quite interesting. However, remember: you cannot use virtual functions in interrupt handler functions, and you should not perform overly complex operations.
class InterruptHandler {
public:
static void handle() __attribute__((interrupt)) {
// Interrupt handling logic
}
};
When engaging in embedded development, the most important thing is to understand the hardware. Just writing code is not enough; you need to know the CPU architecture, how to use registers, and how to configure timing. I recommend frequently reviewing the development board schematics and browsing through the chip manuals.
Honestly, developing embedded systems with C++ is quite enjoyable. Object-oriented programming allows for better code management, and template metaprogramming can resolve many issues at compile time. However, remember one thing: do not overuse any feature; if something can be done simply, avoid making it complex.
This work is like building a house; the foundation must be laid well, the framework should be designed clearly, and the decoration must also be considered. Take your time, don’t rush, practice more, and you will eventually become an expert.