PROGMEM / pgmspace.h help

I’m trying to load a bunch of arrays of bytes out of SRAM to get my program to run better, but having a little bit of typical trouble with understanding and using PROGMEM and the related pgmspace.h fucntions.

Here’s the run-down: I want to store about 10 arrays of bytes (approximately 100 entries in length each) for use in my program. The arrays simply denote a pattern of PWM pulses to be sent successively to an LED to create a sort of animation.

I am charlie-plexing some LEDs to be able to run 6 total LEDs using 2 PWM pins on an ATTiny85, meaning one pin needs to be LOW, another pin needs to be HIGH (or some analog value) to turn on individual LEDs.

Now, I want to take a reference of one of the byte array patterns in memory using a variable called CHAN_1_PATTERN. Then I want to step through whatever pattern that variable is currently pointing to, outputting that pattern as a series of analogWrite calls (wherein the value passed to the function changes while we loop through the pattern).

Question is, I’m not sure how exactly to step through an array of bytes in PROGMEM: do I need to increment an integer value, or what exactly?

Here’s my code (and my attempt at stepping through the pattern), can someone tell me if this is the correct thing to do? The program compiles, but I have not yet tested it in my circuit (had to take it apart for a few days, and wanted to get a headstart on the code)

#include <avr/pgmspace.h>

// Define the driver (+) pins
#define  CHAN_1_DRIVER_PIN  0

const byte NUM_LOW_PINS = 3;
const byte LOW_PINS[] = {2, 3, 4};

// Separate variables for two concurrent channels
byte CHAN_1_PIN;
byte * CHAN_1_PATTERN;
byte CHAN_1_PATTERN_SIZE;
byte CHAN_1_COUNTER;
byte CHAN_1_PATTERN_COUNTER;

byte k;

// Define all the firefly patterns (based on real data)
prog_uchar pattern1[] PROGMEM = {2, 14, 44, 95, 141, 175, 192, 186, 149, 94, 60, 35, 38, 50, 76, 83, 73, 58, 43, 30, 20, 14, 16, 25, 30, 27, 20, 11, 4, 10, 17, 19, 14, 8, 4, 4, 6, 9, 9, 4, 2, 4, 14, 37, 79, 133, 173, 185, 172, 143, 106, 70, 45, 36, 44, 60, 75, 75, 59, 37, 20, 12, 16, 22, 26, 25, 22, 18, 14, 10, 7, 5, 7, 10, 10, 9, 7, 6, 5, 4, 5, 6, 6, 6, 5, 4, 3, 2, 2, 1 };

// Set up an array for all the patterns
byte patternSizes[] = { 90, 103, 99, 119, 108, 130, 98, 136, 93, 68 }; 

void setup() {
  // Set up all the pin directions
  pinMode(CHAN_1_DRIVER_PIN, OUTPUT);
  
  for(int i=0; i<3; i++)
    pinMode(LOW_PINS[i], OUTPUT);

  // Initialize both driver pins to LOW, so no power reaches LEDs
  digitalWrite(CHAN_1_DRIVER_PIN, LOW);
  
  // Initialize all grounding pins to HIGH, so that when the
  // driver pins do go HIGH, not all LEDs turn on.
  for(int i=0; i<3; i++)
    digitalWrite(LOW_PINS[i], HIGH);
    
  CHAN_1_PIN = LOW_PINS[0];
  CHAN_1_PATTERN_COUNTER = 0;

  CHAN_1_PATTERN = pattern1;
}

void loop() {

  // Drive LED cathode LOW (provide path to ground)  
  digitalWrite(CHAN_1_PIN, LOW);
 
  // Obtain current frame of animation
  byte currentValue = pgm_read_byte_near(CHAN_1_PATTERN + CHAN_1_PATTERN_COUNTER);
  
  // Output current frame to LED
  analogWrite(CHAN_1_DRIVER_PIN, currentValue);
 
  // Increment pattern counter, or reset counter and pick a new pattern
  if(CHAN_1_PATTERN_COUNTER < CHAN_1_PATTERN_SIZE) {
    CHAN_1_PATTERN_COUNTER++;
  } else {
    CHAN_1_PATTERN_COUNTER = 0;    
  }
 
 delay(10);
  
}

Well, you don’t need pointers and all that business with PROGMEM… all you’ve gotta do is use pgm_read_byte() (or _near, I’m not sure what the difference is quite yet), and give that the value you’re trying to work with.

So, instead of analogWrite(2, pattern1[counter]), you’d use analogWrite(2, pgm_read_byte(pattern1[counter]). :slight_smile:

They made it really simple and I’m often confused by how it works so elegantly, and I often tend to overthink how to work with it. But it really is that simple… surround the parts where you’d usually work with the value itself, with “pgm_read_byte()” :slight_smile:

You also don’t need to include pgmspace.h, since it’s already included in the wiring libraries to access digitalRead/digitalWrite functions (which use some kind of lookup table in PROGMEM).

Also, there are no definitions for CHAN_1_PATTERN_SIZE in the code, so it’s declared and checked, but it’s checked against an undetermined value… I wasn’t sure where that value’s supposed to come from, so I left it broken :confused:

(Untested since I don’t have such an LED setup, and CHAN_1_PATTERN_SIZE is left hanging, so the program won’t run)

// Define the driver (+) pins
#define  CHAN_1_DRIVER_PIN  0

const byte NUM_LOW_PINS = 3;
const byte LOW_PINS[] = {2, 3, 4};

// Separate variables for two concurrent channels
byte CHAN_1_PIN;
byte * CHAN_1_PATTERN;
byte CHAN_1_PATTERN_SIZE;
byte CHAN_1_COUNTER;
byte CHAN_1_PATTERN_COUNTER;

byte k;

// Define all the firefly patterns (based on real data)
byte pattern1[] PROGMEM = {2, 14, 44, 95, 141, 175, 192, 186, 149, 94, 60, 35, 38, 50, 76, 83, 73, 58, 43, 30, 20, 14, 16, 25, 30, 27, 20, 11, 4, 10, 17, 19, 14, 8, 4, 4, 6, 9, 9, 4, 2, 4, 14, 37, 79, 133, 173, 185, 172, 143, 106, 70, 45, 36, 44, 60, 75, 75, 59, 37, 20, 12, 16, 22, 26, 25, 22, 18, 14, 10, 7, 5, 7, 10, 10, 9, 7, 6, 5, 4, 5, 6, 6, 6, 5, 4, 3, 2, 2, 1 };

// Set up an array for all the patterns
byte patternSizes[] = { 90, 103, 99, 119, 108, 130, 98, 136, 93, 68 }; 

void setup() {
  // Set up all the pin directions
  pinMode(CHAN_1_DRIVER_PIN, OUTPUT);
  
  for(int i=0; i<3; i++)
    pinMode(LOW_PINS[i], OUTPUT);

  // Initialize both driver pins to LOW, so no power reaches LEDs
  digitalWrite(CHAN_1_DRIVER_PIN, LOW);
  
  // Initialize all grounding pins to HIGH, so that when the
  // driver pins do go HIGH, not all LEDs turn on.
  for(int i=0; i<3; i++)
    digitalWrite(LOW_PINS[i], HIGH);
    
  CHAN_1_PIN = LOW_PINS[0];
  CHAN_1_PATTERN_COUNTER = 0;

  CHAN_1_PATTERN = pattern1;
}

void loop() {
  // Drive LED cathode LOW (provide path to ground)  
  digitalWrite(CHAN_1_PIN, LOW);
 
  // Obtain current frame of animation
  byte currentValue = pgm_read_byte_near(CHAN_1_PATTERN[CHAN_1_PATTERN_COUNTER]);
  
  // Output current frame to LED
  analogWrite(CHAN_1_DRIVER_PIN, currentValue);
 
  // Increment pattern counter, or reset counter and pick a new pattern
  if(CHAN_1_PATTERN_COUNTER < CHAN_1_PATTERN_SIZE) {
    CHAN_1_PATTERN_COUNTER++;
  } else {
    CHAN_1_PATTERN_COUNTER = 0;    
  }
 
 delay(10);
  
}

h4t:
…can someone tell me if this is the correct thing to do? The program compiles, but I have not yet tested it in my circuit …

How about just printing the values before trying to test in a circuit:

#include <avr/pgmspace.h>

const prog_uchar pattern1[] PROGMEM = {
      2, 14, 44, 95, 141, 175, 192, 186, 149,  94,
     60, 35, 38, 50,  76,  83,  73,  58,  43,  30,
     20, 14, 16, 25,  30,  27,  20,  11,   4,  10,
     17, 19, 14,  8,   4,   4,   6,   9,   9,   4,
      2,  4, 14, 37,  79, 133, 173, 185, 172, 143,
    106, 70, 45, 36,  44,  60,  75,  75,  59,  37,
     20, 12, 16, 22,  26,  25,  22,  18,  14,  10,
      7,  5,  7, 10,  10,   9,   7,   6,   5,   4,
      5,  6,  6,  6,   5,   4,   3,   2,   2,   1
};

// Let the compiler calculate the size
const byte CHAN_1_PATTERN_SIZE = sizeof(pattern1) / sizeof(pattern1[0]);

// Set a pointer equal to the address of pattern1[0]
const prog_uchar * PROGMEM CHAN_1_PATTERN = pattern1;

// A "global" variable used to keep count of the pattern
byte CHAN_1_PATTERN_COUNTER; // This is initialized to zero here

void setup()
{
    Serial.begin(9600);
    Serial.println("Count\tPattern");
}

void loop()
{
    byte currentValue = pgm_read_byte(CHAN_1_PATTERN + CHAN_1_PATTERN_COUNTER);
    Serial.print(CHAN_1_PATTERN_COUNTER, DEC);
    Serial.print(":\t  ");Serial.println(currentValue, DEC);

    // Increment the array index.  If it has gone beyond
    // the end of the array, reset it to zero so that it
    // will start all over again;
    //
    ++CHAN_1_PATTERN_COUNTER;
    if (CHAN_1_PATTERN_COUNTER >= CHAN_1_PATTERN_SIZE) {
        CHAN_1_PATTERN_COUNTER = 0;
    }

    delay(1000);

}

Output:


Count	Pattern
0:	  2
1:	  14
2:	  44
3:	  95
4:	  141
5:	  175
6:	  192
7:	  186
8:	  149
9:	  94
10:	  60
11:	  35
12:	  38
13:	  50
14:	  76
15:	  83
16:	  73
17:	  58
.
.
.

Regards,

Dave

FalconFour: Well, you don't need pointers and all that business with PROGMEM... all you've gotta do is use pgm_read_byte()

Actually you do need a pointer to feed to pgm_read_byte(). You don't necessarily need to declare a separate pointer variable since the name any kind of array (without the [] brackets) is a pointer whose (constant) value is the address of the first element of the array.

FalconFour: ...analogWrite(2, pgm_read_byte(pattern1[counter])

Not quite. You could use

analogWrite(2, pgm_read_byte(&pattern1[counter]);

Or, equivalently:

analogWrite(2, pgm_read_byte(pattern1+counter);

h4t had a pointer variable set to the value of the address of the first element in the array, and his pgm_read_byte() would work just fine, as my example showed. That may important since it allows setting the pointer to point to different arrays in progmem. Or, maybe that's not what he had in mind. My point is that you can use a separate pointer variable or you can use the name of the array, but ya gotta feed pgm_read_byte() a pointer to work with.

(See Footnote.)

Regards,

Dave

Footnote: Some extremely important notation in C and C++ (and, therefore, in Arduino):

If p is a pointer data type and n is an integer data type, the following are completely equivalent. Not "essentially equivalent." Not "more-or-less" equivalent. Exactly equivalent.

p + n

Is treated by the compiler exactly the same as

&p[n]

Of course this means that the following are equivalent:

*(p + n)

Is treated by the compiler exactly the same as:

p[n]

Remember: The name of an array is a pointer whose (constant) value is equal to the address of the first element of the array.

The result is that you can always (yes always) use array notation with pointer variables and you can use pointer notation (and pointer arithmetic) with arrays just by using the array name as a pointer.

Now: Please, please (please) note that I am not saying that pointers are the same as arrays in C/C++. They are not. Never have been. Never will be. It's just notation.

Of course this means that the following are equivalent:

Code: *(p + n)

Is treated by the compiler exactly the same as:

Code: p[n]

And don't forget the beautifully symmetrical

n[p]

The nice thing about pointers to and data structures in program memory is that all the standard C operations that operate on addresses of items (either explicitly or implicitly) work just fine, and you only need to use pgm_read_byte() (or similar) at the last moment.

So if you have:

  struct {
     long number;
     int base;
     char array[5] [10];
  } PROGMEM foo[5] = { {0x12345678, 10, {{1, 2, 3, 4, 5}, {11, 22, 33, 44, 55}, ... }}},
                       {0x0BADBEEF, 16, {{1, 2, 3, 4, 5}, {11, 22, 33, 44, 55}, ... }}},
                       ...
                     };

You can pass around array indicies and whatever you want, and then do something like:

  mybyte = pgm_read_byte(&(foo[x].array[i][j]));

You should never have to explicitly convert your PROGMEM accesses to be pointer based, or do any of your own fancy math, except for the final addition of that "&" when you actually read the data...

Got it working now, thanks!

But now I have a new problem: the patterns are being run through much too fast. I'm trying to simulate the effect of fireflies (a la http://www.instructables.com/id/Jar-of-Fireflies/), but right now, the LEDs are not flashing at all like fireflies do.

At the bottom of my program you can see a called to delay(). With that function call commented out, the entire animation occurs in a blink of an eye. When I add a delay in, you are able to perceive the LED changing, but no matter what value I give it (1 or 10000), you still perceive a sharp flickering as the program goes through the pattern. I have even tried using delayMicroseconds, but haven't been able to create a smooth, yet perceivable pattern. Seems as though its very easy to perceive the different brightnesses in the LED as they occur.

The byte arrays are directly copied from the source code of the Instrucables project, but his code is much more advanced than mine, so most of my work is in loosely interpreting his code. I found this note in one of his C files that seems to refer to how much of a delay there is between updates to the LEDs:

    /*
     * Set up the 8-bit timer 0.
     *
     * Timer0 will be set in CTC up with a prescalar of 8 and an OCR0A copmare 
     * value of 25. 
     * A core clock frequency of 8mhz / 4 means we should get an interrupt 
     * frequency of about 39hz.  (freq = 976.563 / (OCR0A))
     */

This has actually be bugging me (off and on) for a couple months, so help is very much appreciated at this point.

8mHz / 4 is 2 mHz, or one cycle every 500 seconds.

I just changed delay() to delayMicroseconds(500), but the animation still occurs much too quickly and I see a lot of 'flickering'. I'm not an expert, but I don't think fireflies flicker quite like this, lol.

I wonder if the digitalWrite and/or analogWrite functions are creating small delays as well, making the total delay something that isn't easy to predict.

Does anyone have any tips on how to display smooth animations using this method (arrays of PWM values) without any flickering?

Shoot, we’re talking on the nanosecond scale here… these instructions only take mere microseconds to execute, and the whole loop (provided you don’t use any Serial.print() or analogRead()) only takes less than 100us to run on average. With 1,000,000 microseconds in 1 meager second, it’s easy to see why it looks goofy.

As a general rule of thumb, anything visual will need to be delayed by at least 1 MILLIsecond (1,000 microseconds) per loop, at a bare minimum, in order to see it. I’d say you just went way overboard with changing delay(10) (delay 1/100th second) into delayMicroseconds(500) (half 1/1000th second). Find a happy medium, like delay(5) or maybe slower with delay(20), but don’t even think of touching delayMicroseconds with this sort of visual delay :wink:

But now I have a new problem: the patterns are being run through much too fast.

I suggest you start a new topic, and post your current code. The code you posted in this thread has ONE led being driven from the progmem values, and I haven't got a clue as to what "whole animation" is flickering too fast...