Using global variable to access array

Hi,

Could someone explain why the following is not working
I basically have a array (bmp_table) which has a array of animation (bitmaps)

If I code in the following function:

display.drawBitmap(20, 40, bmp_table[0][7], 16, 16, 1);

I see the correct bitmap on my screen, at the correct location, the correct size, everything is OK

If I define two global variables and put everything in a function I do get the correct location and size, but the bitmap itself is no longer correct. -> basically showing junk.

byte character_no = 0; 
byte character_size = 16;

void drawCharacter(byte x, byte y, byte c, byte a, byte s){
  Serial.println(x);
  Serial.println(y);
  Serial.println(c);
  Serial.println(a);
  Serial.println(s);
  
  display.drawBitmap(x, y, bmp_table[c][a], s, s, 1);
}

drawCharacter(20, 40, character_no, 7, character_size);

Why does this not create the exact same result?
The serialprint does show the values to be exactly the same as the first hard-coded example.
For some reason this whole array addressingstops working :S

Any help would be greatly appricated!

Posting a code snippet is useless

Post a complete sketch that gives you the problem

What values do you see when you print them in the function ?
Better names for the function parameters would help understanding what you are doing

At least the declaration of bmp_table is missing hence the advice to post (or attach) the whole sketch.
Its data type may not be printable without some conversion.

My appologies, I used snippets because right now it’s part of a big squetch which I cannot just post.
So I made a demo squetch which replicates the issue

The serial shows:
11:21:11.818 → Started
11:21:12.854 → 30
11:21:12.854 → 0
11:21:12.854 → 0
11:21:12.854 → 0
11:21:12.854 → 16
11:21:12.854 → 60
11:21:12.854 → 0
11:21:12.854 → 0
11:21:12.901 → 0
11:21:12.901 → 16
11:21:12.901 → Final

I should expect 3 times the same icon (happy_1) on my screen but it shows the first one correct and the 2nd and third icon are wrong (however they are the same)
The odd thing is, if I take out line saying: “drawCharacter(60, 0, char_num, animation, 16);”
I get 2 correct icons (happy_1) showing up.

/**************************************************************************
  This is an example for our Monochrome OLEDs based on SSD1306 drivers

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/category/63_98

  This example is for a 128x64 pixel display using I2C to communicate
  3 pins are required to interface (two I2C and one reset).

  Adafruit invests time and resources providing this open
  source code, please support Adafruit and open-source
  hardware by purchasing products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries,
  with contributions from the open source community.
  BSD license, check license.txt for more information
  All text above, and the splash screen below must be
  included in any redistribution.
 **************************************************************************/

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#include <avr/pgmspace.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

static const unsigned char PROGMEM happy_1[] = {
  B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000
};

static const unsigned char PROGMEM sad_1[] = {
  B00000000, B11000000,
  B00000000, B11000000,
  B00000000, B11000000,
  B00000000, B11100000,
  B00000000, B11100000,
  B00000000, B11111000,
  B00000000, B11111111,
  B00000000, B10011111,
  B00000000, B11111100,
  B00000000, B01110000,
  B00000000, B10100000,
  B00000000, B11100000,
  B00000000, B11110000,
  B00000000, B11110000,
  B00000000, B01110000,
  B00000000, B00110000
};

static const unsigned char PROGMEM happy_2[] = {
  B00000000, B00000000,
  B00000000, B00000000,
  B00000000, B00000000,
  B00000000, B00000000,
  B00000000, B00000000,
  B00000000, B00000000,
  B00000000, B00000000,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000
};

static const unsigned char PROGMEM sad_2[] = {
  B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00000000, B00000000,
  B00000000, B00000000,
  B00000000, B00000000,
  B00000000, B00000000,
  B00000000, B00000000,
  B00000000, B00000000,
  B00000000, B00000000,
  B00000000, B00000000,
  B00000000, B00000000
};

const unsigned char * const character_1[] PROGMEM = { happy_1, sad_1 };
const unsigned char * const character_2[] PROGMEM = { happy_2, sad_2 };

const unsigned char * const* const bmp_table[] PROGMEM = { character_1, character_2};

byte char_num = 0;
byte animation = 0;

void setup() {
  Serial.begin(9600);
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;); // Don't proceed, loop forever
  }
  delay(1000);
  Serial.println(F("Started"));
  
  display.clearDisplay();
  display.display();
  delay(1000); //we should now have an empty screen
  
  display.drawBitmap(0, 0, bmp_table[0][0], 16, 16, 1); //draw the bitmap with hard coded values
  drawCharacter(30, 0, 0, 0, 16); //draw the bitmap via function with hard codes values
  drawCharacter(60, 0, char_num, animation, 16); //draw the bitmap with global varaibles

  display.display();

  Serial.println(F("Final"));
}

void loop() {
}

void drawCharacter(byte pos_x, byte pos_y, byte character, byte animation, byte icon_size){
  Serial.println(pos_x);
  Serial.println(pos_y);
  Serial.println(character);
  Serial.println(animation);
  Serial.println(icon_size);
 
  display.drawBitmap(pos_x, pos_y, bmp_table[character][animation], icon_size, icon_size, 1);
}
  display.drawBitmap(0, 0, bmp_table[0][0], 16, 16, 1); //draw the bitmap with hard coded values

To use data from PROGMEM, ie the bmp_table array, do you not have to access in a special way rather than just using the array name ?

  drawCharacter(30, 0, 0, 0, 16); //draw the bitmap via function with hard codes values
  drawCharacter(60, 0, char_num, animation, 16); //draw the bitmap with global varaibles

The only difference between these two function calls is the x position because both char_num and animation are set to zero so no surprise that you get the same character with both

UKHeliBob:

  display.drawBitmap(0, 0, bmp_table[0][0], 16, 16, 1); //draw the bitmap with hard coded values

To use data from PROGMEM, ie the bmp_table array, do you not have to access in a special way rather than just using the array name ?

No, it seems the library allows you to point to the bitmap. (Someone looked in the lib and told me this, this is not something I was able to judge myself)
I can draw any bitmap from the bmp_table and it works just fine, i tried 0-0, 0-1, 1-0 and 1-1 : al result in the correct icon displayed perfectly.
Thing just go south when the number is not in the code line but defined as a variable.
You mentioned:

The only difference between these two function calls is the x position because both char_num and animation are set to zero so no surprise that you get the same character with both

It indeed is no surpice, but if I remove the bottom drawCharacter() the code does display the correct 2nd icon.
More over, if I take the original code delay it shows the first icon correct and the 2nd and third wrong.
And if I then take out the last drawCharacter() the 2nd icon shows fine.

display.clearDisplay();
  display.display();
  delay(1000); //we should now have an empty screen

  display.drawBitmap(0, 0, bmp_table[0][0], 16, 16, 1); //draw the bitmap with hard coded values
  display.display();
  delay(1000);
  drawCharacter(30, 0, 0, 0, 16); //draw the bitmap via function with hard codes values
  display.display();
  delay(1000);
//  drawCharacter(60, 0, char_num, animation, 16); //draw the bitmap with global varaibles

the issue is that the pointers are in progmem. I think if you remove the (nested) lookup tables (or maybe just the lookup table to the lookup table) from progmem (and remove the 'const') it should work again.

Post a complete sketch that gives you the problem

Or just continue with your previous post. (ask the moderator to merge them would be best)

Why does this not create the exact same result?

Because you are passing constants for the lookup table.
In other words.. does this work ?

const unsigned char *  character_1[]  = {sad, happy};
const unsigned char *  character_2[]  = {happy, sad};
const unsigned char * *  bmp_table[]  = {character_1, character_2};

I tried this and it works as it should, I now have 3 the same icons.

But wouldnt this affect my RAM usage a lot when I'm extending this with more character and more animations?

This issue comes up fairly frequently: "When I use constants to fetch data from PROGMEM I get the right answer but when I use variables I get garbage." When you use constants to index into a constant array, the compiler does the work and uses the right answer rather than doing the indexing at run time. When you use variables, the indexing is done at run time and, since your arrays are in PROGMEM, you have to use the special functions for fetching values from PROGMEM. If you don't use the special functions for reading from the PROGMEM address space, those fetches are done in the RAM address space and what you fetch is NOT the data you wanted.

But wouldnt this affect my RAM usage a lot when I'm extending this with more character and more animations?

Up to some extend of course it does, but they are just pointers, 16-bit each.
The bulk of the memory is used up by the bitmaps.

When you use variables, the indexing is done at run time and, since your arrays are in PROGMEM, you have to use the special functions for fetching values from PROGMEM. If you don't use the special functions for reading from the PROGMEM address space, those fetches are done in the RAM address space and what you fetch is NOT the data you wanted.

Yeah i was wondering about this. So then we get nested calls to 'pgm_read_word_near()' ? eh drawBitmap() takes a pointer to PROGMEM (in an overloaded function) , so how would that look in the original setup with both lookup tables in PROGMEM ? (I still find it a pity that the topic is not merged with the other one, i was gonna have a quick look at the relevant part of adafruit_GFX.h that i posted in there)

Deva_Rishi:
so how would that look in the original setup with both lookup tables in PROGMEM ?

It looks like this:

void drawCharacter(byte pos_x, byte pos_y, byte character, byte animation, byte icon_size)
{
  Serial.println(pos_x);
  Serial.println(pos_y);
  Serial.println(character);
  Serial.println(animation);
  Serial.println(icon_size);
  
  unsigned char const * const * bitmapPointerPointer = pgm_read_ptr_near(&bmp_table[char_num]);
  unsigned char const * bitmapPointer = pgm_read_ptr_near(&bitmapPointerPointer[animation]);
  display.drawBitmap(0, 0, bitmapPointer, 16, 16, 1); //draw the bitmap with hard coded values
  // display.drawBitmap(pos_x, pos_y, bmp_table[character][animation], icon_size, icon_size, 1);
}

I'm sure it is possible to reduce it to a single line but that would make it vary hard to read.

Great thanks !

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.