A countdown counter with the MAX7221 and a seven-segment display

3, 2, 1 … Go!

© Lead Image © bluedarkat, 123RF.com

© Lead Image © bluedarkat, 123RF.com

Article from Issue 210/2018

Build a countdown counter with a Raspberry Pi and some electronics, and you can count down the time to any event.

Days until Christmas, time until retirement: We count the days to many events in life. A countdown counter can help make the time to wait fly by and increase anticipation. In this article, I show you how to use the Raspberry Pi as a control unit to build a chic countdown counter based on an LED segment display.

To ensure that the countdown can be seen from a distance easily, a large seven-segment Kingbright SC08-11SRWA [1] display (20.32mm/0.8 inch high) is used to display the remaining days to an event. The LEDs use a common cathode, which makes the display compatible with a MAX7221 display driver.

The combination of parts used here requires the use of a soldering iron. Completely assembled modules that match the capabilities of the MAX7221 are available, but they have considerably smaller displays of (typically) eight digits. Four digits should be enough for a day countdown (i.e., 9,999 days or more than 27 years). If you really want to count down for a longer period of time, simply add an additional segment to the setup.

MAX7221 Display Driver

In the MAX7221 display driver, an external resistor controls the current for the LEDs. I'll shoot for 33kohm, which results in a current of just under 20mA for one segment of the display. Otherwise, the MAX7221 does not require external wiring. Internally, the display stores the information to be displayed in 8x8-bit RAM.

Table 1 is an overview of the MAX7221 registers and their functions; a data sheet [2] provides further information. The MAX7221 is addressed through the SPI interface. Many microcontrollers are suitable for this purpose – I concentrate on the Raspberry Pi.

Table 1

MAX7221: Register Assignment






Functionless. In the case of several SPI blocks on one bus, it is used to address only one block. All others receive a no-op.



Digit 0 of the display.



Digit 1 of the display.



Digit 2 of the display.



Digit 3 of the display.



Digit 4 of the display.



Digit 5 of the display.



Digit 6 of the display.



Digit 7 of the display.


Decode mode

Each bit corresponds to one digit of the display (bit 0 = digit 0, etc.). If it is set, the digit works as a segment display; the lower 4 bits of the digit registers are then interpreted as a BCD number. If it is deleted, each segment of the digit can be controlled as a single LED.



The lower 4 bits of the register control the brightness of the LEDs.


Scan limit

The lower 3 bits of the register control which digits of the display are used (0x00 = only digit 0; 0x01 = digits 0 and 1; etc.).



Bit 0 = 1: Normal mode, all displays and functions active. Bit 0 = 0: Shutdown mode, all displays off, no response to commands.


Display test

Lights all LEDs.

Circuit Diagram

In the countdown counter circuit diagram (Figure 1), the MAX7221 is the central component. Connected to the four seven-segment displays, it only requires one external component: resistor R1. The complete circuit operates at 3.3V, which is supplied by a type LD1117v33 voltage regulator by STMicroelectronics (input voltage range = 4.3-15V).

Figure 1: The Fritzing circuit diagram of the countdown project.

The setup is thus quite independent of the voltage source (e.g., a USB power supply, car battery, etc.). Two capacitors buffer fluctuations in the input voltage and counteract the oscillation of the voltage regulator. A header plug joins the connectors of the SPI interface to the Raspberry Pi. Figure 2 shows the complete setup; all components are listed in Table 2.

Table 2





Price/Unit (EUR)


SC08-11SRWA 7-segment display




MAX7221CNG 8-digit LED display driver




PCB prototype 8x12




Various small parts

Craft kit


Total cost ~EUR20

Figure 2: (Top) Countdown counter on a Euroboard; (bottom) Raspberry Pi.


The experimental setup is based on a Raspberry Pi 1 with a current Raspbian image, but the steps described here also work with any other Raspberry Pi model.

First, activate the SPI interface with raspi-config in 5 Interfacing Options | P4 SPI. After the next reboot, the /dev/spidev0.0 Raspberry Pi device directory contains the SPI device, which can be used as a character-oriented device under Unix.

Listing 1 shows the counter.c example program, which loads all the libraries used and defines the necessary variables. The write_register() function (lines 14-17) encapsulates the write() C function and arranges the parameters accordingly. The main() function (line 19 to end of code) establishes the connection to the SPI device and sets the speed of the SPI interface. It then initializes the MAX7221 to use digits 0 to 3 in decoder mode and sets the brightness to the maximum.

Listing 1


01 #include <fcntl.h>
02 #include <sys/ioctl.h>
03 #include <linux/spi/spidev.h>
04 #include <inttypes.h>
05 #include <stdio.h>
06 #include <time.h>
08 static const char *device = "/dev/spidev0.0";
09 static uint32_t speed = 500000;
10 int handle, diff;
11 time_t ts_now, ts_target;
12 struct tm target;
14 void write_register(int handle, uint8_t regi, uint8_t value) {
15   uint8_t tx[] = { regi, value };
16   write(handle,tx,2);
17 }
19 int main() {
20   if ((handle = open(device, O_RDWR)) < 0) {
21     perror("SPI Device Error");
22     return 1;
23   }
24   if (ioctl(handle, SPI_IOC_RD_MAX_SPEED_HZ, &speed)<0) {
25     perror("SPI Speed Error");
26     return 1;
27   }
28   // MAX7221-Setup
29   write_register(handle,0x0C,0x01);
30   write_register(handle,0x09,0xFF);
31   write_register(handle,0x0F,0x00);
32   write_register(handle,0x0B,0x03);
33   write_register(handle,0x0A,0x0F);
34   ts_now = time(NULL);
35   target.tm_mday=1;
36   target.tm_mon=4;    // Month 1 (Jan = 0)
37   target.tm_year=119; // Year - 1900
38   target.tm_sec=59;
39   target.tm_min=59;
40   target.tm_hour=23;
41   ts_target=mktime(&target);
42   diff=(ts_target-ts_now)/(60*60*24);
43   write_register(handle,0x04,(diff%10000)/1000);
44   write_register(handle,0x03,(diff%1000)/100);
45   write_register(handle,0x02,(diff%100)/10);
46   write_register(handle,0x01,(diff%10));
47 }

The target date structure (line 12) generates the timestamp of the target date. This structure has some peculiar characteristics that can cause a great deal of trouble: Days count from 1 and months from 0, and 1900 must be deducted from the year. The values for time of day can be written one to one into the structure.

The remaining lines of the program calculate the difference between the current and target dates and write it to the corresponding memory locations of the MAX7221. By transferring the start of the program to a cronjob (see the "Cron" box), you can start the program once a day, calculate the current display value, and transfer it to the display driver. Save the program and compile it with gcc (GNU C compiler):

$ gcc counter.c -o counter
$ ./counter

The last line runs the program.


The Unix cron service allows commands and scripts to be executed automatically at certain times. The crontab command-line tool is used for configuration. Use crontab -l to see the current settings for the logged in user. The -e switch puts Crontab in edit mode. An entry contains, separated by white space, the specifications for minute, hour, day of the month, month, and weekday, as well as the command to be executed at the specified time.

The first five parameters control execution time. Numbers stand for times or dates and an asterisk for any given minute, hour, or day. If necessary, you can also use a backslash to define multiple values (e.g., */2 for every second minute). As far as timing is concerned, it is important to ensure that one call can be processed before the next initiates; otherwise, the system will become overloaded over time.

The following are some typical cron examples:

* * * * *    /home/user/script    # every minute
0 */2 * * *  /home/user/script    # at the start of every other hour
0 0 * * *    /home/user/script    # every day at midnight
30 18 * * *  /home/user/script    # every day at 6:30pm
* * 1 * *    /home/user/script    # every minute on the first day of the month

Further detailed information can be found on the cron man page. Useful pages on the web, such as at Corntab [3], can help you to compose cron lines. You need to make sure that cron executes scripts with the rights of its own user account but does not use environment variables. To do this, specify all paths in full and do not use aliases.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • ARM64 Assembly and GPIO

    Reading, writing, and arithmetic with the Raspberry Pi in ARM64 assembly language.

  • GPIO on Linux Devices

    The general purpose input/output interface is not just for small-board computers anymore: You can use GPIO on your Linux desktop or laptop, too, through the USB port.

  • FreeSWITCH

    FreeSWITCH is a powerful and versatile telephony platform that can scale from a softphone to a PBX and even to a carrier-class softswitch.

  • Digital IC Simulation on Linux

    Designing field-programmable gate arrays is only half the job: The hardest part is the simulation, but Linux is the best place to tackle certain challenges.

  • Escape Room Puzzle

    A digital puzzle presents a challenge for young people in an escape room.

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More