SevenSeg
SevenSeg
1
Sigvald Marholm
18.04.2017
1
Disclaimer
I do not take any responsibility for the usage of my code (or additional material
such as this document). Although I’ve tested most of it, some parts may not
be working, and some information herein may not be correct. Updates and
backward/-forward compatibility are not guaranteed (but it may still happen).
Use it at your own risk. I do provide it free of charge.
Further on, I discovered after I started developing this library that there
already was a similar library at the Arduino playground called SevSeg v.2.0. I
decided to proceed, however, since that didn’t quite have the flexibility I wanted.
Admittedly, SevSeg was first.
2
1 Introduction
SevenSeg is a flexible library for Arduino for outputting information to 7-
segment displays. The main focus is to be a library which gets you started
quickly while being flexible and cover most needs. That is, the most common
7-segment displays should be easily connected to an Arduino and information of
various kinds should be easily output to it. This library is intended for beginners
as well as more sophisticated users who just want something up and running.
It is not intended to be an extremely lightweight library. Key functionality
includes:
• Leading zero suppression (e.g. 123 is displayed as 123 rather than 0123
when using 4 digits)
• No shadow artifact
Future releases may happen, so feel free to contact me with suggested im-
provements and corrections ([email protected]). In future releases,
functionality and beauty of the code1 will likely be prioritized above for in-
stance backward compatibility.
1 Today,parts of the code are clean an beautiful, while other parts are not. In the end I
just had to finish up the project before getting tired of it. I hope to provide a cleaner code in
future versions.
3
A
F B
E C
D DP
2 Getting Started
Let’s start with a quick example to get most users up and running. We will
assume a 4-digit 7-segment common anode display. The segments in each digit
are labelled A to G in the standard way as depicted in Fig. 1. Some displays
also have an 8th segment for the decimal point (DP). This example assumes a
display without that segment (see Sec. 3.3 for how to use SevenSeg with decimal
points).
The schematics for a 4-digit common anode display is depicted in Fig. 2.
Each segment houses a LED light. All anodes (positive terminal) on a digit are
tied together into one pin which is hereinafter referred to as a digit pin (dig 1
to dig 4 on the schematic). The cathodes (negative terminals) of segments A to
G are tied together across the digits, and their pins are hereinafter referred to
as segment pins A to G.
For a common cathode display, it’s the other way around: the cathodes
are the digit pins, being connected together within each digit (hence the name
“common cathode”) while the anodes act as segment pins and are connected
across the digits. Fig. 3 shows a 4-digit common cathode display.
Nevertheless, the digit pins should be connected directly to available output
pins2 on the Arduino, while the segment pins should be connected to Arduino
output pins through appropriately dimensioned resistors (see Fig. 10). Make
sure the resistors are dimensioned such that neither the display nor the Arduino
is damaged. For dimensioning of resistors, see App. A.
Finally, an example code for making this work is shown below:
1 # include < SevenSeg .h >
2
3 SevenSeg disp (11 ,7 ,3 ,5 ,6 ,10 ,2) ;
4
5 const int numOfDigits =4;
6 int digitPins [ numOfDigits ]={12 ,9 ,8 ,13};
7
8 void setup () {
9
10 disp . setDigitPins ( numOfDigits , digitPins ) ;
11
12 }
2 Especially in the hobbyist environments I frequently see people connect resistors on the
digits pins instead of the segment pins. This is poor circuit design and I advise against it.
Please use a few extra resistors instead.
4
dig 1 dig 2 dig 3 dig 4
A B C D E F G
13
14 void loop () {
15
16 disp . write (1358) ;
17
18 }
5
using interrupt timers to free resources. See Sec. 4.4 for more details.
3 Hardware Setup
This section contain information about how to configure the SevenSeg-library
to the hardware. These member-functions are typically called in setup(){...}
except for the declaration of the SevenSeg-object which can be put in global scope
to make it accessible in loop(){..}.
Configures whether the segment and digit pins should be active high
or low. As an example, a common cathode display has segment pins
that are active high, since the LEDs light up for high segment pins.
The digit pins, however, are active low, since only digits with a low
digit pin are on. Hence, calling setCommonCathode(); is equivalent to
calling setActivePinState(HIGH,LOW);.
6
A B C D E F G
7
dig 1 dig 2 dig 3 dig 4
UC LC
A B C D E F G Colon
Figure 5: A 4-digit common anode display with a separate segment pin for colon
dig 1 dig 2 dig 3 dig 4 symb
AP UC LC
A B C D E F G
Figure 6: A 4-digit common anode display with a separate symbol digit pin for
colon and apostrophe
8
3.5 Example: 4-digit Display with Symbols
Finally, a more complex example. How to setup a 4-digit common cathode
display with decimal points, one unterminated LED for apostrophe, and one
unterminated LED for colon.
First, the cathodes of the apostrophe LED and the colon LED are tied
together and connected to Arduino I/O pin 1. This is the symbol pin. The
anode of the apostrophe LED is tied together with segment pin A and connected
to Arduino I/O pin 11 through a resistor. The anode of the colon LED is tied
together with segment pin B and connected to the Arduino I/O pin 7 through a
resistor. The other segment pins C-G are connected (through resistors) to I/O
pins 3, 5, 6, 10 and 2, respectively. The DP segment pin is connected to pin 4.
At last, the digit pins 1 to 4 (leftmost to rightmost) are connected to pins 12,
9, 8 and 13, respectively. Then, the code to initialize it would be:
1 # include < SevenSeg .h >
2
3 SevenSeg disp (11 ,7 ,3 ,5 ,6 ,10 ,2) ;
4
5 const int numOfDigits =4;
6 int digitPins [ numOfDigits ]={12 ,9 ,8 ,13};
7
8 void setup () {
9
10 disp . setDigitPins ( numOfDigits , digitPins ) ;
11 disp . s e t C o m m o n C a t h o d e () ;
12 disp . setDPPin (4) ;
13 disp . setSymbPins (1 ,7 ,7 ,11) ;
14
15 }
16
17 void loop () {
18
19 // Printing functions here
20
21 }
9
5
6 const int numOfDigits1 = 4;
7 const int numOfDigits2 = 2;
8 int digitPins1 [ numOfDigits1 ]={9 ,8 ,7 , A4 };
9 int digitPins2 [ numOfDigits2 ]={6 ,5};
10
11 void setup () {
12
13 disp1 . setDigitPins ( numOfDigits1 , digitPins1 ) ;
14 disp1 . setDigitDelay (1667) ;
15
16 disp2 . setDigitPins ( numOfDigits2 , digitPins2 ) ;
17 disp2 . setDigitDelay (1667) ;
18
19 }
20
21 void loop () {
22
23 disp1 . write (1234) ;
24 disp1 . clearDisp () ;
25
26 disp2 . write (56) ;
27 disp2 . clearDisp () ;
28
29 }
Moreover, since the two objects are unaware of each other both running in
loop(){...}, the refresh rate will not be 100 Hz which is default. The function
setRefreshRate() also do not work. When multiplexing several displays the time
to spend (in microseconds) on each digit must be manually calculated and spec-
ified using setDigitDelay(). For this example the time is calculated as follows
(assuming a refresh rate of f = 100 Hz which is usually a good number):
1 1
Tdigit = = ≈ 1667 µs (1)
nf 6 · 100 Hz
Unfortunately, it is not possible for several displays to share segment pins
while using interrupt timers.
10
Writes the number num on the display. See Sec. 2 for an example.
Supports signed integers. If the numbers are out of range they
are trimmed to the largest positive or negative number the display
can show. I.e. write(1234) will output 1234 on a 4-digit display but
99 on a 2-digit display. write(-50) will print -9 on a 2-digit display
and -50 on displays with at least three digits.
void write(long num, int point);
Similar to write(long num) except that this one writes a fixed point
decimal. The integer point tells how many digit should be treated as
decimals. Example: write(1234,2) outputs “12.34”. As for the above
function num will be trimmed if outside the range of what the display
can handle.
Same as above but uses the String object rather than null-terminated
string.
void writeClock(int mm,int ss,char c);
-0.5 is rounded. To 0 or to -1? The thing is that “rounding” is not a well-defined operation so
both could be correct. This library uses the convention of rounding -0.5 to -1 for no particular
reason.
11
Same as above but automatically uses colon if it exists, decimal point
if not or simply nothing if the display has neither colon nor decimal
points. See Sec. 3 for configuration of decimal points and colons.
void writeClock(int ss,char c);
This writes the time in the format mm:ss, mm.ss or mmss depending
on whether c is ’:’, ’.’ or ’_’. The minutes are derived from the sec-
onds. Example: writeClock(72,’:’) outputs 01:12 since 72 seconds
is 1 minute and 12 seconds.
void writeClock(int ss);
Activates the digit given by digit (and deactivates the others). I.e.
changeDigit(1) makes the leftmost digit the active one. Each time
changeDigit() is called all the segments are cleared. Hence, the fol-
lowing (erroneous) code will leave digit 2 empty:
1 writeDigit (4) ; // Activates segments in ’4 ’
2 changeDigit (2) ; // Error : clears all segments
3 delay (5) ;
12
void writeDigit(int digit);
Writes the number digit to the active digit. See changeDigit() for an
example of how to use it.
void writeDigit(char digit);
Outputs the character digit to the active digit. The following ex-
ample outputs the string “-3F”:
1 changeDigit (1) ;
2 writeDigit ( ’ - ’) ;
3 delay (5) ;
4 changeDigit (2) ;
5 writeDigit ( ’3 ’) ;
6 delay (5) ;
7 changeDigit (3) ;
8 writeDigit ( ’F ’) ;
9 delay (5) ;
Valid characters are a-z, A-Z, 0-9, minus (-), space ( ), and degree (◦ ).
Small and capital letters are displayed equally. The degree-symbol
should probably be written as an escaped character, i.e writeDigit(’
\370’).
void setDP();
Turns on the colon segment(s). See Sec. 3.4 for how colons are
implemented in hardware. If colon utilizes an additional segment
pin, this function behaves similar to setDP() in that it is cleared on
each changeDigit(). If a separate digit pin for symbols is used instead,
setColon() means that colon segment should be automatically turned
on each time the symbol pin is activated using changeDigit(’s’). To
clear it, you must call clearColon().
void clearColon();
13
void setApos();
void clearApos();
4.3 Multiplexing
The high-level printing functions (c.f. Sec. 4.1) automatically parses and mul-
tiplexes6 the data to be displayed. The functions in this subsection allows the
user to tweak parameters of the multiplexing.
void setRefreshRate(int freq);
Sets the refresh rate used for the display for high-level printing func-
tions. I.e. setRefreshRate(150) means that the whole display (all
digits) updates 150 times each second.
If you have n digits and a refresh rate of f (in Hz) the display
will spend Tdigit = 1/(nf ) seconds per digit7 . The limit for when
flickering becomes visible lies at under 50 Hz8 (or perhaps somewhat
higher if the display is vibrating or moving with respect to the ob-
server). The SevenSeg-library has a default refresh rate of 100 Hz
to ensure smooth operation by default.
void setDigitDelay(long int delay)
Rather than setting the refresh rate you can also set the quantity
Tdigit to delay (in microseconds) directly. See setRefresRate().
void setDutyCycle(int dc);
digit and so on. Doing this repetitively and at a sufficiently fast refresh rate makes it appear
as if all digits light up at the same time.
7 If you have for instance a 4-digit display with a separate symbol digit for apostrophe and
colon then n also includes the symbol pin; n = 5. See Sec. 3.4.
8 That’s why old CRT TVs has a refresh rate of 50Hz (in Europe at least).
9 Technically, it might be more correct to say that dc/n is the duty cycle rather than dc,
since that’s the percentage of the time each digit is on. I.e. if you set dc = 100% and have
n = 4 digits then, technically, each digit is on only 25% of the time, not 100%. Nevertheless, I
find the definition used herein more convenient, since its easier and maps directly to brightness
without depending on the number of digits.
14
4.4 Using Interrupt Timers
NB: Interrupt timers are currently only supported on ATmega 168 and 328
microcontrollers.
Running the printing functions in an endless loop to perform multiplexing
is not always an ideal way to do things. Outputting information to a 7-segment
display is not a computationally intensive task, but due to the delay used for
multiplexing, the microcontroller just sits there and waits for most of the time.
Sure, you can insert commands taking little time in a loop together with the
printing functions, but if they are slightly time-consuming, the display will halt
or flicker. For this purpose it is possible to use SevenSegalong with interrupt
timers. Then, you can do whatever you want inside loop(){...}, and simply
run a high-level printing function only when you want to change what’s on the
display. The microcontroller will automatically be interrupted just briefly to
update the display as needed. You can still change the refresh rate and the
duty cycle like normal, the SevenSeg-library will take care of configuring the
timers for you.
Let’s begin with an example of how to get started with timers:
1 # include < SevenSeg .h >
2
3 SevenSeg disp (11 ,7 ,3 ,5 ,6 ,10 ,2) ;
4
5 const int numOfDigits =4;
6 int digitPins [ numOfDigits ]={12 ,9 ,8 ,13};
7
8 void setup () {
9
10 disp . setDigitPins ( numOfDigits , digitPins ) ;
11
12 disp . setTimer (2) ;
13 disp . startTimer () ;
14
15 }
16
17 void loop () {
18
19 for ( int i =1; i <=10; i ++) {
20 disp . write ( i ) ;
21 delay (1000) ; // Or other time - consuming tasks
22 }
23
24 }
25
26 ISR ( T I M E R 2 _ C O M P A _ v e c t ) {
27 disp . in t er ru p tA c ti on () ;
28 }
15
Arduino platform uses the interrupt timers internally for its built-in functions
such as delay(), tone(), for serial communications, etc. delay() for instance uses
timer 0. Hence in order for delay() to work, you can not use timer 0 for SevenSeg
(or other purposes).
void setTimer(int timerID);
Tells the library that timer number timerID is to be used for mul-
tiplexing. timerID can be ’0’, ’1’ or ’2’. Timers 3, 4 and 5 are not
supported yet.
void clearTimer();
Clears the timer settings from the SevenSeg-object such that the ob-
ject can again multiplex in the default way.
void interruptAction();
Version History
v1.0 (12.07.2013) Initial version
v1.1 (02.06.2015)
• writeFloat(float) changed to write(double).
• write(String) created to support String objects (previously only char-
acter arrays was supported).
• Maximum number of digits increased from 4 to 9 (changed int to
long int several places).
• User guide now includes example of how to control multiple display
objects (also possible with v1.0 library).
• Leading zero suppression implemented (e.g. 123 is displayed as 123
and not 0123 on a 4-digit display).
• write(double,int) implemented.
v1.2 (08.06.2015) – Bug fix: Error in leading zero suppression. Numbers ’0.02’
would show as ’ . 2’ (and similar).
1.2.1 (18.04.2017) – Bug fixes. Updated library according to Arduino IDE 1.5
library specification.
16
V cc
IF n
VF
Figure 7: Forward biased diode with current limiting resistor. The order of the
diode and the resistor is insignificant.
A Current Calculations
A.1 Basic LED Current Calculation
Consider first a simple forward biased10 Light Emitting Diode (LED) as shown
in Fig. 7. LEDs are current controlled devices, and the easiest way to control the
current through an LED is by limiting it with a resistor. The way to dimension
the resistor is to first choose the forward current IF through the LED, then,
determine the voltage drop VF over the LED, and finally you compute the
resistance R of the resistor.
As an example, we’ll assume the diode 17-21USRC from Everlight. Some
selections from the datasheet are included in Fig. 8 and Fig. 9. We see that
the diode has a maximum forward current of 25mA, but according to the curve
of luminous intensity IV versus forward current, the diode should still light up
relatively well at smaller currents (for more about luminous intensity, see Avago
Application Brief D-004). Besides, we should have some margin to account for
component tolerances and round-off errors in selection of components. For now
we’ll just choose the forward current somewhere in the mid-range: IF = 10 mA.
Next, the IF vs. VF curve shows that the voltage will be approx. VF = 1.8V .
If Vcc is the output of an Arduino then Vcc = 5V (when the output is high) and
the voltage across the resistor is
ground to its cathode such that the current flows in the forward direction. Diodes prevent
currents from flowing in the reverse direction. For 7-segment displays this is utilized for
multiplexing by letting only one digit be forward biased at a time.
17
Figure 8: Absolute maximum characteristics for Everlight 17-21USRC. Note
that they may deviate from these values at temperatures other than T =
25 deg C.
18
Figure 9: IV vs. IF (left) and IF vs. VF (right) for Everlight 17-21USRC. Notice
how the dashed line is for higher currents than maximum DC-rated IF . This
region can only be utilized if multiplexing sufficiently fast.
This increased resistance will make the forward current slightly smaller, but
visually not notably different.
Sometimes datasheet lack information. A typical dirty way to do it is to
simply assume IF to 5 or 10 mA and VF = 2V , but you should make sure you’re
not overriding the absolute maximum characteristics which always should be in
the datasheet.
19
1. IF should not override the maximum value in the datasheet, typically
20-30 mA.
2. The maximum reverse voltage VR for the segment LEDs should be higher
than Arduinos I/O pin voltage Vcc = 5V since multiplexing 7-segment
displays imply reverse biasing some segments LEDs.
3. The current IF will flow through the segment pins. Hence IF should be
lower than the maximum current handled by the Arduino I/O pin, which
is 40 mA. You should probably stay well below this due to tolerances, etc.,
say, not more than 20-30 mA.
4. 7IF will flow through the digit pin while displaying the number ’8’. Hence
7IF should also be lower than what can handled by the Arduino IF pin;
40 mA or preferrably not more than 20-30 mA. Note that some displays
have more than 7 segments per digit, i.e. if there is a decimal point (see
Fig. 10) or colon (see Fig. 5). In that case, you need to multiply IF by 8
or whatever number of segments are present per digit.
As a design procedure, you could start by assuming that you want Iavg =
3 mA of average current through each segment and calculate the resistors ac-
cordingly. You make sure all the above criteria are met, and test it (without
using setDutyCycle()). From there on, experiment with different values of re-
sistors until you are satisfied. If you want to utilize the adjustable brightness
feature this will now be the maximum brightness. This is it! If you have troubles
overriding the above listed criteria, go on reading the next two subsections.
20
dig 1 dig 2
A B C D E F G DP
Figure 10: A 2-digit common anode digit with resistors (and a decimal point)
21