SAMD21 - Measuring AC OUT Frequency with TC

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

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 :)

This topic has a solution.
Last Edited: Sun. May 15, 2022 - 08:53 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Never used TC_EVCTRL_EVACT_PPW. Therefore, no idea really.

 

Other than that I remember I had an issue with EVSYS_CHANNEL_PATH_ASYNCHRONOUS. Therefore I would probably try EVSYS_CHANNEL_PATH_SYNCHRONOUS and EVSYS_CHANNEL_EDGSEL_BOTH_EDGES, just to see if you get any TC interrupts that way.

 

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

ezharkov wrote:

Never used TC_EVCTRL_EVACT_PPW. Therefore, no idea really.

 

Other than that I remember I had an issue with EVSYS_CHANNEL_PATH_ASYNCHRONOUS. Therefore I would probably try EVSYS_CHANNEL_PATH_SYNCHRONOUS and EVSYS_CHANNEL_EDGSEL_BOTH_EDGES, just to see if you get any TC interrupts that way.

 

 

Thank you for your quick reply!

 

 

Fortunately I have just found the solution. While reading couple random blog posts on EVSYS + TC, I noticed that the input number used for EVSYS channel selection differs between generator and user.

 

 

When setting user channel, one needs to increment by one:

EVSYS_USER_CHANNEL(<channel> + 1)

 

When setting generator channel, one must not increment by one (here was the issue):

EVSYS_CHANNEL_CHANNEL(<channel>)
Last Edited: Sun. May 15, 2022 - 08:54 AM