Grey-scale rendering

From Gamebuino Wiki
Jump to: navigation, search

The following is outdated as the GREY color is now part of the core library. Please refer to gb.display.setColor.

The following code shows how to display 3 colors on the Gamebuino LCD: black, white and grey. It achieves this by alternating the color of the grey pixels across subsequent frames. The demo also scrolls the background to show that real-time flicker-free full-screen rendering is indeed possible.

The image in this demo was provided by Gamebuino Forum user Erico and is a concept mock-up of a "Moon Patrol" style game. A video of this demo in action can be found at https://www.youtube.com/watch?v=JHqHEXQQD6M

// Gamebuino full-screen grey-scale rendering demo - by Mark Feldman (aka "Myndale")
//
// Based on the PCD8544 display code at http://playground.arduino.cc/Code/PCD8544

#include <TimerOne.h>

// enable this to scroll the image horizontally every frame
#define SCROLLING

// when this is enable only the bottom 40 rows are scrolled
#define SCORE_BAR

// how often to update the screen, 41 seems to be the magic number to minimize flicker
#define FRAME_RATE 41

#define PIN_SCE   A1
#define PIN_RESET A0
#define PIN_DC    A2
#define PIN_SDIN  11
#define PIN_SCLK  13
#define PIN_BACKLIGHT 5

#define PORT_SCLK PORTB
#define SCLK_BIT 5
#define PORT_SDIN PORTB
#define SDIN_BIT 3

#define LCD_C     LOW
#define LCD_D     HIGH

#define LCD_X     84
#define LCD_Y     48
#define LCD_CMD   0

int frame = 0;    // which of the two buffer's we're displaying
int ofs = 0;      // horizontal offset, mainly used for scrolling

// declare two display buffers, the interrupt will use then to update the LCD
// this isn't the most memory-efficient way of representing a 3-color screen
// but it'll work fine for this demo
byte buffers[2][LCD_X*LCD_Y/8] =
{
  {
    0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,
    0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0x0f,0x0f,0xef,0xaf,0xef,0x0f,0xef,0xaf,0xef,0x0f,0x2f,
    0xef,0x2f,0x0f,0x0f,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0x0f,0x0f,0xcf,0x4f,
    0xef,0x0f,0x2f,0xef,0x2f,0x0f,0x2f,0xef,0x8f,0x0f,0xef,0x6f,0x6f,0x0f,0x6f,0xef,0xef,0x0f,0xef,0xaf,0xef,
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,
    0xd7,0xc7,0xd7,0xc7,0xd7,0xef,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfa,0xff,0xff,0xff,
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0xef,0xe7,0xfb,0xbf,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
    0xff,0xff,0xff,0xef,0xcf,0xbf,0x9f,0xbf,0xcf,0xef,0xff,0xff,0xff,0xff,0xfe,0xfd,0xfc,0xfd,0xfe,0xff,0xff,
    0xff,0xff,0xff,0xff,0xff,0xff,0xf9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,0xff,0x7f,0xff,0x7f,0xff,
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
    0xff,0xfe,0xff,0xfe,0xfd,0xfa,0xfc,0xf8,0xf8,0xfc,0xfe,0xfe,0xfe,0x7f,0x7f,0xff,0xff,0xff,0x7f,0x7f,0xfe,
    0xff,0xfe,0xfd,0xfa,0xf0,0xf8,0xf8,0xc8,0xfd,0xfc,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,
    0xfc,0xfe,0xfc,0xfe,0xfc,0xfc,0xf8,0xf4,0xf8,0xf4,0xf8,0xf0,0xe8,0xf0,0xc0,0x80,0xc0,0xf0,0xf8,0xfc,0xfe,
    0xfe,0xfe,0xfe,0xfe,0xfe,0xfc,0xfa,0xe0,0xf0,0xf8,0xfc,0xfe,0xfe,0xfe,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfe,
    0x00,0x00,0x00,0x00,0x07,0x01,0x3f,0x03,0x07,0x05,0x05,0x02,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x19,0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x1f,0x1f,0x1f,0x7f,0xff,0xff,0xff,0xbf,0xbf,0xbf,0xff,0xbf,0xbf,0x3f,0x9f,0x7f,0x1f,0x1f,0x1f,0x1f,0x1f,
    0x1f,0x1f,0x1f,0x0f,0x0f,0x0f,0x0f,0x0f,0xdf,0xef,0xff,0xef,0xff,0x6f,0x5f,0xcf,0x0f,0x0f,0x4f,0xaf,0x4f,
    0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x0f,0x07,0x01,0x00,0x00,0x00,0x00,0x00,
    0x00,0x01,0x07,0x0f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x0f,0x0f,0x0f,0x1f,0x1f,0x1f,
  },
  {
    0xef,0x4f,0x4f,0x4f,0x4f,0x4f,0xcf,0x4f,0x4f,0x4f,0x0f,0x0f,0x8f,0x0f,0x0f,0x0f,0x0f,0x0f,0x8f,0x0f,0x0f,
    0x0f,0x0f,0x0f,0x8f,0x0f,0x0f,0x0f,0x0f,0x0f,0x8f,0x0f,0x0f,0xef,0xaf,0xef,0x0f,0xef,0xaf,0xef,0x0f,0x2f,
    0xef,0x2f,0x0f,0x0f,0x0f,0x2f,0x6f,0x2f,0x0f,0x2f,0x6f,0x2f,0x0f,0x2f,0x6d,0x2f,0x0f,0x0f,0x0f,0xcf,0x4f,
    0xef,0x0f,0x2f,0xef,0x2f,0x0f,0x2f,0xef,0x8f,0x0f,0xef,0x6f,0x6f,0x0f,0x6f,0xef,0xef,0x0f,0xef,0xaf,0xef,
    0xff,0xff,0xff,0xff,0xff,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,
    0xd7,0xc7,0xc7,0xc7,0xd7,0xef,0xff,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
    0xff,0xff,0xff,0xff,0xff,0xff,0xfd,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf8,0xf8,0xff,0xff,0xff,
    0xff,0xff,0xff,0xff,0xff,0xff,0xcf,0xcf,0xe7,0xe1,0x99,0x9f,0xff,0xff,0xff,0xff,0xff,0xf7,0xff,0xff,0xff,
    0xff,0xff,0xff,0xef,0xcf,0x9f,0x9f,0x9f,0xcf,0xef,0xff,0xff,0xff,0xff,0xfe,0xbc,0xfc,0xfc,0xfe,0xff,0xff,
    0xff,0xff,0xff,0xff,0xff,0xff,0xf9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f,0x3f,0x7f,0x7f,0x3f,0x3f,0xff,
    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
    0xfe,0xfc,0xdc,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xfc,0xfe,0xfe,0xfe,0x7f,0x7f,0xff,0xff,0xfe,0x7e,0x7e,0xfc,
    0xfc,0xf8,0xf0,0xf0,0xf0,0xf8,0xf8,0xc8,0xfd,0xfc,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0x7e,0xfc,0xfc,0xfc,
    0xfc,0xfc,0xf8,0xf8,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xe0,0xe0,0xc0,0xc0,0x80,0x80,0xc0,0xf0,0xf8,0xfc,0xfe,
    0xfe,0xfe,0xfc,0xfc,0xf8,0xf0,0xe0,0xe0,0xf0,0xf8,0xfc,0xfe,0xfe,0xfe,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfe,
    0x00,0x00,0x00,0x00,0x07,0x01,0x3f,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x19,0x00,0x00,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x1f,0x1e,0x1d,0x7e,0xfd,0xda,0x14,0x08,0x12,0x08,0x00,0x10,0x00,0x10,0x00,0x10,0x10,0x10,0x10,0x18,0x10,
    0x10,0x18,0x10,0x08,0x08,0x08,0x08,0x08,0x58,0xe8,0x58,0xe8,0xd8,0x68,0x58,0xc8,0x08,0x08,0x48,0xa8,0x48,
    0x10,0x18,0x10,0x10,0x18,0x10,0x10,0x10,0x18,0x12,0x10,0x1a,0x15,0x0b,0x07,0x01,0x00,0x00,0x00,0x00,0x00,
    0x00,0x01,0x07,0x0b,0x15,0x1a,0x10,0x12,0x18,0x10,0x18,0x10,0x10,0x18,0x10,0x08,0x08,0x08,0x10,0x18,0x10
  }
};


void LcdClear(void) {
  for (int index = 0; index < LCD_X * LCD_Y / 8; index++)
  {
    LcdWrite(LCD_D, 0x00);
  }
}

void LcdInitialise(void) {
  pinMode(PIN_SCE,   OUTPUT);
  pinMode(PIN_RESET, OUTPUT);
  pinMode(PIN_DC,    OUTPUT);
  pinMode(PIN_SDIN,  OUTPUT);
  pinMode(PIN_SCLK,  OUTPUT);

  digitalWrite(PIN_RESET, LOW);
  digitalWrite(PIN_RESET, HIGH);

  LcdWrite( LCD_CMD, 0x21 );  // LCD Extended Commands.
  LcdWrite( LCD_CMD, 0xB9 );  // Set LCD Vop (Contrast). //B1
  LcdWrite( LCD_CMD, 0x04 );  // Set Temp coefficent. //0x04
  LcdWrite( LCD_CMD, 0x14 );  // LCD bias mode 1:48. //0x13
  LcdWrite( LCD_CMD, 0x0C );  // LCD in normal mode. 0x0d for inverse
  LcdWrite(LCD_C, 0x20);
  LcdWrite(LCD_C, 0x0C);
}

void LcdWrite(byte dc, byte data) {
  digitalWrite(PIN_DC, dc);
  digitalWrite(PIN_SCE, LOW);
  shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data);
  digitalWrite(PIN_SCE, HIGH);
}

void gotoXY(int x, int y) {
  LcdWrite( 0, 0x80 | x);  // Column.
  LcdWrite( 0, 0x40 | y);  // Row.  
}

void setup(void) {
  LcdInitialise();
  LcdClear();
  pinMode(PIN_BACKLIGHT, OUTPUT);
  digitalWrite(PIN_BACKLIGHT, HIGH);
  
  Timer1.initialize(1000000/FRAME_RATE);         // initialize timer1, and set a 1/2 second period
  Timer1.pwm(9, 512);                // setup pwm on pin 9, 50% duty cycle
  Timer1.attachInterrupt(callback);  // attaches callback() as a timer overflow interrupt
}

/* this code works but digitalWrite is much slower than directly writing to the port
#define bitOut(val, bit) {                       \
  digitalWrite(PIN_SDIN, !!(val & (1 << bit)));  \
  digitalWrite(PIN_SCLK, HIGH);                  \
  digitalWrite(PIN_SCLK, LOW);                   \
}
*/

#define bitOut(val, bit) {                       \
  if (val & (1 << bit))                          \
    PORT_SDIN |= _BV(SDIN_BIT);                  \
  else                                           \
    PORT_SDIN &= ~_BV(SDIN_BIT);                 \
  PORT_SCLK |= _BV(SCLK_BIT);                    \
  PORT_SCLK &= ~_BV(SCLK_BIT);                   \
}

inline void byteOut(byte val)
{
  bitOut(val, 0);
  bitOut(val, 1);
  bitOut(val, 2);
  bitOut(val, 3);
  bitOut(val, 4);
  bitOut(val, 5);
  bitOut(val, 6);
  bitOut(val, 7);
}

void callback() {
  digitalWrite(PIN_DC, LCD_D);
  digitalWrite(PIN_SCE, LOW);  
  for (int i=0; i<504; i++)
    byteOut(buffers[frame][i+ofs]);
  digitalWrite(PIN_SCE, HIGH);
  frame = 1 - frame;
  
#ifdef SCROLLING
  if (frame == 0)
    scrollBuffers();
#endif // SCROLLING    
}

void scrollBuffers() {
  for (int frame=0; frame<2; frame++)
  {
#ifdef SCORE_BAR
    int y = 1;
#else
    int y = 0;
#endif
    byte * buffer = buffers[frame] + y*84;
    for (; y<(LCD_Y/8); y++, buffer+=LCD_X)
    {
      byte start = *buffer;
      for (int x=0; x<LCD_X-1; x++)
        buffer[x] = buffer[x+1];
      buffer[LCD_X-1] = start;
    }
  }
}

void loop(void) {
  // loop still gets called, and the LCD is being updated via an interrupt
  // so put your game code here and update the display buffers at whatever
  // frame rate you like
}