如何制作3D面具

时间:2015-03-25 02:25:41

标签: c

目前我遇到一个技术问题,这让我想改进以前的实现,情况是:

我有5个GPIO引脚,我需要使用这些引脚作为硬件标识符,例如:

pin1: LOW
pin2: LOW
pin3: LOW
pin4: LOW
pin5: LOW

这意味着我的一个硬件变种,所以我们可以有很多组合。在以前的设计中,开发人员使用if-else来实现这一点,就像:

if(PIN1 == LOW && ... && ......&& PIN5 ==LOW) 
{
    HWID = variant1;
}
else if( ... )
{
}
...
else
{
}

但我认为这不好,因为它将有200多个变种,代码将变得很长,我想把它改成掩码。我的想法是将这五个引脚视为五位寄存器,因为我可以预测我需要根据GPIO状态分配哪个变量(这已由硬件团队定义,它们提供变量列表,所有这些GPIO引脚配置)因此,代码可能如下所示:

enum{
    variant 0x0    //GPIO config 1
    ...
    variant 0xF3   //GPIO config 243
}

然后我可以先读取这五个GPIO引脚状态,并与一些掩码进行比较,看它们是否相等。

问题

然而,对于GPIO,它有三种状态,即:LOW,HIGH,OPEN。如果有任何好的计算方法可以使用3-D掩模?

2 个答案:

答案 0 :(得分:1)

这是我解决问题的建议

#include<stdio.h>

#define LOW 0
#define HIGH 1
#define OPEN 2

#define MAXGPIO 5

int main()
{
    int gpio[MAXGPIO] = { LOW, LOW, OPEN, HIGH, OPEN };

    int mask = 0;

    for (int i = 0; i < MAXGPIO; i++)
        mask = mask << 2 | gpio[i];  

    printf("Masked: %d\n", mask);
    printf("Unmasked:\n");

    for (int i = 0; i < MAXGPIO; i++)
        printf("GPIO %d = %d\n", i + 1, (mask >> (2*(MAXGPIO-1-i))) & 0x03); 

    return 0;
}

关于代码的一点解释。

<强>掩蔽
我使用2位来保存每个GPIO值。组合是:

  • 00: LOW
  • 01: HIGH
  • 02: OPEN
  • 03无效

我正在迭代数组gpio(我有获取的值)并在mask变量中创建一个掩码,左移2位并应用or操作。

<强>取消屏蔽
为了获得初始值,我只是将相反的操作向右移2位乘以GPIO - 1并用0x03屏蔽

我正在应用 0x03 的掩码,因为这些是我感兴趣的。

这是该计划的结果

$ cc -Wall test.c -o test;./test
Masked: 38
Unmasked:
GPIO 1 = 0
GPIO 2 = 0
GPIO 3 = 2
GPIO 4 = 1
GPIO 5 = 2

希望这有帮助

答案 1 :(得分:1)

你有5个引脚,每个引脚有3个状态。您可以通过几种方式表达这一点。

首先,想象一下使用这种框架:

#define LOW (0)
#define HIGH (1)
#define OPEN (2)

uint16_t config = PIN_CONFIG(pin1, pin2, pin3, pin4, pin5);

if(config == PIN_CONFIG(LOW, HIGH, OPEN, LOW, LOW))
{
    // do something
}

switch(config) {
    case PIN_CONFIG(LOW, HIGH, OPEN, LOW, HIGH):
        // do something;
        break;
}

uint16_t config_max = PIN_CONFIG(OPEN, OPEN, OPEN, OPEN, OPEN);
uint32_t hardware_ids[config_max + 1] = {0};

// init your hardware ids
hardware_ids[PIN_CONFIG(LOW, HIGH, HIGH, LOW, LOW)] = 0xF315;
hardware_ids[PIN_CONFIG(LOW, LOW, HIGH, LOW, LOW)] = 0xF225;

// look up a HWID
uint32_t hwid = hardware_ids[config];

这段代码就是你想用引脚配置做的事情。剩下要执行的唯一内容是PIN_CONFIG


方法1

第一种方法是继续将其用作位域,但每个引脚使用2位代表每个引脚状态,而不是每个引脚1位。我认为这是最干净的,即使你为每个引脚“浪费”了一半。

#define PIN_CLAMP(x) ((x) & 0x03)
#define PIN_CONFIG(p1, p2, p3, p4, p5)  \\
  (PIN_CLAMP(p1) &                      \\
  (PIN_CLAMP(p2) << 2) &                \\
  (PIN_CLAMP(p3) << 4) &                \\
  (PIN_CLAMP(p4) << 6) &                \\
  (PIN_CLAMP(p5) << 8))

这很好,因为如果您稍后要进行搜索,它会为“请勿关注”或“无效”值留出空间。


方法2

或者,您可以使用算术来执行此操作,确保使用所需的最小位数。也就是说,~1.5位来编码3个值。正如预期的那样,从0到242,总共3 ^ 5 = 243个状态。 在不了解您的情况的情况下,我相信这是您的引脚状态的最小完整编码。 (实际上,你必须使用8位来编码243个值,因此每个引脚的位数要高1.5位)

#define PIN_CLAMP(x) ((x) % 3) /* note this should really assert */
#define PIN_CONFIG(p1, p2, p3, p4, p5)  \\
  (PIN_CLAMP(p1) &                      \\
  (PIN_CLAMP(p2) * 3) &                 \\
  (PIN_CLAMP(p3) * 9) &                 \\
  (PIN_CLAMP(p4) * 27) &                \\
  (PIN_CLAMP(p5) * 81))

方法1.1

如果你不喜欢预处理器的东西,你可以使用这样的函数:

enum PinLevel (low = 0, high, open);

void set_pin(uint32_t * config, uint8_t pin_number, enum PinLevel value) {
    int shift = pin_number * 2; // 2 bits
    int mask = 0x03 << shift; // 2 bits set to on, moved to the right spot
    *config &= ~pinmask;
    *config |= (((int)value) << shift) & pinmask;
}

enum PinLevel get_pin(uint32_t config, uint8_t pin_number) {
    int shift = pin_number * 2; // 2 bits
    return (enum PinLevel)((config >> shift) & 0x03);
}

这是第一个(每个值2位)方法。


方法1.2

另一种方法是使用C的酷位域语法:

struct pins {
    uint16_t pin1 : 2;
    uint16_t pin2 : 2;
    uint16_t pin3 : 2;
    uint16_t pin4 : 2;
    uint16_t pin5 : 2;
};

typedef union pinconfig_ {
    struct pins pins;
    uint16_t value;
} pinconfig;

pinconfig input;
input.value = 0; // don't forget to init the members unless static

input.pins.pin1 = HIGH;
input.pins.pin2 = LOW;

printf("%d", input.value);

input.value = 0x0003;
printd("%d", input.pins.pin1);

联合允许您将位域视为数字,反之亦然。

(注意:所有代码都未经测试)