Problems with my alarm clock project, not sure it is the debouncing

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

Hi, I'm portuguese and I'm making a alarm clock project with ATmega644 and a 4 digit 7 segment display. When I power up my circuit, it give me the current time, I did this through RTC module and it works perfectly. Now I want that while I press the button -PB0 - he enter in the mode set_alarm, with hours and minutes to zero, so far so good. The part that doesn't work is: while this button(PB0) is pressed, I want use PB1 and PB2 buttons to set the alarm hours and alarm minutes. But when I press one of these two buttons, the respective display changes to the next number but immediately returns to zero and I dont know why. Check my code , if there are some parts of the rest of the code that you wanna see, let me know, I only paste the part that I think you need to see. The debounce code I see the "Software debouncing of buttons" pdf from Snigelen and it works fine. And help me please, thanks!

 

int main(void)
{
	inic();												
// 	DS1307_Lock(true);									
// 	Modo24h();											
// 	Time_Set(2,25,1);									
	Get_Time();											
	
	while (1) 
    {	
		if( !(PINB & (1<<PB0)) )
		{	
			set_alarm();									//goes to set_alarm
		}
		else 
		{
			digits_clock();									//Show clock
		}
	}
}

(...)

void digits_clock()
{	
	PORTC = 0b00010000;									
	display_num(Tempo[2]);								
	_delay_ms(6.1);
	
	PORTC = 0b10000000;									
	display_num(Tempo[3]);								
	_delay_ms(6.1);
 	
	PORTC = 0b01000000;									
	display_num(Tempo[4]);							
	_delay_ms(6.1);
	
	PORTC = 0b00100000;									
	display_num(Tempo[5]);								
	_delay_ms(6.1);
}

void digits_alarm()
{
	PORTC = 0b00010000;										
	display_num(minutos%10);								
	_delay_ms(6.1);
	
	PORTC = 0b10000000;									
	display_num(minutos/10);								
	_delay_ms(6.1);
	
	PORTC = 0b01000000;									
	display_num(horas%10);								
	_delay_ms(6.1);
	
	PORTC = 0b00100000;									
	display_num(horas/10);								
	_delay_ms(6.1);
}

void set_alarm()
{
	debounce();                                 
	if (button_down(BUTTON2_MASK))                                       //PB1 pressed
	{
		minutos ++;
	}
	if (button_down(BUTTON3_MASK))                                       //PB2 pressed
	{
		horas ++;
	}
	digits_alarm();
}

  

This topic has a solution.
Last Edited: Wed. Nov 1, 2017 - 10:54 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The title asks about debouncing but the only sign of that is a call to a mysterious debounce() routine? Don't you think it might have been an idea to post the code you think maybe at fault? 

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

Yes but as I said, doesn't seem to me that the problem is the debouncing because I already tested before in a simpler program and it incremented digits fine. but here you have

 

debounce.h 

/*
debounce.h. Snigelens version of Peter Dannegger’s debounce routines.
Debounce up to eight buttons on one port. $Rev: 577 $
*/
#ifndef DEBOUNCE_H
#define DEBOUNCE_H

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

// Buttons connected to PA0 and PA1
#define BUTTON_PORT PORTB
#define BUTTON_PIN PINB
#define BUTTON_DDR DDRB
#define BUTTON1_MASK (1<<PB0)
#define BUTTON2_MASK (1<<PB1)
#define BUTTON3_MASK (1<<PB2)
#define BUTTON_MASK (BUTTON2_MASK | BUTTON3_MASK | BUTTON1_MASK)

// Variable to tell that the button is pressed (and debounced).
// Can be read with button_down() which will clear it.
extern volatile uint8_t buttons_down;

// Return non-zero if a button matching mask is pressed.
uint8_t button_down(uint8_t button_mask);

// Make button pins inputs and activate internal pullups.
void debounce_init(void);

// Decrease 2 bit vertical counter where mask = 1.
// Set counters to binary 11 where mask = 0.
#define VC_DEC_OR_SET(high, low, mask) \
low = ~(low & mask); \
high = low ^ (high & mask)

// Check button state and set bits in the button_down variable if a
// debounced button down press is detected.
// Call this function every 10 ms or so.
static inline void debounce(void)
{
	// Eight vertical two bit counters for number of equal states
	static uint8_t vcount_low = 0xFF, vcount_high = 0xFF;
	
	// Keeps track of current (debounced) state
	static uint8_t button_state = 0;
	
	// Read buttons (active low so invert with ~). Xor with
	// button_state to see which ones are about to change state
	uint8_t state_changed = ~BUTTON_PIN ^ button_state;
	
	// Decrease counters where state_changed = 1, set the others to 0b11.
	VC_DEC_OR_SET(vcount_high, vcount_low, state_changed);
	
	// Update state_changed to have a 1 only if the counter overflowed
	state_changed &= vcount_low & vcount_high;
	
	// Change button_state for the buttons who’s counters rolled over
	button_state ^= state_changed;
	
	// Update button_down with buttons who’s counters rolled over
	// and who’s state is 1 (pressed)
	buttons_down |= button_state & state_changed;
}
#endif

 

C file:

volatile uint8_t buttons_down;

// Return non-zero if a button matching mask is pressed.
uint8_t button_down(uint8_t button_mask)
{
	// ATOMIC_BLOCK is needed if debounce() is called from within an ISR
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
	{
		// And with debounced state for a one if they match
		button_mask &= buttons_down;
		// Clear if there was a match
		buttons_down ^= button_mask;
	}
	// Return non-zero if there was a match
	return button_mask;
}

 

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

The words in your thread title are "not sure it is the debouncing". How could anyone here give an opinion on that otherwise?

 

The comment above debounce() say "call this about every 10ms". As your code is written how are you ensuring this 10ms? I don't see it. 

 

Usually temporal debounce is done out of a ticker ISR. 

Last Edited: Sun. Oct 29, 2017 - 06:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Done. My atmega is working at 1MHz. I put a prescaler of 64. 

 

ISR (TIMER0_OVF_vect)
{
	cnt_ovf ++;
	if (cnt_ovf == 3) debounce();
}

Every 12 ms it does "debounce". Now it is worst, my clock doesnt advance and when I press PB1 or PB2 the display doesnt even react

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

In the parts of your program that you have shown us there is only one place where horas and minutos are assigned any value:

	if (button_down(BUTTON2_MASK))                                       //PB1 pressed
	{
		minutos ++;
	}
	if (button_down(BUTTON3_MASK))                                       //PB2 pressed
	{
		horas ++;
	}

You have not shown the definitions of those two variables but neither of the above increments should consistently set them to zero as you've described.

 

We have little or no chance to help you unless you...

 

Post a minimal but complete program that builds and runs and demonstrates the problem. Note minimal and complete.

 

The work is on you to produce that. Remove everything that does not have to do with the problem itself. E.g. you could cut it down to only handle minutos for now.

 

Without that my only speculation is that your program resets for some reason.

  • This could be an electrical problem, e.g. something causing a shortcut and power surge, so it would help if you also posted a schematic.
  • It could also be because you for some reason enabled an interrupt for which you have not supplied an ISR. This might cause a reset. (You didn't enable an external interrupt on the pins connected to the switched, did you? If so, don't.)

I'm sure there are plenty of other possible reasons for your problem. Complete code that we can examine is the key to getting help with this.

 

Remember, high quality of your question and the info you supply is the best guarantee for high quality answers.

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Ok, I will do that. My full code is here

 

#include <avr/io.h>
#include <util/twi.h>
#include "ds1307.h"
#include <stdio.h>
#include <stdlib.h>
#include <avr/interrupt.h>

#define F_CPU 1000000UL									//Freq -> 1 MHz -> CLKDIV 8 ativado
#include <util/delay.h>									//Biblioteca delay.h
#include "7segment.h"									//Adiciona a biblioteca do Display de 7 Segmentos
#include "debounce.h"					//Adiciona a biblioteca do Debouncing dos botões
 
static unsigned char horas, minutos;					
#define pwm(val) 255*val/100
static unsigned char cnt_ovf;
volatile char Tempo[11];
volatile uint8_t buttons_down;

/**********************************************************
*						Testes
**********************************************************/

/**********************************************************
*                   Funções Usadas 
**********************************************************/

void inic();
void digits_clock();
void digits_alarm();
void alarm();
void check_alarm();
void alarm_on();
void set_alarm();

int main(void)
{
	inic();												//Inicializações
// 	DS1307_Lock(true);									//Desbloqueia RTC
// 	Modo24h();											//Modo 24h ativo
// 	Time_Set(2,25,1);									//Acertar relógio -> este comando só é incluído na primeira vez
	Get_Time();											//Obter tempo atual
	
	while (1) 
    {	
		if( !(PINB & (1<<PB0)) )
		{	
			set_alarm();									//Mostra Alarme
		}
		else 
		{
			digits_clock();									//Mostra relógio
		}
	}
}

/******************************************************************
*						 Inicializações
*******************************************************************/

void inic()
{	
	/* PORTS */
	DDRA = 0xFF;												//Todos os bits como saídas -> Ordem dos Segmentos: a - f - b - e - d - dot - c - g
	DDRB = (0<<0) | (0<<1) | (0<<2);							//PIN's dos botões como entrada 
	DDRC = (0<<4) | (0<<5) | (0<<6) | (0<<7);					//PC5 -> Dígito 4 ; PC4 -> Dígito 1 ; PC6 -> Dígito 2 ; PC7 -> Dígito 3
	DDRD = (1<<1) | (1<<3) | (1<<6);							//PIN do besouro como saída assim como os PINs dos motores
	
	PORTB = (1<<0) | (1<<1) | (1<<2);
	PORTD = (0<<1) | (0<<3) | (0<<6);
	
	/* TWI */
	TWSR &= ~((1<<TWPS1) | (1<<TWPS0));							//Prescaler -> 1
	TWBR = 2;													//TWI_Freq = 100 kHz -> TWBR = ((F_CPU / TWI_FREQ) - 16) / 2
	TWCR |= (1<<TWEN);											//Enable da comunicação
	
	/* TWI */ 
	Tempo[6] = 4;
	Tempo[7] = 2;
	Tempo[8] = 5;
	Tempo[9] = 1;
	
	/*	TIMER 0	*/
	TCCR0B |= (1<<CS01) | (1<<CS00);
	TIMSK0 |= 1<<TOIE0;
// 	TCCR0A |= (1<<WGM01);										//Normal Port Operation ; Modo CTC
// 	TCCR0B |= (1<<CS02) | (1<<CS00);							//Prescaler -> 1024 ; T = 50 ms
// 	OCR0A = 121;
	
	/*TIMSK0 |= (1<<OCIE0A);										//Enable da Interrupção Compare Match A*/
		
	/* TIMER 1 */												// Timer para o Besouro
	TCCR1A |= (1<<COM1A1) | (1<<WGM10);
	TCCR1B |= (1<<WGM12) | (1<<CS10);
	OCR1AL = 0;
	
	sei();
}

/********************************************************************
*								Testes
********************************************************************/
ISR (TIMER0_OVF_vect)
{
	cnt_ovf ++;
	if (cnt_ovf == 3) debounce();
	digits_clock();
}

// Return non-zero if a button matching mask is pressed.
uint8_t button_down(uint8_t button_mask)
{
	// ATOMIC_BLOCK is needed if debounce() is called from within an ISR
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
	{
		// And with debounced state for a one if they match
		button_mask &= buttons_down;
		// Clear if there was a match
		buttons_down ^= button_mask;
	}
	// Return non-zero if there was a match
	return button_mask;
}

 /********************************************************************
 *                    4 x Display de 7 Segmentos
 ********************************************************************/

void digits_clock()
{	
	PORTC = 0b00010000;									//Liga o dígito das unidades 
	display_num(Tempo[2]);								//Mostra o valor que estiver na variável dig4
	_delay_ms(6.1);
	
	PORTC = 0b10000000;									//Liga o dígito das dezenas
	display_num(Tempo[3]);								//Mostra o valor que estiver na variável dig3
	_delay_ms(6.1);
 	
	PORTC = 0b01000000;									//Liga o dígito das centenas
	display_num(Tempo[4]);								//Mostra o valor que estiver na variável dig2
	_delay_ms(6.1);
	
	PORTC = 0b00100000;									//Liga o dígito dos milhares
	display_num(Tempo[5]);								//Mostra o valor que estiver na variável dig1
	_delay_ms(6.1);
}

void digits_alarm()
{
	PORTC = 0b00010000;										//Liga o dígito das unidades
	display_num(minutos%10);								//Mostra o valor que estiver na variável dig4
	_delay_ms(6.1);
	
	PORTC = 0b10000000;									//Liga o dígito das dezenas
	display_num(minutos/10);								//Mostra o valor que estiver na variável dig3
	_delay_ms(6.1);
	
	PORTC = 0b01000000;									//Liga o dígito das centenas
	display_num(horas%10);								//Mostra o valor que estiver na variável dig2
	_delay_ms(6.1);
	
	PORTC = 0b00100000;									//Liga o dígito dos milhares
	display_num(horas/10);								//Mostra o valor que estiver na variável dig1
	_delay_ms(6.1);
}

void set_alarm()
{
	if (button_down(BUTTON2_MASK))
	{
		minutos ++;
	}
	if (button_down(BUTTON3_MASK))
	{
		horas ++;
	}
	digits_alarm();
}

/**************************************************************************
 *                                Alarme
 *************************************************************************/
void check_alarm()
{
	if (Tempo[2] == Tempo[6] && Tempo[3] == Tempo[7] && Tempo[4] == Tempo[8] && Tempo[5] == Tempo[9])
	{
		alarm_on();
	}
}

/**************************************************************************
*							Funções de Controlo
`*************************************************************************/ 

void DesativaTWI()
{
	TWCR &= ~(1<<TWEN);
}

void StartTWI()
{
	TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
	while(!(TWCR&(1<<TWINT)));
}

void StopTWI()
{
	TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
}

/**************************************************************************
*					  Função de Bloqueio/Desbloqueio
`*************************************************************************/

void DS1307_Lock(bool onoff)
{
	uint8_t unlock;
	
	if (onoff==true)
	{
		Ler_DS1307(0x00,&unlock);
		unlock&=~(1<<DS_CH);
		Escrever_DS1307(0x00,unlock);
	}
	else
	{
		Ler_DS1307(0x00,&unlock);
		unlock|=(1<<DS_CH);
		Escrever_DS1307(0x00,unlock);
	}
}

/****************************************************************
*                         Modo 24h
*****************************************************************/

void Modo24h()
{
	uint8_t mode;
	Ler_DS1307(0x02,&mode);
	mode&=~(1<<DS_24H);
	Escrever_DS1307(0x02,mode);
}

/****************************************************************
*                         Conversões
*****************************************************************/

char dec_bcd(char num)
{
	return ((num/10 * 16) + (num % 10));					//Converte número decimal para BCD
}

char bcd_dec(char num)
{
	return ((num/16 * 10) + (num % 16));					//Converte BCD para número decimal
}

/****************************************************************
*                         Escrever
*****************************************************************/

bool EscreverByte_TWI(uint8_t byte)
{
	TWDR = byte;												//O registo TWDR contém a sequência de 8 bits a transmitir
	
	TWCR = (1<<TWINT)|(1<<TWEN);								//Começo da comunicação
	while (!(TWCR&(1<<TWINT)));									//Espera pelo fim

	/*
	Verifica se na comunicação houve conclusão de um dos processos:
	SLA+W transmitido e ACK recebido || Byte de dados transmitido e ACK recebido || SLA+R transmitido e ACK recebido
	*/
	if ((TW_STATUS == TW_MT_SLA_ACK) || (TW_STATUS == TW_MT_DATA_ACK) || (TW_STATUS == TW_MR_SLA_ACK))
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool Escrever_DS1307(uint8_t address, uint8_t data)
{
	bool result;
	StartTWI();
	result=EscreverByte_TWI((DS_SLA|TW_WRITE));
	if (result==false) return false;
	result=EscreverByte_TWI(address);
	if (result==false) return false;
	result=EscreverByte_TWI(data);
	if (result==false) return false;
	StopTWI();
	return true;
}

/****************************************************************
*                            Ler
*****************************************************************/

bool LerByte_TWI(uint8_t *data, bool ack)
{
	if(ack==false)
	{
		TWCR&=~(1<<TWEA);										//Se receber um NACK, diz que quer de receber dados
	}
	else
	{
		TWCR|=(1<<TWEA);										//Retorna ACK depois da receção
	}
	
	TWCR|=(1<<TWINT);											//Começo da receção de dados
	while(!(TWCR&(1<<TWINT)));									//Espera que termine
	
	/*
	Verifica se na comunicação houve conclusão de um dos processos: 
	Dados recebidos e ACK retornado || Dados recebidos e NACK retornado
	*/
	if ((TW_STATUS==TW_MR_DATA_ACK)||(TW_STATUS==TW_MR_DATA_NACK))
	{
		*data=TWDR;
		return true;
	}
	else
	{
		return false;
	}
}

bool Ler_DS1307(uint8_t address, uint8_t *data)
{
	bool result;
	StartTWI();
	result=EscreverByte_TWI((DS_SLA|TW_WRITE));
	if (result==false) return false;
	
	result=EscreverByte_TWI(address);
	if (result==false) return false;
	
	StartTWI();
	result=EscreverByte_TWI((DS_SLA|TW_READ));
	if (result==false) return false;
	
	result=LerByte_TWI(data,false);
	if (result==false) return false;
	
	StopTWI();
	
	return true;
}

/****************************************************************
*                      Acertar Horas e Minutos
*****************************************************************/

void Time_Set(uint8_t hour, uint8_t min, uint8_t sec)
{
	Escrever_DS1307(0x00,((sec/10)<<4)+(sec%10));				//seconds=20 //0-59
	Escrever_DS1307(0x01,((min/10)<<4)+(min%10));				//minute=19 //0-59
	Escrever_DS1307(0x02,((hour/10)<<4)+(hour%10));				//hour= 18 //0-23
}

/****************************************************************
*                      Obter o Tempo
*****************************************************************/

void Get_Time()
{
	uint8_t value;
	Ler_DS1307(0x00,&value);
	Tempo[0] = value & 0b00001111;
	Tempo[1] = (value & 0b01110000)>>4;
	Ler_DS1307(0x01,&value);
	Tempo[2] = value & 0b00001111;
	Tempo[3] = (value & 0b01110000)>>4;
	Ler_DS1307(0x02,&value);
	Tempo[4] = value & 0b00001111;
	Tempo[5] = (value & 0b01110000)>>4;
}

 

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

diogofvc wrote:
My full code is here
At least we can now see the reason for this:

Now it is worst, my clock doesnt advance and when I press PB1 or PB2 the display doesnt even react

It's because of the digits_clock(); in the overflow interrupt.

 

The "my clock doesnt advance" called my attention. I can't see how it could advance before the change. You are doing a Get_Time() only once at start-up. So I think you saw an advance only because of permanent software resets. That would also explain the "immediately returns to zero". 

Was this perhaps not commented out before the change?

	/*TIMSK0 |= (1<<OCIE0A);										//Enable da Interrupção Compare Match A*/

 

 

Stefan Ernst

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

No no, that commented part I used in other test that I make.

 

You call my attention when you speak about the localization of my Get_Time(). I already managed to make the digit not return to zero, but the clock does not update. where do you think I should put Get_Time()?

 

I did it work with my isr routine like this:

 

ISR (TIMER0_OVF_vect)
{
	debounce();
}

but the clock doesnt advance

 

Last Edited: Sun. Oct 29, 2017 - 09:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

diogofvc wrote:

Done. My atmega is working at 1MHz. I put a prescaler of 64. 

 

ISR (TIMER0_OVF_vect)
{
	cnt_ovf ++;
	if (cnt_ovf == 3) debounce();
}

Every 12 ms it does "debounce". Now it is worst, my clock doesnt advance and when I press PB1 or PB2 the display doesnt even react

 

I haven't read the whole thread but this jumped out at me.  The given code won't call debounce() every 12 mS because cnt_ovf is not being reset.  Based on the context I'm assuming the ISR is firing every 4 mS.  The declaration of cnt_ovf is not shown but if we assume byte size then debounce() will be called every 256 * 4 = 1.024 seconds.  If it's 16 bits you're now talking 4.37 minutes between each call.  Either would make it appear to not work.

Letting the smoke out since 1978

 

 

 

 

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

digitalDan wrote:

diogofvc wrote:

The declaration of cnt_ovf is not shown but if we assume byte size then debounce() will be called every 256 * 4 = 1.024 seconds. 

 

I had not even thought about that. thank you! It was declared like this: "volatile unsigned char cnt_ovf". to have interrupt every 4 ms , how I have to declare?

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 how I have to declare

The fault is not in the declaration. Read #10.

You need

ISR (TIMER0_OVF_vect)
{
	cnt_ovf ++;
	if (cnt_ovf == 3)
	{
	    debounce();
            cnt_ovf = 0;
	}   
}

 

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

Thank you all. Now it works as I want!