Go Down

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

mladek89

Hi Martin,

ohh thank you for the hint.
Yes the library I am using, uses the micros function:
https://github.com/hjgode/homewatch/blob/master/arduino/libraries/NewRemoteSwitch/NewRemoteReceiver.cpp

It is called in the void NewRemoteReceiver::interruptHandler() function.

Do you know how it can be handled with the MKR1010? I think the processor is the same SAMD21 as the ZERO uses?

Thank you very much!

Best regards,
Alex

MartinL

#136
Oct 19, 2018, 01:51 pm Last Edit: Oct 19, 2018, 01:52 pm by MartinL
Hi Alex,

It may or may not solve the problem, but you could try replacing micros() with micros2() function.

The micros2() function uses the timer counter TC3 to calculate micros() in a similar way to the AVR Arduinos such as the Uno, Mega, etc...

I've successfully used it in attachInterrupt() interrupt service routines for my flight control firmware.

Here's the code, (together with test output to the console):

Code: [Select]
// Micros2 function
volatile unsigned long timer2Counter;

void setup() {
  Serial.begin(115200);                            // Set up the serial port for test purposes
 
  // Set up the generic clock (GCLK4) used to clock timers
  GCLK->GENDIV.reg = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                    GCLK_GENDIV_ID(4);             // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  GCLK->GENCTRL.reg = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                      GCLK_GENCTRL_GENEN |         // Enable GCLK4
                      GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                      GCLK_GENCTRL_ID(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization
 
  // Feed GCLK4 to TCC2 (and TC3)
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC2 (and TC3)
                      GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                      GCLK_CLKCTRL_ID_TCC2_TC3;    // Feed GCLK4 to TCC2 (and TC3)
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  TC3->COUNT8.PER.reg = 0xFF;                      // Set period register to 255
  while (TC3->COUNT8.STATUS.bit.SYNCBUSY);         // Wait for synchronization

  TC3->COUNT8.INTENSET.reg = /*TC_INTENSET_MC1 | TC_INTENSET_MC0 |*/ TC_INTENSET_OVF; // Enable TC3 interrupts
 
  //NVIC_DisableIRQ(TC3_IRQn);
  //NVIC_ClearPendingIRQ(TC3_IRQn);
  NVIC_SetPriority(TC3_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC3 to 0 (highest)
  NVIC_EnableIRQ(TC3_IRQn);         // Connect TC3 to Nested Vector Interrupt Controller (NVIC)

  // Set the TC3 timer to tick at 2MHz, or in other words a period of 0.5us - timer overflows every 128us
  // timer counts up to (up to 255 in 128us)
  TC3->COUNT8.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV8 |      // Set prescaler to 8, 16MHz/8 = 2MHz
                           TC_CTRLA_PRESCSYNC_PRESC |     // Set the reset/reload to trigger on prescaler clock
                           TC_CTRLA_MODE_COUNT8;          // Set the counter to 8-bit mode
                           
  TC3->COUNT8.CTRLA.bit.ENABLE = 1;               // Enable TC3
  while (TC3->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization

  TC3->COUNT8.READREQ.reg = TC_READREQ_RCONT |            // Enable a continuous read request
                            TC_READREQ_ADDR(0x10);        // Offset of the 8 bit COUNT register
  while (TC3->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for (read) synchronization
}

void loop() {
  Serial.println(micros2());                      // Testing the micros2() function
  delay(1000);                                    // Wait 1 second
}

// Micros2 is used to measure the receiver pulsewidths down to 1us accuracy
uint32_t micros2()
{
  uint32_t m;
  uint8_t t;
     
  noInterrupts();                                 // Disable interrupts
  m = timer2Counter;                              // Get the number of overflows
  t = TC3->COUNT8.COUNT.reg;                      // Get the current TC3 count value

  if (TC3->COUNT8.INTFLAG.bit.OVF && (t < 255))   // Check if the timer has just overflowed (and we've missed it)
  {
    m++;                                          // Then in this case increment the overflow counter
  } 
  interrupts();                                   // Enable interrupts
  return ((m << 8) + t) / 2;                      // Return the number of microseconds that have occured since the timer started
}

// This ISR is called every 128us
void TC3_Handler()           // ISR TC3 overflow callback function
{
  if (TC3->COUNT8.INTFLAG.bit.OVF)
  {
    timer2Counter++;           // Increment the overflow counter
  }
  TC3->COUNT8.INTFLAG.reg = TC_INTFLAG_OVF;   // Rest the overflow interrupt flag
}

mladek89

Hi Martin,

wow cool... I will try it out.. thank you very much!
I will let you know if it helped or not.

Best regards,
Alex

mwolter

Hello MartinL,
A couple of months ago you graciously assisted with generating a variable PWM frequency on pins that use TCC0 (Pin 3) and TCC1 (Pin 8). Using the same MCU, I am attempting to read the duty cycle of a 45hz PWM signal and running into a few issues.

First, the CPU is already a little taxed (SPI, calculating PID, sending serial data, etc.) and is producing erratic readings. Second, it does not appear that the examples that use TCCx will work since they will require feeding a different clock frequency to TCC0 and TCC1.

The basic pulseIn function worked but it added a 13ms delay to the loop processing that I am trying to avoid.

I saw that you were able to free the CPU by using DMAC in post #102.  Is it possible to use DMAC for TC3 example you posted?

Requirements: Read the duty cycle of a 45hz, not interested in frequency, utilize DMAC, utilize TCx.

Appreciate the help!

MartinL

Hi mwolter,

What sort of accuracy do you require the pulse width (duty-cycle) measurement? This essentially determines the timer's speed. The examples earlier in this thread are designed to read a radio controlled receiver, accurate to 1 microsecond, would that be enough for your project?

It might also be possible to use timer TCC2, if you're not using it already?

Also, which pin would you like to receive the incoming signal, any pin can be used except the Non-Maskable Interrupt (NMI), which is digital pin 2 on the Arduino Zero and digital pin 4 on the M0 Pro/M0.

The pulseIn() function blocks while waiting for the pulse, thereby making it unsuitable for time critical projects.


mwolter

#140
Oct 20, 2018, 04:58 pm Last Edit: Oct 20, 2018, 05:05 pm by mwolter
One microsecond should be more than enough. Not using TCC2 as far as I can tell. Pin 12 is available. Also is 11 if it is required to use TCC2.

MartinL

#141
Oct 22, 2018, 09:47 am Last Edit: Oct 22, 2018, 09:48 am by MartinL
Hi mwolter,

Here's the code that reads the period and pulse width of a 45Hz signal on D12 with TCC2/DMAC and outputs the values in microseconds (us) to the console:

Code: [Select]
// Setup TCC2 to capture pulse-width and period with DMAC transfer
volatile uint32_t dmacPeriod;
volatile uint32_t dmacPulsewidth;

typedef struct                              // DMAC descriptor structure
{
  uint16_t btctrl;
  uint16_t btcnt;
  uint32_t srcaddr;
  uint32_t dstaddr;
  uint32_t descaddr;
} dmacdescriptor ;

volatile dmacdescriptor wrb[12] __attribute__ ((aligned (16)));               // Write-back DMAC descriptors
dmacdescriptor descriptor_section[12] __attribute__ ((aligned (16)));         // DMAC channel descriptors
dmacdescriptor descriptor __attribute__ ((aligned (16)));                     // Place holder descriptor

void setup()
{
  SerialUSB.begin(115200);                  // Send data back on the Zero's native port
  while(!SerialUSB);                        // Wait for the SerialUSB port to be ready
 
  DMAC->BASEADDR.reg = (uint32_t)descriptor_section;                // Set the descriptor section base address
  DMAC->WRBADDR.reg = (uint32_t)wrb;                                // Set the write-back descriptor base adddress
  DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf);      // Enable the DMAC and priority levels
 
  DMAC->CHID.reg = DMAC_CHID_ID(0);                                 // Select DMAC channel 0
  // Set DMAC channel 1 to priority level 0 (lowest), to trigger on TCC2 match compare 1 and to trigger every beat
  DMAC->CHCTRLB.reg = DMAC_CHCTRLB_LVL(0) | DMAC_CHCTRLB_TRIGSRC(TCC2_DMAC_ID_MC_0) | DMAC_CHCTRLB_TRIGACT_BEAT;
  //DMAC->CHINTENSET.reg = DMAC_CHINTENSET_MASK ;                   // Enable all DMAC interrupts
  descriptor.descaddr = (uint32_t)&descriptor_section[0];           // Set up a circular descriptor
  descriptor.srcaddr = (uint32_t)&TCC2->CC[0].reg;                  // Take the contents of the TCC2 counter comapare 0 register
  descriptor.dstaddr = (uint32_t)&dmacPeriod;                       // Copy it to the "dmacPeriod" variable
  descriptor.btcnt = 1;                                             // This takes one beat
  descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_HWORD | DMAC_BTCTRL_VALID;   // Copy 16-bits (HWORD) and flag discriptor as valid
  memcpy(&descriptor_section[0], &descriptor, sizeof(dmacdescriptor));  // Copy to the channel 0 descriptor
 
  DMAC->CHID.reg = DMAC_CHID_ID(1);                                 // Select DMAC channel 1
  // Set DMAC channel 1 to priority level 0 (lowest), to trigger on TCC2 match compare 1 and to trigger every beat
  DMAC->CHCTRLB.reg = DMAC_CHCTRLB_LVL(0) | DMAC_CHCTRLB_TRIGSRC(TCC2_DMAC_ID_MC_1) | DMAC_CHCTRLB_TRIGACT_BEAT;
  //DMAC->CHINTENSET.reg = DMAC_CHINTENSET_MASK ;                   // Enable all DMAC interrupts
  descriptor.descaddr = (uint32_t)&descriptor_section[1];           // Set up a circular descriptor
  descriptor.srcaddr = (uint32_t)&TCC2->CC[1].reg;                  // Take the contents of the TCC2 counter comapare 0 register
  descriptor.dstaddr = (uint32_t)&dmacPulsewidth;                   // Copy it to the "dmacPulseWidth" variable
  descriptor.btcnt = 1;                                             // This takes 1 beat
  descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_HWORD | DMAC_BTCTRL_VALID;    // Copy 16-bits (HWORD) and flag discriptor as valid
  memcpy(&descriptor_section[1], &descriptor, sizeof(dmacdescriptor));   // Copy to the channel 1 descriptor
 
  PM->APBCMASK.reg |= PM_APBCMASK_EVSYS;     // Switch on the event system peripheral
 
  GCLK->GENDIV.reg = GCLK_GENDIV_DIV(3) |    // Divide the 48MHz system clock by 3 = 48MHz/3 = 16MHz
                     GCLK_GENDIV_ID(5);      // Set division on Generic Clock Generator (GCLK) 5
  while (GCLK->STATUS.bit.SYNCBUSY);         // Wait for synchronization

  GCLK->GENCTRL.reg = 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*/

  GCLK->CLKCTRL.reg = 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

  // Enable the port multiplexer on digital pin D12
  PORT->Group[g_APinDescription[12].ulPort].PINCFG[g_APinDescription[12].ulPin].bit.PMUXEN = 1;
  // Set-up the pin as an EIC (interrupt) peripheral on D12
  PORT->Group[g_APinDescription[12].ulPort].PMUX[g_APinDescription[12].ulPin >> 1].reg |= PORT_PMUX_PMUXO_A;

  //attachInterrupt(12, NULL, HIGH);                                        // Attach interrupts to digital pin 12 (external interrupt 3)
  EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO3;                                // Enable event output on external interrupt 3
  EIC->CONFIG[0].reg |= EIC_CONFIG_SENSE3_HIGH;                           // Set event detecting a HIGH level
  EIC->CTRL.reg |= EIC_CTRL_ENABLE;                                       // Enable EIC peripheral
  while (EIC->STATUS.bit.SYNCBUSY);                                       // Wait for synchronization
 
  EVSYS->USER.reg = EVSYS_USER_CHANNEL(1) |                                // Attach the event user (receiver) to channel 0 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_TCC2_EV_1);              // Set the event user (receiver) as timer TCC2, event 1

  EVSYS->CHANNEL.reg = 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

  TCC2->EVCTRL.reg |= 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                                   
 
  TCC2->CTRLA.reg |= TCC_CTRLA_CPTEN1 |             // Enable capture on CC1
                     TCC_CTRLA_CPTEN0 |             // Enable capture on CC0
                     TC_CTRLA_PRESCSYNC_PRESC |     // Set the reset/reload to trigger on prescaler clock
                     TCC_CTRLA_PRESCALER_DIV16;     // Set prescaler to 16, 16MHz/16 = 1MHz
                   
  TCC2->CTRLA.bit.ENABLE = 1;
  while (TCC2->SYNCBUSY.bit.ENABLE);                // Wait for synchronization

  DMAC->CHID.reg = DMAC_CHID_ID(0);                 // Select DMAC channel 0
  DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;         // Enable DMAC channel 0
  DMAC->CHID.reg = DMAC_CHID_ID(1);                 // Select DMAC channel 1
  DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;         // Enable DMAC channel 1
}

void loop()
{
  SerialUSB.print(dmacPeriod);                      // Output the results
  SerialUSB.print(F("   "));
  SerialUSB.println(dmacPulsewidth);
}

If you only require pulse width, then it's possible to just comment out the period DMAC channel.

mwolter

Thank you Martin! Appreciate the help and will test it today.

mladek89

Hi Martin,

thank you soooo much, it is working now!
You are so right, the problem was the micros() function of the MKR1010.
It was working as soon as I replaced the micros() against your awesome micros2() function inside the NewRemoteSwitch library.

But I do not understand the whole code of you... How does the TC3_handler function() work?.. How will that function be called?
Do you have some literature to understand that assembler code? I would like to understand that code.

Thank you very much!

With best regards,
Alexander


mladek89

#144
Dec 14, 2018, 02:42 pm Last Edit: Dec 14, 2018, 02:43 pm by mladek89
Dear Martin,

is it possible to use your micros2() function for two libraries in parallel?
My problem is, if I am using two different libraries for reading different 433Mhz signals I would need in both libraries the new micros2() function.

If I am using the same function, I get an error message that some functions has been used already.
Do I need to set a second timer for that purpose?

Thank you so much for your help!

With best regards
Alexander

MartinL

Hi Alexander,

To use the micros2() function in two different libraries it's necessary to declare the function in a header file, say "micros2.h". The function declaration just specifies its parameters and return value, terminated with a semicolon, (rather than the function definition that contains the function's code in the curly brackets {}):

Code: [Select]
extern unsigned long micros2(void);
This header file can be placed in the Arduino IDE's "libraries" directory (where the sketches are stored by default). The file can then be included at the beginning of both libraries:

Code: [Select]
#include <micros2.h>
Doing this essentially defers the resolution of the micros2() function from compiler to the linker, allowing the function to be called by both libraries.

For example, if you look at the standard micros() code, you'll see that the function is declared in the Arduino core file "delay.h":

Code: [Select]
#ifdef __cplusplus
extern "C" {
#endif

// ...

extern unsigned long micros( void ) ;

// ...

#ifdef __cplusplus
}
#endif

...but defined in the file "delay.c":

Code: [Select]
unsigned long micros( void )
{
  uint32_t ticks, ticks2;
  uint32_t pend, pend2;
  uint32_t count, count2;

  ticks2  = SysTick->VAL;
  pend2   = !!(SCB->ICSR & SCB_ICSR_PENDSTSET_Msk)  ;
  count2  = _ulTickCount ;

  do
  {
    ticks=ticks2;
    pend=pend2;
    count=count2;
    ticks2  = SysTick->VAL;
    pend2   = !!(SCB->ICSR & SCB_ICSR_PENDSTSET_Msk)  ;
    count2  = _ulTickCount ;
  } while ((pend != pend2) || (count != count2) || (ticks < ticks2));

  return ((count+pend) * 1000) + (((SysTick->LOAD  - ticks)*(1048576/(VARIANT_MCK/1000000)))>>20) ;
  // this is an optimization to turn a runtime division into two compile-time divisions and
  // a runtime multiplication and shift, saving a few cycles
}

mladek89

#146
Dec 19, 2018, 10:26 pm Last Edit: Dec 19, 2018, 10:44 pm by mladek89
Hi Martin,

thank you so much for your help!
I have tried to write your Code as .h and .cpp files.

But the problem is that it didn't know the GCLK etc. commands.
error: 'GCLK' was not declared in this scope
error: 'GCLK_GENDIV_DIV' was not declared in this scope etc.

I am already using the 1.8.7 Arduino.cc IDE.

Do you know where the problem is? Are these files correct written?

header file looks like:
Code: [Select]

#ifndef micros2_h
#define micros2_h

#include <Arduino.h>


class micros2{
public:
micros2();
~micros2();
void micros_2();
void TC3_Handler();
};


#endif


cpp File looks like this:
Code: [Select]
#include "micros2.h"
#include <Arduino.h>

micros2::micros2(){
  // Set up the generic clock (GCLK4) used to clock timers
  GCLK->GENDIV.reg = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                    GCLK_GENDIV_ID(4);             // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  GCLK->GENCTRL.reg = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                      GCLK_GENCTRL_GENEN |         // Enable GCLK4
                      GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                      GCLK_GENCTRL_ID(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization
 
  // Feed GCLK4 to TCC2 (and TC3)
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC2 (and TC3)
                      GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                      GCLK_CLKCTRL_ID_TCC2_TC3;    // Feed GCLK4 to TCC2 (and TC3)
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  TC3->COUNT8.PER.reg = 0xFF;                      // Set period register to 255
  while (TC3->COUNT8.STATUS.bit.SYNCBUSY);         // Wait for synchronization

  TC3->COUNT8.INTENSET.reg = /*TC_INTENSET_MC1 | TC_INTENSET_MC0 |*/ TC_INTENSET_OVF; // Enable TC3 interrupts
 
  //NVIC_DisableIRQ(TC3_IRQn);
  //NVIC_ClearPendingIRQ(TC3_IRQn);
  NVIC_SetPriority(TC3_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC3 to 0 (highest)
  NVIC_EnableIRQ(TC3_IRQn);         // Connect TC3 to Nested Vector Interrupt Controller (NVIC)

  // Set the TC3 timer to tick at 2MHz, or in other words a period of 0.5us - timer overflows every 128us
  // timer counts up to (up to 255 in 128us)
  TC3->COUNT8.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV8 |      // Set prescaler to 8, 16MHz/8 = 2MHz
                           TC_CTRLA_PRESCSYNC_PRESC |     // Set the reset/reload to trigger on prescaler clock
                           TC_CTRLA_MODE_COUNT8;          // Set the counter to 8-bit mode
                           
  TC3->COUNT8.CTRLA.bit.ENABLE = 1;               // Enable TC3
  while (TC3->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization

  TC3->COUNT8.READREQ.reg = TC_READREQ_RCONT |            // Enable a continuous read request
                            TC_READREQ_ADDR(0x10);        // Offset of the 8 bit COUNT register
  while (TC3->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for (read) synchronization
}

micros2::~micros2(){/*nothing to destruct*/}

volatile unsigned long timer2Counter;

// Micros2 is used to measure the receiver pulsewidths down to 1us accuracy
void micros2::micros_2()
{
  uint32_t m;
  uint8_t t;
     
  noInterrupts();                                 // Disable interrupts
  m = timer2Counter;                              // Get the number of overflows
  t = TC3->COUNT8.COUNT.reg;                      // Get the current TC3 count value

  if (TC3->COUNT8.INTFLAG.bit.OVF && (t < 255))   // Check if the timer has just overflowed (and we've missed it)
  {
    m++;                                          // Then in this case increment the overflow counter
  } 
  interrupts();                                   // Enable interrupts
  return ((m << 8) + t) / 2;                      // Return the number of microseconds that have occured since the timer started
}

// This ISR is called every 128us
void micros2::TC3_Handler()           // ISR TC3 overflow callback function
{
  if (TC3->COUNT8.INTFLAG.bit.OVF)
  {
    timer2Counter++;           // Increment the overflow counter
  }
  TC3->COUNT8.INTFLAG.reg = TC_INTFLAG_OVF;   // Rest the overflow interrupt flag
}




Thank you in advance with best regards
Alex

MartinL

#147
Dec 20, 2018, 10:33 am Last Edit: Dec 20, 2018, 10:36 am by MartinL
Hi Alex,

Including the "Arduino.h" header file like you've done, should be enough to make the CMSIS (Cortex Microcontroller Software Interface Standard) register definitions such as GCLK, TC3 etc... visible to your sketch.

When writing small C++ programs, I just use a simple editor such as Notepad++, then place the .h and .cpp files in a folder of the same name and deposit them in the Arduino IDE libraries directory. It's then possible to include the header file in your sketch and compile using the Arduino IDE. If there are any errors in your C++ code the compiler in the Arduino IDE will flag them up. This should allow the register definitions to become visible in your sketch.

In your code, I suggest removing #include <Arduino.h> line from your .cpp file, as it's already been included in the header file. Also, the micros2() function should return an unsigned long value.

The TC3_Handler() function has already been declared outside the scope of you C++ class, in the Atmel CMSIS "samd21g18a.h" file:

Code: [Select]
//...
void TCC0_Handler                ( void );
void TCC1_Handler                ( void );
void TCC2_Handler                ( void );
void TC3_Handler                 ( void );
void TC4_Handler                 ( void );
void TC5_Handler                 ( void );
//...

One way to implement this to create a static C++ member function, say: static void TC3_IrqHandler(), then call this from the TC3_Handler() function at the bottom of the .cpp file:

Code: [Select]
void TC3_Handler()            // TC3 Interrupt Service Routine
{
  micros2::TC3_IrqHandler();  // Call the micros2() interrupt handler function
}

Another option is to declare a micro2() object, by including the line:

Code: [Select]
extern micros2 micros2;
... at the bottom of the .h file, then instantiate (define) it at the bottom of the .cpp file:

Code: [Select]
micros2 micros2();
... following this you can just call on the non-static C++ member function: void TC3_IrqHandler(), also at the bottom of the .cpp file:

Code: [Select]
void TC3_Handler()            // TC3 Interrupt Service Routine
{
  micros2.TC3_IrqHandler();  // Call the micros2() interrupt handler function
}

mladek89

Hi Martin,

thank you soo much for your support and help!
I haven't tried your hints but I will do that in the next days for sure.

I wish you a Merry Christmas and a Happy New Year!

Best regards,
Alex

mladek89

Hi Martin,

I am very happy to let you know that its working now :D
thank you sooo much for your help!

With best regards,
Alex

Go Up