Chatting as you do about “stuff” on the Raspberry Pi, someone on the #raspberrypi IRC channel mentioned LEDs and driving them. Subsequently someone else on the raspberrypi.org forums was asking about then too… And so another little project was born…
So off to my (now usual!) online store, SKPang and a 4-digit, 7-segment display module found its way into my order. (Actually, since it has decimal points, then technically it’s an 8-segment display, but for some reason they’re always referred to as 7-segment displays!)
There are many strategies for driving 7-segment displays – I’ve chosen to use one of the lower-level ones, doing more in software than in hardware to keep the hardware design simple – so as you can see here, it’s almost as simple as it can get. We have 8 GPIO outputs going to the 8 segments of the display (white + yellow wires) and 4 GPIO outputs going to the individual digits common line. (the black and green wires) This is a common anode display, so the anodes of each of the 8 LEDs in a digit are connected together.
To drive the display, we start by setting all GPIO pins to outputs, set the 4 digit GPIO pins to logic zero and the 8 segment GPIO pins to logic 0. Now we need to light up the display one segment at a time…We do this by selecting the digit and driving it’s common connection to logic 1 (this is the common anode), then drive the segments one at a time to logic 0 to illuminate them, pause for a brief period of time (I’m using 500 μS here), then turn it off by setting it to logic 1 and moving on to the next segment. 4 digits, 8 segments per digit, 500 μS per segment means that the total scan time will be 16mS, or just over 60 times a second. That ought to be fast enough to eliminate flicker.
To increase scan frequency, we could light up more than one segment, but as they are all going via the same resistor then the segments will get dimmer the more we turn on… To prevent this, we could use 8 resistors, however then the combined current of 8 GPIO pins being fed from one GPIO pin would be too much for the Pi’s SoC. We could then use a seprate drive transistor to boost the current, but we’re trying to keep things as simple as possible here…
So, we illuminate each segment of each digit in-turn and if we do it fast enough then our persistence of vision will fool us into thinking they’re all turned on all the time.
So now we have a problem… How do we scan through the segments fast enough while at the same time allowing our program to run? And, what happens if, while scanning the segments, Linux decides to go off and so something else? (Linux is a pre-emptive, multi-tasking, multi-user operating system, after-all!)
This is when we really need a real-time operating system, and while there are some patches to the Linux kernel, they’re not standard in the Pi kernels, and even if they were, they still might not be suitable for use here – to do it “properly” really does require very tight control over the hardware, interrupts, peripherals and so on, and I am not convinced that Linux will give us all that control.
However, there are some simple things we can do to improve things – to the point of making a task like this relatively easy and do-able.
This first thing we can do is tell Linux that we want to have a higher scheduling priority and that we want to be considered for real-time scheduling too. We used the sched_setscheduler() system call to effect this. It’s not perfect – a higher priority process can interrupt your program (and if you want to maintain things like keyboard entry and allowing other programs to run, then you must allow this!) but it’s a good way to give your program a good boost.
The other thing we can do is to use Posix Threads and run what’s effectively a 2nd program concurrently with your main program, and have that 2nd program (although it’s actually a function inside your main program rather than a 2nd program) manage the LED updates.
As long as the LED display routine calls delay() every now and then, then the rest of the system will carry on which the display is kept updating. (It can use other methods to de-schedule itself, but calling a wait function is easiest – I actually call delayMicroseconds() in the display code).
All that remains is to establish a way to communicate between our main program and the display routine and I use a simple global variable here: A string array which I can write to in the main program and read from in the display routine – we don’t need any fancy locking, etc. for something as simple as this.
Even after that, setting the real-time priorities and running the separate display thread, I still see the occasional flicker or glitch on the display. It’s probably no real issue for a display, but imagine if we were driving a stepper motor… One reason I still maintain that Linux is really not the right tool to do that form of “hard” real-time control, but for LED displays? it’s OK.
Any down-sides? Well the overhead of keeping the display updated is between 15 and 20% of the CPU usage on my Pi! So it has to be said that the Raspberry Pi really isn’t the right device to be driving such a display, but it’s good to know that if we had to then we can.
And I’m sure some of the tricks in my program will be useful in other applications. You can find the software here.
Finally another of my most excellent videos:
Awesome! Can’t wait to try this!
This is great, just what I need for my latest project. However, the whole thing is written in Python! I looked for a converter but to no avail. Is there any chance you could do the same for Pi newbies but in Python??
A small problem is that I’ve never written a Python program… I’ve mainly been programming in C for the past 30+ years, and Python is currently read-only for me…
So maybe you can see it as a challenge to translate my C into Python 😉
I think the principles are there, however issues you might run into is that of speed – that C program runs fast – fast enough to keep the display lit, even though it’s only pulsing the LED segments, one at a time for 200uS! The other issue is running a concurrent thread with the main program. To keep the display lit-up, I run a thread that does the display refresh, leaving the main program to get on with its thing without having to do the display refresh. I’m sure you can start threads in Python, but I’ve really no idea where to start – sorry…
Could you also attach a drawing of the circuit? It would make the whole wiring more clear.
Thanks in advance
Will put something together next week for it.
Thank you for the detailed explanation of how to get the display up and running using Software and Linux.
Would it be possible to add some details about how to wire it up? I appreciate you have probably moved on to pastures new by now but any details would be welcome.
Thanks in advance,
I’m slowly trying to go back and improve things using Fritzing diagrams where I can, and the LED one is on my to-do list!
However for that one, it was a bit tricky working out the pins on the display – the 4 digits have a common cathode, and this is connected to the resistor (so 4 resistors) They’re connected to 4 GPIO pins and the 8 segment lines (the LED anodes) are connected to another 8 GPIO pins. The program has the exact pin assignments.
Doing it that was was rather economical in hardware terms at the expense of slightly more complex software as only one segment of the 4 digits could be lit at any one time… If I did it the other way and used 8 resistors, I could like up one whole digit’s worth of segments at once – which would be a little more efficient from the software side (and produce a slightly brighter display)
Hi Gordon, thanks for your almost instant reply (8 minutes!) which I have only just noticed today.
I am very new to electronics (I hadn’t heard of Fritzing diagrams until today) so I’ll have to dissect your reply but I’m sure I’ll get it.
Thanks again for your help!