1. Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.
    Dismiss Notice

Binary Clock with ATMEGA16

Discussion in 'AVR' started by cosmonavt, Jan 20, 2012.

  1. cosmonavt

    cosmonavt Member

    Joined:
    Nov 13, 2011
    Messages:
    87
    Likes:
    0
    Hello guys, I am making a binary clock with Atmega16. Here's how it works:

    When it powers on, it sends you to the configureMode function with variable sec, min and hour zero. You can configure the clock with the following buttons:

    PINA0 increment hours by 1
    PINA1 increment minutes by 10
    PINA2 increment minutes by 1
    PINA3 leave conf mode and send these variables (hour, min and sec) as arguments to the displayTime_BCD function.

    Now this function counts time and each second, calls the updateSec funct to update the ports in binary according to the current time in decimal. The updateX functions are working OK and so is the baseConv function (since the LEDs blink until 59 seconds is reached). Then the clock just hangs there and does nothing. The problem is in displayTime_BCD function but I don't know what it is.

    P.S. Any moment pressing the PINA3 button will send the clock to configuration mode which will be enabled after 2000 milliseconds. Then you can configure the clock again and press PINA3 to pass the hours and minutes to displayTime_BCD funct.

    Code (text):
    /*
     * BinaryClock.c
     *
     * Created: 12/30/2011 10:50:06 PM
     */


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

    int baseConv(int i){
        int binaryNum = 0, multiplier = 1;
        for (int n = 0; n < 4; ++n){
            binaryNum += ( i % 2 ) * multiplier;
            i /= 2;
            multiplier *= 10;
        }
        return binaryNum;
    }

    void updateSec(int sec){
        int lowerNib = sec % 10; //second column
        lowerNib = baseConv(lowerNib);
       
        int upperNib = sec / 10; //first column
        upperNib = baseConv(upperNib);
       
        PORTB = (((upperNib/1000) % 10) << 7)|(((upperNib/100) % 10) << 6)|(((upperNib/10) % 10) << 5)|((upperNib % 10) << 4)|(((lowerNib/1000) % 10) << 3)|(((lowerNib/100) % 10) << 2)|(((lowerNib/10) % 10) << 1)|( (lowerNib % 10) << 0);
    }

    void updateMin(int min){
        int lowerNib = min % 10; //second column
        lowerNib = baseConv(lowerNib);
       
        int upperNib = min / 10; //first column
        upperNib = baseConv(upperNib);
       
        PORTC = (((upperNib/1000) % 10) << 7)|(((upperNib/100) % 10) << 6)|(((upperNib/10) % 10) << 5)|((upperNib % 10) << 4)|(((lowerNib/1000) % 10) << 3)|(((lowerNib/100) % 10) << 2)|(((lowerNib/10) % 10) << 1)|( (lowerNib % 10) << 0);
    }

    void updateHour(int hour){
        int lowerNib = hour % 10; //second column
        lowerNib = baseConv(lowerNib);
       
        int upperNib = hour / 10; //first column
        upperNib = baseConv(upperNib);
       
        PORTD = (((upperNib/1000) % 10) << 7)|(((upperNib/100) % 10) << 6)|(((upperNib/10) % 10) << 5)|((upperNib % 10) << 4)|(((lowerNib/1000) % 10) << 3)|(((lowerNib/100) % 10) << 2)|(((lowerNib/10) % 10) << 1)|( (lowerNib % 10) << 0);
    }

    void displayTime_BCD(int _hour, int _min, int _sec){
        while(1)
        {
            for (; _hour < 24; ++_hour )
            {
                updateHour(_hour);
                for (; _min < 60; ++_min )
                {
                    updateMin(_min);
                    for (; _sec < 60; ++_sec )
                    {
                        updateSec(_sec);
                       
                        if (bit_is_clear(PINA, 3)){
                            configureMode(_hour, _min, _sec);
                        }                      
                        _delay_ms(1000);
                    }
                }
            }
        }
    }

    void configureMode(int hour, int min, int sec){
        sec = 0;
        _delay_ms(2000);
        while(1){
            if (bit_is_clear(PINA, 0))
            {
                ++hour;
                if (hour >= 24)
                {
                    hour %= 24;
                }
                updateHour(hour);
            }
            if (bit_is_clear(PINA, 1))
            {
                min +=10;
                if (min >= 60)
                {
                    min %= 60;
                    ++hour;
                    if (hour >= 24)
                    {
                        hour %= 24;
                        updateHour(hour);
                    }
                }
                updateMin(min);
            }
            if (bit_is_clear(PINA, 2))
            {
                ++min;
                if (min >= 60)
                {
                    min %= 60;
                    ++hour;
                    if (hour >= 24)
                    {
                        hour %= 24;
                        updateHour(hour);
                    }
                }
                updateMin(min);
            }
            if (bit_is_clear(PINA, 3))
            {
                displayTime_BCD(hour, min, sec);
            }
        }
    }

    void main(void)
    {
        //DECLARE THE DATA DIRECTION BITS
        DDRB = DDRC = 0x7F;
        DDRD = 0x3F;
        DDRA = 0x00;
       
        //FIRST FOUR PINS OF A AND PIN 7 OF B WILL BE USED FOR INPUT
        PORTB = 0b10000000;
        PORTA = 0x0F;
        configureMode(0,0,0);
    }
     
    Last edited: Jan 20, 2012
  2. Ian Rogers

    Ian Rogers Super Moderator Most Helpful Member

    Joined:
    Mar 28, 2011
    Messages:
    9,152
    Likes:
    907
    Location:
    Rochdale UK
    ONLINE
    when the loops are incremented... ie ++ _sec & ++ _min & ++ _hour... you need to reset them to 0 when they go past 60, or the loops wont work again
     
  3. cosmonavt

    cosmonavt Member

    Joined:
    Nov 13, 2011
    Messages:
    87
    Likes:
    0
    Hi,

    Thanks for the reply. I sorted it out. Now the clock works fine apart from the fact that I need four buttons to configure the time. Here is my new code:

    I have highlighted two parts of the code in red. In the main function, I am using the highlighted statement to bypass the configureMode function because the buttons are causing problems, so I passed the time directly to the displayTime_BCD function. The highlighted part in the dispayTime function is meant to get input each second from the user. But unfortunately, the statement if (bit_is_clear(PIND, 7)) takes a lot of time to process and the delay of 1000 ms actually becomes 2000 ms (double). How do I solve this? The buttons of configureMode function are also causnig problems. They seem to be getting pressed on their own while there is NOTHING connected on the pins, not even switches.

    How is this happening?
     
    Last edited: Jan 31, 2012
  4. dave

    Dave New Member

    Joined:
    Jan 12, 1997
    Messages:
    -
    Likes:
    0


     
  5. kubeek

    kubeek Well-Known Member

    Joined:
    Mar 11, 2006
    Messages:
    1,486
    Likes:
    185
    Location:
    Prague, Czechia (not Chechnya)
    ONLINE

    Well, you realize that every CMOS INPUT pin has to be connected to SOMETHING? If you leave it floating it will capture stray electric fields from its surroundings and change it state all the time.
    Either connect it to a known state or activate the pullups on the unused pins.
     
    Last edited: Jan 31, 2012
  6. kubeek

    kubeek Well-Known Member

    Joined:
    Mar 11, 2006
    Messages:
    1,486
    Likes:
    185
    Location:
    Prague, Czechia (not Chechnya)
    ONLINE
    And please learn to comment your code, each function should have a short description what it´s parameters mean, what it does, and what it returns if it is not void.
     
  7. kubeek

    kubeek Well-Known Member

    Joined:
    Mar 11, 2006
    Messages:
    1,486
    Likes:
    185
    Location:
    Prague, Czechia (not Chechnya)
    ONLINE
    I don´t see any break statement in the while loop in configureMode(), how do you exit that mode?
     
  8. Ian Rogers

    Ian Rogers Super Moderator Most Helpful Member

    Joined:
    Mar 28, 2011
    Messages:
    9,152
    Likes:
    907
    Location:
    Rochdale UK
    ONLINE
    kubeek is right.... configureMode() requires a return statement in the while loop... secondly....Why have you placed the key press detection in the display routine... thirdly how can you have a clock that has different run times depending on button presses...... You need to set up an interrupt to drive the clock variables or you will slowly loose time.... All the processes take a small amount of time so a delay every second won't be accurate enough..
     
  9. cosmonavt

    cosmonavt Member

    Joined:
    Nov 13, 2011
    Messages:
    87
    Likes:
    0
    Known state? How do I do that? Hook it up to VCC? And how would enabling pullups on unused pins sort out the problem? I have the entire Port C unused, every other pin is in use. Should I set all the pins on Port C to high?
    By pressing the button on PD7.
    Every second, the clock will check if the user has called the configuration mode (by pressing PD7) . Then the current time will be passed to configure mode function. The configure mode function will modify it and then pressing PD7 again will return to the time display function.
     
  10. kubeek

    kubeek Well-Known Member

    Joined:
    Mar 11, 2006
    Messages:
    1,486
    Likes:
    185
    Location:
    Prague, Czechia (not Chechnya)
    ONLINE
    You can't simply exit a while loop by calling another function. It does not return to the display function, it nests up. So when you set the time multiple times in a row, you will end up being in displayTime(configureMode(displayTime(configureMode(displayTime(configureMode(displayTime(configureMode(....)))))))) so after a few attempts the caller return stack will overflow and the chip either reboots or starts processing garbage.

    When you end the configure function, you need to use break; instead of displayTime();. Configure is allways called from displaytime, so after you're configured you should return back to where you were before, i.e. displaytime.

    Ian is right, you should use the hardware timer and update and display time in the Interrupt Service Routine - ISR. Then when you configure time, you disable the interrupt and increment/decrement time as you want, when you're finished you reenable the interrupt and time starts going again.
     
  11. cosmonavt

    cosmonavt Member

    Joined:
    Nov 13, 2011
    Messages:
    87
    Likes:
    0
    What about the main question, how do I deal with the bouncing effect. If the pin is floating in the air, it interacts with stray electric fields. What good would it do if I pull all the unused pins high?
     
  12. cosmonavt

    cosmonavt Member

    Joined:
    Nov 13, 2011
    Messages:
    87
    Likes:
    0
    Isn't that the wrong way round? The interrupt should be the configuration thing. When we are done setting the time, we disable the interrupt and the time starts going again.....
     
  13. kubeek

    kubeek Well-Known Member

    Joined:
    Mar 11, 2006
    Messages:
    1,486
    Likes:
    185
    Location:
    Prague, Czechia (not Chechnya)
    ONLINE
    When you pull them high or low, they are loaded by some resistance, somehting like 100K IIRC, instead of having impedance in the order of tens of megaohms. When they are left floating they can oscillate and introduce these HF oscillations into the rest of the chip which might crrupt data in other parts of it.
    The rule is that every input has to be connected to a known state or it will probably oscillate and cause problems.
    Or you can simply change any unused inputs to outputs instead of activating the pullups.
     
  14. cosmonavt

    cosmonavt Member

    Joined:
    Nov 13, 2011
    Messages:
    87
    Likes:
    0
    so in my case, I have to set the entire Port C to input and pull it high?
     
  15. kubeek

    kubeek Well-Known Member

    Joined:
    Mar 11, 2006
    Messages:
    1,486
    Likes:
    185
    Location:
    Prague, Czechia (not Chechnya)
    ONLINE
    No. The timer interrupt is the part that is precise and should be keeping track of time if you want your clock to be precise. All other stuff should be done by polling in the main while loop, so there you would check for the configure button and act accordingly. You want to disable the interrupt during the configuration so that the time doesn't move when your setting it.
     
  16. cosmonavt

    cosmonavt Member

    Joined:
    Nov 13, 2011
    Messages:
    87
    Likes:
    0
    What about this:

     
  17. kubeek

    kubeek Well-Known Member

    Joined:
    Mar 11, 2006
    Messages:
    1,486
    Likes:
    185
    Location:
    Prague, Czechia (not Chechnya)
    ONLINE
    No, you're still calling one from the other and never return.

    It should be something like:
    Code (text):

    ISR(timer_interrupt_0){
    increment_time();
    update_display();
    }

    void main(){
     init();
     while(1){
      if(conf_button){
       disable_timer();
       configure();
       enable_timer();
       }
     delay 100ms;

     }//end main while
    }

    void confugre(){
    while(1){
     if(button1)increment_hour;
     else if(button2)decrement_hour;
     ...
     update_display();
     delay 500ms;
     if(conf_button){
       break;
     }
     }//end loop

    }
     
     
    Last edited: Jan 31, 2012
  18. cosmonavt

    cosmonavt Member

    Joined:
    Nov 13, 2011
    Messages:
    87
    Likes:
    0
    OK I am rethinking this. I have decided to skip this functionality of interrupts. The time will be configurable only at start. Then to reconfigure the time, simply restart the clock. Based on this, I do not need timers. Plus, timers need to be reset and the best we can do with timers is to make them count will 60 seconds, increment minutes, reset the counter and so on.

    Now, I just need to recode the configureMode function. The main function will call configureMode(), configureMode will set time and return values (hours and mins) to the main routine and the main routine will then call the displayTime_BCD() function. The only problem now is the bouncing effect. How do I get rid of it? Just set all of the Port C to output? (which is the only unused port)? All the rest I/O pins are configured.

    Here is my configureMode() as it looks:

    Code (text):
    int configureMode(){
        while(1){
            /****IF PINA6 is clear, increment hours by 1****/
            if (bit_is_clear(PINA, 6))
            {
                ++hour;
                if (hour >= 24)
                {
                    hour %= 24;
                }
                updateHour(hour);
            }
            /****IF PINA7 is clear, increment minutes by 10****/
            if (bit_is_clear(PINA, 7))
            {
                min +=10;
                if (min >= 60)
                {
                    min %= 60;
                    ++hour;
                    if (hour >= 24)
                    {
                        hour %= 24;
                        updateHour(hour);
                    }
                }
                updateMin(min);
            }
            /****IF PIND7 is clear, increment minutes by 1****/
            if (bit_is_clear(PINC, 0))
            {
                ++min;
                if (min >= 60)
                {
                    min %= 60;
                    ++hour;
                    if (hour >= 24)
                    {
                        hour %= 24;
                        updateHour(hour);
                    }
                }
                updateMin(min);
            }
            /****IF PIND7 is clear, break the loop****/
            if (bit_is_clear(PIND, 7))
                break;
        }
        // need to return hours and minutes here (TWO VALUES!!)
    }
    Which method would you recommend to pass two values by a function in AVR programming. Should I use array? Or a structure?
     
    Last edited: Jan 31, 2012
  19. kubeek

    kubeek Well-Known Member

    Joined:
    Mar 11, 2006
    Messages:
    1,486
    Likes:
    185
    Location:
    Prague, Czechia (not Chechnya)
    ONLINE
    To get rid of bouncing switches, you usually sample them a few times say every 25ms , and if you get for example four same values in a row you consider it a true pushed button.
    This might be harder to implement for multiple switches that can be pressed in the same part of code.

    If you don' t want to use interrupts, you need to think about how many instructions do your time updates take, because if you spend say 100uS calculating the next displayed time, then you get an error of 1s after 10000s, that is an error of 1 second every roughly three hours. You would have to alter your delay by this amount, which might not even be constant, so that you get accurate results.
     
  20. kubeek

    kubeek Well-Known Member

    Joined:
    Mar 11, 2006
    Messages:
    1,486
    Likes:
    185
    Location:
    Prague, Czechia (not Chechnya)
    ONLINE
    I would use global variables for hour and minute. This way you can use and alter them in any function.
     
  21. cosmonavt

    cosmonavt Member

    Joined:
    Nov 13, 2011
    Messages:
    87
    Likes:
    0
    I did exactly the same, before I read your post! Thanks a lot for that "four samples" advice. Its working perfect now!

    Thanks a lot guys!
     

Share This Page