Enabling TCA0 interrupt causes a reset

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

When configuring TCA0 and enabling it's interrupt, it causes my ATmega4808 to reset. The reset actually occurs when I call sei()

But it will only reset once (after the initial reset), and then continue but the timer does not generate an overflow interrupt.

Another problem is when I enable the timer, all the interrupt flags are set.

 

The code is reduced to only the configuration of TCA0. Once TCA0 has been configured, I call the global interrupt enable with sei().

 

This is my current implementation:

// Stop timer
TCA0.SINGLE.CTRLA &= ~(TCA_SINGLE_ENABLE_bm);  

// Force a hard reset
TCA0.SINGLE.CTRLESET = TCA_SINGLE_CMD_RESET_gc;

// Set the timer mode
TCA0.SINGLE.CTRLB = TCA_SINGLE_WGMODE_NORMAL_gc;

// Disable event counting
TCA0.SINGLE.EVCTRL &= ~(TCA_SINGLE_CNTEI_bm);

// Enable running in debug mode
TCA0.SINGLE.DBGCTRL = TCA_SINGLE_DBGRUN_bm;

// Set the period
TCA0.SINGLE.PER = 63536;

// Set the timer clock
TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc;

//  Enable the timer
TCA0.SINGLE.CTRLA |= TCA_SINGLE_ENABLE_bm;

// Clear any pending interrupt flags
TCA0.SINGLE.INTFLAGS = 0xFF;

// Enable the interrupts
TCA0.SINGLE.INTCTRL = TCA_SINGLE_OVF_bm;

Although I'm only interested in the overflow interrupt, I do have interrupt handlers for all the TCA interrupts.

 

1.) Why would I get a reset?

2.) Why are all the interrupt flags set?

3.) Why does the interrupt flags not clear when I try and clear them?

4.) Why will the timer not run?

 

Thanks in advance.

Last Edited: Mon. Sep 14, 2020 - 07:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The usual cause of reset after enabling interrupts is that there is no ISR  present.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

For completeness you should post the Overflow ISR code.

 

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

Or even better a small complete program that demo's the problem so other Freaks can help you troubleshoot it!

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

works for me (nano curiosity mega 4809)

 

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

ISR(TCA0_OVF_vect) {
  TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm;
  PORTF.OUTTGL = PIN5_bm;
}

void main(void) {

  PORTF.DIRSET = PIN5_bm;		/* LED output */
  sei();

  // Stop timer
  TCA0.SINGLE.CTRLA &= ~(TCA_SINGLE_ENABLE_bm);  

  // Force a hard reset
  TCA0.SINGLE.CTRLESET = TCA_SINGLE_CMD_RESET_gc;

  // Set the timer mode
  TCA0.SINGLE.CTRLB = TCA_SINGLE_WGMODE_NORMAL_gc;

  // Disable event counting
  TCA0.SINGLE.EVCTRL &= ~(TCA_SINGLE_CNTEI_bm);

  // Enable running in debug mode
  TCA0.SINGLE.DBGCTRL = TCA_SINGLE_DBGRUN_bm;

  // Set the period
  TCA0.SINGLE.PER = 63536;

  // Set the timer clock
  TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc;
  TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV8_gc;

  //  Enable the timer
  TCA0.SINGLE.CTRLA |= TCA_SINGLE_ENABLE_bm;
  
  // Clear any pending interrupt flags
  TCA0.SINGLE.INTFLAGS = 0xFF;

  // Enable the interrupts
  TCA0.SINGLE.INTCTRL = TCA_SINGLE_OVF_bm;

  while (1);

 

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

Here are my interrupt handlers:

ISR(__vector_default) {}

ISR(TCA0_CMP0_vect)
{	
	// Clear the interrupt flag
	TCA0.SINGLE.INTFLAGS = TCA_SINGLE_CMP0_bm;
}

ISR(TCA0_CMP1_vect)
{	
	// Clear the interrupt flag
	TCA0.SINGLE.INTFLAGS = TCA_SINGLE_CMP1_bm;
}

ISR(TCA0_CMP2_vect)
{	
	// Clear the interrupt flag
	TCA0.SINGLE.INTFLAGS = TCA_SINGLE_CMP2_bm;
}

ISR(TCA0_OVF_vect)
{	
	// If the callback is assigned
	if (micro_interrupt_Callbacks_APF[MICRO_INTERRUPT_CALLBACK_TCA_E].Callback_V != NULL)
	{
		// Execute the callback
		micro_interrupt_Callbacks_APF[MICRO_INTERRUPT_CALLBACK_TCA_E].Callback_V();
	}
	
	// Clear the interrupt flag
	TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm;
}

 

I will put a complete program together.

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

Here is a complete program with the same symptoms (double reset and timer not generating an overflow interrupt). I've loaded CMP0, CMP1 and CMP2 to prevent them from generating an interrupt when enabling the timer. WDT is disabled.

 

#include <avr/interrupt.h>

int main(void)
 {
	// TCA

	// Stop timer
	TCA0.SINGLE.CTRLA &= ~(TCA_SINGLE_ENABLE_bm);

	// Force a hard reset
	TCA0.SINGLE.CTRLESET = TCA_SINGLE_CMD_RESET_gc;

	// Enable the interrupts
	TCA0.SINGLE.INTCTRL = TCA_SINGLE_OVF_bm;

	// Initialise CMP0
	TCA0.SINGLE.CMP0 = 0xFFFF;

	// Initialise CMP1
	TCA0.SINGLE.CMP1 = 0xFFFF;

	// Initialise CMP2
	TCA0.SINGLE.CMP2 = 0xFFFF;

	// Reset the count
	TCA0.SINGLE.CNT = 2000;

	// Set the timer mode
	TCA0.SINGLE.CTRLB = TCA_SINGLE_WGMODE_NORMAL_gc;

	// Disable event counting
	TCA0.SINGLE.EVCTRL &= ~(TCA_SINGLE_CNTEI_bm);

	// Enable running in debug mode
	TCA0.SINGLE.DBGCTRL = TCA_SINGLE_DBGRUN_bm;

	// Set the period
	TCA0.SINGLE.PER = 2000;

	// Set the timer clock
	TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc;

	//  Enable the timer
	TCA0.SINGLE.CTRLA |= TCA_SINGLE_ENABLE_bm;

	// Clear any pending interrupt flags
	TCA0.SINGLE.INTFLAGS = 0xFF;

        // Global interrupt enable
        sei(); 

	while (1);
}

ISR(__vector_default) {}

ISR(TCA0_OVF_vect)
{
	// Clear the interrupt flag
	TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm;
}

 

Last Edited: Mon. Sep 14, 2020 - 09:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your latest example works on my 4809. I did comment out the debugrun since you cannot debug (step through) and check what's going on while its running.

 

Your symptoms sound like an incorrectly named interrupt, even though they are the correct names. What happens on an avr0/1 when you have a 'bad' name, is you end up in __bad_interrupt which normally just jumps to 0, so the LVLEX0 bit remains set and you get no more interrupts because there was no reti executed (and you will get no more reti until the LVLEX0 bit is cleared by a reti- catch-22). You can look at the lss listing to see what the vector looks like, your tca0 ovf should be in there.

 

In this case with the __vector_default created, with a bad name you would end up there instead (via a jmp from __bad_interrupt), and since it will have the ISR epilogue you get the reti and all is well. Except if you ended up there, you probably will not escape since nothing was done to correct the errant interrupt (you do get one instruction executed after reti, before you end up back in that isr, so other code still runs, quite slowly).

 

If you want to use __vector_default, then have it do something useful (if debugger in use, put a nop and a breakpoint in it). Eventually you can keep it and simply place a software reset inside so you never get 'stuck' with the LVL0EX bit on (even though once the app is done, should never get there).

 

 

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

The ATmega4808 header (iomega4808.h) has the interrupt vectors defined as:

 

#define TCA0_OVF_vect_num  7
#define TCA0_OVF_vect      _VECTOR(7)  /*  */

 

but the datasheet says:

 

 

The ATmega4808 is a 48KB device. Should the vector address not be 0x0E??

 

@curtvm: 

 

1.) Can you tell me where your interrupt vector for TCA0 overflow is located?

2.) Could you run my source on your micro if I compiled it for the 4809?

 

Thansk

Last Edited: Tue. Sep 15, 2020 - 07:13 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

None of this made sense so I soldered a new device onto the board to check the default (factory) FUSE settings.

With the new device on the board the interrupt is working fine. The only difference in my FUSE settings is the BOD level which is 1.8V by default and in mine it was set to 4.2V but the BOD is disabled.

I will investigate further...

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

This is all my fault. What a waste of time.

 

The previous chip had a bootloader on and the BOOTEND was configured.

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

>The previous chip had a bootloader on and the BOOTEND was configured.

 

That is how is discovered the LVLEX0 problem- I was messing around with a bootloader and forgot to change the fuses back when done. I forgot to mention in my previous post the hardware vector table can also move around via bootend.