高性能裸金属抽象

时间:2016-02-15 23:57:55

标签: c++ templates arm embedded bare-metal

最近,我们想到了使用模板为裸机开发创建通用的高性能抽象。

通常每个芯片制造商都提供这样的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);
  }
};

所有这一切的一点是,我们可以在一个平台上从单个外围进行抽象,从而为具有相同结构的所有外围设备创建通用驱动程序,例如定时器,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,但是将高写入TxPin将与汇编程序一样高效,所有这些都不使用宏。

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

我的问题是,是否有任何可以保持效率的解决方法?(重写寄存器定义除外) 在C ++ 11中,我认为使用constexpr来创建函数,该函数将获得静态结构的地址,然后可以将其用作模板参数。不幸的是,我们不能指望C ++ 11的可用性。有任何想法吗?我们需要修改/编写自己的寄存器定义吗?

2 个答案:

答案 0 :(得分:1)

很抱歉,但很难理解您的实际需求。如果我理解正确,你需要一种通用的方法来从第三方标题提供的指针中获取特定结构的偏移量(假设你知道结构的对齐方式)。 如果您声称可以使用C ++ 11 constexpr函数实现目标,请尝试使用C ++ 03模板。

我想你需要引入一个更高级别的包装器,将指针转换为offset:

$mail = new PHPMailer;

$mail = new PHPMailer();  // create a new object
$mail->IsSMTP(); // enable SMTP
$mail->SMTPDebug = 4;  // debugging: 1 = errors and messages, 2 = messages only
$mail->SMTPAuth = true;  // authentication enabled
$mail->SMTPSecure = 'ssl'; // secure transfer enabled REQUIRED for GMail
$mail->Host = 'smtp.gmail.com';
$mail->Port = 465; 
$mail->Username = 'baliwisata.email@gmail.com';  
$mail->Password = 'SECRET !';   

$mail->From = 'baliwisata.email@gmail.com';
$mail->FromName = 'Bali Wisata';
$mail->addAddress($companyEmail);
$mail->addReplyTo($this->email);

$mail->isHTML(true);    

然后使用:

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

作为旁注,我建议您阅读Practical Guide to Bare Metal C++。它将为您提供有关实现通用异步定时器,uarts和其他外设的一些想法。

答案 1 :(得分:0)

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

要在同一芯片上处理相同类型的多个硬件外围设备,通常会将第一个寄存器的地址作为参数,以使它们分开。这似乎是你的代码正在做的事情。

为了进一步提取抽象级别,您可以创建一个抽象基类“UART”,它包含所有UART的通用函数,例如设置波特率和通信格式,如果需要,设置硬件握手,发送,接收等等。然后,所有UART驱动程序都必须继承基类功能接口。

然后,应用程序的调用者不需要知道或关心特定硬件外设如何在给定的MCU上工作。呼叫者应用程序将完全可移植。

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

相关问题