Close
0%
0%

Driving an LED Matrix From a Raspberry Pi

Controlling an LED Matrix with a MAX7221/MAX7219 From a Raspberry Pi

Similar projects worth following
This is a walk through of how to drive an LED Matrix display with the MAX7221/MAX7219 driver chips from a Raspberry Pi. First the hardware interconnections will be covered, then the low level software that talks to the driver chips will be covered. The software driver is a character based system accepting ASCII in and displaying one character in each 5x8 display. The LED display modules (LiteOn LTP2558AA) and the interconnection of the MAX7221 are pretty well covered in one of the logs in my LED Matrix Display project.
Maxim has produced 2 LED driver chips, the MAX7219 and the MAX7221 that are extremely similar. Throughout this article, I will refer to the MAX7221 because that is the chip that I used. Everything in this article should be applicable to both chips.

            Communications

Maxim's MAX7221 LED Driver chips communicate with the host computer via the SPI (Synchronous Peripheral Interface) bus. SPI based systems normally have a single bus controller (in this case a Raspberry Pi) and one or more target devices (in this case, LED Driver chips). It is normally a short distance bus and transfer rates range from a few hundred Kbits/second up to 25Mbits/second. Most uses of it are at the lower range of speeds. A SPI bus has 4 primary signals and a ground. These signals are:

CS  Chip Select or Chip Enable, a usually active low signal that indicates the beginning and end of a SPI transaction. This signal comes from the bus controller.

SCLK Serial Clock, this signal synchronizes the data transfer within the transaction. This signal comes from the bus controller.

MOSI Master Out Slave In, this is the data out of the bus controller, destined to the target device(s). On target devices, this may be labelled Data In. This signal comes from the bus controller.

MISO Master In Slave Out, this is the data from the target devices to the bus controller. On target devices, this may be labelled Data Out. This signal comes from the target device.

One method of creating a bus with SPI communications is via "daisy chaining".  Daisy chaining allows connecting multiple target devices in a sequence. Not all SPI devices support the daisy chain method of interconnecting targets, but the MAX7221 devices do. In a daisy chained system, the CS and SCLK signals are common to all devices on the bus. The MOSI signal from the controller is connected to the DataIn signal of the first target device in the chain. The DataOut signal of the first device becomes the InterDigit_Data_1 signal and is connected to the Data In signal of the second target device in the chain. This connection of target DataOut to the next target DataIn signals is repeated for the rest of the devices in the chain as InterDigit_Data_N. In some systems, the last MISO/DataOut signal may be connected back to the bus controller, but in this system, it must not be. The Raspberry Pi runs on 3.3V and the MAX7221 chips run on 5.0V. The last target devices MISO/DataOut pin will be a 5.0V signal that would damage the 3.3V MISO/DataIn pin on the Raspberry Pi. There is nothing useful to read back in any event.

Block Diagram of a Daisy Chained SPI Interface.
Block Diagram of a Daisy Chained SPI Interface.

Pin numbers on the Raspberry Pi 40 Pin Connector are shown. Pin numbers on the MAX7221/7219 chips are shown for reference. In practice, there will be connectors on the edge of the board for each display module, but the layout and pin numbering of the display modules are specific to the design of the modules and will vary depending on the module source.

3 Display Modules forming a Daisy Chain SPI Bus.
3 Display Modules forming a Daisy Chain SPI Bus.

In the picture above, the Raspberry Pi is connected to the cable at the right end of the 3 modules. Note the In and Out labels on the boards below the connector.

                 Details of a SPI Transaction

The following description of a SPI transaction refers to the most common configuration called Mode 0 that is used by the MAX7221 and should be accepted by the MAX7219 chips. Other Mode configurations result in different timing relationships between the SCLK and the MOSI signals and the CS signal may be inverted from what is described here.

Scope Shot of a Single Byte SPI Transaction With a Data Value of 0xA3
A scope shot of a single byte SPI transaction with a data value of 0xA3. Ch1 = CS, Ch2 = SCLK, Ch3 = MOSI

In the scope shot above, the picture starts with the Chip Select (CS) signal high and the Serial Clock (SCLK) signal is inactive. The value of the MOSI signal does not matter while the CS signal is high and the SCLK inactive. The falling edge of the CS signal is the start of the SPI transaction. This transaction is sending the hex value of A3 or binary 1010 0011. The Most Significant Bit (bit...

Read more »

MAX_Single_Driver_RasPi_11_27_2020.tgz

Source for the low level code to talk to MAX7221 or MAX7219 driven 5x8 matrix LED displays from a Raspberry Pi. Open source under the BSD license.

x-compressed-tar - 10.33 kB - 11/27/2020 at 19:27

Download

  • Using the Code

    Bharbour12/11/2020 at 12:29 0 comments

    If you use the C libraries that are in the files section, there are 2 macros that are important to set to match the hardware configuration. The first one is in the MatrixDriver_MAX7221.h file at the top of the Defines area:

    //=========================================================================
    // Defines
    //=========================================================================
    #define NUM_DISPLAY_MODULES (3)   // Number of display modules on the I2C bus

    Set NUM_DISPLAY_MODULES to the number of MAX7221 chips on the SPI chain.

    The second one is in the the file MatrixDriver_MAX7221.c at the top of the Defines area:

    //=========================================================================
    // Defines
    //=========================================================================
    // Uncomment to run a left to right (physical) SPI chain. The early single
    // driver boards ran left to right data flow, and since the first byte out
    // of the SPI Master winds up in the far end of the SPI chain, the // character display data needs to have it's order swapped. Later versions // and the dual driver versions run a right to left SPI chain and do not
    // need the order swapped.
    #define LEFT_TO_RIGHT_SPI_CHAIN

    This one pertains to the physical direction of the SPI chain. If the SPI chain runs from the leftmost module to the rightmost module, you want this uncommented. Conversely, if the SPI chain runs from the rightmost module to the leftmost module, comment this define out.

    The NUM_DISPLAY_MODULES value is used in many places in the MatrixDriver_MAX7221 file and will be useful in the top level application code for sizing output arrays and such.

    The LEFT_TO_RIGHT_SPI_CHAIN value is only used in the Send_Buffer() function in the MatrixDriver_MAX7221.c file to determine the assembly order in the translation from ASCII string to MAX7221 data.




  • Continuation from the Details Section

    Bharbour11/28/2020 at 23:23 0 comments

    In order to test this code and have something to play with, I wrote a simple driver stub. It contains the main() function and accepts typed in characters and can modify the display brightness. It is all in the top.c file. If the driver code were used in a real application, this entire file would be replaced.

    /*---------------------------------------------------------------------------
    * Filename:  top.c
    * $Revision: 1.2 $
    * Author: Bob Harbour
    * $Date: 2020-11-26 20:59:58 $
    *
    *
    * Revisions:
    *------------------------------------------------------------------------*/
    
    
    //===================================================================
    // INCLUDES
    //===================================================================
    #include <stdint.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    #include "std_constants.h"
    #include "chargen_5x8.h"
    #include "MatrixDriver_MAX7221.h"
    
    //===================================================================
    // Defines
    //===================================================================
    
    //===================================================================
    // Globals
    //===================================================================
    
    //===================================================================
    // Externals
    //===================================================================
    
    //===================================================================
    // Function Prototypes
    //===================================================================
    int strip_newline_from_string (char *instr, int maxlen);
    
    //===================================================================
    // Functions
    //===================================================================
    int main (int argc, char *argv[])
    {
      int retval, len, trunc_boundary, done, count;
      char inbuffer [SHORT_BUFFER_LEN], *inptr;
      uint8_t brightness;
    
      retval = OK;
      done = FALSE;
      brightness = MX_DEFAULT_BRIGHTNESS;
    
      if ((retval = init_matrix_display_system ()) == OK)
      {
        count = 0;
    
        fprintf (stdout,"<ESC>Q to quit, <ESC>+ or <ESC>- adjust brightness\n");
    
        while (!done)
        {
          // get user input
          if (fgets (inbuffer, SHORT_BUFFER_LEN, stdin) != NULL)
          {
            len = strip_newline_from_string (inbuffer, SHORT_BUFFER_LEN);
            if (inbuffer[0] == ESC)
            {
              if ((inbuffer[1] == 'Q') || (inbuffer[1] == 'q'))
                done = TRUE;
              else
                if (inbuffer[1] == '+')
                {
                  if (brightness < MX_MAX_BRIGHTNESS)
                  {
                    brightness += 1;
                    set_display_brightness (brightness);
                    fprintf (stdout,"brightness: %1d\n", brightness);
                  }
                  else
                    fprintf (stdout,"Already at MAX brightness\n");
                }
                else
                if (inbuffer[1] == '-')
                {
                  if (brightness > MX_MIN_BRIGHTNESS)
                  {
                    brightness -= 1;
                    set_display_brightness (brightness);
                    fprintf (stdout,"brightness: %1d\n", brightness);
                  }
                  else
                    fprintf (stdout,"Already at MIN brightness\n");
                }
                else
                {
                  fprintf (stdout,"Unrecognized Escape Sequence: ESC %c\n",
                           inbuffer[1]);
                }
            }  // pairs with: if (inbuffer[0] == ESC)
            else
            {
              //retval = insert_display_chars_from_left (inbuffer, len, &trunc_boundary);
              retval = insert_display_chars_from_right (inbuffer, len, &trunc_boundary);
              printf ("truncation boundary: %1d\n", trunc_boundary);
              count += 1;
            }  // pairs with: if (inbuffer[0] == ESC) else
          }   // pairs with: if (fgets () )
          else
          {
            if (feof (stdin))
            {
              done = TRUE;
            }
          }      // pairs with: if (fgets () != NULL)  else
        }  // pairs with : while (!done)
        shutdown_matrix_display_system (FALSE);
      }
      else
      {
        fprintf (stderr,"ERROR, init_matrix_display_system() barfed\n");
      }
    
      exit (retval);
    }
    
    //===================================================================
    //  strip_newline_from_string()
    //  Removes the newline character from a string.
    //  Returns the number of characters in the resulting string
    //
    //  8/2007
    //===================================================================
    int strip_newline_from_string (char *instr, int maxlen)
    {
      char *ptr;
      int len;
    
      ptr = instr;
      len = 0;
      // leave room for terminator
      maxlen -= 1;
    
      // count to end of line
      while ((*ptr != CR) && (*ptr...
    Read more »

View all 2 project logs

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates