Tutorial_11 : Seven Segment Multiplexing Library - sudeshmoreyos/Morey_os-demo-1.0 GitHub Wiki
This library is used to implement Seven-Segment multiplexing. This library currently supports display of numbers with or without dot, numbers/digits which are reversed upside down and custom symbols. The pre-requisites for this library are SevenSegment Library and Timer Delay Driver.
1. Introduction to Seven Segment Multiplexing
As we learned in the SevenSegment Library tutorial, we need 8 digital outputs to control a single seven-segment display. There are many applications where more than one seven-segment display is required. For example, in a digital clock, we need at least four seven-segment displays to show the hour and minute digits. This means we would require 8 × 4 = 32 digital outputs. However, an Arduino Uno provides only 20 I/O pins. Therefore, it is not possible to directly implement a four-digit digital clock using the standard approach. So how do we solve this problem?
The answer is seven-segment multiplexing. By multiplexing seven-segment displays the number of pins required to drive the displays can be reduced. In this scheme we connect seven segments as shown below :

As shown above, the a pins of all seven-segment displays are connected together. Similarly, the b, c, d, e, f, g, and h pins are also connected together. However, the common pins of each seven-segment display are kept independent. The eight segment pins (a through h) are connected to eight digital output pins of the controller, while the four common pins are connected to four additional independent digital pins. As a result, instead of requiring 32 pins, we need only 8 + 4 = 12 digital pins. Effectively, we save 20 pins. The basic principle of operation is that only one seven-segment display is turned ON at a time. For example, if we wish to display 1234, the sequence would be:
- First display 1xxx, where x indicates the display is OFF.
- Then display x2xx.
- Then display xx3x.
- Finally display xxx4.
- Then repeat from 1xxx again.
This process continues continuously. The ON/OFF switching is performed so quickly that our eyes perceive all four seven-segment displays as being ON simultaneously. However, implementing seven-segment multiplexing in software can be somewhat complex. Managing multiplexing along with other application tasks can make the code even more difficult to maintain. The SevenSegmentMulti Library simplifies this process and handles the multiplexing operation for us. A typical connection with an Arduino Uno is shown below:

2. SevenSegmentMulti Library
SevenSegmentMulti Library uses SevenSegment Library and TimerDelay Driver. Kindly refer these before proceeding further in this tutorial.
To use Seven Segment Multiplexing library, you need to include following header file :
#include "lib/SevenSegmentMulti.h"
#include "TimerDelay.h"
Since SevenSegmentMulti needs TimerDelay library, hence its header must also be included. However, SevenSegment library is included by default. You can also find sources for SevenSegmentMulti library here SevenSegmentMulti Lib. This Library supports following initialization and functions :
2.1 Seven Segment Multiplexing Initialization
First step is to initialize three variables
- Seven segment structure
- Seven segment multi structure and
- Array containing common pins.
These variables holds the essential data required by SevenSegmentMulti library functions. Initialization is done as follows :
SevenSegment_t seg1 = {pin2,pin3,pin4,pin5,pin6,pin7,pin8,pin9};
SevenSegmentMulti_t multi_seg1;
mos_uint8_t seg_pins1[4] = {pinA0, pinA1, pinA2, pinA3};
In above declaration, first we have created a struct of type SevenSegment_t. We have assigned digital pins to struct which should be connected to Seven segment display in segment order A, B, C, D, E, F, G and H. Then we created a struct of type SevenSegmentMulti_t. This struct will hold all data needed by SevenSegmentMulti functions. And lastly we created an array seg_pins, which holds the digital pins connected to common pins of Seven Segments. All these variables should be defined above setup function.
2.2 SevenSegmentMulti_begin
Declaration:
void SevenSegmentMulti_begin(SevenSegmentMulti_t *seven_segment_multi_obj,
SevenSegment_t *seven_segment_obj,
mos_uint8_t *segment_common_pins,
mos_uint8_t segment_count);
This function takes four input parameters:
- A pointer to a SevenSegmentMulti_t structure.
- A pointer to a SevenSegment_t structure.
- An array containing the digital pins connected to the common pins of the seven-segment displays.
- The total number of seven-segment displays used in the multiplexed setup.
This function is typically called inside the setup() function, as shown below:
void setup(void)
{
SevenSegment_begin(&seg1, CATHODE);
SevenSegmentMulti_begin(&multi_seg1, &seg1, seg_pins1, 4);
TimerDelay.begin(TIMER_DELAY0, 1, &timer_callback);
}
As shown above, it is important to note that SevenSegment_begin() must be called before SevenSegmentMulti_begin() so that the seven_segment_obj structure is properly initialized. It is also important to note that the TimerDelay Driver is required for multiplexing. Therefore, TimerDelay.begin() must also be called inside the setup() function. The SevenSegment_begin() function initializes the seg1 structure. This structure is then passed as an input to SevenSegmentMulti_begin(), which uses it to control the multiplexed seven-segment displays. The timer_callback() function is discussed in the next section.
2.3 SevenSegmentMulti_callback
Declaration:
void SevenSegmentMulti_callback(SevenSegmentMulti_t *seven_segment_multi_obj);
This function switches the active display after every call. For example, if the number to be displayed on a four-digit seven-segment array is 1234, the display pattern after each call to SevenSegmentMulti_callback() will be:
- First call: 1xxx, where x indicates the display is OFF.
- Second call: x2xx
- Third call: xx3x
- Fourth call: xxx4
- Fifth call: 1xxx
- And the process continues.
This function must be called periodically. To achieve this, we use the TimerDelay Driver.
As discussed in the introduction section, seven-segment multiplexing works by switching ON one display at a time. This is implemented by periodically calling the SevenSegmentMulti_callback() function using the TimerDelay Driver.
In the example below, TimerDelay.begin() is used to call timer_callback() every 1 millisecond. This means the active seven-segment display is switched every 1 millisecond. The TimerDelay Driver uses TIMER_DELAY0 (i.e. TIMER0 of the ATmega328P used on the Arduino Uno). For more details about the TimerDelay Driver, please refer to TimerDelay Driver.
The code should look as follows:
void timer_callback(void)
{
SevenSegmentMulti_callback(&multi_seg1);
}
void setup(void)
{
SevenSegment_begin(&seg1, CATHODE);
SevenSegmentMulti_begin(&multi_seg1, &seg1, seg_pins1, 4);
TimerDelay.begin(TIMER_DELAY0, 1, &timer_callback);
}
Please note that SevenSegmentMulti_callback() should not be called from an independently running task for the following reasons:
-
Morey_os uses cooperative threads for multitasking and is not a real-time operating system. Therefore, macros such as DELAY_SEC() and DELAY_SEC_PRECISE() do not guarantee the exact specified delay on every execution. In contrast, the TimerDelay Driver uses the hardware timer of the controller, so the specified delay interval is guaranteed.
-
The SevenSegmentMulti_callback() function usually needs to be called at very short intervals, such as 1 millisecond. In a Morey_os task, it is generally recommended not to use delays smaller than 0.05 seconds (50 milliseconds) with DELAY_SEC() macros. Therefore, using a normal task is not suitable for implementing seven-segment multiplexing.
2.4 SevenSegmentMulti_print
Declaration:
void SevenSegmentMulti_print(SevenSegmentMulti_t *seven_segment_multi_obj,
mos_uint16_t number);
This function is used to display a number on a multiplexed seven-segment array. It takes two input parameters:
- A pointer to a SevenSegmentMulti_t structure.
- The number to be displayed.
A typical function call is shown below:
SevenSegmentMulti_print(&multi_seg1, 2345);
In the above example, the number 2345 will be displayed on the seven-segment array. Please note that the maximum number that can be displayed depends on the number of seven-segment displays configured during initialization. For example, if 4 seven-segment displays are used, numbers from 0 to 9999 can be displayed.
2.5 SevenSegmentMulti_printDot
Declaration:
void SevenSegmentMulti_printDot(SevenSegmentMulti_t *seven_segment_multi_obj,
mos_uint16_t number,
mos_uint8_t *dot_position);
This function is similar to SevenSegmentMulti_print(), with the additional capability of displaying one or more digits with a decimal point (dot). It takes three input parameters:
- A pointer to a SevenSegmentMulti_t structure.
- The number to be displayed.
- An array indicating the positions of digits that should display a dot.
A typical function call is shown below:
SevenSegmentMulti_printDot(&multi_seg1, 2345, {0,0,1,0});
In the above example, the number 2345 is displayed. The dot_position array specifies which digits should display a decimal point. A value of 1 indicates that the corresponding digit should be displayed with a dot, while a value of 0 indicates that the dot should remain OFF. Thus, in the above example, the third digit from the left (4) will be displayed with a dot. 234.5 number will be displayed, which means third digit i.e. 4 will be displayed along with a dot.
2.6 SevenSegmentMulti_printReverse
Declaration:
void SevenSegmentMulti_printReverse(SevenSegmentMulti_t *seven_segment_multi_obj,
mos_uint16_t number,
mos_uint8_t *reverse_position);
This function is similar to SevenSegmentMulti_print(), with the additional capability of displaying one or more digits in a reversed (upside-down) orientation. It takes three input parameters:
- A pointer to a SevenSegmentMulti_t structure.
- The number to be displayed.
- An array indicating the positions of digits that should be displayed in a reversed manner.
A typical function call is shown below:
SevenSegmentMulti_printReverse(&multi_seg1, 2345, {0,0,1,1});
In the above example, the number 2345 is displayed. The reverse_position array specifies which digits should be displayed in a reversed orientation. A value of 1 indicates that the corresponding digit should be displayed in a reversed manner, while a value of 0 indicates normal display. Thus, in the above example, the third and fourth digits, i.e. 4 and 5, will be displayed in a reversed orientation.
2.7 SevenSegmentMulti_printDotReverse
Declaration:
void SevenSegmentMulti_printDotReverse(SevenSegmentMulti_t *seven_segment_multi_obj,
mos_uint16_t number,
mos_uint8_t *dot_position,
mos_uint8_t *reverse_position);
This function is a combination of SevenSegmentMulti_printDot() and SevenSegmentMulti_printReverse(). It allows one or more digits to be displayed with a dot, in a reversed orientation, or both. It takes four input parameters:
- A pointer to a SevenSegmentMulti_t structure.
- The number to be displayed.
- An array indicating the positions of digits that should display a dot.
- An array indicating the positions of digits that should be displayed in a reversed manner.
A typical function call is shown below:
SevenSegmentMulti_printDotReverse(&multi_seg1, 2345, {0,1,1,0}, {0,0,1,1});
In the above example, the number 2345 is displayed. The dot_position array specifies which digits should be displayed with a dot, while the reverse_position array specifies which digits should be displayed in a reversed orientation. Thus, in the above example:
- The second and third digits (3 and 4) will be displayed with a dot.
- The third and fourth digits (4 and 5) will be displayed in a reversed orientation.
- The third digit (4) will be displayed both with a dot and in a reversed orientation.
2.8 SevenSegmentMulti_customPrint
Declaration:
void SevenSegmentMulti_customPrint(struct seven_segment_struct *seven_segment_obj,
mos_uint8_t **custom_data);
This function is used to display custom symbols or patterns on a seven-segment array. It takes two input parameters:
- A pointer to a SevenSegmentMulti_t structure.
- A two-dimensional array containing the ON/OFF state of each segment for every display.
The custom_data array must be of size segment_count × 8, where segment_count is the total number of seven-segment displays configured during initialization. Each row represents one display, while the eight values represent the states of segments A through H. A typical function call is shown below:
SevenSegmentMulti_customPrint(&multi_seg1,
{
{0,1,1,0,0,0,0,0},
{0,0,0,0,0,0,1,0},
{1,1,1,1,1,1,0,0},
{0,0,0,0,0,0,1,0}
});
In the above example, custom segment patterns are provided for four seven-segment displays. The resulting display output will be:
1-0-
This function is useful when the required display pattern cannot be generated using the standard number-printing functions and custom symbols need to be displayed instead.
3. SevenSegmentMulti lib Example
Let us now check an example code named seven-segment-multiplexing that can be found in example folder (examples/board-examples/arduino-uno/seven-segment-multilpexing). The code is as follows :
#include "morey_os.h"
#include "TimerDelay.h"
#include "lib/SevenSegmentMulti.h"
SevenSegment_t seg1 = {pin2,pin3,pin4,pin5,pin6,pin7,pin8,pin9};
SevenSegmentMulti_t multi_seg1;
mos_uint8_t seg_pins1[4] = {pinA0, pinA1, pinA2, pinA3};
void timer_callback(void)
{
SevenSegmentMulti_callback(&multi_seg1);
}
// Declare all initialization functions of controller peripherals in the setup function below
void setup(void)
{
SevenSegment_begin(&seg1, CATHODE);
SevenSegmentMulti_begin(&multi_seg1,&seg1,4,seg_pins1);
TimerDelay.begin(TIMER_DELAY0,1,&timer_callback);
}
// Delcare all processes here
TASK_CREATE(multi1,"Multi Seg-1");
// Delcare autostart processes here. Atleast one process must be autostarted;
TASK_AUTOSTART(&multi1);
TASK_RUN(multi1)
{
// Declare all variables here, please read documentation to understand
// which variables should be declared as static variables
static int i=0;
// Process starts here
BEGIN();
while(1)
{
for(i=0; i<10000; i++)
{
SevenSegmentMulti_print(&multi_seg1, i);
DELAY_SEC_PRECISE(0.01);
}
}
// process ends here
END();
}
3.1 Code Explanation :
1. Header Files and Variable Declarations
The morey_os.h header file must be included in all applications. Since this example uses the TimerDelay Driver, we also include TimerDelay.h. In addition, we include lib/SevenSegmentMulti.h to use the SevenSegmentMulti Library functions.
#include "morey_os.h"
#include "TimerDelay.h"
#include "lib/SevenSegmentMulti.h"
We need to create three variables:
- A structure seg1 of type SevenSegment_t.
- A structure multi_seg1 of type SevenSegmentMulti_t.
- An array seg_pins1 containing the common pins of the multiplexed seven-segment displays.
SevenSegment_t seg1 = {pin2,pin3,pin4,pin5,pin6,pin7,pin8,pin9};
SevenSegmentMulti_t multi_seg1;
mos_uint8_t seg_pins1[4] = {pinA0, pinA1, pinA2, pinA3};
2. Timer Callback Function
Next, we declare the timer_callback() function. This function contains the SevenSegmentMulti_callback() call, which is responsible for periodically switching the active display.
void timer_callback(void)
{
SevenSegmentMulti_callback(&multi_seg1);
}
3. Initialization in setup()
Three functions must be called inside the setup() function. The first function is SevenSegment_begin(), which initializes the segment pins and segment type. The second function is SevenSegmentMulti_begin(), which initializes the multiplexing functionality. The third function is TimerDelay.begin(), which initializes the TimerDelay Driver. In the example below, SevenSegmentMulti_begin() initializes a four-digit multiplexed display. TimerDelay.begin() uses TIMER_DELAY0 and calls timer_callback() every 1 millisecond.
void setup(void)
{
SevenSegment_begin(&seg1, CATHODE);
SevenSegmentMulti_begin(&multi_seg1, &seg1, seg_pins1, 4);
TimerDelay.begin(TIMER_DELAY0, 1, &timer_callback);
}
4. Task Creation
We create a single task named multi1. Please note that the TASK_CREATE macro takes two inputs. The first input is the task name, while the second input is a string used during debugging. We will discuss debugging in more detail in subsequent tutorials.
TASK_CREATE(multi1,"Multi Seg-1");
5. Task Autostart
Next, we configure multi1 to start automatically when the controller boots. Tasks can also be started manually from other tasks. Task creation, startup, and termination will be discussed in later tutorials. For now, we simply autostart the task at boot time.
TASK_AUTOSTART(&multi1);
6. Task Implementation
Next, we implement TASK_RUN() for the multi1 task. This task demonstrates the use of the SevenSegmentMulti_print() function. Inside TASK_RUN(multi1), a static integer variable i is declared. Please note that all variables in Morey_os must be declared as static. The reason for this requirement will be discussed in subsequent tutorials. After the BEGIN() macro, the code enters an infinite while(1) loop. Inside the loop, a for loop counts from 0 to 9999. During each iteration, the value of i is displayed on the multiplexed seven-segment display using SevenSegmentMulti_print(). The displayed value is updated every 10 milliseconds.
To create delays in Morey_os, we can use DELAY_SEC() or DELAY_SEC_PRECISE() macros. A detailed discussion of these macros is available in DELAY_SEC Macros. It is also important to note that Morey_os uses cooperative threading. Therefore, every infinite while(1) loop must contain at least one DELAY_SEC() or DELAY_SEC_PRECISE() macro. In this example, DELAY_SEC_PRECISE(0.01) is executed during every loop iteration, satisfying this requirement.
TASK_RUN(multi1)
{
static int i = 0;
BEGIN();
while(1)
{
for(i = 0; i < 10000; i++)
{
SevenSegmentMulti_print(&multi_seg1, i);
DELAY_SEC_PRECISE(0.01);
}
}
END();
}
7. Makefile Configuration
The contents of the Makefile are shown below:
PROJECT = seven-segment-multiplexing
MOREY_OS_PATH = ../../../..
BOARD = ARDUINO_UNO
include $(MOREY_OS_PATH)/Makefile.include
The PROJECT macro specifies the project name. MOREY_OS_PATH specifies the location of the Morey_os root directory. The BOARD macro should be set to ARDUINO_UNO, since this example targets the Arduino Uno board.
The last line should remain unchanged.
8. config.h Configuration
This example requires one special configuration. The TimerDelay Driver is based on interrupts. Since interrupt functions are not called directly from the application code, the compiler may not always know whether they are required. To allow Morey_os to selectively include the TimerDelay Driver, the TIMER_DELAY_ENABLE macro is used. If this macro is not defined, the TimerDelay Driver is excluded from compilation. Since this example uses the TimerDelay Driver, the TIMER_DELAY_ENABLE macro must be defined in config.h as shown below:
#ifndef CONFIG_H
#define CONFIG_H
#define COMPILER AVR_GCC
#define TIMER_DELAY_ENABLE
#endif