Arduino to 384 LED display module (MCU Interface)

http://www.sureelectronics.net/goods.php?id=142

Hi, I'd be very grateful if anybody could give me any clues how to
drive this board from my Arduino, I'm new to Arduino (had mine a few days) So far I've buit a 10 LED metronome with it (Easy stuff!), I saw lots of LED matrixes on youtube and thought I'd like to try. I came across the LED board above on E-Bay and since it only cost me £7 (Inc shipping) fron China I thought I'd get one. That's where my trouble begins! :slight_smile: The auction description said it uses an 'SPI - Like'
controller (which i would like to learn about), turns out it uses an MCU Interface (never heard of!)... ANY info at all on wiring/sending commands would be greatly appreciated!

Link to this board's PDF Manual (745k):
http://www.sureelectronics.net/pdfs/DE-DP016.pdf

Thank you very much for looking :slight_smile:

Some of the blurb:

2416 Dot Matrix Red Display Information Boards is manufactured by Sure Electronics. It can be driven by SPI like interface. It can be easy interfaced to any microcontrollers. It can be widely used in panel meter, big clocks and any other information display usage.
It's a memory mapping LED display board and driven by HT1632 LED display controller. The device supports 16-gradation LEDs for each outline using PWM control with software instructions. A serial interface is conveniently provided for the command mode and data mode. Only three or four signals are required for the interface between the host controller and the information board. The display can be extended by cascading the information board for wider applications.
Main Feature

  • Size: 4inch×2.5inch display area.
  • 2416 dot matrix on each board.
  • LED emitting color is Red.
  • Operating voltage and current: 5V, 350mA (Max.), 200mA(Avg.)
  • 16-level PWM brightness control.
  • Each board contains 6 pieces of 0808 LED dot matrix boards.
  • Serial MCU interface----CS, RD*(optional), WR, DATA.
  • Cascading function for extended applications (up to 4 boards possible).
  • Power can be supplied and command data can be transmitted by IDC sockets from microcontrollers.
  • Auxiliary power supply terminal blocks may be needed when connected to next LED matrix board.

Looks pretty neat; The driver chip used (HT1632) seems to have a lot more information at Hotek (the manufacturer) site, including some app notes that should help.
http://www.holtek.com/English/docum/consumer/1632.htm

It's not a great display; no driver transistors for each column, so there may be brightness or uniformity issues driving more than one led at a time. OTOH, it looks like you have full control over which pins do what...

Thanks westfw, much appreciated. Having read alot of the info on this board I've concluded it's beyond my current ability to use. I seem to be bound up in acronyms and their relation to each other! I can see why a lot of people would find electronics in general to be impenetrable or some kind of 'black art' (Ironically, thats why i wanted to learn :))
O.K. A dose of humility and onto my next plea! :-[
If anyone could help me atleast power this thing and make it show sign's
of life I'd be grateful. Simply applying power does nothing, there's no sign it's even 'ready' e.t.c.

Thank you :slight_smile:

Well, it's more like an LCD display than a simple set of registers connected to LEDs. You can probably get by with a write-only setup; you write either commands that control the configuration of the display, or data that turns on and off particular LEDs. To get anything to happen at all, it looks like you have to send several configuration commands first. shiftout() probably works, but it has enough variables that I wouldn't like to try to debug it without a device in front of me...

(want to have a second display shipped to me? "Will develop open source HW/SW in exchange for toys." :slight_smile: )

There is C code for talking to the display, that accompanies their App Note HA0127E, that looks like it could be converted to arduino code without TOO much difficulty (mostly replacing direct port manipulation with digitalWrite() and similar?) Not particularly a beginner project, though; and still tough to debug without hardware.

http://www.holtek.com/english/tech/appnote/uc/pdf/ha0127e.pdf (app note. Sorta sucky.)
http://www.holtek.com/english/tech/appnote/uc/asm_zip/ha0127.zip (code, including C code.)

Hi westfw,
Pm me your address please and I'll get a board shipped out to you within the next seven days.

Just wondering if anything came of this. I ordered a board, too, and am looking for others who have tried to interface to it via the arduino. Any help appreciated.

Hi wncranger,

In exchange for figuring it out, i had a board sent to westfw. He should get to it in due course and I'll share the results with you when i hears something back :slight_smile:

Huh. This went rather more quickly than I thought it would. Practically worked the first time, even.

Here's a demo program that "does something." I expect to write a more "generally useful" library this coming holiday weekend, but this is a good start...

This is the sketch and include file. Comments to follow in a separate message.

Code demo16x24.pde:

/*
 * 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
 */

#include "ht1632.h"

/*
 * 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
/*
 * 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(1000);
}

void loop ()
{
  byte bits;
  byte addr;
  for (addr=0; addr < 96; addr++) {        // Shift in ON bits
    for (bits=8; ; bits=(bits>>1)+8) {
      ht1632_senddata(addr, bits);
      delay(30);
      if (bits == 15) break;
    }
  }
  delay(1000);
  for (addr=0; addr < 96; addr++) {      // Now shift in OFF bits
    for (bits=15; ; bits=(bits>>1)) {
      ht1632_senddata(addr, bits);
      delay(10);
      if (bits == 0) break;
    }
  }
  delay(1000);
}

Code ht1632.h:

/*
 * ht1632.h
 * defintions for Holtek ht1632 LED driver.
 */


#if !defined(DEBUGPRINT)
#define DEBUGPRINT(fmt, args...)
#endif

/*
 * commands written to the chip consist of a 3 bit "ID", followed by
 * either 9 bits of "Command code" or 7 bits of address + 4 bits of data.
 */
#define HT1632_ID_CMD 4            /* ID = 100 - Commands */
#define HT1632_ID_RD  6            /* ID = 110 - Read RAM */
#define HT1632_ID_WR  5            /* ID = 101 - Write RAM */

#define HT1632_CMD_SYSDIS 0x00      /* CMD= 0000-0000-x Turn off oscil */
#define HT1632_CMD_SYSON  0x01      /* CMD= 0000-0001-x Enable system oscil */
#define HT1632_CMD_LEDOFF 0x02      /* CMD= 0000-0010-x LED duty cycle gen off */
#define HT1632_CMD_LEDON  0x03      /* CMD= 0000-0011-x LEDs ON */
#define HT1632_CMD_BLOFF  0x08      /* CMD= 0000-1000-x Blink ON */
#define HT1632_CMD_BLON   0x09      /* CMD= 0000-1001-x Blink Off */
#define HT1632_CMD_SLVMD  0x10      /* CMD= 0001-00xx-x Slave Mode */
#define HT1632_CMD_MSTMD  0x14      /* CMD= 0001-01xx-x Master Mode */
#define HT1632_CMD_RCCLK  0x18      /* CMD= 0001-10xx-x Use on-chip clock */
#define HT1632_CMD_EXTCLK 0x1C      /* CMD= 0001-11xx-x Use external clock */
#define HT1632_CMD_COMS00 0x20      /* CMD= 0010-ABxx-x commons options */
#define HT1632_CMD_COMS01 0x24      /* CMD= 0010-ABxx-x commons options */
#define HT1632_CMD_COMS10 0x28      /* CMD= 0010-ABxx-x commons options */
#define HT1632_CMD_COMS11 0x2C      /* CMD= 0010-ABxx-x commons options */
#define HT1632_CMD_PWM    0xA0      /* CMD= 101x-PPPP-x PWM duty cycle */

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();
}