The goal is to create a structure that contains 3 fields. One of those fields needs to be a uint32_t union but the flags will be different for each element. I believe the best way to do this is to create a separate union for each peripheral and assign it to a void pointer in the parent struct (somehow).
The Current Plan:
//--------------Register for AAA
typedef union
{
struct
{
uint32_t spare : 32; //< Bits 0-31 not used
}Bits;
uint32_t FullData;
}ABC_EMPTY_DATA;
ABC_EMPTY_DATA AAA_Data;
//--------------Register for BBB
typedef union
{
struct
{
uint32_t use32BitColor : 1; //< Bit0
uint32_t enableTimer : 1; //< Bit1
uint32_t fooTheBar : 1; //< Bit2
uint32_t spare : 29; //< Bits 3-31 not used
}Bits;
uint32_t FullData;
}ABC_BBB_DATA;
ABC_EMPTY_DATA BBB_Data;
//--------------Register for CCC
typedef union
{
struct
{
uint32_t useInternalFrameTick : 1; //< Bit0
uint32_t useExternalFrameTick : 1; //< Bit1
uint32_t spare : 30; //< Bits 2-31 not used
}Bits;
uint32_t FullData;
}ABC_CCC_DATA;
ABC_CCC_DATA CCC_Data;
//-------The Peripherals with register element
typedef struct
{
char *Name;
unsigned int HexCmd;
void *Data; //Connect to corasponding XXXX_Data
}ABC_dataStructs;
//----- Initializing Peripherals
typedef struct
{
ABC_dataStructs AAA = {.Name = "AAA", .HexCmd = 0x00, .Data = (void*) &AAA_Data};
ABC_dataStructs BBB = {.Name = "BBB", .HexCmd = 0x00, .Data = (void*) &BBB_Data};
ABC_dataStructs CCC = {.Name = "CCC", .HexCmd = 0x00, .Data = (void*) &CCC_Data};
}ABC_CommandList;
void main(){
ABC_CommandList everything;
everything.CCC.FullData = 0x000F;
everything.BBB.use32BitColor = 0;
int myint = everything.AAA.HexCmd;
}
Currently, I am having initializeing the values within the struct
ABC_dataStructs AAA = {.Name = "AAA", .HexCmd = 0x00, .Data = (void*) &AAA_Data};
is invalid
Also and more importantly the assigning of the union is not working as intended. The current result only allows for everything.AAA.Data without being able to assign/read individual bits.
I also tried to explain the intent of the SW in order to avoid an "XY problem" and if I am approaching this wrong or you think of something better let me know. Thank You.
Note: C struct, union pointer to struct has another approach of putting structs in the unions instead of unions to the structs, but it doesn't help with my main issue of assigning these to a void*
The problem you're having with the initialization is that you're defining a type and attempting to initialize it as if it's a variable at the same time. You need to first define the struct, then initialize an instance of it:
typedef struct
{
ABC_dataStructs AAA;
ABC_dataStructs BBB;
ABC_dataStructs CCC;
}ABC_CommandList;
void main(){
ABC_CommandList everything = {
.AAA = {.Name = "AAA", .HexCmd = 0x00, .Data = &AAA_Data},
.BBB = {.Name = "BBB", .HexCmd = 0x00, .Data = &BBB_Data},
.CCC = {.Name = "CCC", .HexCmd = 0x00, .Data = &CCC_Data},
};
...
Note also that it's not required to cast to/from a void *.
Then the problem here:
everything.CCC.FullData = 0x000F;
everything.BBB.use32BitColor = 0;
Is that CCC and BBB are of type ABC_dataStructs, and that type does not have fields named FullData or use32BitColor. You would need to cast the Data member to the proper pointer type, then dereference that:
((ABC_CCC_DATA *)everything.CCC.Data)->FullData = 0x000F;
((ABC_BBB_DATA *)everything.BBB.Data)->use32BitColor = 0;
All that being said, there's a better way of modeling this data. What you really want is a union of the 3 register types. Then ABC_dataStructs would contain that union along with a separate field which flags which union to use.
typedef struct
{
uint32_t spare : 32; //< Bits 0-31 not used
} ABC_EMPTY_DATA;
typedef struct
{
uint32_t use32BitColor : 1; //< Bit0
uint32_t enableTimer : 1; //< Bit1
uint32_t fooTheBar : 1; //< Bit2
uint32_t spare : 29; //< Bits 3-31 not used
} ABC_BBB_DATA;
typedef struct
{
uint32_t useInternalFrameTick : 1; //< Bit0
uint32_t useExternalFrameTick : 1; //< Bit1
uint32_t spare : 30; //< Bits 2-31 not used
} ABC_CCC_DATA;
typedef struct
{
char *Name;
unsigned int HexCmd;
union {
ABC_EMPTY_DATA AAA;
ABC_BBB_DATA BBB;
ABC_CCC_DATA CCC;
uint32_t FullData;
};
}ABC_dataStructs;
Then you can define the following:
ABC_CommandList everything = {
.AAA = {.Name = "AAA", .HexCmd = 0x00, .AAA = {}},
.BBB = {.Name = "BBB", .HexCmd = 0x00, .BBB = { .use32BitColor = 0 }},
.CCC = {.Name = "CCC", .HexCmd = 0x00, .FullData = 0x000F },
};
Related
Say you have a struct in C:
typedef struct ID_Info {
uint16_t model_number;
uint16_t serial_number;
uint16_t firmware_version;
} ;
ID_Info id_info;
Now, say I need to set each uint16 variable in this struct to the values of data received byte by byte. So for example, if I received the following bytes: 0x00, 0x11, 0x22, 0x33, 0x44 and 0x55 in some data array data[], I now need to set the values as follows:
id_info.model_number = data[1]*256 + data[0]; // 0x1100
id_info.serial_number = data[3]*256 + data[2]; // 0x3322;
id_info.firmware_version = data[5]*256 + data[4]; // 0x5544;
This is easy enough to hard code as shown above. However, I'd like to be able to do this without hard-coding values and iteratively if possible. Therefore, if I needed to add a variable to the struct, my code and loop would automatically know I need to iterate for two more bytes (assuming a unit16). So this loop would need to iterate foreach member in the struct. Furthermore, is there a way to infer the variable type to know how many bytes I need? Say I needed to add a uint8, and in this case the code could know I only need one byte.
So maybe the pseudo-code would look something like this:
int i = 0;
foreach(member in id_info)
if(member is uint8)
id_info.member = data[i];
i =+ 1;
else if (member is uint16)
id_info.member = data[i] + 256*data[i+1];
i =+ 2;
else
throw error
This way I could easily add and removed struct members without many changes to the code. Thanks in advance for any insight!
If it's not a performance issue (your sample data looks like it isn't), instead of a hard-coded structure with C types, you could define a structure where the type information is encoded, perhaps based on an enum, the name information as a string, and that along with a large enough value type.
The enum type might look like this:
typedef enum {
ui16, ui8
} Type;
One entry could be defined as:
struct entry {
Type type;
char *name;
long value;
};
It is assumed that long is large enough for the largest data type.
A small, self-contained C test program based on your example might then look like the following:
#include <stdio.h>
typedef enum {
ui16, ui8
} Type;
struct entry {
Type type;
char *name;
long value;
};
struct entry id_info[] = {
{ui16, "model_number", 0},
{ui16, "serial_number", 0},
{ui16, "firmware_version", 0}
};
int main(void) {
unsigned char data[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
int x = 0;
for (int i = 0; i < sizeof(id_info) / sizeof(id_info[0]); i++) {
struct entry *current = &id_info[i];
switch (current->type) {
case ui8:
current->value = data[x];
x++;
break;
case ui16:
current->value = data[x] + 256 * data[x + 1];
x += 2;
break;
}
}
//and now print it
for (int i = 0; i < sizeof(id_info) / sizeof(id_info[0]); i++) {
struct entry *current = &id_info[i];
switch (current->type) {
case ui8:
printf("uint8_t %s: %02lx\n", current->name, current->value);
break;
case ui16:
printf("uint16_t %s: %04lx\n", current->name, current->value);
break;
}
}
return 0;
}
The program would produce the following output on the debug console:
uint16_t model_number: 1100
uint16_t serial_number: 3322
uint16_t firmware_version: 5544
One way to do this is with preprocessor macros.
With this method, it is easy to add new elements. And, the import/export functions will be automatically updated.
#ifndef NOINC
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#endif
// define all struct members
#define ALLSTRUCT(_cmd) \
_cmd(uint16_t,"%u",model_number) \
_cmd(uint16_t,"%u",serial_number) \
_cmd(uint16_t,"%u",firmware_version)
// define symbol
#define SYMDEF(_typ,_fmt,_sym) \
_typ _sym;
// define struct
typedef struct ID_Info {
ALLSTRUCT(SYMDEF)
} ID_Info;
ID_Info id_info;
// deserialize
#define SYMIN(_typ,_fmt,_sym) \
do { \
str->_sym = *(_typ *) ptr; \
ptr += sizeof(_typ); \
} while (0);
// serialize
#define SYMOUT(_typ,_fmt,_sym) \
do { \
*(_typ *) ptr = str->_sym; \
ptr += sizeof(_typ); \
} while (0);
// print
#define SYMPRT(_typ,_fmt,_sym) \
printf(" " #_sym "=" _fmt " (%8.8X)\n",str->_sym,str->_sym);
// struct_out -- output struct to byte array
uint8_t *
struct_out(const ID_Info *str,uint8_t *ptr)
{
ALLSTRUCT(SYMOUT)
return ptr;
}
// struct_in -- input struct from byte array
const uint8_t *
struct_in(ID_Info *str,const uint8_t *ptr)
{
ALLSTRUCT(SYMIN)
return ptr;
}
// struct_prt -- print struct to byte array
void
struct_prt(const ID_Info *str)
{
printf("struct_prt:\n");
ALLSTRUCT(SYMPRT)
}
// prtu8 -- print byte array
void
prtu8(const uint8_t *ptr,size_t count,const char *sym)
{
printf("%s:",sym);
for (size_t idx = 0; idx < count; ++idx)
printf(" %2.2X",ptr[idx]);
printf("\n");
}
int
main(void)
{
uint8_t data_in[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
uint8_t data_out[sizeof(data_in)];
// show original byte array
prtu8(data_in,sizeof(data_in),"data_in");
// import data into struct
struct_in(&id_info,data_in);
// show struct values
struct_prt(&id_info);
// export data from struct
struct_out(&id_info,data_out);
// show exported byte array
prtu8(data_out,sizeof(data_out),"data_out");
// reimport the struct data
struct_in(&id_info,data_out);
// show struct data
struct_prt(&id_info);
return 0;
}
Here is the [redacted] preprocessor output:
typedef struct ID_Info {
uint16_t model_number;
uint16_t serial_number;
uint16_t firmware_version;
} ID_Info;
ID_Info id_info;
uint8_t *
struct_out(const ID_Info * str, uint8_t * ptr)
{
do {
*(uint16_t *) ptr = str->model_number;
ptr += sizeof(uint16_t);
} while (0);
do {
*(uint16_t *) ptr = str->serial_number;
ptr += sizeof(uint16_t);
} while (0);
do {
*(uint16_t *) ptr = str->firmware_version;
ptr += sizeof(uint16_t);
} while (0);
return ptr;
}
const uint8_t *
struct_in(ID_Info * str, const uint8_t * ptr)
{
do {
str->model_number = *(uint16_t *) ptr;
ptr += sizeof(uint16_t);
} while (0);
do {
str->serial_number = *(uint16_t *) ptr;
ptr += sizeof(uint16_t);
} while (0);
do {
str->firmware_version = *(uint16_t *) ptr;
ptr += sizeof(uint16_t);
} while (0);
return ptr;
}
void
struct_prt(const ID_Info * str)
{
printf("struct_prt:\n");
printf(" " "model_number" "=" "%u" " (%8.8X)\n", str->model_number, str->model_number);
printf(" " "serial_number" "=" "%u" " (%8.8X)\n", str->serial_number, str->serial_number);
printf(" " "firmware_version" "=" "%u" " (%8.8X)\n", str->firmware_version, str->firmware_version);
}
void
prtu8(const uint8_t * ptr, size_t count, const char *sym)
{
printf("%s:", sym);
for (size_t idx = 0; idx < count; ++idx)
printf(" %2.2X", ptr[idx]);
printf("\n");
}
int
main(void)
{
uint8_t data_in[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
uint8_t data_out[sizeof(data_in)];
prtu8(data_in, sizeof(data_in), "data_in");
struct_in(&id_info, data_in);
struct_prt(&id_info);
struct_out(&id_info, data_out);
prtu8(data_out, sizeof(data_out), "data_out");
struct_in(&id_info, data_out);
struct_prt(&id_info);
return 0;
}
Here is the test program output:
data_in: 00 11 22 33 44 55
struct_prt:
model_number=4352 (00001100)
serial_number=13090 (00003322)
firmware_version=21828 (00005544)
data_out: 00 11 22 33 44 55
struct_prt:
model_number=4352 (00001100)
serial_number=13090 (00003322)
firmware_version=21828 (00005544)
I've been searching for a while what are for square-brackets in an addressed-of pointer, but I continue without understanding it. Here are the lines of the function, where "id" variable is an uint32_t pointer that has been previously passed as an argument.
#define LIST_STARTED (0x0001) /*defined out of the function*/
#define LIST_FIRST (0x0002) /*defined out of the function*/
uint32_t *pointer = id;
uint16_t *flags = &((uint16_t *)pointer)[0];
uint16_t *index = &((uint16_t *)pointer)[1];
bool started = *flags & LIST_STARTED;
bool first = *flags & LIST_FIRST;
if (!started){
/* does something */
*flags = MSEC_PRM_MGMT_LIST_STARTED;
return true;
}
if (!first){
/* does something */
*flags |= MSEC_PRM_MGMT_LIST_FIRST;
*index = 1;
return true;
}
if (*index == final_index)
/* does something */
return false;
*index += 1;
I understand what the logic of the function is, but I don't understand what do the following lines. I put "all" the code above in case it helps you.
uint16_t *flags = &((uint16_t *)pointer)[0];
uint16_t *index = &((uint16_t *)pointer)[1];
I would appreciate if someone could help me!
Thank you!
I edit to say that this C code works fine in an Embedded System, I'm not modifying it, I was just watching its behaviour.
The following code tries to read a uint32_t object as an array of two uint16_t objects:
uint32_t *id = ...;
uint32_t *pointer = id;
uint16_t *flags = &((uint16_t *)pointer)[0];
uint16_t *index = &((uint16_t *)pointer)[1];
and that is undefined behaviour to read a uint32_t object as 2 uint16_t objects through flags and index pointers because that breaks strict aliasing rules.
The correct way is:
uint16_t flags = *id; // The lowest order bits of id.
uint16_t index = *id >> 16; // The highest order bits of id.
In the above assignments of uint32_t to uint16_t it truncates the highest order bits of id.
uint32_t *pointer = id;
uint16_t *flags = &((uint16_t *)pointer)[0];
it is an equivalent of.
uint32_t *pointer = id;
uint16_t *flags = (uint16_t *)pointer;
The definition:
uint16_t *index = &((uint16_t *)pointer)[1];
Is an equivalent of:
uint16_t *temp = (uint16_t *)pointer;
uint16_t *index = temp + 1;
//or
uint16_t *index = &temp[1];
This is called: pointer punning and it is considered dangerous and not portable.
You can use unions for safe punning (at least when using gcc or its derivatives)
typedef union
{
uint64_t u64;
uint32_t u32[2];
uint16_t u16[4];
uint8_t u8[8];
struct
{
uint8_t n1: 4;
uint8_t n2: 4;
}u4[8];
}union_pune_t;
uint16_t foo16(uint32_t *p32)
{
union_pune_t *d64 = (void *)p32;
return d64 -> u16[1];
}
uint8_t foo8(uint32_t *p32)
{
union_pune_t *d64 = (void *)p32;
return d64 -> u8[5];
}
uint8_t foon4(uint32_t *p32)
{
union_pune_t *d64 = (void *)p32;
return d64 -> u4[9].n2;
}
In the function i need to pass in myFlg and somVar as input parameters. They are modified withing the function and then returned newLevel.
Do i pass in pointers for myFlg and somVar and how do i do that. The flg and counter need to be updated inside the function but they are not globals. So pass in two parameters/pointers and return a value, one return value and two modified values/pointers.
in flags.h
#define FlagReg1set_u 0b01000001
typedef union
{
struct
{
uint8_t Flg1 : 1;
uint8_t Flg2 : 1;
uint8_t Flg3 : 1;
uint8_t Flg4 : 1;
uint8_t Flg5 : 1;
uint8_t Flg6 : 1;
uint8_t my_Flg : 1;
uint8_t foo : 1;
} bits;
uint8_t byte;
} FlagReg1_t = FlagReg1set_u;
in level.c
uint16_t level(uint8_t Flag, uint8_t SomeVariable, uint8_t SomeCount)
{
static uint16_t newLevel = SomeVariable;
static uint8_t count = SomeCount;
static uint8_t somVar;
if (FlagReg1.bits.my_Flg == 1)
{
/* do something */
newLevel = 0xFFFF;
}
else
{
FlagReg1.bits.my_Flg = 0
}
if (somVar == SomeVariable)
{
count = 0;
}
else
{
count++;
}
return newLevel
}
in main .c
#include "flags.h"
main()
{
/* Variable Declaration */
uint16_t level(uint8_t Flag, uint8_t SomeVariable, uint8_t SomeCount);
uint8_t count = 0;
uint8_t ownlevel;
uint16_t newLevel;
level(FlagReg1_t my_Flg, ownlevel, count);
if (newLevel == 0)
{
//do something
}
else
{
//do something
}
}
Well excuses to the community I use answer space , this is more a comment but I need the tools to explain.
So , eumac , the usual purpose of a union is:
typedef union eumac_un_t
{
struct eumac_str
{
uint8_t flag_4;
uint8_t flag_3;
uint8_t flag_2;
uint8_t flag_1;
};
int the_flag;
}eumac_un;
This takes 4 bytes (a memory area) , stores the same data which can be retrieved in all the various ways.
Example if you store
eumac_un_t.eumac_str.flag_1 = 150 //(1001 0110)
eumac_un_t.eumac_str.flag_2 = 20 //(0001 0100)
what you get for
eumac_un_t.the_flag == 5270 //its 0000 0000 - 0000 0000 - 0001 0100 - 1001 0110
This is why we use a union.So in your case you won't need it.Is that clear??
Now in your case , usually you pass as pointers when you have an array.If flag is just an integer , then you can simply pass it as an integer.
So yes , exactly the function you mention is very nice
uint16_t level(FlagReg1_t *ptrToFlagReg1, uint8_t *SomeVariable, uint8_t *SomeCount)
{
ptrToFlagReg1->something = something;
ptrToFlagReg1->something_else = something_else;
return number_of_something;
}
Integers don't really have to be passed as pointers if there's no need to.
uint8_t SomeVariable, uint8_t SomeCount
I've been writing some code that deals with USB at the packet level. One "hangup" I've run into is that with USB, you have some structs that contain arrays of other structs (not pointers):
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
} PACKED endp_descriptor_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface;
endp_descriptor_t endpoints[];
} PACKED int_descriptor_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
int_descriptor_t interfaces[];
} PACKED cfg_descriptor_t;
The cfg_descriptor_t contains zero or more int_descriptor_ts, and each int_descriptor_t contains zero or more endp_descriptor_ts.
I'm working on a microcontroller with fairly limited space (256K flash, 64K RAM), so I wanted to avoid dynamic allocation as much as possible and define these structs at compile time:
static cfg_descriptor_t cfg_descriptor = {
.bLength = 9,
.bDescriptorType = 2, //config descriptor
.wTotalLength = 18,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0x80, //bus powered, no remote wakeup
.bMaxPower = 250, //x2 = 500mA (maximum allowed by spec)
.interfaces = {
{
.bLength = 9,
.bDescriptorType = 4, //interface
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = 0xFF,
.bInterfaceSubClass = 0x0,
.bInterfaceProtocol = 0x0,
.iInterface = 0,
.endpoints = {
{
.bLength = 7,
.bDescriptorType = 5, //endpoint
.bEndpointAddress = 0x81,
.bmAttributes = 0x03,
.wMaxPacketSize = 64,
.bInterval = 10,
}
}
}
}
};
Unfortunately, it seems gcc doesn't understand what I'm trying to do here, and complains about too many initializers for βint_descriptor_t [0]β - i.e. it apparently can't determine the length of the array from the initializers. (Maybe I've just made some simple mistake here?)
I thought I might take advantage of C++11's variadic templates for this:
template<typename... interfaces>
struct PACKED x_cfg_descriptor_t {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
interfaces...;
};
but this doesn't work either: expected unqualified-id before β...β token on the interfaces... line.
I've seen some examples of variable-sized tuples using variadic templates, but I couldn't quite understand what they were doing, and they suggested that the actual layout in memory would be in reverse order of the template parameters. (I think recursive templates are involved...)
So the question is: can I define a static struct instance with a variable number of members at compile time?
Well, I didn't exactly answer the question, but I found a much better method: move the arrays out of the structs, and define one large struct:
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
} PACKED endp_descriptor_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface;
//endp_descriptor_t endpoints[];
} PACKED int_descriptor_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
//int_descriptor_t interfaces[];
} PACKED cfg_descriptor_t;
static struct {
cfg_descriptor_t cfg;
int_descriptor_t iface[1];
endp_descriptor_t endp[1];
} PACKED cfg_descriptor = {
.cfg = {
.bLength = 9,
.bDescriptorType = 2, //config descriptor
.wTotalLength = 18,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0x80, //bus powered, no remote wakeup
.bMaxPower = 250, //x2 = 500mA (maximum allowed by spec)
},
.iface = {
{ //iface[0]
.bLength = 9,
.bDescriptorType = 4, //interface
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = 0xFF,
.bInterfaceSubClass = 0x0,
.bInterfaceProtocol = 0x0,
.iInterface = 0,
},
},
.endp = {
{ //endp[0]
.bLength = 7,
.bDescriptorType = 5, //endpoint
.bEndpointAddress = 0x81,
.bmAttributes = 0x03,
.wMaxPacketSize = 64,
.bInterval = 10,
}
}
};
Using PACKED (which expands to __attribute__((__packed__))) ensures the memory layout is still what USB expects, and this allows to define any number of endpoints and interfaces per configuration descriptor. The downside is that I don't see a way to have this in an external file, besides defining a typedef struct for every configuration descriptor (and having to update it in two different places) or exporting it as a void* (and being unable to address individual fields).
This doesn't really answer the question about whether variadic templates can be used for this purpose, though...
Is it possible to initialise an array of uint8_t with a struct?
What I want to achieve is something similar to:
#define BIGGER_THAN_STRUCT 1024
struct Device {
uint32_t address;
uint32_t id;
};
const uint8_t bytes[BIGGER_THAN_STRUCT] = (struct Device) {
.address = 123,
.id = 456,
};
The reason I want to do this it to get an easy overlay view of the content I write to the byte array. I simply want an easy interface to whatever first bytes I need for the information that is displayed by the structure.
If thats not possible, what's the closest thing to it?
The standard way in C to overlay data types is by using unions:
#include <stdio.h>
#include <stdint.h>
#define BIGGER_THAN_STRUCT 1024
struct Device {
uint32_t address;
uint32_t id;
};
union Memory {
uint8_t bytes[BIGGER_THAN_STRUCT];
struct Device devices[BIGGER_THAN_STRUCT/sizeof(struct Device)];
};
const union Memory memory = {
.devices = {
{ .address = 123, .id = 30 },
{ .address = 111, .id = 89 }
}
};
int main(void)
{
unsigned i;
for (i = 0; i < 16; i++)
printf("%d ", memory.bytes[i]);
putchar('\n');
return 0;
}
,
$ ./a
123 0 0 0 30 0 0 0 111 0 0 0 89 0 0 0
Beside approaching this via a union (as proposed by hdante here https://stackoverflow.com/a/27462808/694576) instead of trying:
const uint8_t bytes[BIGGER_THAN_STRUCT] = (struct Device) {
.address = 123,
.id = 456,
};
do quick and dirty:
uint8_t bytes[BIGGER_THAN_STRUCT] = {0};
*((struct Device *) bytes) = ((struct Device) {
.address = 123,
.id = 456,
});
or better do:
struct Device dev = {
.address = 123,
.id = 456,
};
uint8_t bytes[BIGGER_THAN_STRUCT] = {0};
...
size_t size_dev = sizeof dev;
memcpy(bytes, &dev, size_dev);
Then inspect array bytes up to the size_dev - 1th element.
This will do it then
static const uint8_t buffer[BIGGER_THAN_STRUCT] = {
0x7b, 0x00, 0x00, 0x00,
0xc8, 0x01, 0x00, 0x00
};
I think you maybe want to do something like that, even if the copy is not that necessary as b_sample is exactly what you need.
#include <stdio.h>
#include <stdint.h>
typedef struct Device dev;
struct Device {
uint32_t address;
uint32_t id;
};
int main(void) {
//create an instance `sample.address=123` and `sample.id=456`
dev sample = (dev) { 123, 456 };
//convert dev pointer to byte pointer, so you loop through bytes
uint8_t* b_sample = (uint8_t *)(&sample);
//buffer for copy
uint8_t* bytes[1024];
int size = (int)(sizeof(dev)/sizeof(uint8_t)), i;
for(i = 0; i < size; i++) {
bytes[i] = b_sample[i];
//see what values you copy
printf("%x ", bytes[i]);
}
return 0;
}
Demo: http://codepad.org/wE8dbBV1
If you want to divide the struct into uint16_t segments you can safely replace all uint8_t with uint16_t
Usually, when I have to work with bag of structured bytes, I create a "view" struct/class that gives me some higher-level interface to a block of memory. Manual pointer arithmetic is usually too error prone to be repeated. Create a "memory view" structure and unit-test it properly.
struct memory_view {
uint32_t *addr;
uint32_t *id;
};
void view_init(struct memory_view *view, void *buf, size_t bufsz) {
// TODO: validate buffer size using bufsz here
view->addr = (uint32_t*)buf;
view->id = (uint32_t*)(buf + sizeof(uint32_t));
}
struct memory_view view;
uint8_t buffer[LARGE_NUMBER];
view_init(&view, buffer, LARGE_NUMBER);
*view->addr = 0xDEADBEEF;
*view->id = 0xCAFEBABE;
You can see a similar technique in device drivers, when structures are initialized to access different hardware registers located in some memory region.
You could also get a buffer pointer, cast it to structure and try using this memory block as it were a structure. Doable, but memory alignment can bite you hard. Such code may or may not work, depending on compiler and system architecture.
Does this:
#define BIGGER_THAN_STRUCT 1024
struct Device {
uint32_t address;
uint32_t id;
};
struct DeviceAndData {
struct Device d;
char filler[BIGGER_THAN_STRUCT - sizeof(Device)];
};
const struct DeviceAndData bytes_pre = { .d = { .address = 123, .id = 456 } };
const uint8_t* bytes = (uint8_t*)&bytes_pre;
do the trick? :)
There is an alternative to using a uint8_t byte[] array. You can also make use of a struct utilizing a bitfield for each addr and id. You (may/may not) find it more convenient, but it does provide an easy way to keep the offset information associated with any give addr/id pair.
I don't believe there is a way to directly make use of struct type Designated Initializers to fill the uint8_t byte array. I think the closest full initialization would be with memcpy. I've included that in the example below. Note, there is nothing that prevents you from filling the uint8_t byte array with memcpy, but then you have to track the offset within the uint8_t byte array to accurately point to any given byte in either addr or id for any given element. This is where the bitfield makes things a little easier. You get a one-to-one correlation between the struct Device index and the uibitfield index with a1..4 and b1..4 being the bytes within each addr and id, respectively.
A version using the uint8_t array is shown below this version.
Here is a short example with test data in an array of struct Device:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
typedef struct /* bitfield corresponding to struct Device */
{
unsigned int a1 : 8,
a2 : 8,
a3 : 8,
a4 : 8;
unsigned int b1 : 8,
b2 : 8,
b3 : 8,
b4 : 8;
} uibitfield;
struct Device { /* original struct Device */
uint32_t addr;
uint32_t id;
};
int main () {
/* test data in an array of struct Device */
struct Device dev[] = { {0x4009f0, 0}, {0x4009f1, 1}, {0x4009f2, 2}, {0x4009f3, 3},
{0x4009f4, 4}, {0x4009f5, 5}, {0x4009f6, 6}, {0x4009f7, 7},
{0x4009f8, 8}, {0x4009f9, 9}, {0x4009fa, 10}, {0x4009fb, 11},
{0x4009fc, 12}, {0x4009fd, 13}, {0x4009fe, 14}, {0x4009ff, 15},
{0x400a00, 16}, {0x400a01, 17}, {0x400a02, 18}, {0x400a03, 19} };
int it = 0; /* general iterator */
size_t sz = sizeof (dev)/sizeof (*dev); /* size of array */
/* create validate and fill bitfield array */
uibitfield *bytes = calloc (sz, sizeof (*bytes));
if (!bytes) {
fprintf (stderr, "error: allocation failed.\n");
return 1;
}
memcpy (bytes, dev, sz * sizeof (dev));
/* print bytes in each addr & id in dev */
for (it = 0; it < sz; it++)
printf ("\n addr[%2d]: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n id[%2d]: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
it, (bytes + it)->a1, (bytes + it)->a2, (bytes + it)->a3, (bytes + it)->a4,
it, (bytes + it)->b1, (bytes + it)->b2, (bytes + it)->b3, (bytes + it)->b4);
printf ("\n");
return 0;
}
output:
$ ./bin/memview
addr[ 0]: 0xf0, 0x09, 0x40, 0x00
id[ 0]: 0x00, 0x00, 0x00, 0x00
addr[ 1]: 0xf1, 0x09, 0x40, 0x00
id[ 1]: 0x01, 0x00, 0x00, 0x00
addr[ 2]: 0xf2, 0x09, 0x40, 0x00
id[ 2]: 0x02, 0x00, 0x00, 0x00
addr[ 3]: 0xf3, 0x09, 0x40, 0x00
id[ 3]: 0x03, 0x00, 0x00, 0x00
addr[ 4]: 0xf4, 0x09, 0x40, 0x00
id[ 4]: 0x04, 0x00, 0x00, 0x00
(snip)
Note: it was a unclear how you would be using/filling struct Device and how much of an initial peek you wanted at the data in stuct Device, so this is just intended as an example of viewing the data.
using a uint8_t byte array:
If you do want to use the `uint8_t array, the changes needed are minimal:
/* using a uint8_t byte array */
uint8_t *bytearr = calloc (sz * 4, sizeof (*bytearr));
if (!bytearr) {
fprintf (stderr, "error: allocation failed.\n");
return 1;
}
memcpy (bytearr, dev, sz * sizeof (dev));
/* print bytes in each addr & id in dev using uint8_t array */
for (it = 0; it < sz * 4; it+=8)
printf ("\n addr[%2d]: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n id[%2d]: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
it, bytearr[it], bytearr[it+1], bytearr[it+2], bytearr[it+3],
it, bytearr[it+4], bytearr[it+5], bytearr[it+6], bytearr[it+7]);
output is the same