1
\$\begingroup\$

I am trying to do a digital clock using an Atmega8A, LCD display (2x16) and four tact switches.

Key number 1 is connected to pin PD2, number 2 to PD3, number 3 to PB6, number 4 to PB7.

When you press key 1 you can choose between hours, minutes, seconds, when you click key 2 you increment hours/minutes/seconds (chosen by button 1) by one. Key 3 is use to stop the clock.

You can modify the clock only when the flag "ableToEdit" is set to one. Key 4 is used to start the clock again. Last thing that I need to do is debouncing. I have tested this method on my other Atmega8A microcontroller and it works.

My method:

    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>

    #define KEY1 (1 << PD2)

    uint8_t key_pressed;

    int main(void)
    {
       DDRD &= ~(1 << PD2);
       DDRB |= (1 << PB0);
       DDRB |= (1 << PB1);

       PORTD |= (1 << PD2);
       PORTB |= (1 << PB0);

        _delay_ms(10);

        while(1)
        {
            if(!key_pressed && !(PIND & KEY1))
            {
                key_pressed = 1;
                PORTB ^= (1 << PB1);

            }else if( key_pressed && (PIND & KEY1)) key_pressed++;
        }

        return 0;
    }

When I tried to implement this in my clock code, it doesn't work as in the code above.

When I click a tact switch the value of hours/minutes/seconds is incremented by one and later I can't do anything; only after a long while when I click a key again is the value incremented by one and so on. Other keys work similar. I click them and only after longer moment it does someting but sometimes I cant't do anything. By saying longer moment I mean a minute or several dozens of seconds.

#include <avr/io.h>
#include <avr/interrupt.h>

#include <hd44780.c>

#include <stdio.h>
#include <stdlib.h>

volatile int modeFlag = 1;
volatile int ableToEdit = 1;
volatile int seconds = 0;
volatile int minutes = 0;
volatile int hours = 0;
uint8_t key_lock1;
uint8_t key_lock2;
uint8_t key_lock3;
uint8_t key_lock4;
char secondsBuffer[3];
char minutesBuffer[3];
char hoursBuffer[3];

void key1Pressed()
{
    if(ableToEdit == 1)
    {
        if(!key_lock1 && !(PIND & (1 << PD2)))
        {
            key_lock1 = 1;
            modeFlag++;
            if (modeFlag > 3)
            {
                modeFlag = 1;
            }
        }
        else if(key_lock1 && (PIND & (1 << PD2)))
        {
            key_lock1 ++;
            if(key_lock1 == 255)
            {
                key_lock1 = 0;
            }
;       }
    }
}
void key2Pressed()
{
    if(ableToEdit == 1)
    {
        if(!key_lock2 && !(PIND & (1 << PD3)))
        {
            key_lock2 = 1;
            switch(modeFlag)
            {
            case 1:
                if(hours == 24)
                {
                    hours = 0;
                }
                hours ++;
                break;
            case 2:
                if(minutes == 60)
                {
                    minutes = 0;
                }
                minutes ++;
                break;
            case 3:
                if(seconds == 60)
                {
                    seconds = 0;
                }
                seconds ++;
                break;
            }
        }
        else if(key_lock2 && (PIND & (1 << PD3)))
        {
            key_lock2 ++;
            if(key_lock2 == 256)
            {
                key_lock2 = 0;
            }
        }
    }
}
void key3Pressed()
{
    if(!key_lock3 && !(PINB & (1 << PB6)))
    {
        key_lock3 = 1;
        ableToEdit = 1;
        TCCR1B &= ~(1 << CS12);
        TCCR1B &= ~(1 << CS10);
    }
    else if(key_lock3 && (PINB & (1 << PB6)))
    {
        key_lock3 ++;
    }

}
void key4Pressed()
{
    if(!key_lock4 && !(PINB & (1 << PB7)))
    {
        key_lock4 = 1;
        ableToEdit = 0;
        TCCR1B |= (1 << CS12) | (1 << CS10);
    }
    else if(key_lock4 && (PIND & (1 << PB7)))
    {
        key_lock4 ++;
    }
}

ISR(TIMER1_COMPA_vect)
{
    seconds++;
    if(seconds == 60)
    {
        seconds = 0;
        minutes ++;
        if(minutes == 60)
        {
            seconds = 0;
            minutes = 0;
            hours ++;
            if(hours == 24)
            {
                hours, minutes, seconds = 0;
            }
        }
    }

}

int main(void)
{
    DDRD &= ~(1 << PD2);
    DDRD &= ~(1 << PD3);
    DDRB &= ~(1 << PB6);
    DDRB &= ~(1 << PB7);

    PORTD |= (1 << PD2);
    PORTD |= (1 << PD3);
    PORTB |= (1 << PB6);
    PORTB |= (1 << PB7);

    _delay_ms(20);

    // LCD init
    LCD_Initalize();

    // Timer init
    TCCR1B |= (1 << WGM12);
    OCR1A = 977;
    TIMSK |= (1 << OCIE1A);

    sei();

    while(1)
    {

        key1Pressed();
        key2Pressed();
        key3Pressed();
        key4Pressed();

        if(hours == 24)
        {
            hours = 0;
        }
        if(minutes == 60)
        {
            minutes = 0;
        }
        if(seconds == 60)
        {
            seconds = 0;
        }

        itoa(seconds, secondsBuffer, 10);
        itoa(minutes, minutesBuffer, 10);
        itoa(hours, hoursBuffer, 10);

        LCD_Clear();
        LCD_GoTo(0, 0);
        LCD_WriteText("Aktualna godzina");
        LCD_GoTo(4, 1);
        switch(hours)
        {
            case 24:
                LCD_WriteText("00");
                break;
            case 0:
                LCD_WriteText("00");
                break;
            case 1:
                LCD_WriteText("01");
                break;
            case 2:
                LCD_WriteText("02");
                break;
            case 3:
                LCD_WriteText("03");
                break;
            case 4:
                LCD_WriteText("04");
                break;
            case 5:
                LCD_WriteText("05");
                break;
            case 6:
                LCD_WriteText("06");
                break;
            case 7:
                LCD_WriteText("07");
                break;
            case 8:
                LCD_WriteText("08");
                break;
            case 9:
                LCD_WriteText("09");
                break;
            default:
                LCD_WriteText(hoursBuffer);
        }
        LCD_GoTo(6, 1);
        LCD_WriteText(":");
        LCD_GoTo(7, 1);
        switch(minutes)
        {
            case 0:
                LCD_WriteText("00");
                break;
            case 1:
                LCD_WriteText("01");
                break;
            case 2:
                LCD_WriteText("02");
                break;
            case 3:
                LCD_WriteText("03");
                break;
            case 4:
                LCD_WriteText("04");
                break;
            case 5:
                LCD_WriteText("05");
                break;
            case 6:
                LCD_WriteText("06");
                break;
            case 7:
                LCD_WriteText("07");
                break;
            case 8:
                LCD_WriteText("08");
                break;
            case 9:
                LCD_WriteText("09");
                break;
            default:
                LCD_WriteText(minutesBuffer);
        }
        LCD_GoTo(9, 1);
        LCD_WriteText(":");
        LCD_GoTo(10, 1);
        switch(seconds)
        {
            case 0:
                LCD_WriteText("00");
                break;
            case 1:
                LCD_WriteText("01");
                break;
            case 2:
                LCD_WriteText("02");
                break;
            case 3:
                LCD_WriteText("03");
                break;
            case 4:
                LCD_WriteText("04");
                break;
            case 5:
                LCD_WriteText("05");
                break;
            case 6:
                LCD_WriteText("06");
                break;
            case 7:
                LCD_WriteText("07");
                break;
            case 8:
                LCD_WriteText("08");
                break;
            case 9:
                LCD_WriteText("09");
                break;
            default:
                LCD_WriteText(secondsBuffer);
        }
        _delay_ms(200);
    }

    return 0;
}

I tried to change the value of the "ableToEdit" variable but it doesn't help. I also tried using INT0 and INT1 interrupts and don't see any effects. Why doesn't it work? The method in the second example is exactly the same as in the first example.

How can I fix it?

\$\endgroup\$
1
  • 3
    \$\begingroup\$ How do you have the switches wired to the MCU? It sounds like you might be missing pullup/pulldown resistors. \$\endgroup\$ – Dave Tweed Jan 1 at 12:39
0
\$\begingroup\$

Your "working" code sets "key pressed" to zero by incrementing it, luckily it's uint_8_t. If that's intentional there should be a comment explaining that. this is undefined behaviour. (so don't compile with optimisation on)

Your "non-working" code has delay_ms(200) instead of delay_ms(10) resulting in twenty times more debounce delay. but it explicitly zeros the debounce counter (which is good)

\$\endgroup\$
3
  • \$\begingroup\$ Hm, unsigned integer wraparound is perfectly defined behavior, though not always warranted (especially not in this case) \$\endgroup\$ – pipe Jan 1 at 13:57
  • \$\begingroup\$ Ok, so what should I change in second code? \$\endgroup\$ – antos Jan 1 at 15:11
  • \$\begingroup\$ either the 255 or the 200 \$\endgroup\$ – Jasen Jan 1 at 20:18
0
\$\begingroup\$

This method doesn't work if we have a lot of code after a key press detection. I have changed my code and right now it looks like this:

/*
     Digital clock
*/

#include <avr/io.h>
#include <avr/interrupt.h>

#include <hd44780.c>

#include <stdio.h>
#include <stdlib.h>

#define KEY1_UP (PIND & (1 << PD2))
#define KEY2_UP (PIND & (1 << PD3))
#define KEY3_UP (PINB & (1 << PB6))
#define KEY4_UP (PINB & (1 << PB7))

#define timer_start 196

uint8_t modeFlag = 1;
uint8_t ableToEdit = 1;
volatile uint8_t seconds = 0;
volatile uint8_t minutes = 0;
volatile uint8_t hours = 0;
char secondsBuffer[3];
char minutesBuffer[3];
char hoursBuffer[3];
volatile uint8_t key1_Status;

ISR(TIMER0_OVF_vect)
{
    TCNT0 = timer_start;
    static uint8_t lkey1 = 0;
    static uint8_t lkey2 = 0;
    static uint8_t lkey3 = 0;
    static uint8_t lkey4 = 0;

    if(!KEY1_UP)
    {
        if(!lkey1)
        {
            lkey1 = 1;
            key1_Status = lkey1;
            if(ableToEdit == 1)
            {
                if(modeFlag > 3)
                {
                    modeFlag = 1;
                }
                modeFlag++;
            }
        }
    }
    else
    {
        if(lkey1)
        {
            lkey1 = 0;
            key1_Status = lkey1;
        }
    }

    if(!KEY2_UP)
    {
        if(!lkey2)
        {
            lkey2 = 1;
            if(ableToEdit == 1)
            {
                switch(modeFlag)
                {
                case 1:
                    hours ++;
                    if(hours >= 24)
                    {
                        hours = 0;
                    }
                    break;
                case 2:
                    minutes ++;
                    if(minutes >= 60)
                    {
                        minutes = 0;
                    }
                    break;
                case 3:
                    seconds ++;
                    if(seconds >= 60)
                    {
                        seconds == 0;
                    }
                    break;
                /*default:
                    hours ++;
                    break;*/
                }
            }
        }
    }
    else
    {
        if(lkey2)
        {
            lkey2 = 0;
        }
    }

    if(!KEY3_UP)
    {
        if(!lkey3)
        {
            lkey3 = 1;
            ableToEdit = 1;
            modeFlag = 1;
            TCCR1B &= ~(1 << CS12);
            TCCR1B &= ~(1 << CS10);
        }
    }
    else
    {
        if(lkey3)
        {
            lkey3 = 0;
        }
    }

    if(!KEY4_UP)
    {
        if(!lkey4)
        {
            lkey4 = 1;
            ableToEdit = 0;
            modeFlag = 1;
            TCCR1B |= (1 << CS12) | (1 << CS10);
        }
    }
    else
    {
        if(lkey4)
        {
            lkey4 = 0;
        }

    }
}

ISR(TIMER1_COMPA_vect)
{
    seconds++;
    if(seconds >= 60)
    {
        seconds = 0;
        minutes ++;
        if(minutes >= 60)
        {
            seconds = 0;
            minutes = 0;
            hours ++;
            if(hours >= 24)
            {
                hours, minutes, seconds = 0;
            }
        }
    }

}

int main(void)
{
    DDRD &= ~(1 << PD2);
    DDRD &= ~(1 << PD3);
    DDRB &= ~(1 << PB6);
    DDRB &= ~(1 << PB7);
    DDRB |= (1 << PB0);

    PORTD |= (1 << PD2);
    PORTD |= (1 << PD3);
    PORTB |= (1 << PB6);
    PORTB |= (1 << PB7);

    _delay_ms(10);

    // LCD init
    LCD_Initalize();

    // Timer1 init
    TCCR1B |= (1 << WGM12);
    OCR1A = 977;
    TIMSK |= (1 << OCIE1A);

    TCNT0 = timer_start;
    TIMSK |= (1 << TOIE0);
    sei();
    TCCR0 |= (1 << CS02) | (1 << CS00);

    while(1)
    {
        if(hours >= 24)
        {
            hours = 0;
        }
        if(minutes >= 60)
        {
            minutes = 0;
        }
        if(seconds >= 60)
        {
            seconds = 0;
        }

        if(modeFlag > 3)
        {
            modeFlag = 1;
        }

        itoa(seconds, secondsBuffer, 10);
        itoa(minutes, minutesBuffer, 10);
        itoa(hours, hoursBuffer, 10);

        LCD_Clear();
        LCD_GoTo(0, 0);
        if(ableToEdit == 1 && modeFlag == 1)
        {
            LCD_GoTo(4, 0);
            LCD_WriteText("Godziny");
        }
        else if(ableToEdit == 1 && modeFlag == 2)
        {
            LCD_GoTo(5, 0);
            LCD_WriteText("Minuty");
        }
        else if(ableToEdit == 1 && modeFlag == 3)
        {
            LCD_GoTo(5, 0);
            LCD_WriteText("Sekundy");
        }
        else
        {
            LCD_GoTo(0, 0);
            LCD_WriteText("Aktualna godzina");
        }

        LCD_GoTo(4, 1);
        switch(hours)
        {
            case 24:
                LCD_WriteText("00");
                break;
            case 0:
                LCD_WriteText("00");
                break;
            case 1:
                LCD_WriteText("01");
                break;
            case 2:
                LCD_WriteText("02");
                break;
            case 3:
                LCD_WriteText("03");
                break;
            case 4:
                LCD_WriteText("04");
                break;
            case 5:
                LCD_WriteText("05");
                break;
            case 6:
                LCD_WriteText("06");
                break;
            case 7:
                LCD_WriteText("07");
                break;
            case 8:
                LCD_WriteText("08");
                break;
            case 9:
                LCD_WriteText("09");
                break;
            default:
                LCD_WriteText(hoursBuffer);
        }
        LCD_GoTo(6, 1);
        LCD_WriteText(":");
        LCD_GoTo(7, 1);
        switch(minutes)
        {
            case 0:
                LCD_WriteText("00");
                break;
            case 1:
                LCD_WriteText("01");
                break;
            case 2:
                LCD_WriteText("02");
                break;
            case 3:
                LCD_WriteText("03");
                break;
            case 4:
                LCD_WriteText("04");
                break;
            case 5:
                LCD_WriteText("05");
                break;
            case 6:
                LCD_WriteText("06");
                break;
            case 7:
                LCD_WriteText("07");
                break;
            case 8:
                LCD_WriteText("08");
                break;
            case 9:
                LCD_WriteText("09");
                break;
            default:
                LCD_WriteText(minutesBuffer);
        }
        LCD_GoTo(9, 1);
        LCD_WriteText(":");
        LCD_GoTo(10, 1);
        switch(seconds)
        {
            case 0:
                LCD_WriteText("00");
                break;
            case 1:
                LCD_WriteText("01");
                break;
            case 2:
                LCD_WriteText("02");
                break;
            case 3:
                LCD_WriteText("03");
                break;
            case 4:
                LCD_WriteText("04");
                break;
            case 5:
                LCD_WriteText("05");
                break;
            case 6:
                LCD_WriteText("06");
                break;
            case 7:
                LCD_WriteText("07");
                break;
            case 8:
                LCD_WriteText("08");
                break;
            case 9:
                LCD_WriteText("09");
                break;
            default:
                LCD_WriteText(secondsBuffer);
        }
        _delay_ms(200);
    }

    return 0;
}

My new method uses a timer0 of an Atmega8A microcontroller. This is more reliable and works exacly as I want.

\$\endgroup\$
2
  • \$\begingroup\$ And what this itoa function do? \$\endgroup\$ – G36 Jan 3 at 0:10
  • \$\begingroup\$ It converts an intiger value to char array. First argument is intiger value which we want to convert, second is chars array which will store a converted value and third argument (10) means a decmial value. I used this function to display hours/minutes/seconds on LCD because "LCD_WriteText" function as an argument takes char. Here is more about "itoa" function link1 , link2 \$\endgroup\$ – antos Jan 4 at 0:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.