Sure 1632 LED Display - Conway's Game of Life - ATTiny 84?

Hello.
I hope you guys can help me out with my latest project. I have the code pretty much done (alot of which isn’t my own) except there is one crippling glitch. I’ve uploaded a video for you guys to see what I’m on about. My project uses a plot() function to turn on a particular LED and I have a clear() function that shuts off all the LEDs at once. I’ve put a bit of test code in the main loop to illustrate the problem. It simply lights up the first two columns one LED at a time. The first iteration works fine, but after the first clear the LEDs turn on in sets of 4 rather than one by one. The CGOL code is commented out.

http://s551.photobucket.com/user/Xplorer822/media/VID_20140412_221407_zps328a79f0.mp4.html

Hopefully one of you with more experience with the Sure display can advise me on whats going on, or point me in the direction of a working library.
Any help is appreciated. Thanks

/* Code written by ... in August 2011 */
/* Simple communication with the LED matrix 32x16 of Sure Electronics */


#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-0xxx-x Slave Mode */
#define HT1632_CMD_MSTMD  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 */

#define X_MAX 31 // 0 based X
#define Y_MAX 15 // 0 based Y
#define CHIP_MAX 4 // Number of HT1632C Chips


#define H 32 //Define height
#define W 16 //Define width


// * Set these constants to the values of the pins connected to the SureElectronics Module
static const byte data = 6;    // Data pin (pin 7)
static const byte clk = 9;     // Data pin (pin 2)
static const byte wrclk = 7;   // Write clock pin (pin 5)
static const byte cs = 8;      // Chip Select (pin 1)

// The should also be a common GND (pins .
// The module with all LEDs like draws about 200mA,
//  which makes it PROBABLY powerable via Arduino +5V

const int bits_l = 64;
const int numChip = 4;

byte pattern[bits_l][numChip];
byte address=0;


const int color = 1;   // 0 = Green
                       // 1 = Red
//====================================================

void setup()
{
  //digitalWrite(cs, HIGH);
  
  
  pinMode(cs, OUTPUT);
  pinMode(wrclk, OUTPUT);
  pinMode(data, OUTPUT);
  pinMode(clk, OUTPUT);
  
  
  for (int i=1; i<=CHIP_MAX; i++) {
    sendcmd(i, HT1632_CMD_SYSDIS); // Disable system
    sendcmd(i, HT1632_CMD_COMS00); // 16*32, PMOS drivers
    sendcmd(i, HT1632_CMD_MSTMD);  // MASTER MODE - Use on-chip clock
    sendcmd(i, HT1632_CMD_SYSON);  // System on - Enable system oscil
    sendcmd(i, HT1632_CMD_LEDON);  // LEDs on 
  }
  
  
  Clear(); // Clear the display
}


void loop ()
{ 
  
  
//  
//    randomSeed(analogRead(0));             //CGOL algorithm
//    bool now[H][W], next[H][W];
//    populateMatrix(now);
//    clearMatrix(next);
//
//    while (1) 
//    {
//    
//      clearMatrix(next);
//      calculate(now, next);
//      swap(now, next);
//      printMatrix(now);
//      delay(100);
//      Clear();
//    }
    
  
  
  int x,y;                                  // test code
  for(x=0; x<2; x++){
    for(y=0; y<16; y++){
      Plot(x,y,1);
      delay(100);
    }
  }
  Clear();

}

//=====================================================

void Plot (int X, int Y, int color)
{
 // color:
 // 0 -> green
 // 1 -> red

 // check the inputs value
 // in case of error return nothing
 if(color > 1 || color < 0)
    return;
 if(X<0 || X>31 || Y<0 || Y>15)  
    return;      

  // variables declaration
  int x = 0;
  int y = 0;
  int nChip = 0;
   
  

  // transform the X/Y coordinates to the number of the microchip
  // that controls the region (1,2,3 or 4) the LED you want to
  // light up
  nChip = 1 + X/16 + (Y>7?2:0);
  chipselect(nChip); // call the function chipselect to send the
				 // information to the matrix
  
  // after the selection of the chip we need just the coordinate
  // for 1 of them, so we need to change the coordinate system
  x = X%16; // columns
  y = Y%8;  // rows
  
  

  // from X/Y to address
  address = ((x*2+y/4)) + color*32;

  // from Y to pattern
  pattern[address][nChip] = pattern[address][nChip] | ((1<<(3-y%4)));      
  
  
  writebits(HT1632_ID_WR, 2);  		   // send ID: WRITE to RAM
  writebits(address, 6);       		   // Send address
  writebits(pattern[address][nChip], 3);   // send 4 bits of data
  
}


//**************************************************************************************************
//Function Name: chipselect
//Function Feature: choose the chipset in which work
//Input Argument: number of the chip
//Output Argument: void
//**************************************************************************************************
void chipselect(int chip){
  
  if(chip<0){
    digitalWrite(cs, LOW); 
    for(int tmp=0; tmp<CHIP_MAX; tmp++) {
      digitalWrite(clk, HIGH);
      digitalWrite(clk, LOW);
    }
  }
  else{

    digitalWrite(cs, HIGH);
    for(int tmp=0; tmp<CHIP_MAX; tmp++) {
      digitalWrite(clk, HIGH);
      digitalWrite(clk, LOW);
    }

    if(chip>0){
      digitalWrite(cs, LOW);
      digitalWrite(clk, HIGH);
      digitalWrite(clk, LOW);
      digitalWrite(cs, HIGH);
      for(int tmp=1 ; tmp<chip; tmp++) {
        digitalWrite(clk, HIGH);
        digitalWrite(clk, LOW);
      }
    }
  }
    
}

//**************************************************************************************************
//Function Name: clear
//Function Feature: clear display
//Input Argument: void
//Output Argument: void
//**************************************************************************************************
void Clear()
{
  char i;

  for (int j=1;j<=CHIP_MAX;j++) {
    chipselect(j);
    writebits(HT1632_ID_WR, 2);  // send ID: WRITE to RAM
    writebits(0, 6);             // Send address
    for (i=0; i<64/2; i++)       // Clear entire display
      writebits(0, 7);           // send 8 bits of data
    chipselect(0);

  }
}

//**************************************************************************************************
//Function Name: writebits
//Function Feature: Write bits (up to 8) to h1632 on pins data, wrclk
//                  Chip is assumed to already be chip-selected
//                  Bits are shifted out from MSB to LSB
//Input Argument: bits: bits to send
//	        length: length of the bits to send
//Output Argument: void
//**************************************************************************************************
void writebits (byte bits, int length)
{
  for (int i=length; i>=0; i--) {
    digitalWrite(wrclk, LOW);
    if (bits & 1<<i) {
      digitalWrite(data, HIGH);
    }
    else {
	digitalWrite(data, LOW);
    }
    digitalWrite(wrclk, HIGH);
  }
}

//**************************************************************************************************
//Function Name: sendcmd
//Function Feature: Send a command to the ht1632 chip.
//                  Select 1 0 0 c7 c6 c5 c4 c3 c2 c1 c0 xx Free
//Input Argument: chipNo: the chip you want to send data
//               command: consists of a 3-bit "CMD" ID, an 8bit command, and
//                        one "don't care bit".
//Output Argument: void
//**************************************************************************************************
static void sendcmd (byte chipNo, byte command)
{
  chipselect(chipNo);
  writebits(HT1632_ID_CMD, 2);  // send 3 bits of id: COMMMAND
  writebits(command, 7);        // send the actual command
  writebits(0, 1); 	        /* one extra dont-care bit in commands. */
  chipselect(0);
}

Hi racerx89,

Sorry, I don’t know anything about this type of display, I’m just interested in Game of life projects in general. It is a strange effect and I wonder if the ht1632 chip is buffering updates and its nothing to do with your code. Why the first loop is not affected, I’ve no idea.

When you do get it working, if you find the GOL generation speed is not as fast as you would like, have a look at some of my posts, I have a much faster algorithm you could use.

Paul

Thanks for the tip Paul. All is well now, I found a MUCH faster library to drive the board here:

The refresh rate is really fast. Here is the latest version of my GOL code. I think there is certainly room for improvement, but it seems to work great. Here it is if anyone is interested.

//Conway's Game of Life
// Required Library:  https://github.com/twisterss/ArduinoLEDMatrixPong

//=============================================================================================================================================================================================

#include "led_matrix.h"

#define H 33 //Define height      (It's 33 to account for a weird bug)
#define W 16 //Define width

static LEDMatrix* matrix;
static const byte DATA_PIN = 6;    // Data pin (pin 7)
static const byte CLK_PIN = 9;     // Data pin (pin 2)
static const byte WRCLK_PIN = 7;   // Write clock pin (pin 5)
static const byte CS_PIN = 8;      // Chip Select (pin 1)
//=============================================================================================================================================================================================

void setup()
{
  matrix = new LEDMatrix(DATA_PIN, CLK_PIN, WRCLK_PIN, CS_PIN);
}

//=============================================================================================================================================================================================

void loop ()
{ 
    randomSeed(analogRead(0));           
    bool now[H][W], next[H][W], previous[H][W];
    int flag = 1;
    populateMatrix(now);
    clearMatrix(next);
    clearMatrix(previous);
    while (flag) 
    {
      copyMatrix(previous, now);
      calculate(now, next);
      copyMatrix(now, next);
      printMatrix(now);
      matrix->render();
      calculate(now, next);
      flag = compareMatrix(previous, next);
      delay(50);
      matrix->clear();
    }
}

//=============================================================================================================================================================================================

void populateMatrix (bool mat[][W])
{
    for (int m = 0; m < H; m++)
    {
        for (int n = 0; n < W; n++)
            mat[m][n] = random(2);
    }
}

//=============================================================================================================================================================================================

int compareMatrix(bool mata[][W], bool matb[][W]) 
{
     int x = 0;
     for (int m = 0; m < H; m++)
     {
         for (int n = 0; n < W; n++)
         {
             if (mata[m][n] == matb[m][n])
                 x++;
         }
     }
     if (x >= H*W-1) // if there are no differences between matrices  
       return 0;
     else 
       return 1;
}

//=============================================================================================================================================================================================

void clearMatrix(bool mat[][W]) //Sets matrix to all dead
{
    for (int m = 0; m < H; m++)
    {
        for (int n = 0; n < W; n++)
            mat[m][n] = 0;
    }
}

//=============================================================================================================================================================================================

void copyMatrix(bool mata[][W], bool matb[][W]) //Replaces first matrix with second
{
     for (int m = 0; m < H; m++)
     {
         for (int n = 0; n < W; n++)
             mata[m][n] = matb[m][n];
     }
}


//=============================================================================================================================================================================================

void printMatrix(bool mat[][W]) //Prints matrix to screen
{
    for (int m = 0; m < H; m++)
    {
        for (int n = 0; n < W; n++)
        {
            if (mat[m][n] == 1)
               matrix->plot(m-1,n,GREEN);
        }
          
    }
}


//=============================================================================================================================================================================================

void calculate(bool mata[][W], bool matb[][W])
{
     unsigned int neighbors;
     for (int m = 0; m < H; m++)
     {
         for (int n = 0; n < W; n++)
         {
             neighbors = 0;
             //Begin counting number of neighbors:
             if (mata[m-1][n-1] == 1) neighbors += 1;
             if (mata[m-1][n] == 1) neighbors += 1;
             if (mata[m-1][n+1] == 1) neighbors += 1;
             if (mata[m][n-1] == 1) neighbors += 1;
             if (mata[m][n+1] == 1) neighbors += 1;
             if (mata[m+1][n-1] == 1) neighbors += 1;
             if (mata[m+1][n] == 1) neighbors += 1;
             if (mata[m+1][n+1] == 1) neighbors += 1;
             
             //Apply rules to the cell:
             if (mata[m][n] == 1 && neighbors < 2)
                matb[m][n] = 0;
             else if (mata[m][n] == 1 && neighbors > 3)
                matb[m][n] = 0;
             else if (mata[m][n] == 1 && (neighbors == 2 || neighbors == 3))
                matb[m][n] = 1;
             else if (mata[m][n] == 0 && neighbors == 3)
                matb[m][n] = 1;
         }
     }
}

So the strange update problem just went away with the new library?

What sort of refresh rate are you getting? On a grid of that size, it should not be too hard to get a decent speed.

In my projects I have update rates of 15 per second on a 128x96 grid, and that's using the TVout library, which uses a large percentage of the ATmega's processing power. On a 128x64 GLCD, I get at least 20 generations per second which includes updating the display.

Paul

Yes the issue was with the original code. Not sure what it was, but this library I'm using now works great. I'm not sure what the refresh rate is. I'd guess it's in the teens. I limit the speed with a delay anyway. Otherwise some patters like the gliders become difficult to recognize. I'll check out the TVout library. Can that be used with this display?

RacerX89:
I'll check out the TVout library. Can that be used with this display?

No, no. TVout library is used to output a composite video signal. No use for your led matrix.

Paul

Gotcha, thanks.

Here is a video of the GOL running at full speed, no delays.
http://s551.photobucket.com/user/Xplorer822/media/VID_20140413_213419_zpsddd95120.mp4.html

I have a new issue now, however. I'm trying to run the display off a ATTiny 84. I've got the program to load onto the chip and triple checked all the connections, and no dice. The display is dead. Nothing. Can the 84 (8MHz) run this code?

Theoretically, yes it should.

Have you tried the standard "blink a single led" script? If so, lets see your schematic and the updated sketch.

The ATtiny will have less memory (ram). Have you worked out how much your sketch needs (roughly)?

Shematic is simply the 84 Chip with 5V and GND and the display connections.
These are the only changes to the code:

//static const byte DATA_PIN = 6;    // Data pin (pin 7)~           // UNO
//static const byte CLK_PIN = 9;     // Data pin (pin 2)~
//static const byte WRCLK_PIN = 7;   // Write clock pin (pin 5)
//static const byte CS_PIN = 8;      // Chip Select (pin 1)

static const byte DATA_PIN = 8;    // Data pin (pin 7)           // ATTiny 84
static const byte CLK_PIN = 7;     // Data pin (pin 2)
static const byte WRCLK_PIN = 10;   // Write clock pin (pin 5)
static const byte CS_PIN = 9;      // Chip Select (pin 1)

I also pulled the 328 out of the Uno and stuck that into a breadboard with power and GND and tried to run the display straight off the chip and again I got nothing. Size compiling on the UNO is about 4.6kb.

Hi RacerX89, I meant have you got the standard "Blink" example sketch to run on the '84? Just to prove you are correctly uploading the sketch and it runs OK. Try it on the '328 on the breadboard also.

I am still concerned about memory on the '84. There is only 0.5K RAM compared to 2K on the '328.

Looking at this line:

bool now[H][W], next[H][W], previous[H][W];

If the compiler allocates one byte for each bool, just one of those arrays would use all available RAM, and you have three, plus your other variables, plus whatever that library uses (it almost certainly have a bitmapped buffer of the display, which could be 128 bytes).

Even if the compiler packs your bools 8 to the byte, which I doubt, its cutting it very fine!

I think it may still be possible to do this with the '84, but it will require some major code changes. You may have to change your GOL algorithm to pack 8 cells to the byte, avoid keeping data for multiple generations of the matrix, and possibly either use the library's buffer memory directly or abandon the library and drive the display directly.

With my TVout Game of Life, at 128x96, a single copy of the life matrix takes 1.5K. The TVout library also has a 1.5K buffer, so with only 2K available, I had to make the memory shared between the library and my sketch.

Paul

Thank you for the advice Paul. Yes the BLINK sketch will load onto the 84 no problem. I've used the ATTiny 84 and 85 on many other projects.

This is weird now, at first it seems the GOL sketch loaded on the 84 just fine. But now I'm getting this error when trying to compile the GOL sketch with the board selected to ATTiny 84 8MHz.

/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/../lib/gcc/avr/4.3.2/../../../../avr/lib/avr25/crttn84.o:(.init9+0x2): relocation truncated to fit: R_AVR_13_PCREL against symbol `exit' defined in .fini9 section in /Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/../lib/gcc/avr/4.3.2/avr25/libgcc.a(_exit.o)

I've ran into this before and I solved it using this solution.
http://forum.arduino.cc/index.php/topic,51984.msg371307.html#msg371307

But I'm on my Mac now, and ^that fix only woks on Windows. I'll try it out on my PC later but since this sketch overflows the RAM I'll just use a 328 chip. I'm saving multiple generations of the matrix for steady state detection. I'm sure they're is a more efficient way of doing it, but I'm really not all that proficient in programming. If I want to program another 328 chip I can just plug it into the socket on the UNO and program it normally, right? I don't have to use the UNO as a ISP programmer to then program another 328 chip? I'll try and trouble shoot it some more when I have time.

Thanks for all your help. I'll report back with any updates.

If you buy a replacement '328 with uno bootloader already programmed in, you can just put it straight in the uno. If you buy a blank '328, you would need to but the old '328 back in the uno, load arduinoIsp and use that to flash the bootloader into the new black chip.

Okay, that makes sense. Which board do I select when loading the bootloader via the UNO as ISP? Do I select UNO?

Yes, I think so. Better double check that.