Displaying an image on a graphic LCD (KS0108)

I have a 128x64 graphic LCD connected to (and working with) my Arduino Duemianove. As a first smoke test I tried out the sample program from http://www.arduino.cc/playground/Code/GLCDks0108 and it works fine.

At this point, I want to try displaying an image. I wrote a Windows program that outputs a monochrome image into a text file using ones and zeros…

bool bits = {1,1,0,0,1,0,1,0…

I then added the data (all 128x64) into a sample Arduino sketch and wrote code to parse the bool array and call GLCD.SetDot(). The sketch compiled, but it was too big for my Arduino.

My second attempt was to use run length encoding to compress the data, so if the image was 110000111011111 it would be 2,4,3,1,5. I wrote code to take an array of RLE-encoded data and print the pixels using SetDot(). This >almost< works, in that the encoding works, and the parsing and displaying also work, however even though my sketch is small enough (it went from 16K to 4K when I used RLE) when I upload the sketch my Arduino crashes.

I narrowed down the issue and it seems to be related to the amount of data I have in my array. If I have around 16 lines of data, everything works, but after I add a few more lines the Arduino locks up. My guess is that there is a limited amount of stack memory and I’m using too much, resulting in the crash (when I say crash, what’s going on is that the machine constantly calls setup() over and over).

So I’m about to try this a third way: pack the bits into an array of bytes and then loop through the array using bit shifting and bit masks, but before I do I thought I’d post a message here and ask if anyone has displayed an image with an Arduino and a GLCD, and if so how did you do it (and if you could provide code that would be even better).

Thanks,

Jeff

Check out this link:

http://codinglab.blogspot.com/2009/03/gimp-plugin-to-export-bitmaps-for.html

and this one.

You're overflowing RAM: you need to move your bitmaps to flash.

You should also store them with 8 bits/byte, lest you chew up all flash, instead.

Ran

Storing them that way is using up 8 bits for every bit of your image. Use an 8 bit byte or uint8_t

Thanks, I’ve got it figured out now. The issue with the large array (as Ran pointed out) is that I needed to move the array to flash memory (using PROGMEM, prog_uchar, and pgm_read_byte_near().

I also implemented it a different way using packed bits and uint8_t.

Thanks!

Jeff

I have not had time to document it but the beta code in the playground has a method that will display a bitmap image stored in flash. The method is DrawBitmap. You pass a pointer to the bitmap (in flash), the x and y coordinates of the upper left hand corner where you want the bitmap displayed, and the BLACK or WHITE to indicate how you want the pixels drawn.

Test sketch:

/*
 * GLCDBitmapExample
 */

#include <ks0108.h>
#include "ArduinoIconInv.h"
#define X_OFFSET 0  


void setup(){
  GLCD.Init(NON_INVERTED);   // initialise the library, non inverted writes pixels onto a clear screen
  GLCD.DrawBitmap(ArduinoIconInv, 0,0, BLACK); //draw the bitmap in the top left of the LCD
}

void loop()
{
  
}

An example bitmap image follows. Save as ArduinoIconInv.h in the sketch directory

/* ArduinoIconInv.h bitmap file for GLCD library */
/* Bitmap created from ArduinoIconInv.bmp        */
/* Date: 9 Nov 2008                             */
/* Copyright (c) 2008 Michael Margolis          */

#include <inttypes.h>
#include <avr/pgmspace.h>

#ifndef ArduinoIconInv_H
#define ArduinoIconInv_H

static uint8_t ArduinoIconInv[] PROGMEM = {
  64, // width
  64, // height

  /* page 0 (lines 0-7) */
  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x40,0x20,0x20,0x20,0x20,0x20,0x40,
  0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0xe0,0xe0,0xe0,
  0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x0,0x0,
  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  
  /* page 1 (lines 8-15) */
  0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x63,0x80,0x0,0x0,0x7f,0x3e,0x1c,0x8,0x0,
  0x80,0x63,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0xf8,0xff,0xff,0x7f,0xf,0x7f,
  0xff,0xff,0xf8,0xc0,0x0,0x0,0x0,0x0,0x0,0x80,0x40,0x20,0x3f,0x20,0x40,0x80,
  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  
  /* page 2 (lines 16-23) */
  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x20,0x11,0x8a,0x8a,0x8a,0x8a,0x8a,0x11,
  0x20,0xc0,0x0,0x0,0x0,0x0,0x80,0xf0,0xfe,0xff,0xff,0xff,0xe3,0xe0,0xe0,0xe0,
  0xe3,0xff,0xff,0xff,0xfe,0xf0,0x80,0x0,0x0,0x81,0x42,0x24,0x24,0x24,0x42,0x85,
  0x28,0x20,0x38,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  
  /* page 3 (lines 24-31) */
  0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x18,0x20,0x40,0x8f,0x88,0x88,0x88,0x8f,0x40,
  0x20,0x18,0x7,0x0,0x60,0xfc,0xff,0xff,0xbf,0x7,0x1,0x1,0x1,0x1,0x1,0x1,
  0x1,0x81,0x1,0x7,0x3f,0xff,0xff,0xfc,0x60,0x1,0x2,0x4,0xfc,0x24,0x22,0x21,
  0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
  
  /* page 4 (lines 32-39) */
  0xc0,0x40,0x40,0x40,0x40,0x70,0xc,0x30,0xc0,0x0,0xc0,0x30,0xd,0x31,0xc0,0x0,
  0xc0,0x30,0x8,0x8,0x88,0x49,0x28,0x29,0x38,0x20,0x20,0x40,0x80,0x40,0x20,0x10,
  0x20,0x98,0x18,0xc0,0xc1,0x0,0x0,0x0,0x20,0x30,0x50,0x48,0x8f,0x80,0x0,0x0,
  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  
  /* page 5 (lines 40-47) */
  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x1,
  0x0,0x0,0x1c,0x63,0x80,0x46,0x4a,0x52,0x62,0x52,0x4a,0x46,0x80,0x63,0x1c,0x2,
  0x1,0x10,0x11,0x0,0x0,0x0,0x0,0x0,0x8,0x8,0x14,0x14,0xa2,0xa2,0x41,0x41,
  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  
  /* page 6 (lines 48-55) */
  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  0x0,0x0,0x0,0x0,0x0,0x1,0x2,0x2,0xfe,0x2,0x2,0x1,0x0,0x0,0x0,0x0,
  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x2,0x5,0xfd,0x0,0x0,0x0,0x0,
  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  
  /* page 7 (lines 56-63) */
  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  0x0,0x0,0x0,0x0,0x0,0x2,0xa,0xa,0x2b,0xa,0xa,0x2,0x0,0x0,0x0,0x0,
  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x2,0xa,0x2b,0xa,0x2,0x2,0x0,
  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
  
};
#endif

Here is a Processing sketch that reads a bmp file and creates a header that can be used by the GLCD library as per the example above. Note the BMP file must be monochrome (one bit per pixel)

/**
 * glcdBitmap. 
 * 
 * Creates a bitmap definition file that can be included in an Arduino sketch
 * to display a bitmap image on a graphics LCD using the Arduino GLCD library.
 *
 *  
 * Created  6 Nov  2008
 */
String sourceImage =  "ArduinoIconInv.bmp";  // change this for your bitmap file
PImage a;
int[] aPixels;

void setup() 
{

  noFill();
  stroke(255);
  frameRate(30);
  // load the image
  a = loadImage(sourceImage);
  if( a != null){
    size(a.width, a.height);
    aPixels = new int[width*height];
    for(int i=0; i<width*height; i++) {
      aPixels[i] = a.pixels[i];
    }
    // display the image
    loadPixels();
    for (int i=0; i<width*height; i++) 
        pixels[i] = aPixels[i];  
    updatePixels();
    writeFile(sourceImage);
    println("created header file for " + sourceImage);  
  }  
}

void writeFile(String inFileName){
  String[] f = split(inFileName, '.');
  String baseName = f[0];

  String outFileName = baseName + ".h"; 
  PrintWriter output;
  output = createWriter(outFileName);

  output.println("/* " + outFileName + " bitmap file for GLCD library */");
  output.println("/* Bitmap created from " + inFileName + "        */");
  String[] monthName = {"","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
  output.println("/* Date: " +  day() + " " + monthName[month()] +  " " +  year() + "                             */" ); 
  output.println("/* Copyright (c) 2008 Michael Margolis          */");
  output.println();
  
  output.println("#include <inttypes.h>");
  output.println("#include <avr/pgmspace.h>");
  output.println();
  output.println("#ifndef " + baseName + "_H");
  output.println("#define " + baseName + "_H");
  
  output.println();
  output.print("static uint8_t ");
  output.print(baseName);
  output.println("[] PROGMEM = {");
 
  output.print("  ");
  output.print(a.width);   // note width and height are bytes so 256 will be 0
  output.println(", // width"); 
  output.print("  ");
  output.print(a.height);
  output.println(", // height"); 
  for(int page=0; page < (a.height + 7)/8; page++) {
     output.println("\n  /* page " + page + " (lines "  + page*8 + "-" + (page*8+7) + ") */");
     output.print("  "); 
     for(int x=0; x < a.width; x++){
          output.print( "0x" + Integer.toHexString(getValue(x,page))) ;   
          if( (x == (a.width-1)) && (page == (((a.height +7)/8)-1))  )
              println("\n"); // this is the last element so new line instead of comma
           else   
              output.print(",");   // comma on all but last entry
          if( x % 16 == 15)
             output.print("\n  "); 
          
  //         else
  //           output.print(",");   
     }
  }  
  output.print("\n};\n");
  output.println("#endif");
  
  output.flush(); // Write the remaining data
  output.close(); // Finish the file

}
int getValue( int x, int page){
  int val = 0;
  for( byte b=0; b < 8; b++){
      int y = page * 8 + b;
      int pos = y * a.width + x;
//      if( aPixels[pos] != -1){        
     if( aPixels[pos] != 0xffffffff ){     
         val |=  (1 << b);   // here if color is not black
      }
  }   
  return val;  
}

void draw() 
{
  
}