Is it possible, in C, to create an array of arrays of sructs?

Go To Last Post
62 posts / 0 new

Pages

Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have a struct.  I also have several arrays of different lengths (stored in flash) containing instances of this struct.  Is it possible to create an array of these arrays?  I don't care if I have to waste some space by making all the elements the same length as the longest array.  Thanks.

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

Sure, you can do it.  Sometimes the access may get confusing, with multiple levels of indirection.

 

Sometimes, I'll make the higher/highest level an array of pointers to the subordinate(s), rather than a full array with the subordinates.  In particular, this sidesteps (at least to some extent) the storage issue with variable-length constructs.

 

A simple (nearly trivial) example...in this case the "character strings" (line buffers) are all the same length but wouldn't have to be.

 

char			line1[24];	// 20-char LCD line plus a few more
char			line2[24];	// 20-char LCD line plus a few more
char			line3[24];	// 20-char LCD line plus a few more
char			line4[24];	// 20-char LCD line plus a few more

char * flash line_ptrs[4]={line1, line2, line3, line4};

 

 

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

Yes.

 

Note that it gets a bit hairy accessing everything from Flash with the legacy PROGMEM.

 

If you use the new __flash modifier,   the new GCC should look after this painlessly.   (just like the other Compilers have done since Day One)

 

In practice,  you either have fixed length arrays inside a struct.   Or you store named arrays separately.  With pointers to these named arrays/strings.    I am guessing that the "variable length" means char strings.

 

David.

 

Edit.   Lee has shown Codevision example.   CV has always handled flash.    GCC now has __flash.   G++ does not have __flash.

 

Last Edited: Wed. May 10, 2017 - 08:45 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
CV has always handled flash.

And as of 3.x (or maybe 2.x), CV also will take the __flash to keep namespaces purer.

 

The flash or __flash keywords can be used to specify that a constant must be placed in FLASH memory, no matter what is the state of the Store Global Constants in FLASH Memory option:

 

flash <type definition> <identifier> = constant expression;

__flash <type definition> <identifier> = constant expression;

...

Accessing the AVR internal EEPROM is accomplished using global variables, preceded by the eeprom or __eeprom memory attributes

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

Thanks, but it's not working for me yet.  If I initialize with:

struct Frame * Animations[3] = {loop1, loop2, loop3};   // Frame is the name of the struct, loopn is the name of the array of Frames in flash

I get a compiler error:  invalid initialization type; found `pointer to __flash struct Frame' expected `pointer to struct Frame'|

 

If I initialize with:

 struct Frame * flash Animations[3] = {loop1, loop2, loop3};

I get the same error.

 

If I initialize with:

struct Frame * __flash Animations[3] = {loop1, loop2, loop3};

Same error.  I'm using Jumpstart C (formerly ICCAVR) which supports __flash.  That's how I declare the loopn arrays.

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

Ah-ha.  I had assumed you were using GCC.

 

Please post a minimal actual code.   I don't have Jumpstart C.    I do have CV and GCC.    They should all recognise __flash keyword.

The actual position of the keyword will affect its meaning.

 

David.

 

 

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

Here are the declarations as they appear in the code (in this order):

struct Frame {
    unsigned char digit;    // Digit:  0 = 1 minutes, 1 = 10 minutes, etc.
    unsigned char seg;      // Segment to turn on:  a - g
    unsigned char onoff;    // 0 = segment off, 1 = segment on
    unsigned char dur;      // Duration in msec. to maintain segment state
};

__flash struct Frame loop1[] = {
{0, 'a', 1, 50}, {0, 'a', 0, 50}, {0, 'b', 1, 50}, {0, 'b', 0, 50}, {0, 'c', 1, 50}, {0, 'c', 0, 50}, {0, 'd', 1, 50}, {0, 'd', 0, 50},
{1, 'd', 1, 50}, {1, 'd', 0, 50}, {2, 'd', 1, 50}, {2, 'd', 0, 50}, {3, 'd', 1, 50}, {3, 'd', 0, 50}, {3, 'e', 1, 50}, {3, 'e', 0, 50},
{3, 'f', 1, 50}, {3, 'f', 0, 50}, {3, 'a', 1, 50}, {3, 'a', 0, 50}, {2, 'a', 1, 50}, {2, 'a', 0, 50}, {1, 'a', 1, 50}, {1, 'a', 0, 50}
};

__flash struct Frame loop2[] = {
{0, 'a', 1, 50}, {0, 'a', 0, 50}, {0, 'b', 1, 50}, {0, 'b', 0, 50}, {0, 'c', 1, 50}, {0, 'c', 0, 50}, {0, 'd', 1, 50}, {0, 'd', 0, 50},
{1, 'd', 1, 50}, {1, 'd', 0, 50}, {2, 'd', 1, 50}, {2, 'd', 0, 50}, {3, 'd', 1, 50}, {3, 'd', 0, 50}, {3, 'e', 1, 50}, {3, 'e', 0, 50},
{3, 'f', 1, 50}, {3, 'f', 0, 50}, {3, 'a', 1, 50}, {3, 'a', 0, 50}, {2, 'a', 1, 50}, {2, 'a', 0, 50}, {1, 'a', 1, 50}, {1, 'a', 0, 50}
};

__flash struct Frame loop3[] = {
{0, 'a', 1, 50}, {0, 'a', 0, 50}, {0, 'b', 1, 50}, {0, 'b', 0, 50}, {0, 'c', 1, 50}, {0, 'c', 0, 50}, {0, 'd', 1, 50}, {0, 'd', 0, 50},
{1, 'd', 1, 50}, {1, 'd', 0, 50}, {2, 'd', 1, 50}, {2, 'd', 0, 50}, {3, 'd', 1, 50}, {3, 'd', 0, 50}, {3, 'e', 1, 50}, {3, 'e', 0, 50},
{3, 'f', 1, 50}, {3, 'f', 0, 50}, {3, 'a', 1, 50}, {3, 'a', 0, 50}, {2, 'a', 1, 50}, {2, 'a', 0, 50}, {1, 'a', 1, 50}, {1, 'a', 0, 50}
};

Here's main(), up to where the error is thrown (just before init();):

int main(void)
{
    char i = 0, x = 0, y, z, a;
    struct Frame * __flash Animations[3] = {loop1, loop2, loop3};
    init();

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
__flash struct Frame * __flash Animations[3] = {loop1, loop2, loop3};

 

Stefan Ernst

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

Woohoo!  That compiles.  Thanks.  One more question.  How do I access each loopn, as well as each element in loopn?  I'm rusty with pointers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include <avr/io.h>

struct Frame {
	unsigned char digit;    // Digit:  0 = 1 minutes, 1 = 10 minutes, etc.
	unsigned char seg;      // Segment to turn on:  a - g
	unsigned char onoff;    // 0 = segment off, 1 = segment on
	unsigned char dur;      // Duration in msec. to maintain segment state
};

const __flash struct Frame loop1[] = {
	{0, 'a', 1, 50}, {0, 'a', 0, 50}, {0, 'b', 1, 50}, {0, 'b', 0, 50}, {0, 'c', 1, 50}, {0, 'c', 0, 50}, {0, 'd', 1, 50}, {0, 'd', 0, 50},
	{1, 'd', 1, 50}, {1, 'd', 0, 50}, {2, 'd', 1, 50}, {2, 'd', 0, 50}, {3, 'd', 1, 50}, {3, 'd', 0, 50}, {3, 'e', 1, 50}, {3, 'e', 0, 50},
	{3, 'f', 1, 50}, {3, 'f', 0, 50}, {3, 'a', 1, 50}, {3, 'a', 0, 50}, {2, 'a', 1, 50}, {2, 'a', 0, 50}, {1, 'a', 1, 50}, {1, 'a', 0, 50}
};

const __flash struct Frame loop2[] = {
	{0, 'a', 1, 50}, {0, 'a', 0, 50}, {0, 'b', 1, 50}, {0, 'b', 0, 50}, {0, 'c', 1, 50}, {0, 'c', 0, 50}, {0, 'd', 1, 50}, {0, 'd', 0, 50},
	{1, 'd', 1, 50}, {1, 'd', 0, 50}, {2, 'd', 1, 50}, {2, 'd', 0, 50}, {3, 'd', 1, 50}, {3, 'd', 0, 50}, {3, 'e', 1, 50}, {3, 'e', 0, 50},
	{3, 'f', 1, 50}, {3, 'f', 0, 50}, {3, 'a', 1, 50}, {3, 'a', 0, 50}, {2, 'a', 1, 50}, {2, 'a', 0, 50}, {1, 'a', 1, 50}, {1, 'a', 0, 50}
};

const __flash struct Frame loop3[] = {
	{0, 'a', 1, 50}, {0, 'a', 0, 50}, {0, 'b', 1, 50}, {0, 'b', 0, 50}, {0, 'c', 1, 50}, {0, 'c', 0, 50}, {0, 'd', 1, 50}, {0, 'd', 0, 50},
	{1, 'd', 1, 50}, {1, 'd', 0, 50}, {2, 'd', 1, 50}, {2, 'd', 0, 50}, {3, 'd', 1, 50}, {3, 'd', 0, 50}, {3, 'e', 1, 50}, {3, 'e', 0, 50},
	{3, 'f', 1, 50}, {3, 'f', 0, 50}, {3, 'a', 1, 50}, {3, 'a', 0, 50}, {2, 'a', 1, 50}, {2, 'a', 0, 50}, {1, 'a', 1, 50}, {1, 'a', 0, 50}
};

void init(void)
{
}

int main(void)
{
	char i = 0, x = 0, y, z, a;
	const struct Frame __flash *Animations[3] = {loop1, loop2, loop3};
	init();
	while (1) {
		for (int i = 0; i < 3; i++) {
			const struct Frame __flash *f = Animations[i];
			for (int j = 0; j < 8; j++) {
				PORTD = f->seg;
				f++;
			}
		}
	}
}

Your compiler might not be as fussy for const as GCC.  But it should accept it ok.

 

That example rums in the Simulator.

 

David.

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

Thanks, David.  I'm trying to understand this before I try to run it.  Each time through the i loop, a pointer to a structure is created as a constant.  This pointer, f, points to Animations[i].  Animations[] is an array of pointers to arrays of structures.  f steps through each element of Animations[i] via the f++ statement in the j loop, getting incremented 8 times.  Some questions:

 

  1. Why make f a constant?
  2. Each Animations[i] has 24 elements, so shouldn't the loop go until j == 24?
  3. Is incrementing f once enough to advance it to the next element of Animations[i]?

 

As I said, my pointer fu is weak.  ;-)

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

lautman wrote:
Woohoo! That compiles.
Really? I missed that it is a local automatic. The second __flash doesn't make any sense then.

lautman wrote:
Why make f a constant?
f is not a constant, it points to a constant.

lautman wrote:
Is incrementing f once enough to advance it to the next element of Animations[i]?
Yes.

 

Another possibility to access each element:

for (uint8_t i = 0; i < 3; i++)
    for (uint8_t j = 0; j < 24; j++)
        PORTD = Animations[i][j].seg;

 

Stefan Ernst

Last Edited: Thu. May 11, 2017 - 07:22 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sternst wrote:
The second __flash doesn't make any sense then.

From a recent thread:

https://www.avrfreaks.net/comment...

Indeed, it must be "unwound".  It can make your head hurt, such as multiple levels of indirection.

 

Further complicated in AVR, with the multiple address spaces.  As a parameter, it can be const but point to SRAM. 

 

Toolchains vary in AVR-address-space implementation.  Digest this set of CodeVision examples:

...

/* Pointer stored in FLASH to a char string placed in FLASH */

flash char * flash flash_ptr_to_flash="This string is placed in FLASH";

... [and other combinations]

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

This works great in my h/w, Stefan; thanks:

for (uint8_t i = 0; i < 3; i++)
    for (uint8_t j = 0; j < 24; j++)
        PORTD = Animations[i][j].seg;

It's also clearer (to me) than the other suggestion.  However, it's only clear because Animations[][] looks like a "normal' two-dimensional array.  In my case, there are three rows and a varying number of columns.  I'd like j to step through the exact number of columns in each Animations[i], but when I calculate sizeof(Animations[i]), the result I get is 2.  It seems to me it should be the number of bytes in each struct (4) x the number of elements (structs) in Animations[i], which is a lot more than 2.  In fact, just executing the statement

x = sizeof(Animations[i]);

causes my code to misbehave (even if x is an int).  What am I missing?

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

But Animations is simply

const struct Frame __flash *Animations[3] = {loop1, loop2, loop3};

So it consists of 3 pointers.   In a small AVR,  this would occupy 3 words (6 bytes).

Just run the Simulation in AS7.    I guess that AS7 will accept code built by ImageCraft.   (AS4 accepted iccavr7 and iccavr8 )

 

Yes,  these constructions are difficult to visualise in your head.    Draw a diagram with pencil and paper.

Then step through with the Simulator inspecting where the data has been stored in memory.   And observe how Flash is accessed with LPM and regular SRAM is done conventionally.

 

Incidentally,  your (and my) example show loop1, loop2, loop3 with the same size.    You do not have a field to say how big anything is.

I generally use a list structure with the address of data and sizeof(data).   Then any walk process knows how far it can go.

 

David.

Last Edited: Thu. May 11, 2017 - 08:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, David.  I'll walk through the code in the simulator (or my emulator and h/w) later when I have more time.  Part of my confusion is, Animations[i][j] looks and works like like a two-dimensional array of structs, not pointers to structs since the pointer doesn't get dereferenced before accessing the seg parameter.  So I assumed sizeof(Animations[i]) would return the number of bytes in the Animations[i] array, not the number of bytes in the pointer to the Animations[i] array (which I understand is 2, as my code indicates).  It's still not clear to me why an array of pointers acts like an array of arrays.

 

As for the loopn lengths, I just copied and pasted loop1 to fill out the test data set.  In practice, the loops will be different lengths, so I was hoping to use sizeof(array)/sizeof(element) to get the length of each loopn array.

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

lautman wrote:
It's still not clear to me why an array of pointers acts like an array of arrays.
You can always dereference a pointer with [].

x[i] is equivalent to *(x+i).

 

Fun fact: because the '+' is commutative it is also equivalent to *(i+x) and i[x]. So instead of Animations[i][j] you could also use j[Animations[i]].

Though that is useful only for confusing people. ;-)

Stefan Ernst

Last Edited: Thu. May 11, 2017 - 09:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would make Animations slightly different e.g.

struct Frame_descriptor { Frame *film; int siz; };
const struct Frame_descriptor0 __flash *Animations[3] = {
  { loop1, sizeof(loop1} },
  { loop2, sizeof(loop2) },
  { loop3, sizeof(loop3} },
};
	
for (uint8_t i = 0; i < 3; i++) {
    int numframes = Animations[i].size / sizeof(struct Frame);
    for (uint8_t j = 0; j < numframes; j++)
        PORTD = Animations[i][j].film->seg;

Untested.   Note that sizeof() always gives you bytes.   Pointer arithmetic uses the size of the object.   Likewise,  if you want to know the number of elements in an array.

 

You could always make your Frame_descriptor struct use number_of_elements instead of size_in_bytes

e.g.

struct Frame_descriptor { Frame *film; int n; };
const struct Frame_descriptor0 __flash *Animations[3] = {
  { loop1, sizeof(loop1}/sizeof(struct Frame) },
  { loop2, sizeof(loop2)/sizeof(struct Frame) },
  { loop3, sizeof(loop3}/sizeof(struct Frame) },
};

David.

Last Edited: Thu. May 11, 2017 - 09:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, David.  I understand logically what you're suggesting, but my compiler doesn't.

struct Frame_descriptor {
    int *Frame;
    int n;
};

int main(void)
{
    int i = 0, x = 0, y, z, a;
    const struct Frame_descriptor __flash *Animations[3] = {
        { loop1, sizeof(loop1}/sizeof(struct Frame) },
        { loop2, sizeof(loop2)/sizeof(struct Frame) },
        { loop3, sizeof(loop3}/sizeof(struct Frame) },
    };

On the first row of *Animations[3] (the one with loop1) it throws, "|198| syntax error; found `,' expecting `}'.  I changed Frame_descriptor since the compiler wasn't accepting it the way you suggested.  This seems to make more sense to me, too, though that may be part of the problem.

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

Sharpen your eyes (-:

You have curly braces at the end of some of the sizeof () calls. Should be closing parenthesis.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

> Sharpen your eyes

 

Whippersnappers making fun of senior citizens!  Is that what this board has come to?  Screw my 59 year old eyes.  I changed the font to 14.

 

> You have curly braces at the end of some of the sizeof () calls. Should be closing parenthesis.

 

Thanks for pointing that out.  I chock it up to my eyes, er, font, and carelessly cutting-and-pasting.

 

Nevertheless, after the fix, the error remains.

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

lautman wrote:
I changed Frame_descriptor since the compiler wasn't accepting it the way you suggested. This seems to make more sense to me, too
???

First member of Frame_descriptor should be a pointer to the actual data (the arrays). Why does a pointer to int make more sense for you?

David was just a little bit sloppy and forgot a few words in front of 'Frame'.

Stefan Ernst

Last Edited: Fri. May 12, 2017 - 08:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Surely a "Frame_descriptor" should have members that point to a Frame struct.

Why would you invent some unrelated type instead?

 

My example was untested.   Looking at your data,  loop1[] has 24 members.

I suggest that you Simulate my example.   And post any syntax errors.

The important point is to have correct types at all times.   i.e. listen to the Compiler messages when it whinges.

 

David.

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

lautman wrote:
Whippersnappers

Thank you! (-:

 

I'm 2 years behind you. And have the eyesight of a mole, more or less...

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Yes, Johan.   But moles have got nice furry coats.   Everyone wants to stroke them.

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

Stefan:  > Why does a pointer to int make more sense for you?

 

Because I'm a spaz (particularly with pointers).  I read my edited code as an int that pointed to a Frame.  I recognize that error, but I was compelled to change Frame *film; as that drives the compiler to say, "|154| invalid struct field declarations."  I didn't find the time to simulate this yesterday, and I'm going out of town in a few minutes until Sunday, so I'll pick it up again then.  Thanks loads for all your help.

 

Stefan:  > David was just a little bit sloppy and forgot a few words in front of 'Frame'.

 

Which words would you suggest?

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

I will write and test an example for you. My apologies for posting Untested code.
.
David.

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

lautman wrote:
Which words would you suggest?
struct, __flash, and const.

Stefan Ernst

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

David: > My apologies for posting Untested code.

No need to apologize. I'm grateful for any help.

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

Change names as desired.  Insert __flash and const as needed.

Replace dots.

datum_t col0[]={ ... };
datum_t col1[]={ ... };
datum_t col2[]={ ... };
datum_t col3[]={ ... };

#define entry(col) { col, sizeof(col)/sizeof(col[0]) }


struct col {
    datum_t *ptr;
    uint8_t count;
} raggedbottom[]= {
    entry(col0),
    entry(col1),
    entry(col2),
    entry(col3)
} ;

enum { colsNum=sizeof(raggedbottom)/sizeof(raggedbottom[0] } ;

...

for(j=0; j< colsNum; ++j) {
    struct col c=raggedbottom[j];
    for(k=0; k< c.count; ++k) {
        stuff with c.ptr[k]
    }  // k
}  // j

I didn't work hard on names.

Iluvatar is the better part of Valar.

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

Thanks, Skeeve. I kind of see what you're doing (cols are actually rows?), but I'm away from my computer until tomorrow, so I'll try it when I get back.

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

I probably messed up rows vs. cols

Unless representing a textbook matrix, I usually think in terms of array2[hi][lo].

Fortran does array2(lo, hi).

If row is always the first index we have confusion when using both.

Iluvatar is the better part of Valar.

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

Hi, Skeeve.  I get the same error I was getting with David's code:  "invalid struct field declarations" on the Frame *ptr; line below:

#define entry(loop) { loop, sizeof(loop)/sizeof(loop[0]) }

struct loop {
    Frame *ptr;
    uint8_t count;
} raggedbottom[]= {
    entry(loop1),
    entry(loop2),
    entry(loop3),
} ;

Frame and loops are still defined as in my May 10, 2017 - 02:57 PM post, above.

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

lautman wrote:
I get the same error I was getting with David's code: "invalid struct field declarations" on the Frame *ptr; line below:
So you ignored everything I said about that line?

 

That should be a pointer to your actual data in Flash. You already had working code with an array of such pointers. Why is it a problem for you to correctly declare a single pointer of that type?

Stefan Ernst

Last Edited: Tue. May 16, 2017 - 07:50 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Frame and loops are still defined as in my May 10, 2017 - 02:57 PM post, above.

Please don't do that..

 

We don't want to stitch together code that might be the same thing you're having. It's bound to end up with us not seeing what you're seeing, and will just cause confusion. Post minimal but complete code that demonstrates the problem.

 

If you absolutely want to refer to another post in a thread, then do not use the timestamp. It is presented to every user based on his local timezone. I.e. in the presentation of the thread I get in my vista there is no post with a timestamp containing "02:57". Use the post number at the top right of every post as a reference. Since it is a link you can even copy that URL and use that to refer to  another post (inside or outside of the thread), if you like.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

__f;lash is a type qualifier, like const and volatile, but fussier.

Pointer to non-const may not point to const, but the reverse works.

__flash must match exactly:

Pointer to __flash may only point to __flash and pointer to non-__flash may only point to non-__flash.

 

Like const and volatile, but not PROGMEM, __flash works on typedef's.

 

PROGMEM objects are subjects of lies told to the compiler and are often accessed by _P functions that know better.

Iluvatar is the better part of Valar.

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

To summarize, I have three loopn arrays of Frame structs listed below, two are the same length and one is different.  I'd like to have an Animations array containing an arbitrary number of these loopn arrays of arbitrary lengths and then select a loopn at random from Animations.  Since I won't know the selected array's length, I'll need to measure it on the fly.  My code so far (developed with the help of everyone in this thread; thanks) allows me to do everything I want except measure the length of each loopn (or Animations[a], below).

struct Frame {
    unsigned char digit;    // Digit:  0 = 1 minutes, 1 = 10 minutes, etc.
    unsigned char seg;      // Segment to turn on:  a - g
    unsigned char onoff;    // 0 = segment off, 1 = segment on
    unsigned char dur;      // Duration in msec. to maintain segment state
};

__flash struct Frame loop1[] = {
{0, 'a', 1, 50}, {0, 'a', 0, 50}, {0, 'b', 1, 50}, {0, 'b', 0, 50}, {0, 'c', 1, 50}, {0, 'c', 0, 50}, {0, 'd', 1, 50}, {0, 'd', 0, 50},
{1, 'd', 1, 50}, {1, 'd', 0, 50}, {2, 'd', 1, 50}, {2, 'd', 0, 50}, {3, 'd', 1, 50}, {3, 'd', 0, 50}, {3, 'e', 1, 50}, {3, 'e', 0, 50},
{3, 'f', 1, 50}, {3, 'f', 0, 50}, {3, 'a', 1, 50}, {3, 'a', 0, 50}, {2, 'a', 1, 50}, {2, 'a', 0, 50}, {1, 'a', 1, 50}, {1, 'a', 0, 50}
};

__flash struct Frame loop2[] = {
{0, 'b', 1, 1}, {0, 'c', 1, 50}, {0, 'b', 0, 1}, {0, 'c', 0, 50}, {0, 'e', 1, 1}, {0, 'f', 1, 50}, {0, 'e', 0, 1}, {0, 'f', 0, 50},
{1, 'b', 1, 1}, {1, 'c', 1, 50}, {1, 'b', 0, 1}, {1, 'c', 0, 50}, {1, 'e', 1, 1}, {1, 'f', 1, 50}, {1, 'e', 0, 1}, {1, 'f', 0, 50},
{2, 'b', 1, 1}, {2, 'c', 1, 50}, {2, 'b', 0, 1}, {2, 'c', 0, 50}, {2, 'e', 1, 1}, {2, 'f', 1, 50}, {2, 'e', 0, 1}, {2, 'f', 0, 50},
{3, 'b', 1, 1}, {3, 'c', 1, 50}, {3, 'b', 0, 1}, {3, 'c', 0, 50}, {3, 'e', 1, 1}, {3, 'f', 1, 50}, {3, 'e', 0, 1}, {3, 'f', 0, 50},
};

__flash struct Frame loop3[] = {
{0, 'a', 1, 50}, {0, 'a', 0, 50}, {0, 'b', 1, 50}, {0, 'b', 0, 50}, {0, 'c', 1, 50}, {0, 'c', 0, 50}, {0, 'd', 1, 50}, {0, 'd', 0, 50},
{1, 'd', 1, 50}, {1, 'd', 0, 50}, {2, 'd', 1, 50}, {2, 'd', 0, 50}, {3, 'd', 1, 50}, {3, 'd', 0, 50}, {3, 'e', 1, 50}, {3, 'e', 0, 50},
{3, 'f', 1, 50}, {3, 'f', 0, 50}, {3, 'a', 1, 50}, {3, 'a', 0, 50}, {2, 'a', 1, 50}, {2, 'a', 0, 50}, {1, 'a', 1, 50}, {1, 'a', 0, 50}
};

void init(void) {
};

int main(void)
{
    int i = 0, x = 0, y, z, a;
    __flash struct Frame * Animations[3] = {loop1, loop2, loop3}
//    __flash struct Frame * __flash Animations[3] = {loop1, loop2, loop3};  // Code works this way and as in previous line
    init();

    for (a = 0; a < 3; a++)
        for (y = 0; y < 5; y++)
            for (i = 0; i < 24; i++) {           // loop2 (Animations[1]) won't play all the way through since it's longer than 24 elements
                if (Animations[a][i].onoff) {
                    segSet(Animations[a][i].seg, Animations[a][i].digit);
                    m_delay(Animations[a][i].dur);
                }
                else {
                    segReset(Animations[a][i].seg, Animations[a][i].digit);
                    m_delay(Animations[a][i].dur);
                }
            }

    while(1)
        ;
    return 0;
}

There have been several suggestions that I create an additional struct to track the size of each loopn, something like this:

struct Frame_descriptor { Frame *film; int siz; };
const struct Frame_descriptor0 __flash *Animations[3] = {
  { loop1, sizeof(loop1} },
  { loop2, sizeof(loop2) },
  { loop3, sizeof(loop3} },
};

I haven't been able to test this, however, as the compiler throws an error on the Frame *film line (or Frame *ptr) in the Frame_descriptor definition:  invalid struct field declarations.  Stefan pointed out that *film should be a pointer to the actual data in flash, but I don't understand this comment as I thought struct definitions are just placeholders; you can't get them to store anything "actual" until you create an instance and initialize it, which you can't do if the compiler won't allow you to define it in the first place.

 

Anyway, this is where I am, and I really appreciate everyone's patience.

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

lautman wrote:
but I don't understand this comment as I thought struct definitions are just placeholders; you can't get them to store anything "actual" until you create an instance and initialize it
But the "placeholder" must be an exact description of what is later stored in.

Stefan Ernst

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

In

struct Frame_descriptor { Frame *film; int siz; };

film is a pointer to a struct called Frame.  How can it be more exact?  While Frame will be stored in flash, 

struct Frame_descriptor { __flash Frame *film; int siz; };
struct Frame_descriptor { const Frame *film; int siz; };

don't compile any better.

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

lautman wrote:

In

struct Frame_descriptor { Frame *film; int siz; };

film is a pointer to a struct called Frame.

No, it is not. It is a pointer to an object of type 'Frame', but that type does not exist in your code.

Stefan Ernst

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

lautman wrote:
but I don't understand this comment as I thought struct definitions are just placeholders

I would opt for the term "template" rather than "placeholder" here.

 

After that, lets straighten out when structs are actually created (I'll refer to such as a "struct occurence"), and when only the template is declared (I'll actually call this a "struct type" from now on). Turns out there are several variants.

 

Starting with just declaring a struct occurence:

 

struct {
    int i;
    double f;
} myStruct1;

 

With this you have one occurence of an actual struct, and you can access it's members:

 

myStruct1.i = 42;

 

Now, you often need several structs - perhaps in different places. C has supported this as long as it has supported structs by allowing you to declare a struct type. You do that like so:

 

struct myStructType{
    int i;
    double f;
};

You now have this type to be used to define as many structs of that type that you wish. Or you can use this type to form an array of such. Or declare another struct (or define a struct type) with this struct type as one of its members.

 

But before we show how to use it, you need to knoe that the compiler keeps a completely separate namespace for such struct types. The C keyword 'struct' is actually the signal to the compiler to use that namespace (put something in it, or look for something in it). Names in the 'struct' namespace won't collide or interfere with names in the "default/unnamed namespace" (where all you other variables live). So, this is actually valid C:

 

struct foo {
	int i;
	double j;
};

int foo;

Not that you'd want to do that. But it will work. And it illustrates the peculiarity that the 'struct' namespace is. Also, note that the above shows one type declared and one variable defined (the integer foo)!

 

So, now we more or less know how to define two structs of the type MyStructType. It goes:

 

struct MyStructType myStruct1;
struct MyStructType myStruct2;

There's nothing stopping you from using a "combined effort to declare a type and define an instance at the same time, and then use the type to define two more instances.

 

struct myStruct4 {
	int i;
	double j;
} MyStructType;

struct MyStructType myStruct5;
struct MyStructType myStruct6;

Again, you would probably not want to do it this way. But again, it illustrates the mechanism.

 

Finally, and in order to hide the somewhat messy struct namespace, we can make use of typedefs. They are like variable definitions, but will define a type. Not a 'struct type' this time, but a name of a type whose name is in the "default/unnamed namespace". Thus such typedefed names will collide with any identical variable name or other typedefed name you have already introduced.

 

Typedefs go like variable definitions, with the one difference that where the variable name gent there goes the type name you want to define. Like this:

 

typedef struct {
    int i;
    double j;
} MyTypedefedStructType;

 

So you need to tell above that it is a struct. No way around that. The nice thing is that you now can use MyTypedefedStructType (without the 'struct' keyword prefixed) to define variable, to use as the element of an array, or as a member of another struct or struct type or typedefed struct type. Examples:

 

MyTypedefedStructType myStruct7;
MyTypedefedStructType myStruct8;

MyTypedefedStructType myArrayOfsuch[5];

typedef struct {
    MyTypedefedStructType s;
    int anotherInt;
} MyTypedefedSuperStruct;

MyTypedefedSuperStruct mySuperStruct;

myStruct7.i = 42;

myArrayOfStructs[3] = 7;

mySuperStruct.s.i = 21;

The typedef approach serves us well most of the time. It removes those pesky extra compiles we must do just because we needed to add a forgotten 'struct' somewhere. It also makes things clearer.

 

Caveat Emptor: I typed most all of the above in without actually testing it. There might be the odd typo in those code snippets.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Last Edited: Wed. May 17, 2017 - 06:07 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In C, struct Frame != Frame .

Iluvatar is the better part of Valar.

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

skeeve wrote:

In C, struct Frame != Frame .

Can you explain the difference?  In this code:

struct Frame {
    unsigned char digit;    // Digit:  0 = 1 minutes, 1 = 10 minutes, etc.
    unsigned char seg;      // Segment to turn on:  a - g
    unsigned char onoff;    // 0 = segment off, 1 = segment on
    unsigned char dur;      // Duration in msec. to maintain segment state
};

__flash struct Frame loop1[] = {
{0, 'a', 1, 50}, {0, 'a', 0, 50}, {0, 'b', 1, 50}, {0, 'b', 0, 50}, {0, 'c', 1, 50}, {0, 'c', 0, 50}, {0, 'd', 1, 50}, {0, 'd', 0, 50},
{1, 'd', 1, 50}, {1, 'd', 0, 50}, {2, 'd', 1, 50}, {2, 'd', 0, 50}, {3, 'd', 1, 50}, {3, 'd', 0, 50}, {3, 'e', 1, 50}, {3, 'e', 0, 50},
{3, 'f', 1, 50}, {3, 'f', 0, 50}, {3, 'a', 1, 50}, {3, 'a', 0, 50}, {2, 'a', 1, 50}, {2, 'a', 0, 50}, {1, 'a', 1, 50}, {1, 'a', 0, 50}
};

a struct called Frame is defined and then an array of them is declared and initialized.  This works as intended, so I don't understand the difference you're highlighting.

Last Edited: Tue. May 16, 2017 - 07:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Life is so much easier if you stick to typedefs and forget struct tags all together with the one exception when you need a struct pointer to the struct itself - then use a tag only for that.

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

sternst wrote:

lautman wrote:

In

struct Frame_descriptor { Frame *film; int siz; };

film is a pointer to a struct called Frame.

No, it is not. It is a pointer to an object of type 'Frame', but that type does not exist in your code.

It's the first thing defined at the top of my code.  Three arrays are initialized containing instances of it.  I can access the Frame array elements, and their parameters.  In what sense does the type not exist?

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

As others have said "struct Frame" is not the same as "Frame".
.
Really, do yourself a favor and learn to typedef.

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

clawson wrote:
As others have said "struct Frame" is not the same as "Frame". . Really, do yourself a favor and learn to typedef.

 

OK, I can do this:

typedef struct {
    unsigned char digit;    // Digit:  0 = 1 minutes, 1 = 10 minutes, etc.
    unsigned char seg;      // Segment to turn on:  a - g
    unsigned char onoff;    // 0 = segment off, 1 = segment on
    unsigned char dur;      // Duration in msec. to maintain segment state
} Frame;

__flash Frame loop1[] = {
{0, 'a', 1, 50}, {0, 'a', 0, 50}, {0, 'b', 1, 50}, {0, 'b', 0, 50}, {0, 'c', 1, 50}, {0, 'c', 0, 50}, {0, 'd', 1, 50}, {0, 'd', 0, 50},
{1, 'd', 1, 50}, {1, 'd', 0, 50}, {2, 'd', 1, 50}, {2, 'd', 0, 50}, {3, 'd', 1, 50}, {3, 'd', 0, 50}, {3, 'e', 1, 50}, {3, 'e', 0, 50},
{3, 'f', 1, 50}, {3, 'f', 0, 50}, {3, 'a', 1, 50}, {3, 'a', 0, 50}, {2, 'a', 1, 50}, {2, 'a', 0, 50}, {1, 'a', 1, 50}, {1, 'a', 0, 50}
};

__flash Frame loop2[] = {
{0, 'b', 1, 1}, {0, 'c', 1, 50}, {0, 'b', 0, 1}, {0, 'c', 0, 50}, {0, 'e', 1, 1}, {0, 'f', 1, 50}, {0, 'e', 0, 1}, {0, 'f', 0, 50},
{1, 'b', 1, 1}, {1, 'c', 1, 50}, {1, 'b', 0, 1}, {1, 'c', 0, 50}, {1, 'e', 1, 1}, {1, 'f', 1, 50}, {1, 'e', 0, 1}, {1, 'f', 0, 50},
{2, 'b', 1, 1}, {2, 'c', 1, 50}, {2, 'b', 0, 1}, {2, 'c', 0, 50}, {2, 'e', 1, 1}, {2, 'f', 1, 50}, {2, 'e', 0, 1}, {2, 'f', 0, 50},
{3, 'b', 1, 1}, {3, 'c', 1, 50}, {3, 'b', 0, 1}, {3, 'c', 0, 50}, {3, 'e', 1, 1}, {3, 'f', 1, 50}, {3, 'e', 0, 1}, {3, 'f', 0, 50},
};

__flash Frame loop3[] = {
{0, 'a', 1, 50}, {0, 'a', 0, 50}, {0, 'b', 1, 50}, {0, 'b', 0, 50}, {0, 'c', 1, 50}, {0, 'c', 0, 50}, {0, 'd', 1, 50}, {0, 'd', 0, 50},
{1, 'd', 1, 50}, {1, 'd', 0, 50}, {2, 'd', 1, 50}, {2, 'd', 0, 50}, {3, 'd', 1, 50}, {3, 'd', 0, 50}, {3, 'e', 1, 50}, {3, 'e', 0, 50},
{3, 'f', 1, 50}, {3, 'f', 0, 50}, {3, 'a', 1, 50}, {3, 'a', 0, 50}, {2, 'a', 1, 50}, {2, 'a', 0, 50}, {1, 'a', 1, 50}, {1, 'a', 0, 50}
};

typedef struct {
    Frame *film;
    int n;
} Frame_descriptor;

void init(void) {
};

int main(void)
{
    int i = 0, x = 0, y, z, a;
    __flash Frame_descriptor *Animations[3] = {
        { loop1, sizeof(loop1)/sizeof(Frame) },
        { loop2, sizeof(loop2)/sizeof(Frame) },
        { loop3, sizeof(loop3)/sizeof(Frame) },
    };
    init();

but then the compiler says, "syntax error; found `,' expecting `}'," on the line beginning with loop1 in the *Animations array.

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

Thanks for the lengthy explanation, sternst.  As clawson advised, I'll stick with typdefs, though it's not solving my problem yet.

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

lautman wrote:
but then the compiler says, "syntax error; found `,' expecting `}'," on the line beginning with loop1 in the *Animations array.

No other errors or warnings?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

JohanEkdahl wrote:

lautman wrote:
but then the compiler says, "syntax error; found `,' expecting `}'," on the line beginning with loop1 in the *Animations array.

No other errors or warnings?

Oh, there are plenty more.  I tend to just focus on the first one, though.  Here's the rest (the Animations array is defined from lines 206 - 210):

|207| syntax error; found `,' expecting `}'|
|207| skipping `,' `sizeof' `(' `loop1' `)' `/' `sizeof' `(' ... up to `}'|
|207| invalid initialization type; found `pointer to __flash Frame' expected `pointer to __flash Frame_descriptor'|
|208| syntax error; found `,' expecting `}'|
|208| skipping `,' `sizeof' `(' `loop2' `)' `/' `sizeof' `(' ... up to `}'|
|208| invalid initialization type; found `pointer to __flash Frame' expected `pointer to __flash Frame_descriptor'|
|209| syntax error; found `,' expecting `}'|
|209| skipping `,' `sizeof' `(' `loop3' `)' `/' `sizeof' `(' ... up to `}'|
|209| invalid initialization type; found `pointer to __flash Frame' expected `pointer to __flash Frame_descriptor'|
|224| unknown field `onoff' of `Frame_descriptor'|
|225| unknown field `seg' of `Frame_descriptor'|
|225| unknown field `digit' of `Frame_descriptor'|
|226| unknown field `dur' of `Frame_descriptor'|
|229| unknown field `seg' of `Frame_descriptor'|
|229| unknown field `digit' of `Frame_descriptor'|
|230| unknown field `dur' of `Frame_descriptor'|
|866|[warning] missing return value|

 

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

lautman wrote:
but then the compiler says, "syntax error; found `,' expecting `}'," on the line beginning with loop1 in the *Animations array.

Another grapeshot missing the target.

You made Animations an array of pointers, but the initialization data are no pointers.

 

BTW:

lautman wrote:
Thanks for the lengthy explanation, sternst.
Johan was the kind guy who wrote the detailed explanation.

Stefan Ernst

Last Edited: Tue. May 16, 2017 - 08:54 PM

Pages