1
  • I need to count the number of pulses in a moving window (e.g. last 60 seconds) using as less CPU time as possible.
  • The first step is to use TCC to capture pulse period.
  • The pulses arrive randomly at PB02 and the average frequency is about 20-100 counts per minute.

Attempt

According to datasheet TCC has Period and Pulse-Width (PPW) Capture Action

External pulse(PB02)-->EIC-->Event channel-->Event user(TC)

volatile uint32_t T;
volatile uint32_t count;
void setup() {
  Serial.begin(9600);
  while (!Serial);
  Serial.println("init");
  md_setup();
  // put your setup code here, to run once:

}

void loop() {
  Serial.println(T);
  Serial.println(count);  
}

void md_setup() {
  //PM
  REG_PM_APBCMASK |= PM_APBCMASK_EVSYS;
  REG_PM_APBCMASK |= PM_APBCMASK_TCC0;
  Serial.println("PM done");

  //GLCK
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(8) |
                    GCLK_GENDIV_ID(5);
  while (GCLK->STATUS.bit.SYNCBUSY);
  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                     GCLK_GENCTRL_GENEN |         // Enable GCLK 5
                     GCLK_GENCTRL_SRC_OSCULP32K |   // Set the clock source to 32K
                     GCLK_GENCTRL_ID(5);          // Set clock source on GCLK 5
  while (GCLK->STATUS.bit.SYNCBUSY);
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |
                     GCLK_CLKCTRL_GEN_GCLK5 |
                     GCLK_CLKCTRL_ID_TCC0_TCC1;
  while (GCLK->STATUS.bit.SYNCBUSY);
  Serial.println("GCLK done");

  //PORT
  //REG_PORT_PMUX1 |= PORT_PMUX_PMUXEN;
  //REG_PORT_PINCFG1 |= PORT_PINCFG_PMUXEN;
  REG_PORT_WRCONFIG1 = PORT_WRCONFIG_PINMASK(2) |           //select pin PB02 (EIC02)
                       PORT_WRCONFIG_PMUXEN |               //enable peripheral function
                       PORT_WRCONFIG_INEN |                 //input mode
                       PORT_WRCONFIG_PMUX(0) |              //select peripheral function A
                       PORT_WRCONFIG_WRPMUX |               //Write to PMUX register
                       PORT_WRCONFIG_WRPINCFG |             //Write to PINCFG register
                       (0 << 31);                           //Select lower pins
  Serial.println("PORT done");

  //EIC
  REG_EIC_EVCTRL |= EIC_EVCTRL_EXTINTEO2;                                 // Enable event output on external interrupt 2
  REG_EIC_CONFIG0 |= EIC_CONFIG_SENSE2_RISE;                            // Set event detecting rise
  REG_EIC_INTENCLR = EIC_INTENCLR_EXTINT2;                                // Clear the interrupt flag on channel 2
  REG_EIC_CTRL |= EIC_CTRL_ENABLE;                                        // Enable EIC peripheral
  //while (EIC->STATUS.bit.SYNCBUSY);
  Serial.println("EIC done");

  //EVSYS
  REG_EVSYS_CHANNEL |= EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT |                       //no output
                       EVSYS_CHANNEL_PATH_ASYNCHRONOUS |                          //async path
                       EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_2) |           // Select EIC2
                       EVSYS_CHANNEL_CHANNEL(1);                                  //Channel 1
  REG_EVSYS_USER |=  EVSYS_USER_USER(EVSYS_ID_USER_TCC0_EV_1) |                   //Select USER = TCC0
                     EVSYS_USER_CHANNEL(2);                                       //Select Channel 1 (N+1)
  //REG_EVSYS_CTRL |= CTRL_SWRST;
  Serial.println("EVSYS done");

  //TCC0
  REG_TCC0_CTRLA &= ~TCC_CTRLA_ENABLE;            // Disable TCC0 peripheral
  REG_TCC0_CTRLBCLR |= TCC_CTRLBCLR_DIR;          // Clear DIR bit to count up
  while (TCC0->SYNCBUSY.bit.CTRLB);               // Wait for (write) synchronization
  REG_TCC0_EVCTRL |= TCC_EVCTRL_TCEI0 |           // Enable the TCC event 0 input
                     //TCC_EVCTRL_TCEI1 |         // Enable the TCC event 1 input
                     TCC_EVCTRL_MCEI1 |           // Enable the match or capture channel 1 event input
                     TCC_EVCTRL_MCEI0 |           // Enable the match or capture channel 0 event input
                     TCC_EVCTRL_EVACT1_PPW;       // Select PPW
  REG_TCC0_CTRLA |= TCC_CTRLA_CPTEN1 |              // Enable capture on CC1
                    TCC_CTRLA_CPTEN0 |              // Enable capture on CC0
                    TCC_CTRLA_PRESCALER_DIV1;
  REG_TCC0_INTENSET = TCC_INTENSET_MC1 |            // Enable compare channel 1 (CC1) interrupts
                      TCC_INTENSET_MC0;             // Enable compare channel 0 (CC0) interrupts
  REG_TCC0_CTRLA |= TCC_CTRLA_ENABLE;             // Enable TCC0
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
  
  NVIC_SetPriority(TCC0_IRQn, 0);      
  NVIC_EnableIRQ(TCC0_IRQn);           
  Serial.println("TCC done");
}

void TCC0_Handler()                             
{
  count++;
  if (TCC0->INTFLAG.bit.MC0)
  {
    T = TCC0->CC[0].reg;                 
  }
  if (TCC0->INTFLAG.bit.MC1);
}

Problem

  • The result (T,count) is always zero.
  • if while (EIC->STATUS.bit.SYNCBUSY); is uncommitted, the execution will lock up there.
7E10FC9A
  • 209
  • 1
  • 6
  • I've been playing around with something very similar; not sure if [the code](https://github.com/ocrdu/Arduino_SAMD21_pulse_frequency_counters) does exactly what you want, but maybe it will help you. – ocrdu Jan 14 '22 at 17:10

0 Answers0