C 表示微芯片上引脚的宏
我正试图用C语言为atmega微控制器编写一些代码,我有一个用于设置单个位的工作宏C 表示微芯片上引脚的宏,c,macros,atmega,C,Macros,Atmega,我正试图用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)) 在最终的代码中,将有更多这
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))
在最终的代码中,将有更多这些宏用于不同的外围设备,这只是一个简化的示例
问题是我不知道如何定义宏PORT
和DDR
,
这样我就可以像这样使用发光二极管
(或按钮
)宏:
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)
typedef struct
{
uint8_t PIN_0: 1;
uint8_t PIN_1: 1;
uint8_t PIN_2: 1;
uint8_t PIN_3: 1;
uint8_t PIN_4: 1;
uint8_t PIN_5: 1;
uint8_t PIN_5: 1;
uint8_t PIN_6: 1;
uint8_t PIN_6: 1;
} REG_t;
#define MY_PINB (*(volatile REG_t *) &PINB)
#define MY_DDRB (*(volatile REG_t *) &DDRB)
#define LED (MY_PINB.PIN0)
LED = 0;
这就是我的动机:
DDRB
行控制管脚的方向(管脚是输入还是输出),PORTB
行设置输出引脚的逻辑值。因为这两行影响同一个管脚,所以我想在1个位置选择管脚(
#define LED…
)
随后在代码中,两种操作(配置方向和设置输出值)仅使用符号名称(LED
,按钮
)
宏DDRB
和PORTB
必须能够进一步扩展(而不仅仅是一次),因为它们是在外部头中定义的(不在我的控制之下)。
我还被一个事实困住了:使用
##
进行连接会阻止宏的进一步扩展。您可以编写宏,使预处理器能够扩展
BIT_SET(DDR(LED), 1);
到
如前所述,但这不是你真正想要的BIT_SET()
本身就是一个宏,您希望最终根据另一个展开所获得的参数得到展开该宏的结果。这是你不能拥有的。在执行任何扩展之前,预处理器会将宏参数分配给宏参数,并且根据您为宏BIT_SET()
给出的定义,预处理器应始终拒绝您提出的参数数目错误的调用
更新为添加:
另一方面,间接标记粘贴的常用技巧是将其包装在双层宏中。例如,预处理器扩展宏堆栈
#define CONCAT2(x, y) x ## y
#define CONCAT(x, y) CONCAT2(x, y)
#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))
#define DDRB somevar
#define LED_CODE B
#define LED_BIT 5
#define X_BIT_SET(x, y, v) BIT_SET(CONCAT(x, y ## _CODE), y ## _BIT, v)
X_BIT_SET(DDR, LED, 0)
#定义CONCAT2(x,y)x##y
#定义CONCAT(x,y)CONCAT 2(x,y)
#定义位掩码(b)(0x01我认为这样做应该可以:
免责声明:未经测试,可能有问题,但你知道
// Use of PINB, PINC, ... macros and PINB0, PINB1, ... macro from avr/io.h
#define LED PORT_PIN(PINB, PINB0)
#define PORT_PIN(port, pin) (((unsigned int) (&(port) - &PINB) / (unsigned int) &PINB) \
<< 4 + (pin))
#define DDR(port_pin) *(((port_pin) >> 4) & 0xf) \
* (unsigned int) (&PINC - &PINB) + &PINB))
#define PORT(port_pin) *(((port_pin) >> 4) & 0xf) \
* (unsigned int) (&DDRC - &DDRB) + &DDRB))
#define PIN(port_pin) ((port_pin) & 0xf)
STATIC_ASSERT(&DDRC - &DDRB == &PINC - &PINB);
STATIC_ASSERT(sizeof PINB == 1 && sizeof DDRB == 1);
作为旁注,同样,根据编译器的不同,您也可以执行以下操作:
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)
typedef struct
{
uint8_t PIN_0: 1;
uint8_t PIN_1: 1;
uint8_t PIN_2: 1;
uint8_t PIN_3: 1;
uint8_t PIN_4: 1;
uint8_t PIN_5: 1;
uint8_t PIN_5: 1;
uint8_t PIN_6: 1;
uint8_t PIN_6: 1;
} REG_t;
#define MY_PINB (*(volatile REG_t *) &PINB)
#define MY_DDRB (*(volatile REG_t *) &DDRB)
#define LED (MY_PINB.PIN0)
LED = 0;
然后您可以像这样访问PIN:
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)
typedef struct
{
uint8_t PIN_0: 1;
uint8_t PIN_1: 1;
uint8_t PIN_2: 1;
uint8_t PIN_3: 1;
uint8_t PIN_4: 1;
uint8_t PIN_5: 1;
uint8_t PIN_5: 1;
uint8_t PIN_6: 1;
uint8_t PIN_6: 1;
} REG_t;
#define MY_PINB (*(volatile REG_t *) &PINB)
#define MY_DDRB (*(volatile REG_t *) &DDRB)
#define LED (MY_PINB.PIN0)
LED = 0;
最后,正如一些评论所建议的,我决定使用内联函数而不是宏。
这是我的结果:
//I/O端口的枚举
类型定义枚举
{
伊欧布,
爱娥克,
伊奥
}ioPort;
//表示一个I/O引脚的结构
类型定义结构
{
ioPort端口;
uint8_t bitIx;
}碘油蛋白;
//将ioPort转换为相应的PORTx指针
内联易失性uint8_t*端口(ioPort iop)
{
开关(iop)
{
案例IO_B:退货和港B;
案例IO_C:退货和港交;
案例IO\U D:退货和退货;
默认值:返回NULL;
}
}
//将ioPort转换为相应的DDRx指针
内联易失性uint8_t*ddr(ioPort iop)
{
开关(iop)
{
案例IO_B:退货和DDRB;
案例IO_C:退货和DDRC;
案例IO\U D:退货和DDRD;
默认值:返回NULL;
}
}
//将端口的一位设置为给定值
静态内联无效位集(易失性uint8\u t*端口、常量uint8\u t比特、常量uint8\u t值)
{
如果(端口)
{
如果(值)
*port |=(1L)使用内联函数不是更简单吗?@WeatherVane你可能是对的,这可能是一个更好的方法。一个端口枚举,也许是一个将端口和pin保持在一起的结构。让编译器为我做优化。我想你可以试着改变一下,例如#定义LED(p)p(B,5)
,#定义端口(a,b)端口#a
,#定义引脚(a,b)(b)
,位集(LED(端口),LED(引脚))
。虽然丑陋,但我反对使用宏,除了简单的参数替换之外,最终结果是创建一个“新的”跟随您的人必须学习或解开的语言。我只会在没有可用语言语法的情况下使用它们。@WeatherVane:访问这些类型寄存器的讨厌之处在于,许多MCU供应商倾向于使它们完全非正交,在mostl阵列中具有稍微不同的布局和功能集y等价于外围设备。这可能会节省硅空间,但也会使编写通用代码变得复杂,在尝试这种方法时,我经常会使用宏黑客来按名称绑定东西,或者使用无休止的专门化。在这种有限的情况下,干净的方法当然是可行的,但当需要指定int时,事情会变得棘手例如,端口B的errupt向量。通常,这是TI在寄存器中定义其位的方式,并通过将这些定义组合在一起定义外围设备的寄存器集。