我试图为我正在使用的微控制器编写一个通用类。这些野兽使用<register prefix> <index> <suffix>
形式的寄存器并不罕见,例如UCSR0B
或TCCR1A
所以我编写了用于连接其参数以形成新标记的宏:
#define uart_is_enabled(i) (UCSR ## i ## B)
#define uart_putchar(i, c) UDR ## i = c
注意:这里我使用UART寄存器,但这只是一个例子,我没有尝试使UART工作,我已经为它编写了代码,我想提高。
编辑:对于好奇的,这里是UmelR0B等寄存器的定义,根据Atmel库:
#ifndef __SFR_OFFSET
# if __AVR_ARCH__ >= 100
# define __SFR_OFFSET 0x00
# else
# define __SFR_OFFSET 0x20
# endif
#endif
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
#define UCSR0B _SFR_IO8(0x25)
现在我想尝试使用模板类来处理这些宏而不需要针对每个可能的索引进行专门化:
template <const unsigned index>
class Uart
{
public:
static bool is_enabled() { return uart_is_enabled(index); }
static void putchar(uint8_t) { uart_putchar(index, c); }
};
当然,对于模板参数,uart<0>
或uart<1>
或任何unsigned int
,我会收到如下错误消息:
error: 'UCSRindexB' was not declared in this scope
这是因为宏参数被评估&#34; (如果我的理解是正确的,那么在编译模板参数值之前,请给我一个迂腐的赦免使用不正确的术语)。有没有办法让这项工作成为可能?
编辑:虽然Phil's answer 100%直接解决了我的问题,但我改变主意并选择了不同的方法。我认为C / C ++是一种需要冗长的语言,即最好声明要管理的所有案例。有些情况,像这样,偷工减料可能会产生更繁琐的代码 - 这当然是我的看法,YMMV。
因此,我通过duck-typing实现了某种形式的硬件抽象:我定义了许多硬件驱动程序类,因为我使用的Atmel处理器中有许多不同的UART - 我很幸运没有&#39 ;与Atmel微控制器中的许多UART类型一样,模型数量。
以下是LIN / UART驱动程序类的示例:
// lin.h (excerpt)
// Auto-detect the one and only serial interface (set UART mode)
#ifdef LINDAT
/*
* UART0 driver — for microcontroller which UART module is shared
* with LIN, e.g. ATmega64M1
*/
struct uart0
{
static INLINE void power_on() { power_lin_enable(); }
static INLINE void power_off() { power_lin_disable(); }
static INLINE void enable() { uart_enable(); }
static INLINE void disable() { uart_disable(); }
...
static void reset();
};
#endif
以下是定义一个或多个UART模块的微控制器的示例:
// uart.h (excerpt)
// Auto-detect first serial interface aka U[S]ART0
#ifdef UDR0
struct uart0
{
static INLINE void power_on() { power_usart0_enable(); }
static INLINE void power_off() { power_usart0_disable(); }
static INLINE void enable() { uart_enable(0); }
static INLINE void disable() { uart_disable(0); }
...
};
#endif
注意我仍然使用上面在这篇文章中介绍的C宏来概括寄存器处理......虽然有些人往往难以阅读,但我承认。
然后我编写了一个模板类(实际上是一个接口类),该参数是接口类继承的驱动程序类。
// Generic UART wrapper. Comes with a circular input buffer
template <class driver>
class tty : public driver
{
protected:
...
public:
static void putchar(char) { driver::putchar(c); }
static void power_off()
{
driver::disable();
driver::power_off();
}
...
};
根据检测到的寄存器名称包含特定于控制器的头文件:
#if defined(UDR0) || defined(UDR1) || defined(UDR)
#include <drv/uart.h>
#endif
#ifdef LINDAT
#include <drv/lin.h>
#endif
使用这种方法,特定于我正在编译的体系结构的驱动程序成员通过接口类公开。它允许我为尽可能多的处理器编写相对通用的代码,以支持单个应用程序:
typedef serio tty<uart0>; // same code for ATmega328p, ATmega64M1, ATtiny1634...
serio::putchar('a');
如果我想将我的应用程序专用于特定的微控制器,我也可以编写非常具体的代码。我只需要使用特定于arch的驱动程序成员。
对于通用方法,按照惯例,我的所有驱动程序类必须公开一定数量的公共成员才能使用该概念,但只需要完成一次,即添加对新微控制器的支持时。这确实是某种重复的任务(参见我的观点&#34;冗长&#34;),我的意思是(几乎)一遍又一遍地看起来相同的类型代码(用你希望支持的每个控制器冲洗/重复)但最终这是我想要的灵活性。
我还检查了生成的汇编代码,我可以确认优化器的工作做得非常好,特别是在我要求它内联代码的时候和地点。
答案 0 :(得分:2)
以下代码可以根据需要使用。首先评估宏,您必须为每个寄存器手动创建模板专业化。
// Simulate register names (to test on any compiler)...
static bool UCSR0B;
static bool UCSR1B;
static char UDR0;
static char UDR1;
与原始帖子相同的宏仍然有用
#define uart_is_enabled(i) (UCSR ## i ## B)
#define uart_putchar(i, c) UDR ## i = c
然后我们宣布一个模板。在这里我使用一个空类。您的默认实现可能会有所不同。
template <const unsigned index>
class Uart
{
};
此时,我使用宏来定义所有寄存器的特化。
// Macro that define a template specialization for a specific UART
#define UART(index) \
template <> \
class Uart<index> \
{ \
public: \
static bool is_enabled() { return uart_is_enabled(index); } \
static void putchar(uint8_t c) { uart_putchar(index, c); } \
}
// Define Uart<0>, Uart<1>... classes
UART(0);
UART(1);
最后,这是一个测试程序,显示它按预期工作
// Test program
void test()
{
Uart<0>::is_enabled();
Uart<1>::is_enabled();
// Uart<2>::is_enabled(); // Would not compile
Uart<0>::putchar('a');
Uart<1>::putchar('b');
// Uart<2>::putchar('c'); // Would not compile
}