2

I have to implement a set of serial shift registers with each 8 outputs. The outputs are connected to switches, so I'm currently using boolean arrays in C that either turn the switch on (true) or off(false).

So each shift register has an array of 8 boolean values, which is in fact a 8 bit unsigned integer. I could continue to work with arrays but I assume thats a lot slower then just bitwise manipulating the integer. Also passing the integer to the SPI interface is a lot easier than an array.

Is there an easy way to convert boolean arrays into integers or manipulate the integers in the same way I could manipulate an array?

I.e.:

bool switch[8];
switch[0] = True; //Switch 1 on
switch[1] = False; //Switch 2 off
...

is the same as

uint8_t switch;
switch = 0b00000001;

But is harder to read and program when thinking in individual switches.

Performance is key, since my SPI needs to be very fast.

8
  • 2
    Why do you assume that using a boolean array is slower? Commented Apr 9, 2019 at 10:36
  • because I have to use a loop if I wanted to set them all to 1 for example, for an integer I could just assign them all at once. So in essence, more for loops, and for loops are slow correct? Commented Apr 9, 2019 at 10:42
  • maybe work with a union of your boolean array and the integer value. Commented Apr 9, 2019 at 10:43
  • Yeah, a union seems promising, will research that Commented Apr 9, 2019 at 10:44
  • For this purpose, don't use a bool array, don't use bit-fields, don't use enums, don't use fluffy poodles, don't use hydraulic excavators. Use a uint8_t. And that's it. Commented Apr 9, 2019 at 10:52

5 Answers 5

2

You cannot use the array syntax (this would need operator overloading, which is possible with C++). But you can use some helperfunction to set and get a bit (/ 8 and % 8 are optimized to bitshifts and ands, note that this is not the same with int as a type for index)

typedef uint8_t bit_mem_t;

static inline void set_bit(bit_mem_t* array, unsigned index, bool value) {
    array[index/8] = (array[index/8] | 1 << (index%8)) ^ !value << (index%8);
}

static inline bool get_bit(bit_mem_t const* array, unsigned index) {
    return (array[index/8] & 1 << (index%8)) != 0;
}

static inline void flip_bit(bit_mem_t* array, unsigned index) {
    array[index/8] ^= 1 << (index%8);
}

/*static inline size_t bit_array_size(size_t bit_count) {
    return (bit_count+7) / 8;
}*/
#define bit_array_size(bit_count) (((size_t)(count)+7)/8)

int main() {
    bit_mem_t array[bit_array_size(3)] {}; // clear it to 0s

    set_bit(array, 0, true);
    set_bit(array, 1, false);
    set_bit(array, 2, !get_bit(array, 1));
}
Sign up to request clarification or add additional context in comments.

Comments

2

Use uint8_t for storing 8 bit hardware-related data, period. If it is a memory-mapped hardware register, it also needs to be volatile qualified.

If you wish to name individual pins you can do it with defines:

#define SWITCH3 (1u << 3);

Access with bitwise operators.

uint8_t switch = 0;
switch = SWITCH1 | SWITCH3;   // set all bits
switch |= SWITCH3;            // set a specific bit
switch &= (uint8_t)~SWITCH3;  // clear a specific bit

4 Comments

@Ctx How is this answer better than the others before? It doesn't use bitfields. ;-)
@AndrewHenle I did not refer to my answer, of course ;) It's not quite fair to cite only a part of my comment and distort its meaning that way
@Ctx Well, that's the only part I have enough information to comment on, as I don't know what's going on with the other posters and their answers, nor who the sock puppet might be. :-D
@AndrewHenle "Who" the sock puppet may be is quite clear I think ;) Well, I simply observed that this answer basically went the same route than two other answers, but was immediately upvoted (while the others weren't) albeit it was posted several minutes later. That smells, and that's what I meant to express. But there may be other reasons of course...
1
#define SWITCH1 1U
#define SWITCH2 2U
#define SWITCH3 4U
#define SWITCH4 8U
#define SWITCH5 16U
#define SWITCH6 32U
#define SWITCH7 64U
#define SWITCH8 128U


unsigned int switches = 0x0U;

To turn on a switch, lets says switch switch 4

switches = (switches | SWITCH4)

To turn off switch, lets says switch switch 4

switches = (switches & ~SWITCH4)

4 Comments

SWITCH 4 should be 0x10
Or rather: 0x1u, 0x2u, 0x4u, 0x8u, 0x10u.
...or if you for some reason insist on using decimal, 1u, 2u, 4u, 8u, 16u, 32u, 64u, 128u.
@Lundin I missed 8U in between, got that now
1

Use macros:

uint8_t sw;
#define SW_ON(s)    sw=(sw|(1u<<(s)))
#define SW_OFF(s)   sw=(sw&(~(1u<<(s))))

and use as:

SW_OFF(3);
SW_ON(2);

2 Comments

Consider using 1u integer constants to avoid surprises during integer promotion. Your SW_OFF macro most often returns a negative integer value, which might not be what the programmer expects.
Thanks, @Lundin, fixed that. Actually the expressions should be assignments to achieve what OP wants. Fixed that too.
0

You could use a bitfield like that:

union {
     struct {
          unsigned char s1:1,
                        s2:1,
                        s3:1,
                        s4:1,
                        s5:1,
                        s6:1,
                        s7:1,
                        s8:1
     } sw;
    unsigned char nr[1];
} u;

You can access the fields individually (u.sw.s1 - u.sw.s8) for the switches, but also access the whole number in u.nr[0].

It might also be possible to use unsigned char nr instead of an array with 1 member, but the standard only declares it valid for char arrays, so better play it safe.

13 Comments

Using bit-fields is a very bad idea. Using char for bit-fields is an incredibly bad idea. If char is signed, there is no telling what this code will do. In addition, there is no telling if s1 is bit 0 or something else. "but the standard only declares it valid for char arrays" No it doesn't. 6.7/5 "A bit-field shall have a type that is a qualified or unqualified version of _Bool, signed int, unsigned int, or some other implementation-defined type." So this code could literally do anything.
@Lundin Not for the bit field, but for the union. But you are correct, should be unsigned char.
The standard allows you to use any type for unions.
No it allows all manner of wild type punning. You can only iterate through some other larger type by using a pointer to character type, but that's another story. Character type meaning char, signed char, unsigned char, uint8_t etc.
signed char is not allowed to have padding bits/trap either. Trap representations are irrelevant anyway, if you access through a pointer which is allowed to alias with a pointer to the effective type of what's stored there. What is the strict aliasing rule?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.