This post is intended to be the first in a series of posts as I delve into the world of software and hardware development using Windows IoT on the Raspberry Pi.
IntroductionBefore doing anything else on the Raspberry Pi, the first task I decided to embark on is displaying output so that I could see the result of any processing being done by my applications. A seven segment display is a simple way of displaying basic numeric output, whether it be temperature, humidity or just a timer.
Seven segment displays can have one or more digits, and come with either common anode or common cathode circuitry. Common anode displays have a shared input for each segment, and each segment has it’s own output, and common cathode have a separate input for each segment of a display and a common output.
Displays that have more than one digit, will have either one anode or one cathode for each digit, but the segment inputs/output are shared. As only one digit can be displayed at any one time, to display numbers across multiple digits, each digit and it’s segments must be switched on and off very quickly giving the impression to the human eye that all required digits are displayed continuously.
The below solution was written using a 4 digit 7 segment common cathode display, but the library is intentionally written to allow for it to be used on a 7 segment common cathode display of any number of digits through the GPIO output.
Prerequisites
- Raspberry Pi running Windows IoT.
- 8 x 220 ohm resistors.
- 7 segment common cathode display. I used a 5641AS in this demo but any will work, just change the pins accordingly.
- Breadboard.
- Connector wires.
Fritzing Diagram
The display used when composing this code was a 5641AS which has 12 pins running anti-clockwise from the bottom left. The data sheets can be found on Google but the pin layout is as follows:
- E
- D
- Decimal Point
- C
- G
- Digit 4
- B
- Digit 3
- Digit 2
- F
- A
- Digit 1
The full solution so available on GitHub [link above] but can be split into 3 main sections, initialization, building the output and displaying the output.
Initialization
To set-up the displays, we need to know which output pins are going to drive the segments and which will drive the displays. Whilst the number of segments is fixed (7, or 8 including the decimal point), the number of digits available on the display may vary depending on the display used. In this example we have 4 digits so we need to provide 4 additional pin numbers and initialize them.
The segments are passed explicitly, and any remaining pins are assumed to be one for each display, passed through the params parameter. Each display linked output pin and segment output is set to high, meaning there is no voltage differential and no current will flow. Consequently, all displays will be off.
public Display(int segA, int segB, int segC, int segD, int segE, int segF, int segG, params int[] displayPins)
{
this.Displays = new GpioPin[displayPins.Length];
for (int i = 0; i < displayPins.Length; i++)
{
GpioPin pin = GpioController.GetDefault().OpenPin(displayPins[i]);
pin.Write(GpioPinValue.High);
pin.SetDriveMode(GpioPinDriveMode.Output);
this.Displays[i] = pin;
}
this.SetupOutputPin(ref this.PinSegA, segA);
this.SetupOutputPin(ref this.PinSegB, segB);
this.SetupOutputPin(ref this.PinSegC, segC);
this.SetupOutputPin(ref this.PinSegD, segD);
this.SetupOutputPin(ref this.PinSegE, segE);
this.SetupOutputPin(ref this.PinSegF, segF);
this.SetupOutputPin(ref this.PinSegG, segG);
this.cts = new CancellationTokenSource();
this.token = new CancellationToken();
}
private void SetupOutputPin(ref GpioPin pin, int pinNo)
{
pin = GpioController.GetDefault().OpenPin(pinNo);
pin.Write(GpioPinValue.High);
pin.SetDriveMode(GpioPinDriveMode.Output);
}
Building the Output
To provide an easy way for the consumer to set the value to display, we will do the work within the library. Lets say we want to display the int 1234, then we need to split the individual numbers into an array of individual digits, with the length of the array being the less than or the same as the number of digits we can display, depending if we’re to display leading zeros.
In the code below, we firstly run some checks to ensure we have a valid number to display, and enough digits to display it. We then go and break it up into its individual digits through the %10 [modulus] calculation.
public void DisplayNumber(int number, bool displayLeadingZero = true)
{
this.displayNo = number;
this.displayLeadingZero = displayLeadingZero;
if (this.displayNo < 0)
{
throw new ArgumentOutOfRangeException("Number cannot be negative");
}
int checkMax = 1;
for(int i = 0; i < this.DisplayDigits.Length; i++)
{
checkMax = checkMax * 10;
}
if(number >= checkMax)
{
throw new ArgumentException("Cannot display numbers greater than " + (checkMax - 1).ToString());
}
if (this.displayNo == 0)
{
this.Blank();
if(this.DisplayDigits.Length > 0)
{
this.DisplayDigits[0] = 0;
}
}
else
{
List<int> listOfInts = new List<int>();
while (this.displayNo > 0)
{
listOfInts.Add(this.displayNo % 10);
this.displayNo = this.displayNo / 10;
}
if (displayLeadingZero)
{
while (listOfInts.Count < this.Displays.Length)
{
listOfInts.Add(0);
}
}
else
{
while (listOfInts.Count < this.Displays.Length)
{
listOfInts.Add(10);
}
}
this.DisplayDigits = listOfInts.ToArray();
}
Displaying the OutputTo display the digits on the output display quickly enough to deceive the eye into thinking all displays are on simultaneously, we have to create a permanent loop that’s sole job is to switch each display on and off in turn. This is done through the ‘Start’ method, which simply starts a loop, and displays the digits in the previously calculated array. If the array is updated, the display automatically updates.
private void Start()
{
if (running)
{
return;
}
running = true;
Task.Factory.StartNew(() =>
{
while (!this.cts.IsCancellationRequested)
{
if (this.DisplayDigits == null)
{
this.Blank();
}
int[] arrDigs = this.DisplayDigits;
for (int i = 0; i < arrDigs.Length; i++)
{
this.SetDisplay(this.Displays[i], arrDigs[i]);
}
}
}, token);
}
The set display function turns all the displays off [set to high], then sets the segments required to display a specific digit to high/low depending on the digit and then sets the display pin to low, allowing the current to flow for just that single digit. This rotates through each digit in turn turning each one on and off. This happens faster than the eye can detect, giving the impression all digits are on simultaneously.
private void SetDisplay(GpioPin displayPin, int value)
{
this.ClearDisplay();
switch (value)
{
case 0:
this.SetHigh(new GpioPin[] { this.PinSegA, this.PinSegB, this.PinSegC, this.PinSegD, this.PinSegE, this.PinSegF });
this.SetLow(new GpioPin[] { this.PinSegG });
break;
case 1:
this.SetHigh(new GpioPin[] { this.PinSegB, this.PinSegC });
this.SetLow(new GpioPin[] { this.PinSegA, this.PinSegD, this.PinSegE, this.PinSegF, this.PinSegG });
break;
case 2:
this.SetHigh(new GpioPin[] { this.PinSegA, this.PinSegB, this.PinSegD, this.PinSegE, this.PinSegG });
this.SetLow(new GpioPin[] { this.PinSegC, this.PinSegF });
break;
case 3:
this.SetHigh(new GpioPin[] { this.PinSegA, this.PinSegB, this.PinSegC, this.PinSegD, this.PinSegG });
this.SetLow(new GpioPin[] { this.PinSegE, this.PinSegF });
break;
case 4:
this.SetHigh(new GpioPin[] { this.PinSegB, this.PinSegC, this.PinSegF, this.PinSegG });
this.SetLow(new GpioPin[] { this.PinSegA, this.PinSegD, this.PinSegE });
break;
case 5:
this.SetHigh(new GpioPin[] { this.PinSegA, this.PinSegC, this.PinSegD, this.PinSegF, this.PinSegG });
this.SetLow(new GpioPin[] { this.PinSegB, this.PinSegE });
break;
case 6:
this.SetHigh(new GpioPin[] { this.PinSegA, this.PinSegC, this.PinSegD, this.PinSegE, this.PinSegF, this.PinSegG });
this.SetLow(new GpioPin[] { this.PinSegB });
break;
case 7:
this.SetHigh(new GpioPin[] { this.PinSegA, this.PinSegB, this.PinSegC });
this.SetLow(new GpioPin[] { this.PinSegD, this.PinSegE, this.PinSegF, this.PinSegG });
break;
case 8:
this.SetHigh(new GpioPin[] { this.PinSegA, this.PinSegB, this.PinSegC, this.PinSegD, this.PinSegE, this.PinSegF, this.PinSegG });
break;
case 9:
this.SetHigh(new GpioPin[] { this.PinSegA, this.PinSegB, this.PinSegC, this.PinSegD, this.PinSegF, this.PinSegG });
this.SetLow(new GpioPin[] { this.PinSegE });
break;
case 10: // Clear Display
this.SetLow(new GpioPin[] { this.PinSegA, this.PinSegB, this.PinSegC, this.PinSegD, this.PinSegE, this.PinSegF, this.PinSegG });
break;
default:
this.SetLow(new GpioPin[] { this.PinSegA, this.PinSegB, this.PinSegC, this.PinSegD, this.PinSegE, this.PinSegF, this.PinSegG });
break;
}
this.SetLow(new GpioPin[] { displayPin });
}
The full code listing including a demo application are available via the link at the top of the page.
Video
The below is a short video of output generated by using this library.
Future DevelopmentCurrently the output is limited to whole, positive integers. Future improvements could include display of decimal or negative numbers, or some alphanumeric characters (e.g. C or F for temperature displays).
Comments