Go Down

Topic: Arduino Zero TCC Capture (Read 14357 times) previous topic - next topic

MartinL

#30
Jul 29, 2016, 09:56 am Last Edit: Jul 29, 2016, 10:50 am by MartinL
Hi Ruch,

The forum member "moresun" flagged up this problem with reading the TCC timer COUNT registers in the past: https://forum.arduino.cc/index.php?topic=341655.0. My apologies for not mentioning it sooner.

Since then this issue has remained unresolved. Reading the COUNT register seems to work for the TC timers, but not for the TCC. This issue doesn't appear in the SAMD21 datasheet errata, so unless wer're both doing something wrong, it might be a hardware bug in the processor.

Rucus

Hi MartinL,

Thanks for your reply. And no need to apologize really. :) Everything is fine.

Indeed I think we are all experiencing the same problem. I tried a few more combinations and re-read that portion of the datasheet but still I can't make it to work. Very interesting though that SAMD21G has a total of 6 Timer/Counter modules (TCC0-2, TC3-5) but technically you can only use 2 when using external clock as clock source. This is because you can only feed this clock to any pair of them (TCC0-TCC1 pair, TCC2-TC3 pair, TC4-TC5 pair). TCC0 and TCC1 seem to not work, so basically I can only use the remaining 2. :)

I will see if I can use Events and count those instead of feeding the external pulses to TC modules as clock. But I am not sure if it will be fast enough. Do you think I can count events (maybe rising edge) with max frequency of 150kHz using the TC and/or TCC modules?

Thanks,
Ruch

Rucus

My seem to work now. I can read the COUNT register of TCC0 module.

Here's what my initialization code looks like:

Code: [Select]
PORT->Group[0].WRCONFIG.reg = PORT_WRCONFIG_HWSEL |
 PORT_WRCONFIG_WRPMUX | PORT_WRCONFIG_PMUXEN | PORT_WRCONFIG_PMUX( PORT_PMUX_PMUXE_H_Val ) | //PMUX set to H alternate function
 PORT_WRCONFIG_INEN |
 PORT_WRCONFIG_WRPINCFG |
 PORT_WRCONFIG_PINMASK( 0xC0 ) ; //Mask other pins except PA22 and PA23
  
  Serial.println("Done PORT config");
  
  /* Set GCLK */
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(10) |    // Divide the input clock by (x)
                    GCLK_GENDIV_ID(6);      // Set division on Generic Clock Generator (GCLK) 6
  while (GCLK->STATUS.bit.SYNCBUSY);        // Wait for synchronization
  Serial.println("Done GCLK GENDIV config for first clock");
  
  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
 GCLK_GENCTRL_GENEN |         // Enable GCLK 6
                     GCLK_GENCTRL_SRC_GCLKIN |    // Set the clock source to generator input pad, set to PA22 in PORT module
                     GCLK_GENCTRL_ID(6);          // Set clock source on GCLK 6
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization*/
  Serial.println("Done GCLK GENCTRL config for first clock");
  
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable the generic clock...
                     GCLK_CLKCTRL_GEN_GCLK6 |     // ....on GCLK6
                     GCLK_CLKCTRL_ID_TCC0_TCC1;    // Feed the GCLK6 to TCC0/1
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization
  Serial.println("Done GCLK CLKCTRL config for first clock");
  
  while (TCC0->SYNCBUSY.bit.CTRLB);                
  REG_TCC0_CTRLBCLR = TCC_CTRLBCLR_DIR;                     // Set register for counter to count up
  while (TCC0->SYNCBUSY.bit.CTRLB);       // Wait for (write) synchronization
  Serial.println("Done TC CTRLB config for third timer");
  
  TCC0->PER.reg = 0xFFFF;              // Set counter Top using the PER register
  while (TCC0->SYNCBUSY.bit.PER == 1); // wait for sync
  
  REG_TCC0_COUNT = 0x0000;               // Clear timer's COUNT value
  while (TCC0->SYNCBUSY.bit.COUNT);     // Wait for synchronization
  Serial.println("Done TC COUNT cleared for third timer");
  
  while (TCC0->SYNCBUSY.bit.CTRLB);
  TCC0->CTRLBSET.bit.CMD = TCC_CTRLBSET_CMD_READSYNC_Val;
  while (TCC0->SYNCBUSY.bit.CTRLB);
  
  REG_TCC0_CTRLA = TCC_CTRLA_PRESCALER_DIV1; // Set timer prescaler
  
  while (TCC0->SYNCBUSY.bit.ENABLE);       // Wait for synchronization
  REG_TCC0_CTRLA |= TCC_CTRLA_ENABLE;               // Enable TCC0
  while (TCC0->SYNCBUSY.bit.ENABLE);       // Wait for synchronization


I created a simple function that gets the COUNT value and prints it to the terminal.

Code: [Select]
void ReadCountValTCC()
{
 if(TCC0->INTFLAG.bit.OVF && TCC0->INTENSET.bit.OVF)
 {
 Serial.println("OVF on TCC0!");
 }
 else
 {
 Serial.print("TCC0: ");
 while (TCC0->SYNCBUSY.bit.CTRLB);
 TCC0->CTRLBSET.bit.CMD = TCC_CTRLBSET_CMD_READSYNC_Val;
 while (TCC0->SYNCBUSY.bit.CTRLB);
 delay(1);                                            // <<--- This line is very important. Without it, COUNT will read zero
 while (TCC0->SYNCBUSY.bit.COUNT);     // Wait for synchronization
 Serial.println(TCC0->COUNT.reg, DEC);
 TCC0->COUNT.reg = 0x0000; //clear count reg
 while (TCC0->SYNCBUSY.bit.COUNT);     // Wait for synchronization
 }
}


This function is called from the loop() every x ms. In my case, I am calling it every 1000 ms.

Basically, the CMD data field of the CTRLBSET reg should be set to READSYNC to force read of the COUNT reg. The delay is important otherwise COUNT will still read as zero.

Thanks,
Ruch

MartinL

#33
Jul 31, 2016, 08:32 pm Last Edit: Aug 01, 2016, 08:59 am by MartinL
Hi Ruch,

Thanks. Now you've pointed it out, I can clearly see it says "READSYNC" in the SAMD21 datasheet.

That's a really useful piece of information for anyone else who wants to read the TCC COUNT register.

MartinL

#34
Aug 01, 2016, 09:25 am Last Edit: Aug 01, 2016, 10:50 am by MartinL
Hi Ruch,

I just ran a little test program. I managed to get it to read the TCC COUNT register without the addition of the 1ms delay.

Here's the code:

Code: [Select]
// Setup TCC0 counter and display the COUNT register continuously in the console
void setup()
{
  SerialUSB.begin(115200);
  while(!SerialUSB);
 
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz system clock by 1 = 48MHz
                    GCLK_GENDIV_ID(4);            // Set division on Generic Clock Generator (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                     GCLK_GENCTRL_GENEN |         // Enable GCLK 4
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the clock source to 48MHz
                     GCLK_GENCTRL_ID(4);          // Set clock source on GCLK 4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable the generic clock...
                     GCLK_CLKCTRL_GEN_GCLK4 |     // ....on GCLK4
                     GCLK_CLKCTRL_ID_TCC0_TCC1;   // Feed the GCLK4 to TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization
 
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Set prescaler to 1, 48MHz/1 = 48Mhz
                    TCC_CTRLA_ENABLE;             // Enable TCC0
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop()
{
  REG_TCC0_CTRLBSET = TCC_CTRLBSET_CMD_READSYNC;  // Trigger a read synchronization on the COUNT register
  while (TCC0->SYNCBUSY.bit.CTRLB);               // Wait for the CTRLB register write synchronization
  while (TCC0->SYNCBUSY.bit.COUNT);               // Wait for the COUNT register read sychronization
  SerialUSB.println(REG_TCC0_COUNT, HEX);         // Print the result
  REG_TCC0_COUNT = 0x5;                           // Set the COUNT register to 5
  while (TCC0->SYNCBUSY.bit.COUNT);               // Wait for the COUNT register write sychronization
}

All this synchronization is getting a bit convoluted, as you mentioned you have to set the "READSYNC" command in the CTRLB register, which itself has to be write synchronized, to trigger a read synchronization in COUNT register and only then can you read the TCC's COUNT register itself, phew!

pguerra75

Hi, MartinL and Rucus

    I have been reading and trying my best to understand this thread you two have been working at. I have to admit much of it is above my head however I am learning a great deal. I am currently working on a project were I am receiving a 20-75kHz square wave signal. I was using the Atmega328P and using the FreqCount Library. I however ran out of head room for the display (low power Sharp Memory LCD) and was looking to move to the SAMD21.

    My question is, Would it be possible to assign TCC0 to count events on a particular pin? Or is TCC0 already assigned to a pin?

    Or am I even barking up the right tree?

Thanks for any advice in advance!
Paul
If it doesn't explode, you didn't build it correctly...

MartinL

#36
Sep 04, 2016, 07:47 pm Last Edit: Sep 04, 2016, 08:16 pm by MartinL
Hi Paul,

Quote
My question is, Would it be possible to assign TCC0 to count events on a particular pin? Or is TCC0 already assigned to a pin?
In the case of TCC or TC capture on the SAMD21, any pin capable of receiving interrupts, which happens to be all of them except digital pin 4 can receive the input pulse. It's the event system that routes these pin change signals to the timer itself.

The SAMD21 will struggle to read a 75kHz signal using TCC capture. The issue is that the signal frequency is so high with respect to the processor's 48MHz clock, that the microcontroller ends up spending all its time servicing the incoming interrupts generated by pulses and nothing else.

If you're using a 20-75kHz signal, you could instead use Ruch's example (above), which uses a generic clock input and feed this to the TCC timer. In this way, it's possible to read the number of events (timer counts) over a given period of time.

Rucus

Hi MartinL,

I am sorry it took me a very long time to revisit this thread. I am glad you made COUNT register work without the need of the delay. That is really great otherwise we would keep on guessing how much delay is needed. Hmmm.. these synchronization thing makes things more complicated indeed. Is it also true for other ARM-based MCU's? I am just curious. This is the first ARM processor that I used where I have to mess up with low-level registers for some functionalities I can't get from the ASF. For other micros, I just use the HAL drivers provided by the vendor.

Thanks,
Ruch

MartinL

Hi Ruch,

Quote
Hmmm.. these synchronization thing makes things more complicated indeed. Is it also true for other ARM-based MCU's?
My only other experience of ARM based microcontrollers is the ARM Cortex M3, SAM3X8E used on the Due.

Although there are a few core CPU similarities, at register level the SAM3X8E is completely different from the SAMD21 and also has the benefit that it doesn't require register synchronization by the programmer.

Martin

Rucus

Very interesting to know that SAM3X8E doesn't need the synchronization! I've never used the Due's myself. Hopefully will have the time to try it out in the future. Thanks Martin for sharing!  :)

MartinL

#40
Sep 11, 2016, 07:33 pm Last Edit: Sep 11, 2016, 10:28 pm by MartinL
The Due's a nice bit of kit. Running at 84MHz, it's a powerful microcontroller and really does supercharge your Arduino projects.

Shame the Due's not produced by Arduino.cc anymore, although it's still manufactured by Arduino.org.

electro_95

Hi Everyone,

Sorry for being away from the forum so long.  I'm happy that this discussion carried and it looks like others have been making discoveries and getting help with TCC0.  For awhile I was happy with using TC instead but now I want to try again to migrate to TCC0.  Using some of the code share earlier I put together this to try to do a PPW capture on PA04.  For the life of me, I cant figure out why this wont work, any help is appreciated!



Code: [Select]
void setup() {
 Serial.begin(115200);
 while (!Serial) ;
 Serial.println("Hello World");
 pinMode(4,INPUT);
 attachInterrupt(4,NULL,HIGH);

 // Enable clock for TC
 REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_TCC0_TCC1) ;
 while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync

 GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID( GCLK_CLKCTRL_ID_EIC));
 while ( GCLK->STATUS.bit.SYNCBUSY == 1 );
 
 TCC0->CTRLA.reg &= ~TCC_CTRLA_ENABLE;   // Disable TC
 while (TCC0->SYNCBUSY.bit.ENABLE == 1); // wait for sync

 TCC0->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV1 | TCC_CTRLA_CPTEN0;   // Set perscaler
 while (TCC0->SYNCBUSY.reg & (TCC_SYNCBUSY_PER | TCC_SYNCBUSY_PERB));

 TCC0->PER.reg = 0xFFFFFFFF;              // Set counter Top using the PER register  
 while (TCC0->SYNCBUSY.bit.PER == 1); // wait for sync

 TCC0->EVCTRL.reg =  TCC_EVCTRL_TCEI1 |  // enable input event
                       TCC_EVCTRL_EVACT1_PPW; // event action = PPW
 
 // Interrupts
 TCC0->INTENSET.reg = 0;                 // disable all interrupts
 TCC0->INTENSET.bit.OVF = 1;          // enable overfollow
 TCC0->INTENSET.bit.MC0 = 1;          

 // Enable InterruptVector
 NVIC_EnableIRQ(TCC0_IRQn);

 // Enable TC
 TCC0->CTRLA.reg |= TCC_CTRLA_ENABLE ;
 while (TCC0->SYNCBUSY.bit.ENABLE == 1); // wait for sync

 
 PM->APBCMASK.reg |= PM_APBCMASK_EVSYS;
 
 EIC->EVCTRL.bit.EXTINTEO4 = 1;
 EIC->INTENCLR.bit.EXTINT4 = 1;

 EVSYS->CHANNEL.reg = EVSYS_CHANNEL_PATH_ASYNCHRONOUS | EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_4) | EVSYS_CHANNEL_CHANNEL(2);
 EVSYS->USER.reg = (uint16_t) (EVSYS_USER_USER(EVSYS_ID_USER_TCC0_EV_0 | EVSYS_USER_CHANNEL(1)));
 
}

void loop() {
 delay(250);
 Serial.println(REG_TCC0_CC0);
}

void TCC0_Handler()
{
 Tcc* TC = (Tcc*) TCC0;       // get timer struct
 if (TC->INTFLAG.bit.OVF == 1) {  // A overflow caused the interrupt
   TC->INTFLAG.bit.OVF = 1;    // writing a one clears the flag ovf flag
   //irq_ovf_count++;                 // for debug leds
   Serial.println("OVF");
 }
 
 if (TC->INTFLAG.bit.MC0 == 1) {  // A compare to cc0 caused the interrupt
   TC->INTFLAG.bit.MC0 = 1;    // writing a one clears the flag ovf flag
   Serial.println("MC0");
 }
}

MartinL

#42
Sep 23, 2016, 11:46 am Last Edit: Sep 23, 2016, 12:01 pm by MartinL
Hi electro_95,

The TCC timers' set-up is subtly different from that of the TC timers.

1. The capture channel enble bits (CC0/CC1) bits have moved from the CTRLC register on TC, to the CTRLA on the TCC.
2. The indivual counter match compare channel inputs (MCEI0/MCEI1) must also be enbled in the TCC's event control register.
3. The TCC event 1 must be used, as this is the only event input capable of pulse-width and period capture. (Event 0 doesn't have this option).

The following code sets-up capture pulse-width and period on TCC0 on digital pin D12:

Code: [Select]
// Setup TCC0 to capture pulse-width and period
volatile boolean periodComplete;
volatile uint16_t isrPeriod;
volatile uint16_t isrPulsewidth;
uint16_t period;
uint16_t pulsewidth;

void setup()
{
  SerialUSB.begin(115200);                  // Send data back on the Zero's native port
  while(!SerialUSB);                        // Wait for the SerialUSB port to be ready
 
  REG_PM_APBCMASK |= PM_APBCMASK_EVSYS;     // Switch on the event system peripheral
 
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |    // Divide the 48MHz system clock by 3 = 16MHz
                    GCLK_GENDIV_ID(5);      // Set division on Generic Clock Generator (GCLK) 5
  while (GCLK->STATUS.bit.SYNCBUSY);        // Wait for synchronization

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                     GCLK_GENCTRL_GENEN |         // Enable GCLK 5
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the clock source to 48MHz
                     GCLK_GENCTRL_ID(5);          // Set clock source on GCLK 5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization*/

  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable the generic clock...
                     GCLK_CLKCTRL_GEN_GCLK5 |     // ....on GCLK5
                     GCLK_CLKCTRL_ID_TCC0_TCC1;   // Feed the GCLK5 to TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_EIC_EVCTRL |= EIC_EVCTRL_EXTINTEO3;                                 // Enable event output on external interrupt 3
  attachInterrupt(12, NULL, HIGH);                                        // Attach interrupts to digital pin 12 (external interrupt 3)
 
  REG_EVSYS_USER = EVSYS_USER_CHANNEL(1) |                                // Attach the event user (receiver) to channel 0 (n + 1)
                   EVSYS_USER_USER(EVSYS_ID_USER_TCC0_EV_1);              // Set the event user (receiver) as timer TCC0, event 1

  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_3) |    // Set event generator (sender) as external interrupt 3
                      EVSYS_CHANNEL_CHANNEL(0);                           // Attach the generator (sender) to channel 0

  REG_TCC0_EVCTRL |= 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_TCEI1 |           // Enable the TCC event 1 input
                     /*TCC_EVCTRL_TCINV1 |*/      // Invert the event 1 input         
                     TCC_EVCTRL_EVACT1_PPW;       // Set up the timer for capture: CC0 period, CC1 pulsewidth
                                       
  //NVIC_DisableIRQ(TCC0_IRQn);
  //NVIC_ClearPendingIRQ(TCC0_IRQn);
  NVIC_SetPriority(TCC0_IRQn, 0);      // Set the Nested Vector Interrupt Controller (NVIC) priority for TCC0 to 0 (highest)
  NVIC_EnableIRQ(TCC0_IRQn);           // Connect the TCC0 timer to the Nested Vector Interrupt Controller (NVIC)
 
  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_CPTEN1 |              // Enable capture on CC1
                    TCC_CTRLA_CPTEN0 |              // Enable capture on CC0
                    TCC_CTRLA_PRESCALER_DIV16 |     // Set prescaler to 16, 16MHz/16 = 1MHz
                    TCC_CTRLA_ENABLE;               // Enable TCC0
  while (TCC0->SYNCBUSY.bit.ENABLE);                // Wait for synchronization
}

void loop()
{
  if (periodComplete)                             // Check if the period is complete
  {
    noInterrupts();                               // Read the new period and pulse-width
    period = isrPeriod;                   
    pulsewidth = isrPulsewidth;
    interrupts();
    SerialUSB.print(period);                      // Output the results
    SerialUSB.print(F("   "));
    SerialUSB.println(pulsewidth);
    periodComplete = false;                       // Start a new period
  }
}

void TCC0_Handler()                              // Interrupt Service Routine (ISR) for timer TCC0
{     
  // Check for match counter 0 (MC0) interrupt
  if (TCC0->INTFLAG.bit.MC0)             
  {   
    isrPeriod = REG_TCC0_CC0;                   // Copy the period
    periodComplete = true;                       // Indicate that the period is complete
  }

  // Check for match counter 1 (MC1) interrupt
  if (TCC0->INTFLAG.bit.MC1)           
  {
    isrPulsewidth = REG_TCC0_CC1;               // Copy the pulse-width
  }
}

Luckily, the code's a bit shorter as the TCC timer requires less synchronization.

electro_95

 :)  :)  :)

You're the best!  Thank you so much for the help, this code does exactly what I was trying to do.

Is there actual documentation that you used to figure this out or is it purely from trial and error?  Maybe I'm missing it but I felt like the datasheet described what PPW capture was like in concept but provided  very little info on how to actually configure it.  Especially to route the event to EV_1 and that MCEI0 and MCEI1 needed to be enabled (same with CPTEN0 and CPTEN1).

Anyway, many many thanks, this one was frustrating.

Now dare I ask (or even attempt) how to put these PPW measurements straight into memory via DMA?

sundancekis

Hello All,

Thanks for discussion on this thread, it has helped me with my project.
I am hoping to get help understanding the code though. Your help is appreciated.
Some background on the code and use:

I am using an arduino MKR1000 which is very similar to the Zero.
The code input captures 3 pwm signals using 3 TCs
The code builds off of the code in this thread to accomplish this, but does not use an ISR
The code works (at bottom sniped and attached to post), I am just curious about how a few lines work mainly the assignments of

REG_EVSYS_USER
REG_EVSYS_CHANNEL
and why I can set the REG separately and it seems to complete 3 functions. I would have thought I would need to OR the commands together like:
Code: [Select]

  REG_EVSYS_USER = EVSYS_USER_CHANNEL(1) |                                // Attach the event user (receiver) to channel 0 (n + 1)
                   EVSYS_USER_USER(EVSYS_ID_USER_TC5_EVU);                // Set the event user (receiver) as timer TC5

  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
 REG_EVSYS_USER |= EVSYS_USER_CHANNEL(3) |                                // Attach the event user (receiver) to channel 2 (n + 1)
                   EVSYS_USER_USER(EVSYS_ID_USER_TC3_EVU);                // Set the event user (receiver) as timer TC3



A snip of the working code:

 
Code: [Select]
// Code will initalize timers on mkr to do 3 input captures and
// output 3 pwm waves that vary duty cycle at a fixed freq

//Input capture. Using TC3 TC4 and TC5

void setup() {
  // put your setup code here, to run once:
   SerialUSB.begin(115200);                  // Send data back on the MKR1000's native port
   while(!SerialUSB);                        // Wait for the SerialUSB port to be ready
 
  init_clocks();                             // Initalize the TC's for inputcapture
}

void loop() {


    getpulse();           // Function to collect the PWM signals

    period = isrPeriod1;
    pulsewidth = isrPulsewidth1;
    SerialUSB.print(period);                      // Output the results...



}


void init_clocks(){
  SerialUSB.println("setup clocks...");
\\ Feed clock code from before
                     
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization*/
 SerialUSB.println("feeding clocks..");
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable the generic clock...
                     GCLK_CLKCTRL_GEN_GCLK5 |     // ....on GCLK5
                     GCLK_CLKCTRL_ID_TC4_TC5;    // Feed the GCLK5 to TC4 and TC5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization
 

 SerialUSB.println("feeding clocks part 2..");    // Doing this a second time does not make sense to me, but it works and does not overwrite the previous registry writting
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable the generic clock...
                     GCLK_CLKCTRL_GEN_GCLK5 |     // ....on GCLK5
                     GCLK_CLKCTRL_ID_TCC2_TC3;    // Feed the GCLK5 to TCC2 and TC3
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization
 
  REG_EIC_EVCTRL |= EIC_EVCTRL_EXTINTEO2;                                 // Enable event output on external interrupt 2 used iwth TC5
  attachInterrupt(A1, NULL, HIGH);                                        // Attach interrupts. Not sure why this is nesseary

  REG_EIC_EVCTRL |= EIC_EVCTRL_EXTINTEO4;                                 // Enable event output on external interrupt 4 to be used with TC4
  attachInterrupt(6, NULL, HIGH);       

  REG_EIC_EVCTRL |= EIC_EVCTRL_EXTINTEO3;                                 // Enable event output on external interrupt 3 to be used with TC3
  attachInterrupt(A2, NULL, HIGH);                                        // Attach interrupts to digital pin A2 (external interrupt 3).
 
//
 SerialUSB.println("setting event system..");
  REG_EVSYS_USER = EVSYS_USER_CHANNEL(1) |                                // Attach the event user (receiver) to channel 0 (n + 1)
                   EVSYS_USER_USER(EVSYS_ID_USER_TC5_EVU);                // Set the event user (receiver) as timer TC5
 
  // Doing this a second time does not make sense to me, but it works and does not overwrite the previous registry writting
 
  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

  // Doing this a third time does not make sense to me, but it works and does not overwrite the previous registry writting
  REG_EVSYS_USER = EVSYS_USER_CHANNEL(3) |                                // Attach the event user (receiver) to channel 2 (n + 1)
                   EVSYS_USER_USER(EVSYS_ID_USER_TC3_EVU);                // Set the event user (receiver) as timer TC3

 SerialUSB.println("setting up channel..");
  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_2) |    // Set event generator (sender) as external interrupt 2
                      EVSYS_CHANNEL_CHANNEL(0);
                     
  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_4) |    // Set event generator (sender) as external interrupt 4
                      EVSYS_CHANNEL_CHANNEL(1);
//                     
  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_3) |    // Set event generator (sender) as external interrupt 3
                      EVSYS_CHANNEL_CHANNEL(2);                           // Attach the generator (sender) to channel

//
 SerialUSB.println("setting up input capture..");
  REG_TC5_EVCTRL |= TC_EVCTRL_TCEI |              // Enable the TC event input
                    TC_EVCTRL_EVACT_PPW;          // Set up the timer for capture: CC0 period, CC1 pulsewidth
  REG_TC4_EVCTRL |= TC_EVCTRL_TCEI |              // Enable the TC event input
                    TC_EVCTRL_EVACT_PPW;          // Set up the timer for capture: CC0 period, CC1 pulsewidth                 
  REG_TC3_EVCTRL |= TC_EVCTRL_TCEI |              // Enable the TC event input
                    TC_EVCTRL_EVACT_PPW;          // Set up the timer for capture: CC0 period, CC1 pulsewidth
                             
  REG_TC5_READREQ = TC_READREQ_RREQ |             // Enable a read request
                    TC_READREQ_ADDR(0x06);        // Offset of the CTRLC register

  while (TC5->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for (read) synchronization
  REG_TC5_CTRLC |= TC_CTRLC_CPTEN1 |              // Enable capture on CC1
                   TC_CTRLC_CPTEN0;               // Enable capture on CC0.
                               

  while (TC5->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for (write) synchronization
 
  REG_TC5_CTRLA |= TC_CTRLA_PRESCALER_DIV16 |     // Set prescaler to 16, 16MHz/16 = 1MHz
                   TC_CTRLA_ENABLE;               // Enable TC5

  while (TC5->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization                                 

// Repeat for TC4 and TC3

//
  SerialUSB.println("setup complete");
}

void getpulse()
{
    REG_TC5_READREQ = TC_READREQ_RREQ |           // Enable a read request
                      TC_READREQ_ADDR(0x18);      // Offset address of the CC0 register. need to find the addresses for all

    while (TC5->COUNT16.STATUS.bit.SYNCBUSY);     // Wait for (read) synchronization
    isrPeriod1 = REG_TC5_COUNT16_CC0;              // Copy the period

    REG_TC5_READREQ = TC_READREQ_RREQ |           // Enable a read request
                      TC_READREQ_ADDR(0x1A);      // Offset address of the CC1 register
    while (TC5->COUNT16.STATUS.bit.SYNCBUSY);     // Wait for (read) synchronization
    isrPulsewidth1 = REG_TC5_COUNT16_CC1;          // Copy the pulse-width
//
//    Repeat for TC4 and TC3
}

Go Up