使用C++写嵌入式代码 - 设备模板的声明和定义

将片上外设进行封装,可以使用如下三种技术:

  1. 使用类定义外设,然后创建对象。
    1. POD类实现
    2. 普通的类
  2. 静态模板(所有的成员都是静态的)

这两种模板共同的特点是不需要动态分配内存,区别在于一个是静态访问成员,一个是对象方法访问,POD类型无法添加自定义的数据成员。

将一个MCU的全部寄存器声明好以后,如果是简单的mcu,那么就可以直接使用了,例如:PORTA.DDR.v |= 0x01 或者USART0.UCSRC.UPM = 0x00;但是总有一些重复步骤和相关的代码,我们就可以将其封装起来,例如异步计数器的配置步骤就是可以复用的。C++可以使用对象封装这些操作,但遗憾的是,像avr编译器并不支持对象的生成和删除,定义好类之后,是不能用的,因为未定义delete操作符和其他基础的类库,这时可以自定义或者引用一些第三方的类库来解决这些问题,如果不想引用第三方类库的话,那么使用静态类模板也是一个不错的选择。如下所示:

template<typename U, volatile U* u, bool base = false>
class USART {
public:
    struct SerialConfig {
        word baud = 115200;
        byte data = USART_DATABIT_8;
        byte stop = USART_STOP_1;
        byte parity = USART_PARITY_DISABLE;
    };
    __f__ void init(const SerialConfig& conf) {
        ...
    }
    __f__ hword calUBRR(const word baud) {
        ...
    }
    ... // 其他操作
private:
};

使用模板参数传递寄存器是为了提高代码效率,base是为了复用模板,__f__是一个宏,他将函数成员定义成静态的。在定义完基本模板后,可以对其进行功能扩展或者功能的特化。例如,在初始化时,除了对USART功能寄存器进行设置之外,还要设置rx,tx管脚,这时,可以利用模板的特化技术来实现:

template<>
void u0::init(const SerialConfig& conf) {
    rx::init(INPUT);
    tx::init(OUTPUT);
    u0_base::init((u0_base::SerialConfig&) conf);
}

template<>
void u1::init(const SerialConfig& conf) {
    ...
}

// 其实可以再次简化,在模板参数总增加管脚定义。

usart0h和usart1的初始化函数可以加进对管脚的初始化,u0_base就是复用模板,我不用在写一遍USART的基本的初始化代码了。使用方法如下:

using u0 = USART<USART0_t, &USART0>;

u0::SerialConfig sc;
sc.baud = 115200;
sc.data = USART_DATABIT_8;
sc.parity = USART_PARITY_DISABLE;
sc.stop = USART_STOP_1;
u0::init(sc);
u0::start();

先声明一个别名,省得打那么长的类名字符:),然后先初始化,在启动,......

转载于:https://my.oschina.net/u/182236/blog/1836509

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值