If you look around you'll find that many compilers for MCU has the ability to support C++, for example the IAR, Keil, and the GCC. Wild ranges of MCUs can be developed in C++, like TI's MSP430, Atmel's AVR, ARM series from any manufacture, and even the MCS-51 if you choose the right compiler.
First of all, C++ doesn't mean big code size. Programs with GUI on PC becomes large because they use a lot of libraries, C++ itself is as efficient as C, if not better than. I had done a commercial project with MSP430F2312, which has only 8KB of FLASH. I wrote the entire code in C++ and it turns out to be one of the most efficient code I have ever done. Until now I still couldn't believe I had packed so many functionalities in such a small chip.
C++ has 3 major benefits over C, inheritance, polymorphism, and templates. Unfortunately, many C++ compilers for MCUs do not support templates, but polymorphism alone is a thing good enough for you to consider using it. Imagining you wrote a software I2C interface, and you can easily change the configuration to assign which I/O port to assign it, or even changing the mapping I/O on the fly!
Here I'll give an easy example with STM32F103 using Keil's SDK. The code is very simple, it just blink a LED on an I/O pin. Not using any RTOS, not using any timer, interrupt, or any library.
First there is the plain C version:
#include <stm32f10x.h>
#define LED1 0x2000 // PC13
void RCC_Init(void)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
}
void blink()
{
unsigned long i;
while(1)
{
GPIOC->ODR ^= LED1;
for (i=0; i<0x400000; i++);
}
}
int main(void)
{
RCC_Init();
GPIOC->CRH = 0x44244444;
blink();
return 0;
}
The C++ version is a little bit longer than the C code, because there need to be the definitions of the class:
file gpio.h:
#ifndef GPIO_H
#define GPIO_H
class GPIO
{
public:
enum Mode
{
FLOAT_INPUT = 0x04,
PUSH_PULL_50M = 0x03,
PUSH_PULL_2M = 0x02,
PUSH_PULL_10M = 0x01,
ANALOG_INPUT = 0x00,
ALT_PUSH_PULL_50M = 0x0B,
ALT_PUSH_PULL_2M = 0x0A,
ALT_PUSH_PULL_10M = 0x09,
PULL_UP_DOWN_INPUT = 0x08
};
GPIO() {}
virtual ~GPIO() {}
virtual void Set1() = 0;
virtual void Set0() = 0;
virtual void Toggle() = 0;
};
#endif
File main.cpp:
#include "stm32f10x.h"
#include "gpio.h"
class PortCPin : public GPIO
{
private:
uint16_t pin;
public:
PortCPin( unsigned char pin_number, GPIO::Mode mode = FLOAT_INPUT)
{
pin = (uint16_t)1<<pin_number;
if (pin_number >= 8)
{
pin_number -= 8;
GPIOC->CRH = (GPIOC->CRH & (~((uint32_t)0x0F << (pin_number*4)))) | ((uint32_t)mode << (pin_number*4));
}
else
{
GPIOC->CRL = (GPIOC->CRL & (~((uint32_t)0x0F << (pin_number*4)))) | ((uint32_t)mode << (pin_number*4));
}
}
~PortCPin()
{
}
void Set1()
{
GPIOC->BSRR = pin;
}
void Set0()
{
GPIOC->BRR = pin;
}
void Toggle()
{
GPIOC->ODR ^= pin;
}
};
void RCC_Init(void)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
}
void blink(GPIO& pin)
{
while(1)
{
pin.Toggle();
for (unsigned long i=0; i<0x400000; ++i);
}
}
int main()
{
RCC_Init();
PortCPin PC13(13, GPIO::PUSH_PULL_2M);
blink(PC13);
return 0;
}
One thing should be of caution is, the definition of PC13 must be behind the RCC_Init() code, it can not be declared as a global, because then the constructor will be executed before the main() then before the RCC is initialized.
If you are curious about the code efficiency, the plain C code is 744 bytes long (mainly because of the system initialization code not listed here), while the C++ version is 788 bytes.