Introduction: ATtiny Seven Segment Display Timer

About: Make it yourself if you cannot buy one ;)

This instructables step by step show how to use ATtiny 8-bit Microcontroller port manipulation drive 4 seven segment display as a simple count up timer.

It is a good starting point for learning how the microcontroller and seven segment display works.

Step 1: What Is Seven Segment Display?

Seven segment display is a display that assembly from 7 long shape LED. The different LED on and off combination can represent 0-9 digit and some of alphabet letter.

In most case, the display also have a decimal point LED at the lower right corner, so the seven segment display actually contains 8 LED.

For circuit simplicity reason, 8 LEDs share same anode or cathode connector and represent this feature in their name:

  • common anode seven segment display
  • common cathode seven segment display

Ref.: https://en.wikipedia.org/wiki/Seven-segment_displa...

Step 2: Preparation

Seven Segment Display

This time I am using 4 x 0.8" common anode seven segment display.

Microcontroller

This project require a 8-bit IO port for driving the seven segment display and 4 more IO pins for switching between 4 seven segment display. I have some ATtiny861A in hand, it has 16 IO pins, it is good enough for this project.

Breadboard

A 830 tie points breadboard is good enough for this project.

Breadboard Wire

Breadboard's best friend!

Power Source

According to ATtiny datasheet, it can operate from 2.8 V to 5.5 V, this time I am using a AAA size lithium iron phosphate (LiFePO4) rechargeable battery (Nominal cell voltage 3.2 V) along with a breadboard friendly AAA battery holder.

Resistor

4 resistors for seven segment display, depends on your power source voltage. From my trial and error, 47 Ohm works for 3.3 - 5 V. (When powered by 5 V, the LED is a little bit too bright but not yet burn it out :P)

ISP Programmer

Any ISP programmer that can program ATtiny is ok, I am using USBasp this time.

Step 3: Software Preparation

Arduino IDE

Download and Install Arduino IDE latest 1.x version if not yet:

https://www.arduino.cc/en/Main/Software

SpenceKonde/ATTinyCore

Follow the install instruction to add ATtiny microcontroller support:

https://github.com/SpenceKonde/ATTinyCore/blob/mas...

Note

The Clock option 8 MHz may have some bug and the time function is not working, so I am using 1 Mhz option. it also can reduce a little bit power usage.

Ref.: https://github.com/SpenceKonde/ATTinyCore

Step 4: One Digit Trial Run

Let's start from one digit first, here is the connection summary:

ATtiny861
Pin 1 MOSI -> ISP programmer MOSI
Pin 2 MISO -> ISP programmer MISO
Pin 3 SCK -> ISP programmer SCK
Pin 5 VCC -> ISP programmer VCC
Pin 6 GND -> ISP programmer GND
Pin 9 D3/PB6 -> 47 Ohm resistor -> Seven Segment Display Common Anode Pin
Pin 10 RESET -> ISP programmer RESET
Pin 11 D13/PA7 -> Seven Segment Display E Pin
Pin 12 D12/PA6 -> Seven Segment Display D Pin
Pin 13 D11/PA5 -> Seven Segment Display C Pin
Pin 14 D10/PA4 -> Seven Segment Display H Pin
Pin 17 D14/PA3 -> Seven Segment Display G Pin
Pin 18 D2/PA2 -> Seven Segment Display F Pin
Pin 19 D1/PA1 -> Seven Segment Display A Pin
Pin 20 D0/PA0 -> Seven Segment Display B Pin

Step 5: GPIO Style Start From Zero

Let's start from display a zero digit on the seven segment display first, copy the follow code to Arduino IDE and then press upload:

void setup() {
// init GPIO
pinMode(3, OUTPUT); // common anode
pinMode(1, OUTPUT); // segment A
pinMode(0, OUTPUT); // segment B
pinMode(11, OUTPUT); // segment C
pinMode(12, OUTPUT); // segment D
pinMode(13, OUTPUT); // segment E
pinMode(2, OUTPUT); // segment F
pinMode(14, OUTPUT); // segment G
pinMode(10, OUTPUT); // segment digit point
}
void loop() {
// display digit zero
digitalWrite(3, HIGH); // enable common anode
digitalWrite(1, LOW); // segment A on
digitalWrite(0, LOW); // segment B on
digitalWrite(11, LOW); // segment C on
digitalWrite(12, LOW); // segment D on
digitalWrite(13, LOW); // segment E on
digitalWrite(2, LOW); // segment F on
digitalWrite(14, HIGH); // segment G off
digitalWrite(10, HIGH); // segment digit point off
}

Note

  • seven segment display common anode pin always require positive power, set the pin HIGH to enable the display
  • in contrast, segment pin require negative power to turn the segment LED on. i.e. power flow from common anode pin to segment pin to turn on the LED

Ref.: https://www.arduino.cc/en/Tutorial/DigitalPins

Step 6: Port Manipulation Style Start From Zero

GPIO style code is simple and straight forward, but it only can manipulate 1 pin for each function call. The code will become too long and hard to read for further development. There are 2 methods to overcome this:

  • refactor common code to function call
  • use port manipulation

Port manipulation allow for lower-level and faster manipulation of the i/o pins of the microcontroller, let's try it first.

ATtiny861 have 2 port IO, port A and port B.

Firstly, use Data Direction Register, DDRA and DDRB, to set the IO pins to output: (0: input; 1: output)

void setup() {
// init GPIO
DDRA = B11111111; // set port A all pins as output
DDRB = B01000000; // set common anode pins as output
}

And then, use Data Register, PORTA and PORTB, to set the IO value: (0: LOW, 1: HIGH)

void loop() {
// display digit zero
PORTB = B01000000; // enable common anode
PORTA = B00011000; // set segment A, B, C, D, E, F on; G, H off
}

Note: the value begin with a letter 'B' and follow with 8 digits represent a 8 bit binary number, it has a few variation, e.g. the follow line all have the same effect:

DDRB = B00011000;
DDRB = 0b00011000;
DDRB = 0B00011000;

Ref.: https://www.arduino.cc/en/Reference/PortManipulat...

Step 7: GPIO Style VS Port Manipulation Style

Port Manipulation direct operate 8 pins at the same port instead of 8 pin operate functions, so:

  • it reduce the code quantity, so it is easier to maintain when the logic growth
  • it run faster (1 register operation instead of 8 Library function), you will find the different if adding more digit later on
  • reduce the program size (836 bytes vs 330 bytes in previous example), it is very important while ATtiny861 microcontroller only have 8192 bytes to store the program

Port manipulation require bitwise calculation, it is a little bit difficult for a beginner but it has many advantage if you need to manipulate many pins at the same time, such as driving the seven segment display. And also we can define some constant to overcome the difficulties.

Step 8: Define Mapping Constant

For ease of code readability, first define all segment and common anode pins mapping constant:

#define SEG_A      B11111101 // PA1
#define SEG_B B11111110 // PA0
#define SEG_C B11011111 // PA5
#define SEG_D B10111111 // PA6
#define SEG_E B01111111 // PA7
#define SEG_F B11111011 // PA2
#define SEG_G B11110111 // PA3
#define SEG_H B11101111 // PA4

#define DIGIT_1 B01000000 // PB6
#define DIGIT_2 B00100000 // PB5
#define DIGIT_3 B00010000 // PB4
#define DIGIT_4 B00001000 // PB3

#define ALL_HIGH B11111111

And then define the digit array: (it also called font constant for seven segment display)

<p>uint8_t digit[] = {<br>  SEG_A & SEG_B & SEG_C & SEG_D & SEG_E & SEG_F, // 0
SEG_B & SEG_C, // 1
SEG_A & SEG_B & SEG_D & SEG_E & SEG_G, // 2
SEG_A & SEG_B & SEG_C & SEG_D & SEG_G, // 3
SEG_B & SEG_C & SEG_F & SEG_G, // 4
SEG_A & SEG_C & SEG_D & SEG_F & SEG_G, // 5
SEG_A & SEG_C & SEG_D & SEG_E & SEG_F & SEG_G, // 6
SEG_A & SEG_B & SEG_C, // 7
SEG_A & SEG_B & SEG_C & SEG_D & SEG_E & SEG_F & SEG_G, // 8
SEG_A & SEG_B & SEG_C & SEG_D & SEG_F & SEG_G // 9
};

Step 9: Show Digit With Constant

You can now use the constant to display the current second value:

void loop() {
PORTA = ALL_HIGH; // reset PORT A all pins to HIGH
PORTB = DIGIT_1; // switch to first digit
PORTA = digit[millis() / 1000 % 10]; // show 0-9
delay(1);
}

ATtiny861 and 7 segment pic.twitter.com/XURSPR7O7h

— 陳亮手痕定律 (@moononournation) May 16, 2018

Step 10: Connect All 4 Digits

After one digit trial run, it's time to connect all 4 digits, here is the connection summary:

ATtiny861
Pin 1 MOSI -> ISP programmer MOSI
Pin 2 MISO -> ISP programmer MISO
Pin 3 SCK -> ISP programmer SCK
Pin 4 D6/PB3 -> 47 Ohm resistor -> 4th digit Seven Segment Display Common Anode Pin
Pin 5 VCC -> ISP programmer VCC
Pin 6 GND -> ISP programmer GND
Pin 7 D5/PB4 -> 47 Ohm resistor -> 3rd digit Seven Segment Display Common Anode Pin
Pin 8 D4/PB5 -> 47 Ohm resistor -> 2nd digit Seven Segment Display Common Anode Pin
Pin 9 D3/PB6 -> 47 Ohm resistor -> 1st digit Seven Segment Display Common Anode Pin
Pin 10 RESET -> ISP programmer RESET
Pin 11 D13/PA7 -> All 4 digits Seven Segment Display E Pin
Pin 12 D12/PA6 -> All 4 digits Seven Segment Display D Pin
Pin 13 D11/PA5 -> All 4 digits Seven Segment Display C Pin
Pin 14 D10/PA4 -> All 4 digits Seven Segment Display H Pin
Pin 17 D14/PA3 -> All 4 digits Seven Segment Display G Pin
Pin 18 D2/PA2 -> All 4 digits Seven Segment Display F Pin
Pin 19 D1/PA1 -> All 4 digits Seven Segment Display A Pin
Pin 20 D0/PA0 -> All 4 digits Seven Segment Display B Pin

Step 11: Download Source Code

Download the full source code at GitHub, compile and upload to the microcontroller:

https://github.com/moononournation/ATtinySevenSegm...

Step 12: How Digit Shared Pins Work?

4 Seven Segment Digit share same 8 IO pins but can display different value, How does it works?

Actually 4 digits is not turned on at the same time, the LEDs in each digit is switching to turn on one by one. However, the switching process is fast enough (1 ms) so you think it is turned on altogether. This optical illusion is called Persistence of vision.

If you set a longer delay between switching logic, say 10 ms or 100 ms, you can visualise the switching process:

delay(100);

Ref.: https://en.wikipedia.org/wiki/Persistence_of_visi...

Step 13: Install Battery

After all program test, you can remove the ISP connection and install the battery holder and power switch to the breadboard.

Step 14: What's Next?

  • add colon LED between minutes and seconds digits
  • add a reset button for count up again
  • add more digits
  • add count down feature
  • add buzzer alert count down finished
  • add RTC chip to make it as a clock

Step 15: Update: Add Colon LED

I have updated the source code for blinking colon LED, here is the addition connection summary:

ATtiny861
Pin 1 MOSI (PB0) -> 47 Ohm resistor -> 2 LED +ve
2 LED -ve -> GND