我需要在寄存器GPIO0_SET_DATA23
中设置一些不连续的位。我想将这些位索引为0,1,...
目前我使用全局数组来保存位位置,#define SET_LED(X)
。
以下是.c
代码:
int led_bits[5] = {8, 9, 23, 24, 27};
以下是.h
代码:
extern int led_bits[];
#define SET_LED(X) (GPIO0_SET_DATA23 = 1 << led_bits[X])
如何在预处理器中没有全局数组的情况下实现这一点?我只用常数整数调用SET_LED
。我不会将其称为SET_LED(i)
。
答案 0 :(得分:1)
在C99或更高版本中,您可以使用数组文字(和static inline
函数):
static inline void SET_LED(int X)
{
GPIO0_SET_DATA23 = 1 << (int [5]){8, 9, 23, 24, 27}[X];
}
这设置了一个全局变量 - 这有点难看。最好使用:
static inline int SET_LED(int X)
{
return 1 << (int [5]){8, 9, 23, 24, 27}[X];
}
然后:
GPIO0_SET_DATA23 = SET_LED(4);
或者你需要的任何东西。这也允许您使用变量作为数字;使用#define LED0 8
等不允许这样做(尽管你明确声明不需要变量支持)。将边界检查添加到内联函数可能是个好主意。 #define
系统的一个优点是,如果您尝试SET_LED(6)
但没有LED6
宏,则代码将无法编译。
如果您正在使用没有C99或更高版本支持的嵌入式系统,则需要使用其他机制;这不适用于(大多数)C90编译器。
请注意,当您拥有优化编译器和函数的常量参数时,编译器将能够像您编写的那样计算值:
GPIO0_SET_DATA23 = 1 << 27;
当然,在编译时也将计算移位的值(如134,217,728 - 在逗号用作千位分隔符的语言环境中)。
答案 1 :(得分:0)
我不确定我是否完全理解你试图避免使用全局数组的原因,但你可以修改宏以使用指针和索引。
#define SET_LED(p, x) (GPIO0_SET_DATA23 = 1 << p[x])
然后,当你想调用SET_LED时,你可以将它传递给你的数组:
int led_bits[5] = {8, 9, 23, 24, 27};
SET_LED(led_bits, x);
这样您就可以避免在标题中使用extern int led_bits[];
。
答案 2 :(得分:0)
最后我找到了一种方法:
#define LED0 8
#define LED1 9
#define LED2 23
#define LED3 24
#define LED4 27
#define SET_LED(X) (GPIO0_SET_DATA23 = 1 << LED##X)
答案 3 :(得分:0)
这里不需要预处理器。一个普通的功能就可以了。
inline void
set_led(const int x)
{
const int bits[] = {8, 9, 23, 24, 27};
GPIO0_SET_DATA23 = (1U << bits[x]);
}
如果使用文字参数调用set_led
,GCC会将其优化为预先计算的值的单个存储,因此与使用宏相比,没有开销。
为了证明,这是GCC在编译时生成的程序集
#define GPIO0_SET_DATA23 (*((volatile unsigned int *) 0xdeadbeef))
/* set_led as above */
int
test()
{
set_led(3);
return 0;
}
-O2
:
test:
.LFB1:
.cfi_startproc
movl $3735928559, %eax
movl $16777216, (%rax)
xorl %eax, %eax
ret
.cfi_endproc