我正在寻找一种使用gcc 4.8.4将嵌入式设备寄存器传递给C++模板的方法.在描述嵌入式器件的数据手册中,寄存器的地址通常作为原始存储器位置给出(例如,0x40008000).
当我测试软件时,我想使用静态整数作为寄存器来查看寄存器值是否设置正确.
所以基本上一个设备外围设备的包装器归结为一个类,其寄存器地址作为模板参数给出:
template < volatile std::uint32_t* Reg >
struct peripheral {};
Run Code Online (Sandbox Code Playgroud)
测试工作正常:
std::uint32_t reg;
peripheral< ® > mocked;
Run Code Online (Sandbox Code Playgroud)
但是当我想用固定的数据表给出地址时,实例化模板:
peripheral< reinterpret_cast< std::uint32_t* >( 0x40008000 ) > mocked;
Run Code Online (Sandbox Code Playgroud)
gcc抱怨:could not convert template argument '1073774592u' to 'volatile uint32_t* {aka volatile long unsigned int*}.clang并没有抱怨这个.
如果我使用给定为整数的地址作为模板参数,我在测试期间使用模拟寄存器的地址来实例化模板时遇到问题:
template < std::intptr_t Reg >
struct peripheral {};
std::uint32_t reg;
peripheral< reinterpret_cast< std::intptr_t >( ® ) > mocked;
Run Code Online (Sandbox Code Playgroud)
这导致了error: conversion from pointer type 'uint32_t* {aka long unsigned int*}' to arithmetic type 'intptr_t {aka int}' in a constant-expression.
我可以想到两个解决方案:
1)使用指针作为模板参数,使用全局变量作为寄存器,并使用某些链接器脚本魔法修复寄存器的地址.
2)使用特殊寄存器类型,这些寄存器类型具有与外围模板的公共接口,但是两个非常不同的实现用于测试和实际应用.
但我正在寻找一种更简单的方法来实现这一目标.任何想法,指针或评论?
我的解决方案如下所示:
template < class Name = int, typename T = std::uint32_t, T* Value = nullptr >
class mocked_register
{
public:
static void set( T v )
{
*address() = v;
}
static T get()
{
return *address();
}
static volatile T* address()
{
static T internal_;
return Value ? Value : &internal_;
}
};
Run Code Online (Sandbox Code Playgroud)
其中 Name 应该是使实例化不同于其他实例化的任何类型。当定义多个类型时,可以使用以前定义的类型作为名称:
typedef mocked_register<> START;
typedef mocked_register< START > STOP;
typedef mocked_register< STOP > COUNT;
Run Code Online (Sandbox Code Playgroud)
为了测试,该类型保留一个“半”静态变量来保存寄存器值。对于 ARM 架构,我有一些情况,使用寄存器数组会很有帮助。在这种情况下,该Value参数可用于提供外部数组:
std::uint32_t capture_regs[ 4 ];
typedef mocked_register< SHUTDOWN, std::uint32_t, capture_regs > CAPTURE;
Run Code Online (Sandbox Code Playgroud)
对于生产部分,模板要简单得多:
template < std::uint32_t Register >
struct addressed_register
{
static void set( std::uint32_t value )
{
*reinterpret_cast< volatile std::uint32_t* >( Register ) = value;
}
static std::uint32_t get()
{
return *reinterpret_cast< volatile std::uint32_t* >( Register );
}
};
Run Code Online (Sandbox Code Playgroud)
在这两种情况(测试和生产)中,设备抽象采用一组模板参数并将它们用作寄存器:
template <
class OUTSET,
class OUTCLEAR,
class DIRSET,
class DIRCLR,
class PIN_CNF,
std::uint8_t Nr >
struct pin
{
static void init_as_input()
{
DIRCLR::set( 1 << Nr );
}
};
Run Code Online (Sandbox Code Playgroud)
如果要实现对 T 的赋值和隐式转换,可以添加更多类似寄存器的语法(但我不太喜欢这个想法):
START start;
COUNT count;
start = 1;
std::uint32_t c = count;
Run Code Online (Sandbox Code Playgroud)