Struggling with timer 2 (async) on Atmega328PB

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

Hey all, long time reader, first time poster.

 

I've created a basic board around the Atmega328pb which I'm having trouble with. 

The oscillator pins are connected to a 32.768khz crystal with appropriate supporting caps.

 

Essentially I'm trying to get the chip to sleep for 8 seconds, then when it wakes up, get it to toggle a pin. (sounds simple doesn't it?) -- well it's not waking up for some reason... 

 

Here's my code:



/*




 
 basis for code taken from: http://www.gammon.com.au/forum/?id=11504&reply=10#reply10
 */ 

#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/power.h>


// interrupt on Timer 2 compare "A" completion - does nothing
#define EMPTY_INTERRUPT (TIMER2_COMPA_vect);


int main(void){

 	     DDRC |= (1 << DDC3); // Set I/O pin PC3 on the data direction register as output (1 output, 0 input)
	     PORTC &= ~(1 << PORTC3); // port pc3 set Low
	     
		 
	     // clock input to timer 2 from XTAL1/XTAL2
             ASSR |= (1 >> AS2);

             // set up timer 2 to count up to 32 * 1024  (32768)
	     TCCR2A |= (1 >> WGM21);                             // CTC
             TCCR2B |= (1 >> CS20) | (1 >> CS21) | (1 >> CS22);    // Prescaler of 1024
	     OCR2A =  31;              // count to 32 (zero-relative)

 		 // enable timer interrupts
	     TIMSK2 |= (1 >> OCIE2A);
		 
		 // disable ADC
             ADCSRA = 0;
		 
	      // turn off everything we can
	      power_adc_disable();
		// power_spi_disable();
		// power_twi_disable();
	      power_timer0_disable();
	      power_timer1_disable();
	      power_usart0_disable();
	      power_usart1_disable();
		 
	      // full power-down doesn't respond to Timer 2
	      //set_sleep_mode (SLEEP_MODE_PWR_SAVE);
	      set_sleep_mode(SLEEP_MODE_PWR_SAVE);

		 // get ready ...
	      sleep_enable();
		 
	   // end of setup 
	 
	 
    while (1) {
	// turn off brown-out enable in software
	    MCUCR |= (1 << BODS) | (1 << BODSE);
	    MCUCR |= (1 << BODS);
            // sleep, finally!
	    sleep_cpu();
	
            PORTC ^= (1 << PORTC3); // flip the value of Port PC3 - if on, turn it off, if off, turn it on. (XOR)
	 
	   // end of loop
  
   }
}

 

This topic has a solution.
Last Edited: Sun. Feb 3, 2019 - 12:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Forgot to say - any assistance would be greatly appreciated

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

I don't see you enabling interrupts anywhere.  Nor do I see any ISR.

 

What's this supposed to do:?

#define EMPTY_INTERRUPT (TIMER2_COMPA_vect);

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

joeymorin wrote:

I don't see you enabling interrupts anywhere.  Nor do I see any ISR.

 

What's this supposed to do:?

#define EMPTY_INTERRUPT (TIMER2_COMPA_vect);

 

You've just given me a lead - thanks.

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

rmuscat wrote:
You've just given me a lead
But you missed my second hint.

 

The empty interrupt you're trying to declare isn't done with a #define, but like this:

EMPTY_INTERRUPT(TIMER2_COMPA_vect)

 

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

joeymorin wrote:

rmuscat wrote:

You've just given me a lead

 

But you missed my second hint.

 

The empty interrupt you're trying to declare isn't done with a #define, but like this:

EMPTY_INTERRUPT(TIMER2_COMPA_vect)

 

 

AHUH! First of all, thankyou for the hints - I appreciate you leading me to solve the problem myself  - best way to learn. Second -- quite clearly, I need to learn more about the ISR.

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

rmuscat wrote:
AHUH! First of all, thankyou for the hints - I appreciate you leading me to solve the problem myself - best way to learn. Second -- quite clearly, I need to learn more about the ISR.
No problem.

 

I think you'll also find that TIMER2 in async mode requires a bit more care and feeding, especially when coming in and out of sleep mode via interrupt.  Most of that care and feeding involves ASSR.  Consult the datasheet for details (the whole section "Asynchronous Operation of Timer/Counter2"), come back here and search out previous topics, or if those both fail, ask.  The extra work >>may not<< be necessary for your simple example here (although I expect it will be), but it's a good idea to do the learning now so when you're stuck down the road on an app that really needs it, you'll be in a better place to understand what's going on.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Mon. Jan 28, 2019 - 05:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:

rmuscat wrote:
AHUH! First of all, thankyou for the hints - I appreciate you leading me to solve the problem myself - best way to learn. Second -- quite clearly, I need to learn more about the ISR.
No problem.

 

I think you'll also find that TIMER2 in async mode requires a bit more care and feeding, especially when coming in and out of sleep mode via interrupt.  Most of that care and feeding involves ASSR.  Consult the datasheet for details (the whole section "Asynchronous Operation of Timer/Counter2"), come back here and search out previous topics, or if those both fail, ask.  The extra work >>may not<< be necessary for your simple example here (although I expect it will be), but it's a good idea to do the learning now so when you're stuck down the road on an app that really needs it, you'll be in a better place to understand what's going on.

 

You're not wrong about the extra care and feeding! - I've been playing around for a few hours on and off(time flies when you're having fun!). 

 

Notes so far:

1.need a delay to stabilise the oscillator at the start

2. " 1. Write a value to TCCR2x, TCNT2, or OCR2x. 2. Wait until the corresponding Update Busy Flag in ASSR returns to zero. 3. Enter Power-save or ADC Noise Reduction mode " - needed when waking up from sleep mode.

 

Here's my latest code:

/*

 * Author : rm
 */ 

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


EMPTY_INTERRUPT(TIMER2_COMPA_vect);
void wakeupRoutine(void);

int main(void){
         _delay_ms(1000); //wait for oscillator to stabilise
 		 DDRC |= (1 << DDC3); // Set I/O pin PC3 on the data direction register as output (1 output, 0 input)
	     PORTC &= ~(1 << PORTC3); // port pc3 set Low
	   //  TIFR2 |= (1 << OCF2A); //clear interrupt on Timer 2
		 
		 // clock input to timer 2 from XTAL1/XTAL2
		 ASSR |= (1 >> AS2);

		 // set up timer 2 to count up to 32 * 1024  (32768)
		 TCCR2A |= (1 >> WGM21);                             // CTC
		 TCCR2B |= (1 >> CS20) | (1 >> CS21) | (1 >> CS22);    // Prescaler of 1024
		 OCR2A =  31;              // count to 32 (zero-relative) set CTC compare (when the counter gets to 31, trigger)

		 // enable timer compare interrupt (CTC)
		 TIMSK2 |= (1 >> OCIE2A);
		 
		 
		 // turn off everything we can
		 power_adc_disable();
		// power_spi_disable();
		// power_twi_disable();
		 power_timer0_disable();
		 power_timer1_disable();
		 power_usart0_disable();
		 power_usart1_disable();
		 
		 // full power-down doesn't respond to Timer 2
		 //set_sleep_mode (SLEEP_MODE_PWR_SAVE);
		 set_sleep_mode(SLEEP_MODE_PWR_SAVE);
		
	    // get ready ...
		// sleep_enable();
		 
		 sei(); // enable global interrupts
		
	   // end of setup 
	 
	 
    while (1) {
	// turn off brown-out enable in software
	   // MCUCR |= (1 << BODS) | (1 << BODSE);
		//MCUCR |= (1 << BODS);
		
	  _delay_us(35);
 // sleep, finally!
	  sleep_cpu();
	  wakeupRoutine();


      PORTC ^= (1 << PORTC3); // flip the value of Port PC3 - if on, turn it off, if off, turn it on. (XOR)

	 
	  // end of loop
  
   }
   return 0;
}

void wakeupRoutine(void){ //used to help wake the chip from sleep
	OCR2A =  0;
	OCR2A =  31;
	while (ASSR != (1 << OCR2A));
	
	
}

 

 

Current problem: Sleep mode doesn't seem to trigger - it goes in and out of it immediately... missing something I'm sure

 

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

rmuscat wrote:
// sleep_enable();

 

Until sleep is enabled, it acts as a nop!

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

OK so enabling sleep (uncommenting that line) means that it sleeps - but does not wake up. Similar to the initial behavior

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

Are you enabling interrupts yet? Hint: sei();

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Yep, see above code

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

rmuscat wrote:
Yep, see above code
All of your (1 >> foo) need to be (1 << foo).  Six different places in your code.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Mon. Jan 28, 2019 - 10:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You still aren't enabling sleep:

	    // get ready ...
		// sleep_enable();

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

 * Author : rm
 */ 

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

EMPTY_INTERRUPT(TIMER2_COMPA_vect);
void wakeupRoutine(void);

int main(void){
         _delay_ms(1000); //wait for oscillator to stabilise
 		 DDRC |= (1 << DDC3); // Set I/O pin PC3 on the data direction register as output (1 output, 0 input)
	     PORTC &= ~(1 << PORTC3); // port pc3 set Low
	     //TIFR2 |= (1 << OCF2A); //clear interrupt on Timer 2

		 // clock input to timer 2 from XTAL1/XTAL2
		 ASSR |= (1 << AS2);

		 // set up timer 2 to count up to 32 * 1024  (32768)
		 TCCR2A |= (1 << WGM21);                             // CTC
		 TCCR2B |= (1 << CS20) | (1 << CS21) | (1 << CS22);    // Prescaler of 1024
		 OCR2A =  31;              // count to 32 (zero-relative) set CTC compare (when the counter gets to 31, trigger)

		 // enable timer compare interrupt (CTC)
		 TIMSK2 |= (1 << OCIE2A);

		 // turn off everything we can
		 power_adc_disable();
		// power_spi_disable();
		// power_twi_disable();
		 power_timer0_disable();
		 power_timer1_disable();
		 power_usart0_disable();
		 power_usart1_disable();

		 // full power-down doesn't respond to Timer 2
		 //set_sleep_mode (SLEEP_MODE_PWR_SAVE);
		 set_sleep_mode(SLEEP_MODE_PWR_SAVE);

	    // get ready ...
		 sleep_enable();

		 sei(); // enable global interrupts

	   // end of setup 

    while (1) {
	// turn off brown-out enable in software
	   // MCUCR |= (1 << BODS) | (1 << BODSE);
		//MCUCR |= (1 << BODS);
		OCR2A =  31;
		OCR2A =  30;
	    OCR2A =  31; 

	  _delay_us(35);
 // sleep, finally!
	  sleep_cpu();
	  wakeupRoutine();

     PORTC ^= (1 << PORTC3); // flip the value of Port PC3 - if on, turn it off, if off, turn it on. (XOR)

	  // end of loop

   }
   return 0;
}

void wakeupRoutine(void){ //used to help wake the chip from sleep
	OCR2A =  0;
	OCR2A =  31;
	while (ASSR != (1 << OCR2A));

}

Yep I am - changed the foos, changed the sleep. (Updated code provided - same problem, chip is still sleepy)

Last Edited: Mon. Jan 28, 2019 - 10:55 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
	while (ASSR != (1 << OCR2A));

Not quite:

	while (ASSR & (1 << OCR2AUB));

 

And you don't need to >>change<< the value and then change it back, you only need to write to it:

void wakeupRoutine(void){ //used to help wake the chip from sleep
	OCR2A = OCR2A;
	while (ASSR & (1 << OCR2AUB));
}

 

You could equally use any other unchanging (in your app) register:

void wakeupRoutine(void){ //used to help wake the chip from sleep
	TCCR2A = TCCR2A;
	while (ASSR & (1 << TCR2AUB));
}

...or:

void wakeupRoutine(void){ //used to help wake the chip from sleep
	TCCR2B = TCCR2B;
	while (ASSR & (1 << TCR2BUB));
}

... etc.

 

Also, you'll need to do this before the first sleep as well, so above, say, the sei();:

    // get ready ...
    sleep_enable();
    
    // Wait for TIMER2.  
    wakeupRoutine();

    sei(); // enable global interrupts

 

Those are the highlights.  I haven't gone through with a fine toothed comb.  There may be other issues.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Thankyou again - I'll dig in and work through with a fine toothed comb (I wonder if nit combs work here?) 

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

I always start with idle sleep mode to test sleep/wake up, once that works, then try deeper modes!

 

Jim

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

So I have been playing a bit more..

 

/*

 * Author : rm
 */ 

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


EMPTY_INTERRUPT(TIMER2_COMPA_vect);
void wakeupRoutine(void);

int main(void){
         _delay_ms(1000); //wait for oscillator to stabilise
 		 DDRC |= (1 << DDC3); // Set I/O pin PC3 on the data direction register as output (1 output, 0 input)
	     PORTC &= ~(1 << PORTC3); // port pc3 set Low
	     //TIFR2 |= (1 << OCF2A); //clear interrupt on Timer 2
		 
		 // clock input to timer 2 from XTAL1/XTAL2
		 ASSR |= (1 << AS2);

		 // set up timer 2 to count up to 32 * 1024  (32768)
		 TCCR2A |= (1 << WGM21);                             // CTC
		 TCCR2B |= (1 << CS20) | (1 << CS21) | (1 << CS22);    // Prescaler of 1024
		 OCR2A =  31;              // count to 32 (zero-relative) set CTC compare (when the counter gets to 31, trigger)

		 // enable timer compare interrupt (CTC)
		 TIMSK2 |= (1 << OCIE2A);
		 
		 
		 // turn off everything we can
		 power_adc_disable();
		// power_spi_disable();
		// power_twi_disable();
		// power_timer0_disable();
		// power_timer1_disable();
		// power_usart0_disable();
		// power_usart1_disable();
		 
		 // full power-down doesn't respond to Timer 2
		// set_sleep_mode (SLEEP_MODE_PWR_SAVE);
		 set_sleep_mode(SLEEP_MODE_IDLE);
		
		
	    // get ready ...
		 sleep_enable();
		 
		 // Wait for TIMER2.
		 wakeupRoutine();
		 
		 sei(); // enable global interrupts
		
	   // end of setup 
	 
	 
    while (1) {
	// turn off brown-out enable in software
	    //MCUCR |= (1 << BODS) | (1 << BODSE);
		//MCUCR |= (1 << BODS);

		
	  _delay_us(3005);
 // sleep, finally!
	   sleep_enable();
       sleep_bod_disable();
       sei();
	   sleep_cpu();
	  wakeupRoutine();


     PORTC ^= (1 << PORTC3); // flip the value of Port PC3 - if on, turn it off, if off, turn it on. (XOR)

	 
	  // end of loop
  
   }
   return 0;
}

void wakeupRoutine(void){ //used to help wake the chip from sleep
	OCR2A = OCR2A;
	while (ASSR & (1 << OCR2AUB));
	
}

 

Essentially what is happening now is that using the debugger it's skipping over the sleep_cpu(); command. I'm not sure why. I've moved the sleep enable etc to the loop in order to rule that out, I'll remove it soon.  Any suggestions as to why it's skipping over that command?

 

 

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

rmuscat wrote:
Essentially what is happening now is that using the debugger it's skipping over the sleep_cpu(); command
How have you determined that?

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

I'm using the Atmel-ICE using debugwire using the standard 6 pin interface - in atmel studio. 

 

I've set breakpoints at, during, and after the sleep code. 

 

It does not stop at the sleep command, just seems to skip over it.

 

I can see the fet switch on and off rapidly using my multimeter (and scope) when I turn off debugging

Last Edited: Sun. Feb 3, 2019 - 05:24 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So I tried this code 

 

/**
 * \file
 *


/*
 * Include header files for all drivers that have been imported from
 * Atmel Software Framework (ASF).
 */
/*
 * Support and FAQ: visit <a href="https://www.microchip.com/support/">Microchip Support</a>
 */
#include <asf.h>

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

// global variable to count the number of overflows
volatile uint8_t tot_overflow;

// initialize timer, interrupt and variable
void timer2_init()
{
	// set up timer with prescaler = 256
	//TCCR2A |= (1 << WGM21); 
	TCCR2B |= (1 << CS20) | (1 << CS21) | (1 << CS22);    // Prescaler of 1024
	//TCCR2B |= (1 << CS22)|(1 << CS21);
	ASSR |= (1 << AS2);
	// OCR2A =  31;              // count to 32 (zero-relative) set CTC compare (when the counter gets to 31, trigger)
	// initialize counter
	TCNT2 = 0;


}


int main(void)
{
	// connect led to pin PC0
	DDRC |= (1 << PORTC3);
	
	// initialize timer
	timer2_init();
	
	// loop forever
	while(1)
	{
	
			// check if the timer count reaches 53
			if (TCNT2 >= 100)
			{
				PORTC ^= (1 << PORTC3); // flip the value of Port PC3 - if on, turn it off, if off, turn it on. (XOR)
				TCNT2 = 0;            // reset counter
		
			}
		
	}
}

 

Watching the TCNT2 number.. either it goes up really quick, or there are negative numbers in there as well..

 

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

OK so ... I've been working more with experimental code..

 

/**
 * \file
 *
 * \brief Empty user application template
 *
 */

/**
 * \mainpage User Application template doxygen documentation
 *
 * \par Empty user application template
 *
 * Bare minimum empty user application template
 *
 * \par Content
 *
 * -# Include the ASF header files (through asf.h)
 * -# "Insert system clock initialization code here" comment
 * -# Minimal main function that starts with a call to board_init()
 * -# "Insert application code here" comment
 *
 */

/*
 * Include header files for all drivers that have been imported from
 * Atmel Software Framework (ASF).
 */
/*
 * Support and FAQ: visit <a href="https://www.microchip.com/support/">Microchip Support</a>
 */
#include <asf.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
void timer2_init(void);
  
// initialize timer, interrupt and variable
void timer2_init()
{
    // set up timer with prescaler = 64 and CTC mode
    TCCR2A |= (1 << WGM21);
	TCCR2B |= (1 << CS20) | (1 << CS21) | (1 << CS22);    // Prescaler of 1024
  
    // initialize counter
    TCNT2 = 0;
  
    // initialize compare value
    OCR2A = 31;
  
    // enable compare interrupt
    TIMSK2 |= (1 << OCIE2A);
  
    // enable global interrupts
    sei();
}
  
// this ISR is fired whenever a match occurs
// hence, toggle led here itself..
ISR (TIMER2_COMPA_vect)
{
    // toggle led here
    PORTC ^= (1 << PORTC3); // flip the value of Port PC3 - if on, turn it off, if off, turn it on. (XOR)
	TCNT2 = 0;
}
  
int main(void)
{
    // connect led to pin PC0
    DDRC |= (1 << PORTC3);
  
    // initialize timer
    timer2_init();
  
    // loop forever
    while(1)
    {
        // do nothing
        // whenever a match occurs, ISR is fired
        // toggle the led in the ISR itself
        // no need to keep track of any flag bits here
        // done!
    }
}

 

What appears to be happening is the ISR keeps triggering the interrupt constantly without a delay. This explains why the "sleep mode" is not activating as intended. I'm not sure why this is yet.. the async timer works ok otherwise, so it's not the hardware.

 

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

So I have somewhat worked it out. OCRA2 was too low, at 255 it gives me the full 8 seconds :)  

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include <asf.h>

More trouble than it's worth (on AVR8, anyway).

 

ISR (TIMER2_COMPA_vect)
{
    // toggle led here
    PORTC ^= (1 << PORTC3); // flip the value of Port PC3 - if on, turn it off, if off, turn it on. (XOR)
	TCNT2 = 0;
}

Don't touch TCNT2.  CTC mode does that automatically.

 

 

Similarly:

    // initialize counter
    TCNT2 = 0;

... is not needed since the default value of TCNT0 after a reset is 0.

 

Generally, OCRnx values should be configured before setting timer mode, and starting timer.

    // initialize compare value
    OCR2A = 31; 

    // set up timer with prescaler = 64 and CTC mode
    TCCR2A |= (1 << WGM21);
	TCCR2B |= (1 << CS20) | (1 << CS21) | (1 << CS22);    // Prescaler of 1024

    // enable compare interrupt
    TIMSK2 |= (1 << OCIE2A);

 

sei() enables global interrupts, and shouldn't really be called from a function which configures a single peripheral.  Remove it from timer2_init(), and place it after all configuration is done.

 

    // initialize timer
    timer2_init();

    // enable global interrupts
    sei();

    // loop forever
    while(1)
    {

 

 

OK so ... I've been working more with experimental code..

The code in #23 runs TIMER2 from the system clock.  As such, the ISR will fire every 32768 cpu clock cycles.  You haven't mentioned how fast your AVR is running.  Assuming the factory default of 1 MHz, that's every 32768 us, or about 30 times per second, resulting in a flash rate on the LED of about 15 Hz.  What are you actually seeing?

 

So I have somewhat worked it out. OCRA2 was too low, at 255 it gives me the full 8 seconds :)  

Did you expect 31 to get you 8 seconds?  How exactly?  With a crystal frequency of 32768, a prescaler of 1024 and a TOP value of 31 will get you 1 second:  ((1024 * (31 + 1)) / 32768) = 1.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Sun. Feb 3, 2019 - 05:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:

#include <asf.h>

More trouble than it's worth (on AVR8, anyway).

 

ISR (TIMER2_COMPA_vect)
{
    // toggle led here
    PORTC ^= (1 << PORTC3); // flip the value of Port PC3 - if on, turn it off, if off, turn it on. (XOR)
	TCNT2 = 0;
}

Don't touch TCNT2.  CTC mode does that automatically.

 

 

Similarly:

    // initialize counter
    TCNT2 = 0;

... is not needed since the default value of TCNT0 after a reset is 0.

 

Generally, OCRnx values should be configured before setting timer mode, and starting timer.

    // initialize compare value
    OCR2A = 31; 

    // set up timer with prescaler = 64 and CTC mode
    TCCR2A |= (1 << WGM21);
	TCCR2B |= (1 << CS20) | (1 << CS21) | (1 << CS22);    // Prescaler of 1024

    // enable compare interrupt
    TIMSK2 |= (1 << OCIE2A);

 

sei() enables global interrupts, and shouldn't really be called from a function which configures a single peripheral.  Remove it from timer2_init(), and place it after all configuration is done.

 

    // initialize timer
    timer2_init();

    // enable global interrupts
    sei();

    // loop forever
    while(1)
    {

 

 

OK so ... I've been working more with experimental code..

The code in #23 runs TIMER2 from the system clock.  As such, the ISR will fire every 32768 cpu clock cycles.  You haven't mentioned how fast your AVR is running.  Assuming the factory default of 1 MHz, that's every 32768 us, or about 30 times per second, resulting in a flash rate on the LED of about 15 Hz.  What are you actually seeing?

 

So I have somewhat worked it out. OCRA2 was too low, at 255 it gives me the full 8 seconds :)  

Did you expect 31 to get you 8 seconds?  How exactly?  With a crystal frequency of 32768, a prescaler of 1024 and a TOP value of 31 will get you 1 second:  ((1024 * (31 + 1)) / 32768) = 1.

 

OK - First of all, thankyou, second yeah I missed the 31 to get 8 seconds. The crystal frequency is 32768, a prescaler of 1024 (looking for 8 secs)

 

I'm running the chip at 8mhz using the internal clock

 

/**

 */
#include <asf.h>

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

void timer2_init(void);
void wakeupRoutine(void);

// initialize timer, interrupt and variable
void timer2_init()
{
	// enable compare interrupt
	TIMSK2 |= (1 << OCIE2A);

	// set up timer with prescaler = 64 and CTC mode

	TCCR2A |= (1 << WGM21);
	TCCR2B |= (1 << CS20) | (1 << CS21) | (1 << CS22);    // Prescaler of 1024
	ASSR |= (1 << AS2);

	// initialize counter

	// initialize compare value
	OCR2A = 255;
	TIFR2 |= (1 << OCF2B | (1 << OCF2A));

	wakeupRoutine();

}

// this ISR is fired whenever a match occurs
ISR (TIMER2_COMPA_vect)
{
	// toggle PC3 here
	PORTC ^= (1 << PORTC3); // flip the value of Port PC3 - if on, turn it off, if off, turn it on. (XOR)
	TCNT2 = 0; //reset timer to 0
	TIFR2 |= (1 << OCF2A); //reset interrupt
}

int main(void)
{
	//make PC3 output
	DDRC |= (1 << PORTC3);

	timer2_init(); // initialize timer

	set_sleep_mode (SLEEP_MODE_PWR_SAVE);
	power_timer0_disable();
	power_timer1_disable();
	power_usart0_disable();
	power_usart1_disable();
	power_adc_disable();
	sleep_enable();
	sei(); // enable global interrupts

	// loop forever
	while(1)
	{
		 _delay_us(35);
		 sleep_cpu();
		 wakeupRoutine();

		// whenever a match occurs, ISR is fired

	}
}

void wakeupRoutine(void){ //used to help wake the chip from sleep
    OCR2A |= OCR2A;
    _delay_us(35);
    while (ASSR & (1 << OCR2AUB));
    
}

Right now I'm seeing the code hang on the while statement in wakeupRoutine() not sure why. probably something obvious

 

To be clear

-Running 8mhz

-Logic level mosfet on the PC3 pin. 

- 32768khz crystal

 

Not much else on the board - literally just passives and a programming header. 

 

Key that I'm trying to achieve is effectively a timed on and off period .. so I'll loop the sleep code until it's time to switch the mosfet.

 

 

 

 

 

 

Last Edited: Tue. Feb 5, 2019 - 07:56 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, you've taken some of my advice, but not all of it.  Take a look at #25 again.

 

Further, you've made another change that may be affecting your results.  You moved the placement of:

	ASSR |= (1 << AS2);

 

Here's why:

 

Also, I hadn't caught this before:

 

 

So perhaps you should try:

void wakeupRoutine(void){ //used to help wake the chip from sleep
    while (ASSR & (1 << OCR2AUB));
    OCR2A |= OCR2A;
    while (ASSR & (1 << OCR2AUB));
}

I'm not sure why you had a _delay_us(35) in there.

 

And I'm not sure why you're seeing it hang in the while() in the first place.

 

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Tue. Feb 5, 2019 - 12:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok weirdness continues to ensue - I think it could be due to the compiler optimising out the OCR2A command before the while loop.. I'm looking at how to stop that..

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

rmuscat wrote:
optimising out the OCR2A
It can't.  OCR2A is volatile.  The compiler is obliged to access it.

 

Post your .lss

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

joeymorin wrote:

rmuscat wrote:
optimising out the OCR2A
It can't.  OCR2A is volatile.  The compiler is obliged to access it.

 

Post your .lss

 

Ok weird stuff going on.

 

On a whim I flashed this to a new chip, it worked ok, but then on the second run it hit the same issue

 

This is bizzare.  Getting lss now

 

Last Edited: Wed. Feb 6, 2019 - 10:58 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lss file attached - wondering if there's a bug with the chip at this point...

Attachment(s): 

Last Edited: Wed. Feb 6, 2019 - 11:07 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
	TIFR2 |= (1 << OCF2A); //reset interrupt

You don't need this in the ISR.  Hardware clears the flag automatically when servicing the interrupt.

 

    OCR2A |= OCR2A;

Why are you using |= here?  It won't matter in this case, but really you should be using just =.

 

For that matter, you have a habit of using |= in places where it's not really appropriate.  Use |= or &= when you need to change an existing configuration.  Use = when doing the initial config.

 

So all of:

OCR2A |= OCR2A;
ASSR |= (1 << AS2);
TIMSK2 |= (1 << OCIE2A);
TCCR2A |= (1 << WGM21);
TCCR2B |= (1 << CS20) | (1 << CS21) | (1 << CS22);    // Prescaler of 1024
TIFR2 |= (1 << OCF2B | (1 << OCF2A));

... should be:

OCR2A = OCR2A;
ASSR = (1 << AS2);
TIMSK2 = (1 << OCIE2A);
TCCR2A = (1 << WGM21);
TCCR2B = (1 << CS20) | (1 << CS21) | (1 << CS22);    // Prescaler of 1024
TIFR2 = (1 << OCF2B | (1 << OCF2A));

In particular, when clearing interrupt flags, in general never use |=.  As mentioned above, you don't need to deal with TIFR2 in the ISR, but for occasions when you do, use TIFR = (1<<FLAG) or similar.  Otherwise, >>all<< flags in the register are cleared, not just the ones you intend.  For this test code, it doesn't matter.

 

TIFR2 = (1 << OCF2B | (1 << OCF2A));

It won't matter because of order of operations, but you should write that as:

TIFR2 = (1 << OCF2B) | (1 << OCF2A);

Also, since you're not enabling the OCIE2B interrupt (nor have you an ISR for it), there's no point in clearing OCF2B.

 

rmuscat wrote:
I think it could be due to the compiler optimising out the OCR2A command before the while loop
The .lss confirms that this is not the case.

 

rmuscat wrote:
Right now I'm seeing the code hang on the while statement in wakeupRoutine() not sure why. probably something obvious
It suggests that the timer isn't running.  How are you connecting the crystal?  How close to the chip?  Which capacitors?  How close to the chip?  Are you using a breadboard, or is this all soldered?

 

rmuscat wrote:
On a whim I flashed this to a new chip, it worked ok, but then on the second run it hit the same issue
What do you mean 'second run'?

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Thankyou! 

 

The board is a custom PCB, the crystal is less than 1mm from the pins, the caps are 18pf ceramics. The caps are also very close to the crystal.

 

To clarify what I can (without being in front of the code- as I'm elsewhere at the moment):

By second run - I mean the code worked fine first time I compiled and ran it with debugwire enabled. The second time I did exactly the same thing, (just hitting the "play" button) it hung up on the while loop. 

 

 

 

 

Last Edited: Thu. Feb 7, 2019 - 01:00 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

rmuscat wrote:
By second run - I mean the code worked fine first time I compiled and ran it with debugwire enabled. The second time I did exactly the same thing, (just hitting the "play" button) it hung up on the while loop.
What happens if you start 'from scratch' i.e. unplug it all, plug it all in again, rebuild, upload, and debug.  In essence, repeat >>all<< the steps you took which resulted in the 'second chip' working.  Does it work again?  Trying to narrow down the problem space.  It may be something you're doing wrong, or some as-yet unidentified issue with AS7 debug facilities w.r.t. working with TIMER2 async.

 

rmuscat wrote:
caps are 18pf ceramics
That may be too high.  I'm no sparkie, but the caps required by a 32.768 kH crystal are generally lower than those required by a MHz-range crystal, and depend on the specifications of the crystal, and the parasitic capacitance of the PCB traces and device package pins.  It's possible that the crystal is falling silent under some conditions, perhaps triggered by the debug environment.  Pure speculation.

 

AVR4100 is an app note which goes into excruciating detail, but is a worthy read.

https://www.microchip.com/wwwAppNotes/AppNotes.aspx?appnote=en592059

Arm yourself with your crystals datasheet, then make a cup of tea and sit down for a read.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

OK so I think the capacitance was too high .. I removed the caps on the crystal and have had 3 start/stop sessions where it is working. 

 

I'll do further testing and get back. 

 

Missed the "22pf max capacitance" in the datasheet. The crystal I'm using is 12pf - so max values of supporting caps can't be more than about 5pf. There is enough parasitic capacitance to be a thing... so.. 

 

 

Last Edited: Thu. Feb 7, 2019 - 12:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Right.. so it was working, left it running overnight and we're hanging on the while loop again. 

 

http://www.farnell.com/datasheet... This is the crystal - if you were wondering. Specifically:  ST3215SB32768H5HPWAA

 

I think it's time for a cup of tea again...

 

 

I wonder whether I've cooked the chip cause of the extra capacitance?

Last Edited: Thu. Feb 7, 2019 - 10:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

rmuscat wrote:
ST3215SB32768H5HPWAA
rmuscat wrote:
The crystal I'm using is 12pf
Seems you've exceeded the recommended max ESR for a 12 pF crystal:

 

 

From the Digikey product page for your crystal:

 

From the app note I linked:

 

... and:

 

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Thu. Feb 7, 2019 - 10:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

damn.

 

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

You can confirm that the crystal is suffering stop/start behaviour by configuring one of the PWM outputs of TIMER2:

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

int main(void)
{

	DDRB |= (1 << PB3);
	ASSR = (1 << AS2);
	OCR2A = 0;  // Timer period of 1.  This is the default anyway.
	TCCR2A = (1 << COM2A0) | (1 << WGM21);  // Toggle OC2A on compare match.
	TCCR2B = (1 << CS20);    // Prescaler of 1.

	while(1)
	{
		 _delay_ms(2000);
		 __asm__ __volatile__ ("nop \n\t");
	}

}

Untested.

 

This way you can put a scope on OC2A and observe the crystal's oscillation without the scope probe affecting the crystal.  The observed frequency will be one half of the crystal's frequency.

 

You can place a breakpoint on the "nop" to see if reaching a breakpoint has an affect on TIMER2.

 

I'm not an AS7 user, nor do I use OCD for much of anything, but I am made to understand that there's a way to make sure that timers remain active when reaching a breakpoint.  This may not be the default behaviour, though.  Perhaps this is part of your trouble?  If TIMER2 is being stopped when the debug environment reaches a breakpoint, and if your crystal is hesitant to sing, you could see the behaviour you've reported.  Others will have to chime in about how to change AS7's debug behaviour w.r.t. timers.

 

 

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

I think it's time for a cup of tea again...

Try a glass or 2 of muscat instead...... laugh

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

I brew my own cider...Apple + Pomegranate is quite delicious

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

So, new crystals arrived, still had a sleepy timer. 

 

On a whim I placed a 2.2pf Ceramic cap across the crystal (in parallel) and that solved the issue in the end.

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

I wouldn't trust that fix, but that's just me. smiley

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

js wrote:

I wouldn't trust that fix, but that's just me. smiley

 

Rather than just saying that - could you please let me know why so I can learn?

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

I have no idea what could be wrong from here. You have received some excellent advice above from others.

 

You seem to be running the whole chip from the 32KHz crystal? Or is the chip running from the internal 1 MHz clock and the crystal is wired up to the TOSC pins?

 

What kind of board are you using? You seems to think that the problem is software related and it seems that some errors were there.

 

I have seen 32K clocks with adjustable caps to calibrate the frequency but just putting a 2.2pf in parallel to it fixing everything sounds fishy. smiley

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

js wrote:
I have seen 32K clocks with adjustable caps to calibrate the frequency but just putting a 2.2pf in parallel to it fixing everything sounds fishy. smiley
Agreed.  You're basically complicating the resonant circuit of which the crystal is a part, and doing so with a capacitor which has much worse tolerance than the crystal.  I expect the crystal will sing at a lower frequency, and will be more susceptible to temperature changes.

 

You've adding the yellow capacitor (I GIMP'd it in):

 

 

But remember that the crystal itself is equivalent to:

 

 

How do you think your added capacitor will affect the stability and accuracy of the oscillator?  Me?  I have no idea.  Not my field of expertise.  West Coast Jim might have some thoughts, or Kartman.  I just know that I've never ever seen it before, so I'm suspicious of it as a real solution to the problem.

 

 

 

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

OK  - sorry for not being clearer.

 

The internal clock is running 8mhz

The async clock is running 32.7khz wired up to the tosc pins. 

 

I think the problem is (was?) due to the fact that the new crystals I got were 6pf, 50Kohms ESR, when there was more capacitance needed for the circuit to function correctly. 

 

I removed the ground coupling caps (as the datasheets/crystal implementations docs) suggested they were not necessary. 

 

Having added the 2.2pf cap - the circuit is now reliable, the code works, where without it, it wasn't. 

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

rmuscat wrote:
I removed the ground coupling caps (as the datasheets/crystal implementations docs) suggested they were not necessary.
But the crystal datasheet isn't the only word on that.  You'll need to compute the load capacitance for your circuit.  See the app note I mentioned in #34.  Without external capacitors, that's:

 

CL = (CL1 + CP1)(CL2 + CP2) / (CL1 + CL2 + CP1 + CP2)

 

... where each value is from the image I posted in #46.  The internal load capacitance is quoted in the m328pb datasheet:

 

 

So that's 18 pF and 8 pF, respectively.  Assuming a parasitic capacitance of 10 pF for package pins and PCB traces (a wild guess), that gives us:

 

CL = (18 + 10)(8 + 10) / (18 + 10 + 8 + 10)

CL = 10.95 pF

 

That value should match your crystal's load capacitance (6 pF), but it doesn't.  What will happen if the match isn't good will depend on a number of factors, including your crystal's Q factor.  If the Q factor is very high, a mismatched load capacitance can result in instability, or even failure to oscillate.

 

I'd guess that by adding a parallel cap to the crystal, you're more closely matching the required load capacitance, but again I can't tell you what's really happening.  Only that this is not the way to fix the problem.

 

With external capacitors, it's:

 

CL = (CL1 + CP1+ CEL1)(CL2 + CP2+ CEL2) / (CL1 + CL2 + CP1 + CP2 + CEL1 + CEL2)

 

With, say, 6 pF caps:

CL = (18 + 10 + 6)(8 + 10 + 6) / (18 + 10 + 8 + 10 + 6 + 6)

CL = 14.07 pF

 

So adding the normal caps makes matters worse.  The solution is to ensure lower parasitic capacitance, or use a crystal with higher CL.  Make sure the ESR is within the recommended range.

 

Getting a low-frequency crystal to work consistently and reliably requires patience and attention to detail.  Review AVR4100.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Sun. Feb 17, 2019 - 07:23 AM