Currently I meet one technique issue, which makes me want to improve the previous implementation, the situation is:
目前我遇到一个技术问题,这让我想改进以前的实现,情况是:
I have 5 GPIO pins, I need use these pins as the hardware identifier, for example:
我有5个GPIO引脚,我需要使用这些引脚作为硬件标识符,例如:
pin1: LOW
pin2: LOW
pin3: LOW
pin4: LOW
pin5: LOW
this means one of my HW variants, so we can have many combinations. In previous design, the developer use if-else
to implement this, just like:
这意味着我的一个硬件变种,所以我们可以有很多组合。在以前的设计中,开发人员使用if-else来实现它,就像:
if(PIN1 == LOW && ... && ......&& PIN5 ==LOW)
{
HWID = variant1;
}
else if( ... )
{
}
...
else
{
}
but I think this is not good because it will have more than 200 variants, and the code will become to long, and I want changed it to a mask. The idea is I treat this five pins as a five bits register, and because I can predict which variant I need to assign according to GPIOs status(this already defined by hardware team, they provide a variant list, with all these GPIO pins configuration), therefore, the code may look like this:
但我认为这不好,因为它将有200多个变种,代码将变得很长,我想把它改成掩码。我的想法是将这五个引脚视为五位寄存器,因为我可以预测我需要根据GPIO状态分配哪个变量(这已由硬件团队定义,它们提供变量列表,所有这些GPIO引脚配置)因此,代码可能如下所示:
enum{
variant 0x0 //GPIO config 1
...
variant 0xF3 //GPIO config 243
}
then I can first read these five GPIO pins status, and compare to some mask to see if they are equal or not.
然后我可以先读取这五个GPIO引脚的状态,并与一些掩码进行比较,看它们是否相等。
Question
题
However, for GPIO, it has three status, namely: LOW, HIGH, OPEN. If there is any good calculation method to have a 3-D mask?
但是,对于GPIO,它有三种状态,即:LOW,HIGH,OPEN。如果有任何好的计算方法来制作3-D掩模?
2 个解决方案
#1
1
You have 5 pins of 3 states each. You can approach representing this in a few ways.
你有5个引脚,每个3个状态。您可以通过几种方式表示这一点。
First, imagine using this sort of framework:
首先,想象一下使用这种框架:
#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];
This code is just the sort of stuff you'd like to do with pin configurations. The only bit left to implement is PIN_CONFIG
这段代码就是你想要的引脚配置。剩下要实现的唯一一点是PIN_CONFIG
Approach 1
The first approach is to keep using it as a bitfield, but instead of 1 bit per pin you use 2 bits to represent each pin state. I think this is the cleanest, even though you're "wasting" half a bit for each pin.
第一种方法是继续将其用作位域,但是每个引脚使用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))
This is kind of nice because it leaves room for a "Don't care" or "Invalid" value if you are going to do searches later.
这有点不错,因为如果您稍后要进行搜索,它会为“请勿关注”或“无效”值留出空间。
Approach 2
Alternatively, you can use arithmetic to do it, making sure you use the minimum amount of bits necessary. That is, ~1.5 bits to encode 3 values. As expected, this goes from 0 up to 242 for a total of 3^5=243 states. Without knowing anything else about your situation I believe this is the smallest complete encoding of your pin states. (Practically, you have to use 8 bits to encode 243 values, so it's higher 1.5 bits per pin)
或者,您可以使用算术来执行此操作,确保使用所需的最小位数。也就是说,~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))
Approach 1.1
If you don't like preprocessor stuff, you could use functions a bit like this:
如果你不喜欢预处理器的东西,你可以使用这样的函数:
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);
}
This follows the first (2 bit per value) approach.
这遵循第一个(每个值2位)方法。
Approach 1.2
YET ANOTHER WAY using C's cool bitfield syntax:
使用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);
The union lets you view the bitfield as a number and vice versa.
联合允许您将位域视为数字,反之亦然。
(note: all code completely untested)
(注意:所有代码完全未经测试)
#2
1
This is my suggestion to solve the problem
这是我解决问题的建议
#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;
}
A little explanation about the code.
关于代码的一点解释。
Masking
I am using 2 bits to save each GPIO value. The combinations are:
屏蔽我使用2位来保存每个GPIO值。组合是:
00: LOW
- 00:低
01: HIGH
- 01:高
02: OPEN
- 02:开放
- 03 is Invalid
- 03无效
I am iterating the array gpio (where I have the acquired values) and creating a mask in the mask
variable shifting left 2 bits and applying an or
operation.
我正在迭代数组gpio(我有获取的值)并在掩码变量中创建一个掩码,左移2位并应用或操作。
Unmasking
To get the initial values I am just making the opposite operation shifting right 2 bits multiplied by the amount of GPIO - 1 and masking with 0x03
取消屏蔽要获取初始值,我只是将相反的操作右移2位乘以GPIO - 1的数量,并用0x03屏蔽
I am applying a mask with 0x03 because those are the bit I am interested.
我正在应用一个带有0x03的掩码,因为那些是我感兴趣的位。
This is the result of the program
这是该计划的结果
$ 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
Hope this helps
希望这可以帮助
#1
1
You have 5 pins of 3 states each. You can approach representing this in a few ways.
你有5个引脚,每个3个状态。您可以通过几种方式表示这一点。
First, imagine using this sort of framework:
首先,想象一下使用这种框架:
#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];
This code is just the sort of stuff you'd like to do with pin configurations. The only bit left to implement is PIN_CONFIG
这段代码就是你想要的引脚配置。剩下要实现的唯一一点是PIN_CONFIG
Approach 1
The first approach is to keep using it as a bitfield, but instead of 1 bit per pin you use 2 bits to represent each pin state. I think this is the cleanest, even though you're "wasting" half a bit for each pin.
第一种方法是继续将其用作位域,但是每个引脚使用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))
This is kind of nice because it leaves room for a "Don't care" or "Invalid" value if you are going to do searches later.
这有点不错,因为如果您稍后要进行搜索,它会为“请勿关注”或“无效”值留出空间。
Approach 2
Alternatively, you can use arithmetic to do it, making sure you use the minimum amount of bits necessary. That is, ~1.5 bits to encode 3 values. As expected, this goes from 0 up to 242 for a total of 3^5=243 states. Without knowing anything else about your situation I believe this is the smallest complete encoding of your pin states. (Practically, you have to use 8 bits to encode 243 values, so it's higher 1.5 bits per pin)
或者,您可以使用算术来执行此操作,确保使用所需的最小位数。也就是说,~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))
Approach 1.1
If you don't like preprocessor stuff, you could use functions a bit like this:
如果你不喜欢预处理器的东西,你可以使用这样的函数:
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);
}
This follows the first (2 bit per value) approach.
这遵循第一个(每个值2位)方法。
Approach 1.2
YET ANOTHER WAY using C's cool bitfield syntax:
使用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);
The union lets you view the bitfield as a number and vice versa.
联合允许您将位域视为数字,反之亦然。
(note: all code completely untested)
(注意:所有代码完全未经测试)
#2
1
This is my suggestion to solve the problem
这是我解决问题的建议
#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;
}
A little explanation about the code.
关于代码的一点解释。
Masking
I am using 2 bits to save each GPIO value. The combinations are:
屏蔽我使用2位来保存每个GPIO值。组合是:
00: LOW
- 00:低
01: HIGH
- 01:高
02: OPEN
- 02:开放
- 03 is Invalid
- 03无效
I am iterating the array gpio (where I have the acquired values) and creating a mask in the mask
variable shifting left 2 bits and applying an or
operation.
我正在迭代数组gpio(我有获取的值)并在掩码变量中创建一个掩码,左移2位并应用或操作。
Unmasking
To get the initial values I am just making the opposite operation shifting right 2 bits multiplied by the amount of GPIO - 1 and masking with 0x03
取消屏蔽要获取初始值,我只是将相反的操作右移2位乘以GPIO - 1的数量,并用0x03屏蔽
I am applying a mask with 0x03 because those are the bit I am interested.
我正在应用一个带有0x03的掩码,因为那些是我感兴趣的位。
This is the result of the program
这是该计划的结果
$ 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
Hope this helps
希望这可以帮助