Question about debouncing buttons and real time clock

Go To Last Post
25 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello. I am new user to your forums.
I bought my first avr mc 5 days ago or so.

Fortunately i have experience in C, but i do not have experience in microcontrollers.

Ive been roaming around your forum reading tutorials and stuff and i've been having serveral problems (for example external crystal usage).

I have opened this thread because i have a particular question.

Yesterday i wrote a library in which i check for button hits and releases (i define the inputs of my choice, up to all 4 * 8 = 32 inputs). It works fine as it is but then i came across some button debouncing threads and i realized that if debounce occurs my libraries wont work properly. So im intending to write a new one using a timer of about 10 ms to check the buttons.

Here is the question.
Lets say i want a clock to tick every 1 second
But i want my buttons to be checked every 10 ms = 0.001 s

What is the best method to do this?

I have an ATMEGA32A

Here is my sample code

The timer ticks every 10 ms and i have a variable to count up to 100, to count 1 sec every 100 timer ticks.

Does this method have errors? (except the internal clock error, im intending to use external crystal).
Which is the best method to use a clock with button debouncing?

Thank you in advance and i hope im posting in the right place

Code below

#include 
#include 
#define bit_flip(p,m) ((p) ^= (m))
#define BIT(x) (0x01 << (x))

uint8_t timer=0;

int main(void)
{	
	//PA2 has a led on it, rest are input
	DDRA = 0b00000100;
	//Input negative logic
	PORTA = 0b11111011;
	
	//Using prescaler of 1 to tick every 10 ms
	OCR1A = 12499;
	
	TIMSK |= (1 <<OCIE1A);
	
	TCCR1B|=(1<<CS10);
	TCNT1=0;
	
	sei();
    while(1)
    {
        //TODO:: Please write your application code 
    }
}

ISR(TIMER1_COMPA_vect)
{
	TCNT1=0;
	++timer;
	timer %= 100;
	//check_buttons();
	if (timer == 0)
		bit_flip(PORTA, BIT(2));
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Lets say i want a clock to tick every 1 second
But i want my buttons to be checked every 10 ms = 0.001 s

What is the best method to do this?


Run the timer at 10ms. Count to 100 for the 1000ms work....

...In fact that would appear to be exactly what your code implementation is doing - so what was the question again? ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:

Lets say i want a clock to tick every 1 second
But i want my buttons to be checked every 10 ms = 0.001 s

What is the best method to do this?


Run the timer at 10ms. Count to 100 for the 1000ms work....

...In fact that would appear to be exactly what your code implementation is doing - so what was the question again? ;-)

I want to build a clock (a real time clock)
I intend to use an external crystal.

I want it to count accurately, so i was asking if what i was thinking is going to have errors or smth.

So do i stick to my code for the clock project?

I also had the idea of having two different timers, but that would end up putting buttons in first place (timer 0) and the clock in the second place (timer 1).

Thank you for your immediate reply.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

...In fact that would appear to be exactly what your code implementation is doing

I personally would avoid doing the "work" in the ISR. It bloats the ISR with save/restore every time (not just when the work needs to be done). Rather, set a flag, keep the ISR skinny, and do the work in the main loop.

To address accurate timekeeping when there might be lengthy operations (SD-card write; LCD init/clear) I might count the ticks in the ISR and then "make up" for any missed ticks in the mainline.

But indeed--never say never. For FATFS work, sometimes it sits in a hard loop waiting for time to expire. So the disktimerproc() needs to be in the ISR (IME).

//
//
// **************************************************************************
// *
// *		T I M E R 2 _ C O M P _ I S R
// *
// **************************************************************************
//
// Timer 2 output compare interrupt service routine
//
// Main timer "tick", occurs every 10.0ms
//
interrupt [TIM2_COMPA] void timer2_compa_isr	(void)

{

// Flag to main loop that a tick has occured
	tick = 1;
	ticks_isr++;

#ifdef TFF
//
//	=================================
//	SD Card with FatFS
//	=================================

	disk_timerproc (card_good_to_go);
#endif

}
//
// **************************************************************************
// *	T I M E R   T I C K
// **************************************************************************
//
//	Clear "tock";  it is only set for one scan of main-loop code.
//	If a tick has occurred, update and see if it is time for tock.
//
//	"tack" is a one second timer, toggled every tock.
//
//	Presumes a 10ms. tick.
//
	tock = 0;
	if (tick)
		{
		tick = 0;

// With the SD card, there may be long delays before we get back here.
//	Ensure no loss of timekeeping.
#asm ("cli")
		ticks += ticks_isr;
		ticks_isr = 0;
#asm ("sei")

		if (ticks >= TICKS_PER_TOCK)
			{
			ticks -= TICKS_PER_TOCK;
...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just a note: making variable "timer" volatile should help in real world implementation.

There's a number of methods for switch debouncing code. All are a bit different, despite all are based on timer ticks:
a flag on input change either raw input is captured in ISR;
the data is processed afterwards in main loop.
See "Ganssle debounce code", vertical counter method and alike for reference. Forum has a lot information for sure. Implementation should depend on input count, methinks (few or many buttons).

IMHO the same timer (say ticking at 10ms) should do for time record and debounce.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I want it to count accurately, so i was asking if what i was thinking is going to have errors or smth.
This part will create inaccuracy:

  TCNT1=0; 

There is an unknown (and fairly significant) amount of time between when the interrupt is triggered and this line of code is run. For a real time clock, this will be fatal. You should use CTC mode which automatically clears the timer when the interrupt triggers.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok i cannot quote all the posts but i followed all your advice and i really appreciate your replies. I built a single port (up to 8 pins of a single port) button debouncer (every 10ms) with a clock of 1 Hz I intend to build a 4 ports button debouncer which will not be that difficult based on what i wrote (if this works ok). So here is my modified code 2 buttons on PINA0, PINA1, one led on PINA2 (for time tick) and 2 7segment on PORTD to test the buttons (each button increases the number on its 7segment) p.s I am not disabling interrupts when processing the global variables outside the isr because i dont know what would happen if an interrupt should have occured when i was processing them. What do you think of it?

 /* * MyDebouncer.c * * Created: 22-Jul-13 2:50:49 PM * Author: Tilemachos Sheva (sheva) */ #include #include #define INPUT_DDR DDRA #define INPUT_PORT PORTA #define INPUT_PIN PINA #define INPUT_KEYS 0b11111100 typedef uint8_t byte; byte releaseToggle=0x00; byte lastState = 0x00; volatile byte timer=0; void io_timer_initiation() { //Setup buttons etc INPUT_DDR = INPUT_KEYS; //Negative logic of INPUT KEYS, so outputs initiate with 0s and inputs initiate with 1s. INPUT_PORT = ~INPUT_KEYS; //Using prescaler of 1 to tick every 10 ms OCR1A = 12499; TIMSK |= (1 <=100) { timer -= 100; return 1; } else return 0; } int main(void) { io_timer_initiation(); DDRD = 0b11111111; PORTD = 0b00000000; byte count=0; byte count2=0; while(1) { PORTD = (count2 << 4) | count; if (isReleased(0)) count = (count + 1) % 10; if (isReleased(1)) count2 = (count2 + 1) % 10; if (timer_tick()) PORTA ^= (0x01 << 2); } } ISR(TIMER1_COMPA_vect) { /* * ****Mask has 0s for inputs and 1s for outputs **** * mask = ~ INPUT_DDR * * ****currentInput has the inverted value of INPUT_PIN (this means we have 1 for button press)**** * currentInput = ~INPUT_PIN * * ****currentState has 0s for non input pins, 0s for unpressed buttons and 1s for pressed buttons **** * currentState = Mask & CurrentInput * * ****releaseToggle flips from 0 to 1 when currentState is 0 and lastState is 1. * We avoid setting 0s to releaseToggle which is currently set to 1 by using a logic OR * between "new" toggle and "last" toggle. * let "new" toggle = (~currentState & lastState) * and "last" toggle = releaseToggle * Generally we set 1s to toggle when a button is unpressed but before was pressed**** * releaseToggle = (~currentState & lastState) | releaseToggle */ timer++; releaseToggle = (~((~INPUT_DDR) & (~INPUT_PIN)) & lastState) | releaseToggle; //Define that LastState = currentState lastState = ((~INPUT_DDR) & (~INPUT_PIN)); } 

Last Edited: Sat. Jun 27, 2015 - 06:55 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I am not disabling interrupts when processing the global variables outside the isr because i dont know what would happen if an interrupt should have occured when i was processing them.
If an interrupt occurs when you are processing them and you have disabled global interrupts, then the interrupt will be serviced when you re-enable global interrupts. The only thing you need to avoid is having the global interrupts disabled long enough for two of the same interrupt to occur. Since your interrupt rate is on the order of milliseconds, this is not a problem.

If you do not disable global interrupts while processing them, it is possible for bugs to creep in. Since your global variables are single bytes, then individual reads and writes will not have a problem. Multiple reads and writes may cause a problem depending on the situation. The read then write of releaseToggle should not have a problem though. However, you MUST declare releaseToggle as volatile.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Koshchi wrote:
Quote:
I am not disabling interrupts when processing the global variables outside the isr because i dont know what would happen if an interrupt should have occured when i was processing them.
If an interrupt occurs when you are processing them and you have disabled global interrupts, then the interrupt will be serviced when you re-enable global interrupts. The only thing you need to avoid is having the global interrupts disabled long enough for two of the same interrupt to occur. Since your interrupt rate is on the order of milliseconds, this is not a problem.

If you do not disable global interrupts while processing them, it is possible for bugs to creep in. Since your global variables are single bytes, then individual reads and writes will not have a problem. Multiple reads and writes may cause a problem depending on the situation. The read then write of releaseToggle should not have a problem though. However, you MUST declare releaseToggle as volatile.

Ok i declared releaseToggle as volatile...

I understand that if isr read/write and main read write of the variable releaseToggle occur simultaneously i may get bugs, but those bugs will affect the button press which is ok for me.

If i do disable and enable interrupts while modifying releaseToggle in "main", the interrupt may occur delayed by an amount of some μseconds, which may not be crucial, if that happens once, but in a month or so i may have significant error in my clock which i don't want.

I don't know how big the error is going to be (since i have to reset the time after 6 months because of time change)....

I may be wrong because i don't know thoroughly how interrupts work. What i mean with that?
Lets say, for example, that i will disable interrupts then process releaseToggle and then enable interrupts again.
Then after processing releaseToggle in main and after i enable interrupts, the interrupt will occur.
So lets say the processing of releaseToggle takes χ microseconds (μs).
So the next interrupt (after the one that will occur right after the enable of interrupts) when will occur? After 10 ms, or after 10 (ms)- χ (μs)?

Thanks for your reply.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
If i do disable and enable interrupts while modifying releaseToggle in "main", the interrupt may occur delayed by an amount of some μseconds, which may not be crucial, if that happens once, but in a month or so i may have significant error in my clock which i don't want.
But the delayed interrupt handling will affect the response to the interrupt, it will not affect the timer generating the interrupt. The timer is still running whether or not the interrupt is serviced and will trigger an interrupt every 10ms. Only if you turn off interrupts for more than 10ms will there be a potential for errors.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Koshchi wrote:
Quote:
If i do disable and enable interrupts while modifying releaseToggle in "main", the interrupt may occur delayed by an amount of some μseconds, which may not be crucial, if that happens once, but in a month or so i may have significant error in my clock which i don't want.
But the delayed interrupt handling will affect the response to the interrupt, it will not affect the timer generating the interrupt. The timer is still running whether or not the interrupt is serviced and will trigger an interrupt every 10ms. Only if you turn off interrupts for more than 10ms will there be a potential for errors.

Hmmm i get it.
Si if i do disable and enable interrupts the interrupt will be just slowed to be served.
So the next interrupt, after the one that was served slowed, (assuming that this one will be served on time) will be served after 10 ms minus the time the last interrupt was slowed by.

Thank you that made some things clear.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
will be served after 10 ms minus the time the last interrupt was slowed by.
Don't worry about the exact timing of when the interrupt is serviced. If you program is written correctly, that timing is entirely unimportant. The important things is that the interrupt is triggered at exactly every 10ms (within the limits of the frequency source, of course). Often ISRs do nothing more than set a flag. The main loop then acts on that flag at its leisure.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok i followed all your advice and ive managed to build a first version of a clock with reset (debounced) buttons.
It seems that it does not count the time properly.
It counts (ticks) faster that it should be. In 6 minutes it can can count 4-6 seconds more than the pc clock.

It seems that i have a problem with my timer initiation.

I assumed that my atmega32a is running at 1 Mhz (i havent fused anything yet, im afraid of brcking it).

Timer initiation

//Using prescaler of 1 to tick every 10 ms
//CTC mode on
	TCCR1B |= (1 << WGM12);
	TCCR1B|=(1<<CS10);
	
	TIMSK |= (1 <<OCIE1A);
	
	OCR1A = 9999;
	
	
	sei();

Timer

ISR(TIMER1_COMPA_vect)
{
	time++;
	releaseToggle = (~((~INPUT_DDR) & (~INPUT_PIN)) & lastState) | releaseToggle;
	lastState = ((~INPUT_DDR) & (~INPUT_PIN));
}

Seconds tick

unsigned char timer_tick()
{
	cli();
	if (time >=100)
	{
		time -= 100;
		sei();
		return 1;
	}
	else
	{
		sei();
		return 0;
	}		
}

I simply do when needed

if (timer_tick())
		csecs++;

If im actually running at 1 MHz why this ticks faster when i'm counting 100 ticks of a timer set for 100 Hz?

If you want the full code please tell me i will post it on pastebin.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I assumed that my atmega32a is running at 1 Mhz (i havent fused anything yet, im afraid of brcking it).

If you mean you are using the internal RC oscillator then read your datasheet. There you will find that the only guarantee of accuracy is that it is 1MHz +/-10%. If there are 86400 seconds in a day then, because of this possible inaccuracy, your clock may be with 8,640 seconds too fast or too slow - that's 2.4 hours either way. Even in a minute it could be 6 seconds fast or slow.

Bottom line: you cannot use the int RC to implement an RTC *unless* you calibrate it against an accurate clock source (and because of voltage and temperature drift you may need to do the recalibration fairly often).

If you spend $0.30 you can buy a piece of quartz in a can. For $0.30 you will get something like 30-50ppm. Let's say it is 50 parts per million. In your 86,400 second day the likely inaccuracy is now within 4.3 seconds. But that still means your clock could be out by more than 2 minutes a month.

The more you pay the more accuracy you can get. If you use a Maxim-Dallas temperature compensated RTC chip they can offer 1-2ppm. That is just short of 0.1 seconds inaccuracy in the 86400 second day. In a month that it is just 2.6 seconds of potential inaccuracy.

Note that while I say +/-10% (that is 100,000ppm), 30-50ppm, 1-2ppm these are "worst case". In reality the spread of accuracy is probably gaussian with only some of the chips being really bad and out at the extremes of accuracy while most are probably nearer the centre. This probably explains why what you saw was not actually as bad as it could have been!

Really, if you want to make a practical clock visit:

http://www.maximintegrated.com/p...

If you want it to be really good follow the "highly accurate" link there.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:

I assumed that my atmega32a is running at 1 Mhz (i havent fused anything yet, im afraid of brcking it).

If you mean you are using the internal RC oscillator then read your datasheet. There you will find that the only guarantee of accuracy is that it is 1MHz +/-10%. If there are 86400 seconds in a day then, because of this possible inaccuracy, your clock may be with 8,640 seconds too fast or too slow - that's 2.4 hours either way. Even in a minute it could be 6 seconds fast or slow.

Bottom line: you cannot use the int RC to implement an RTC *unless* you calibrate it against an accurate clock source (and because of voltage and temperature drift you may need to do the recalibration fairly often).

If you spend $0.30 you can buy a piece of quartz in a can. For $0.30 you will get something like 30-50ppm. Let's say it is 50 parts per million. In your 86,400 second day the likely inaccuracy is now within 4.3 seconds. But that still means your clock could be out by more than 2 minutes a month.

The more you pay the more accuracy you can get. If you use a Maxim-Dallas temperature compensated RTC chip they can offer 1-2ppm. That is just short of 0.1 seconds inaccuracy in the 86400 second day. In a month that it is just 2.6 seconds of potential inaccuracy.

Note that while I say +/-10% (that is 100,000ppm), 30-50ppm, 1-2ppm these are "worst case". In reality the spread of accuracy is probably gaussian with only some of the chips being really bad and out at the extremes of accuracy while most are probably nearer the centre. This probably explains why what you saw was not actually as bad as it could have been!

Really, if you want to make a practical clock visit:

http://www.maximintegrated.com/p...

If you want it to be really good follow the "highly accurate" link there.

Oh thanks for the info.
I already have two crystals (one 16 Mhz and on 18.something Mhz) and two capacitors of 22pf.
I was planning on implementing the 16 Mhz crystal on my board but i was afraid of that fuse bits thing since i am new to this.
But since i am going to have significant error (2 minutes per day or so) i am gonna check the highly accurate parts.
I think for example this
http://www.maximintegrated.com/datasheet/index.mvp/id/4627
is more accurate than a normal crystal.

Thank you for your time.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Unless price is an issue, I would use DS3232 - the 236 bytes of battery backed RAM might come in useful somewhere

See http://pdfserv.maximintegrated.c... for the differences....

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

But since i am going to have significant error (2 minutes per day or so) i am gonna check the highly accurate parts.

I think you may have mis-read my figures. I was assuming a typical 16MHz crystal might be 50ppm. That equated to a possible (worst case) inaccuracy of 4.3 seconds per day or, in other words, 2 minutes per month. You then just said "2 minutes per day". That is not correct, it is a possible error of "2 minutes per month".

However, like I say, if you want the ultimate in accuracy (and don't plan to connect to a truly accurate time source like 50Hz mains frequency (over time), GPS or a DSF radio receiver) then those 2ppm Maxim chips are the thing to look at.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MartinM57 wrote:
Unless price is an issue, I would use DS3232 - the 236 bytes of battery backed RAM might come in useful somewhere

See http://pdfserv.maximintegrated.c... for the differences....

Hm interesting ill check it out.

clawson wrote:
Quote:

But since i am going to have significant error (2 minutes per day or so) i am gonna check the highly accurate parts.

I think you may have mis-read my figures. I was assuming a typical 16MHz crystal might be 50ppm. That equated to a possible (worst case) inaccuracy of 4.3 seconds per day or, in other words, 2 minutes per month. You then just said "2 minutes per day". That is not correct, it is a possible error of "2 minutes per month".

However, like I say, if you want the ultimate in accuracy (and don't plan to connect to a truly accurate time source like 50Hz mains frequency (over time), GPS or a DSF radio receiver) then those 2ppm Maxim chips are the thing to look at.

Yeh i got what you said :)
I mean i did not missunderstood what you said its just English is not my native language and what i intend to write is not what i actually write.

2ppm error possibility is ok for me ill check if i can find any 5v product in their site to buy

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

ill check if i can find any 5v product

All the high accuracy ones (see Martin's link) are 2.7 .. 5.5V operation.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just tried to use an external crystal
I used this site
http://www.engbedded.com/fusecalc/
for atmega32a
I selected the last option of the clock (CSKEL = 1111 SUT=111). I read somewhere to choose the largest startup time (i dont know if this is the right choice, but it seems it isnt so important except i choose a small srartup time which may make the clock malfunction.).

The atmega32a was not running well and then i fused again with CKOPT = 0 and it worked really well.
But i dont fully understand what CKOPT is.

Ill try to fuse again today with JTAGEN set to 1 since im not using it.

Nevermind im trying the crystal.
It performs much better than the internal oscillator....

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

But i dont fully understand what CKOPT is.

The resonant circuit inside the AVR that uses the crystal to oscillate can drive it in "low power" mode or "full swing mode". The latter uses more power but is required for crystals of 8MHz and above in order to get the quartz fully resonating. CKOPT, when activated, turns on full swing/power mode.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:

But i dont fully understand what CKOPT is.

The resonant circuit inside the AVR that uses the crystal to oscillate can drive it in "low power" mode or "full swing mode". The latter uses more power but is required for crystals of 8MHz and above in order to get the quartz fully resonating. CKOPT, when activated, turns on full swing/power mode.

Hmmm that's why it was working but "not really well" when i had CKOPT set to 1 (off).

I guess its better to use something bellow 8 MHz when on battery and not on usb because full power mode may consume the battery faster i suppose.

A crystal at about 6 MHz is probably the "medium freq" choice i suppose.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I guess its better to use something bellow 8 MHz when on battery and not on usb because full power mode may consume the battery faster i suppose.

if you are using battery you are presumably going to sleep most of the time? if so then common experience here suggests it makes no difference whether you wake up and do a burst of work quickly on a fast clock or wake up for longer and do the work at a slower rate. Of course this may depend on the work that needs to be done when the CPU is not sleeping.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:

I guess its better to use something bellow 8 MHz when on battery and not on usb because full power mode may consume the battery faster i suppose.

if you are using battery you are presumably going to sleep most of the time? if so then common experience here suggests it makes no difference whether you wake up and do a burst of work quickly on a fast clock or wake up for longer and do the work at a slower rate. Of course this may depend on the work that needs to be done when the CPU is not sleeping.

Sorry for the late answer but i had work to do.

Well i was talking about the real clock.
I mean except the other things that i want to implement, im going to have an lcd displaying the current time (or a timer counting to 0).
So i was talking about the clock only and the battery it is going to consume the clock itself.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

So i was talking about the clock only and the battery it is going to consume the clock itself.


And the answer is the same as the one that Cliff gave to you: With proper use of sleep modes, you will use less power by doing the needed "work" periodically and then going to a deep sleep mode than you would by staying awake at a slow clock rate.

How much power >>your<< LCD is using depends on ... your LCD.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.