Timer interrupt on ATMega3209

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


Trying to get a interrupt to run on timer overflow but despite all registers seemingly being set correctly i never enter the ISR.

 

Here is the register setup from the timer 2

 

As can be seen it is enabled, it is counting, CCMP is set to the desired 0xFF4F, interrupt is not masked and the interrupt flag gets raised on compare match.

 

Global interrupts are enabled via the sei() function and can be seen by the debugger.

 

I have a breakpoint in the ISR that is never reached and if it did the flag gets reset when entering the ISR as a precaution.

ISR(TCB2_INT_vect)
{
	COUNTER_SEC_TIMER.INTFLAGS |= TCB_CAPT_bm; //Clear interrupt flag
	subSecCnt++;
}

 

Its probably me missing something completely mundane but at this point i dont know what else to check.

 

 

This topic has a solution.
Last Edited: Thu. May 12, 2022 - 09:56 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

 

Post you test code so we can try it ourselves in the Studio 7 simulator.

 

EDIT: so I've never used a 4809 but I had  quick squint at the datasheet and wrote this..

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

ISR(TCB2_INT_vect) {
    _NOP();
}

int main(void) {
    TCB2.CCMP = 100;
    TCB2.CTRLA = TCB_ENABLE_bm;
    TCB2.INTCTRL = TCB_CAPT_bm;
    sei();

    while(1) {
        _NOP();
    }
}

This first hits my interrupt after 133 cycles..

 

If I reset the cycle counter and try again it hits my interrupt after 22/23 cycles each time.

 

I have probably misunderstood something - I have a distant memory that Xmega chips need the interrupting condition manually cleared so there's probably something I have to do in the ISR() to clear the interrupt flag?

 

Anyway this code interrupts so it could be a start.

 

EDIT2: so, yeah it seems clearing the flag was the "magic" I was looking for. This hits the NOP in the ISR every 101/102 cycles...

 

 

 PS I thought you had said 4809 so that's what I used for my experiments - I expect a 3209 will be very similar.

 

This was an interesting learning experience for me. Kind of surprised not to find separate OVF and COMP interrupt vectors like tiny/mega AVRs have. It seems "TCB2_INT_vect" is a "do everything interrupt" and you set a mode to say what kind.

 

Presumably if you wanted the equivalent of "overflow" you would set CCMP=0xFFFF ?

Last Edited: Wed. May 11, 2022 - 12:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did a little clean up of the code so its more readable and verified that the same behavior takes place.

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

volatile unsigned int subSecCnt = 0;

ISR(TCB2_INT_vect)
{
	TCB2.INTFLAGS |= TCB_CAPT_bm; //Clear interrupt flag
	subSecCnt++;
}

int main(void)
{
	
	TCB2.CTRLA = 0x00;
	TCB2.INTCTRL = 0x00;
	
	TCB2.CCMP = 65359;
	TCB2.CTRLA |= TCB_CLKSEL1_bm;
	TCB2.INTCTRL |= TCB_CAPT_bm;
	TCB2.CTRLA |= TCB_ENABLE_bm;
	TCB2.INTFLAGS |= TCB_CAPT_bm; //Clear interrupt flag

	sei(); 
	
    /* Replace with your application code */
    while (1) 
    {
		
		_NOP();
		
    }
}

To my eyes our code is very similar yet my program never enters the ISR.

 

Regarding the interrupt type i am looking for an overflow type interrupt but the value is very much intended to be a little lower at 65359 or 0xFF4F.

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


It worked fine and reached a breakpoint in the ISR.

 

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


Could it be fuse related? I have the fuses set as follows

 

This gives me a bootloader section that ends at 0x1000 and an appcode section ending at 0x7E00

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

I have published all the code.
That is, the fuse does nothing with the default value after erasing.

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

So you have a bootloader?

 

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

Last Edited: Wed. May 11, 2022 - 02:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Blocky_G wrote:
To my eyes our code is very similar yet my program never enters the ISR.
Then slowly morph my example to your version and see what line "breaks" it ;-)

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

I'm thinking simulator bug. I cannot for the live of me get CNT to increment at all. My CLKSEL bits in CTRLA clear 1 cycle after I set them, like maybe the simulator thinks they are change-protected.

 

I'm running Studio version 7.0.2542

 

If I use TCB_CLKSEL_CLKDIV1_gc instead of TCB_CLKSEL1_bm it works. TCA0 must be configured to use TCB_CLKSEL1_bm.

Last Edited: Wed. May 11, 2022 - 11:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No not at the moment but it needs to be compatible with one later on and im using the NVMC controller for other parts of my project so i have to have it defined.

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

Did this but it still doesnt work on chip. Simulator works fine but not when programming it to the actual MCU. I even tried turning off optimization in case that had an effect. Checked the CPUINT.STATUS register and notced that the Level 0 Interrupt Executing (LVL0EX) bit is set. This to me indicates that the interrupt is being triggered but for some reason the ISR isnt being serviced. Could the fuses ive set have an effect on the interrupt vector table and cause the ISR function to be unlinked somehow?

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

Blocky_G wrote:
I even tried turning off optimization in case that had an effect.
It's more usually the case that stuff STOPS working when you disable optimization! (see #4 in my sig).

 

Have you checked the datasheet for errata? It could be that some functionality you are trying to use does not actually work as originally intended n the chip design.

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


Have checked the errdata documents and there is no mention of errors with the interrupt controller. There are errors related to TCB timers where none are directly related to this issue. There is a note on the event systems but from my understanding that shouldnt affect this issue.

 

Also retested using normal debug optimization and still have the same issue.

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

Your code in #3 does not compile !!

 

    TCB2.CTRLA = 0x00;
    TCB2.INTCTRL = 0x00;
    
    TCB2.CCMP = 65359;
    //TCB2.CTRLA |= TCB_CLKSEL_CLKDIV2_gc;  //div2
    //TCB2.CTRLA |= TCB_CLKSEL_1_bm;  //Use CLK_TCA from TCA0
    TCB2.CTRLA |= TCB_CLKSEL1_bm;  //ERROR : TCB_CLKSEL1_bm undeclared

 

If I use CLKDIV2 (like Cliff) everything works on a 4809.

So the real question is : why did you post TCB_CLKSEL1  ?

 

But the gobsmacking question is : why do people use |= when = would be more appropriate?

e.g. initialising SFRs

e.g. clearing interrupt flags.

 

David.

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

Figured out the issue. It is because of my fuse settings. The MCU i was using had been used on the main project and the fuse settings was still active (BOOTEND = 0x10 and APPEND = 0x7E), By default the vector table gets placed at the end of the BOOT section of the flash and when im doing my test programs im programming my code into the BOOT section. While im not super sure why this causes a mismatch and results in the IRS not being serviced unless you set the WRITE PROTECTED bit IVSEL to 1 in the CPUINT.CTRLA. This moves the vector table to the start of the boot section and make the ISR reachable.

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

The TCB_CLKSEL1 was a mistake. I misunderstood what the define represented.

 

As for the |= i do it out of habit. When i wanna change a single bit for only part of a byte i do that so in case i do other changes to the register i dont accidentally override it or block off future changes. Not really that married to it tho. Is there a big difference between using |= and = other than the OR operation?

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

Blocky_G wrote:

Is there a big difference between using |= and = other than the OR operation?

 

It's all about starting from a known, solid, position.

 

ALL initialisation of peripherals should be done using '=' so that you are making no assumptions about what's currently in the register. Once running, '|=' is fine EXCEPT you need to understand the risks involved in non-atomic access.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

Blocky_G wrote:
Is there a big difference between using |= and = other than the OR operation?
One can only set extra bits (and leaves anything already there alone). The other sets all 8 bits to known 0/1 states.

 

It's all very well do do something like:

ADCSRA = (1 << ADEN) | (1 << ADIE) | (0b101 << ADPS0);

    ... (later)
    
ADCSRA |= (1 << ADSC);

where != is used to "add in" the extra "start conversion" bit after all the other bits have already been set. But for that initial setting it's as well to use = not |= because now you absolutely guarantee that only ADEN/ADIE and some of the ADPS bits are set and know for a fact that the other things are in the known 0 state.

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

Definitely agree with that. That is also why i set the registers to 0x00 using = before starting to configure them.

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

Blocky_G wrote:
That is also why i set the registers to 0x00 using = before starting to configure them.
But why?? What is to be gained from:

ADCSRA = 0x00;
ADCSRA |= (1 << ADEN) | (1 << ADIE) | (0b101 << ADPS0);

when:

ADCSRA = (1 << ADEN) | (1 << ADIE) | (0b101 << ADPS0);

would achieve the same result in less code and less cycles (and is moderately clearer to the maintainer!)

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

Well this code in particular was yoinked from a library i was using. So in this case its a remnant of that. When trowing together a test project to figure out a bug in my code im fine with a couple of extra instructions before the main loop :P

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

You can follow whichever style makes you happy.

 

The important thing is that you THINK first.