目前我遇到一个技术问题,这让我想改进以前的实现,情况是:
我有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掩模?
答案 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
我正在迭代数组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
第一种方法是继续将其用作位域,但每个引脚使用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))
这很好,因为如果您稍后要进行搜索,它会为“请勿关注”或“无效”值留出空间。
或者,您可以使用算术来执行此操作,确保使用所需的最小位数。也就是说,~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))
如果你不喜欢预处理器的东西,你可以使用这样的函数:
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位)方法。
另一种方法是使用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);
联合允许您将位域视为数字,反之亦然。
(注意:所有代码都未经测试)