Arduino to 384 LED display module (MCU Interface)

Here are the comments to go with the demo program.

First, the video (if it works):

The hardware:
I used a separate power supply for the LED board. It ends up drawing about 250mA with all the LEDs on, so it's probably ok to power it from the arduino...

  • Digital Output 12 of the Arduino is connected to pin 1 of the module connector (CS). the "1" position of the CS siwtch on the module is flipped to "on".
  • Digitial Output 11 goes to pin 5 of the module (WRCLK)
  • Digital Output 10 goes to pin 7 of the module (DATA)
  • GND from the digital output are goes to pin 11 on module (GND)

The 24-g wires that fit nicely in the arduino connectors were pretty lose in the connectors that came with the module. You might need to use "real" connectors!

The chip documentation mentions that the chip may not be up to driving ALL the LEDs in a full-sized matrix without driver transistors (which are not used on the SureElectronics Board), and this is easily observable. In the demo, the LEDs are turned on one at a time, and you can see the brightness start to go down after about 1/3 of the LEDs are lit ("only 128"), and it continues to get dimmer till they're all lit. I would say that brightness is still "acceptable" will all LEDs on, and even-ness is pretty good, but it may be bothersome if you're trying to maintain even brightness between "few leds" and "many leds" displays.

Of course, the display does not reset when the arduino resets, and the initial configuraton does not clear the LED memory until the setup() code does it explicitly. This clearing is an example of "full speed" access to the display by the arduino - hit your reset button when the display is full, and watch how fast it blanks. (Note: this is not at all optimized; there are several things that would make it faster. It's just how fast the current code is without any "delay" calls.)

The addressing of individual dots is a bit weird (or "inconvenient", anyway.) The "bottom left LED" is something like bit 0 of word 3, for example. It's sort of a shame, because I think it would have been easy enough to build the board differently. Oh well; it's possible that the current scheme works out better for text displays... This is something I should fix in software in the final version of the code.

I guess that's all I have for now... It's a neat little board!

Thanks westfw! Great work!

Video below of it running with Arduino onboard power.
I just tinkered with the delays a little. I'd no idea how to implement the .h file and couldn't find a howto on the subject, tried making a standalone file that went into the /library e.t.c. Figured it out in the end. wncranger If you need help just PM me.

Glad you got it working!

In general, you can just put the .h files in the same directory as the sketch and it will find it without problems...

Here's some more demo code to play with. Now has "plot(x,y)" function, and line drawing.

The brightness "problem" is more serious than I thought since LEDs start to dim when more than one third are lit in any particular ROW (my initial statement suggest 1/3 of all the LEDs, which was silly given than only one row is actually illuminated at any one time. Sigh.) As a result, for example, a horizontal line and a vertical line have significantly different brightnesses. This shows up in the "Cross" part of the demo.

It looks like the top 8 levels of PWM dimming are all about the same brightness when used with the displays in this board.

/*
 * demo16x24.c - Arduino demo program for Holtek HT1632 LED driver chip,
 *            As implemented on the Sure Electronics DE-DP016 display board
 *            (16*24 dot matrix LED module.)
 * Nov, 2008 by Bill Westfield ("WestfW")
 */

#include "ht1632.h"
#define X_MAX 23
#define Y_MAX 15

#define ASSERT(condition) //nothing


/*
 * Set these constants to the values of the pins connected to the SureElectronics Module
 */
static const byte ht1632_data = 10;  // Data pin (pin 7)
static const byte ht1632_wrclk = 11; // Write clock pin (pin 5)
static const byte ht1632_cs = 12;    // Chip Select (1, 2, 3, or 4)
// The should also be a common GND.
// The module with all LEDs like draws about 200mA,
//  which makes it PROBABLY powerable via Arduino +5V

#define DEMOTIME 30000  // 30 seconds max on each demo is enough.
#define DISPDELAY 40    // Each "display" lasts this long
#define LONGDELAY 1000  // This delay BETWEEN demos


/*
 * ht1632_writebits
 * Write bits (up to 8) to h1632 on pins ht1632_data, ht1632_wrclk
 * Chip is assumed to already be chip-selected
 * Bits are shifted out from MSB to LSB, with the first bit sent
 * being (bits & firstbit), shifted till firsbit is zero.
 */
void ht1632_chipselect(byte chipno)
{
  DEBUGPRINT("\nHT1632(%d) ", chipno);
  digitalWrite(chipno, 0);
}

void ht1632_chipfree(byte chipno)
{
  DEBUGPRINT(" [done %d]", chipno);
  digitalWrite(chipno, 1);
}

void ht1632_writebits (byte bits, byte firstbit)
{
  DEBUGPRINT(" ");
  while (firstbit) {
    DEBUGPRINT((bits&firstbit ? "1" : "0"));
    digitalWrite(ht1632_wrclk, LOW);
    if (bits & firstbit) {
      digitalWrite(ht1632_data, HIGH);
    } 
    else {
      digitalWrite(ht1632_data, LOW);
    }
    digitalWrite(ht1632_wrclk, HIGH);
    firstbit >>= 1;
  }
}

static void ht1632_sendcmd (byte command)
{
  ht1632_chipselect(ht1632_cs);  // Select chip
  ht1632_writebits(HT1632_ID_CMD, 1<<2);  // send 3 bits of id: COMMMAND
  ht1632_writebits(command, 1<<7);  // send the actual command
  ht1632_writebits(0, 1);       /* one extra dont-care bit in commands. */
  ht1632_chipfree(ht1632_cs); //done
}

static void ht1632_senddata (byte address, byte data)
{
  ht1632_chipselect(ht1632_cs);  // Select chip
  ht1632_writebits(HT1632_ID_WR, 1<<2);  // send ID: WRITE to RAM
  ht1632_writebits(address, 1<<6); // Send address
  ht1632_writebits(data, 1<<3); // send 4 bits of data
  ht1632_chipfree(ht1632_cs); // done
}

void setup ()  // flow chart from page 17 of datasheet
{
  pinMode(ht1632_cs, OUTPUT);
  digitalWrite(ht1632_cs, HIGH);       /* unselect (active low) */
  pinMode(ht1632_wrclk, OUTPUT);
  pinMode(ht1632_data, OUTPUT);
  ht1632_sendcmd(HT1632_CMD_SYSDIS);  // Disable system
  ht1632_sendcmd(HT1632_CMD_COMS11);  // 16*32, PMOS drivers
  ht1632_sendcmd(HT1632_CMD_MSTMD);       /* Master Mode */
  ht1632_sendcmd(HT1632_CMD_SYSON);       /* System on */
  ht1632_sendcmd(HT1632_CMD_LEDON);       /* LEDs on */
  for (byte i=0; i<128; i++)
    ht1632_senddata(i, 0);  // clear the display!
  delay(LONGDELAY);
}

/*
 * we keep a copy of the display controller contents so that we can
 * know which bits are on without having to (slowly) read the device.
 * Note that we only use the low four bits of the shadow ram, since
 * we're shadowing 4-bit memory.  This makes things faster, but we
 * COULD do something with the other half of our bytes !
 */
byte ht1632_shadowram[96];  // our copy of the display's RAM

/*
 * plot a point on the display, with the upper left hand corner
 * being (0,0), and the lower right hand corner being (23, 15).
 * Note that Y increases going "downward" in contrast with most
 * mathematical coordiate systems, but in common with many displays
 * No error checking; bad things may happen if arguments are out of
 * bounds!  (The ASSERTS compile to nothing by default
 */
void plot (char x, char y, char val)
{
  char addr, bitval;

  ASSERT(x >= 0);
  ASSERT(x <= X_MAX);
  ASSERT(y >= 0);
  ASSERT(y <= y_MAX);

  /*
   * The 4 bits in a single memory word go DOWN, with the LSB
   * (last transmitted) bit being on top.  However, writebits()
   * sends the LSB first, so we have to do a sort of bit-reversal
   * somewhere.  Here, this is done by shifting the single bit in
   * the opposite direction from what you might expect.
   */
  bitval = 8>>(y&3);  // compute which bit will need set
  addr = (x<<2) + (y>>2);  // compute which memory word this is in
  if (val) {  // Modify the shadow memory
    ht1632_shadowram[addr] |= bitval;
  } 
  else {
    ht1632_shadowram[addr] &= ~bitval;
  }
  // Now copy the new memory value to the display
  ht1632_senddata(addr, ht1632_shadowram[addr]);
}

/*
 * scan()
 * scan all the leds one at a time,
 * using the plot() function, so as to demonstate the limits
 * of "plot" performance.
 */
void scan ()
{
  byte x,y, bits;
  for (y=0; y<=Y_MAX; y++) {
    for (x=0; x <= X_MAX; x++) {
      plot(x,y,1);
      delay(DISPDELAY);
      plot(x,y,0);
    }
  }
}

/*
 * random_walk()
 * have a single LED walk all over the display, randomly.
 */
void random_walk ()
{
  char x,y, dx, dy;
  dx = dy = 1;
  byte change;

  for (int i=0; i < (DEMOTIME/DISPDELAY);  i++) {
    plot(x,y,1);  // draw a point
    delay(DISPDELAY); // wait a bit.
    change = random(1,32);
    /*
     * figure out where to go next.  This code is a bit
     * random in more senses than one, but it seems to
     * have results that are more or less what I had in
     * mind for this portion of the demo.
     */
    if (change < 9) {
      // do nothing
    } 
    else if (change == 10) {
      dx = -dx;
    } 
    else if (change == 11) {
      dy = -dy;
    } 
    else if (change == 12) {
      dx = dy = 0;
    } 
    else if (change == 13) {
      dx = dy = 1;
    } 
    else if (change == 14) {
      dx = dy = -1;
    }

    plot(x,y,0); // erase the point

    x = x + dx;
    y = y+ dy;
    if (x > X_MAX) {
      dx = -1;
      x = X_MAX;
    } 
    else if (x < 0) {
      x = 0;
      dx = 1;
    }
    if (y >  Y_MAX) {
      y = Y_MAX;
      dy = -1;
    } 
    else if (y < 0) {
      y = 0;
      dy = 1;
    }
  }
}
// end of part 1

oops. It didn't fit in a single message any more. Here's part 2...

//begin part 2

/*
 * Draw a line between two points using the bresenham algorithm.
 * This particular bit of code is copied nearly verbatim from
 *   http://www.gamedev.net/reference/articles/article1275.asp
 * I don't like it much (too many local variables!), but it works,
 * and is fully explained at the site, so...
 */
void bres_line(byte x1, byte y1,
unsigned char x2, unsigned char y2,
unsigned char val )
{
  char deltax = abs(x2 - x1);        // The difference between the x's
  char deltay = abs(y2 - y1);        // The difference between the y's
  char x = x1;                       // Start x off at the first pixel
  char y = y1;                       // Start y off at the first pixel
  char xinc1, xinc2, yinc1, yinc2, den, num, numadd, numpixels, curpixel;

  if (x2 >= x1) {                // The x-values are increasing
    xinc1 = 1;
    xinc2 = 1;
  }  
  else {                          // The x-values are decreasing
    xinc1 = -1;
    xinc2 = -1;
  }

  if (y2 >= y1)                 // The y-values are increasing
  {
    yinc1 = 1;
    yinc2 = 1;
  }
  else                          // The y-values are decreasing
  {
    yinc1 = -1;
    yinc2 = -1;
  }

  if (deltax >= deltay)         // There is at least one x-value for every y-value
  {
    xinc1 = 0;                  // Don't change the x when numerator >= denominator
    yinc2 = 0;                  // Don't change the y for every iteration
    den = deltax;
    num = deltax / 2;
    numadd = deltay;
    numpixels = deltax;         // There are more x-values than y-values
  }
  else                          // There is at least one y-value for every x-value
  {
    xinc2 = 0;                  // Don't change the x for every iteration
    yinc1 = 0;                  // Don't change the y when numerator >= denominator
    den = deltay;
    num = deltay / 2;
    numadd = deltax;
    numpixels = deltay;         // There are more y-values than x-values
  }

  for (curpixel = 0; curpixel <= numpixels; curpixel++)
  {
    plot(x, y, val);             // Draw the current pixel
    num += numadd;              // Increase the numerator by the top of the fraction
    if (num >= den)             // Check if numerator >= denominator
    {
      num -= den;               // Calculate the new numerator value
      x += xinc1;               // Change the x as appropriate
      y += yinc1;               // Change the y as appropriate
    }
    x += xinc2;                 // Change the x as appropriate
    y += yinc2;                 // Change the y as appropriate
  }
}

/*
 * Draw a crpss on the display, and then use the PWM function
 * to demonstate how well (or not) the ht1632 dimming function works.
 */
void cross ()
{
  byte x, y;
  char intensity;
  for (x=0; x <= X_MAX; x++) {
    plot(x,7,1);
  }
  for (y=0; y < Y_MAX; y++) {
    plot(12,y,1);
  }
  for (intensity=14; intensity >= 0; intensity--) {
    ht1632_sendcmd(HT1632_CMD_PWM + intensity);
    delay(LONGDELAY/4);
  }
  ht1632_sendcmd(HT1632_CMD_PWM + 15); // back to max brightness
}

/*
 * bouncyline
 * Do the classic "bouncing line" demo, where the endpoints of a line
 * move independently and bounce off the edges of the display.
 * This should demonstrate (more or less) the performance limits of
 * the line drawing function.
 */
void bouncyline ()
{
  char x1,y1, x2,y2, dx1, dy1, dx2, dy2;

  x1 = random(0,X_MAX);
  x2 = random(0,X_MAX);
  y1 = random(0,Y_MAX);
  y2 = random(0,Y_MAX);
  dx1 = random(1,4);
  dx2 = random(1,4);
  dy1 = random(1,4);
  dy2 = random(1,4);
  for (int i=0; i < DEMOTIME/DISPDELAY; i++) {
    bres_line(x1,y1, x2,y2, 1);
    delay(DISPDELAY);
    bres_line(x1,y1, x2,y2, 0);

    x1 += dx1;
    if (x1 > X_MAX) {
      x1 = X_MAX;
      dx1 = -random(1,4);
    } 
    else if (x1 < 0) {
      x1 = 0;
      dx1 = random(1,4);
    }

    x2 += dx2;
    if (x2 > X_MAX) {
      x2 = X_MAX;
      dx2 = -random(1,4);
    } 
    else if (x2 < 0) {
      x2 = 0;
      dx2 = random(1,4);
    }

    y1 += dy1;
    if (y1 > Y_MAX) {
      y1 = Y_MAX;
      dy1 = -random(1,3);
    } 
    else if (y1 < 0) {
      y1 = 0;
      dy1 = random(1,3);
    }

    y2 += dy2;
    if (y2 > Y_MAX) {
      y2 = Y_MAX;
      dy2 = -random(1,3);
    } 
    else if (y2 < 0) {
      y2 = 0;
      dy2 = random(1,3);
    }
  }
}


void loop ()
{
  randomSeed(analogRead(0));

  bouncyline();
  delay(LONGDELAY);
  
  bres_line(0,0, 16,16, 1);
  delay(LONGDELAY);
  
  cross();
  delay(LONGDELAY);
  
  scan();
  delay(LONGDELAY);

  random_walk();

  delay(LONGDELAY);
}

Again, Great Work!
Also, thanks for the detailed comments in the sketches, it really helps when trying to decipher them and is an invaluable learning aid!

The last bits are some character displays (A-Z, 0-9) and "life" (can't have a graphics display without life, eh?)

Then factor the code a bit better so that only functions dependent on the chip details get ht1632_ in front of them, and "device independent graphics" get plot_ or something, and the demo routines get demo_, and a whole separate text document explaining at least the bottom level...

Excellent! Looking forward to the new stuff!

First of all, enormous thanks to webobject for tipping us off to these display boards and to westfw for doing the hard work of writing the code. And now as suggested, Conway's Life powered by an Arduino Nano (cool!):

(Arduino Nano & 24x16 LED display board - YouTube)

Andrew

PS code to follow very shortly

And here's the code:

/*

  • matrix_16x24_life
  • Andrew Hedges
  • Dec, 2008
  • Adapted from code by Bill Westfield ("WestfW")
    */

#include "ht1632.h"
#define X_MAX 23
#define Y_MAX 15
#define X_SIZE 24
#define Y_SIZE 16

#define ASSERT(condition) //nothing

/*

  • Set these constants to the values of the pins connected to the SureElectronics Module
  • NOTE - these are different from the original demo code by westfw
    */
    static const byte ht1632_data = 3; // Data pin (pin 7)
    static const byte ht1632_wrclk = 4; // Write clock pin (pin 5)
    static const byte ht1632_cs = 6; // Chip Select (1, 2, 3, or 4)
    // The should also be a common GND.
    // The module with all LEDs like draws about 200mA,
    // which makes it PROBABLY powerable via Arduino +5V

#define DEMOTIME 30000 // 30 seconds max on each demo is enough.
#define DISPDELAY 40 // Each "display" lasts this long
#define LONGDELAY 1000 // This delay BETWEEN demos

/*

  • ht1632_writebits
  • Write bits (up to 8) to h1632 on pins ht1632_data, ht1632_wrclk
  • Chip is assumed to already be chip-selected
  • Bits are shifted out from MSB to LSB, with the first bit sent
  • being (bits & firstbit), shifted till firsbit is zero.
    */
    void ht1632_chipselect(byte chipno)
    {
    DEBUGPRINT("\nHT1632(%d) ", chipno);
    digitalWrite(chipno, 0);
    }

void ht1632_chipfree(byte chipno)
{
DEBUGPRINT(" [done %d]", chipno);
digitalWrite(chipno, 1);
}

void ht1632_writebits (byte bits, byte firstbit)
{
DEBUGPRINT(" ");
while (firstbit) {
DEBUGPRINT((bits&firstbit ? "1" : "0"));
digitalWrite(ht1632_wrclk, LOW);
if (bits & firstbit) {
digitalWrite(ht1632_data, HIGH);
}
else {
digitalWrite(ht1632_data, LOW);
}
digitalWrite(ht1632_wrclk, HIGH);
firstbit >>= 1;
}
}

static void ht1632_sendcmd (byte command)
{
ht1632_chipselect(ht1632_cs); // Select chip
ht1632_writebits(HT1632_ID_CMD, 1<<2); // send 3 bits of id: COMMMAND
ht1632_writebits(command, 1<<7); // send the actual command
ht1632_writebits(0, 1); /* one extra dont-care bit in commands. */
ht1632_chipfree(ht1632_cs); //done
}

static void ht1632_senddata (byte address, byte data)
{
ht1632_chipselect(ht1632_cs); // Select chip
ht1632_writebits(HT1632_ID_WR, 1<<2); // send ID: WRITE to RAM
ht1632_writebits(address, 1<<6); // Send address
ht1632_writebits(data, 1<<3); // send 4 bits of data
ht1632_chipfree(ht1632_cs); // done
}

void setup () // flow chart from page 17 of datasheet
{
pinMode(ht1632_cs, OUTPUT);
digitalWrite(ht1632_cs, HIGH); /* unselect (active low) /
pinMode(ht1632_wrclk, OUTPUT);
pinMode(ht1632_data, OUTPUT);
ht1632_sendcmd(HT1632_CMD_SYSDIS); // Disable system
ht1632_sendcmd(HT1632_CMD_COMS11); // 16
32, PMOS drivers
ht1632_sendcmd(HT1632_CMD_MSTMD); /* Master Mode /
ht1632_sendcmd(HT1632_CMD_SYSON); /
System on /
ht1632_sendcmd(HT1632_CMD_LEDON); /
LEDs on */
for (byte i=0; i<128; i++)
ht1632_senddata(i, 0); // clear the display!
delay(LONGDELAY);
}

/*

  • we keep a copy of the display controller contents so that we can
  • know which bits are on without having to (slowly) read the device.
  • Note that we only use the low four bits of the shadow ram, since
  • we're shadowing 4-bit memory. This makes things faster, but we
  • COULD do something with the other half of our bytes !
    */
    byte ht1632_shadowram[96]; // our copy of the display's RAM

/*

  • plot a point on the display, with the upper left hand corner
  • being (0,0), and the lower right hand corner being (23, 15).
  • Note that Y increases going "downward" in contrast with most
  • mathematical coordiate systems, but in common with many displays
  • No error checking; bad things may happen if arguments are out of
  • bounds! (The ASSERTS compile to nothing by default
    */
    void plot (char x, char y, char val)
    {
    char addr, bitval;

ASSERT(x >= 0);
ASSERT(x <= X_MAX);
ASSERT(y >= 0);
ASSERT(y <= y_MAX);

/*

  • The 4 bits in a single memory word go DOWN, with the LSB
  • (last transmitted) bit being on top. However, writebits()
  • sends the LSB first, so we have to do a sort of bit-reversal
  • somewhere. Here, this is done by shifting the single bit in
  • the opposite direction from what you might expect.
    */
    bitval = 8>>(y&3); // compute which bit will need set
    addr = (x<<2) + (y>>2); // compute which memory word this is in
    if (val) { // Modify the shadow memory
    ht1632_shadowram[addr] |= bitval;
    }
    else {
    ht1632_shadowram[addr] &= ~bitval;
    }
    // Now copy the new memory value to the display
    ht1632_senddata(addr, ht1632_shadowram[addr]);
    }

byte world[X_SIZE][Y_SIZE][2];
long density = 50;

void setupLife() {
randomSeed(analogRead(5));
for (int i = 0; i < X_SIZE; i++) {
for (int j = 0; j < Y_SIZE; j++) {
if (random(100) < density) {
world*[j][0] = 1;*

  • }*
  • else {*
    _ world*[j][0] = 0;_
    _
    }_
    _ world[j][1] = 0;
    }
    }
    }
    void life() {
    while (1) {
    // Display current generation*
    * for (int i = 0; i < X_SIZE; i++) {
    for (int j = 0; j < Y_SIZE; j++) {*

    plot(i, j, world*[j][0]);
    }
    }
    delay(DISPDELAY);
    // Birth and death cycle*

    * for (int x = 0; x < X_SIZE; x++) {
    for (int y = 0; y < Y_SIZE; y++) {*

    * // Default is for cell to stay the same*
    * world[x][y][1] = world[x][y][0];
    int count = neighbours(x, y);
    if (count == 3 && world[x][y][0] == 0) {
    // A new cell is born*

    * world[x][y][1] = 1;
    }
    if ((count < 2 || count > 3) && world[x][y][0] == 1) {
    // Cell dies*

    * world[x][y][1] = 0;
    }
    }
    }*_

* // Copy next generation into place*
[/quote]
[continued in next post]

... and the rest:

// Copy next generation into place
for (int x = 0; x < X_SIZE; x++) {
for (int y = 0; y < Y_SIZE; y++) {
world[x][y][0] = world[x][y][1];
}
}
}
}

int neighbours(int x, int y) {
return world[(x + 1) % X_SIZE][y][0] +
world[x][(y + 1) % Y_SIZE][0] +
world[(x + X_SIZE - 1) % X_SIZE][y][0] +
world[x][(y + Y_SIZE - 1) % Y_SIZE][0] +
world[(x + 1) % X_SIZE][(y + 1) % Y_SIZE][0] +
world[(x + X_SIZE - 1) % X_SIZE][(y + 1) % Y_SIZE][0] +
world[(x + X_SIZE - 1) % X_SIZE][(y + Y_SIZE - 1) % Y_SIZE][0] +
world[(x + 1) % X_SIZE][(y + Y_SIZE - 1) % Y_SIZE][0];
}

void loop ()
{
setupLife();
life();
}

For a seasonal flavour (for those of us in the Northern hemisphere anyway), replace the appropriate bits of code with the following:

#define NUMFLAKES 7
byte flakes[NUMFLAKES][2];

void snowflakes() {
for (int i = 0; i < NUMFLAKES; i++) {
flakes*[0] = random(X_MAX + 1);*
flakes*[1] = random(Y_MAX + 1);
_
}_
_
while (1) {_
_
// Display flakes*_
* for (int i = 0; i < NUMFLAKES; i++) {*
plot(flakes_[0], flakes*[1]-1, 1);
plot(flakes[0]+1, flakes[1], 1);
plot(flakes[0], flakes[1]+1, 1);
plot(flakes[0]-1, flakes[1], 1);
}
delay(DISPDELAY * 2);
// Erase flakes*

* for (int i = 0; i < NUMFLAKES; i++) {
plot(flakes[0], flakes[1]-1, 0);
plot(flakes[0]+1, flakes[1], 0);
plot(flakes[0], flakes[1]+1, 0);
plot(flakes[0]-1, flakes[1], 0);
}*_

* // Move down*
* for (int i = 0; i < NUMFLAKES; i++) {*
flakes_[1] = flakes*[1] + 1;
if (random(10) > 7) {
flakes[0] = flakes[0] + (random(3) - 1);
}_

if (flakes[1] > (Y_MAX + 1)) {
_ flakes[1] = -1;_
flakes[0] = random(X_MAX + 1);
_ }
}
}
}
void loop ()
{
snowflakes();
}
[/quote]*

Andrew_

BTW, my version of life ran significantly faster if I only updated the pixels that changed... Though I'm not sure that running life faster is necessarily better (and I left the demo code in "slow" update-every-pixel mode, since I claimed that I was demonstrating the performance of doing that...)

For animation in general, you might want to investigate the "multiple write" capability of the chip (as seen in the latest code dump in ht1632_clear())

/*
 * ht1632_clear
 * clear the display, and the shadow memory, and the snapshot
 * memory.  This uses the "write multiple words" capability of
 * the chipset by writing all 96 words of memory without raising
 * the chipselect signal.
 */
void ht1632_clear()
{
  char i;

  ht1632_chipselect(ht1632_cs);  // Select chip
  ht1632_writebits(HT1632_ID_WR, 1<<2);  // send ID: WRITE to RAM
  ht1632_writebits(0, 1<<6); // Send address
  for (i = 0; i < 96/2; i++) // Clear entire display
    ht1632_writebits(0, 1<<7); // send 8 bits of data
  ht1632_chipfree(ht1632_cs); // done
  for (i=0; i < 96; i++)
    ht1632_shadowram[i] = 0;
}

(my; this worked out pretty nicely. I got a display and a pleasant diversion, and the code works and seems to be usable, and assorted others have taken off and done additional neat things with it!)

Wow. I mean, WOW. You are all geniuses. I have been beating my brains out trying to figure this thing out. The board expands out to 4 and I wanted something I could tinker with and build on because the price is outstanding. And to learn to use the Arduino to command it is priceless.

I'm still not clear on the connections between the Arduino and the board. Is there anyway a quickie schematic can be drawn up? That would be much appreciated.

I want to especially thank webobject, westfw and Andrew for sharing your code.

I read the source and your notes and was able to successfully connect the arduino to the display board. Thank you, again!

Andrew, I am curious about your comments regarding the "multiple write" capabilities of the ht1632. Could you elaborate on that? Would that mean that you can pre-load the graphics/text data before commanding the board to begin display?

What about text? Do I need to design my own font or does the chip have one on board?

The complete code is getting a bit big to post here, but you should be able to get it from Yahoo | Mail, Weather, Search, Politics, News, Finance, Sports & Videos
This version has some 5x7 text capability, with a font for A-Z and 0-9 (the chip does NOT have any internal character generator.)

I'm trying to add some text and explanations to get the whole thing into a more "article-like" publication. Assuming I get it finished, I'll put a note here and eventually try getting it up on the playgroup as well. (it is, however, somewhat painful to have working code and sketches and be faced with writing mere text. Sigh.)

The multiple-write thing is a speedup. Normally you send a "write data" command, followed by 7 bits of address, followed by 4 bits of data, and then you start a new command. Most of the library code I wrote does that, so the display is always updated 4bits at a time, taking a good 16-odd "pin wiggles" for each pixel you change. In the demo program, it seems like that is "fast enough" for most of the things I tried, so I didn't do a lot of additional work. However, if you send an additional 4 bits of data after the first set, the ht1632 will increment the address and put those in the next word, and so on, for as many words of data as you provide. That means that you COULD update the entire display in about 12 + (964) ~= 400 wiggles instead of 38416 ~= 6000 wiggles; a rather impressive improvement...

Could you please tell us what is the licence of the code you have posted here?
I'd like to use it on my personal pinball machine project. For that purpose, I intend to host modified versions of this code at Google Code.

this is the Google Code page for my Pinball Fantasies / Party Land Remake project: Google Code Archive - Long-term storage for Google Code Project Hosting.

also there is a video here: pinball fantasies remake prototype - YouTube
and some photos here: Felipe Sanches | Flickr

Oh! I just noticed the licensing statement on the header!

In my project, I intend to concatenate a series of displays so that I can have a 168x16 dot matrix display. I have some ideas on how to proceed, in order to do that. I will host this code at google code and then I can give you write access to the svn if you wish to contribute there.

give me your username at google and I will add you as a contributor, westfw.

then you can follow these instructions:
http://code.google.com/p/how-to-build-a-pinball/source/checkout