0% found this document useful (0 votes)
171 views

Directly Driving A 7-Segment LED Display With The STM32 - Andys Workshop

7-Segment LED Display

Uploaded by

rusty123x
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
171 views

Directly Driving A 7-Segment LED Display With The STM32 - Andys Workshop

7-Segment LED Display

Uploaded by

rusty123x
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 12

08/07/2019 Directly driving a 7-segment LED display with the STM32 | Andys Workshop

hardware
stm32
stm32plus
arduino
downloads
Help
Forum

Custom Search

Home » hardware » Directly driving a 7-segment LED display with the STM32

hardware
stm32
stm32plus

Oct28

Tags
led display
stm32
stm32plus

Related Posts

Process automation: another RTD sensor board


Process automation: temperature sensing
A high current power supply built around a server voltage regulator
Nanocounter is an accurate frequency counter using an FPGA, STM32 and a bluetooth android app

Share This

Recent Posts

andybrown.me.uk/2018/10/28/7segment/ 1/12
08/07/2019 Directly driving a 7-segment LED display with the STM32 | Andys Workshop

How to use a 4k TV as a computer monitor


Directly driving a 7-segment LED display with the STM32
Process automation: another RTD sensor board
Process automation: temperature sensing
Process automation: relays and triacs

Directly driving a 7-segment LED display with the


STM32
Posted on Oct 28, 2018 in hardware, stm32, stm32plus

Seven segment LEDs are an extremely cost effective way to add a large, bright and very readable numeric
display to your project.

Displays similar to the one pictured above can be had for as little as 50 cents each on ebay in the common
heights of 0.56″, 0.36″ and 0.28″. You can choose anywhere between one and four digits in the same
package. They’re referred to as seven segment but really they’re eight because each digit comes with a little
decimal point down at the bottom right.

Configuration
The multiple digit packages utilise a wiring configuration designed to minimise the number of pins required
to drive it without having to embed any logic at all within the package.

If you count the number of segments on, for example, a three digit display you’d quickly realise that a simple
configuration that exposed each LED on its own dedicated pin would require (8 * 3) + 1 = 25 pins on the
package, of which you would need to attach 24 to your MCU to drive it. That’s far too many and is the
reason why they come in common cathode or common anode configurations.

Common cathode configuration


Let’s look at common cathode first.

andybrown.me.uk/2018/10/28/7segment/ 2/12
08/07/2019 Directly driving a 7-segment LED display with the STM32 | Andys Workshop

In this configuration there are dedicated power pins for each of the 7 segments but the same segment on each
digit are all connected together. On the other side of the LED you can see that all eight cathodes for a digit
are tied together and presented at a single pin.

If you take a moment to digest this you can see how we can light up a segment of our choosing on any digit.
For example, to light up segment A on digit two we would apply a current to pin 11 while grounding pin 9.
Pins 8 and 12 must be disconnected or otherwise prevented from allowing current flow.

To light segment A on digit 1 we would disconnect pin 9 and ground pin 12, and finally for digit 3 we would
disconnect pin 12 and ground pin 8.

Multiplexing
Now you should be getting an idea of how these displays are intended to be driven. Let’s look at a fully
worked example of how we would display the number “123”.

Firstly we would light segments E, F on digit 1 by enabling current flow through pins 1, 10 and 12. Then we
would light segments A, B, D, E, G on digit 2 by enabling pins 11, 7, 2, 1, 5 and 9. Finally we would light
segments A, B, C, D, G by enabling only pins 11, 7, 4, 2, 5 and 8.

If we repeat the above actions fast enough then the human eye will perceive all three digits to be constantly
lit even though we are switching them on and off very quickly.

Common anode configuration


This article is going to focus on the common cathode type of display but for completeness I’ll show you the
other configuration, just so you know that two incompatible types are available.

In the common anode configuration we again have separate pins for each segment and again all equal
segments on all digits are wired together but this time the cathode ends of the segment LEDs are individually
exposed and it’s the anodes that are all connected together on each digit.

andybrown.me.uk/2018/10/28/7segment/ 3/12
08/07/2019 Directly driving a 7-segment LED display with the STM32 | Andys Workshop

The multiplexed driving technique is exactly the same for common anode displays but that doesn’t mean you
could use common anode where a design calls for common cathode because you can’t, you would have to
change the design.

Driving with an MCU


There are a few options available if you have an MCU and you want to drive one of these displays. If you
have an MCU with a limited number of IO pins, such as an Arduino Uno then your best option is to use a
dedicated driver IC that will do the work for you.

The Maxim MAX7221 will drive common cathode displays of up to a whopping eight digits while requiring
just a three wire SPI interface to the host MCU. Using just one of these ICs you could have two of the
biggest four digit displays in your project at a cost of just three MCU pins. I’ve used this IC many times
before in projects that you can read about on this site. The main drawback of this IC is that it requires a 5V
supply and 5V levels at the SPI pins. This is no problem for the Arduino Uno but it means it can’t be used
with an STM32 without a level shifter.

If you’re using an MCU with a large number of GPIOs, such as most of the STM32 packages, then you have
the option of driving these displays directly for the cost of just eight resistors and three n-channel MOSFETs,
and that’s the method that we’re going to explore here today.

Direct drive circuit


Here’s the circuit diagram that I use to drive a three digit display that has blue LEDs. It’s a snapshot from a
much larger circuit that I’m working on.

andybrown.me.uk/2018/10/28/7segment/ 4/12
08/07/2019 Directly driving a 7-segment LED display with the STM32 | Andys Workshop

The choice of resistor is important because it limits the amount of current flow and sets the overall brightness
of the display. I’ll be using the STM32 F0 discovery board that hosts an STM32F051 MCU to implement
this circuit.

The first thing that I need to do is find the MCU datasheet and determine the maximum current that the
device can source and sink.

Those limits make reference to another table earlier in the datasheet that tells us the total current source and
sink for all pins.

So we have a per-pin absolute limit of 20mA and an overall device limit of 120mA. To avoid heat buildup
and allow the device to actually do other work as well we will stay far away from those limits.

Are there any other limits? Yes there are. It pays to read the entire datasheet because hidden away in a
footnote there is a very important limitation regarding GPIOs PC13 to PC15.

We will not use these pins.

andybrown.me.uk/2018/10/28/7segment/ 5/12
08/07/2019 Directly driving a 7-segment LED display with the STM32 | Andys Workshop

Resistor calculation

To calculate the resistor values we need to know the forward voltage of the LEDs in the display. This is
easily tested by using your multimeter in its diode testing mode.

The meter shows a forward voltage of about 2.6V which is average for a blue LED. Now I’ll take a wild
guess that because modern LEDs are very bright at low currents then 2mA will be sufficient current to get a
nice, readable brightness. To match the STM32 F0 Discovery board I’ll test this with a 3.0V supply. That
means a resistor of (3.0 – 2.6) / 0.002 = 200Ω is required.

LEDs don’t photograph well so please take my word for it that this is nice and bright. Can the STM32 handle
it? 2mA falls well below the per-pin limit and the worst case scenario is going to be all eight segments lit at
the same time giving a total current source of 8 * 2 = 16mA. No problem at all. The package shouldn’t even
get warm.

andybrown.me.uk/2018/10/28/7segment/ 6/12
08/07/2019 Directly driving a 7-segment LED display with the STM32 | Andys Workshop

The problem with using the 200Ω resistor that we calculated is that each digit is only lit for 33% of the time
which will make it appear three times as dim as we are expecting. Therefore we need to lower the resistor by
a factor of 3 and use a value of 68Ω instead.

This will raise the peak current seen by the LED to 6mA but the average current will still be 2mA. In the
worst-case scenario where your MCU hangs or crashes while driving all eight segments of a digit then it will
be sourcing 8 * 6mA = 48mA. This is still within safe levels and will not burn up the package.

This figure of 48mA is the reason for each digit pin being switched on or off using a MOSFET. If we were to
directly connect these pins to the MCU then we would be in danger of sinking 48mA into a single pin which
would probably permanently damage it.

The resistors, MOSFETs and jumper wires are all in place and we are ready to develop the firmware. My
project circuit specifies the Vishay SI2374DS MOSFET which is a surface mount device. For this test I am
using the through-hole BS170 instead. The choice of n-channel MOSFET is not important but for
efficiencies sake you should choose one with a low on-state drain-to-source resistance. Less than 1Ω is easily
found.

Firmware
I chose to implement the firmware as an example project within my stm32plus library. The concepts are
simple so you should have no issues porting it to whatever framework suits your project. The firmware is
implemented in a single file that you can view here on Github.

The design works by using Timer 1 to generate interrupts at a frequency of 180Hz. Each time the interrupt
fires we turn off the digit that we were last displaying and move on to setting the GPIOs necessary to light
the next digit. Therefore each digit flickers rapidly at 180/3 = 60Hz, a figure I selected to match the refresh
rate most commonly used by PC monitors. This gives a display that appears stable to the human eye.

Here’s a breakdown of the important parts of the firmware.

static const uint8_t AsciiTable[]= {


0, // SPACE
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // skip
0b11111100, // 0
0b01100000, // 1
0b11011010, // 2

andybrown.me.uk/2018/10/28/7segment/ 7/12
08/07/2019 Directly driving a 7-segment LED display with the STM32 | Andys Workshop

0b11110010, // 3
0b01100110, // 4
0b10110110, // 5
0b10111110, // 6
0b11100000, // 7
0b11111110, // 8
0b11110110 // 9
};

We want to allow the controller to display ASCII text strings so we need a table to convert ASCII to a bitmap
of which segments should light up for that character. Printable ASCII starts at 32 (space) so we start our table
there.

Each entry in the table is a single byte with one bit per lit-up segment in the format ABCDEFG0. Unused
ASCII codes are set to zero. In this example I only need the digits 0-9 so that’s all there is in there. You can
easily see how to extend this.

enum {
SEGA = 0, // PA0
SEGB = 3, // PA3
SEGC = 8, // PB8
SEGD = 4, // PB4
SEGE = 3, // PB3
SEGF = 1, // PA1
SEGG = 2, // PA2
SEGP = 5, // PB5
DIG1 = 9, // PB9
DIG2 = 2, // PB2
DIG3 = 10 // PB10
};

The pins used for each GPIO are stored in an enum for easy reference. The seemingly random assignment
matches a project I’m currently working on and also shows that the pin placement is completely flexible.

GpioA<DefaultDigitalOutputFeature<SEGA,SEGB,SEGF,SEGG>> pa;
GpioB<DefaultDigitalOutputFeature<DIG1,DIG2,DIG3,SEGC,SEGD,SEGE,SEGP>> pb;

All pins are initialised as outputs. To light a segment I will set the segment output and the corresponding
digit MOSFET gate output HIGH. Current will flow from the segment output GPIO, through the LED and
the MOSFET and the LED will light. To switch a digit off I simply switch off its MOSFET.

/*
* Initialise timer1 running from the high speed internal APB2 (APB on the F0)
* clock with an interrupt feature
*/

Timer1<
Timer1InternalClockFeature, // the timer clock source is APB2 (APB on the F0)
Timer1InterruptFeature // gain access to interrupt functionality
> timer;

/*
* Set ourselves up as a subscriber for interrupts raised by the timer class.
*/

timer.TimerInterruptEventSender.insertSubscriber(
TimerInterruptEventSourceSlot::bind(this,&Timer7SegmentTest::onInterrupt)
);

/*
* Set an up-down-timer up to tick at 80kHz with an auto-reload value of 444
* The timer will count from 0 to 444 inclusive, raise an Update interrupt and
* then go backwards back down to 0 where it'll raise another Update interrupt
andybrown.me.uk/2018/10/28/7segment/ 8/12
08/07/2019 Directly driving a 7-segment LED display with the STM32 | Andys Workshop

* and start again. Each journey from one end to the other takes 1/180 second.
*/

timer.setTimeBaseByFrequency(80000,444,TIM_CounterMode_CenterAligned3);

/*
* Enable just the Update interrupt, clearing any spurious pending flag first
*/

timer.clearPendingInterruptsFlag(TIM_IT_Update);
timer.enableInterrupts(TIM_IT_Update);

/*
* Start the timer
*/

timer.enablePeripheral();

Setting up the timer in stm32plus is a simple task of declaring it with the clock and interrupt feature,
inserting ourselves as a subscriber to the interrupts, setting the desired frequency and then enabling the
peripheral.

int value = -1;

for(;;) {

value++;

if(value>999)
value = 0;

// translate value to ascii, left justified

_display[0]=_display[1]=_display[2]=0;
StringUtil::itoa(value, const_cast<char *>( _display), 10);

// wait for 100ms

MillisecondTimer::delay(100);
}

The example code then goes into an infinite loop counting up from zero to 999 and then wrapping around
and starting again.

/*
* Subscriber callback function. This is called when the update interrupt that we've
* enabled is fired.
*/

void onInterrupt(TimerEventType tet,uint8_t /* timerNumber */) {

// verify our expectation

if(tet!=TimerEventType::EVENT_UPDATE)
return;

// turn off the last digit we displayed. This needs to be done first to avoid
// switched off segments becoming faintly visible during multiplexing

_digits[_currentDigit].reset();

// advance to the digit we just set up

if(_currentDigit>=2) {
_currentDigit=0;
_currentDigitPtr=_display;

andybrown.me.uk/2018/10/28/7segment/ 9/12
08/07/2019 Directly driving a 7-segment LED display with the STM32 | Andys Workshop

}
else
_currentDigit++;

// get the character to display at this position

uint8_t c=*_currentDigitPtr++;

// check the bottom end of the range

if(c<=' ')
c=' ';

// get the segment state bitmap from the table

uint8_t bits=AsciiTable[c-' '];

// for each bit in the map, set/reset the correct state in the segments

for(uint8_t j=0;j<7;j++) {
bool state=(bits & 0x80)!=0;
_segments[j].setState(state);
bits <<= 1;
}

// process the decimal point if there is one

if(*_currentDigitPtr=='.') {
_segments[7].set();
_currentDigitPtr++;
}
else
_segments[7].reset();

// switch on the digit we have set up

_digits[_currentDigit].set();

// we'll be back in 1/180s which means we are displaying each digit at 60Hz
}

This is the interrupt handler where the real work happens. We switch off the previous digit before setting up
the seven segments that form the next digit. We then explicitly check to see if we need to turn on the decimal
point before finally lighting up the next digit.

You’ll need to view the entire file to see the types of the member variables that are used but you should be
able to understand the logic flow from this snippet.

andybrown.me.uk/2018/10/28/7segment/ 10/12
08/07/2019 Directly driving a 7-segment LED display with the STM32 | Andys Workshop

Here’s a photograph of the display in action. It works as expected and the display is a comfortable and even
brightness with no artifacts or flickering observed.

Adapting this technique for your project


If you want to use this technique in your own project then you should follow the same procedure that I did.
To summarise:

1. Count up the pins you’ll need and verify you have enough available on your MCU.
2. Measure the forward voltage of your LED segments and experiment to find a low current level that
gives a usable brightness.
3. Calculate a resistor that limits the LED current to your selected value and then divide it by the number
of digits on your display.
4. Verify that your MCU can source the current you will draw, taking into account the worst case scenario
where the MCU hangs and a digit is permanently on with all segments lit.
5. Select an n-channel MOSFET with a low drain-source resistance (less than 1Ω is easily achievable)
and check that the on-state power dissipation is well below the maximum the package can support.

If you need any help with driving these displays then please feel free to contact me or leave a message down
below in the comments.

Recent Posts
How to use a 4k TV as a computer monitor
Directly driving a 7-segment LED display with the STM32
Process automation: another RTD sensor board
Process automation: temperature sensing
Process automation: relays and triacs
Process automation: building a process controller

Categories

arduino (32)
atmega (4)
attiny (3)
enclosure (3)
andybrown.me.uk/2018/10/28/7segment/ 11/12
08/07/2019 Directly driving a 7-segment LED display with the STM32 | Andys Workshop

fpga (5)
gps (1)
hardware (51)
laser cutting (2)
PC (7)
power (3)
process control (4)
stm32 (45)
stm32plus (34)
temperature (2)

Feed and Social Subscribe by email


Enter your email address to receive notifications of new posts
by email.
Follow @andyworkshop
Email Address

Subscribe

Copyright © Andy Brown 2012-2017. All rights reserved | Terms and conditions | Contact

andybrown.me.uk/2018/10/28/7segment/ 12/12

You might also like