Introduction: BI-COLORS MATRIX 32x32 SHOWING 256 COLORS WITH GIF PICTURES

About: PLC, Arduino - Do it yourself project

Today, I'd share with you how “32x32 Bicolor Led Matrix” can display 256 separated colors by using 74HC595 for column scan and 74HC238 for row scan. Of course, if the human eye can recognize this color difference. The flickers you see on videos is due to CAMERA, human eyes can't see these flickers.

Bicolor matrix 32x32 has totally 1024 RED leds, 1024 GREEN leds. And with B.A.M - 4 bit (Bit Angle Modulation), every combination (RED + GREEN) led can generated 256 separated colors. How can Arduino handle these jobs with cheap and easy-to-find ICs on the market ?

In addition, this project also share how to display animation pictures (GIF pictures) by simple manual way.

Step 1: BILL OF MATERIALS

Besides, I have to buy 3 copper clad boards, single sided and do PCB etching by myself.

Step 2: Bi-Colors Led Matrix 8x8 - Overview

With order code 2088ABEG-5, led matrix 8x8 is common anode, dimension 60x60mm.

Step 3: Make a Simple LED Tester

Check the topic: https://www.instructables.com/id/Make-a-simple-LED...

Check whether Led Matrix 8x8 is common anode or common cathode, and identify all red and green pins.

Step 4: Principle Diagram

In simulation schematic, we don’t see (2N3904/2N2222 + TIP42C) connected after 74HC238, as well as, (ULN2803 + R100) connected after 74HC595. See next step for more detail.

Step 5: Actual Schematic - Row Scanning

Totally, for 32 rows scanning, we just need:

  • 02 x 74HC238.
  • 16 x 2N3904/2N2222.
  • 16 x TIP42C.

Why do we use 74HC238 instead of 74HC138?

Because in my design, I need to connect a decoder with active-high outputs to transistor 2N3904 (NPN).

Note: 74HC138 (active-low outputs) & 74HC238 (active-high outputs) have totally the same pinouts.

Row scanning program:

void rowScan(byte row)
{
if (row & 0x01) PORTD |= 1<<RowA_Pin
else PORTD &= ~(1<<RowA_Pin);
if (row & 0x02) PORTD |= 1<<RowB_Pin;
else PORTD &= ~(1<<RowB_Pin);
if (row & 0x04) PORTD |= 1<<RowC_Pin;
else PORTD &= ~(1<<RowC_Pin); 
if (row & 0x08) PORTD |= 1<<RowD_Pin;
else PORTD &= ~(1<<RowD_Pin);
}

With multiplexer pins setup on PORTD of Arduino Uno:

#define RowA_Pin 4
#define RowB_Pin 5
#define RowC_Pin 6
#define RowD_Pin 7

Step 6: Actual Schematic - Column Scanning

For column scanning, totally components using:

  • 16 x 74HC595.
  • 16 x ULN2803.
  • 128 x R100.

So, it means we need 2 boards as above, one for RED led & one for GREEN led.

Program with row and column scanning:

ISR(TIMER1_COMPA_vect)
{
PORTD |= ((1<<blank_pin));
if (BAM_Counter == 8)
BAM_Bit++;
else
if (BAM_Counter == 24)
BAM_Bit++;
else
if (BAM_Counter == 56)
BAM_Bit++;
BAM_Counter++;
switch (BAM_Bit)
{
case 0:
//Red
	myTransfer(red[0][level + 0]);
        myTransfer(red[0][level + 1]);
        myTransfer(red[0][level + 2]);
        myTransfer(red[0][level + 3]);
        myTransfer(red[0][level + 64]);
        myTransfer(red[0][level + 65]);
        myTransfer(red[0][level + 66]);
        myTransfer(red[0][level + 67]);
	//Green
        myTransfer(green[0][level + 0]);
        myTransfer(green[0][level + 1]);
        myTransfer(green[0][level + 2]);
        myTransfer(green[0][level + 3]);
        myTransfer(green[0][level + 64]);
        myTransfer(green[0][level + 65]);
        myTransfer(green[0][level + 66]);
        myTransfer(green[0][level + 67]);   
	break;
    case 1:     
      //Red
	myTransfer(red[1][level + 0]);
        myTransfer(red[1][level + 1]);
        myTransfer(red[1][level + 2]);
        myTransfer(red[1][level + 3]);
	myTransfer(red[1][level + 64]);
        myTransfer(red[1][level + 65]);
        myTransfer(red[1][level + 66]);
        myTransfer(red[1][level + 67]);             
      //Green
	myTransfer(green[1][level + 0]);
        myTransfer(green[1][level + 1]);
        myTransfer(green[1][level + 2]);
        myTransfer(green[1][level + 3]);     
        myTransfer(green[1][level + 64]);
        myTransfer(green[1][level + 65]);
        myTransfer(green[1][level + 66]);
        myTransfer(green[1][level + 67]);
      break;
    case 2:
      //Red
	myTransfer(red[2][level + 0]);
        myTransfer(red[2][level + 1]);
        myTransfer(red[2][level + 2]);
        myTransfer(red[2][level + 3]);        
        myTransfer(red[2][level + 64]);
        myTransfer(red[2][level + 65]);
        myTransfer(red[2][level + 66]);
        myTransfer(red[2][level + 67]);
       //Green
	myTransfer(green[2][level + 0]);
        myTransfer(green[2][level + 1]);
        myTransfer(green[2][level + 2]);
        myTransfer(green[2][level + 3]);
        myTransfer(green[2][level + 64]);
        myTransfer(green[2][level + 65]);
        myTransfer(green[2][level + 66]);
        myTransfer(green[2][level + 67]);
	break;
    case 3:
      //Red
	myTransfer(red[3][level + 0]);
        myTransfer(red[3][level + 1]);
        myTransfer(red[3][level + 2]);
        myTransfer(red[3][level + 3]); 
        myTransfer(red[3][level + 64]);
        myTransfer(red[3][level + 65]);
        myTransfer(red[3][level + 66]);
        myTransfer(red[3][level + 67]);
      //Green
	myTransfer(green[3][level + 0]);
        myTransfer(green[3][level + 1]);
        myTransfer(green[3][level + 2]);
        myTransfer(green[3][level + 3]);
        myTransfer(green[3][level + 64]);
        myTransfer(green[3][level + 65]);
        myTransfer(green[3][level + 66]);
        myTransfer(green[3][level + 67]);

if(BAM_Counter == 120)
{
  BAM_Counter=0;
  BAM_Bit=0;
  }
  break;
}
rowScan(row);
PORTD |= 1<<latch_pin;
PORTD &= ~(1<<<latch_pin);
delaymicroseconds(5);
PORTD &= ~(1<<blank_pin); 
delayMicroseconds(5); 
level = row*4;
if(row==16)
row=0;
if(level==64)
level=0;
pinMode(blank_pin, OUTPUT);
}

Step 7: Actual Schematic – Led Matrix Display 32x32

With matrix 32x32, it includes 16 x Bi-colors Led Matrix 8x8. Picture above is PCB design for Led display.

Step 8: Connection Diagram

1. For ROWS

  • Rows 0 ~ 7 and rows 16 ~ 23 are connected to TIP42C_0 – TIP42C_7.
  • Rows 8 ~ 15 and rows 24 ~ 31 are connected to TIP42C_8 – TIP42C_15.

2. For COLUMNS:

  • RED Led:
    • Led Matrix #0 & #4 are connected to 74HC595_RED_0.
    • …….
    • Led Matrix #11 & #15 are connected to 74HC595_RED _7.
  • GREEN Led:
    • Led Matrix #0 & #4 are connected to 74HC595_GREEN_0.
    • …….
    • Led Matrix #11 & #15 are connected to 74HC595_GREEN _7.

Step 9: Main Program

Main program for 32x32 Bicolor Led Matrix using B.A.M - 4 bit method

//************************************************************************************************************//
//********************* THE 32x32 BICOLOR RG LED MATRIX USING BIT ANGLE MODULATION METHOD ********************//
//************************************************************************************************************//
#include
#include "bitmap.h"
#define blank_pin 3 // Defines actual BIT of PortD for blank - is Arduino UNO pin 3, MEGA pin 5
#define latch_pin 2 // Defines actual BIT of PortD for latch - is Arduino UNO pin 2, MEGA pin 4
#define clock_pin 13 // used by SPI, must be 13 SCK 13 on Arduino UNO, 52 on MEGA
#define data_pin 11 // used by SPI, must be pin MOSI 11 on Arduino UNO, 51 on MEGA
#define RowA_Pin 4
#define RowB_Pin 5
#define RowC_Pin 6
#define RowD_Pin 7
byte red[4][128];
byte green[4][128];
int level=0;//keeps track of which level we are shifting data to
int row=0;
int BAM_Bit, BAM_Counter=0; // Bit Angle Modulation variables to keep track of things
/** An RG color template */
struct Color
{
unsigned char red, green;
Color(int r, int g) : red(r), green(g) {}
Color() : red(0), green(0) {}
};
const Color redcolor = Color(0x0F,0x00);
const Color orangecolor = Color(0x0F,0x0F);
const Color yellowcolor = Color(0x0F,0x09);
const Color greencolor = Color(0x00,0x0F);
const Color clearcolor = Color(0x00,0x00);
#define RED 0x0F,0x00
#define ORANGE 0x0F,0x04
#define YELLOW 0x0F,0x09
#define GREEN 0x00,0x0F
#define CLEAR 0x00,0x00
void setup()
{
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0);
SPI.setClockDivider(SPI_CLOCK_DIV2);
noInterrupts();
TCCR1A = B00000000;
TCCR1B = B00001011;
TIMSK1 = B00000010;
OCR1A = 20;
pinMode(latch_pin, OUTPUT);
pinMode(blank_pin, OUTPUT);
pinMode(data_pin, OUTPUT);
pinMode(clock_pin, OUTPUT);
pinMode(RowA_Pin, OUTPUT);
pinMode(RowB_Pin, OUTPUT);
pinMode(RowC_Pin, OUTPUT);
pinMode(RowD_Pin, OUTPUT);
SPI.begin();
interrupts();
}
void loop()
{
for(int i=0; i<20; i++)
{
drawImage(32, 32, TIGER01, clearcolor, redcolor);
delay(80);
drawImage(32, 32, TIGER02, clearcolor, redcolor);
delay(80);
drawImage(32, 32, TIGER03, clearcolor, redcolor);
delay(80);
drawImage(32, 32, TIGER04, clearcolor, redcolor);
delay(80);
drawImage(32, 32, TIGER05, clearcolor, redcolor);
delay(80);
drawImage(32, 32, TIGER06, clearcolor, redcolor);
delay(80);
}
clearfast();
for (int x = 0; x < 32; x++)
{
for (int y = 0; y < 32; y++)
{
LED(x,y,int (x/2),int (y/2));
delay(50);
}
}
delay(5000);
clearfast();
fillTable(4,8);
delay(5000);
clearfast();
fillTable(8,8);
delay(5000);
clearfast();
fillTable(RED);
delay(5000);
clearfast();
fillTable(GREEN);
delay(5000);
clearfast();
fillTable(ORANGE);
delay(5000);
clearfast();
fillTable(YELLOW);
delay(5000);
clearfast();
}
void LED(int X, int Y, int R, int G)
{
X = constrain(X, 0, 31);
Y = constrain(Y, 0, 31);
R = constrain(R, 0, 15);
G = constrain(G, 0, 15);
int WhichByte = int(Y*4+ X/8);
int WhichBit = 7-(X%8);
for (byte BAM = 0; BAM < 4; BAM++)
{
bitWrite(red[BAM][WhichByte], WhichBit, bitRead(R, BAM));
bitWrite(green[BAM][WhichByte], WhichBit, bitRead(G, BAM));
}
}
void rowScan(byte row)
{
if (row & 0x01) PORTD |= 1<
else PORTD &= ~(1<
if (row & 0x02) PORTD |= 1<
else PORTD &= ~(1<
if (row & 0x04) PORTD |= 1<
else PORTD &= ~(1<
if (row & 0x08) PORTD |= 1<
else PORTD &= ~(1<
}
ISR(TIMER1_COMPA_vect){
PORTD |= ((1<
if(BAM_Counter==8)
BAM_Bit++;
else
if(BAM_Counter==24)
BAM_Bit++;
else
if(BAM_Counter==56)
BAM_Bit++;
BAM_Counter++;
switch (BAM_Bit)
{
case 0:
//Red
myTransfer(red[0][level + 0]);
myTransfer(red[0][level + 1]);
myTransfer(red[0][level + 2]);
myTransfer(red[0][level + 3]);
myTransfer(red[0][level + 64]);
myTransfer(red[0][level + 65]);
myTransfer(red[0][level + 66]);
myTransfer(red[0][level + 67]);
//Green
myTransfer(green[0][level + 0]);
myTransfer(green[0][level + 1]);
myTransfer(green[0][level + 2]);
myTransfer(green[0][level + 3]);
myTransfer(green[0][level + 64]);
myTransfer(green[0][level + 65]);
myTransfer(green[0][level + 66]);
myTransfer(green[0][level + 67]);
break;
case 1:
//Red
myTransfer(red[1][level + 0]);
myTransfer(red[1][level + 1]);
myTransfer(red[1][level + 2]);
myTransfer(red[1][level + 3]);
myTransfer(red[1][level + 64]);
myTransfer(red[1][level + 65]);
myTransfer(red[1][level + 66]);
myTransfer(red[1][level + 67]);
//Green
myTransfer(green[1][level + 0]);
myTransfer(green[1][level + 1]);
myTransfer(green[1][level + 2]);
myTransfer(green[1][level + 3]);
myTransfer(green[1][level + 64]);
myTransfer(green[1][level + 65]);
myTransfer(green[1][level + 66]);
myTransfer(green[1][level + 67]);
break;
case 2:
//Red
myTransfer(red[2][level + 0]);
myTransfer(red[2][level + 1]);
myTransfer(red[2][level + 2]);
myTransfer(red[2][level + 3]);
myTransfer(red[2][level + 64]);
myTransfer(red[2][level + 65]);
myTransfer(red[2][level + 66]);
myTransfer(red[2][level + 67]);
//Green
myTransfer(green[2][level + 0]);
myTransfer(green[2][level + 1]);
myTransfer(green[2][level + 2]);
myTransfer(green[2][level + 3]);
myTransfer(green[2][level + 64]);
myTransfer(green[2][level + 65]);
myTransfer(green[2][level + 66]);
myTransfer(green[2][level + 67]);
break;
case 3:
//Red
myTransfer(red[3][level + 0]);
myTransfer(red[3][level + 1]);
myTransfer(red[3][level + 2]);
myTransfer(red[3][level + 3]);
myTransfer(red[3][level + 64]);
myTransfer(red[3][level + 65]);
myTransfer(red[3][level + 66]);
myTransfer(red[3][level + 67]);
//Green
myTransfer(green[3][level + 0]);
myTransfer(green[3][level + 1]);
myTransfer(green[3][level + 2]);
myTransfer(green[3][level + 3]);
myTransfer(green[3][level + 64]);
myTransfer(green[3][level + 65]);
myTransfer(green[3][level + 66]);
myTransfer(green[3][level + 67]);
if(BAM_Counter==120){
BAM_Counter=0;
BAM_Bit=0;
}
break;
}
rowScan(row);
PORTD |= 1<
PORTD &= ~(1<
//delayMicroseconds(5);
PORTD &= ~(1<
//delayMicroseconds(5);
row++;
level = row*4;
if(row==16)
row=0;
if(level==64)
level=0;
pinMode(blank_pin, OUTPUT);
}
inline static uint8_t myTransfer(uint8_t C_data){
SPDR = C_data;
asm volatile("nop");
asm volatile("nop");
}
void clearfast ()
{
for (unsigned char j=0; j<128; j++)
{
red[0][j] = 0;
red[1][j] = 0;
red[2][j] = 0;
red[3][j] = 0;
green[0][j] = 0;
green[1][j] = 0;
green[2][j] = 0;
green[3][j] = 0;
}
}
void fillTable(byte R, byte G)
{
for (byte x=0; x<32; x++)
{
for (byte y=0; y<32; y++)
{
LED(x, y, R, G);
}
}
}
void drawImage(uint8_t width, uint8_t height, const uint8_t *image, Color For_color, Color Bk_color)
{
for (uint8_t y = 0; y < height; y++)
{
for (uint8_t x = 0; x < width; x++)
{
uint8_t WhichByte = x/8 + y*4;
uint8_t WhichBit = 7-(x % 8);
uint8_t colorImage = bitRead(pgm_read_byte(&image[WhichByte]), WhichBit) & 1;
if (colorImage)
{
LED(x, y, For_color.red, For_color.green);
}
else
{
LED(x, y, Bk_color.red, Bk_color.green);
}
}
}
}

This Hex code show GIF picture.

#include<avr/pgmspace.h>
/*-----------------------------------------------------------------------------------------------
TIGER ANIMATION
-----------------------------------------------------------------------------------------------*/
staticconstuint8_t TIGER00[]PROGMEM = {
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, 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, 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, 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, 0xFF, 0xFF, 0xFF, 0xFF,
};
staticconstuint8_t TIGER01[]PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x7E, 0x00, 0xC0, 0x00, 0x7F, 0x01, 0xE0,
0x00, 0xFF, 0x83, 0xFC, 0x00, 0xFF, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, 0xFC,
0x01, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0x00,
0x7F, 0x7F, 0xFE, 0x00, 0x3E, 0x7F, 0xFE, 0x00, 0x00, 0x7F, 0xFC, 0x00, 0x00, 0xFF, 0xFC, 0x00,
0x00, 0x7F, 0xF8, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x0F, 0xB8, 0x00,
0x00, 0x07, 0xB8, 0x00, 0x00, 0x0F, 0xD8, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x1E, 0xF8, 0x00,
0x00, 0x1C, 0x78, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
staticconstuint8_t TIGER02[]PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60,
0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x1F, 0x3B, 0xFC,
0x00, 0x3F, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xD0,
0x00, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0x00,
0x01, 0xFF, 0xFF, 0x00, 0xC3, 0xFF, 0xFF, 0x00, 0x7F, 0xFE, 0xFF, 0x80, 0x3E, 0x7E, 0x7F, 0xC0,
0x1C, 0x3E, 0x3B, 0xC0, 0x00, 0x3E, 0x38, 0xE0, 0x00, 0x3E, 0x38, 0xC0, 0x00, 0x3E, 0xF9, 0xC0,
0x00, 0x77, 0xF3, 0xC0, 0x00, 0x63, 0xF3, 0x80, 0x00, 0x63, 0xC0, 0x00, 0x00, 0x61, 0xC0, 0x00,
0x00, 0x70, 0xC0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00,
};
staticconstuint8_t TIGER03[]PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x03, 0xF0,
0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x3F, 0xFE,
0x00, 0x1C, 0xFF, 0xFC, 0x00, 0x3F, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0x80,
0x03, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, 0x00, 0x0E, 0xFF, 0xFF, 0x00, 0x1C, 0xFF, 0xFF, 0xF0,
0x18, 0xFF, 0xFF, 0xF8, 0xF8, 0xFF, 0xFF, 0xFC, 0x71, 0xFF, 0xFF, 0xFE, 0x01, 0xFC, 0xFF, 0xEE,
0x03, 0xFC, 0x01, 0xEE, 0x07, 0xDC, 0x00, 0x64, 0x07, 0x9C, 0x00, 0x60, 0x07, 0x1C, 0x00, 0xE0,
0x06, 0x1C, 0x00, 0xE0, 0x0E, 0x18, 0x00, 0xC0, 0x0C, 0x1C, 0x00, 0x00, 0x0C, 0x1C, 0x00, 0x00,
0x0C, 0x1C, 0x00, 0x00, 0x0E, 0x0F, 0x00, 0x00, 0x0E, 0x07, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00,
};
staticconstuint8_t TIGER04[]PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x39, 0xFC, 0x00, 0x00, 0x7F, 0xFE,
0x0F, 0xBF, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xFE, 0x70, 0xFF, 0xFF, 0xFC,
0x60, 0xFF, 0xFF, 0xC0, 0xC1, 0xFF, 0xFF, 0x80, 0x81, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0xF8,
0x03, 0xFF, 0xFF, 0xFC, 0x03, 0xF9, 0xFF, 0xDE, 0x0F, 0xF0, 0xFB, 0xEC, 0x1F, 0xF0, 0x01, 0xF6,
0x1F, 0xF0, 0x00, 0x7E, 0x3F, 0xE0, 0x00, 0x3E, 0xFF, 0xC0, 0x00, 0x1C, 0xFF, 0x80, 0x00, 0x00,
0xFE, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
};
staticconstuint8_t TIGER05[]PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1F, 0x80, 0x00, 0xC0,
0x3F, 0xC0, 0x01, 0xE0, 0x01, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0xFE, 0x7C, 0x00, 0x7F, 0xFF, 0xFC,
0x00, 0x7F, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xFF,
0x06, 0x7F, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xC0, 0x0F, 0xFC, 0xFF, 0x80,
0x3F, 0xF8, 0xFF, 0x80, 0x3F, 0xF0, 0x7F, 0xC0, 0x1F, 0xC0, 0x3F, 0xE0, 0x1E, 0x80, 0x07, 0xF0,
0x3C, 0x00, 0x07, 0xF8, 0x78, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x07, 0x3F, 0xE0, 0x00, 0x03, 0x8F,
0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x01, 0xC0,
0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x30,
};
staticconstuint8_t TIGER06[]PROGMEM = {
0x0E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00,
0x01, 0xE0, 0x00, 0x00, 0x00, 0xFE, 0x00, 0xC0, 0x00, 0x7F, 0x80, 0xC0, 0x00, 0x3F, 0xC0, 0xE0,
0x00, 0x3F, 0xF0, 0xF0, 0x03, 0x7F, 0xFE, 0x78, 0x03, 0x7F, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0xFC,
0x0F, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0xFF, 0x3D, 0xFF, 0xFF, 0xFF, 0x79, 0xFF, 0xFF, 0xFE,
0x3F, 0xFD, 0xFF, 0xFE, 0x07, 0x80, 0xFF, 0x80, 0x07, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0x00,
0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0xF7, 0x80, 0x00, 0x00, 0xE7, 0x80, 0x00, 0x00, 0xE3, 0xC0,
0x00, 0x00, 0xC1, 0xC0, 0x00, 0x01, 0xC0, 0xE0, 0x00, 0x01, 0xC0, 0x78, 0x00, 0x01, 0x80, 0x7C,
0x00, 0x01, 0xC0, 0x3C, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00,
};
view rawbitmap.h hosted with ❤ by GitHub

Step 10: How Does B.A.M Work?

To control a bicolor LED, we pay attention to two parameters: COORDINATES and COLORS, for example: LED(x, y, red, green).

To set one LED, see program below:

void LED(int X, int Y, int R, int G)
{
X = constrain(X, 0, 31);//Matrix X axis  
Y = constrain(Y, 0, 31);//Matrix Y axis
  
R = constrain(R, 0, 15);
G = constrain(G, 0, 15);

int WhichByte = int(Y*4+ X/8);
int WhichBit = 7-(X%8);

for (byte BAM = 0; BAM < 4; BAM++)

{
//*** RED ***
    bitWrite(red[BAM][WhichByte], WhichBit, bitRead(R, BAM));

//*** GREEN ***
    bitWrite(green[BAM][WhichByte], WhichBit, bitRead(G, BAM));

}
}

Now for more specific, we explain a command LED (19, 22, 10, 5) with:

  • Coordinates: x = 19, y = 22.
  • Colors: red = 10, green = 5.

With 4 bit B.A.M, red & green colors are limited in range: 0 ~ 15.

Step 11: Coordinates

Following the main program, coordinates (x=19, y=22) is calculated and transferred to byte & bit:

  • WhichByte = 90 <--> int(y*4+ x/8)
  • WhichBit = 4 <--> 7 - (x%8)

Step 12: Colors

At address [Byte = 90][Bit = 4], color is calculated:

  • RED = 10 (00001010)
RED[0][90][4] = 0
RED[1][90][4] = 1
RED[2][90][4] = 0
RED[3][90][4] = 1
  • GREEN = 5 (00000101)
GREEN[0][90][4] = 1
GREEN[1][90][4] = 0
GREEN[2][90][4] = 1
GREEN[3][90][4] = 0

In picture above, you can see all raw data for describing B.A.M method and prepare for timing chart on next step.

Step 13: Coordinates With Colors

Let start from Timer 1 setup:

TCCR1A = B00000000;
TCCR1B = B00001011;
TIMSK1 = B00000010;
OCR1A=20;

With setup above, our timer clock runs at 250kHz (16MHz/64=250kHz) or 1/250kHz = 4us. When we set OCR1A to 20, this means the interrupt will be called every (20+1)x4us=84us (so called Time_Base), which gives a multiplex frequency of about 11,9 kHz.

In program, we take note the BAM_Counter with these numbers = 8, 24, 56 and 120, and BAM_Bit ++. For every TIMER1 interruption (after 84us), BAM_Counter will increment the value to 1, and if it reaches 120 then reset to 0. The relationship between BAM_Counter and BAM_Bit is as follows:

  • BAM_Bit = 0, when BAM_Counter is in range: 0 ~ 7.
  • BAM_Bit = 1, when BAM_Counter is in range: 8 ~ 23.
  • BAM_Bit = 2, when BAM_Counter is in range: 24 ~ 55.
  • BAM_Bit = 3, when BAM_Counter is in range: 56 ~ 119.

Or we can say, the X axis on timing chart above indicates the time of LEDs are ON / OFF with Time_Base = 84us (one unit on X axis is 84us).

  • Bam_Bit[0] = 8 x Time_Base = 672us --> RED[0][90][4] = 0 & GREEN[0][90][4] = 1, means at address (x=19, y=22), RED led OFF and GREEN led ON during 672 us.
  • Bam_Bit[1]=16 x Time_Base = 1,344us --> RED[1][90][4] = 1 & GREEN[1][90][4] = 0, means at address (x=19, y=22), RED led ON and GREEN led OFF during 1,344us.
  • Bam_Bit[2]=32 x Time_Base = 2,688us --> RED[2][90][4] = 0 & GREEN[2][90][4] = 1, means at address (x=19, y=22), RED led OFF and GREEN led ON during 2,688us.
  • Bam_Bit[3]=64 x Time_Base = 5,376us --> RED[3][90][4] = 1 & GREEN[3][90][4] = 0, means at address (x=19, y=22), RED led ON and GREEN led OFF during 5,376us.

Step 14: Actual Pictures

Actual images for:

  • LED(19, 22, 10, 5).
  • fillTable(4,8).
  • fillTable(8,8).
  • And command:
for (int x = 0; x < 32; x++)
{
for (int y = 0; y < 32; y++)
{
LED(x, y, int(x / 2), int(y / 2));
delay(50);
}
}

Step 15: GIF Pictures

All GIF pictures above is including the introduction video. We have to choose size 32x32 or resize GIF picture to fit with matrix display 32x32.

The way I display my animations is very simple. Firstly, I converted *.GIF file to some *.JPG files. And then converting JPG picture to HEX code and show them with suitable delay. That's all!

There're many conversion programs on internet, you can search by keyword: "convert gif to jpg online" and "picture to hex converter online"

Programs below to show pictures:

  • Draw image from HEX code
drawImage(32, 32, FACE00, clearcolor, greencolor);
  • RG Color template

struct Color
{
unsigned char red, green;
Color(int r, int g) : red(r), green(g) {}
Color() : red(0), green(0) {}
};
const Color redcolor = Color(0x0F,0x00);
const Color orangecolor = Color(0x0F,0x0F);
const Color yellowcolor = Color(0x0F,0x09);
const Color greencolor = Color(0x00,0x0F);
const Color clearcolor = Color(0x00,0x00);
  • Image HEX code
static const uint8_t FACE00[]PROGMEM = {0xFF, 0xBF, 0xFF, 0xFF, 0xC0, 0x20, 0x07, 0xFF, 0xFF, 0xC0, 0x00, 0x03, 0xFF, 0xE0, 0x00, 0xFF, 
   0x00, 0x30, 0x01, 0xFF, 0x00, 0x38, 0x03, 0x81, 0x00, 0x3C, 0x07, 0x01, 0xC0, 0xF0, 0x0F, 0x83, 
   0xFF, 0xE0, 0x1F, 0xFF, 0xEF, 0x70, 0x1F, 0xFF, 0xBF, 0xD0, 0x1F, 0xFF, 0x00, 0x20, 0x1B, 0xFF, 
   0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 
   0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x01, 0x1C, 0x01, 
   0x00, 0x0B, 0xFC, 0x03, 0x00, 0x0F, 0xFC, 0x03, 0x00, 0x03, 0xFC, 0x01, 0x00, 0x01, 0xF8, 0x03, 
   0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x10, 0x03, 0x00, 0x07, 0x7C, 0x02, 0x00, 0x3F, 0xFF, 0x86, 
   0x01, 0xFF, 0xFF, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x08, 0x00, 0x1E, 0x07, 0x00,  };
  • Image drawing program
void drawImage(uint8_t width, uint8_t height, const uint8_t *image, Color For_color, Color Bk_color)

{
    for (uint8_t y = 0; y < height; y++)
    {
        for (uint8_t x = 0; x < width; x++)
        {
            uint8_t  WhichByte =  x/8 + y*4;
            uint8_t   WhichBit = 7-(x % 8);
            uint8_t   colorImage = bitRead(pgm_read_byte(&image[WhichByte]), WhichBit) & 1;
	if (colorImage)
                {
                LED(x, y, For_color.red, For_color.green);
                }
              else
                {
                LED(x, y, Bk_color.red, Bk_color.green);        
                }
          }
    }
}

Step 16: VU Meter

To make VU meter, I bought 2 MSGEQ7 ICs (Seven Band Graphic Equalizer Display Filter), one for left channel the other for right channel. MSGEQ7 can divides the audio spectrum into seven bands: 63Hz, 160Hz, 400Hz, 1kHz, 2.5kHz, 6.25kHz, 16kHz and output a amplitude of each frequency band.

MSGEQ7 datasheet: https://www.sparkfun.com/datasheets/Components/Gen...

Step 17: PCB Design

All pictures above are PCB designs for all project:

  • LED matrix board.
  • Row scanning board.
  • Column scanning board.

Hope this helps!!!

Step 18: More Videos

For more videos, you can check at:

And

Step 19: And More Pictures

In videos, you can see some more effects like plasma, color-wheel, test scrolling, picture scrolling...As I said before, flicker is due to CAMERA, human eye cannot detect this flicker in reality....