Overview
On microcontrollers, programming is typically done in assembly or C, while C++ is used far less often. This article explains some reasons for that and demonstrates a simple LED example on STM32 using Keil MDK to illustrate differences when using C++.
Why C is preferred over C++ on microcontrollers
C is a procedural language and C++ is object oriented. For the purposes of embedded development, procedural code often produces smaller binaries and can have better runtime efficiency. Assembly typically yields even smaller code size and higher performance than C. Because many microcontrollers have limited RAM and Flash and constrained CPU performance, C has historically been the dominant language for MCU development. As MCU memory and performance increase, some engineers have started using C++ for embedded projects, but the ecosystem is still largely C-based.
Development environment
Keil MDK uses the ARM compiler. Both Arm Compiler v5 and v6 support C++. When mixing C and C++ source files in a project, use extern "C" guards in C headers so the C++ compiler can link to C functions, for example:
#ifdef __cplusplus
extern "C" {
#endif
// C source code here
#ifdef __cplusplus
}
#endif
Defining an LED class
This example assumes familiarity with implementing an LED chaser on STM32 using C. The following shows a basic C++ approach. Create a main.cpp and define an LED class:
class LED_Class{
}
Define private members:
class LED_Class{
private:
GPIO_TypeDef *GPIOx;
uint16_t GPIO_Pin;
uint32_t RCC_APB2Periph;
}
Then add functions for initialization, turn on and turn off:
class LED_Class{
private:
GPIO_TypeDef *GPIOx;
uint16_t GPIO_Pin;
uint32_t RCC_APB2Periph;
public:
LED_Class(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin, uint32_t RCC_APB2Periph){
LED_Class::GPIOx = GPIOx;
LED_Class::GPIO_Pin = GPIO_Pin;
LED_Class::RCC_APB2Periph = RCC_APB2Periph;
}
void Init(void){
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOx, &GPIO_InitStruct);
}
void Open(void){
GPIO_SetBits(GPIOx, GPIO_Pin);
}
void Close(void){
GPIO_ResetBits(GPIOx, GPIO_Pin);
}
};
Even if you are not familiar with C++, this code is straightforward for someone who knows C.
LED chaser implementation (main)
The C++ approach is similar to C: initialize then loop. Example using stack-allocated objects:
int main(void)
{
LED_Class LED1(GPIOF, GPIO_Pin_7, RCC_APB2Periph_GPIOF);
LED_Class LED2(GPIOF, GPIO_Pin_8, RCC_APB2Periph_GPIOF);
LED1.Init();
LED2.Init();
while(1)
{
LED1.Open();
LED2.Open();
Delay(10);
LED1.Close();
LED2.Close();
Delay(10);
}
}
Or using dynamic allocation:
int main(void)
{
LED_Class *LED1 = new LED_Class(GPIOF, GPIO_Pin_7, RCC_APB2Periph_GPIOF);
LED_Class *LED2 = new LED_Class(GPIOF, GPIO_Pin_8, RCC_APB2Periph_GPIOF);
LED1->Init();
LED2->Init();
while(1)
{
LED1->Open();
LED2->Open();
Delay(50);
LED1->Close();
LED2->Close();
Delay(50);
}
}
One practical drawback is that C++ code can produce larger binaries compared with equivalent C code. A more significant factor is the MCU development ecosystem: many existing libraries and mature solutions are written in C, so most MCU projects remain C-centric. As microcontroller storage and performance improve, C++ use in embedded projects is gradually increasing.