views:

136

answers:

7

On my AVR I have an array of chars that hold color intensity information in the form of {R,G,B,x,R,G,B,x,...} (x being an unused byte). Is there any simple way to write a long int (32-bits) to char myArray[4*LIGHTS] so I can write a 0x00BBGGRR number easily?

My typecasting is rough, and I'm not sure how to write it. I'm guessing just make a pointer to a long int type and set that equal to myArray, but then I don't know how to arbitrarily tell it to set group x to myColor.

uint8_t myLights[4*LIGHTS];
uint32_t *myRGBGroups = myLights; // ?

*myRGBGroups = WHITE; // sets the first 4 bytes to WHITE
                      // ...but how to set the 10th group?

Edit: I'm not sure if typecasting is even the proper term, as I think that would be if it just truncated the 32-bit number to 8-bits?

+1  A: 

Think of using C union, where the first field of the union is a int32 and the second a vector of 4*chars. But, not sure if this is the best way for you.

Andres
Wouldn't that double my memory requirements? I'm fairly space and speed constrained on my 8-bit AVR, and the compiler seems to like having my intensity values sequential so it can generate smaller/faster code when it relays the data the 4-channel light controllers.
Nick T
No, a union takes all of the fields and uses the same piece memory for all of them. Consequently, any changes to one of the fields will affect all of the other fields, since they read from the same memory address. That also means the added int32 will be read from the same piece of memory that you're currently reading the individual RGBA values from.
Michael Madsen
+1  A: 

You need to account for the endianness of uint32_t on the AVR to make sure the components are being stored in the correct order (for later dereferencing via myLights array) if you're going to do this. A quick Google seems to indicate that AVRs store data in memory little-endian, but other registers vary in endianness.

Anyway, assuming you've done that, you can dereference myRGBGroups using array indexing (where each index will reference a block of 4 bytes). So, to set the 10th group, you can just do myRGBGroups[ 9 ] = COLOR.

ezod
A: 

if can use arithmetic on myRGBgroup, for example myRGBgroups ++ will give next group, similarly you can use plus, minus, etc. operators.those operators operate using type sizes, rather than single byte

myRGBgroups[10] // access group as int
((uint8_t*)(myRGBgroups + 10)) // access group as uint8 array
aaa
+5  A: 
typedef union {
    struct {
         uint8_t    red;
         uint8_t    green;
         uint8_t    blue;
         uint8_t    alpha;
    }          rgba;
    uint32_t   single;
} Color;

Color    colors[LIGHTS];

colors[0].single = WHITE;
colors[0].rgba.red -= 5;

NOTE: On a little-endian system, the low-order byte of the 4-byte value will be the alpha value; whereas it will be the red value on a big-endian system.

Steve Emmerson
Shouldn't the inner union be a struct?
Michael Madsen
sure, union within union makes no sense. the inner should be a struct. I think you can even drop the name of the inner struct (here rgba), this gives you direct access to inners, like colors.red.
lImbus
Struct seems more proper to me (though unions are new to me), but when I compile it either way I get the exact same hex file. This seems like an appropriate way to learn about them. -- One of the last nagging things for me is whenever I try to grab the char array with something like `uint8_t *pwmVals = colors;` I get a `warning: initialization from incompatible pointer type`. `= colors[0].red` works but it looks bad :P
Nick T
You shouldn't get the same file with an inner `union` instead of `struct`: try assigning different values to `red`, `green`, `blue`, and `alpha`. And I don't know what you're trying to do with `uint8_t *pwmVals = colors;`
Alok
+2  A: 

Your code is perfectly valid. You can use myRGBGroups as regular array, so to access 10th pixel you can use

myRGBGroups[9]
el.pescado
alas, the tenth element would be [9]
lImbus
I think you need to explicitly cast though. `uint32_t *myRGBGroups = (uint32_t *) myLights;` At least you do in C++; maybe not in C.
Grumdrig
Of course, 10th element index is 9, my fault;)Explicit casting is not necessary in C, but is necessary in C++. C++ is more strict in this matter than C.
el.pescado
A: 

Union of a struct and uint32_t is a much better idea than making a uint8_t of size 4 * LIGHTS. Another fairly common way to do this is to use macros or inline functions that do the bitwise arithmetic necessary to create the correct uint32_t:

#define MAKE_RGBA32(r,g,b,a) (uint32_t)(((r)<<24)|((g)<<16)|((b)<<8)|(a))
uint32_t colors[NUM_COLORS];
colors[i] = MAKE_RGBA32(255,255,255,255);

Depending on your endianness the values may need to be placed into the int in a different order. This technique is common because for older 16bpp color formats like RGBA5551 or RGB565, it makes more sense to think of the colors in terms of the bitwise arithmetic than in units of bytes.

Dan Olson
A: 

You can perform something similar using struct assignment - this gets around the endian problem:

typedef struct Color {
    unsigned char r, g, b, a;
} Color;

const Color WHITE   = {0xff, 0xff, 0xff, 0};
const Color RED     = {0xff, 0, 0, 0};
const Color GREEN   = {0, 0xff, 0, 0};

Color colors[] = {WHITE, RED};
Color c;

colors[1] = GREEN;
c = colors[1];

However comparison is not defined in the standard you can't use c == GREEN and you can't use the {} shortcut in assignment (only initialisation) so c = {0, 0, 0, 0} would fail.

Also bear in mind that if it's an 8 bit AVR (as opposed to an AVR32 say), then you most likely won't see any performance benefit from either technique.

Peter Gibson