2016-02-15 40 views
2

最近,我们有了一个想法,使用模板创建裸机开发的通用高性能抽象。高性能裸金属抽象

通常每芯片制造提供了C头是这样的:

//Following structure is POD so we can rely on its memory layout 
struct Periphery{ 
    volatile uint32_t reg1; 
    volatile uint32_t reg2; 
}; 

#define PERIPHERY0BASE 0x000000ab //address where does registers of periphery start 
#define PERIPHERY1BASE 0x000000cd 
static Periphery* PERIPHERY0 = (Periphery*)(PERIPHERY0BASE); 
//or 
#define PERIPHERY1 (Periphery*)(PERIPHERY1BASE) 

我们的想法是再创造司机这是具体的,但一般对于给定的外围类型的平台:

template<int addr> 
PeripheryDriver{ 
    static inline void doSomething(int foo){ 
    (PERIPHERY0*)(addr)->reg1 = foo; 
    } 
} 
//typedefs for different peripheries of the same type 
typedef Periphery<PERIPHERY0BASE> Periphery0; 
typedef Periphery<PERIPHERY1BASE> Periphery1; 

这将是再用于平台独立模块,如下所示:

template<class P> 
class DriverUser{ 
    DriverUser(){ 
    P::doSomething(0x00); 
    } 
}; 

Po整体来说,我们可以从一个平台上的单个外围进行抽象,从而为所有具有相同结构的周边创建通用驱动程序,例如一个处理器系列中的定时器,Uarts等。此外,它允许我们创建高性能的平台独立的模块如我们可以例如创建高性能引脚访问的是用汇编语言编写,但同时高度可重用的高效:

//normally would be in PCB specific header 
typedef Pin<Port0, Pin0> Pin0; 
typedef Pin<Port1, Pin7> Pin1; 

//application specific code 
typedef Pin0 TxPin; 
typedef Pin1 RxPin; 

void main(){ 
    SoftwareUart<TxPin,RxPin> my_uart(115200); 
    my_uart.send("hello world"); 
} 

然后可以实现SoftwareUart完全独立于平台,但写入High到TxPin将与汇编器一样高效,而不使用宏。

我们的问题是,在某些平台上,制造商的头文件不包含 宏,它们将为地址定义名称,但只有宏已经是地址已经转换为指针的宏,因此我们不能将它们用作模板参数。 如PERIPHERY0BASE是不是只能PERIHPERY0

我的问题是,如果有可能是其将保持效率?(除改写寄存器定义)在C++ 11我想用constexpr创建功能,将任何解决办法 获得可作为模板参数的静态结构的地址。不幸的是,我们不能指望C++ 11的可用性。有任何想法吗?我们是否需要修改/编写我们自己的注册欺诈?

+0

对不起,题外话。而且你的概念似乎完全忽略了内置模块,设备特定等。 – Olaf

+0

在寄存器层的抽象并没有多大意义,因为很难找到任何跨平台匹配的外设寄存器。在芯片供应商产品线或某个供应商的一系列产品中,确定您可能会发现它们更常见。如果你想抽象一个uart,你可以用一个init来抽象它,写字符,读字符层不在寄存器层。 gpio,定时器等也是如此。 –

+0

您的所有代码都已损坏,没有'volatile'声明。 – Lundin

回答

1

对不起,但很难理解你真正需要什么。如果我正确地理解了你,你需要一个通用的方法来从第三方头文件提供的指针(假设你知道结构的对齐)中得到特定结构的偏移量。 如果您声称可以使用C++ 11 constexpr函数实现目标,请尝试为C++ 03使用模板。

我想你需要引进转换指针变成一个更高层次的包装偏移:

template <typename T, T ptr, unsigned TAlignmentMask> 
struct AddrRetriever 
{ 
    static const int value = (int)ptr & TAlignmentMask; 
} 

然后用:

typedef Periphery< 
    AddrRetriever< 
     volatile void*, // use the type of the pointer vendor provides 
     PTR_FROM_VENDOR, 
     KNOWN_ALIGNMENT_MASK>::value 
> Periphery0; 

作为一个方面说明,我想推荐阅读Practical Guide to Bare Metal C++。它会给你一些想法,就像你想要的那样实现通用异步定时器,飞镖和其他外设。

+0

感谢那种模式:template 是我一直想念的。 +感谢有用的链接。 –

0

您应该始终在硬件外设周围创建一个硬件抽象层。这意味着调用者不需要知道或关心寄存器的位和字节。换句话说,为给定MCU上的给定外设制作标准硬件驱动程序。

要在同一芯片上处理多个相同类型的硬件外设,通常需要将第一个寄存器的地址作为参数,以使它们分开。看起来这就是你的代码在做什么。要进一步提取抽象层次,可以创建一个抽象基类“UART”,该类包含所有UART的通用函数,例如设置波特率和通信格式,如果需要设置硬件握手,发送,接收等等上。你所有的UART驱动程序都必须继承基类函数接口。

然后,应用程序的调用者不需要知道或关心特定硬件外设在给定MCU上的工作方式。调用者应用程序将完全便携。

这是做专业固件设计的标准方法。通常它是用C完成的,但在C++中也可以做一些小心操作,而不会产生太多的静重(避免模板)。

+0

这正是我们一直在做的。传递引用或指向基类的问题是,你经常不能强制编译器优化掉函数调用,而模板不存在这样的问题。 –