ATtiny1616 Can't Get TCA0 OVF Interrupt to Fire

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

Hello everyone,

 

I had this working on this same PCB (though I get the same behaviour on 2 different PCB's so I don't suspect this is a h/w issue).  But something has changed and I don't get the interrupt firing for the TCA0 overflow anymore.  And I can't figure it out.

 

I have gone through the Introduction to Attiny TCA0 about 100 times and I believe I have everything set up correctly.

 

The project uses Atmel Start to configure the I/O and general project structure.   While I have added my own code, all the code other than very basic code to get timer to run again has been commented out.  From the debugger I can see that the TCA0.SINGLE.CNT register increments and when it gets to the TCA0.SINGLE.PER register value (0x3E8) then the OVF flag is set and the CNT register is reset to 0x0 and starts again.  That is all good.  But the interrupt will NOT fire.

 

I have confirmed that the global interrupt enable bit is set and that the TCA0.INTCTRL.OVF bit is set.  I do notice that the CMPx flag bits are also set when the OVF bit is set.  I don't recall seeing that before but I wasn't looking specifically for it.  It sort of makes sense since the CMP registers are all at their default 0x0 value so when the CNT is reset to 0x0 the comparators would all toggle.  The bits to enable the CMPx interrupts are all OFF, though as I mentioned I still see the flags being set using the debugger window.

 

I checked the errata and didn't see anything relevant.

 

Atmel Studio 7.0.2397 with its default compiler

 

Here is the code.  Atmel Start has timer configured with enable already set and it runs (as confirmed).

 

~~~~~~~~~~~~~~~~~~~~

main.c

 

int main(void)
{
    /* Initializes MCU, drivers and middleware */
    atmel_start_init();

    // turn on global interrupts
    cpu_irq_enable();

 

while(1)
{
    if( TCA0.SINGLE.CNT >= 0x3D0 )  // just for debugging, same behaviour with and without this line
        asm("nop");
}
   

 

~~~~~~~~~~~~~~~~~~~~

tick.c

 

ISR(TCA0_OVF_vect)
{
    TickCnt++;

    /* The interrupt flag has to be cleared manually */
    TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm;
}

 

This topic has a solution.
Last Edited: Mon. Mar 16, 2020 - 03:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Futhermore, I can verify that the interrupt routine is getting compiled in.  From the .lss file:

 

The vector table with the TCA0 overflow interrupt being vector 8:

 

Disassembly of section .text:

00000000 <__vectors>:
   0:    0c 94 3e 00     jmp    0x7c    ; 0x7c <__ctors_end>
   4:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
   8:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
   c:    0c 94 1f 01     jmp    0x23e    ; 0x23e <__vector_3>
  10:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  14:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  18:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  1c:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  20:    0c 94 92 06     jmp    0xd24    ; 0xd24 <__vector_8>
  24:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  28:    0c 94 67 00     jmp    0xce    ; 0xce <__vector_10>
  2c:    0c 94 a0 00     jmp    0x140    ; 0x140 <__vector_11>
  30:    0c 94 d9 00     jmp    0x1b2    ; 0x1b2 <__vector_12>
  34:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  38:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  3c:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  40:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  44:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  48:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  4c:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  50:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  54:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  58:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  5c:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  60:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  64:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  68:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  6c:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  70:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  74:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>
  78:    0c 94 5b 00     jmp    0xb6    ; 0xb6 <__bad_interrupt>

 

 

 

and right at the bottom:

 

00000d24 <__vector_8>:

// timer is clocked by 10MHz (CPU clock), period set to 1000 cycles so we
// should get overflow every 1,000 10MHz cycles i.e. 100us
//
ISR(TCA0_OVF_vect)
{
 d24:    1f 92           push    r1
 d26:    0f 92           push    r0
 d28:    00 90 3f 00     lds    r0, 0x003F    ; 0x80003f <__RODATA_PM_OFFSET__+0x7f803f>
 d2c:    0f 92           push    r0
 d2e:    11 24           eor    r1, r1
 d30:    2f 93           push    r18
 d32:    8f 93           push    r24
 d34:    9f 93           push    r25
 d36:    af 93           push    r26
 d38:    bf 93           push    r27
 d3a:    ef 93           push    r30
 d3c:    ff 93           push    r31
 d3e:    cf 93           push    r28
 d40:    df 93           push    r29
 d42:    cd b7           in    r28, 0x3d    ; 61
 d44:    de b7           in    r29, 0x3e    ; 62
    /* Insert your TCA overflow interrupt handling code */
    /* Insert your TCA Compare 0 Interrupt handling code here */
    TickCnt++;
 d46:    80 91 01 38     lds    r24, 0x3801    ; 0x803801 <TickCnt>
 d4a:    90 91 02 38     lds    r25, 0x3802    ; 0x803802 <TickCnt+0x1>
 d4e:    a0 91 03 38     lds    r26, 0x3803    ; 0x803803 <TickCnt+0x2>
 d52:    b0 91 04 38     lds    r27, 0x3804    ; 0x803804 <TickCnt+0x3>
 d56:    01 96           adiw    r24, 0x01    ; 1
 d58:    a1 1d           adc    r26, r1
 d5a:    b1 1d           adc    r27, r1
 d5c:    80 93 01 38     sts    0x3801, r24    ; 0x803801 <TickCnt>
 d60:    90 93 02 38     sts    0x3802, r25    ; 0x803802 <TickCnt+0x1>
 d64:    a0 93 03 38     sts    0x3803, r26    ; 0x803803 <TickCnt+0x2>
 d68:    b0 93 04 38     sts    0x3804, r27    ; 0x803804 <TickCnt+0x3>

    /* The interrupt flag has to be cleared manually */
    TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm;
 d6c:    80 e0           ldi    r24, 0x00    ; 0
 d6e:    9a e0           ldi    r25, 0x0A    ; 10
 d70:    21 e0           ldi    r18, 0x01    ; 1
 d72:    fc 01           movw    r30, r24
 d74:    23 87           std    Z+11, r18    ; 0x0b
}
 d76:    00 00           nop
 d78:    df 91           pop    r29
 d7a:    cf 91           pop    r28
 d7c:    ff 91           pop    r31
 d7e:    ef 91           pop    r30
 d80:    bf 91           pop    r27
 d82:    af 91           pop    r26
 d84:    9f 91           pop    r25
 d86:    8f 91           pop    r24
 d88:    2f 91           pop    r18
 d8a:    0f 90           pop    r0
 d8c:    00 92 3f 00     sts    0x003F, r0    ; 0x80003f <__RODATA_PM_OFFSET__+0x7f803f>
 d90:    0f 90           pop    r0
 d92:    1f 90           pop    r1
 d94:    18 95           reti

 

 

So that all looks good.  But no interrupt is firing!

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

You seem to be confusing output compare (interrupt) with overflow (interrupt).

A timer overflow interrupt occurs when the timer rolls over from its max value (255 or 65535) back to zero!

A timer compare match interrupt occurs when the timer reaches the same value as the compare reg!

 

As you seem to say you have it set up to clear on compare match, then it will never reach its max value, so will not produce the overflow interrupt.

Jim

 

 

 

 

 

Last Edited: Thu. Feb 6, 2020 - 07:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the feedback Jim, but I don't think that is the case.  From the datasheet:

 

During normal operation, the counter value is continuously compared to zero and the period (PER) value
to determine whether the counter has reached TOP or BOTTOM.

So when counting up, the OVF flag is set when the CNT register reaches the PER(iod) value.  Then it resets the CNT register to 0x0.

 

With the 3 compare registers all being a value of 0x0 (because I'm not using them) I think the CMPx flags are set at this point.  Strange because I have the interupt enable turned off for each of those, but maybe the flags are still set, the interrupt engine just ignores it.

 

James.

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

Your relevant code is not shown. Create a simple+complete example that anyone can compile and provide help. Here is a tca0 example for a tiny817 which simply blinks a light every second. This happens to work and the led blinks, so there cannot be much to it with this little code. While you are creating your example you will most likely run across your problem and see what is wrong, if not then post your simple example.

 

//tiny817 Xplained, default 3.33MHz
#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint8_t ticks;
ISR(TCA0_OVF_vect){
    TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm;
    if( ++ticks < 25 ) return; //3.33MHz/65536/25 = 0.5Hz
    ticks = 0;
    VPORTC.IN |= PIN0_bm; //led toggle
}

int main(){
    VPORTC.DIR |= PIN0_bm; //PC0 = led output
    TCA0.SINGLE.INTCTRL = TCA_SINGLE_OVF_bm;
    TCA0.SINGLE.CTRLA = TCA_SINGLE_ENABLE_bm;
    sei();
    for(;;){}
}

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

curtvm wrote:

Your relevant code is not shown. Create a simple+complete example that anyone can compile and provide help. Here is a tca0 example for a tiny817 which simply blinks a light every second. This happens to work and the led blinks, so there cannot be much to it with this little code. While you are creating your example you will most likely run across your problem and see what is wrong, if not then post your simple example.

 

//tiny817 Xplained, default 3.33MHz
#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint8_t ticks;
ISR(TCA0_OVF_vect){
    TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm;
    if( ++ticks < 25 ) return; //3.33MHz/65536/25 = 0.5Hz
    ticks = 0;
    VPORTC.IN |= PIN0_bm; //led toggle
}

int main(){
    VPORTC.DIR |= PIN0_bm; //PC0 = led output
    TCA0.SINGLE.INTCTRL = TCA_SINGLE_OVF_bm;
    TCA0.SINGLE.CTRLA = TCA_SINGLE_ENABLE_bm;
    sei();
    for(;;){}
}

 

This is an Atmel Start project, so it's difficult to give the complete project.  I did start from scratch again with a fresh project and it worked as expected.  But my full project with most of the application code won't work and I can't figure out why.  I have commented out all of the application code to leave what was left above and it still doesn't work.

 

I guess the only thing I'm missing is the Atmel Start configuration, but that is difficult to post.

 

What I can tell you is that this did work as the timer was the first thing I got running with Atmel Start so I had a reliable timing mechanism.  Somewhere along the line it stopped working somehow.

 

James.

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

One suspect would be inadvertently enabling an interrupt. Once you get to __bad_interrupt(), it will jump to 0 and start over, but the LVL0EX bit will remain set and you will get no more interrupts.

 

add to your code-

 

void __vector_default(){
    asm("nop"); //set breakpoint here
}

 

Run your code in the debugger and if you hit that line you have enabled an interrupt you do not have an isr for. If that happens, you can continue to run the code and see what happens- take a look at the LVL0EX bit in CPUINT.STATUS and see that it is still set. Until you get to a reti that clears it, no more interrupts, since no more interrupts, no more reti. You see the problem.

 

 

>I guess the only thing I'm missing is the Atmel Start configuration, but that is difficult to post.

 

They have a 'save configuration' option that I assume could be posted here and anyone could load it into atmel start. Its still possible the configuration is not the problem, although they seem to allow you to enable a peripheral interrupt without also generating the isr so that could be a possible problem.

Last Edited: Fri. Feb 7, 2020 - 12:22 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks @curtvm.  I believe that was the problem.  I ended up rebuilding the project from the ground up as I only had a handful of drivers in the Atmel Start project.  I had been experimenting with a few things and likely got into a state where there was an ISR enabled but nothing defined for it.

 

My project runs fine now.  I added your suggested default ISR handler as good practice for future debugging.

 

Thanks again.