My first adventure in PCB production

The finished clock

After being a member of PING for a while I got more and more interested in electronics and microcontroller programming. As I didn’t own a standalone digital clock, I decided to make one myself. We had all the necessary components: Seven-segment displays, a microcontroller and appropriate resistors.

Prototyping

Multiplexing the displays

I connected every segment on all the displays to pins on the controller, and the ground pin of each display to their own pins on the controller, as seen here:

Multiplexing the displays.
Multiplexing the displays.

To give the illusion that all the displays are on, I quickly connect the ground pin of the first display to GND, and the active segment pins to VCC, disconnect the ground pin of the first display, and then repeat the procedure for each display forever.

Programming the controller

Updating the display

I constantly update the displays:

while(1)
{
    putdigit(DISP_0, hours/10,   seconds & 32);
    putdigit(DISP_1, hours%10,   seconds & 16);
    putdigit(DISP_2, minutes/10, seconds & 8);
    putdigit(DISP_3, minutes%10, seconds & 4);
    putdigit(DISP_4, seconds/10, seconds & 2);
    putdigit(DISP_5, seconds%10, seconds & 1);
}

putdigit updates each display:

static void putdigit(uint8_t display, uint8_t digit, uint8_t dot)
{
    int i;

    PORTA = pgm_read_byte(&seg[digit]);

    if (dot)
        PORTA |= SEG_0;

    for (i=DISP_0; i<=DISP_5; ++i)
        PORTC |= (1 << i);

    PORTC &= ~(1 << display);
    _delay_ms(DELAY);
}

The fonts are stored in program memory:

static uint8_t seg[] PROGMEM = {
    SEG_1 | SEG_2 | SEG_3 | SEG_5 | SEG_6 | SEG_7,
    SEG_1 | SEG_7,
    SEG_2 | SEG_3 | SEG_4 | SEG_6 | SEG_7,
    SEG_1 | SEG_2 | SEG_4 | SEG_6 | SEG_7,
    SEG_1 | SEG_4 | SEG_5 | SEG_7,
    SEG_1 | SEG_2 | SEG_4 | SEG_5 | SEG_6,
    SEG_1 | SEG_2 | SEG_3 | SEG_4 | SEG_5 | SEG_6,
    SEG_1 | SEG_6 | SEG_7,
    SEG_1 | SEG_2 | SEG_3 | SEG_4 | SEG_5 | SEG_6 | SEG_7,
    SEG_1 | SEG_2 | SEG_4 | SEG_5 | SEG_6 | SEG_7,
    SEG_1 | SEG_3 | SEG_4 | SEG_5 | SEG_6 | SEG_7,
    SEG_1 | SEG_2 | SEG_3 | SEG_4 | SEG_5,
    SEG_2 | SEG_3 | SEG_5 | SEG_6,
    SEG_1 | SEG_2 | SEG_3 | SEG_4 | SEG_7,
    SEG_2 | SEG_3 | SEG_4 | SEG_5 | SEG_6,
    SEG_3 | SEG_4 | SEG_5 | SEG_6,
    SEG_3 | SEG_4,
    SEG_3 | SEG_4 | SEG_1 | SEG_5,
};

where SEG_? corresponds to these physical segments:

/*
 * Segments:
 *
 *   6
 * 5   7
 *   4
 * 3   1
 *   2   0
 */

Timers, buttons and interrupts

Putting the digits on the displays is well enough, but this is a clock, so we want to update the time as well. Enter the timer. I enable the internal timer on the Atmega16, and make it trigger an interrupt every second:

TCCR1B |= (1 << WGM12);
TIMSK  |= (1 << OCIE1A);
sei();
OCR1A   = 15624;
TCCR1B |= (1 << CS12)  | (1 << CS10);
MCUCR  |= (1 << ISC11) | (1 << ISC10) | (1 << ISC01) | (1 << ISC00);
GICR   |= (1 << INT1)  | (1 << INT0);

The interrupts need to be handled, so I make them increment the seconds variable:

ISR(TIMER1_COMPA_vect)
{
    ++seconds;

    if (seconds == 60)
    {
        seconds = 0;
        ++minutes;

        if (minutes == 60)
        {
            minutes = 0;
            ++hours;

            if (hours == 24)
                hours = 0;
        }
    }
}

The only thing missing now is a way to set the time. For that, I use one button for incrementing minutes, and one for incrementing hours:

ISR(INT0_vect)
{
    hours = (hours + 1) % 24;
}

ISR(INT1_vect)
{
    minutes = (minutes + 1) % 60;
}

The final prototype

I hooked a breadboard up to an STK500 developement board, and connected everything together.

Prototype.
The final prototype.

The finished product

After a night of etching and soldering, this was the result.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>