我正在尝试用C编写一些用于atmega微控制器的代码,并且我有一个工作宏BIT_SET
用于设置一个位:
#define BIT_MASK(b) (0x01 << (b))
#define BIT_ON(p,b) ((p) |= BIT_MASK(b))
#define BIT_OFF(p,b) ((p) &= ~BIT_MASK(b))
#define BIT_SET(p,b,v) (v ? BIT_ON(p,b) : BIT_OFF(p,b))
现在我想定义代表I / O引脚的单行宏。类似的东西:
#define LED B,5 // an LED diode is connected to bit 5 of port B
#define BUTTON B,4 // a button is connected to bit 4 of port B
最终代码中将有更多这些宏用于不同的外围设备,这只是一个简化的例子。
问题我不知道如何定义宏PORT
和DDR
,
这样我就可以像这样使用LED
(或BUTTON
)宏:
BIT_SET(DDR(LED), 1); // which should expand to: BIT_SET(DDRB, 5, 1)
BIT_SET(PORT(LED), 0); // which should expand to: BIT_SET(PORTB, 5, 0)
这是我的动力:
DDRB
线控制引脚的方向(引脚是输入还是输出),
PORTB
行设置输出引脚的逻辑值
因为两条线都影响同一个引脚,所以我想在一个地方选择引脚(#define LED ...
)
稍后在代码中仅对两个操作使用符号名称(LED
,BUTTON
)(配置方向和设置输出值)。
宏DDRB
和PORTB
必须能够进一步扩展(而不仅仅是一次),因为它们是在外部标头中定义的(不在我的控制之下)。
此外,我被一个使用##
连接的事实所困,这阻止了宏的进一步扩展。
答案 0 :(得分:0)
最后,我决定使用内联函数而不是宏,正如一些评论所建议的那样。 这是我的结果:
// enumeration for I/O ports
typedef enum
{
IO_B,
IO_C,
IO_D
} ioPort;
// structure representing one I/O pin
typedef struct
{
ioPort port;
uint8_t bitIx;
} ioPin;
// converts ioPort to corresponding PORTx pointer
inline volatile uint8_t* port(ioPort iop)
{
switch(iop)
{
case IO_B: return &PORTB;
case IO_C: return &PORTC;
case IO_D: return &PORTD;
default: return NULL;
}
}
// converts ioPort to corresponding DDRx pointer
inline volatile uint8_t* ddr(ioPort iop)
{
switch(iop)
{
case IO_B: return &DDRB;
case IO_C: return &DDRC;
case IO_D: return &DDRD;
default: return NULL;
}
}
// sets one bit of a port to given value
static inline void bitSet(volatile uint8_t* port, const uint8_t bitIx, const uint8_t value)
{
if(port)
{
if(value)
*port |= (1L << bitIx);
else
*port &= ~(1L << bitIx);
}
}
// configuring where peripheral devices are connected
const ioPin led = {IO_C, 5};
const ioPin button = {IO_C, 6};
// (later in a code)
// setting direction of the pin with an led connected
bitSet(ddr(led.port), led.bitIx, 1);
我必须承认,严格来说,这不是我原来问题的答案,因为我要求使用宏的解决方案。但我希望这对某人来说仍然有用。