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.

  • 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.

Binary Clock with ATMEGA16

Status
Not open for further replies.

cosmonavt

Member
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:
/*
 * 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:

cosmonavt

Member
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:

/*
* 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/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 updateSec_bin(int sec){
sec = baseConv(sec);

PORTB = (((sec/10) % 10) << 4)|( (sec % 10) << 0);
PORTD = (((sec/1000) % 10) << 4)|(((sec/100) % 10) << 0);
PORTA = (((sec/100000) % 10) << 4)|(((sec/10000) % 10) << 0);
}

void updateMin(int min){
int lowerNib = min % 10; //second column
lowerNib = baseConv(lowerNib);

int upperNib = min / 10; //first column
upperNib = baseConv(upperNib);

PORTD = (((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_bin(int min){
min = baseConv(min);

PORTB = (((min/10) % 10) << 5)|( (min % 10) << 1);
PORTD = (((min/1000) % 10) << 5)|(((min/100) % 10) << 1);
PORTA = (((min/100000) % 10) << 5)|(((min/10000) % 10) << 1);
}

void updateHour(int hour){
int lowerNib = hour % 10; //second column
lowerNib = baseConv(lowerNib);

int upperNib = hour / 10; //first column
upperNib = baseConv(upperNib);

PORTA = (((upperNib/10) % 10) << 5)|((upperNib % 10) << 4)|(((lowerNib/1000) % 10) << 3)|(((lowerNib/100) % 10) << 2)|(((lowerNib/10) % 10) << 1)|( (lowerNib % 10) << 0);
}

void updateHour_bin(int hour){
hour = baseConv(hour);

PORTB = (((hour/10) % 10) << 6)|( (hour % 10) << 2);
PORTD = (((hour/1000) % 10) << 6)|(((hour/100) % 10) << 2);
PORTA = (((hour/10000) % 10) << 2);
}

void displayTime_BCD(int _hour, int _min){

int hour, min, sec;

while(1)
{
for (hour = _hour; hour < 24; ++hour )
{
updateHour(hour);
for (min = _min; min < 60; ++min )
{
updateMin(min);
for (sec = 0; sec < 60; ++sec )
{
updateSec(sec);
/******************
PROBLEMS
if (bit_is_clear(PIND, 7))
{
configureMode(hour, min);
}
**************************/

_delay_ms(1000);
}
}
_min = 0;
}
_hour = 0;
}
}

int configureMode(int hour, int min){
_delay_ms(2000);
while(1){
if (bit_is_clear(PINA, 6))
{
++hour;
if (hour >= 24)
{
hour %= 24;
}
updateHour(hour);
}
if (bit_is_clear(PINA, 7))
{
min +=10;
if (min >= 60)
{
min %= 60;
++hour;
if (hour >= 24)
{
hour %= 24;
updateHour(hour);
}
}
updateMin(min);
}
if (bit_is_clear(PINC, 0))
{
++min;
if (min >= 60)
{
min %= 60;
++hour;
if (hour >= 24)
{
hour %= 24;
updateHour(hour);
}
}
updateMin(min);
}
if (bit_is_clear(PIND, 7))
{
displayTime_BCD(hour, min);
}
}
}

void main(void)
{
//DECLARE THE DATA DIRECTION BITS
DDRA = 0b00111111;
DDRB = DDRD = 0b01111111;

PORTA = (1 << 7)|(1 << 6);
PORTB = PORTD = (1 << 7);
displayTime_BCD(17,47);
}

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:

kubeek

Well-Known Member
Most Helpful Member
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:

kubeek

Well-Known Member
Most Helpful Member
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.
 

kubeek

Well-Known Member
Most Helpful Member
I don´t see any break statement in the while loop in configureMode(), how do you exit that mode?
 

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
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..
 

cosmonavt

Member
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.
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?
I don´t see any break statement in the while loop in configureMode(), how do you exit that mode?
By pressing the button on PD7.
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..
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.
 

kubeek

Well-Known Member
Most Helpful Member
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.
 

cosmonavt

Member
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?
 

cosmonavt

Member
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.
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.....
 

kubeek

Well-Known Member
Most Helpful Member
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.
 

kubeek

Well-Known Member
Most Helpful Member
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.....
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.
 

cosmonavt

Member
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.

What about this:

void displayTime(){
while(1)
{
//start counting time
//if conf button is pressed, break;
}
configureMode(hour, min);
}

void configureMode{
while(1)
{
//accept changes from user
// if conf button is pressed again, break;
}
displayTime(hour, min);
}
 

kubeek

Well-Known Member
Most Helpful Member
No, you're still calling one from the other and never return.

It should be something like:
Code:
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:

cosmonavt

Member
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:
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:

kubeek

Well-Known Member
Most Helpful Member
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.
 

kubeek

Well-Known Member
Most Helpful Member
Which method would you recommend to pass two values by a function in AVR programming. Should I use array? Or a structure?
I would use global variables for hour and minute. This way you can use and alter them in any function.
 

cosmonavt

Member
I would use global variables for hour and minute. This way you can use and alter them in any function.
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!
 
Status
Not open for further replies.

EE World Online Articles

Loading
Top