I'm trying to set up HW timer/counter interrupts on an Arduino Nano Every with the ATMega4809 chip, but they seem to not work the way I expect, specifically the interrupts come at a rate independent from the counter.
int main()
{
CPU_CCP = 0xD8;
CLKCTRL.MCLKCTRLB = 0b00001011; // cpu clock prescale
PORTD.DIR = 255;
PORTB.DIR |= 0b00000001;
PORTMUX.TCAROUTEA = PORTMUX_TCA0_PORTB_gc;
TCA0.SINGLE.CTRLA = TCA_SINGLE_ENABLE_bm;
TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm | TCA_SINGLE_WGMODE_SINGLESLOPE_gc;
TCA0.SINGLE.INTCTRL = TCA_SINGLE_CMP0_bm;
TCA0.SINGLE.PER = 1024;
TCA0.SINGLE.CMP0 = 512;
sei();
while(1);
}
ISR(TCA0_CMP0_vect) {
PORTD.IN = 255; // XOR output
}
This is a stripped down test code that basically flips the state of all pins of a port on each compare interrupt (in theory). The signal on the counter output (PB0) is correct (about 243 Hz). With this code I thought the pins of PORTD would output a square wave with half the timer frequency. But they don't, in fact the frequency is even higher (around 4300 Hz) and doesn't even change if i change the PER or CMP0 values, it's only affected by the main cpu clock. My question of course is why does this happen, and more importantly how to make it work the way described above?
Thanks in advance.
Unlike the older AVR architecture, that cleared corresponding interrupt flag automatically, new xmega based avrs don't have this auto-clean feature and it's running as fast as it can.
I've confirmed it on my Curiosity Nano board (but I'm toggling PF5 on visible frequency) and there is no issue if you are clearing the flag:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/xmega.h>
int main(void) {
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, CLKCTRL_PDIV_64X_gc | CLKCTRL_PEN_bm);
PORTF.DIRSET = PIN5_bm;
TCA0.SINGLE.CTRLA = TCA_SINGLE_ENABLE_bm;
TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm | TCA_SINGLE_WGMODE_SINGLESLOPE_gc;
TCA0.SINGLE.INTCTRL = TCA_SINGLE_CMP0_bm;
TCA0.SINGLE.PER = 25000-1;
TCA0.SINGLE.CMP0 = 12500;
sei();
while(1);
}
ISR(TCA0_CMP0_vect) {
PORTF.OUTTGL = PIN5_bm; // but .IN works too
TCA0.SINGLE.INTFLAGS = TCA_SINGLE_CMP0_bm;
}