Hi fellow Cortex Programmers,
I am trying to measure AC output signal frequency by looping the AC output signal through EVSYS to TC4 and creating interrupts from TC4.
The setup is fairly simple:
void setup() { __disable_irq(); // Setup Frequency Measurement Components setup_evsys(); setup_tc4(); setup_ac0(); __enable_irq(); }
Channel 0 of EVSYS is enabled with AC output as event generator and TC4 as event user.
void setup_evsys() { REG_PM_APBCMASK |= PM_APBCMASK_EVSYS; /* EVSYS APB Clock Enable */ REG_GCLK_CLKCTRL = GCLK_CLKCTRL_GEN(0) /* Generic Clock Generator */ | GCLK_CLKCTRL_ID_EVSYS_0 /* Generic Clock Selection ID */ | GCLK_CLKCTRL_CLKEN; /* Clock Enable */ while ( REG_GCLK_STATUS & GCLK_STATUS_SYNCBUSY ) {}; REG_EVSYS_USER = EVSYS_USER_CHANNEL(0x1) /* Channel Event Selection */ | EVSYS_USER_USER(EVSYS_ID_USER_TC4_EVU); /* User Multiplexer Selection */ REG_EVSYS_CHANNEL = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT /* Edge Detection Selection */ | EVSYS_CHANNEL_PATH_ASYNCHRONOUS /* Path Selection */ | EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_AC_COMP_0) /* Event Generator Selection */ | EVSYS_CHANNEL_CHANNEL(0x1); /* Channel Selection */ }
TC4 uses Period and Pulse-Width Capture Action to create interrupts.
void setup_tc4() { REG_PM_APBCMASK |= PM_APBCMASK_TC4; /* TC APB Clock Enable */ //REG_TC4_CTRLA = TC_CTRLA_SWRST; /* Software Reset */ //while ( REG_TC4_STATUS & TC_STATUS_SYNCBUSY ) {}; REG_GCLK_CLKCTRL = GCLK_CLKCTRL_GEN(0) /* Generic Clock Generator */ | GCLK_CLKCTRL_ID_TC4_TC5 /* Generic Clock Selection ID */ | GCLK_CLKCTRL_CLKEN; /* Clock Enable */ while ( REG_GCLK_STATUS & GCLK_STATUS_SYNCBUSY ) {}; REG_TC4_CTRLA = TC_CTRLA_PRESCSYNC_GCLK /* Prescaler and Counter Synchronization */ | TC_CTRLA_PRESCALER_DIV1 /* Prescaler */ | TC_CTRLA_MODE_COUNT32; /* Timer Counter Mode */ while ( REG_TC4_STATUS & TC_STATUS_SYNCBUSY ) {}; REG_TC4_CTRLC = TC_CTRLC_CPTEN0 | TC_CTRLC_CPTEN1; /* Capture Channel 0 & 1 Enable */ while ( REG_TC4_STATUS & TC_STATUS_SYNCBUSY ) {}; REG_TC4_EVCTRL = TC_EVCTRL_TCEI /* TC Event Enable */ | TC_EVCTRL_EVACT_PPW; /* Event Action */ while ( REG_TC4_STATUS & TC_STATUS_SYNCBUSY ) {}; REG_TC4_INTENSET = TC_INTENSET_MC0 | TC_INTENSET_MC1; /* Match or Capture Channel 0 & 1 Interrupt Enable */ while ( REG_TC4_STATUS & TC_STATUS_SYNCBUSY ) {}; REG_TC4_COUNT32_COUNT = 0; /* Counter Value */ while ( REG_TC4_STATUS & TC_STATUS_SYNCBUSY ) {}; NVIC_EnableIRQ(TC4_IRQn); REG_TC4_CTRLA |= TC_CTRLA_ENABLE; /* Enable: enable */ while ( REG_TC4_STATUS & TC_STATUS_SYNCBUSY ) {}; }
Upon TC4 interrupt a flag is set and the corresponding capture value is copied.
void TC4_Handler() { if (REG_TC4_INTFLAG & TC_INTFLAG_MC0) { zero_cross = 1; /* Set zero_cross flag */ capture = REG_TC4_COUNT32_CC0; /* Copy capture 0 value */ } else { zero_cross = 2; /* Set zero_cross flag */ capture = REG_TC4_COUNT32_CC1; /* Copy capture 1 value */ } }
AC runs continuously and creates events with its output.
void setup_ac0() { REG_PM_APBCMASK |= PM_APBCMASK_AC; /* AC APB Clock Enable */ //REG_AC_CTRLA = AC_CTRLA_SWRST; /* Software Reset */ //while ( REG_AC_STATUSB & AC_STATUSB_SYNCBUSY ) {}; REG_GCLK_CLKCTRL = GCLK_CLKCTRL_GEN(0) /* Generic Clock Generator */ | GCLK_CLKCTRL_ID_AC_DIG /* Generic Clock Selection ID */ | GCLK_CLKCTRL_CLKEN; /* Clock Enable */ while ( REG_GCLK_STATUS & GCLK_STATUS_SYNCBUSY ) {}; REG_GCLK_CLKCTRL = GCLK_CLKCTRL_GEN(0) /* Generic Clock Generator */ | GCLK_CLKCTRL_ID_AC_ANA /* Generic Clock Selection ID */ | GCLK_CLKCTRL_CLKEN; /* Clock Enable */ while ( REG_GCLK_STATUS & GCLK_STATUS_SYNCBUSY ) {}; REG_PORT_DIRCLR0 = PORT_PA05 | PORT_PA06; /* Port Data Direction Clear */ PORT->Group[0].PINCFG[5].reg |= PORT_PINCFG_PMUXEN; /* Peripheral Multiplexer Enable */ PORT->Group[0].PINCFG[6].reg |= PORT_PINCFG_PMUXEN; /* Peripheral Multiplexer Enable */ PORT->Group[0].PMUX[5].reg |= PORT_PMUX_PMUXO_B; /* Peripheral Multiplexing for Odd-Numbered Pin */ PORT->Group[0].PMUX[6].reg |= PORT_PMUX_PMUXE_B; /* Peripheral Multiplexing for Even-Numbered Pin */ //REG_AC_INTENSET = AC_INTENSET_COMP0; REG_AC_COMPCTRL0 = AC_COMPCTRL_FLEN_OFF /* Filter Length */ | AC_COMPCTRL_HYST /* Hysteresis Enable */ | AC_COMPCTRL_SWAP /* Swap Inputs and Invert */ | AC_COMPCTRL_MUXPOS_PIN1 /* Positive Input Mux Selection */ | AC_COMPCTRL_MUXNEG_PIN2 /* Negative Input Mux Selection */ // | AC_COMPCTRL_INTSEL_TOGGLE | AC_COMPCTRL_SPEED_HIGH /* Speed Selection */ & ~AC_COMPCTRL_SINGLE /* Single-Shot Mode */ | AC_COMPCTRL_ENABLE; /* Enable */ while ( REG_AC_STATUSB & AC_STATUSB_SYNCBUSY ) {}; REG_AC_EVCTRL = AC_EVCTRL_COMPEO0; /* Comparator 0 Event Output Enable */ while ( REG_AC_STATUSB & AC_STATUSB_SYNCBUSY ) {}; //NVIC_EnableIRQ(AC_IRQn); REG_AC_CTRLA |= AC_CTRLA_ENABLE; /* Enable: enable */ while ( REG_AC_STATUSB & AC_STATUSB_SYNCBUSY ) {}; }
When not using EVSYS + TC4 and instead directly creating interrupts with AC and measuring frequency with micros() or millis(), I get interrupts and plausible results. So the AC setup seems to be fine.
void AC_Handler() { if (REG_AC_INTFLAG & AC_INTFLAG_COMP0) { zero_cross = 1; /* Set zero_cross flag */ REG_AC_INTFLAG = AC_INTFLAG_COMP0; /* Clear Intflag */ } }
When trying to take the AC + EVSYS + TC4 route the TC4 interrupt handler never is called and frequency is never measured. The problem must lie somewhere in EVSYS setup or TC4 setup or TC4 interrupt handler.
I have read the manual up and down and looked for examples on g**gle but without success. I have never programmed a microcontroller and help is much appreciated!
Cheers :)