Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 表示微芯片上引脚的宏_C_Macros_Atmega - Fatal编程技术网

C 表示微芯片上引脚的宏

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)) 在最终的代码中,将有更多这

我正试图用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))
在最终的代码中,将有更多这些宏用于不同的外围设备,这只是一个简化的示例

问题是我不知道如何定义宏
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在寄存器中定义其位的方式,并通过将这些定义组合在一起定义外围设备的寄存器集。