How to replicate this structure in C

Go To Last Post
21 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Busy learning CVAVR after many years of ASM. Not doing too bad, but this exceeds my ability...

I have an ASM program that has a lot of "structures", all of similar shape but different size defined like this (different size means different number of bytes from the 3rd byte onwards and different numbers of data bytes):

.equ DATA_BYTE = 0x00
.equ _NUL      = 0x00
.equ _ESC	   = 0x1B
.equ _DC1	   = 0x11
.SET X_POS     = 120
.SET Y_POS     = 150

EEPROM_spi_XXX1_start:
	.db 	_DC1, (EEPROM_spi_XXX1_end - EEPROM_spi_XXX1_start - 1)
	.db		_ESC, "Z", "F", "0", _ESC, "F", "Z", "1", "P", "Z", "L", low(X_POS),high(X_POS),low(Y_POS),high(Y_POS)
EEPROM_spi_XXX1_value:
	.db		DATA_BYTE
	.db		DATA_BYTE
	.db		DATA_BYTE
	.db		DATA_BYTE
EEPROM_spi_XXX1_end:
	.db		_NUL
.equ spi_XXX1_offset = EEPROM_spi_XXX1_value - EEPROM_spi_XXX1_start

EEPROM_spi_XXX2_start:
...etc

The second byte is the size of the structure after the second byte. It's worked out by the assembler meaning I don't have to. Also I need to know the offset from the start of the structure data where the data bytes are so I can insert data at the right places at run time (before sending the whole stream of bytes via SPI to an external device) - the assembler also works that out for me and gives me a value(?) I can use in the code.

So how could I declare and use a similar structure in C?

(hope this makes sense - ask if it doesn't :))

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would make one structure that handles all conditions.

struct MyStruct
{
    uint8_t DC1;
    char *Text;
    int X_POS;
    int Y_POS;
    uint8_t *Data;
}

The size of what Text is pointing to would not be needed since it could be a C string. You would need a way of determining the size of DATA at run time, so you might want one more entry in the struct for that.

You don't need the offset to elements since you just use the name.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You could use a union of the "tail" sub-structures sharing the first three bytes/fields of the "head" structure. But I'd just cut-and-paste and have a separate structure for each record type. Once you peek at the ID bytes you can then cast to apply the correct template.

For the offset, here is a piece of one of my apps, with a menu system to modify values:

//
//	"Outputs" Information -- Digital Outputs module/offset "address", type, mask (for multiple CIP)
//	=============================================
struct output_struct
{
unsigned char	module;			// hard-code module 0 as the display, so 0 here is "unused"
unsigned char	offset;			// bit location 1<<offset, or channel number
//char	name[9];		// arbitrary length
unsigned char	nameidx;		// future index into I/O signal names
unsigned char	type;			// SIGNAL_DIGITAL, _DRIVER, _BOOST
// Add the period/duty "PWM" information to this table, as well as dispense info
unsigned char	sec_period;		// "PWM" period setpoint in seconds, max 240
unsigned char	sec_duty;		// "PWM" duty setpoint in seconds, max 240
unsigned int	oz_to_squirt;	// alternate/additional ounces per period and/or [tbd] time
};


typedef	struct output_struct	Output;

...
// Menu/field data
//	=============================================
struct				field_map
{
char flash *			title;
char flash *			units;
unsigned char			line1;	// tbd First line is "title" of the field
unsigned char			line2;	// tbd Second line is the "units" and value of the field
int						min;	// min/max values
int						max;
unsigned char			type;	// field type; decides how to display
unsigned char			incr;	// Up-increment value
unsigned char			index;	// where to find the value in EEPROM or SRAM (offset from a base)
unsigned char			width;	// 1=byte; 2=word
unsigned char			col;	// cursor column
unsigned char			row;	// cursor row
};

typedef	struct field_map		Field;

...
// Outputs
//	Output channels are 0-relative in the outputs table ee_output.
flash	Field	ffields_output[] =
{ //	title,	units,			line1,	line2,	min,	max,	type,	incr,	index,	width,	col,	row
	{menu_rec_mod, menu_rec_mod_units,	1,	2,	2, 	32,	ITEM_MOD,	1,
												offsetof(Output, module),	1,	19,	2},
	{menu_rec_type, menu_rec_type_units,	1,	2,	5, 	8,	ITEM_RESOURCE,	1,
												offsetof(Output, type),	1,	19,	2},
	{menu_rec_chan, menu_rec_chan_units16,	1,	2,	1, 	16,	ITEM_1REL,	1,
												offsetof(Output, offset),	1,	19,	2},
	{menu_out_duty, menu_rec_ho_units,	1,	2,	0, 	240,	ITEM_3DIGIT,	10,
												offsetof(Output, sec_duty),	1,	19,	2},
	{menu_out_period, menu_rec_ho_units,	1,	2,	0, 	240,	ITEM_3DIGIT,	10,
												offsetof(Output, sec_period),	1,	19,	2},
	{menu_out_squirt, menu_out_ounces,		1,	2,	0, 	999,	ITEM_3DIGIT,	10,
												offsetof(Output, oz_to_squirt),	1,	19,	2},
};

Quote:

The second byte is the size of the structure after the second byte.

For that you'd use sizeof(struct frog). And probably subtract your 1 or 2 or HEADER_SIZE or whatever.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You cannot declare a variable size structure in C. You can however use a few tricks.

struct EEPROM_spi {
    uint8_t dc1;
    uint16_t size;
    uint8_t string[11];
    uint16_t xpos, y_pos;
    uint8_t variable_len[1];
};

When you come to allocate a new struct, you malloc() sizeof(struct EEPROM_spi) + # of DATA_BYTEs.
Copy all the requisite data into the fields and calculate the size field.

If you want to statically initialise the whole caboodle, you have two choices:
1. declare the largest array ever needed for 'variable_len[BIG]'
2. declare the variable strings in separate memory. use a pointer in the struct.

You can do all sorts of clever things in assembler. Especially those forward references to size and offsets.

In C, you have to declare the strings first if you want to initialise anything with a pointer to the string.

Using Koshchi's struct you can initialise quite easily. Remember that in CodeVision anonymous strings are 'char flash *'. Named strings will be 'char *' to SRAM.

Personally I would do a bit of practice with printable strings just to get the hang of it.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

eek. Just on/above my limit of understanding. I'll have a play using some of these ideas. Thanks all....

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Koshchi wrote:
The size of what Text is pointing to would not be needed since it could be a C string.
Doh - that was the other issue...some of the data bytes are 0x00 and I presume that would mess up trying to hold them within a string?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

can't put 8 bit binary data bytes in a "string". Need to use an array of bytes and a separate variable stating the current number of bytes in the array.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
some of the data bytes are 0x00 and I presume that would mess up trying to hold them within a string?
Then don't treat the data as a string. If what Text and Data are pointing to are arrays whose size is known at compile time, then there is really no problem. You just store the size at the same time you assign the array to the pointer.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK - I'm just about keeping up with this. I'm having some success using simple byte arrays rather than structures and working out the size by hand/inserting the size at run time via sizeof() calls.

I'm struggling more with simple C stuff that I can do with my eyes shut in ASM...how can I just replace a element in an array

uint8_t cmd1[5] {0x1b, 3, 0xFF, 0xFF, 0xFF};
//...where the 0xFF's are placeholders for run time values 
//...where I just want to do
cmd1[3] = 0x00;
cmd1[4] = 'S';
cmd1[5] = '8';

..which of course is invalid C (not a modifiable lvalue or some such nonsense) :)

EDIT: and those indexes should probably be 2, 3, and 4 anyway

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Array indices are ZERO based. Your three 0xFF's are at elements cmd1[2], cmd1[3] and cmd1[4].

(not sure why you got the lvalue warning though I do note that you have forgotten the = sign before the { when setting the initial values ;-)).

try:

uint8_t cmd1[5] = {0x1b, 3, 0xFF, 0xFF, 0xFF}; 
//...where the 0xFF's are placeholders for run time values 
//...where I just want to do 
cmd1[2] = 0x00; 
cmd1[3] = 'S'; 
cmd1[4] = '8';

BTW you'll hit the zero based thing again if you try to iterate through the array with a for() loop. Something like:

for (i=0; i<5; i++) {
  uart_send(cmd1[i]);
}

and not:

for (i=1; i<=5; i++) {
  uart_send(cmd1[i]);
}

Note that the correct initial value for the array index is 0 and not 1. And the end test is <5 ("less than five") not <=5 ("less than or equal to five")

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Guilty as charged:
- charge 1 - not pasting exact code (missing '=')
- charge 2 - being a C newbie (but I did spot the 2,3,4 indexing error 10 secs after my post and made an edit)

lvalue problem was when I had the byte array inside a structure (IIRC - but I've binned the structure idea as too hard anyway)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

lvalue problem was when I had the byte array inside a structure (IIRC - but I've binned the structure idea as too hard anyway)

But structures are one of the nice features of C (in fact the entire C++ language is effectively an extension of the concept). Being able to group different types to form a "data record" is really useful. All that would change in your example is something like:

// this defines the structure...
typedef struct {
 int number;
 char c;
 uint8_t cmd1[5];
} my_struct_type;

// this creates and initialises a structure of that type...

my_struct_type a_structure = { 
  12345, 
  'A', 
  {0x1b, 3, 0xFF, 0xFF, 0xFF}
}; 

// and this changes the entries in the array...
a_structure.cmd1[2] = 0x00; 
a_structure.cmd1[3] = 'S'; 
a_structure.cmd1[4] = '8';

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK - if you're happy to spoon-feed I'm happy to learn :)

How would I then create (and call) a function to 'walk through' an arbitrary structure (I have many, one for each command type all fundamentally the same as above but with the cmd1 element being different lengths) byte by byte, spi'ing the individual bytes, including .number being sent lsb first?

EDIT - maybe I don't need multiple structures if I can use an element cmd1[], rather than cmd1[5], but that's where I came in and got stuck

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well you can pass structures in C but usually it's easier to pass a pointer to a structure (the key thing to then remember is that elements in the structure will be accessed with -> rather than . as before). Usually you'd write the function to take a pointer to the specific type of structure. Something like this:

typedef struct {
 int n;
 char c;
 uint8_t arr[5];
} struct_type_A;

void set_struct(struct_type_A * p_struct) {
  p_struct->n = 12345;
  p_struct->c = 'A';
  p_struct->arr[3] = 'S';
}

int main(void) {
  struct_type_A struct_A;

  set_struct(&struct_A);
}

& means "the address of" so what gets passed to the routine is the address where struct_A is located. The entry into the routine is (struct_type_A * p_struct) in which the * means "pointer to". So this creates a local variable called p_struct which is a "pointer to a struct of type struct_type_A". As the call used & to send the address the two things match up nicely. As I said above the only difference when using a pointer is that you cant use p_struct.n which means "the n element within the p_struct" but instead, because p_struct is not a struct but just holds the "address of a struct" you use p_struct->n which means the "n element within the struct that p_struct is pointing to" (where "pointing to" means "is holding the address of").

If you are saying that the same routine has to accept the addresses of different types of structs then things are a bit more complicated...

typedef struct {
 char struct_type;
 int n;
 char c;
 uint8_t arr[5];
} struct_type_A;

typedef struct {
 char struct_type;
 long l;
 short s;
 uint16_t arr[3];
} struct_type_B;


void set_struct(void * p_struct) {
  struct_type_A * p_A = (struct_type_A *)p_struct;
  struct_type_B * p_B = (struct_type_B *)p_struct;

  if (p_A->struct_type == 'A') {
    p_A->n = 12345;
    p_A->c = 'A';
    p_A->arr[3] = 'S';
  }
  else {
    p_B->l = 0xDEADBEEF;
    p_B->s = 22222;
    p_B->arr[1] = 4567;
  }
}

int main(void) {
  struct_type_A struct_A;
  struct_type_B struct_B;

  struct_A.struct_type = 'A';
  struct_B.struct_type = 'B';
  set_struct((void *)&struct_A);
  set_struct((void *)&struct_B);
}

Here I rely on the fact that the first 'char' in each struct is a "struct_type" and is set to 'A' or 'B' to say what kind of struct the rest is. When the address of the struct's are passed to set_struct() the (void *) in brackets is a typecast and says "while this may well be a pointer to a specific type forget that for a moment and just treat this as a generic pointer that could be pointing to anything".

In the set struct function I then assign the same passed pointer to both p_A (a pointer to struct type A) and p_B (a pointer to type B). I then just treat it as if it's a pointer to A (because both structs have the first "struct_type" char the same so it doesn't matter) to see if it's really a type A or a type B. Within the else I then use either the p_A or the p_B pointer to access the relevant elements.

Sorry if this is all a bit complex but what you appear to be asking to do IS something that is quite complex. Maybe my colleagues here have a "better" solution.

(I think the suggestion of a union within the struct has already been suggested - maybe that was the better solution?).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Excellent - I definitely understand, just not clever enough (yet :)) to come up with it myself.

So the final element is calling a function passing passing a pointer to one of the specific structures (and probably a sizeof()) where I can somehow interpret the pointer as the first memory location and walk through memory one byte at a time for sizeof() elements, uart'ing or spi'ing them out?

That assumes:
- the compiler stores structures in contiguous memory in the order defined in the C definition (I think so)
- EDIT: that walking through the bytes will give me the (needed) LSB of an int followed by the MSB (I also thinks so, but not really sure)
- I knew how to "walk through memory" starting at a position given by the pointer for an arbitrary sizeof() passed number of bytes (which I don't)

Thanks so far - a great read, hopefully for others as well.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Martin,

Here is a little CodeVision program: (change # to %)

#include 

// Standard Input/Output functions
#include 
#include 

struct MyStruct
{
    uint8_t DC1;
    char flash *Text;
    int X_POS;
    int Y_POS;
    uint8_t *Data;
};
// Declare your global variables here
struct MyStruct david = { 17, "\033ZF0\033FZ1PZL", 120, 150, "David Prentice"};
struct MyStruct cliff = { 17, "\033ZF0\033FZ1PZL", 120, 200, "Cliff Lawson"};
struct MyStruct brian = { '1', ":text", 120, 150, "Brian Jones"};
struct MyStruct dennis = { '5', ":different text", 120, 200, "Dennis Thatcher"};

void showstruct(struct MyStruct *msg)
{
    printf("#c#p at #d,#d then #s\r\n", msg->DC1, msg->Text, msg->X_POS, msg->Y_POS, msg->Data);
}

void main(void)
{
    // Declare your local variables here
    // Crystal Oscillator division factor: 1
#pragma optsize-
    CLKPR = 0x80;
    CLKPR = 0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
    // USART initialization
    // Communication Parameters: 8 Data, 1 Stop, No Parity
    // USART Receiver: On
    // USART Transmitter: On
    // USART0 Mode: Asynchronous
    // USART Baud Rate: 9600
    UCSR0A = 0x00;
    UCSR0B = 0x18;
    UCSR0C = 0x06;
    UBRR0H = 0x00;
    UBRR0L = 0x67;

    while (1) {
        // Place your code here
        showstruct(&brian);
        showstruct(&dennis);
        while (1);           // just stop here
    }
}

Note that you can initialise your struct with data from flash or SRAM. Obviously it depends on whether you want to change your struct contents at run time or not.

The real advantage of struct is that you just write your functions to take a struct pointer. Life becomes a lot easier.

David.

p.s. you can have arrays of struct. Or you can simply have an array of struct pointers. Or you can have linked lists.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If all you want to do is UART_send() a variable length structure I'd go with something MUCH simpler:

typedef struct { 
 int n; 
 char c; 
 uint8_t arr[5]; 
} struct_type_A; 

typedef struct { 
 long l; 
 short s; 
 uint16_t arr[3]; 
} struct_type_B; 

UART_send(uint8_t * p, uint16_t len) {
  uint16_t i;
  for (i=0; i

In this I removed the "struct_type" field from each struct as the type doesn't matter, only the length. I then defined the interface to the UART_send() function (which assumes there is a UART_putchar() to send single bytes) to accept a pointer to uint8_t (that is unsigned char or just "raw bytes"). The function also takes a separate parameter that is the number of bytes to send (the length of the struct).

Each time the function is invoked from main() I use a typecast (uint8_t *) to convert the address of the struct type A or B to just be a pointer to raw bytes which is what the function expects. I also use C's sizeof() function which executes at compile time. It can see that struct type A is 2+1+5 bytes and struct type B is 4+2+6 bytes so the code compiles as if it says:

  UART_send((uint8_t *)&struct_A, 8);
  UART_send((uint8_t *)&struct_B, 12);

And the loop in UART_send then just outputs 8 or 12 "raw" bytes, ignoring the actual structure of the struct. (and yes multi-byte fields are held little endian so 0xDEADBEEF would send 0xEF then 0xBE then 0xAD then 0xDE)

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I may be completely wrong, but I think that Martin just wants to send some data to various different o/p devices.

So he will use different parts of the structure for different jobs.

Personally, I would keep the different fields that are appropriate to their use. Then call suitable functions that o/p little-endian integers or escape sequences.

I doubt that there is any need to pack a structure in the exact order that one particular o/p function might need.

This is the fundamental difference between the ASM mindset and a HLL mindset. Yes, I would have written the ASM exactly the same as the OP's original quote.
Nowadays, I would use the 'struct' approach even with Assembler.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Excellent - I think I can see taking elements of both the above (David's structures and Cliff's outputting maybe - but I don't want to start a war :)) to make what I'm after.

Time to get experimenting - shame about having to do a day job at the same time. I'll report back...

Thanks again - it's really appreciated.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
I may be completely wrong, but I think that Martin just wants to send some data to various different o/p devices.
So he will use different parts of the structure for different jobs.
Close. It's the same device (a SPI connected high end graphics LCD) where you send different commands (there are 100's of them):
- draw a circle with this colour line centred at x,y of radius r
- draw a bargraph in this x1,y1,x2,y2 rectangle with a scale of 0 to 100, a current value of 67 and a label of "Intensity"
- etc

Each command is different in terms of construction but there are common elements:
- byte 1 - "start command" byte
- byte 2 - number of bytes in the command (= N plus 1 for the 8 bit checksum at the end)
- bytes * N
- a checksum

bytes * N are structured depending on the command - lots have X and Y - some don't, like Clear Screen - (and which will be fixed in my app), some have colours of fonts to be used (which may be fixed at compile time or variable at run time), some have the text for labels (probably fixed) etc.

So in the code I want easy, named, access to the different parts of each command I want to vary at run time and a generic way of SPI'ing the specific structure out to the display. Which is what my ASM technique gave me.

(...thus making this thread just like the ones I really hate - OP comes along with a specific generic question and eventually ends up admitting what he really wants right at the end. Which, if that was known at the start.... :D)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
in fact the entire C++ language is effectively an extension of the concept
And in fact provides an ideal solution to the problem. You create a base class of a command to send which has the code to do the sending (but is an abstract class so it has no data to send and is never used by itself). Then create subclasses for each command that contain the space for the data to send. Then all you need to do is instantiate a class of the proper type, fill in the data that needs to be sent, then call its send function.

Regards,
Steve A.

The Board helps those that help themselves.