MKRZero PPW Catpture on Timer TC4

Well my brain is fried trying to get this to work.
I have a MKRZero that I am trying to use the PPW Capture feature for the TC so I can measure the frequency and pulse width. The test signal I am trying to read is a 40ms period and 24ms width on digital pin 0.

I know the counter works cause COUNT seems to show a 1 usec rate.

It is constantly triggering the overflow interrupt, and CC0 and CC1 always read 0.

void tc4Configure(){
  //counter clock is defined in GCLKConfigure()

  //Serial.println("tc4Configure...");
  tc4Reset(); 
  //Port I/O Configuration for TC4
  //Port PA22 
  PORT->Group[PORTA].CTRL.reg       |= PORT_CTRL_SAMPLING(22); //Continous sampling of PA22 enabled
  PORT->Group[PORTA].DIRCLR.reg     |= PORT_DIRCLR_DIRCLR(22); //Configure PA22 as input
  PORT->Group[PORTA].PMUX[22].reg   |= PORT_PMUX_PMUXE_E; //Configurre PA22 to peripheral E (TC)
  PORT->Group[PORTA].PINCFG[22].reg |= PORT_PINCFG_PMUXEN; //perpheral multiplexer enabled
  //PM Configuration for TC4 and PPW
  //turn on clock for TC4
  PM->APBCMASK.reg |= PM_APBCMASK_TC4
                    | PM_APBCMASK_EVSYS;
  //set tc4 to 16bits, prescaler 8, enable catpure on channel 0 and 1, not in inverted mode,   
  TC4->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16
                         | TC_CTRLA_PRESCALER_DIV8;
  while(TC4->COUNT16.STATUS.bit.SYNCBUSY);
  //both capture channels must be enabled to record period and pulse width 
  //TC4->COUNT16.READREQ |= TC_READREQ_RREQ
  //                      | TC_READREQ_ADDR(0x06);
  while(TC4->COUNT16.STATUS.bit.SYNCBUSY);
  TC4->COUNT16.CTRLC.reg |= TC_CTRLC_CPTEN0 
                          | TC_CTRLC_CPTEN1;
  while(TC4->COUNT16.STATUS.bit.SYNCBUSY);
  //enabled capture channel 0 and input event
  TC4->COUNT16.EVCTRL.reg |= TC_EVCTRL_EVACT_PPW //PERIOD IN CC0, PULSE WIDTH IN CC1
                            | TC_EVCTRL_TCEI
                            | TC_EVCTRL_MCEO0
                            | TC_EVCTRL_MCEO1;
  //enable interrupts for channel 0 and 1 capture, capture overflow errors
  TC4->COUNT16.INTENSET.reg |= TC_INTENSET_MC0 
                             | TC_INTENSET_MC1
                             | TC_INTENSET_OVF
                             | TC_INTENSET_ERR;
  //There might be some work needed with EVSYS config

  REG_EIC_EVCTRL |= EIC_EVCTRL_EXTINTEO6;    //Enable event output on external interrupt 6
  attachInterrupt(0, NULL, HIGH);         //Attach interrupts to digital pin 0 (external interrupt 6)

  REG_EVSYS_USER = EVSYS_USER_CHANNEL(1)                              //Attach the event user (receiver) to channel 0 (n+1)
                 | EVSYS_USER_USER(EVSYS_ID_USER_TC4_EVU);            //Set the event user (receiver) as timer TC3
  REG_EVSYS_CHANNEL = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT               //No event edge detection
                    | EVSYS_CHANNEL_PATH_ASYNCHRONOUS                 //Set event path as asynchronous
                    | EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_6)  //Set event generator (sender) as external interrupt 6
                    | EVSYS_CHANNEL_CHANNEL(0);                       //Attach the generator (sender) to channel 0

  //enable timer
  TC4->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
  while(TC4->COUNT16.STATUS.bit.SYNCBUSY);

  NVIC_SetPriority(TC4_IRQn, 4);
  NVIC_EnableIRQ(TC4_IRQn);
  Serial.println("Done with TC4Configure");

void loop() {
  // put your main code here, to run repeatedly:
  //Serial.println("loop");
  uint16_t val = TC4->COUNT16.COUNT.reg;
  uint16_t period = TC4->COUNT16.CC[1].reg;
  uint16_t width = TC4->COUNT16.CC[0].reg;
  Serial.print("Period: ");
  Serial.println(period);
  Serial.print("Width: ");
  Serial.println(width);
  Serial.print("State:");
  Serial.println(digitalRead(0));
    //val = TC5->COUNT16.COUNT.reg;
  //Serial.print("TC5 Count:");
  //Serial.println(val);
  delay(1000);
}
}


[Ign and Inj Timing.ino|attachment](upload://cJD5RB6lDUzHznA5K5fQ6mr4HQe.ino) (6.3 KB)

Well I got this working.

Couple issues I had were:

  1. Didn't power EIC or provide it a GCLK
  2. The PMUX should have been directed to A, as that is directed to EXTINT6 since there is no way to directly sent the PA22 input signal to TC4 or any of the timer.
  3. I removed the duration function attachInterrupt cause I don't know if it was going to give me the correct interrupt settings I wanted.

  Serial.println("tc4Configure...");
  tc4Reset(); 

  //PM Configuration for TC4 and PPW
  //turn on clock for TC4
  PM->APBCMASK.reg |= PM_APBCMASK_TC4
                    | PM_APBAMASK_EIC
                    | PM_APBCMASK_EVSYS;


  //counter clock is defined in GCLKConfigure()
  //Port I/O Configuration for External Interrupt 6
  //Port PA22 
  PORT->Group[PORTA].CTRL.reg       |= PORT_CTRL_SAMPLING(22); //Continous sampling of PA22 enabled
  PORT->Group[PORTA].DIRCLR.reg     |= PORT_DIRCLR_DIRCLR(22); //Configure PA22 as input
  //PORT->Group[PORTA].PMUX[22].reg   |= PORT_PMUX_PMUXE_E; //Configurre PA22 to peripheral E (TC) This is wrong for capture event
  PORT->Group[PORTA].PMUX[22/2].reg   |= PORT_PMUX_PMUXE_A;  //Conffigure PA22 tp peripheral A (External Interrupt 6)
  PORT->Group[PORTA].PINCFG[22].reg |= PORT_PINCFG_PMUXEN; //perpheral multiplexer enabled
  PORT->Group[PORTA].PINCFG[22].reg |= PORT_PINCFG_INEN; //input buffer enabled
  //PORT->Group[PORTA].PINCFG[22].reg |= PORT_PINCFG_PULLEN; //internal pull up resistor enabled

  EIC->CONFIG[0].bit.SENSE6 = EIC_CONFIG_SENSE6_HIGH_Val; // None of RISE,FALL or BOTH work for this WHY????
  EIC->EVCTRL.bit.EXTINTEO6 = 1; // Enable event output on external interrupt 6 

  // The EIC interrupt is only for verifying the input
  //REG_EIC_INTENSET = EIC_INTFLAG_EXTINT6;
  //NVIC_EnableIRQ(EIC_IRQn);
  //while (EIC->STATUS.reg & EIC_STATUS_SYNCBUSY);

  //set tc4 to 16bits, prescaler 8, enable catpure on channel 0 and 1, not in inverted mode,   
  TC4->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16
                         | TC_CTRLA_PRESCALER_DIV8;
  while(TC4->COUNT16.STATUS.bit.SYNCBUSY);
  //both capture channels must be enabled to record period and pulse width 
  TC4->COUNT16.CTRLC.reg |= TC_CTRLC_CPTEN0 
                          | TC_CTRLC_CPTEN1;
  while(TC4->COUNT16.STATUS.bit.SYNCBUSY);
  //enabled capture channel 0 and input event
  TC4->COUNT16.EVCTRL.reg |= TC_EVCTRL_EVACT_PPW //PERIOD IN CC0, PULSE WIDTH IN CC1
                            | TC_EVCTRL_TCEI
                            | TC_EVCTRL_MCEO0
                            | TC_EVCTRL_MCEO1;
  //enable interrupts for channel 0 and 1 capture, capture overflow errors
  TC4->COUNT16.INTENSET.reg |= TC_INTENSET_MC0 
                             | TC_INTENSET_MC1
                             | TC_INTENSET_OVF
                             | TC_INTENSET_ERR;
  NVIC_EnableIRQ(TC4_IRQn);
  
  REG_EVSYS_USER = EVSYS_USER_CHANNEL(2)                              //Attach the event user (receiver) to channel 1 (n-1)
                 | EVSYS_USER_USER(EVSYS_ID_USER_TC4_EVU);            //Set the event user (receiver) as timer TC4
  while(!EVSYS->CHSTATUS.bit.USRRDY1);

  REG_EVSYS_CHANNEL = EVSYS_CHANNEL_PATH_ASYNCHRONOUS                 //Set event path as asynchronous
                    | EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_6)  //Set event generator (sender) as external interrupt 6
                    | EVSYS_CHANNEL_CHANNEL(1);                       //Attach the generator (sender) to channel 
  while(EVSYS->CHSTATUS.bit.CHBUSY1);

  //enable timer
  TC4->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
  while(TC4->COUNT16.STATUS.bit.SYNCBUSY);

  Serial.println("Done with TC4Configure");

Also I will note this helped me solve this the most.
https://community.atmel.com/forum/samd21-tcc-ppw-or-pwp-capture-how

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.