KIRAN KUMAR BOLLUKONDA
Published

MAX7219 Interfacing with Raspberry PI 2 - Windows 10

SPI Interface with MAX7219 LED Display.

BeginnerWork in progress1,663
MAX7219 Interfacing with Raspberry PI 2 - Windows 10

Things used in this project

Schematics

PIN CONNECTIONS

Code

Interfacing Raspberry PI 2 with MAX7219 8x8 matrix display

C#
Main page code behind.
/*
Author   : KIRAN KUMAR BOLLUKONDA
Email    : BK.KIRAN.KUMAR@GMAIL.COM
           BK.KIRAN.KUMAR@HOTMAIL.COM
Created  : 21-JUNE-2015

Comments : Please note that this code is written only for beginners 
           and I tried to keep as simple as possible. This code allows 
           the beginner to understand the MAX7219 via Raspberry Pi 2 
           SPI with Windows 10. I will try to post full version when
           I find free time
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.Devices.Enumeration;
using Windows.Devices.Spi;
using Windows.Devices.Gpio;
using System.Threading.Tasks;


namespace ScrollingDisplay
{
    public sealed partial class MainPage : Page
    {
        private const string SPI_CONTROLLER_NAME = "SPI0";  // Use SPI0.
        private const Int32 SPI_CHIP_SELECT_LINE = 0;       // Line 0 is the CS0 pin which is 
                                                            // the physical pin 24 on the Rpi2.

        
        // private const UInt32 MAX_DISPLAYS = 1;
        // private const UInt32 DISPLAY_ROWS = 8;
        private const UInt32 DISPLAY_COLUMNS = 8;
        // private byte[] DisplayBuffer = new byte[DISPLAY_COLUMNS * 2]; // Including address byte. 1 Address byte, 1 Data byte.

        private string Message;                             // Message to be displayed on the LED display.
        private byte[] MessageBuffer;                       // Message Buffer to hold total character font.
        private int ScrollDelay = 40;                       // In milliseconds. Use this field to change dynamically for scroll speed.
        private byte[] SendBytes = new byte[2];             // Send to Spi Display without drawing memory.

        // COMMAND MODES for MAX7219. Refer to the table in the datasheet.
        private static readonly byte[] MODE_DECODE      = { 0x09, 0x00 }; // , 0x09, 0x00 };
        private static readonly byte[] MODE_INTENSITY   = { 0x0A, 0x00 }; // , 0x0A, 0x00 };
        private static readonly byte[] MODE_SCAN_LIMIT  = { 0x0B, 0x07 }; // , 0x0B, 0x07 };
        private static readonly byte[] MODE_POWER       = { 0x0C, 0x01 }; // , 0x0C, 0x01 };
        private static readonly byte[] MODE_TEST        = { 0x0F, 0x00 }; // , 0x0F, 0x00 };
        private static readonly byte[] MODE_NOOP        = { 0x00, 0x00 }; // , 0x00, 0x00 };

        private SpiDevice SpiDisplay;                   // SPI device on Raspberry Pi 2
        private GpioController IoController;            // GPIO Controller on Raspberry Pi 2

        private int uCtr, rCtr;     // Counter variables for updating message.

        public MainPage()
        {
            this.InitializeComponent();
            // Initialize Scrolling Message
            InitScrollMessage(); // You can override the default message by providing some string here. 
            Initialize();        // Initialize SPI and GPIO on the current system.
        }

        /// <summary>
        /// Initialize Scroll Message.
        /// </summary>
        /// <param name="msg">Message to scroll.</param>
        private void InitScrollMessage(string msg = "KIRAN KUMAR BOLLUKONDA * ")
        {
            Message = msg;                                  // Scroll Message.
            MessageBuffer = new byte[Message.Length * 8];   // Message Buffer containing the character font.

            // Fill message buffer with the character font.
            for (int i = 0; i < Message.Length; i++)
            {
                Array.Copy(Character.Get(Message[i]),   // Source array.
                                    0,                  // Source start index.
                                    MessageBuffer,      // Destination array.
                                    (i * 8),            // Destination array index.
                                    8);                 // Length of bytes to copy.
            }
        }

        /// <summary>
        /// Initialize SPI, GPIO and LED Display
        /// </summary>
        private async void Initialize()
        {
            try
            {
                InitGpio();            
                await InitSpi();       
                await InitDisplay();
            }
            catch (Exception ex)
            {
                txtStatus.Text = "Error Occurred: \r\n" + ex.Message;
                if (ex.InnerException != null)
                {
                    txtStatus.Text += "\r\nInner Exception: " + ex.InnerException.Message;
                }
                return;
            }
        }

        /// <summary>
        /// Initialize SPI.
        /// </summary>
        /// <returns></returns>
        private async Task InitSpi()
        {
            try
            {
                var settings = new SpiConnectionSettings(SPI_CHIP_SELECT_LINE);
                settings.ClockFrequency = 100000;
                settings.Mode = SpiMode.Mode0;

                string spiAqs = SpiDevice.GetDeviceSelector(SPI_CONTROLLER_NAME);       /* Find the selector string for the SPI bus controller          */
                var devicesInfo = await DeviceInformation.FindAllAsync(spiAqs);         /* Find the SPI bus controller device with our selector string  */
                SpiDisplay = await SpiDevice.FromIdAsync(devicesInfo[0].Id, settings);  /* Create an SpiDevice with our bus controller and SPI settings */
            }
            /* If initialization fails, display the exception and stop running */
            catch (Exception ex)
            {
                throw new Exception("SPI Initialization Failed", ex);
            }
        }

        /// <summary>
        /// Initialize LED Display. Refer to the datasheet of MAX7219
        /// </summary>
        /// <returns></returns>
        private async Task InitDisplay()
        {
            SpiDisplay.Write(MODE_SCAN_LIMIT);
            await Task.Delay(10);
            SpiDisplay.Write(MODE_INTENSITY);
            await Task.Delay(10);
            SpiDisplay.Write(MODE_POWER);
            await Task.Delay(10);
            SpiDisplay.Write(MODE_TEST); // Turn on all LEDs.
            await Task.Delay(10);
        }

        /// <summary>
        /// Initiazlie GPIO.
        /// </summary>
        private void InitGpio()
        {
            IoController = GpioController.GetDefault(); 
            if (IoController == null)
            {
                throw new Exception("Unable to find GPIO on the current system.");
            }
        }

        // Use the below method to debug or test if the font character is correct.
        //private async void DisplayChar(char ch)
        //{
        //    byte[] c = Character.Get(ch);
        //    for (int i = 0; i < 8; i++)
        //    {
        //        SpiDisplay.Write(new byte[] { (byte)(i + 1), c[i] });
        //        await Task.Delay(10);
        //    }
        //}

        
        /// <summary>
        /// Update Message to LED display.
        /// </summary>
        /// <returns></returns>
        private async Task UpdateMessage()
        {
            for (uCtr = 0; uCtr < 8; uCtr++)
            {
                SendBytes[0] = (byte)(uCtr + 1); // Address
                SendBytes[1] = MessageBuffer[uCtr];
                // SpiDisplay.Write(new byte[] { (byte)(uCtr + 1), MessageBuffer[uCtr] });
                SpiDisplay.Write(SendBytes);
            }
            await Task.Delay(ScrollDelay);
        }

        /// <summary>
        /// Rotate message by one column in the message buffer.
        /// </summary>
        private void RotateDisplay()
        {
            byte tmpByte = MessageBuffer[0];
            for (rCtr = 1; rCtr < MessageBuffer.Length; rCtr++)
            {
                MessageBuffer[rCtr - 1] = MessageBuffer[rCtr];
            }
            MessageBuffer[MessageBuffer.Length - 1] = tmpByte;
        }

        /// <summary>
        /// Start scrolling forever.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void btnScroll_Click(object sender, RoutedEventArgs e)
        {
            btnScroll.IsEnabled = false;
            while (true)
            {
                await UpdateMessage();  //  Update message in LED display.
                RotateDisplay();        // Rotate message in buffer.
            }
        }
    }
}

Main Page XAML

XML
<Page
    x:Class="ScrollingDisplay.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ScrollingDisplay"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="#FF7C88FF">
        <TextBlock x:Name="txtStatus" HorizontalAlignment="Left" Margin="10,58,0,0" TextWrapping="Wrap" Text="Click Scroll to start scrolling" VerticalAlignment="Top" Width="965" Height="150"/>
        <Button x:Name="btnScroll" Content="Scroll" HorizontalAlignment="Left" Margin="437,497,0,0" VerticalAlignment="Top" Click="btnScroll_Click" Width="102"/>
    </Grid>
</Page>

Character.cs

C#
Basic font file.
/*
Author   : KIRAN KUMAR BOLLUKONDA
Email    : BK.KIRAN.KUMAR@GMAIL.COM
           BK.KIRAN.KUMAR@HOTMAIL.COM
Created  : 21-JUNE-2015

Comments : Please note that this code is written only for beginners 
           and I tried to keep as simple as possible. This code allows 
           the beginner to understand the MAX7219 via Raspberry Pi 2 
           SPI with Windows 10.
*/

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ScrollingDisplay
{
    public static class Character
    {
        public static byte[] Get(char ch)
        {
            switch (ch)
            {
                case ' ': return new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
                case '!': return new byte[] { 0x00, 0x06, 0x5F, 0x5F, 0x06, 0x00, 0x00, 0x00 };
                case '"': return new byte[] { 0x00, 0x07, 0x07, 0x00, 0x07, 0x07, 0x00, 0x00 };
                case '#': return new byte[] { 0x14, 0x7F, 0x7F, 0x14, 0x7F, 0x7F, 0x14, 0x00 };
                case '$': return new byte[] { 0x24, 0x2E, 0x6B, 0x6B, 0x3A, 0x12, 0x00, 0x00 };
                case '%': return new byte[] { 0x46, 0x66, 0x30, 0x18, 0x0C, 0x66, 0x62, 0x00 };
                case '&': return new byte[] { 0x30, 0x7A, 0x4F, 0x5D, 0x37, 0x7A, 0x48, 0x00 };
                case '\'': return new byte[] { 0x04, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 };
                case '(': return new byte[] { 0x00, 0x1C, 0x3E, 0x63, 0x41, 0x00, 0x00, 0x00 };
                case ')': return new byte[] { 0x00, 0x41, 0x63, 0x3E, 0x1C, 0x00, 0x00, 0x00 };
                case '*': return new byte[] { 0x08, 0x2A, 0x3E, 0x1C, 0x1C, 0x3E, 0x2A, 0x08 };
                case '+': return new byte[] { 0x08, 0x08, 0x3E, 0x3E, 0x08, 0x08, 0x00, 0x00 };
                case ',': return new byte[] { 0x00, 0x80, 0xE0, 0x60, 0x00, 0x00, 0x00, 0x00 };
                case '-': return new byte[] { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00 };
                case '.': return new byte[] { 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 };
                case '/': return new byte[] { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00 };
                case '0': return new byte[] { 0x3E, 0x7F, 0x71, 0x59, 0x4D, 0x7F, 0x3E, 0x00 };
                case '1': return new byte[] { 0x40, 0x42, 0x7F, 0x7F, 0x40, 0x40, 0x00, 0x00 };
                case '2': return new byte[] { 0x62, 0x73, 0x59, 0x49, 0x6F, 0x66, 0x00, 0x00 };
                case '3': return new byte[] { 0x22, 0x63, 0x49, 0x49, 0x7F, 0x36, 0x00, 0x00 };
                case '4': return new byte[] { 0x18, 0x1C, 0x16, 0x53, 0x7F, 0x7F, 0x50, 0x00 };
                case '5': return new byte[] { 0x27, 0x67, 0x45, 0x45, 0x7D, 0x39, 0x00, 0x00 };
                case '6': return new byte[] { 0x3C, 0x7E, 0x4B, 0x49, 0x79, 0x30, 0x00, 0x00 };
                case '7': return new byte[] { 0x03, 0x03, 0x71, 0x79, 0x0F, 0x07, 0x00, 0x00 };
                case '8': return new byte[] { 0x36, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00, 0x00 };
                case '9': return new byte[] { 0x06, 0x4F, 0x49, 0x69, 0x3F, 0x1E, 0x00, 0x00 };
                case ':': return new byte[] { 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 };
                case ';': return new byte[] { 0x00, 0x80, 0xE6, 0x66, 0x00, 0x00, 0x00, 0x00 };
                case '<': return new byte[] { 0x08, 0x1C, 0x36, 0x63, 0x41, 0x00, 0x00, 0x00 };
                case '=': return new byte[] { 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x00, 0x00 };
                case '>': return new byte[] { 0x00, 0x41, 0x63, 0x36, 0x1C, 0x08, 0x00, 0x00 };
                case '?': return new byte[] { 0x02, 0x03, 0x51, 0x59, 0x0F, 0x06, 0x00, 0x00 };
                case '@': return new byte[] { 0x3E, 0x7F, 0x41, 0x5D, 0x5D, 0x1F, 0x1E, 0x00 };
                case 'A': return new byte[] { 0x7C, 0x7E, 0x13, 0x13, 0x7E, 0x7C, 0x00, 0x00 };
                case 'B': return new byte[] { 0x41, 0x7F, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00 };
                case 'C': return new byte[] { 0x1C, 0x3E, 0x63, 0x41, 0x41, 0x63, 0x22, 0x00 };
                case 'D': return new byte[] { 0x41, 0x7F, 0x7F, 0x41, 0x63, 0x3E, 0x1C, 0x00 };
                case 'E': return new byte[] { 0x41, 0x7F, 0x7F, 0x49, 0x5D, 0x41, 0x63, 0x00 };
                case 'F': return new byte[] { 0x41, 0x7F, 0x7F, 0x49, 0x1D, 0x01, 0x03, 0x00 };
                case 'G': return new byte[] { 0x1C, 0x3E, 0x63, 0x41, 0x51, 0x73, 0x72, 0x00 };
                case 'H': return new byte[] { 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00 };
                case 'I': return new byte[] { 0x00, 0x41, 0x7F, 0x7F, 0x41, 0x00, 0x00, 0x00 };
                case 'J': return new byte[] { 0x30, 0x70, 0x40, 0x41, 0x7F, 0x3F, 0x01, 0x00 };
                case 'K': return new byte[] { 0x41, 0x7F, 0x7F, 0x08, 0x1C, 0x77, 0x63, 0x00 };
                case 'L': return new byte[] { 0x41, 0x7F, 0x7F, 0x41, 0x40, 0x60, 0x70, 0x00 };
                case 'M': return new byte[] { 0x7F, 0x7F, 0x0E, 0x1C, 0x0E, 0x7F, 0x7F, 0x00 };
                case 'N': return new byte[] { 0x7F, 0x7F, 0x06, 0x0C, 0x18, 0x7F, 0x7F, 0x00 };
                case 'O': return new byte[] { 0x1C, 0x3E, 0x63, 0x41, 0x63, 0x3E, 0x1C, 0x00 };
                case 'P': return new byte[] { 0x41, 0x7F, 0x7F, 0x49, 0x09, 0x0F, 0x06, 0x00 };
                case 'Q': return new byte[] { 0x1E, 0x3F, 0x21, 0x71, 0x7F, 0x5E, 0x00, 0x00 };
                case 'R': return new byte[] { 0x41, 0x7F, 0x7F, 0x09, 0x19, 0x7F, 0x66, 0x00 };
                case 'S': return new byte[] { 0x26, 0x6F, 0x4D, 0x59, 0x73, 0x32, 0x00, 0x00 };
                case 'T': return new byte[] { 0x03, 0x41, 0x7F, 0x7F, 0x41, 0x03, 0x00, 0x00 };
                case 'U': return new byte[] { 0x7F, 0x7F, 0x40, 0x40, 0x7F, 0x7F, 0x00, 0x00 };
                case 'V': return new byte[] { 0x1F, 0x3F, 0x60, 0x60, 0x3F, 0x1F, 0x00, 0x00 };
                case 'W': return new byte[] { 0x7F, 0x7F, 0x30, 0x18, 0x30, 0x7F, 0x7F, 0x00 };
                case 'X': return new byte[] { 0x43, 0x67, 0x3C, 0x18, 0x3C, 0x67, 0x43, 0x00 };
                case 'Y': return new byte[] { 0x07, 0x4F, 0x78, 0x78, 0x4F, 0x07, 0x00, 0x00 };
                case 'Z': return new byte[] { 0x47, 0x63, 0x71, 0x59, 0x4D, 0x67, 0x73, 0x00 };
                case '[': return new byte[] { 0x00, 0x7F, 0x7F, 0x41, 0x41, 0x00, 0x00, 0x00 };
                case '\\': return new byte[] { 0x01, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00 };
                case ']': return new byte[] { 0x00, 0x41, 0x41, 0x7F, 0x7F, 0x00, 0x00, 0x00 };
                case '^': return new byte[] { 0x08, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x08, 0x00 };
                case '_': return new byte[] { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 };
                case '`': return new byte[] { 0x00, 0x00, 0x03, 0x07, 0x04, 0x00, 0x00, 0x00 };
                case 'a': return new byte[] { 0x20, 0x74, 0x54, 0x54, 0x3C, 0x78, 0x40, 0x00 };
                case 'b': return new byte[] { 0x41, 0x7F, 0x3F, 0x48, 0x48, 0x78, 0x30, 0x00 };
                case 'c': return new byte[] { 0x38, 0x7C, 0x44, 0x44, 0x6C, 0x28, 0x00, 0x00 };
                case 'd': return new byte[] { 0x30, 0x78, 0x48, 0x49, 0x3F, 0x7F, 0x40, 0x00 };
                case 'e': return new byte[] { 0x38, 0x7C, 0x54, 0x54, 0x5C, 0x18, 0x00, 0x00 };
                case 'f': return new byte[] { 0x48, 0x7E, 0x7F, 0x49, 0x03, 0x02, 0x00, 0x00 };
                case 'g': return new byte[] { 0x98, 0xBC, 0xA4, 0xA4, 0xF8, 0x7C, 0x04, 0x00 };
                case 'h': return new byte[] { 0x41, 0x7F, 0x7F, 0x08, 0x04, 0x7C, 0x78, 0x00 };
                case 'i': return new byte[] { 0x00, 0x44, 0x7D, 0x7D, 0x40, 0x00, 0x00, 0x00 };
                case 'j': return new byte[] { 0x60, 0xE0, 0x80, 0x80, 0xFD, 0x7D, 0x00, 0x00 };
                case 'k': return new byte[] { 0x41, 0x7F, 0x7F, 0x10, 0x38, 0x6C, 0x44, 0x00 };
                case 'l': return new byte[] { 0x00, 0x41, 0x7F, 0x7F, 0x40, 0x00, 0x00, 0x00 };
                case 'm': return new byte[] { 0x7C, 0x7C, 0x18, 0x38, 0x1C, 0x7C, 0x78, 0x00 };
                case 'n': return new byte[] { 0x7C, 0x7C, 0x04, 0x04, 0x7C, 0x78, 0x00, 0x00 };
                case 'o': return new byte[] { 0x38, 0x7C, 0x44, 0x44, 0x7C, 0x38, 0x00, 0x00 };
                case 'p': return new byte[] { 0x84, 0xFC, 0xF8, 0xA4, 0x24, 0x3C, 0x18, 0x00 };
                case 'q': return new byte[] { 0x18, 0x3C, 0x24, 0xA4, 0xF8, 0xFC, 0x84, 0x00 };
                case 'r': return new byte[] { 0x44, 0x7C, 0x78, 0x4C, 0x04, 0x1C, 0x18, 0x00 };
                case 's': return new byte[] { 0x48, 0x5C, 0x54, 0x54, 0x74, 0x24, 0x00, 0x00 };
                case 't': return new byte[] { 0x00, 0x04, 0x3E, 0x7F, 0x44, 0x24, 0x00, 0x00 };
                case 'u': return new byte[] { 0x3C, 0x7C, 0x40, 0x40, 0x3C, 0x7C, 0x40, 0x00 };
                case 'v': return new byte[] { 0x1C, 0x3C, 0x60, 0x60, 0x3C, 0x1C, 0x00, 0x00 };
                case 'w': return new byte[] { 0x3C, 0x7C, 0x70, 0x38, 0x70, 0x7C, 0x3C, 0x00 };
                case 'x': return new byte[] { 0x44, 0x6C, 0x38, 0x10, 0x38, 0x6C, 0x44, 0x00 };
                case 'y': return new byte[] { 0x9C, 0xBC, 0xA0, 0xA0, 0xFC, 0x7C, 0x00, 0x00 };
                case 'z': return new byte[] { 0x4C, 0x64, 0x74, 0x5C, 0x4C, 0x64, 0x00, 0x00 };
                case '{': return new byte[] { 0x08, 0x08, 0x3E, 0x77, 0x41, 0x41, 0x00, 0x00 };
                case '|': return new byte[] { 0x00, 0x00, 0x00, 0x77, 0x77, 0x00, 0x00, 0x00 };
                case '}': return new byte[] { 0x41, 0x41, 0x77, 0x3E, 0x08, 0x08, 0x00, 0x00 };
                case '~': return new byte[] { 0x02, 0x03, 0x01, 0x03, 0x02, 0x03, 0x01, 0x00 };
                default:
                    return new byte[] { };
            }
        }
    }
}

Credits

KIRAN KUMAR BOLLUKONDA
1 project • 2 followers
I am working as a Technical Architect. Electronics+Computers is my hobby.

Comments