HAVE FUN WITH BICOLOR MATRIX 32x32

25,555

92

26

Introduction: HAVE FUN WITH BICOLOR MATRIX 32x32

About: PLC, Arduino - Do it yourself project

Today I will share following topics base on a bicolor matrix led 32x32:

  1. D.I.Y a bicolor led matrix 32x32.
  2. How to control led board above with NodeMCU ESP8266 via B.A.M method.
  3. Introduce and share my code for some cool real time clock projects with this bicolor led board.

Let's start with some videos:

  • Rainbow letters and images: is controlled by NODEMCU.

  • Handwritten Clock Version 2:

  • Handwritten Clock Version 1:

  • Morphing Digital Clock: is referenced from HariFun.

Step 1: MATERIAL LIST

Step 2: SCHEMATIC

This is the circuit diagram of the whole project; it is a quite big file and you can download high resolution PDF file HERE. With this overall schematic, we will easily understand how to scan rows and columns for led matrices, as well as, how to apply the 4-bit BAM method during row and column scanning.

On the circuit diagram, I separated 32x32 = 1024 red and 32x32 = 1024 green LEDs in groups and connect default matrix pin in Fritzing to rows and columns buses. You have to connect color pins properly according to the specifications after purchasing bicolor led matrix.

NODECU pins are used as below:

// PINS USING ON NODEMCU
#define blank_pin	D8  // BLANK PIN - 74HC595
#define latch_pin	D4  // LATCH PIN - 74HC595
#define clock_pin	D5  // CLOCK PIN - 74HC595
#define data_pin	D7  // DATA PIN - 74HC595

#define RowA_Pin	D0  // A PIN - 74HC238
#define RowB_Pin	D1  // B PIN - 74HC238
#define RowC_Pin	D2  // C PIN - 74HC238
#define RowD_Pin	D3  // D PIN - 74HC238
//#define OE_Pin	D8  // ENABLE OUTPUT PIN - 74HC238 (Connect to blank_pin).

Note that:

  • I used 5 signals to control 2x74HC238 for implementing one 4 to 16 decoder. Then these 16 outputs will control 32 rows of bicolor led matrix through PNP and NPN transistors. In other words, my led panel have 1/16 scan rate, so only one row of 16 is on at a time. You can see how scan pattern a 32x32 led board with scan rate 1/16 looks like, at this link: https://www.sparkfun.com/sparkx/blog/2650
  • OE PIN - 74HC238 and BLANK PIN - 74HC595 are both connect to NODEMCU - D8 pin. This is an important key to minimize flickering and noise because we only enable the OE pin (Output Enable) for 74HC595 (columns) and 74HC238 (rows) when we needed. I have also tested this Led board on Arduino Mega 2560 with 2 OE signals which are individually controlled, one for 74HC595 and the remaining signal for 74HC238. It also gave good results. Check detail at STEP 5.

Step 3: PCB BOARD DESIGN AND ETCHING

You can download as attached links for following schematics and PCB files:

1. Row scanning circuit schematic and Eagle PCB real size.

2. Column scanning circuit schematic and Eagle PCB real size.

3. Led matrix board 32x32 schematic and Eagle PCB real size for 16x32 led board.

I etched myself these printed circuit boards at home by Toner Transfer Method and concentrated ferrous chloride. The pictures below are the results that I did:

  • Column Scanning Board: we need 2 sets of column scanning PCB for RED and GREEN color.

  • Row Scanning Board

  • Led Matrix 16x32 Board: Combine 2 sets of led board 16x32 PCB to become Led Board 32x32.

Step 4: ASSEMBLY WORK

  • Soldering female header and NodeMCU output pins on Double Sided Printed Prototyping Board. For power supply, I connected 5V to Vin pin of NODEMCU. And NODECU pins are used as described on STEP 2.

    • I also soldered one more circuit for Arduino Mega 2560 to test this Led Panel, with DS3231 (SDA - SCL), one potentiometer (A10), one push button (D4) and one audio jack (Right A8 - Left A9).

    • Prepared the 5V power supply

    • Connecting PCB boards and NODEMCU all together following the schematic on STEP 2. Putting all PCBs, nodeMCU and Led Board into box.

    • It's ready for programming.

    Step 5: PROGRAMMING

    Controlling 32x32 bicolor led table with NODEMCU, 16x74HC595 and 2x74HC238 is not easy. Because NodeMCU has limited inputs/ output pins, and timing calibration works also caused many difficulties. I have tested and dealed with noise and flickering issues many times. But in the end, I minimized these problems and my result was amazing!!!

    In this project, I used Timer 1 in NODEMCU and a callback routine to implement B.A.M process.

    timer1_isr_init();				// Initializes the timer 1.
    
    timer1_attachInterrupt(timer1_ISR);		// Attach the interrupt service routine (timer1_ISR).
    
    timer1_enable(TIM_DIV16, TIM_EDGE, TIM_SINGLE);	// Setup and enable timer 1.
    
    timer1_write(60);				// Tell timer 1 to call the timer1_ISR after 60 clock cycles.

    The reason I used Timer 1 instead of Timer 0 or Ticker standard library because:

    • Timer 0 has been used for WiFi Functions.

    The table below describes the Timer 1 and B.A.M settings as well as their relationship. With this setup, our timer clock runs at 5MHz (80MHz/16=5MHz) or 1/5MHz = 0.2us. When we set timer1_write (60), this means the interrupt will be called every 60 x 0.2us = 12us and B.A.M process need about 8 Ticks x 12us = 96us to set the "Least Significant Bit of BAM_Bit" to "ON" or "OFF", which gives a multiplex frequency of about 10.4 kHz.

    Note: This explanation base on 8 Ticks elapsed time on the lowest bit weight of BAM_Bit. You can check detail at item "main attach interrupt service routine" for my final setting.

    I marked my timer1_ISR() with ICACHE_RAM_ATTR attribute to avoid an illegal instruction error when interrupt happens.

    void ICACHE_RAM_ATTR timer1_ISR(void);

    For row scanning, firstly, I want to speed up my code by using direct control digital pins like this.

    //Setting the pin HIGH
    
    GPOS = (1 << RowA_Pin);
    
    //Setting the pin LOW
    
    GPOC = (1 << RowA_Pin);

    But it was really too fast, even some rows of matrix panel could not be displayed so I had to turn back traditional pin control with "digitalWrite". It worked great because NODEMCU has 80MHz clock speed :-)

    void rowScan(byte row) {
      
      if (row & 0x01) digitalWrite(RowA_Pin,HIGH);
        else          digitalWrite(RowA_Pin,LOW);
      if (row & 0x02) digitalWrite(RowB_Pin,HIGH);   
        else          digitalWrite(RowB_Pin,LOW);          
      if (row & 0x04) digitalWrite(RowC_Pin,HIGH);   
        else          digitalWrite(RowC_Pin,LOW);          
      if (row & 0x08) digitalWrite(RowD_Pin,HIGH);   
        else          digitalWrite(RowD_Pin,LOW);  
    }

    Main attach interrupt service routine:

    void ICACHE_RAM_ATTR timer1_ISR(void)
    {
      digitalWrite(blank_pin, HIGH);  // Set BLANK PIN high - 74HC595 & 74HC238               
      if(BAM_Counter==6)	// Bit weight 2^0, lasting time = 6 ticks x interrupt interval time
      BAM_Bit++;
      else
      if(BAM_Counter==18)   // Bit weight 2^1, lasting time = 18 ticks x interrupt interval time
      BAM_Bit++;
      else
      if(BAM_Counter==42)   // Bit weight 2^3, lasting time = 42 ticks x interrupt interval time
      BAM_Bit++;
      BAM_Counter++;
      switch (BAM_Bit)
        {
        case 0:
          //Red     
            SPI.transfer(red[0][level + 0]);
            SPI.transfer(red[0][level + 1]);
            SPI.transfer(red[0][level + 2]);
            SPI.transfer(red[0][level + 3]);
            SPI.transfer(red[0][level + 64]);
            SPI.transfer(red[0][level + 65]);
            SPI.transfer(red[0][level + 66]);
            SPI.transfer(red[0][level + 67]);
          //Green       
            SPI.transfer(green[0][level + 0]);
            SPI.transfer(green[0][level + 1]);
            SPI.transfer(green[0][level + 2]);
            SPI.transfer(green[0][level + 3]);
            SPI.transfer(green[0][level + 64]);
            SPI.transfer(green[0][level + 65]);
            SPI.transfer(green[0][level + 66]);
            SPI.transfer(green[0][level + 67]);      
          break;
        case 1:      
          //Red
            SPI.transfer(red[1][level + 0]);
            SPI.transfer(red[1][level + 1]);
            SPI.transfer(red[1][level + 2]);
            SPI.transfer(red[1][level + 3]);  
            SPI.transfer(red[1][level + 64]);
            SPI.transfer(red[1][level + 65]);
            SPI.transfer(red[1][level + 66]);
            SPI.transfer(red[1][level + 67]);             
          //Green
            SPI.transfer(green[1][level + 0]);
            SPI.transfer(green[1][level + 1]);
            SPI.transfer(green[1][level + 2]);
            SPI.transfer(green[1][level + 3]);        
            SPI.transfer(green[1][level + 64]);
            SPI.transfer(green[1][level + 65]);
            SPI.transfer(green[1][level + 66]);
            SPI.transfer(green[1][level + 67]);
          break;
        case 2:     
          //Red
            SPI.transfer(red[2][level + 0]);
            SPI.transfer(red[2][level + 1]);
            SPI.transfer(red[2][level + 2]);
            SPI.transfer(red[2][level + 3]);      
            SPI.transfer(red[2][level + 64]);
            SPI.transfer(red[2][level + 65]);
            SPI.transfer(red[2][level + 66]);
            SPI.transfer(red[2][level + 67]);                    
           //Green
            SPI.transfer(green[2][level + 0]);
            SPI.transfer(green[2][level + 1]);
            SPI.transfer(green[2][level + 2]);
            SPI.transfer(green[2][level + 3]);       
            SPI.transfer(green[2][level + 64]);
            SPI.transfer(green[2][level + 65]);
            SPI.transfer(green[2][level + 66]);
            SPI.transfer(green[2][level + 67]);
          break;
        case 3:
          //Red
            SPI.transfer(red[3][level + 0]);
            SPI.transfer(red[3][level + 1]);
            SPI.transfer(red[3][level + 2]);
            SPI.transfer(red[3][level + 3]);               
            SPI.transfer(red[3][level + 64]);
            SPI.transfer(red[3][level + 65]);
            SPI.transfer(red[3][level + 66]);
            SPI.transfer(red[3][level + 67]);    
          //Green
            SPI.transfer(green[3][level + 0]);
            SPI.transfer(green[3][level + 1]);
            SPI.transfer(green[3][level + 2]);
            SPI.transfer(green[3][level + 3]);              
            SPI.transfer(green[3][level + 64]);
            SPI.transfer(green[3][level + 65]);
            SPI.transfer(green[3][level + 66]);
            SPI.transfer(green[3][level + 67]);        
        if(BAM_Counter==90)    //Bit weight 2^3, lasting time = 90 ticks x interrupt interval time
        {
        BAM_Counter=0;
        BAM_Bit=0;
        }
        break;
      }
      rowScan(row);
      digitalWrite(latch_pin, HIGH);    // Set LATCH PIN low - 74HC595
      digitalWrite(latch_pin, LOW);     // Set LATCH PIN low - 74HC595
      digitalWrite(blank_pin, LOW);     // Set BLANK PIN low - 74HC595 & 74HC238
      row++;
      level = row<<2;
      if(row==16)
      row=0;
      if(level==64)
      level=0;
      timer1_write(60);     //Interrupt interval time 60 x 0.2us = 12us
    }

    To adjust and calibrate the led matrix, we can change the elapsed time on bit weight of BAM_Bit (4 bit) following below rule:

    You can play with above numbers of BAM_Counter and timer_write(xx) to find out your best parameters. Finally for me, I setup:

    • timer1_write(60).
    • BAM_Bit Tick numbers: 6, 18, 42, 90.

    You can download this table template in Excel format here: NODEMCU_TIMER1_BAM. It shows the relationship between interrupt interval time and how many Ticks for the lowest bit weight - BAM Bit - are selected.

    The project code for NODEMCU is available at my GitHub:

    https://github.com/tuenhidiy/NODEMCU-WITH-32x32-BI...

    Step 6: LED BOARD TESTING

    We can use some online tools for converting image file to C/C++ code by google key word "convert image to hex". Or we can convert animation GIF file to many separated images then convert these images to HEX code.

    • Rainbow Testing: picture below shows the different colors of LEDs that are generated from the B.A.M process.

    Here below are some projects that made me excited. With some slight modified, I can reuse them on my led board and Arduino Mega 2560. Thank to HariFun and TobiasB48 for their Morphing Digital Clock and Tetris Clock .

    • Morphing Digital Clock from HariFun

    I declared digit in "typedef struct" and updated clock with background color as below:

    typedef struct
    {
    byte _value;
    uint16_t xOffset;
    uint16_t yOffset;
    Color _color;
    Color _bgcolor;
    } Digit;

    My code for Morphing Digital Clock is available at GitHub link:

    https://github.com/tuenhidiy/DIGITAL_CLOCK_MATRIX_...

    • Tetris Time Clock from TobiasB48

    Original version from TobiasB48 displayed the time on RGB matrix 16x32. For my Tetris Clock, with some modification, it can show hour, minute and second on bicolor matrix 32x32. Tetris Clock effect looks great and I can look at it all day :-)

    My code for Tetris Clock is available at GitHub:

    https://github.com/tuenhidiy/TETRIS_CLOCK_MATRIX_3...

    • Handwritten Clock Version 1:

    • Handwritten Clock Version 2:

    For 2 versions of handwritten clocks, I created 3 handwritten font sizes:

    *** Small font size 7x11.

    *** Inverted small font size 8x16.

    *** Big font size 14x20.

    For Handwritten Clock version 1, I used small and inverted small number fonts for clock effects. With Handwritten Clock version 2, I combined small and big number fonts to show time on matrix 32x32. They are both look very amazing.

    My code for Handwritten Clock is available at GitHub:

    https://github.com/tuenhidiy/HANDWRITTEN_CLOCK_MAT...

    Step 7: FINISH

    Here are some pictures for my projects.

    Thank you for reading to the last page.

    Colors of the Rainbow Contest

    Participated in the
    Colors of the Rainbow Contest

    Be the First to Share

      Recommendations

      • Puzzles Speed Challenge

        Puzzles Speed Challenge
      • "Can't Touch This" Family Contest

        "Can't Touch This" Family Contest
      • CNC Contest 2020

        CNC Contest 2020

      26 Discussions

      0
      geraldcameron876
      geraldcameron876

      Question 11 months ago

      What's the value of the resistors on the row scanning board ?

      0
      geraldcameron876
      geraldcameron876

      Question 11 months ago

      Where's the video of the audio output controlling the LEDs, I note no mention is made of same and very little information on the circuit using the Arduino Mega 2560 ?

      0
      geraldcameron876
      geraldcameron876

      12 months ago

      If this project is alive I have made a PCB layout for the Matrix LED using the A-2088BHG suggested by the author, gerber files available. See attached pdf

      0
      GeraldC17
      GeraldC17

      Question 1 year ago

      Has anyone made this project yet ? I'm trying to do a PCB and I find some anomolies between the schematic and the authors PCB in the Column scan sections in which the 74595 do not properly align with the ULN2803A and it gave me plenty headaches as I was wondering how his PCB line up those pins so straight until I discovered that there's serious variations between the pin numbers of the schematics and the PCB, it's not possible to have a straight trace running between the output of the 74595 and the input of the 2803A, they normally cross over each other, please tell me I'm wrong, over to you author.
      I see what the author did, the whole input to the 2803A has been reversed contrary to the schematic.

      0
      tuenhidiy
      tuenhidiy

      Answer 1 year ago

      Hi Garald. In my design, 74HC595 and ULN2803 are in opposite directions. With this arrangement, 7 pins can be connected straightly, just remains 1 pin we have to jump. Please check the picture below with my notes.

      20190526_075344.jpg
      0
      GeraldC17
      GeraldC17

      1 year ago

      I noticed that your schematics makes no references to the values of the resistors used in your circuits, could you update the schematics as it's difficult to try and make them out on the row scanning board picture. The column board seems to be 10 ohm resistors.

      0
      GeraldC17
      GeraldC17

      1 year ago

      I'm going to do this project in Orcad Capture 17.2 and Allegro PCB 17.2.

      0
      tuenhidiy
      tuenhidiy

      Reply 1 year ago

      It’s good. You should check the Matrix pins before design a PCB.

      0
      GeraldC17
      GeraldC17

      Reply 1 year ago

      I would like the author to show some more pictures e.g. the same matrix pins and the actual back of board wirings

      0
      tuenhidiy
      tuenhidiy

      Reply 1 year ago

      Hi Gerald. In my led board design, led matrix red and green pins are interspersed with each other. So I used the following tip to make the red pins and green pins of a led matrix located on the same ribbon cables. Please see the picture: Firstly I removed all wires at one side out of the header by small screwdriver. Then after accurately determining the pins of the led matrix, I will put them back into the header in the correct order.
      If you're in the process of designing this printed circuit board, you could make the red and green pins in the same header and it would save a lot of time.
      Hope this help! I will update this issue on my Instructable.

      Ribbon cable tip.jpg
      0
      Mic100
      Mic100

      1 year ago

      Very nice big work !
      Thank you :)

      0
      tuenhidiy
      tuenhidiy

      Reply 1 year ago

      Hi Mic. Thanks.

      0
      echoon1
      echoon1

      Question 1 year ago

      Hi, would it be possible to display and "L" and then flash between the L and the frowning face if someone gets to close?
      Obviously, I'd be wanting to mount this in/on the back of the car.

      L-150x150.pngslightly-frowning-face_1f641.png
      0
      fred1896
      fred1896

      Answer 1 year ago

      pure genius mate, im gonna try that :D :D :D

      (if i can constrain myself enough to keep it only a frowning face) ;)

      U would probably have to make your own frowny emoji and L plate tho, and it would probly have to be simpler with no gradients...

      0
      echoon1
      echoon1

      Reply 1 year ago

      Yes, I was thinking of something other than a frown at 1st but the driving school I work for may get lots of calls for the wrong reason.
      Yes just a simple bright solid yellow/orange would be fine a finger would be even better :P

      0
      ultraled12
      ultraled12

      1 year ago

      Actually the explanation is more helpful to us, mainly the bachelor of electronics and electrical , also we have presented a project related to the Ultra LED Display ,link is:
      https://ultraleddisplay.com/ , which might be useful for the one searching for the LED based projects.

      0
      GeraldC17
      GeraldC17

      Question 1 year ago on Step 7

      What's the finished dimensions of the matrix display ? I assume 60mm x4 (24cms) height by width based on the parts.

      0
      tuenhidiy
      tuenhidiy

      Answer 1 year ago

      Hi GeraldC17. You're right. I used bi-color matrix 60x60mm.

      0
      GeraldC17
      GeraldC17

      Reply 1 year ago

      Thanks but I'm asking what's the finished dimension of the display ?

      0
      tuenhidiy
      tuenhidiy

      Reply 1 year ago

      :-). My LED board dimension is about 26x28x12 in cm (HxLxW).