drm0hr:
Registers TC_RA and TC_RB are actually capture registers, at least when the TC is put into capture mode. Setting TC_CMR_LDRA and TC_CMR_LDRB dictate when the registers capture the counter value based on the TIOA signal behaviour. In my original example I use TC_CMR_LDRA_FALLING, so every time the TIOA signal falls the current counter value is stored in register TC_RA. This way you never need to actually read the counter value directly.
I seem to have a working sketch now. It incorporates some code from the Atmel Software Framework, mainly the direct register manipulation.
The basic working principle is that a timer counter (TC) is put into capture mode. The TC is told to load capture register A (TC_RA) on the rising edge of a PWM signal and capture register B (TC_RB) on the falling edge. The number of counts that the signal is active for is equal to TC_RB - TC_RA. An interrupt routine is run every time TC_RB is loaded and saves the register values to variables. The clock is also reset on the falling edge. The PWM frequency, duty cycle and time active can be calculated because the TC clock frequency, and therefore the duration of each count, is known.
I have tested this using all 5 available TIOAx channels individually and they all seem to work. I have not yet tested multiple channels running at the same time. The sketch also has some issues when the PWM signal is noisy, occasionally giving very incorrect values.
TL;DR The timer counter calculates PWM frequency and duty cycle while using almost no CPU time (i.e., it is non-blocking)
/*
- PWM Capture using Arduino Due Timer Counters
- Available channels:
* TC Chan NVIC irq Handler PMC ID Arduino Pin
* TC0 0 TC0_IRQn TC0_Handler ID_TC0 D2 (TIOA0)
* TC0 1 TC1_IRQn TC1_Handler ID_TC1 D61/A7 (TIOA1)
* TC2 0 TC6_IRQn TC6_Handler ID_TC6 D5 (TIOA6)
* TC2 1 TC7_IRQn TC7_Handler ID_TC7 D3 (TIOA7)
* TC2 2 TC8_IRQn TC8_Handler ID_TC8 D11 (TIOA8)
- Change the following defines to use different channels as input.
*/
#define CAPTURE_TC TC0
#define CAPTURE_CHANNEL 0
#define CAPTURE_IRQn TC0_IRQn
#define CAPTURE_Handler TC0_Handler
#define CAPTURE_ID ID_TC0
#define CAPTURE_PIN 2
#define CAPTURE_CLOCK_SELECTION TC_CMR_TCCLKS_TIMER_CLOCK3
// clock divisors corresponding to CAPTURE_CLOCK_SELECTION
static const uint32_t divisors[5] = { 2, 8, 32, 128, 0};
volatile uint32_t captured_pulses = 0;
volatile uint32_t captured_ra = 0;
volatile uint32_t captured_rb = 0;
uint32_t frequency, duty_cycle, active_time;
// timer interrupt handle
void CAPTURE_Handler() {
if ((TC_GetStatus(CAPTURE_TC, CAPTURE_CHANNEL) & TC_SR_LDRBS) == TC_SR_LDRBS) {
captured_pulses++;
captured_ra = CAPTURE_TC->TC_CHANNEL[CAPTURE_CHANNEL].TC_RA;
captured_rb = CAPTURE_TC->TC_CHANNEL[CAPTURE_CHANNEL].TC_RB;
}
}
void setup() {
Serial.begin(57600);
Serial.print("Initializing...");
// configure the PIO pin as peripheral
const PinDescription config = &g_APinDescription[CAPTURE_PIN];
PIO_Configure(
config->pPort,
config->ulPinType,
config->ulPin,
config->ulPinConfiguration
);
// enable timer peripheral clock
pmc_enable_periph_clk(CAPTURE_ID);
// configure the timer
TC_Configure(CAPTURE_TC, CAPTURE_CHANNEL,
CAPTURE_CLOCK_SELECTION / Clock Selection /
| TC_CMR_LDRA_RISING / RA Loading: rising edge of TIOA /
| TC_CMR_LDRB_FALLING / RB Loading: falling edge of TIOA /
| TC_CMR_ABETRG / External Trigger: TIOA /
| TC_CMR_ETRGEDG_FALLING / External Trigger Edge: Falling edge */
);
// configure TC interrupts
NVIC_DisableIRQ(CAPTURE_IRQn);
NVIC_ClearPendingIRQ(CAPTURE_IRQn);
NVIC_SetPriority(CAPTURE_IRQn, 0);
NVIC_EnableIRQ(CAPTURE_IRQn);
// enable interrupts
CAPTURE_TC->TC_CHANNEL[CAPTURE_CHANNEL].TC_IER = TC_IER_LDRBS;
// start timer counter
CAPTURE_TC->TC_CHANNEL[CAPTURE_CHANNEL].TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;
Serial.println("ready!");
}
void loop() {
// measurement is interrupt based so delay doesn't really block it
delay(1000);
Serial.print("Captured "); Serial.print(captured_pulses);
Serial.println(" pulses from TC since last read");
captured_pulses = 0;
// frequency in Hz
frequency
= (F_CPU / divisors[CAPTURE_CLOCK_SELECTION]) / captured_rb;
// duty cycle in percent
duty_cycle
= (captured_rb - captured_ra) * 100 / captured_rb;
// time active in microseconds
active_time
= ((captured_rb - captured_ra) * 1000) /
((F_CPU / divisors[CAPTURE_CLOCK_SELECTION]) / 1000);
Serial.print("Captured wave frequency = "); Serial.print(frequency);
Serial.print(" Hz, Duty cycle = "); Serial.print(duty_cycle);
Serial.print(" %, Pulse width = "); Serial.print(active_time);
Serial.println(" us");
}
output:
Initializing...ready!
Captured 52 pulses from TC since last read
Captured wave frequency = 52 Hz, Duty cycle = 6 %, Pulse width = 1292 us
Captured 53 pulses from TC since last read
Captured wave frequency = 52 Hz, Duty cycle = 6 %, Pulse width = 1292 us
Captured 53 pulses from TC since last read
Captured wave frequency = 52 Hz, Duty cycle = 6 %, Pulse width = 1290 us
Captured 52 pulses from TC since last read
Captured wave frequency = 52 Hz, Duty cycle = 6 %, Pulse width = 1292 us
when i compile this code got an error
'TC0' was not declared in this scope
what could be the reason