使⽤C++编写嵌⼊式应⽤程序
⼤多数嵌⼊式⼯程师使⽤C语⾔来编写Cortex-M系列MCU 的程序,⼤家总觉得C++是⽤来编写Windows 或者Linux 应⽤程序的。特别是硬件⼯程师,也许压根就没有使⽤C++来编写程序。 当我们阅读Mbed OS 的代码时却发现,许多是使⽤C++来编写的。C++是⼀种功能强⼤的⾯向对象的程序设计语⾔,在嵌⼊式系统软件开发中使⽤C++,会获得意想不到的简洁和喜悦。 事实上,arduino 的程序设计语⾔也是C/C++。在arduino中也可以设计C++的类。许多arduino的库都是C++的类。Arm keil 中的MDK-ARM V5.14 也⽀持C++程序设计语⾔。
但是⾝边的嵌⼊式⼯程师使⽤C++语⾔特性的并不多见。
对C++语⾔的误解
对于嵌⼊式程序员来讲,对C++语⾔有⼀些误解:
1. C++太慢
2. C++ ⽣成的⽬标代码过于庞⼤
3. C++ 不是ROMable的
4. C++ 库过于庞⼤
5. 抽象造成了低效率
6. 虚拟函数太慢
所以这些观点都是错误的。据有关⽂献表明,C++的源代码可能⽐C++程序多⼀些,但是⽬标代码并不⽐C语⾔产⽣的代码⼤和慢。最多只有10%的差异。
C++的版本
C++ 是⼀种⾯向对象的程序设计语⾔,它是1979年美国贝尔实验室在C语⾔的基础上发展起来的。国家标准化组织对C++建⽴了标准化,之前稳定的版本是C++98版本。 C++的版本为C++11 是ISO 2011
年的标准版本,C++14 是2014年的标准版本,C++17 是2017年的标准版本。
甲醇燃料灶C++是C语⾔的⼀个超集,所以在C++编译环境下,我们依然可以使⽤C编写程序。
IKRTV
C++ 的特点
C++和C最⼤的区别就是C++是⾯向对象的程序设计语⾔。⽽C是⾯向过程的程序设计语⾔。通俗地讲,C++是以数据为中⼼的,⽽C是以流程为中⼼的。学习C++ 程序设计的关键不是去熟悉C++的语法,⽽是要从以流程为中⼼转向对象为中⼼的思维模式。
C++最重要的概念就是类和对象。
对象(Objects)是要程序处理的任何⼀个物体,我觉得可以翻译成物体,事物等等。⾯向对象,台湾翻译成 物件导向,我觉得更为贴切⼀点。对于嵌⼊式程序⽽⾔,软件更加接近物理物体。⽐如⼀个引脚,⼀个马达,⼀个SPI端⼝,甚⾄于⼀台CNC机床,⼀台注塑机。在C++ 程序中都被看做⼀个和多个对象。
它是如何做到的呢?使⽤的最基本的⽅法就是简化和抽象。在⾯向对象的程序设计语⾔中,将对象抽象成了⼀组数据,以及对这些数据操作的函数,并称之为类(class)。
形式上,就好⽐将⼏个函数加⼊到了结构类型(struct)中。在 C 中,结构是数据的凝聚,它将 数据捆绑在⼀起,使得我们可以将它们看作⼀个包。它们的处理可能在其它的地⽅。然⽽将函数也放在这个包内,结构就变成了新的创造物,它既能描述属性(就像 C中的 struct 能做的⼀样),⼜能描述⾏为,这就形成了对象的 概念。对象是⼀个独⽴的有约束的实体,有⾃⼰的记忆和活动。
为了加深印象,我们来举⼀些例⼦:
例如:我们来定义⼀个循环缓冲区类:
循环队列是嵌⼊式程序设计中常⽤的数据结构,例如在 串⼝UART通信中,如果使⽤终端⽅式就需要使⽤循环队列。它⼤概需要下⾯⼏个数据结构和操作:
缓冲区buffer
输⼊指针 input_index;
输出指针 output_index;
计数器 counter
操作程序包括
init()
put(char v)
char get()
C语⾔的代码为:
uint8_t buffer[SIZE];
int input_index;
int output_index;
int counter
void init()
{
input_index=0;
怎么自制纳米胶output_index=0;
程控电压衰减器
counter=0;
}
void put(uint8_t value)
{
buffer[input_index++]=value;
if (input_index==SIZE-1) input_inedx=0; counter++;
}
uint8_t get(){
int temp;
temp=buffer[output_index++];
if (output_index==(SIZE-1))
output_index=0;
counter--;
}
如果使⽤C++来编写⼀个循环队列的类:
class circular_buffer {
public:
circular_buffer();
put(uint8_t val);
uint8_t get();
private:
int input_index;
int output_index;
int buffer[SIZE];
}
circular_buffer:: circular_buffer()
{
input_index=0;
output_index=0;
counter=0;
}
void circular_buffer:: put(uint8_t value)
{
buffer[input_index++]=value;
if (input_index==SIZE-1) input_inedx=0; counter++;
}
uint8_t circular_buffer:: get(){
int temp;
temp=buffer[output_index++];
if (output_index==(SIZE-1))
output_index=0;
counter--;
}
对象是类的实例,就好⽐变量是类型的实例。上⾯的类实例化
circular_buffer mybuffer;
从形式上看,没有太多的变化,实际上,我们使⽤Class 描述的循环队列,不那么凌乱。更像是⼀个硬件的元器件。⾯向对象程序设计是我们⽤更接近物理特性的⽅式来描述物件。⽤类描述的循环缓冲区更像⼀个硬件器件。这才是OOP 的独特魅⼒。
如果我们在程序中使⽤多个循环缓冲区的话,使⽤类(CLASS) 就显得简洁了,只要写成:
circular_buffer inputBuffer;
circular_buffer outputBuffer;
通过C++ 的类,我们将循环缓冲区变成了像⼀个元器件,我们不在需要关⼼内部的实现细节,⽽只需要关⼼它给使⽤者呈现出来的性能就可以了。我们也可以使⽤⼀个符号来表⽰。
我们有必要再去学习C++呢?我也疑惑过,以⾄于很长时间没有去关⼼Arm公司C++编译器。直到Mbed OS 中⼤量地使⽤C++来编写程序,才开始认真地开始研究C++在嵌⼊式程序设计中的应⽤。
从C语⾔转向C++,不仅仅是你要学习新的语⾔规则,⽽是要改变编程的思维⽅式。最有价值的可能不是那些已存在的代码库(给出合适的⼯具,可以转变它),⽽是已存在的头脑库。⼀旦从C语⾔⾯向过程的⽅式转变为⾯向对象程序设计⽅式。你的效率会⼤⼤提⾼。所以C程序员学习C++的⼀项值得的投资。程序员是在⽤问题空间的术语描述问题的解(例如“把锁链放在箱⼦⾥”),⽽不是⽤计算机的术语,也就 是解空间的术语,描述问题的解(例如“设置芯⽚的⼀位即合上继电器”)。程序员所涉及的是 较⾼层的概念,⼀⾏代码能做更多的事情。
在MBed OS 中,硬件接⼝都是使⽤类来定义。使程序员不需要关注细节,就可以直接访问和控制硬件电路。例如下⾯这些都是接⼝类。
DigitalOut 数字输出类
构造函数
DigitalOut myled(PF_14); 它真正指定了STM32F429 的⼀个GPIO 脚
成员函数载人行李箱
write (int value);
read ();;
使⽤⽅式:
myled.write(1);
myled=1;
myled=!myled;
看到了吧?使⽤C++定义的硬件类,使我们控制GPIO 变得⾮常的⽅便。
四巧板
使⽤C++ 的类,和类似硬件设计那样,利⽤简单的功能电路构造构建了⼀个功能更强⼤,更智能的硬件零部件。在modular-2 电脑中,所有的I/O 接⼝板都对应封装了⼀个接⼝类(interface Class).将硬件抽象成了软件的类。
应⽤程序不再需要了解IO 模块的硬件细节,只要了解接⼝类就好了。正是由于Mbed OS 使⽤了C++类来设计API。才成就了Mbed OS 简单易学的特⾊。
C++ 的类是软件模块化设计的基⽯。使⽤类,可以⼏乎描述所以的物理部件的特性,⽐如步进电机,ADC 等等。
现在,我爱使⽤C++ 编写嵌⼊式程序,这个过程和使⽤各种元器件搭建PCB板,然后构成⼀个设备⾮常相似。编写⼀个好的类,就好像设计了⼀个芯⽚。既可以⾃⼰使⽤,也可以给朋友分享。