我自己在Web开发方面长期使用Java语言(和C++一样,Java本身是面向对象的语言),因而习惯于面向对象的开发方式。而在单片机的开发中,一直都是使用面向过程的C语言开发。但使用Arduino开发单片机程序时,可以看到,在Arduino开发中使用C++这种面向对象的方式开发是司空见惯的事情,因此就想着能否在Keil的开发下使用面向对象的方式呢?
作为例子,准备在使用雅特力的AF32F405开发板上,在LED_Toggle例程的基础上,尝试改造为C++方式。选择LED_Toggle例程为基础,是因为这个处理最简单,改造上也相对容易。在改造之前,先做一下说明。当我们把GPIO作为一个驱动LED的对象使用时,不可避免地要使用到系统提供的库函数,或者操作单片机内部的寄存器,这些处理都是以C的方式提供的。但如果我们打开相关的头文件时,比如AF32F405开发板本身的at32f402_405_board.c文件使用at32f402_405_board.h文件:
#ifndef __AT32F402_405_BOARD_H
#define __AT32F402_405_BOARD_H
#ifdef __cplusplus
extern "C" {
#endif
#include "stdio.h"
#include "at32f402_405.h"
...
#ifdef __cplusplus
}
#endif
#endif
在文件中可以看到
#ifdef __cplusplus
extern "C" {
#endif
和
#ifdef __cplusplus
}
#endif
这是预处理代码。有了这个预处理代码,就表示提供的源文件是支持C和C++混合编程。也就是说,你可以放心地在你的程序中加入C++代码,并使用系统函数,而不用担心编译出错。雅特力提供的所有源文件是都支持C和C++混合编程的,因此我们就不用这方面花功夫了。
除了预处理,我们还需要改变工程的一些编译选项,以方便混合编译。点击魔术棒,
在Target选项Tab中修改编译选项Version 5 为 6
在C/C++所在的Tab中修改选项:
就这两项就行,其它的什么都不用改,使用原来例程默认的选项。
接下来我们在工程中加入main.cpp文件,在main.cpp中加入以下代码:
#include "at32f402_405_board.h" #include "at32f402_405_clock.h" class LED_Class{ private: gpio_type *port; uint16_t pins; crm_periph_clock_type periphClock; public: LED_Class(gpio_type *port, uint16_t pins, crm_periph_clock_type clock){ LED_Class::port = port; LED_Class::pins = pins; LED_Class::periphClock = clock; } void Init(void){ gpio_init_type gpio_init_struct; /* enable the led clock */ crm_periph_clock_enable(periphClock, TRUE); /* set default parameter */ gpio_default_para_init(&gpio_init_struct); /* configure the led gpio */ gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT; gpio_init_struct.gpio_pins = pins; gpio_init_struct.gpio_pull = GPIO_PULL_NONE; gpio_init(port, &gpio_init_struct); } void Open(void){ port->clr = pins; } void Close(void){ port->scr = pins; } void Toggle(void){ port->togr = pins; } }; int main(void) { system_clock_config(); at32_board_init(); uart_print_init(115200); printf("Start main ...\r\n"); LED_Class LED1(GPIOF, GPIO_PINS_4, CRM_GPIOF_PERIPH_CLOCK); LED1.Init(); while(1) { LED1.Toggle(); delay_ms(500); } }
可以看到在代码中,引入了一个LED_Class类,是一个专门用于驱动GPIO为LED用的类。在构造函数中,指明所在GPIO口和使用的引脚,以及所在的时钟总线。在LED_Class中提供了Init初始化函数和Open、Close、Toggle函数。其中的处理使用了底层函数,或者直接操纵寄存器。在main函数中在必要的处理之后,声明一个LED_Class对象LED1,并在while循环中周期亮灭LED(开发板上的红色LED)
在编译之前还有一件事儿要做,那就是把原来的main.c中的main函数改名,随便改成什么名字都行,就是不能再用main了,我改成了main_old,然后对工程进行编译、下载,就可以看到改造后的成果了。
使用面向对象方式开发程序,是有很多优点的,比如逻辑结构清晰,便于理解等,但在实际开发中,尤其是面对资源比较少的单片机,还是不推荐使用C++编程的。原因在于使用C++编程占用的资源,比C大。作为尝试,使用面向对象的方式开发,学习的意义,大于实际应用。至于以后,随着单片机本身提供的资源足够大,用面向对象的方式开发单片机应用,还有很值得期待的。