Help - PROGMEM with typedef's ?

Hi,

My C is a bit rusty... I have a working program, but when I make my array's bigger it just fails in random ways, I'm guessing the arrays are filling the SRAM, so I want to move them to flash.

I have :

typedef struct _point {int x,y;} point;

struct _point pointsTarget[]={
  {0x01,0x01},{0x01,0x40},{0x01,0x80},{0x01,0xC0},{0x01,0xFF},{0x40,0xFF},{0x80,0xFF},
  {0xC0,0xFF},{0xFF,0xFF},{0xFF,0xC0},{0xFF,0x80},{0xFF,0x40},{0xFF,0x01},{0xC0,0x01},
  {0x80,0x01},{0x40,0x01},{0x01,0x01},{0x40,0x40},{0x80,0x80},{0xC0,0xC0},{0xFF,0xFF},
  {0x00,0x00},{0xFF,0x01},{0xC0,0x40},{0x80,0x80},{0x40,0xC0},{0x01,0xFF},{0x00,0x00}
};

void DrawFrame (struct _point * pShape, int nArraySize, int nScale, int nAngle, int nDelay) {
  for (p=0; p<nArraySize; p++){
    x = pShape[p].x;
    y = pShape[p].y;

//more code ...

    }
}

I know I have to add PROGMEM to my array def, but them I'm a bit lost where to put the pgm_read_byte macros with a type def ?
Can someone help me with the syntax ?

A couple of the arrays are large...

Thanks.

Jon.

A couple of the arrays are large...

But, are they read-only? That is critical to being able to use PROGMEM.

Yep. They're all read only...

Jon.

I'm a bit lost where to put the pgm_read_byte macros with a type def ?

The use of typedefs has absolutely nothing to do with where to use the pgm_read_bytes macros. The macro is used wherever the value needs to be read from PROGMEM.

If you are going to the trouble of using a typedef statement to tell the compiler that the name point refers to a struct _point, why are you then defining an array of struct _point objects?

Seems to me that the typedef statement is completely unnecessary.

So, do I just do ?

    x = pgm_read_byte(&pShape[p].x);
    y = pgm_read_byte(&pShape[p].y);

The typedef just makes the code easier to read I feel. I tried the definition without the _points alias, but when I try and use points type in the array the compiler throws errors when it gets to the function definition for DrawFrame ... Thats a question for a different thread... :slight_smile:

Thanks.

Jon.

The typedef just makes the code easier to read I feel.

It would if you actually used it.

I tried the definition without the _points alias,

The _points bit isn't an alias. It's required. The points name on the end is the alias for the "struct _points" bit. It is optional when defining a struct, but not when using a typedef statement.

when I try and use points type in the array the compiler throws errors when it gets to the function definition for DrawFrame ... Thats a question for a different thread...

No problem keeping all of your issues in one place.

Looking at your code, you are using ints for what appears to be byte (0-255) - if so, you can halve the space you need if you make one change:

typedef struct _point {int x,y;} point;

to

typedef struct _point {byte x,y;} point;

...and by the way, changing to byte might help you in another way: if you find you can now add roughly 2x the amounts of values before the program goes funny, you can guess its a memory issue (since you have room for 2x the values). But if not, it may be another code issue, like a bug, or other memory issue, like stack overflow (if your graphics routines use a lot of recursion, for example).

Thanks for all the help guys. I now have all my arrays in flash, and as expected, my program is behaving much better.

Well spotted, all my arrays are 0-255, so its a very valid point to move them all to bytes, rather than ints. It’s a legacy from my original design. The arrays are shapes and pictures for a laser projector (eventually I hope to code a full "font" for the laser too), and I still debate in my head whether I should make them higher resolution. Currently all source images are 0,0-255,255 and I scale them to 4095x4095 as I have a 12 bit DAC driving the laser galvos.

I will probably stick with 0-255, so I will switch them to bytes and save a stack of space...

Thanks again... all looking good, and have a dancing logo on my wall as I type ... :slight_smile:

I have two other outstanding issues which someone here may have an opinion on;

  • I need to digitise an outlined font so I can write words with the laser. I've looked but can find an existing one somewhere ? There are lots of "fonts" for 8x8 LED displays, but I can’t find an outlined vector font for use with lasers ?

  • I currently digitise the shapes with all the intermediate points. If you just scan the laser from one point to the other, too far away, the line it draws is to faint. i.e. if you have 4 points for a square shape you don’t get a very bright line. So currently I draw a dot ever 16 “pixels” so a straight line from one point to another is actually 10 or so points. This makes the digitisation more complex and also it’s a waste of storage. Ideally I’d like to store the start and end point and then calculate the intermediate pixels in code. I’ve implemented a Bresenham Line algorithm, with code I got from RosettaCode.org. It works perfectly, but … I get about 1 frame every 2 seconds ! :frowning: instead of 10 frames a second with the hard coded pixels. Any ideas for speeding up Bresenham algorithm ?

Thanks again.

Jon.

There are lots of "fonts" for 8x8 LED displays

How about passing a simple edge detector over a font array?

JonRussell:
I’ve implemented a Bresenham Line algorithm, with code I got from RosettaCode.org. It works perfectly, but … I get about 1 frame every 2 seconds ! :frowning: instead of 10 frames a second with the hard coded pixels. Any ideas for speeding up Bresenham algorithm ?

Bresenham's pretty darn fast - perhaps post the code function here and we can take a look at it. Offhand, I'd take a stab that if it uses floating point, multiplies, or divides, these would be the first things to speed up.

Also, using a byte value (0-255) may open up some bit fiddling in place of * or /

And of course, moving the array out of variable RAM will slow things down with the access functions - is there any way you can cache the current item in RAM when drawing?

The Bresenham code is all integer ... its just the code from Rosetta.

I added the the "if (n%100==0)" so it only draws dots every 100 "pixels" not continuous lines ... but its still slow. I've not done much investigation yet in to which bit is slow ...

void BresenhamLine(int x0, int y0, int x1, int y1){
int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1; 
int err = (dx>dy ? dx : -dy)/2, e2;
int n=0;

  for(;;){
    if (n%100==0) {
      WriteDACw((word)x0,(word)y0);
      }
    if (x0==x1 && y0==y1) break;
    e2 = err;
    if (e2 >-dx) { err -= dy; x0 += sx; }
    if (e2 < dy) { err += dx; y0 += sy; }
    n+=1;
  }
}

JonRussell:
I added the the "if (n%100==0)" so it only draws dots every 100 "pixels" not continuous lines ... but its still slow. I've not done much investigation yet in to which bit is slow ...

Yeah, here's the killer:

    if (n%100==0) {
      WriteDACw((word)x0,(word)y0);
      }

I don't know how fast WriteDACw() runs, but since you're only doing it 1/100 of the times you loop, that's good - but the n%100 is a divide, and divides are slow.

Try this instead:

    if ((n&0x3F)==0) {
      WriteDACw((word)x0,(word)y0);
      }

This masking will write once every 64 loops, but without a divide.

You can also play around with the value - for example, using 0xFF will do it 1 in 256, 0x1FF on in 512, etc.

Changing the (n%100==0) to ((n&0x64)==0) made a noticeable difference to the frame rate. Thanks. Good tip.

However it’s still very slow. a frame every second or so. I will play with sx & sy at the weekend and see if making them +/-2 instead of +/-1 will improve things too. Going from +1 to +2 will in theory double the speed ... Seeing as I am only plotting ever 100 pixels being 1 or 2 pixels out on each line won’t be noticeable.

I will try and make some videos of the output and post them up here for comment. I was intending to blog the results of the project anyway ...

Thanks for everyone’s help ...

Jon.

If you want a quick timing test, comment out:

WriteDACw((word)x0,(word)y0);

Then see how fast it runs - I'm guessing that's the remaining problem.

Also, you showed code like this:

((n&0x64)==0)

It should be

((n&0x63)==0)

Using 64 is in effect cutting out 1/2 the cycles only, rather than 1/64th. Specifically, it's skipping the work when n=0 to n=63, then doing it from n=64 to n=127, then back again, cycling every 64 counts of n.