Having a brain-void... help with casting a struct to an array element? - Page 1
Forum section: electronics / general

author: jnz
author_rank: Frequent Contributor
timestamp: July 11, 2015, 04:59:18 pm
content: I thought about this before going to bed and still have nothing this morning. I know the answer should be easy.I have 64 bytes. An address byte and a data byte. It's transfer (simultaneous read/write) over SPI. Each address byte is constant. Each data byte has a bitmapped structure.So,Code:data; //shadow copy, local copyin;out;typedef struct {int some_bits:3;int one_bit:1;int rest_bits:4;}; data_byte0;SPI_transfer(in, out, 64);//correlate data_byte struct to a single element in the array. Can't be 100% certain about compiler paddingdata_byte0.some_bits=7;What I want is to cast data_byte0 struct to a specific element in the array, in this case let's say . I want to be able to use data_byte0.some_bits=7; and obviously have that reflect in the array.WHY I want to do this is fairly simple, I would treat the entire thing like a struct and be done, but the SPI commands use arrays. So I want the ease of access to the bitmap.I THINK I can do this with casting a pointer to 's address. But I feel like maybe there is another option, or something I'm missing. I also have to figure out how to store the address data in each location before the data byte, but since I'll likely use a cost list, I'll probably just write a quick array copy to the right places.Any help? Thanks!

author: grumpydoc
author_rank: Super Contributor
timestamp: July 11, 2015, 05:10:16 pm
content: The most immediate is take the address, cast it to a pointer to your struct and dereference it.eg((struct data_byte0 *)data).some_bits.However it might be cleaner and clearer to sayunion { unsigned char *raw;data_byte0 *data} data_buffer.Then set up with egdata_buffer.raw = out;and access with egdata_buffer.data.some_bits = 7;Disclaimer: random brain dump, might need polishing before use.

author: nctnico
author_rank: Super Contributor
timestamp: July 11, 2015, 05:23:32 pm
content: IMHO this can quickly turn into a mess with byte padding etc. It's better to declare a buffer and fill the elements using shifts & masks. That way the code will be much more predictable and portable.Code:uint8_t data_buf;data=some_bits | one_bit<<3 | rest_bits<<4;

author: jnz
author_rank: Frequent Contributor
timestamp: July 11, 2015, 05:24:55 pm
content: Quote from: grumpydoc on July 11, 2015, 05:10:16 pmok. need to look at that. I guess on thing i should have said was every other byte in the array gets a unique struct. So i don't think second example would work for that right?Also, Ill make plenty of calls to these, so if I csn avoid casting every single time which would make the first example less ideal.

author: jnz
author_rank: Frequent Contributor
timestamp: July 11, 2015, 05:27:30 pm
content: Quote from: nctnico on July 11, 2015, 05:23:32 pmThis was my fallback, but i need to write like 50-100 random bits at a time, out of the 512 possible. So if I do it that way I need to keep track of what bits are in which byte. If i could cast it once it would be easier to handle long term.

author: miguelvp
author_rank: Super Contributor
timestamp: July 11, 2015, 05:42:08 pm
content: Declare your arrays because the default won't be unsigned chars (or uint8_t)Use: #pragma pack (push, 1) to ensure no packing #pragma pack (1) will work too but the nice thing about push is that you can follow it with #pragma pack (pop) to restore the previous alignment when you are done with the structures that need to be byte aligned.Use unsigned char or uint8_t for the structure membersDepending how you get your 64 bytes of data you might need to mind the system endianness.

author: nctnico
author_rank: Super Contributor
timestamp: July 11, 2015, 05:55:17 pm
content: Quote from: jnz on July 11, 2015, 05:27:30 pmIn that case I'd create a function or macro to do the hard work.@miguelvp: how bits/bytes are packed can depend on how the aligment is set and how the variable/type is declared. This can get messy and vary between one compiler and the other. Endiannes is also something to consider. My advice is not to have the compiler handle packing/alignment but do it yourself.

author: grumpydoc
author_rank: Super Contributor
timestamp: July 11, 2015, 06:03:33 pm
content: Quote+1Do not leave the shifts & masking operations in your code, wrap in an inline function or macro.That way they will not obscure the logic of what you are trying to do, can easily be made platform-specific if needed and if you do a lot of bit operations you will get one wrong which can be hard to spotQuoteWell, it will but if there's a byte of padding then declare that in your struct.Your code will have fewer errors if you fully describe your data structures.

author: andersm
author_rank: Super Contributor
timestamp: July 11, 2015, 07:10:48 pm
content: If you are using a Cortex-M, then the bit-band alias is a great (but obviously not portable) way to access random bits in an array.

author: jnz
author_rank: Frequent Contributor
timestamp: July 11, 2015, 07:15:19 pm
content: Quote from: andersm on July 11, 2015, 07:10:48 pmI am using a cortex-M (pfft why wouldn't I be!but in this case I need structured access to every other 8 bits in the area and in order, so it's not really random bits. But I like the reminder.

author: IanB
author_rank: Super Contributor
timestamp: July 11, 2015, 07:35:51 pm
content: Quote from: jnz on July 11, 2015, 04:59:18 pmAs others have indicated above, I think I would write a function like this:// array is byte array to be modified// element is byte offset into array// first is first bit in bit field// last is last bit in bit field (e.g. the first three bits would be 0:2, first = 0, last = 2)// value is the value to be assignedset_bits(unsigned char *array, size_t element, size_t first, size_t last, int value);Then you can call it like this:set_bits(in, 1, 0, 2, 7);This would assign the value 7 to in, bits 0:2.You can write a companion function for reading:int get_bits(unsigned char *array, size_t element, size_t first, size_t last);This can read back the value from a given bit field in a given array element.

author: jnz
author_rank: Frequent Contributor
timestamp: July 11, 2015, 07:43:38 pm
content: QuoteCan you elaborate on this for a second?I'm almost following but the padding and describe your data structures I'm not following.I have data_byte0 struct with its own elements and 32 more unique struct as well. I need to apply each to every other byte in my data array.So you are saying wrap something like this:(struct data_byte0 *) myData).someStructMemberSo that I can use that style of casting but the macro as easy to write? Because the one issue I see there is I don't want to have write a macro for each struct member.I'm glad this isn't a clear and easy question. I had it in my head that this was supposed to be easy I was just being dumb

author: jnz
author_rank: Frequent Contributor
timestamp: July 11, 2015, 07:53:50 pm
content: Quote from: IanB on July 11, 2015, 07:35:51 pmI get the idea, but the whole reason of using a struct is so that I have a clearly defined and readable list of variables. I dont want to have to keep a copy of the data sheet open to reference how many and where the bits are for why I could otherwise easily call someStructMember.I guess I'm wondering why I can't just define a (struct *) for each data element and point it to the proper array element's address.It would be 'nice' to be able to point whatever I end up with simultaneously atCode:data, in, or outbut I could live without that if I had to.

author: IanB
author_rank: Super Contributor
timestamp: July 11, 2015, 08:04:31 pm
content: Quote from: jnz on July 11, 2015, 07:53:50 pmYou can, if that's what you want to do:Code:struct data_byte0 {int some_bits:3;int one_bit:1;int rest_bits:4;};((struct data_byte0)(in)).some_bits = 7;(Not tested, but I think it's right.)Note: the "in" array is indexed to element 1 using the original data type "in", then thevalue(not the pointer) is cast to the appropriate struct type, and then the field within the struct is accessed.

author: Jeroen3
author_rank: Super Contributor
timestamp: July 11, 2015, 08:07:14 pm
content: Casting from array pointer to typedef pointer can be used, but be aware of the following:- Unaligned or (non-)byte oriented data access might not be supported.- Bitfields are not guaranteed to be packed in a standardized way.- Neither are structs. Add padding bytes yourself.Also your SPI routine should accept void pointers. Much less casting required. Be aware of data alignment.If you want to run the code on multiple platforms, you should manually do all the shifting to prevent issues for your future co-workers.You can use #if to optimize for specific platforms. For example, ARM has bit banding, which looks ideal for you speeding up your needs.Or you can create a C++ class with a data field and access operations for all bitfields in the data stream.

author: grumpydoc
author_rank: Super Contributor
timestamp: July 11, 2015, 09:27:37 pm
content: QuoteI probably need to understand the layout a bit moreYou have 64 bytes of memory, every other byte is a bitfield, and each of the 32 bitfields is differentAre the bytes in-between meaningful or just padding?I assume the block has a structure as a message or control data for something. In this case you want your code to be written such that this high-level structure is evident in the code as someone reads it because that makes the meaning clearer and review and debugging easier.You could define a structure to cover the whole block - it will be fairly long winded to do this, but will then allow straightforward access to the data. If your C compiler supports anonymous structures you can cut out some cluttereg suppose you definestruct {struct {unsigned int byte_0_field_1:3;unsigned int byte_0_field_2:1;unsigned int byte_0_field_3:4;} /* byte 0 */;unsigned int pad1:8;struct {unsigned int byte_2_field_1:1;unsigned int byte_2_field_2:2;unsigned int byte_2_field_3:5;} /* byte 1 */;unsigned int pad3:8;/* and so on for 30 more definitions */} my_data_struct;For safety I would add whatever instruction your compiler needs to peck the data tightly without additional padding.You now have a struct my_data_struct which covers all 64 bytes and their meaning. Because you have defined the structure in its entirety the compiler will generate the necessary shifts etc.You now can go a bit further and union this with a raw 64 byte buffer, and for convenience give the whole lot a type nameunion {struct my_data_struct;unsigned char bytes} data_buffer;If you want to use these in a function you just then declare a local variable -data_buffer in;data_buffer out;You can now set fields pretty easily egout.byte_0_field_3 = 12;out.byte_2_field_2 = 3;assuming meaningful names in place of byte_X_field_X your code will now be very easy to read - note there's no casting being used here.If you don't have anonymous structs then you will have to name the intermediaries but you can do this so the access ends up asout.byte_0.field_3 = 12;out.byte_2.field_2 = 3;you can then call the SPI routine withSPI_transfer(in.bytes, out.bytes, 64);You might just need to cast in.bytes and out.bytes to void * for this call.By describing the data *completely* to the compiler you have let it do the work of generating all of the bit shifts to access the data and saving you the leg work.Generally I'm not sure all embedded programmers are comfortable with this so you can do it another way.Lets assume you define a macro to extract a `w' bit wide bitfield at offset `o' bits#define get_bitfield(byte, w, o) (((byte) >> (o)) & ((1<<(w))-1))You can now define macros to read each field that you are interested in#define BYTE_0_FIELD_1(data) get_bitfield(data, 3, 0)#define BYTE_0_FIELD_2(data) get_bitfield(data, 1, 3)#define BYTE_0_FIELD_3(data) get_bitfield(data, 4, 4)#define BYTE_2_FIELD_1(data) get_bitfield(data, 1, 0)#define BYTE_2_FIELD_2(data) get_bitfield(data, 2, 1)#define BYTE_2_FIELD_3(data) get_bitfield(data, 5, 3)You will need corresponding macros to set a bitfield within a word and to set each bitfield that you are interested inNow you just need the in and out byte arrays in your functionunsigned char in, outand saySET_BYTE_0_FIELD_3(out, 12);SET_BYTE_2_FIELD_2(out, 3);SPI_transfer(in.bytes, out.bytes, 64);Hope you get the idea of the two styles, each produces something reasonable without a lot of casts getting in the way of reading the code.

author: jnz
author_rank: Frequent Contributor
timestamp: July 11, 2015, 09:56:59 pm
content: Ok, now I'm following what everyone means about describing completely.Yes. The first byte in the array is meaningful, it's the address going to the SPI device. The second byte is the data. So if I wanted to write a register, theand  pair would be A0FF, A0 being the write command and address in one byte, and FF being the data.For some reason, this was on the tip of my brain, but just wouldn't resolve:Code:union {struct my_data_struct;unsigned char bytes} data_buffer;Yes, I'll likely do this! Define out all bytes in the struct including the addresses/padding. Then union the struct and array together. So simple now that I see itFor some reason I just didn't think it was going to be that clear.I'm using Keil and their middleware, I'm pretty sure my SPI routines will accept void * but I'm not sure I'll need to do that just yet, it might take it as is. Although I have to say, I love all of the other ideas here. Makes me feel dumb, but that's ok.

author: ale500
author_rank: Frequent Contributor
timestamp: July 16, 2015, 10:26:20 am
content: Why do you want to use void * ?. Void pointers are sometimes useful, but overuse can cause problems. If you want to give some pointer to a function because it is going to be called with different say structures, and you don't want to cast on function call, it is ok, you will have to cast on the function itself. The problem with void * is that those pointers point to unsized memory chunks, you cannot use pointer arithmetic, and you cannot "misuse" them to access the memory as an array...Code:void *start, *end;if ((end - start) < sizeof(int)) // this is bad...start++; // this is even worse, how much should be added to start ?Sadly gcc allows (it may be some waring available like... ansi ? pendantic?)Code:start + 3and things like that...Could you please make an example of the use of void * in your program/routine ?.

author: Jeroen3
author_rank: Super Contributor
timestamp: July 16, 2015, 12:15:39 pm
content: Void pointers are nice if you want to access a block of memory of given size. For mempcy, spi transmit, ethernet transmit, dma transfer.Void pointers *can* be used for more advanced features, when you have not heard of C++ yet. This is where is becomes a risk. It will never be obvious in the way it works.The compiler refuses to do void pointer arithmetic. Does GCC allow this?Code:error: #852: expression must be a pointer to a complete object typeUse uint32_t to do raw address arithmetic, on 32 bit systems. Which you can then cast to whatever you like, when casting you know your playing around, void is often implicit.

author: jnz
author_rank: Frequent Contributor
timestamp: July 17, 2015, 06:19:18 pm
content: Trying not to use void pointers, and trying to sanitize the pointer logic as much as possible.But I may not have a choice with this issue here:Using this with a fully defined struct and array union:Code:union {struct my_data_struct;unsigned char bytes} data_buffer;I have passed *bytes to another function. I didn't really see the need to pass the entire structure through.So I have Do_Something(*bytes). I have maybe 10 other functions just like it. But in one of them I find myself perhaps needing the struct again. So what I wanted to do was create an instance of the struct inside this one function then allign the new struct's bytes with my pointer I've already taken in. I'm a little confused how to do this because the array is inside the struct and Keil doesn't seem to like any of the ideas I've had so far...Code:Do_Something(*bytes) {data_buffer new_instance;byte = 5;x = byte;// new_instance.data should align with *bytes//So I can new_instance.my_data_struct.structmember = 3; where this is aligned at the correct spot inside of *bytes}Of course, I could clear this up by just passing a pointer to the union and using it like that, I just kinda wanted to keep all my functions common. Seems like there should be a way to quickly align my new_instance union to the bytes member... Right?

author: IanB
author_rank: Super Contributor
timestamp: July 17, 2015, 06:56:14 pm
content: Quote from: jnz on July 17, 2015, 06:19:18 pmYes. Instead of declaring a concrete struct instance like "new_instance", you just need to use the structure definition as a data type and base it off your data pointer.Something like this:Code:data_buffer *buff_pointer = (data_buffer *)bytes;buff_pointer->structmember = 3;

author: jnz
author_rank: Frequent Contributor
timestamp: July 17, 2015, 06:59:15 pm
content: Quote from: IanB on July 17, 2015, 06:56:14 pmOf course. I figured it was an easy casting but my brain just wouldn't connect the right neurons.I really hope you guys individually struggle with other things that I find easy