[SOLVED] Input capture on Arduino Due

Hi,
I am trying to get input capture working but fails. Pin 5 is connected to the pps output of a gps. I have verified that I can read the pulse using digitalRead() so I know the pulse is there and the Due can measure it. I just cant get the input capture to work.
I have followed the "Timer interrupts on Due" thread and read the data sheet and some but not all relevant source code in the arduino 1.5 package.

This is the code I am running, based on the code examples in the other thread. I tried to comment it to explain what it does.

/*
trying to get input capture working for Arduion Due with GPS PPS signal connected to
pin 5. Based on the information in this discussion thread: http://arduino.cc/forum/index.php?topic=130423.0
It does not work, for some reason!
by Paul Dreik http://www.pauldreik.se/
*/
volatile boolean l;

/*
interrupt callback. TC6=timer counter TC2 channel 0
*/
void TC6_Handler()
{
//reads the interrupt. necessary to clear the interrupt flag.
const uint32_t status=TC_GetStatus(TC2, 0);

//has the timer overflowed?
const bool overflowed=status & TC_SR_COVFS;

//input capture
const bool inputcaptureA=status & TC_SR_LDRAS;

//loading overrun
const bool loadoverrun=status & TC_SR_LOVRS;

Serial.print("TC6 interrupt! value=");
Serial.print(TC_ReadCV(TC2,0));

//read LDRA
if(inputcaptureA) {
const uint32_t ra= TC2->TC_CHANNEL[0].TC_RA;
Serial.print(" ra=");
Serial.print(ra);
}

Serial.print(" overflow=");
Serial.print(overflowed);
Serial.print(" LDRA=");
Serial.print(inputcaptureA);
Serial.print(" loadoverrun=");
Serial.print(loadoverrun);
Serial.print(" status=");
Serial.println(status);
}

void startTimer(Tc *tc, uint32_t channel, IRQn_Type irq) {

//see 37.7.9
REG_TC2_WPMR=0x54494D00;

//enable configuring the io registers. see 32.7.42
REG_PIOC_WPMR=0x50494F00;

//we need to configure the pin to be controlled by the right peripheral.
//pin 5 is port C. PIOC_PDR is defined in hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/instance/instance_pioc.h
//and PIO_PDR_P25 is defined in hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/component/component_pio.h
//this disables the pio from controlling the pin. see 32.7.2

REG_PIOC_PDR |= PIO_PDR_P25;

//next thing is to assign the io line to the peripheral. See 32.7.24.
//we need to know which peripheral we should use. Read table 37-4 in section 37.5.1.
//TIOA6 is peripheral B, so we want to set that bit to 1.
//REG_PIOC_ABSR is defined in hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/instance/instance_pioc.h
//PIO_ABSR_P25 is defined in hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/component/component_pio.h
REG_PIOC_ABSR |= PIO_ABSR_P25;

//allow configuring the clock.
pmc_set_writeprotect(false);

/*
Every peripheral in the SAM3X is off by default (to save power) and
should be turned on.
*/
pmc_enable_periph_clk(ID_TC6);

/*
configure the timer. All this is about setting TC_CMRx, see 37.7.10 in atmel pdf.
CLOCK1 is 42 MHz. We want input capture. Nothing else should be necessary.
*/
TC_Configure(tc, channel, TC_CMR_TCCLKS_TIMER_CLOCK1 | TC_CMR_LDRA_RISING);

//set the interrupt flags. We want interrupt on overflow and TIOA6 (pin 5) going high.
const uint32_t flags=TC_IER_COVFS | TC_IER_LDRAS;
tc->TC_CHANNEL[channel].TC_IER=flags;
tc->TC_CHANNEL[channel].TC_IDR=~flags;//assume IER and IDR are equally defined.

NVIC_EnableIRQ(irq);

//read away the status.
// TC_GetStatus(tc, channel);

//start the timer
TC_Start(tc,channel);
}

void setup(){
Serial.begin(115200);
startTimer(TC2, 0, TC6_IRQn); //TC2 channel 0, the IRQ for that channel
Serial.println("setup complete");
}

//just repeatedly print the timer value, so we know that the processor is alive.
void loop(){
Serial.print("timer value is ");
Serial.println( TC_ReadCV(TC2,0));
delay(4000);

}

This is the output I get:

setup complete
timer value is 105013
TC6 interrupt! value=15911893 ra=15842838 overflow=0 LDRA=1 loadoverrun=0 status=458784
timer value is 168425172
timer value is 336509174
timer value is 504593174
(snip)
timer value is 3866273174
timer value is 4034357174
timer value is 4202441174
TC6 interrupt! value=69116 overflow=1 LDRA=0 loadoverrun=0 status=327697
timer value is 75768118
timer value is 243809998
timer value is 411893996

As you see, there is an LDRA interrupt just at start and the overflow interrupt works as it should, but I would expect to get the LDRA interrupt (PPS) once per second.
Does anyone know what is going on?

thanks,
Paul

I found the solution!
The answer was in section 37.6.8 of the manual:

RA is loaded only if it has not been loaded since the last trigger or if RB has been loaded since
the last loading of RA.

So I updated the code to also capture TIOA6 falling in RB. The problem is now solved.

The updated code, with more comments on the relevant parts:

/*
Input capture working for Arduino Due with GPS PPS signal connected to
pin 5. Based on the information in this discussion thread: Arduino Forum
by Paul Dreik http://www.pauldreik.se/
*/
volatile boolean l;

/*
interrupt callback. TC6=timer counter TC2 channel 0
*/
void TC6_Handler()
{
//reads the interrupt. necessary to clear the interrupt flag.
const uint32_t status=TC_GetStatus(TC2, 0);

//has the timer overflowed?
const bool overflowed=status & TC_SR_COVFS;

//input capture
const bool inputcaptureA=status & TC_SR_LDRAS;
const bool inputcaptureB=status & TC_SR_LDRBS;

//loading overrun
const bool loadoverrun=status & TC_SR_LOVRS;

Serial.print("TC6 interrupt! value=");
Serial.print(TC_ReadCV(TC2,0));

//read LDRA. If we dont, we will get overflow (TC_SR_LOVRS)
if(inputcaptureA) {
const uint32_t ra= TC2->TC_CHANNEL[0].TC_RA;
Serial.print(" ra=");
Serial.print(ra);
}
//read LDRB. If we dont, we will get overflow (TC_SR_LOVRS)
if(inputcaptureB) {
const uint32_t rb= TC2->TC_CHANNEL[0].TC_RB;
Serial.print(" rb=");
Serial.print(rb);
}

Serial.print(" overflow=");
Serial.print(overflowed);
Serial.print(" LDRA=");
Serial.print(inputcaptureA);
Serial.print(" loadoverrun=");
Serial.print(loadoverrun);
Serial.print(" status=");
Serial.println(status);
}

void startTimer(Tc *tc, uint32_t channel, IRQn_Type irq) {

//see 37.7.9
REG_TC2_WPMR=0x54494D00;

//enable configuring the io registers. see 32.7.42
REG_PIOC_WPMR=0x50494F00;

//we need to configure the pin to be controlled by the right peripheral.
//pin 5 is port C. PIOC_PDR is defined in hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/instance/instance_pioc.h
//and PIO_PDR_P25 is defined in hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/component/component_pio.h
//this disables the pio from controlling the pin. see 32.7.2

REG_PIOC_PDR |= PIO_PDR_P25;

//next thing is to assign the io line to the peripheral. See 32.7.24.
//we need to know which peripheral we should use. Read table 37-4 in section 37.5.1.
//TIOA6 is peripheral B, so we want to set that bit to 1.
//REG_PIOC_ABSR is defined in hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/instance/instance_pioc.h
//PIO_ABSR_P25 is defined in hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/component/component_pio.h
REG_PIOC_ABSR |= PIO_ABSR_P25;

//allow configuring the clock.
pmc_set_writeprotect(false);

/*
Every peripheral in the SAM3X is off by default (to save power) and
should be turned on.
*/
pmc_enable_periph_clk(ID_TC6);

/*
configure the timer. All this is about setting TC_CMRx, see 37.7.10 in atmel pdf.
We use CLOCK1 at 42 MHz to get the best possible resolution.
We want input capture on TIOA6 (pin 5). Nothing else should be necessary, BUT there is a caveat:
As mentioned in 37.6.8, we only get the value loaded in RA if not loaded since the last trigger,
or RB has been loaded. Since I do not want to trigger as that sets the timer value to 0, I
instead let register B be loaded when the pulse is going low.
*/
TC_Configure(tc, channel, TC_CMR_TCCLKS_TIMER_CLOCK1 | TC_CMR_LDRA_RISING | TC_CMR_LDRB_FALLING);

//set the interrupt flags. We want interrupt on overflow and TIOA6 (pin 5) going high.
const uint32_t flags=TC_IER_COVFS | TC_IER_LDRAS;
tc->TC_CHANNEL[channel].TC_IER=flags;
tc->TC_CHANNEL[channel].TC_IDR=~flags;//assume IER and IDR are equally defined.

NVIC_EnableIRQ(irq);

//read away the status.
// TC_GetStatus(tc, channel);

//start the timer
TC_Start(tc,channel);
}

void setup(){
Serial.begin(115200);
startTimer(TC2, 0, TC6_IRQn); //TC2 channel 0, the IRQ for that channel
Serial.println("setup complete");
}

//just repeatedly print the timer value, so we know that the processor is alive.
void loop(){
Serial.print("timer value is ");
Serial.println( TC_ReadCV(TC2,0));
delay(4000);
}

This is the output:

setup complete
timer value is 105013
TC6 interrupt! value=38407215 ra=38337962 overflow=0 LDRA=1 loadoverrun=0 status=458784
TC6 interrupt! value=80406613 ra=80337631 rb=42537930 overflow=0 LDRA=1 loadoverrun=0 status=458848
TC6 interrupt! value=122406374 ra=122337301 rb=84537599 overflow=0 LDRA=1 loadoverrun=0 status=458848
TC6 interrupt! value=164406132 ra=164336970 rb=126537269 overflow=0 LDRA=1 loadoverrun=0 status=458848
timer value is 169475292
TC6 interrupt! value=206405894 ra=206336639 rb=168536938 overflow=0 LDRA=1 loadoverrun=0 status=458848
TC6 interrupt! value=248405295 ra=248336308 rb=210536607 overflow=0 LDRA=1 loadoverrun=0 status=458848
(snip)
timer value is 4235747292
TC6 interrupt! value=4238374212 ra=4238304942 rb=4200505239 overflow=0 LDRA=1 loadoverrun=0 status=458848
TC6 interrupt! value=4280373614 ra=4280304613 rb=4242504910 overflow=0 LDRA=1 loadoverrun=0 status=458848
TC6 interrupt! value=69117 rb=4284504581 overflow=1 LDRA=0 loadoverrun=0 status=327761
TC6 interrupt! value=27406077 ra=27336987 overflow=0 LDRA=1 loadoverrun=0 status=458784
TC6 interrupt! value=69405839 ra=69336658 rb=31536955 overflow=0 LDRA=1 loadoverrun=0 status=458848
timer value is 110333875
TC6 interrupt! value=111405598 ra=111336329 rb=73536626 overflow=0 LDRA=1 loadoverrun=0 status=458848
TC6 interrupt! value=153404996 ra=153335999 rb=115536297 overflow=0 LDRA=1 loadoverrun=0 status=458848
TC6 interrupt! value=195404757 ra=195335670 rb=157535967 overflow=0 LDRA=1 loadoverrun=0 status=458848
TC6 interrupt! value=237404518 ra=237335341 rb=199535638 overflow=0 LDRA=1 loadoverrun=0 status=458848
TC6 interrupt! value=279404276 ra=279335011 rb=241535309 overflow=0 LDRA=1 loadoverrun=0 status=458848
timer value is 280097996

Thanks for this it saved me a heap of time.

BTW TC_IER / TC_DER are somewhat magical - the idea is you write bits into them which then set or clear bits in the actual register (which you can't directly access).

So you only need..
tc->TC_CHANNEL[channel].TC_IER = TC_IER_COVFS | TC_IER_LDRAS;

Also for the benefit of future explorers: the data sheet uses 'TCn' to mean 2 different things and doesn't say which is which. Sometimes is's a timer counter module (3 of these) and the other time it a timer counter channel (9 of these, 3 per module).