Netduino Day 4 : Eight Digit Seven Segment LED Display with MAX7219



In Day 3 tutorial, we learnt basics of seven segment LED displays and discussed about a time-division multiplexing technique that reduces the number of required microcontroller I/O pins to drive multiple seven segment LED displays. Today we will move a step further and discuss about a serial interface (SPI) for driving 8 seven segment LED displays. The technique uses MAXIM’s MAX7219 LED driver chip that allows you to control 8 (or more in cascade configuration) common-cathode seven segment LED displays with only 3 I/O pins of Netduino. For illustrative purpose, we will use our 8-digit serial seven segment LED (8DSSSLED) display module here. The benefit of using MAX7219 is, it will do all the hard work of multiplexing operation and let the microcontroller do more important jobs. It supports SPI serial communication protocol.

Netduino controlling 8 Digit Serial Seven Segments LED using MAX7219

Circuit Setup and Theory

The SPI communication method is the heart of the project besides the codes that controls what to display in the seven segments LED.  In our setup, Netduino Plus acts as a SPI Master device and 8DSSSLED as a Slave. Among the several digital I/O pins of Netduino, Pins 11, 12 and 13 are designed to support the SPI Bus communication. Pin 12 is Master In Slave Out (MISO), pin 11 is Master Out Slave In (MOSI), and pin 13 serves as the master clock. In our project the Netduino Plus does not receive any data back from the slave device (MAX7219), so the MISO pin is unused. The MOSI pin (11) of Netduino goes to DIN of 8DSSSLED and pin 13 drives the CLK signal of MAX7219. The LOAD signal (which is a Chip Select pin of MAX7219) of the 8DSSSLED board can be connected to any Digital I/O of a microcontroller. In our case we have specified this to pin 10 of Netduino.

Since the circuit diagram and all the necessary details of 8DSSSLED is well explain here so allow me not to reinvent the wheel.

Circuit Diagram - Eight Digit Serial Seven Segments LED and Netduino

C#.NET Program

The program basically focused around the MAX7219 drive then tailored towards the 8DSSSLED. The .NET Micro FramWork provides a SPI class so we don’t have to worry too much about working with SPI. We will learn more about the SPI in a moment. Let’s look at the overview of this class. All the methods and properties can be seen in the picture below. The MAX7219 class, most of the time, disables the BCDDecode mode and hence, controls individual segments to display alphanumeric characters.

Overview of MAX7219 Driver

In order to use the Max7219 class we first need to create an instance of this class and directly call the Display related methods to show the test/numbers in the Seven Segments LED. In the example below we are also setting intensity, clearing the display, setting the textDirection to LeftToRight.

max = new Max7219(Pins.GPIO_PIN_D10);
max.SetIntensity(Max7219.Intensity.Max);
 
max.LeftToRight = true;
max.DisplayAutoScorllFirstHalf("i_am on first line.", 1000);

We will talk about utilizing the Max7219 in output section so let’s look at the Max7219 class. Following section shows the constructor of the class. Notice the argument does not require any of the SPI pins rather it asks for the data pin which is I/O 10 in our case. You can see we first defined the SPI Configuration and then providing that to the new SPI instance. Then it jumps to Initialize() method.

/// <summary>
/// Netduino Pin:
/// D11 - SPI MOSI --> DIn of Board
/// D12 - None
/// D13 - SPI CLK --> CLK of Board         
/// </summary>
/// <param name="chipSelect">Load pin of Board</param>
public Max7219(Cpu.Pin chipSelect)
{
    SPI.Configuration spiConfiguration = new SPI.Configuration(chipSelect, false, 0, 0, false, true, 2000, SPI.SPI_module.SPI1);
    Spi = new SPI(spiConfiguration);
    Initialize();
}

The Initialize method basically initializes some fields and then creates a Hashtable for the supported characters. It calls some other methods like EnableDigits, DisplayTest and ShutDown to make the Seven Segments ready to take the input. As these methods are defined public, we can call these outside the class when needed.

Let’s look at the basic methods that control the Max7219 IC.

public void Initialize()
{
    segmentsOneAtaTime = new byte[] { 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00 };
    segmentsPbarAscending1By1 = new byte[] { 0x00, 0x40, 0x60, 0x70, 0x78, 0x7C, 0x7E };
    segmentsPbarDescending = new byte[] { 0x3E, 0x1E, 0x0E, 0x06, 0x02, 0x00 };
    segmentsPbarAscendingAll = new byte[] { 0x7E, 0x5E, 0x6E, 0x76, 0x7A, 0x7C, 0x3E, 0x7E };
 
 
    CharacterAddressMap = new Hashtable();
    CharacterAddressMap.Add("0", 0x7E);
    CharacterAddressMap.Add("1", 0x30);
    CharacterAddressMap.Add("2", 0x6D);
    CharacterAddressMap.Add("3", 0x79);
    CharacterAddressMap.Add("4", 0x33);
    CharacterAddressMap.Add("5", 0x5B);
    CharacterAddressMap.Add("6", 0x5F);
    CharacterAddressMap.Add("7", 0x70);
    CharacterAddressMap.Add("8", 0x7F);
    CharacterAddressMap.Add("9", 0x7B);
    CharacterAddressMap.Add("A", 0x77);
    CharacterAddressMap.Add("B", 0x1F);
    CharacterAddressMap.Add("C", 0x4E);
    CharacterAddressMap.Add("D", 0x3D);
    CharacterAddressMap.Add("E", 0x4F);
    CharacterAddressMap.Add("F", 0x47);
    CharacterAddressMap.Add("G", 0x5E);
    CharacterAddressMap.Add("H", 0x37);
    CharacterAddressMap.Add("I", 0x30);
    CharacterAddressMap.Add("J", 0x3C);
    CharacterAddressMap.Add("K", 0x2F);
    CharacterAddressMap.Add("L", 0x0E);
    CharacterAddressMap.Add("M", 0x55);
    CharacterAddressMap.Add("N", 0x15);
    CharacterAddressMap.Add("O", 0x1D);
    CharacterAddressMap.Add("P", 0x67);
    CharacterAddressMap.Add("Q", 0x73);
    CharacterAddressMap.Add("R", 0x05);
    CharacterAddressMap.Add("S", 0x5B);
    CharacterAddressMap.Add("T", 0x0F);
    CharacterAddressMap.Add("U", 0x3E);
    CharacterAddressMap.Add("V", 0x1C);
    CharacterAddressMap.Add("W", 0x5C);
    CharacterAddressMap.Add("X", 0x49);
    CharacterAddressMap.Add("Y", 0x3B);
    CharacterAddressMap.Add("Z", 0x6D);
    CharacterAddressMap.Add(" ", 0x00); // white space
    CharacterAddressMap.Add(".", 0x80);
    CharacterAddressMap.Add("-", 0x01);
    CharacterAddressMap.Add("_", 0x08);
 
    SpiWriteBuffer = new ushort[1];
    EnableDigits(8);
 
    DisplayTest(false);
    ShutDown(false);
    Clear();
}
public void Clear(byte numberOfDigits = 8)
{
    for (byte i = 1; i <= numberOfDigits; i++)
        Write(i, 0x00);
}
public void SetIntensity(Intensity intensity)
{
    Write((byte)RegisterAddressMap.Intensity, (byte)intensity);
}
public void SetBCDDecodeMode(BCDDecodeMode decodeMode)
{
    Write((byte)RegisterAddressMap.DecodeMode, (byte)decodeMode);
}
public void DisplayTest(bool enable)
{
    if (enable)
        Write((byte)RegisterAddressMap.DisplayTest, 0x01);
    else
        Write((byte)RegisterAddressMap.DisplayTest, 0x00);
}
public void ShutDown(bool shutDown)
{
    if (shutDown)
        Write((byte)RegisterAddressMap.Shutdown, 0x00);
    else
        Write((byte)RegisterAddressMap.Shutdown, 0x01);
}
public void Write(byte register, byte value)
{
    SpiWriteBuffer[0] = (ushort)register;
    SpiWriteBuffer[0] <<= 8;
    SpiWriteBuffer[0] |= value;
    Spi.Write(SpiWriteBuffer);
 
    Debug.Print(SpiWriteBuffer[0].ToString());
}

Now, let’s look the methods that controls the Display of the LEDs.
The Blink method, simply shuts the unit down for the given time and it does that for given number of time.

public void Blink(int times, int delay)
{
    for (int i = 0; i < times; i++)
    {
        Thread.Sleep(delay);
        ShutDown(true);
        Thread.Sleep(delay);
        ShutDown(false);
    }
}

The main method that is responsible to show the information in the 8DSSSLED is the Display method where given string is looped for each character in the string then it calls the Write method through _Display method.

public void Display(string value, byte startDigit)
{
    char[] texts = value.ToCharArray();
    string displayText;
 
    for (int i = 0; i < texts.Length; i++)
    {
        displayText = texts[i].ToString().ToUpper();
        if (CharacterAddressMap.Contains(displayText))
            _Display((byte)startDigit, Convert.ToByte(CharacterAddressMap[displayText].ToString()));
 
        if (LeftToRight)
            startDigit -= 1;
        else
            startDigit += 1;
    }
}

The auto scrolling functionality is added on DisplayAutoScroll, DisplayAutoScrollFirstHalf and DisplayAutoScrollSecondHalf methods. DisplayAutoScroll uses all 8 digits of the Seven Segments LED whereas DisplayAutoScrollFirstHalf, and DisplayAutoScrollSecondHalf uses only first four and last four digits respectively. The first argument is the information to display and second delay argument to hold the display for that long until if scrolls to another character.

public void DisplayAutoScroll(string value, int delay)
{
    SetBCDDecodeMode(BCDDecodeMode.Disable);
    string text;
 
    byte startDigit = 8;
    if (!LeftToRight) startDigit = 1;
 
    for (int i = 0; i < System.Math.Max(value.Length - 8 + 1, 1); i++)
    {
        text = value.Substring(i, System.Math.Min(8, value.Length));
        Display(text, startDigit);
 
        Thread.Sleep(delay);
    }
}
public void DisplayAutoScorllFirstHalf(string value, int delay)
{
    SetBCDDecodeMode(BCDDecodeMode.Disable);
    string text;
 
    byte startDigit = 4;
    if (!LeftToRight) startDigit = 1;
 
    for (int i = 0; i < System.Math.Max(value.Length - 4 + 1, 1); i++)
    {
        text = value.Substring(i, System.Math.Min(4, value.Length));
        Display(text, startDigit);
 
        Thread.Sleep(delay);
    }
}
public void DisplayAutoScorllSecondHalf(string value, int delay)
{
    SetBCDDecodeMode(BCDDecodeMode.Disable);
    string text;
 
    byte startDigit = 8;
    if (!LeftToRight) startDigit = 5;
 
    for (int i = 0; i < System.Math.Max(value.Length - 4 + 1, 1); i++)
    {
        text = value.Substring(i, System.Math.Min(4, value.Length));
        Display(text, startDigit);
 
        Thread.Sleep(delay);
    }
}

The Max7219 class also provides some nice way to show a busy sign in the seven segments LED. It supports four major methods which can be doubled by setting the direction (clockwise/counter clockwise).

public void DisplayBusy(bool clockwise, int delay, BusyStyle style)
{
    byte[] segments;
    switch (style)
    {
        case BusyStyle.OneAtaTime:
            segments = segmentsOneAtaTime;
            break;
        case BusyStyle.ProgressBarAscending1By1:
            segments = segmentsPbarAscending1By1;
            break;
        case BusyStyle.ProgressBarDescending:
            segments = segmentsPbarDescending;
            break;
        case BusyStyle.ProgressBarAscendingAll:
            segments = segmentsPbarAscendingAll;
 
            break;
        default:
            segments = segmentsOneAtaTime;
            break;
    }
 
 
    SetBCDDecodeMode(BCDDecodeMode.Disable);
    byte digits = 0;
 
 
    if (clockwise)
    {
        for (byte i = 1; i < 8 + 1; i++)
        {
            for (int j = 0; j < segments.Length; j++)
            {
                digits = LeftToRight ? (byte)(8 - i + 1) : i;
                _Display(digits, segments[j]);
                Thread.Sleep(delay);
            }
        }
    }
    else
    {
        for (byte i = 1; i < 8 + 1; i++)
        {
            for (int j = segments.Length - 1; j-- > 0; )
            {
                digits = LeftToRight ? (byte)(8 - i + 1) : i;
                _Display(digits, segments[j]);
                Thread.Sleep(delay);
            }
        }
    }
}

Output

Let’s utilize the Max7219 class and use it for displaying different results.

Displaying number and alphabets in 8DSSSLED

Following example shows the auto scroll functionality on first four digits and then last four digits. The second argument in the method is delay to hold the display for that long.

max = new Max7219(Pins.GPIO_PIN_D10);
max.SetIntensity(Max7219.Intensity.Max);
 
while (true)
{
    max.DisplayBusy(true, 100, Max7219.BusyStyle.OneAtaTime);
}
 
max.Clear();
max.LeftToRight = true;
max.DisplayAutoScorllFirstHalf("i_am on first line.", 1000);
 
max.LeftToRight = false;
max.DisplayAutoScorllSecondHalf("second line here", 1000);

Now let’s use both first four and last four inside a counter loop and also change the brightness/intensity at the same time.

max.LeftToRight = true;
for (int i = 1000; i < 1050; i += 1)
{
    max.SetIntensity((Max7219.Intensity)(i % 15));
 
    max.DisplayAutoScorllFirstHalf(i.ToString(), 0);
    max.DisplayAutoScorllSecondHalf((2000 - i).ToString(), 0);
    Thread.Sleep(200);
}

Let’s send a long string to use all digits and change the LeftToRight to true and then to false to see the scrolling effect.

max.LeftToRight = true;
max.DisplayAutoScroll("0123456789abcdefghijklmnopqrstuvwxyz.", 1000);
 
max.DisplayAutoScroll(" --  -- ", 0);
max.Blink(2, 200);
 
max.LeftToRight = false;
max.DisplayAutoScroll("0123456789abcdefghijklmnopqrstuvwxyz.", 1000);

As mentioned earlier, the Max7219 class offers some busy signs, progress bar, so let’s take a look.

max.LeftToRight = true;
max.SetIntensity(Max7219.Intensity.Max);
max.DisplayBusy(true, 100, Max7219.BusyStyle.OneAtaTime);
max.DisplayBusy(false, 100, Max7219.BusyStyle.OneAtaTime);
 
max.Clear();
max.DisplayBusy(true, 100, Max7219.BusyStyle.ProgressBarAscending1By1);
 
 
max.Clear();
max.SetIntensity(Max7219.Intensity.Min);
max.DisplayBusy(true, 100, Max7219.BusyStyle.ProgressBarAscendingAll);
 
max.Clear();
max.DisplayBusy(false, 100, Max7219.BusyStyle.ProgressBarAscendingAll);
 
max.Clear();
max.SetIntensity(Max7219.Intensity.Average);
max.DisplayAutoScroll("00000000", 1); // turn all on
max.DisplayBusy(true, 100, Max7219.BusyStyle.ProgressBarDescending);

Downloads

1) C#.NET solution (Code) file.

What Next

In our next tutorial, we will not do any circuitry rather utilize the SD card that Netduino Plus supports. We will learn to write to a text file that resides in an SD card. <Link will be added>

Leave a Reply

Your email address will not be published. Required fields are marked *