I am making a mental flowchart of how my system will run. Reading on Timer Capture Mode seems to be a good fit for my purpose but i have a few questions
Timer Capture Mode basically captures the time when a pin state changed.
Here are my questions:
What are the pins on the Due that is capable on doing this?
Can multiple pins be set to this mode at the same time? If multiple pins can be set, do they have individual registers for their own respective time? or do they share 1 register and any new state would override the previous one
How would this be different than setting a pin on interrupt and save current time on a variable ?
You need a broken out TIOAx pin and select the corresponding Timer Counter in Capture mode. Not using interrupts permits higher frequencies (and duty cycle) detection.
Graynomad pinout diagram and Sam3x datasheet are mandatory to understand the example code below.
An example sketch with 2 Timer Counters: A TC is in waveform mode to simulate the input signal, the other in Capture mode to decipher frequency and duty cycle.
/**************************************************************************************/
/* Capture up to 1.5 MHz frequency */
/* Hook a jumper between pin 2 (TIOA0) and pin A7 (TIOA1) */
/**************************************************************************************/
void setup() {
Serial.begin(250000);
/************* Timer Counter 0 Channel 0 to generate PWM pulses thru TIOA0 ************/
PMC->PMC_PCER0 |= PMC_PCER0_PID27; // Timer Counter 0 channel 0 IS TC0
PIOB->PIO_PDR |= PIO_PDR_P25;
PIOB->PIO_ABSR |= PIO_ABSR_P25;
TC0->TC_CHANNEL[0].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 // MCK/2, clk on rising edge
| TC_CMR_WAVE // Waveform mode
| TC_CMR_WAVSEL_UP_RC // UP mode with automatic trigger on RC Compare
| TC_CMR_ACPA_CLEAR // Clear TIOA0 on RA compare match
| TC_CMR_ACPC_SET; // Set TIOA0 on RC compare match
TC0->TC_CHANNEL[0].TC_RC = 28; //<********************* Frequency = (Mck/2)/TC_RC Hz
TC0->TC_CHANNEL[0].TC_RA = 14; //<******************** Duty cycle = (TC_RA/TC_RC) * 100 %
TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC0 counter and enable
/************* Timer Counter 0 Channel 1 to capture PWM pulses thru TIOA1 ************/
PMC->PMC_PCER0 |= PMC_PCER0_PID28; // Timer Counter 0 channel 1 IS TC1
TC0->TC_CHANNEL[1].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 // capture mode, MCK/1, clk on rising edge
| TC_CMR_ABETRG // TIOA1 is used as the external trigger
| TC_CMR_LDRA_RISING // load RA on rising edge of trigger input
| TC_CMR_LDRB_FALLING; // load RB on falling edge of trigger input
TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC1 counter and enable
}
void loop() {
const uint32_t _F_TC1 = F_CPU / 2; //<************ = TC1 clock
uint32_t CaptureCountA, CaptureCountB, Period, Duty;
boolean CaptureFlag;
uint32_t counter;
uint32_t Frequency;
uint32_t _Duty;
uint32_t _CaptureCountA;
uint32_t status;
while (true) {
status = TC0->TC_CHANNEL[1].TC_SR;
while (status == 0) {
status = TC0->TC_CHANNEL[1].TC_SR;
} // Wait till status != 0
if (status & TC_SR_LDRAS) { // If ISR is triggered by LDRAS then ....
CaptureCountA = (uint32_t) TC0->TC_CHANNEL[1].TC_RA; // get data from capture register A of TC0 channel 1
Period = CaptureCountA - _CaptureCountA;
_CaptureCountA = CaptureCountA;
}
else { /*if ((status & TC_SR_LDRBS) == TC_SR_LDRBS)*/ // If ISR is triggered by LDRBS then ....
CaptureCountB = (uint32_t) TC0->TC_CHANNEL[1].TC_RB; // get data from capture register B of TC0 channel 1
Duty = CaptureCountB - _CaptureCountA;
CaptureFlag = 1; // set flag indicating a new capture value is available
}
if (counter > 100000) { // write Frequency and Duty every 10000 samples
if ( CaptureFlag == 1) {
Frequency = (uint32_t)(_F_TC1 / Period) ; // (Mck/2 is TC1 clock) F in Hz
_Duty = (uint32_t)((Duty * 100) / Period);
if (_Duty <= 100) printf(" F = %d Hz , Duty = %d per cent\n", Frequency, _Duty);
counter = 0;
CaptureFlag = 0;
}
}
counter++;
}
}