Driving the Max 7219 7-Segment Display Module from ASP.NET Core on the Raspberry Pi

In a previous blog post, I discussed how to create a dotnet core 2.0 WebApi application to run on the Raspberry Pi and trigger an LED. This post will build upon that by outlining the steps I took to connect a Max 7219 7-Segment Display Module to the Pi and extend my WebApi to display numbers on the display.
I recently purchased a Max 7219 7-Segment Display Module on eBay. It came in the mail today and I got right to work interfacing it with my Raspberry Pi. I used the excellent instructions found in this post on Plingboot:
Driving the Max7219 with the Raspberry Pi.
I had to convert the C++ code to C# to get it to work with dotnet core, but that didn't take too long. I also added a hex decoder and the ability to use multiple digits.
I hooked up my Pi using the following pin configuration:
- Pin 11 (Gpio 17) - Data (DIN)
- Pin 15 (Gpio 22) - Clock (CLK)
- Pin 16 (Gpio 23) - Load (CS)
- Pin 1 (3.3v) - 220 Ohm resistor, then to VCC
- Pin 6 (GND) - GND
I used the same Cake code and foundation WebApi code and added a new Controller for the Max7219. Here is the code:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Unosquare.RaspberryIO;
using Unosquare.RaspberryIO.Gpio;
namespace WebApi.Controllers
{
[Route("api/[controller]")]
public class Max7219Controller : Controller
{
private GpioPin DataPin = Pi.Gpio.Pin00; // GPIO 17 (WiringPi pin num 0) header pin 11
private GpioPin ClockPin = Pi.Gpio.Pin03; // GPIO 22 (WiringPi pin num 3) header pin 15
private GpioPin LoadPin = Pi.Gpio.Pin04; // GPIO 23 (WiringPi pin num 4) header pin 16
private Dictionary<char, int> Characters = new Dictionary<char, int>
{
{'-', 0b01000000},
{'0', 0b01111110},
{'1', 0b00110000},
{'2', 0b01101101},
{'3', 0b01111001},
{'4', 0b00110011},
{'5', 0b01011011},
{'6', 0b01011111},
{'7', 0b01110000},
{'8', 0b01111111},
{'9', 0b01111011},
{'A', 0b01110111},
{'B', 0b00011111},
{'C', 0b01001110},
{'D', 0b00111101},
{'E', 0b01001111},
{'F', 0b01000111},
{'O', 0b00011101},
{'R', 0b00000101},
{' ', 0b00000000}
};
private int DECODE_MODE = 0x09;
private int INTENSITY = 0x0a;
private int SCAN_LIMIT = 0x0b;
private int SHUTDOWN = 0x0c;
private int DISPLAY_TEST = 0x0f;
public Max7219Controller()
{
//We need 3 output pins to control the Max7219: Data, Clock and Load
DataPin.PinMode = GpioPinDriveMode.Output;
ClockPin.PinMode = GpioPinDriveMode.Output;
LoadPin.PinMode = GpioPinDriveMode.Output;
}
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
Max7219Send(DISPLAY_TEST, 1);
Max7219Send(DECODE_MODE, 0); // Set BCD decode mode off
Max7219Send(INTENSITY, 1); // set brightness 0 to 15
Max7219Send(SHUTDOWN, 1); // come out of shutdown mode / turn on the digits
return new string[] { "Test mode. Call Max7219/{number} to show a number on the display" };
}
[HttpGet("{value}")]
public string Get(int value)
{
/*
BCD decode mode off : data bits correspond to the segments (A-G and DP) of the seven segment display.
BCD mode on : 0 to 15 = 0 to 9, -, E, H, L, P, and ' '
*/
Max7219Send(DECODE_MODE, 1); // Set BCD decode mode on
Max7219Send(INTENSITY, 1); // set brightness 0 to 15
Max7219Send(SHUTDOWN, 1); // come out of shutdown mode / turn on the digits
Max7219Send(1,value);
/*
var pin = Pi.Gpio.Pin00;
pin.PinMode = GpioPinDriveMode.Output;
pin.Write(value);
return $"pin {pin.BcmPinNumber} set to {value}";
*/
return $"sent {value} to Max7219";
}
/*
GetFormatted() will set the value based on the first argument passed in. It will either display the hex or the decimal/integer value.
http://servername/api/max7219/hex/10
will display an "A"
*/
[HttpGet("{format}/{value}")]
public string GetFormatted(string format, int value)
{
Max7219Send(DISPLAY_TEST, 0); // Disable test mode
Max7219Send(INTENSITY, 0x01); // set brightness 0 to 15
Max7219Send(SHUTDOWN, 0x01); // come out of shutdown mode / turn on the digits
Max7219Send(SCAN_LIMIT, 0x07); // use all digits
/*
BCD decode mode off : data bits correspond to the segments (A-G and DP) of the seven segment display.
BCD mode on : 0 to 15 = 0 to 9, -, E, H, L, P, and ' '
*/
Max7219Send(DECODE_MODE, 0); // Set BCD decode mode off
var outValue = format.ToLower() == "hex" ? value.ToString("X") : value.ToString();
if (outValue.Length > 8)
{
// send error message
outValue = "ERROR";
}
// make sure we have enough characters to work with
outValue = outValue.PadLeft(8, ' ');
var values = outValue.Reverse().Take(8).ToArray();
for (int i = 0; i < 8; i++)
{
var outChar = values[i];
Max7219Send(i + 1, Characters[outChar]);
}
return $"sent {outValue.Trim()} to Max7219 in {format} format.";
}
private void Max7219Send(int regNumber, int dataOut)
{
LoadPin.Write(GpioPinValue.High); // set LOAD High to start
Send16Bits((regNumber << 8) + dataOut); // send 16 bits ( reg number + dataout )
LoadPin.Write(GpioPinValue.Low); // LOAD Low to latch
LoadPin.Write(GpioPinValue.High); // set LOAD high to finish
}
private void Send16Bits(int output)
{
for(short i = 16; i > 0; i--)
{
int mask = 1 << (i - 1); // calculate bitmask
ClockPin.Write(GpioPinValue.Low);
// send one bit on the data pin
if (Convert.ToBoolean(output & mask))
{
DataPin.Write(GpioPinValue.High);
}
else
{
DataPin.Write(GpioPinValue.Low);
}
ClockPin.Write(GpioPinValue.High);
}
}
}
}
This code will allow you to use either decimal(integer) or hexadecimal values depending on how you call the method.
http://servername/api/max7219/hex/42
will display "2A" on the display, while
http://servername/api/max7219/decimal/42
will display "42".
Also, if the output data is more than 8 characters in length, it will display "Error" (line 113).
ERROR on the Max7219 8-character 7-segment display
Update - November 29, 2017 - Here are the hexadecimal digits for versions of C# prior to 7 that do not support the binary literals:
private Dictionary<char, int>; Characters = new Dictionary<char, int>
{
{'0', 0x7e},
{'1', 0x30},
{'2', 0x6d},
{'3', 0x79},
{'4', 0x33},
{'5', 0x5b},
{'6', 0x5f},
{'7', 0x70},
{'8', 0x7f},
{'9', 0x7b},
{'A', 0x77},
{'B', 0x1f},
{'C', 0x4e},
{'D', 0x3d},
{'E', 0x4f},
{'F', 0x47},
{'O', 0x1d},
{'R', 0x05},
{'-', 0x40},
{' ', 0x00}
};