【发布时间】:2015-11-23 20:05:41
【问题描述】:
我正在尝试用 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 行设置输出引脚的逻辑值。
因为两条线都影响同一个引脚,所以我想在 1 个位置选择引脚 (#define LED ...)
稍后在代码中仅对两个操作(配置方向和设置输出值)使用符号名称(LED、BUTTON)。
宏 DDRB 和 PORTB 必须能够进一步扩展(而且不仅仅是一次),因为它们是在外部标头中定义的(不受我的控制)。
我还被一个事实所困,即使用## 进行连接会阻止宏的进一步扩展。
【问题讨论】:
-
使用内联函数不是更简单吗?
-
@WeatherVane 你可能是对的,这可能是一个更好的方法。端口的枚举,可能是用于将端口和引脚保持在一起的结构。并让编译器为我做优化。
-
我想你试着把东西翻过来,即
#define LED(p) p(B, 5)、#define PORT(a,b) PORT##a、#define PIN(a,b) (b)、BIT_SET(LED(PORT), LED(PIN))。虽然丑得像罪。 -
我反对使用宏,除了简单的参数替换,最终结果是创建一种“新”语言,跟随你的人必须学习或解开。我只会在没有可用的语言语法时使用它们。
-
@WeatherVane:访问这些类型的寄存器令人讨厌的是,许多 MCU 供应商倾向于使它们相当非正交,在大多数等效外设阵列中布局和功能集略有不同。据推测,这节省了硅空间,但它也使编写通用代码变得复杂,在尝试它时,我经常以宏 hacks 来通过名称绑定事物或无穷无尽的专业化。在这种有限的情况下,干净的方法当然是可行的,但是当您需要为端口 B 指定中断向量时,事情就会变得很棘手。