打印
[STM32]

用C++做单片机开发

[复制链接]
5311|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
13925089531|  楼主 | 2018-6-15 16:23 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
用C++做开单片机开发
做了多年的单片机开发,我们早已习惯了C语言+库函数的方式,偶尔在需要的地方加入几句汇编。然而我们还不知足,大部分编译器已经支持汇编、C语言和C++编译了,这次让我们体验一下用C++开发。当然,前提是牺牲那么一点效率。

在这里我们先介绍一下作者使用的开发环境(发一点小广告):单片机是ELMOS公司的E703.15,内核类似于MSP430(兼容MSP430指令),仿真器为MAZ JTAG,集成开发环境为IAR for MSP430 6.4,PC采用Windows7 32位旗舰版。接下来主要介绍用C++实现2个串口,并利用C++的封装、集成和多态的特性,复用代码。

1.1 串口类设计分析
当然,用上了C++,我们先用封装特性,把串口的数据和操作封装为类(class)。

设计前我们分析一下,E703.15有2个串口,2个串口有相同的操作,但是有不同的硬件寄存器如引脚等。设计串口类的时候,我们采用基类和派生类的方式设计。

✦  基类:封装串口变量并定义串口的操作;
✦  派生类:根据串口硬件特性重载硬件相关操作。



1.2 串口基类设计
✦  数据:串口号;
✦  操作:串口初始化(不同串口不同,虚函数);
✦  操作:串口发送一个字节;
✦  操作:串口接收一个字节;
✦  操作:串口发送字符串。
class CUartBase {
public:
    CUartBase(uart_num_t eUart);
   ~CUartBase(void);
   
public:
    void     uartByteSend(uint8_t ucData);
    uint8_t uartByteRecv(void);
    void     uartStrWrite(char *pcStr);
   
public:
    virtual void uartInit(void) = 0;
   
protected:
    uart_num_t  eUartNum;
};


1.2.1 串口基类定义
在介绍定义之前,先说明一下,具体的实现是调用了ELMOS提供的E703.15的外设库,如uart_transmit_byte函数。由于操作都比较简单,所以不进行详细介绍。
CUartBase::CUartBase(uart_num_t eUart)
{
    eUartNum = eUart;
}

CUartBase::~CUartBase(void)
{
}

void CUartBase::uartByteSend(uint8_t ucData)
{
    uart_transmit_byte( eUartNum, ucData, true );
}

uint8_t CUartBase::uartByteRecv(void)
{
    uint8_t ucData;
   
    uart_receive_byte( eUartNum, &ucData, true );               
   
    return ucData;
}

void CUartBase::uartStrWrite(char *pcStr)
{
    while ( '\0' != *pcStr ) {
        uartByteSend( *pcStr++ );
    }
}


1.3 串口0派生类
定义了基类之后,我们用上C++继承的特性,派生出串口0和串口1,由于串口0和串口1除了硬件初始化不同之外,其他操作相同,所以只介绍串口0派生类。同样先介绍原型,再介绍定义。

1.3.1 串口0原型
由于已经设计了基类,代码复用程度很高,所以派生类设计非常方便,只需重载硬件初始化虚函数即可(uartInit函数)。需要注意:构造函数使用了默认参数UART0。
class CUart0 : public CUartBase {
   
public:
    CUart0(uart_num_t eUart = UART0);
   ~CUart0(void);

public:
    void uartInit(void);
};


1.3.2串口0定义
只要重载硬件初始化函数即可,其他操作集成自基类,具体定义如下。uartInit函数在派生类实现(基类定义为虚函数),即利用C++的多态。
CUart0::CUart0(uart_num_t eUart) : CUartBase(eUart)
{
    uartInit();
}

CUart0::~CUart0(void)
{
}

void CUart0::uartInit(void)
{
    sys_state_module_enable( SYS_STATE_MODULE_UART0, false );   /* 禁用串口         */
    sys_state_module_enable( SYS_STATE_MODULE_UART0, true );   /* 使能串口           */

    uart_init( eUartNum, UART_SPEED_115200, UART_FRAC_115200, UART_DATA_LEN_8, UART_PARITY_EVEN, UART_ONE_STOP );
    uart_fifo_clear( eUartNum, true, true );
   
    sys_state_module_enable( SYS_STATE_MODULE_IOMUX_CTRL, true ); /* 使能串口         */
    iomux_ctrl_select( IOMUX_IO_0, IOMUX_FUNC_2ND );               /* 使能发送引脚      */
}


1.4 主函数测试
定义了串口基类和派生类之后,在主函数new出2个串口进行测试,测试代码如下(定义基类指针,用来存放派生类,可以利用多态的特性,为了简化,这里并没有体现出多态)。
#include "uart0.h"
#include "uart1.h"

int main (void)
{
    CUartBase *phUart0 = new CUart0();
    CUartBase *phUart1 = new CUart1();
   
    phUart0->uartStrWrite( "Uart0: Hello world\r\n" );
    phUart1->uartStrWrite( "Uart1  Hello world\r\n" );
   
    while(1);
}


1.5 温馨提示
虽然IAR for MSP430 6.4支持C++开发,但是并不完全支持C++的所有特性,大致支持特性和不支持特性如下,具体可参考编译器帮助文档。

支持特性:
✦   类(class);
✦   多态(Polymorphism);
✦   重载(Overloading);
✦   new和delete;
✦   模板(Templates);
✦   名字空间(namespace)。

不支持特性:
✦   异常(Exception);
✦   运行时类型识别(RTTI)。

1.6 结束语
根据以上介绍,是否发现用C++开发单片机,利用封装、集成和多态特性,可以增加代码的复用率,提高系统的稳定性呢。有兴趣的童鞋可以在自己的开发环境中试一下C++开发,换个角度看待单片机。
提高信号质量
信号在较高的转换速率情况下,信号边沿能量遇到阻抗不匹配时,会产生信号反射;传输线缆横截面的几何结构发生变化,线缆的特征阻抗会随之变化,也会造成反射。

在总线线缆的末端,阻抗急剧变化导致信号边沿能量反射,总线信号上会产生振铃,若振铃幅度过大,就会影响通信质量。在线缆末端增加一个与线缆特征阻抗一致的终端电阻,可以将这部分能量吸收,避免振铃的产生。
我们进行了一个模拟试验,位速率为1Mbit/s,收发器CANH、CANL接一根10m左右的双绞线,收发器端接120Ω电阻保证隐性转换时间,末端不加负载。末端信号波形如图6,信号上升沿出现了振铃。
若双绞线末端增加一个120Ω的电阻,末端信号波形明显改善,振铃消失,如图7。
一般在直线型拓扑中,线缆两端即是发送端,也是接收端,故线缆两端需各加一个终端电阻。
为什么选120Ω
任何一根线缆的特征阻抗都可以通过实验的方式得出。线缆的一端接方波发生器,另一端接一个可调电阻,并通过示波器观察电阻上的波形。调整电阻阻值的大小,直到电阻上的信号是一个良好的无振铃的方波,此时的电阻值可以认为与线缆的特征阻抗一致。更多学习交流可以加Q3472880374
大部分汽车线缆都是单线的。如果你采用两根汽车使用的典型线缆,将它们扭制成双绞线,就可根据上述方法得到特征阻抗大约为120Ω,这也是CAN标准推荐的终端电阻阻值。

相关帖子

沙发
一路向北lm| | 2018-6-15 21:13 | 只看该作者
C++封装是很方便,代码简洁,就怕在项目中不够高效啊,UCOS中的任务切换还是用汇编执行的快。

使用特权

评论回复
板凳
McuPlayer| | 2018-6-15 23:27 | 只看该作者
我现在STM32已经全面转向C++了,维护难度大大降低,当然也牺牲了一点效率

使用特权

评论回复
地板
mcu5i51| | 2018-6-17 11:35 | 只看该作者
以后全面支持C++了就可以放心用了,

使用特权

评论回复
5
515192147| | 2018-7-9 10:37 | 只看该作者
我们的开发板 都是 用 C++ 开发的,有空 下载个 研究研究
通过CAN现场总线可Web网页浏览监控的开发板
https://bbs.21ic.com/icview-1945634-1-1.html

使用特权

评论回复
6
airwill| | 2018-7-13 21:41 | 只看该作者
这么说, C++ 确实会影响效率, 不过不知道影响有多少?
嵌入式的 C++ 是经过裁减了的, 据说模版是不支持的. 不知道还裁掉了什么

使用特权

评论回复
7
515192147| | 2018-11-30 18:04 | 只看该作者
airwill 发表于 2018-7-13 21:41
这么说, C++ 确实会影响效率, 不过不知道影响有多少?
嵌入式的 C++ 是经过裁减了的, 据说模版是不支持的.  ...

不会影响  效率的,我们 一直 用 C++ 的,只会更简单

使用特权

评论回复
8
515192147| | 2018-11-30 18:05 | 只看该作者
airwill 发表于 2018-7-13 21:41
这么说, C++ 确实会影响效率, 不过不知道影响有多少?
嵌入式的 C++ 是经过裁减了的, 据说模版是不支持的.  ...

放心吧 ,不会影响  效率的,我们 一直 用 C++ 的,只会更简单的

使用特权

评论回复
9
airwill| | 2018-12-3 07:26 | 只看该作者
515192147 发表于 2018-11-30 18:05
放心吧 ,不会影响  效率的,我们 一直 用 C++ 的,只会更简单的

不会影响  效率的?  你是怎么对比的呢,怎么得到这样的结论的呢

使用特权

评论回复
10
john_lee| | 2018-12-3 10:38 | 只看该作者
本帖最后由 john_lee 于 2018-12-3 10:40 编辑
airwill 发表于 2018-7-13 21:41
这么说, C++ 确实会影响效率, 不过不知道影响有多少?
嵌入式的 C++ 是经过裁减了的, 据说模版是不支持的.  ...

没有什么“嵌入式的C++”的说法,有的只是各家厂商对C++标准的实现程度多少和扩展方言问题。
C++ 同时支持各种编程范式:过程式(procedural)、函数式(functional)、面向对象(object-oriented)、泛型(generic),这些范式中,只有面向对象稍微有一些隐藏的开销外,其它三种都是0开销(zero-cost)的,而且就算是使用面向对象范式所带来的附加开销,比起它带来的好处,是微不足道的。
用 C++,并不是说必须用到 C++ 全部泛型和特性,而是要根据具体需求有选择性的使用最少的范式,C++ 编程,是做减法而不是做加法。
C++ 的0开销的抽象能力,是 C 无法望其项背的,如果使用得当,完全可以做出各种灵活高效的库和框架。这是本人在5年前做的一个小项目,一个最简的 USB Audio 示例,基于本人自制的一个 C++ USB 框架,加上应用部分全部的代码就只有1.8K,这是任何基于 C 的框架所无法达到的。
C++ 的缺点是过于庞杂(这其中有很多历史包袱)导致学习异常艰难,还有就是很多人认为它语法很丑陋。

使用特权

评论回复
11
airwill| | 2018-12-3 22:40 | 只看该作者
实际上, 要从最终执行代码的效率, C 还是优于 C++ 的.
当然看源代码, 对于较大的工程, C++ 也许会有些优势  

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

33

主题

34

帖子

0

粉丝