用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标准推荐的终端电阻阻值。 |