C 用于嵌入式开发的引脚掩码约定

C 用于嵌入式开发的引脚掩码约定,c,embedded,C,Embedded,对于嵌入式C应用程序,我始终使用以下约定来定义GPIO引脚掩码: “传统”示例:带有32位GPIO端口的32位CPU #define PIN(x) (1u << (x##_PIN_NUM)) ... #define LED_CTRL_PIN_NUM (5) ... void gpio_set(uint16_t pin_mask) { GPIO_OUT |= pin_mask; } ... //turn the led on gpio_set(PIN(LED_CTRL));

对于嵌入式C应用程序,我始终使用以下约定来定义GPIO引脚掩码:

“传统”示例:带有32位GPIO端口的32位CPU

#define PIN(x) (1u << (x##_PIN_NUM))
...
#define LED_CTRL_PIN_NUM (5)
...
void gpio_set(uint16_t pin_mask) {
    GPIO_OUT |= pin_mask;
}
...
//turn the led on
gpio_set(PIN(LED_CTRL));
在GPIO输出寄存器中断言位5以打开led

#define BIT5 (1u << 5)      //or maybe even just hardcode as 0x20
...
#define PIN_LED_CTRL (BIT5) //LED_CTRL is Pin #5
...
void gpio_set(uint16_t pin_mask) {
    GPIO_OUT |= pin_mask;
}
...
//turn the led on
gpio_set(PIN_LED_CTRL);

#使用第一种方法定义位5(1u),编译器将解释最后一行,如下所示:

gpio_set(((1u << 5)));
gpio_set((1u << ((5)));
它仍然具有同样多的语法意义,但需要更多的行才能写入…

假设在“Alternative”示例中输入错误(在最后一行中,您指定了未定义的“LED\U CTRL”…我假设您指的是“LED\U CTRL\U PIN\NUM”),那么我相信这两个示例将生成相同的机器代码。因此,在这两个选择之间……这只是风格偏好的问题。(第二个示例中的##只是一个嵌入注释。)

就我个人而言,我会使用完全不同的方法。我会使用内置的位字段构造来修改位字段,这样编译器就可以决定最有效的方法。示例如下:

struct {
    unsigned int :4, LED_CTRL:1, :27;
} GPIO_OUT;
...
GPIO_OUT.LED_CTRL = 1;

请注意“:4”和“:27”表示寄存器中的其他位……您可能希望映射整个I/O寄存器,而不仅仅是其中的一位。

位字段:SGeorgiades在上面给出一个答案,这是一项风险业务*

如果您使用C位字段来定义管脚,请非常小心

  • 位字段“类型”依赖于实现(需要引号;int/uint)
  • 默认情况下,字段不是按位连续的,并提升为依赖于impl的大小
  • 跨越int bitsize边界的位域在位域映射中将不连续!(需要引用)
对于GCC/C11,我将使用以下位字段语法(如果我使用它)

注释

  • 通过将pin位置的概念合并到struct中,您不能与其他用户共享它 gpio中的寄存器(即可能是DIR、in、INT_EN、IFG等)

  • 我将举一个x86反汇编的例子来说明这里提到的tmrw。它们非常依赖于实现和编译器配置


*如果我错了,请纠正我。

两种方法几乎相同,只是风格不同,但问题相同

通常情况下,pin不仅仅由位号定义,您还需要知道端口

因此,在更改端口时,不仅需要修改定义,还需要修改代码

#define PIN_LED_CTRL (BIT5) //LED_CTRL is Pin #5
...
void gpio_set_port2(uint16_t pin_mask) {
    GPIO2_OUT |= pin_mask;
}
...
//turn the led on
gpio_set_port2(PIN_LED_CTRL);
我更喜欢只定义一次pin,所有不同的依赖项都是通过宏或函数构建的

#define HW_SPI_CLOCK    2,5    // uses port2,bit5
#define HW_RESET_EXT    4,3    // uses port4,bit3
然后我使用一些宏来定义get端口方向、pushPull和其他寄存器。
这些宏几乎不依赖于平台和工具链

/**
 * Generic GH-Macros
 */
#define PASTE2(a,b)       a##b
#define PASTE3(a,b,c)     a##b##c
#define __PASTE3(a,b,c)     PASTE3(a,b,c)
#define PASTE4(a,b,c,d)   a##b##c##d
#define PASTE6(a,b,c,d,e,f)     a##b##c##d##e##f
#define __PASTE6(a,b,c,d,e,f)     PASTE6(a,b,c,d,e,f)

#define GH_PORT(port,pin)       port
#define GH_PIN(port,pin)       pin
#define GPIO_PIN(gh)      __PASTE6(FIO,GH_PORT(gh),PIN_bit.P,GH_PORT(gh),_,GH_PIN(gh))
#define GPIO_DIR(gh)      __PASTE6(FIO,GH_PORT(gh),DIR_bit.P,GH_PORT(gh),_,GH_PIN(gh))

我知道TI使用的是“传统”方法。如果我没记错的话,Atmel&Microchip也会这样做。*修复了引脚(x)定义中的括号计数。引脚(x)上的捕捉良好妄想症;我确实错过了一个。修复了它。在这里不征求意见;我有我的,我确实喜欢它。这里的目标是理解“alt”背后的原因,因为原始开发人员不再可用。也许是在其他地方使用#;例如,在某种数组索引中。@justinmreina我不知道这个电气工程stuff(我认为这就是全部内容),但如果有指定管脚编号的管脚,那么第二种方法可以产生更自然的语法。我将在10分钟内编辑我的答案,进一步解释这一点……期待:)我认为您的编辑#2只是“同时使用”了alt和traditional。但是我确实喜欢你在PIN(x)中给出的简化(1u位域绝对是一个选项!虽然在嵌入式编译器的某些情况下,当您只需要AND/OR/XOR时,位域会导致读取/修改/写入。超级烦人!因此我使用更简单的_set/_clr/_XOR宏。请确保使用压缩,并在此处声明大小!此处不可移植代码;如果我是正确的话。@justinmreina-原始代码h)ere已经在读修改写了,所以我想那是cool@justinmreina-是的,打包是一个很好的建议…如果这是我的代码,我一定会定义一个int32类型,只是为了澄清一下…虽然当您写入硬件寄存器时,可移植性并不是一个真正的问题…其他处理器会发生这种情况的可能性有多大有完全相同的寄存器吗?关于实现依赖性,我完全同意你的第一点。你的第二点似乎也是正确的…建议谨慎,并在开始时进行一些实验以确保行为符合你的预期。你的第三点肯定是正确的…必须小心位字段!这是没错……但鉴于这是一个基于微控制器的嵌入式系统,我相信值得研究比特域的使用情况,因为它们可以产生更高效的代码。比特域可能有点痛苦,但根据我的经验,它们非常值得!我真的很喜欢这个解决方案!我会标记为“答案”,但当然是为了这篇文章没有真正的“答案”。不过这很有帮助。。
struct {
    unsigned int :4, LED_CTRL:1, :27;
} GPIO_OUT;
...
GPIO_OUT.LED_CTRL = 1;
struct gpio_out {
    int b0        : 1;
    int led_ctrl  : 1;
    ....
    int b30       : 1;
    int b31       : 1;
} __attribute__((packed));
#define PIN_LED_CTRL (BIT5) //LED_CTRL is Pin #5
...
void gpio_set_port2(uint16_t pin_mask) {
    GPIO2_OUT |= pin_mask;
}
...
//turn the led on
gpio_set_port2(PIN_LED_CTRL);
#define HW_SPI_CLOCK    2,5    // uses port2,bit5
#define HW_RESET_EXT    4,3    // uses port4,bit3
/**
 * Generic GH-Macros
 */
#define PASTE2(a,b)       a##b
#define PASTE3(a,b,c)     a##b##c
#define __PASTE3(a,b,c)     PASTE3(a,b,c)
#define PASTE4(a,b,c,d)   a##b##c##d
#define PASTE6(a,b,c,d,e,f)     a##b##c##d##e##f
#define __PASTE6(a,b,c,d,e,f)     PASTE6(a,b,c,d,e,f)

#define GH_PORT(port,pin)       port
#define GH_PIN(port,pin)       pin
#define GPIO_PIN(gh)      __PASTE6(FIO,GH_PORT(gh),PIN_bit.P,GH_PORT(gh),_,GH_PIN(gh))
#define GPIO_DIR(gh)      __PASTE6(FIO,GH_PORT(gh),DIR_bit.P,GH_PORT(gh),_,GH_PIN(gh))