我正在尝试在AVR 16-AVR 128系列微控制器的库中编写宏set_clock()、clr_clock()等。
我有问题wiht添加字母V端口名称在一个宏。
在下面的代码中,我描述了当是一个地方时,如果启用了虚拟端口,预处理器应该在其中添加字母V。
我不能使用函数。虚拟端口在CPU时钟的1个周期内改变端口中的位,我需要它。所以我必须使用宏来生成最快的代码。
//=== From microcontroller toolchain header: ioavr128db64.h =========
/* Virtual Ports */
typedef struct VPORT_struct
{
register8_t DIR;
register8_t OUT;
register8_t IN;
register8_t INTFLAGS;
} VPORT_t;
#define VPORTA (*(VPORT_t *) 0x0000)
#define VPORTB (*(VPORT_t *) 0x0004)
#define VPORTC (*(VPORT_t *) 0x0008)
// ... etc.
//--- PORTS definitions (normal ports) -----
typedef struct PORT_struct
{
register8_t DIRSET;
register8_t DIRCLR;
// etc.
} PORT_t;
#define PORTA (*(PORT_t *) 0x0400)
#define PORTB (*(PORT_t *) 0x0420)
#define PORTC (*(PORT_t *) 0x0440)
// ... etc.
//========== My definitions ==================================
/* chceck if virtual ports are available */
// Caution! VVV is place where preprocessor should add letter V to choosen port
#ifdef VPORTA
#define VPORTS 1
#define set_DIR(x, y) VVV(x).DIR |= (y) /* should generate: VPORTA.DIR |= 3 */
#define clr_DIR(x, y) VVV(x).DIR &=~(y) /* should generate: VPORTA.DIR &=~ 3 */
/* etc. */
#else
#define VPORTS 0
#define set_DIR(x, y) (x).DIRSET = (y) /* should generate: PORTA.DIRSET = 3 */
#define clr_DIR(x, y) (x).DIRCLR = (y) /* should generate: PORTA.DIRCLR = 3 */
/* etc. */
#endif
//Caution! Its very important - must be like this!
#define PORT PORTA /* may be chanched e.g. PORTC */
int main(void)
{
//example
set_DIR(PORT, 3);
return 0;
}
字符串
当我在VVV的地方粘贴了一个字母V时,会发生错误:
Severity Code Description Project File Line
Warning implicit declaration of function 'V' [-Wimplicit-function-declaration] tests main.c 11
型
怎么能修好呢?
5条答案
按热度按时间2admgd591#
如果你必须使用一个宏来完成这个任务,那么你似乎需要引入一些间接的方法来完成token的连接。下面是三个宏的集合,它们可以完成我认为你会问的事情:
字符串
给定
#define VPORTA (*(VPORT_t *) 0x0000)
、#define PORT PORTA
和OP问题中建议的宏调用set_DIR(PORT, 3);
,这将扩展为(((*(VPORT_t *) 0x0000)).DIR |= (3));
。set_DIR
宏接受参数PORT
和3
,并将PORT
扩展为PORTA
(来自PORT
的#define
)在使用PORTA
和3
调用X_set_DIR
宏之前,X_set_DIR
宏将标记V
和PORTA
连接起来,然后调用XX_set_DIR
宏,参数为VPORTA
和3
。最后,XX_set_DIR
宏扩展为访问VPORTA
的#define
指示的结构体的DIR
字段的代码,即最终扩展为(((*(VPORT_t *) 0x0000)).DIR |= (3));
。更新
当我测试上面的代码时,我定义了
VPORTA
,但没有定义PORTA
。在@KamilCuk的评论之后,我发现包含PORTA
的定义会干扰set_DIR
宏的扩展。经过进一步的审查,似乎OP所需的解决方案是误导一般。除了使用宏在第一位(@gulpr)的可扩展性,OP选择机制的虚拟与正常端口没有意义。
特别是,OP代码有
#ifdef VPORTA
。这作为选择机制没有意义,因为工具链头文件已经定义了VPORTA
。OP将需要找到另一种方法来指示正在使用的是虚拟端口还是普通端口。最简单的方法是将
PORT
设置为所需的端口,如下所示:型
如果OP需要在虚拟和正常端口之间切换的机制,则可以使用
#define
来具体指示端口是虚拟端口还是正常端口:型
Here is a link to the Godbolt Compiler Explorer显示了上述宏的扩展。它可以工作(目前),但这是一个问题的解决方案。如果
A
或B
或任何其他端口选择器令牌在其他地方定义,这可能不会像预期的那样工作。如果你打算使用宏来实现这个功能,只需使用简单的方法,并将
PORT
定义为你想要的特定端口。但是你可能需要重新考虑是否需要使用宏。无论你做什么,对端口结构的实际访问都将在运行时发生。你从宏获得的任何性能提升都将是最小的,因为使用宏会带来所有问题。qhhrdooz2#
1.避免!根本不要使用那些宏。使用函数并信任你的编译器
1.不要在宏中取消引用这些指针
1.如果是硬件寄存器,则需要将其设置为
volatile
,因为您希望处理寄存器中存储的实际值(可能会被硬件或ASIC代码更改),而不是处理器寄存器中存储的值。字符串
示例生成代码:
手臂:
型
AVR:
型
https://godbolt.org/z/jbKaaPGWE
编辑
型
s4chpxco3#
将
#define set_DIR(x, y) x ## .DIR |= ## y
更改为#define set_DIR(x, y) ((x).DIR |= (y))
。第一:上面的代码生成的错误没有将PORT转换为VPORTA。
在宏处理中,
##
在结果被重新扫描以进行进一步的宏替换之前被处理,因此PORT
在被VPORTA
替换之前与另一个令牌组合。当##
被删除时,这不是问题。第二:联接时产生网点误差。
解析表达式是C主要处理的一部分。它不是预处理的一部分,包括宏替换,你不应该尝试使用
##
操作符。##
用于组合语法标记。在宏中,大多数情况下,你只是简单地编写C代码,就好像宏参数是普通的标识符一样,除了它们通常应该被括号括起来,这样,当参数是一个带有运算符的表达式时,它在宏扩展产生的代码中被视为括号中的一个项目。
ntjbwcob4#
字符串
这是不可能实现的。不可能部分扩展一个宏。
set_DIR(PORT
的参数中的PORT
是要么根本不扩展,在这种情况下它保持为PORT
,或者完全扩展为(*(PORT_t *) 0x0400)
。不可能先将宏“部分”扩展为PORT -> PORTA
,然后添加V
字符。最简单的方法是,只需为您的转换选择一个不同的唯一PORT名称,您可以在其中定义自己的端口。
型
bvhaajcl5#
因为在这个主题中有关于虚拟端口的额外讨论,我决定展示avr-gcc编译器:
字符串
在某些AVR系列微控制器(如AVR 128 DB 64)的引脚上使用按位操作时,可以使用单周期汇编指令:
型
结果:
型
在美国:
型