DUE Timer Counter in capture mode.

Hello all !
I have some problem to understand how TC works on DUE.

I want to mesurate 10 fan speed, and 5 are critical, so i think Timer was the solution.

Here is a part of my code:

void Init_TC () {
	REG_PIOA_PDR = 0x4;
	REG_PIOB_PDR = 0x2000000;
	REG_PIOC_PDR = 0x12000000;
	REG_PIOD_PDR = 0x80;

	REG_PIOB_ABSR = REG_PIOB_ABSR | 0x2000000u;
	REG_PIOC_ABSR = REG_PIOC_ABSR | 0x12000000u;
	REG_PIOD_ABSR = REG_PIOD_ABSR | 0x80u;

	REG_PMC_PCER0 = REG_PMC_PCER0 | 0x18000000;							// Activation des Timers, ID27 et ID28, bits 28 et 29 de PMC_PCSR0.
	REG_PMC_PCER1 = REG_PMC_PCER1 | 0xE;								// Activation des Timers, ID33, ID34 et ID35, bits 2, 3 et 4 de PMC_PCSR1

// Timer 0: Mesure de vitesse du ventilateur supérieur 1.
	TC_Configure ( TC0, 0, TC_CMR_TCCLKS_TIMER_CLOCK5 | TC_CMR_ETRGEDG_RISING | TC_CMR_ABETRG | TC_CMR_LDRA_RISING );
// Timer 1: Mesure de vitesse du ventilateur supérieur 2.
	TC_Configure ( TC0, 1, TC_CMR_TCCLKS_TIMER_CLOCK5 | TC_CMR_ETRGEDG_RISING | TC_CMR_ABETRG | TC_CMR_LDRA_RISING );
// Timer 6: Mesure de vitesse du ventilateur supérieur 3.
	TC_Configure ( TC2, 0, TC_CMR_TCCLKS_TIMER_CLOCK5 | TC_CMR_ETRGEDG_RISING | TC_CMR_ABETRG | TC_CMR_LDRA_RISING );
// Timer 7: Mesure de vitesse de la pompe 1.
	TC_Configure ( TC2, 1, TC_CMR_TCCLKS_TIMER_CLOCK5 | TC_CMR_ETRGEDG_RISING | TC_CMR_ABETRG | TC_CMR_LDRA_RISING );
// Timer 8: Mesure de vitesse de la pompe 2.
	TC_Configure ( TC2, 2, TC_CMR_TCCLKS_TIMER_CLOCK5 | TC_CMR_ETRGEDG_RISING | TC_CMR_ABETRG | TC_CMR_LDRA_RISING );
}

void read_WATERPUMPS_speed () {
	if ( REG_PWM_CDTY6 >= MINIMAL_HDW_WATERPUMP1_SPEED ) {
		const uint32_t tc7ra = TC2->TC_CHANNEL[1].TC_RA;										// La pompe 1 tourne.
		const uint32_t tc7cv = TC_ReadCV ( TC2, 1 );

		if ( ( tc7cv < PULSE_TIMEOUT ) && ( tc7ra < BLOCK_TIMEOUT ) ) WATERPUMP1_speed = ( 960000 / tc7ra );
		else if ( tc7ra >= BLOCK_TIMEOUT ) Serial.println ( ERRORMSG_WATERPUMP1_BLOCKED );
		else WATERPUMP1_speed = 0;
		WATERPUMP2_speed = 0;
	}
	else if ( REG_PWM_CDTY5 >= MINIMAL_HDW_WATERPUMP2_SPEED ) {
		const uint32_t tc8ra = TC2->TC_CHANNEL[2].TC_RA;										// La pompe 2 tourne.
		const uint32_t tc8cv = TC_ReadCV ( TC2, 2 );

		if ( ( tc8cv < PULSE_TIMEOUT ) && ( tc8ra < BLOCK_TIMEOUT ) ) WATERPUMP2_speed = ( 960000 / tc8ra );
		else if ( tc8ra >= BLOCK_TIMEOUT ) Serial.println ( ERRORMSG_WATERPUMP2_BLOCKED );
		else WATERPUMP2_speed = 0;
		WATERPUMP1_speed = 0;
	}
	else if ( ( REG_PWM_CDTY6 >= MINIMAL_HDW_WATERPUMP1_SPEED ) && ( REG_PWM_CDTY5 >= MINIMAL_HDW_WATERPUMP2_SPEED ) ) {
		const uint32_t tc7ra = TC2->TC_CHANNEL[1].TC_RA;										// Les deux pompes tournent.
		const uint32_t tc8ra = TC2->TC_CHANNEL[2].TC_RA;
		const uint32_t tc7cv = TC_ReadCV ( TC2, 1 );
		const uint32_t tc8cv = TC_ReadCV ( TC2, 2 );

		if ( ( tc7cv < PULSE_TIMEOUT ) && ( tc7ra < BLOCK_TIMEOUT ) ) WATERPUMP1_speed = ( 960000 / tc7ra );
		else if ( tc7ra >= BLOCK_TIMEOUT ) Serial.println ( ERRORMSG_WATERPUMP1_BLOCKED );
		else WATERPUMP1_speed = 0;
		if ( ( tc8cv < PULSE_TIMEOUT ) && ( tc8ra < BLOCK_TIMEOUT ) ) WATERPUMP2_speed = ( 960000 / tc8ra );
		else if ( tc8ra >= BLOCK_TIMEOUT ) Serial.println ( ERRORMSG_WATERPUMP2_BLOCKED );
		else WATERPUMP2_speed = 0;
	}
}

void read_TOP_fans () {
	if ( REG_PWM_CDTY0 != TOP_FAN_STOP ) {
		const uint32_t tc0ra = TC0->TC_CHANNEL[0].TC_RA;
		const uint32_t tc1ra = TC0->TC_CHANNEL[1].TC_RA;
		const uint32_t tc6ra = TC2->TC_CHANNEL[0].TC_RA;
		const uint32_t tc0cv = TC_ReadCV ( TC0, 0 );
		const uint32_t tc1cv = TC_ReadCV ( TC0, 1 );
		const uint32_t tc6cv = TC_ReadCV ( TC2, 0 );

		if ( ( tc0cv < PULSE_TIMEOUT ) && ( tc0ra < BLOCK_TIMEOUT ) ) TOP1_speed = ( 960000 / tc0ra );
		else if ( tc0ra >= BLOCK_TIMEOUT ) Serial.println ( ERRORMSG_TOP1_BLOCKED );
		else TOP1_speed = 0;
		if ( ( tc1cv < PULSE_TIMEOUT ) && ( tc1ra < BLOCK_TIMEOUT ) ) TOP2_speed = ( 960000 / tc1ra );
		else if ( tc1ra >= BLOCK_TIMEOUT ) Serial.println ( ERRORMSG_TOP2_BLOCKED );
		else TOP2_speed = 0;
		if ( ( tc6cv < PULSE_TIMEOUT ) && ( tc6ra < BLOCK_TIMEOUT ) ) TOP3_speed = ( 960000 / tc6ra );
		else if ( tc6ra >= BLOCK_TIMEOUT ) Serial.println ( ERRORMSG_TOP3_BLOCKED );
		else TOP3_speed = 0;

		const uint32_t TOP_speed_out = 82031 / ( ( ( TOP1_speed + TOP2_speed + TOP3_speed ) / 3 ) / 30 );
		REG_PWM_CPRDUPD4 = TOP_speed_out;
		REG_PWM_CPRDUPD7 = TOP_speed_out;
	}
	else {
		TOP1_speed = 0;
		TOP2_speed = 0;
		TOP3_speed = 0;

		REG_PWM_CPRDUPD4 = 0;
		REG_PWM_CPRDUPD7 = 0;
	}
}

TC0, TC1 and TC6 are assembled to create a PWM mirror of them.

For now, it works, but sometime speed values are out of range. Is there a way to optimize this ?
I have try to modify TC options, but i cannot really understand.

Hi there @Black_Wolf,

In a previous post I have announced a library for using the TC modules (Timer Modules) channels of DUE's Atmel ATSAM3X8E micro-controller. You have the announce in this post:

https://forum.arduino.cc/index.php?topic=144446.msg2562995#msg2562995

The name of the library is tc_lib, and is available at: GitHub - antodom/tc_lib: This is a library to take advantage of different functionalities available on Timer Counter (TC) modules in Arduino Due's Atmel ATSAM3X8E microcontrollers

The TC module channels available in the ATSAM3X8E working in capture mode can measure duty and period of digital signals directly on hardware. The capture objects available on tc_lib abstract the use of TC module channels in capture mode. Using tc_lib's capture objects is easy, just have a look to the example capture_test.ino which comes with the library. The library uses the interrupts of TC modules when using them in capture mode, so I think you will be able to get to frequencies the of 1 Mhz or greater.

Have a try. In any case, I think that you even could find out how to do what you want having a look to tc_lib's code.

I hope it helps.

Thanks Antodom for your lib.
I test it, but I have some problem to insert it in all my code. So I try to make my own code and for now it run.
I just have a question. I'm not a C++ programer and I dont realy understand for the Overrun.

It's the number of time that the RC value match the Timer value ?

How can you determine this ? I found static_cast<uint32_t>(static_cast(1<<32)-1) /ticks_per_usec(); but I dont understand the 1<<32. It's a little bit hard for me.

Hi there Black_Wolf,

In relation to the overrun, if you have a look to Atmel's ATSAM3X8E micro-controller, the TC modules ,when in capture mode, can inform you of overruns while capturing signals, document subsection 36.6.8. The overrun happens when the MCU (usually an interrupt service routine) have not been able to read the previous value of registers RA ,or RB, and a new capture has happened on the corresponding register, whether RA or RB. It is basically the indication that you are losing signal ticks (edges) because the signal you are trying to measure is too fast. The faster is the signal, the more overruns you get. Thus, in tc_lib, when a capture object detects too much overruns, it stops capturing to avoid to get the MCU "stuck" (paralysed) running interrupts associated to capture events, when measuring a signal which is very fast (from my own experience, signals with frequencies greater than 1 Mhz).

In relation to 1<<32, operator << in C/C++ is the binary shift left operator, in this case, shifts the value 1 32 bit positions to the left. It is another way to write 2^32=4294967296 (maximum number of ticks you can count with a 32-bit positive integer binary number (TC module counters are 32-bit registers).

If you need any help about how to use tc_lib with your problem, maybe I can provide you with some advice if you clarify me better what you want to do specifically.

Best.

OK.
Thanks for the overrun.

My problem with tc_lib... On a simple sketch, no problem, but in my program I have an init.cpp, an sensor.cpp, and much more.
I'm realy too lazy, and I dont want to declare many vars who switch between any portion of code.

And I like to understand what I'm dooing.

For now, I want to read a speed in RPM, so it's the period in tc_lib, and my signal is between 40Hz and... less ! The µC can make that without problem.
For now, I use 5 TC to mesurate speed. Only RA and RC on Timer are used.
The CPCS flag stop the timer if speed is less than 25 rpm but I want to re-enable Timer if it's not started.
How can I read if the Timer is started ? A read of the RA value or is there a flag for that ?

Thanks for all !

Hi again,

In relation to the timer stopped when CPCS flag is set, you can configure the timer to re-start immediately when that happens, setting the triggering of the timer with flag CPCTRG in TC_CMR (data-sheet section 3.6.6). Maybe this is what you want.

In any case, using five tc_lib capture objects you could get easily the instantaneous speeds you want to measure. You know I have try to convince you to use my library :wink:

Best.

Hi,

I found for the CPCS flag today, but I'm not online every day.
For the CPCTRG it's an idea. and with that I have less code.

Sorry for your lib. It's a really nice job, but for this project I start without any aditional lib and I want to see all what I'm doing.
Perhaps when I make a better code and when I'm more familiate with the DUE and the C++.

Thanks !

Ok :slight_smile:

Just tell me if my suggestion worked.

Herm...
I make a short test and it's not working.
I already use the CPCTRG flag in TC_CMR, and after a read of the CPCS flag in TC_SR I stop the interrupt and the Timer.
And after, I must use an software condition to restart the Timer.

It's not a problem because on my code the fans are run with a PWM value higher than 0% of duty-cycle.
I see a compare counter that can be triggered for PWM, but it's also talking about the channel 0. I dont make test of this for the moment, but if the compare can run on my 6 PWM channels ( including PWM0 ) I can win few seconds in the loop.
PS. the 8 PWM channels are used. 6 for driving fans, and 2 to creating speeds.

Hi again Black_Wolf,

One question, why do you want to stop the timer when the timer reach the RC value you establish?. I see that you want to do something when the speed falls bellow 25 rpm, but you can detect the situation using the interrupt associated to the RC match. And if you set the CPCTRG flag, the timer restarts automatically. Having a quick look to the code you have provided, if I do not understand bad, I think you are not doing anything when the speed falls bellow the 25 rpm threshold, aren't you?.

Hi !
Perhaps it's me who can not explain what I'm really make.

I want to stop or speed up or dowm my computer fan and my watercooling ( Because sound reason ).
When fan is stopped or blocked, I stop both the Timer and PWM signal and +12V. I think 25 rpm was a good value to test this in a first time.
But sometimes fans are grouped and if 1 is blocked, the other must stop too.

To start or restart, I test the PWM duty-cycle. When it was equal or more than 12,5% fans are running.

In my Timer code, I already use the CPCTRG flag in TC_CMR, and I test the CPCS flag in TC_SR

#include "init_dueboard.h"
#include "configuration_adv.h"
#include "Arduino.h"

#define HORLOGE 42000000

void Init_BOARD () {
 long pioPER = 0x200C0000;
 long pioOER = 0xC0000;

// Si ventilateurs à 4 broches, configuration de la broche Arduino en OUTPUT.
 if ( TOP_FAN_NB_PINS == 4 ) {
 pioPER |= 0x8;
 pioOER |= 0x8;
 }
 if ( HDD_FAN_NB_PINS == 4 ) {
 pioPER |= 0x20;
 pioOER |= 0x20;
 }
 if ( FRONT_FAN_NB_PINS == 4 ) {
 pioPER |= 0x80;
 pioOER |= 0x80;
 }
 if ( GPU_FAN_NB_PINS == 4 ) {
 pioPER |= 0x200;
 pioOER |= 0x200;
 }

//BEGIN - Initialisation des I/O:
 REG_PIOA_PER = 0x11C10058; // 0001 0001 1100 0001 0000 0000 0101 1000, Définition des entrées des boutons et des sorties pour les retours vers la carte mère.
 REG_PIOA_ODR = 0x10000000; //                                          Activation des entrées.
 REG_PIOA_PUER = 0x10000000; //                                          Activation des PULL_UP. ( Pin D10 )
 REG_PIOB_PER = 0xC224000; // 1100 0010 0010 0100 0000 0000 0000 0000, Définition des sorties diverses. ( Pins D52, D53 )
 REG_PIOB_OER = 0xC204000; //                                          Activation des sorties.
 REG_PIOC_PER = pioPER; // 0010 0100 0000 1100 0000 00?? ???? ??00, Définition des sorties PWM et diverses.
 REG_PIOC_ODR = 0x20000000; //                                          Activation des entrées.
 REG_PIOC_PUER = 0x20000000; //                                          Activation des PULL_UP. ( Pin D10 )
 REG_PIOC_OER = pioOER; //                                          Activation des sorties. ( Pins D4, d35, d37, d39, d41, D44, D45 )
 REG_PIOD_PER = 0x4E; //                               0100 1110, Définition des entrées des ventilateurs. ( Pins D11, D12, D25, D26, D27, D28, D30, D32 )
 REG_PIOD_ODR = 0x4E; //                                          Activation des entrées.
 REG_PIOD_PUER = 0x4E; //                                          Activation des PULL_UP.
//END
}

void Init_PWM () {
 long pioPDR = 0x1E00000;

// Si ventilateurs à 4 broches, configuration des sorties PWM Low, si ventilateurs à 3 broches, configuration des PWM Hight.
 if ( TOP_FAN_NB_PINS == 4 ) pioPDR |= 0x4;
 else pioPDR |= 0x8;
 if ( HDD_FAN_NB_PINS == 4 ) pioPDR |= 0x10;
 else pioPDR |= 0x20;
 if ( FRONT_FAN_NB_PINS == 4 ) pioPDR |= 0x40;
 else pioPDR |= 0x80;
 if ( GPU_FAN_NB_PINS == 4 ) pioPDR |= 0x100;
 else pioPDR |= 0x200;

 REG_PIOC_PDR = pioPDR; // 0000 0001 1110 0000 0000 00?? ???? ??00, PIO Disable Register.
 REG_PIOC_ABSR = REG_PIOC_ABSR | pioPDR; // 0000 0001 1110 0000 0000 00?? ???? ??00, Abandon de la fonction A, sélection de la fonction B.
 REG_PMC_PCER1 = REG_PMC_PCER1 | 16; // Activation de l'horloge pour le PWM, ID36, bit 5 de PMC_PCSR1.
 REG_PWM_ENA = REG_PWM_SR | 0xFF; // Activation de toutes les fonctions PWM.

 REG_PWM_CMR4 = 0xA;
 REG_PWM_CMR7 = 0xA;

 REG_PWM_CPRD0 = 3360; // 84MHz / 3360 = 25kHz
 REG_PWM_CPRD1 = 3360;
 REG_PWM_CPRD2 = 3360;
 REG_PWM_CPRD3 = 3360;
 REG_PWM_CPRD4 = 0; // Dynamique ! Estimé à environ 40 Hz.
 REG_PWM_CPRD5 = 3360;
 REG_PWM_CPRD6 = 3360;
 REG_PWM_CPRD7 = 0; // Dynamique ! Estimé à environ 40 Hz.
 
 REG_PWM_CDTY0 = 3360; // Ventilateurs supérieurs - Duty_cycle de 100%
 REG_PWM_CDTY1 = 3360; // Ventilateurs de disque durs
 REG_PWM_CDTY2 = 3360; // Ventilateur avant
 REG_PWM_CDTY3 = 3360; // Ventilateur arrière
 REG_PWM_CDTY4 = 5;
 REG_PWM_CDTY5 = 3360; // Pompe 2
 REG_PWM_CDTY6 = 3360; // Pompe 1
 REG_PWM_CDTY7 = 5;
}

void Init_TC () {
 REG_PIOA_PDR = 0x4;
 REG_PIOB_PDR = 0x2000000;
 REG_PIOC_PDR = 0x12000000;
 REG_PIOD_PDR = 0x80;

 REG_PIOB_ABSR = REG_PIOB_ABSR | 0x2000000u;
 REG_PIOC_ABSR = REG_PIOC_ABSR | 0x12000000u;
 REG_PIOD_ABSR = REG_PIOD_ABSR | 0x80u;

 REG_PMC_PCER0 = REG_PMC_PCER0 | 0x18000000; // Activation des Timers, ID27 et ID28, bits 28 et 29 de PMC_PCSR0.
 REG_PMC_PCER1 = REG_PMC_PCER1 | 0xE; // Activation des Timers, ID33, ID34 et ID35, bits 2, 3 et 4 de PMC_PCSR1

 uint32_t ticks_per_usecs = HORLOGE / 1000000;

// Timer 0: Mesure de vitesse du ventilateur supérieur 1.
// TC_Configure ( TC0, 0, TC_CMR_TCCLKS_TIMER_CLOCK5 | TC_CMR_ETRGEDG_RISING | TC_CMR_ABETRG | TC_CMR_LDRA_RISING );
 TC_Configure ( TC0, 0,
 TC_CMR_TCCLKS_TIMER_CLOCK1 | // Horloge de Timer 1 ( 84 MHz / 2 => 42 MHz ).
 TC_CMR_CPCTRG | // Reset du Timer quand il est égal à RC.
 TC_CMR_LDRA_FALLING | // Capture de RA sur front descendant. Periode du signal !
 TC_CMR_ETRGEDG_FALLING | // Capture externe sur front descendant.
 TC_CMR_ABETRG // Capture externe sur TIOA. C'est mon signal.
 );

 TC_SetRC ( TC0, 0, 1200000 * ticks_per_usecs ); // Temps maximum de capture du signal ( = 1200 mS * 42 ), ou ( durée du signal * Nombre de Ticks par µS. )

 TC0->TC_CHANNEL[0].TC_IER = TC_IER_LDRAS; // Interruption sur chargement de RA.
 TC0->TC_CHANNEL[0].TC_IER = TC_IER_CPCS; // Interruption sur comparaison RC.

// NVIC_SetPriority ( TC0_IRQn, 0 );
// NVIC_ClearPendingIRQ ( TC0_IRQn );
 NVIC_EnableIRQ ( TC0_IRQn );
// Timer 1: Mesure de vitesse du ventilateur supérieur 2.
// TC_Configure ( TC0, 1, TC_CMR_TCCLKS_TIMER_CLOCK5 | TC_CMR_ETRGEDG_RISING | TC_CMR_ABETRG | TC_CMR_LDRA_RISING );
 TC_Configure ( TC0, 1,
 TC_CMR_TCCLKS_TIMER_CLOCK1 |
 TC_CMR_CPCTRG |
 TC_CMR_LDRA_FALLING |
 TC_CMR_ETRGEDG_FALLING |
 TC_CMR_ABETRG
 );

 TC_SetRC ( TC0, 1, 1200000 * ticks_per_usecs );

 TC0->TC_CHANNEL[1].TC_IER = TC_IER_LDRAS;
 TC0->TC_CHANNEL[1].TC_IER = TC_IER_CPCS;

// NVIC_SetPriority ( TC1_IRQn, 0 );
// NVIC_ClearPendingIRQ ( TC1_IRQn );
 NVIC_EnableIRQ ( TC1_IRQn );
// Timer 6: Mesure de vitesse du ventilateur supérieur 3.
// TC_Configure ( TC2, 0, TC_CMR_TCCLKS_TIMER_CLOCK5 | TC_CMR_ETRGEDG_RISING | TC_CMR_ABETRG | TC_CMR_LDRA_RISING );
 TC_Configure ( TC2, 0,
 TC_CMR_TCCLKS_TIMER_CLOCK1 |
 TC_CMR_CPCTRG |
 TC_CMR_LDRA_FALLING |
 TC_CMR_ETRGEDG_FALLING |
 TC_CMR_ABETRG
 );

 TC_SetRC ( TC2, 0, 1200000 * ticks_per_usecs );

 TC2->TC_CHANNEL[0].TC_IER = TC_IER_LDRAS;
 TC2->TC_CHANNEL[0].TC_IER = TC_IER_CPCS;

// NVIC_SetPriority ( TC6_IRQn, 0 );
// NVIC_ClearPendingIRQ ( TC6_IRQn );
 NVIC_EnableIRQ ( TC6_IRQn );
// Timer 7: Mesure de vitesse de la pompe 1.
// TC_Configure ( TC2, 1, TC_CMR_TCCLKS_TIMER_CLOCK5 | TC_CMR_ETRGEDG_RISING | TC_CMR_ABETRG | TC_CMR_LDRA_RISING );
 TC_Configure ( TC2, 1,
 TC_CMR_TCCLKS_TIMER_CLOCK1 |
 TC_CMR_CPCTRG |
 TC_CMR_LDRA_FALLING |
 TC_CMR_ETRGEDG_FALLING |
 TC_CMR_ABETRG
 );

 TC_SetRC ( TC2, 1, 1200000 * ticks_per_usecs );

 TC2->TC_CHANNEL[1].TC_IER = TC_IER_LDRAS;
 TC2->TC_CHANNEL[1].TC_IER = TC_IER_CPCS;

// NVIC_SetPriority ( TC7_IRQn, 0 );
// NVIC_ClearPendingIRQ ( TC7_IRQn );
 NVIC_EnableIRQ ( TC7_IRQn );
// Timer 8: Mesure de vitesse de la pompe 2.
// TC_Configure ( TC2, 2, TC_CMR_TCCLKS_TIMER_CLOCK5 | TC_CMR_ETRGEDG_RISING | TC_CMR_ABETRG | TC_CMR_LDRA_RISING );
 TC_Configure ( TC2, 2,
 TC_CMR_TCCLKS_TIMER_CLOCK1 |
 TC_CMR_CPCTRG |
 TC_CMR_LDRA_FALLING |
 TC_CMR_ETRGEDG_FALLING |
 TC_CMR_ABETRG
 );

 TC_SetRC ( TC2, 2, 1200000 * ticks_per_usecs );

 TC2->TC_CHANNEL[2].TC_IER = TC_IER_LDRAS;
 TC2->TC_CHANNEL[2].TC_IER = TC_IER_CPCS;

// NVIC_SetPriority ( TC8_IRQn, 0 );
// NVIC_ClearPendingIRQ ( TC8_IRQn );
 NVIC_EnableIRQ ( TC8_IRQn );
}

Hi again,

You are setting RC to 1200 msecs. but 25 rpm is a period of 40 msecs, right?. In any case, I see now that the CPCTRG flag is of no use here.

Just an idea, according to the ATSAM3X8E's datatsheet, if you use RB you can stop the timer automatically when reaching the value contained in RB. I imagine you are stopping the timer somewhere explicitly, and then use SWTRG to restart the timer, aren't you?. Using RB, at least, you might stop the timer automatically.

Best.

Sorry this morning I exceed the forum limits so my post was not complet.

25 rpm is a period of 40 msecs, but there was 2 tops per tour. So it's a period of 20 msecs. But for this time it's an empyric value for testing whats append if fans are blocked.

For SWTRG no, I'm not use this.

END of previous post...
Partial sensors code

void read_WATERPUMPS_speed () {
 if ( REG_PWM_CDTY6 >= MINIMAL_HDW_WATERPUMP1_SPEED ) {
 if ( tc7ra > 0 ) WATERPUMP1_speed = 60 / ( 0.000001 * ( tc7ra / 21 ) );
 else WATERPUMP1_speed = 0;
 WATERPUMP2_speed = 0;
 }
 else if ( REG_PWM_CDTY5 >= MINIMAL_HDW_WATERPUMP2_SPEED ) {
 if ( tc8ra > 0 ) WATERPUMP2_speed = 60 / ( 0.000001 * ( tc8ra / 21 ) );
 else WATERPUMP2_speed = 0;
 WATERPUMP1_speed = 0;
 }
 else if ( ( REG_PWM_CDTY6 >= MINIMAL_HDW_WATERPUMP1_SPEED ) && ( REG_PWM_CDTY5 >= MINIMAL_HDW_WATERPUMP2_SPEED ) ) {
 if ( tc7ra > 0 ) WATERPUMP1_speed = 60 / ( 0.000001 * ( tc7ra / 21 ) );
 else WATERPUMP1_speed = 0;
 if ( tc8ra > 0 ) WATERPUMP2_speed = 60 / ( 0.000001 * ( tc8ra / 21 ) );
 else WATERPUMP2_speed = 0;
 }
}

void read_TOP_fans () {
 if ( REG_PWM_CDTY0 != TOP_FAN_STOP ) {
 if ( tc0ra > 0 ) TOP1_speed = 60 / ( 0.000001 * ( tc0ra / 21 ) );
 else TOP1_speed = 0;
 if ( tc1ra > 0 ) TOP2_speed = 60 / ( 0.000001 * ( tc1ra / 21 ) );
 else TOP2_speed = 0;
 if ( tc6ra > 0 ) TOP3_speed = 60 / ( 0.000001 * ( tc6ra / 21 ) );
 else TOP3_speed = 0;

 const uint32_t TOP_speed_out = 82031 / ( ( ( TOP1_speed + TOP2_speed + TOP3_speed ) / 3 ) / 30 );
 REG_PWM_CPRDUPD4 = TOP_speed_out;
 REG_PWM_CPRDUPD7 = TOP_speed_out;
 }
 else {
 TOP1_speed = 0;
 TOP2_speed = 0;
 TOP3_speed = 0;

 REG_PWM_CPRDUPD4 = 0;
 REG_PWM_CPRDUPD7 = 0;
 }
}

...

void TC0_Handler () {
 statusTC0 = TC_GetStatus( TC0, 0 );

 if ( ( statusTC0 & TC_SR_LDRAS ) && ( TC0 -> TC_CHANNEL[0].TC_IMR & TC_IMR_LDRAS ) )  {
 tc0ra = TC0 -> TC_CHANNEL[0].TC_RA;
 }

 if ( ( statusTC0 & TC_SR_CPCS ) && ( TC0 -> TC_CHANNEL[0].TC_IMR & TC_IMR_CPCS ) ) {
 TC0Try_nb ++;
 tc0ra = 0;
 if ( TC0Try_nb >= NB_TRY ) {
 REG_PWM_CDTYUPD0 = TOP_FAN_STOP;
 NVIC_DisableIRQ ( TC0_IRQn );
 TC_Stop ( TC0, 0 );
 Serial.println ( ERRORMSG_TOP1_BLOCKED );
 TC0Try_nb = 0;
 }
 }
}

...

Partial loop

void loop () {
//BEGIN Routine de marche / arrêt des pompes / ventilateurs.
 if ( ( REG_PWM_CDTY6 < MINIMAL_HDW_WATERPUMP1_SPEED ) && ( !( REG_PIOC_ODSR & 19 ) ) ) REG_PIOC_CODR |= 1<<19; // Arrêt de la pompe 1.
 else {
 if ( REG_PIOC_ODSR & 19 ) REG_PIOC_SODR |= 1<<19; // Démarrage de la pompe 1.
 if ( ( statusTC7 & TC_SR_CPCS ) && ( TC2 -> TC_CHANNEL[1].TC_IMR & TC_IMR_CPCS ) ) {
 TC_Start ( TC2, 1 ); // Démarrage du Timer si non démarré !
 }
 }

 if ( ( REG_PWM_CDTY5 < MINIMAL_HDW_WATERPUMP2_SPEED ) && ( !( REG_PIOC_ODSR & 18 ) ) ) REG_PIOC_CODR |= 1<<18; // Arrêt de la pompe 2.
 else {
 if ( REG_PIOC_ODSR & 18 ) REG_PIOC_SODR |= 1<<18; // Démarrage de la pompe 2.
 if ( ( statusTC8 & TC_SR_CPCS ) && ( TC2 -> TC_CHANNEL[2].TC_IMR & TC_IMR_CPCS ) ) {
 TC_Start ( TC2, 2 ); // Démarrage du Timer si non démarré !
 }
 }

 if ( FRONT_FAN_NB_PINS == 4 ) {
 if ( ( REG_PWM_CDTY2 < MINIMAL_HDW_FRONT_FAN_SPEED ) && ( !( REG_PIOC_ODSR & 7 ) ) ) REG_PIOC_CODR |= 1<<7; // Arrêt du ventilateur frontal.
 else if ( ( REG_PWM_CDTY2 >= MINIMAL_HDW_FRONT_FAN_SPEED ) && ( REG_PIOC_ODSR & 7 ) ) REG_PIOC_SODR |= 1<<7; // Démarrage du ventilateur frontal.
 }

 if ( ( REG_PWM_CDTY0 < MINIMAL_HDW_TOP_FAN_SPEED ) && ( !( REG_PIOC_ODSR & 3 ) ) ) {
 if ( TOP_FAN_NB_PINS == 4 ) REG_PIOC_CODR |= 1<<3; // Arrêt des ventilateurs supérieurs.
 NVIC_DisableIRQ ( TC0_IRQn );
 NVIC_DisableIRQ ( TC1_IRQn );
 NVIC_DisableIRQ ( TC6_IRQn );
 TC_Stop ( TC0, 0 );
 TC_Stop ( TC0, 1 );
 TC_Stop ( TC2, 0 );
 }
 else if ( ( REG_PWM_CDTY0 >= MINIMAL_HDW_TOP_FAN_SPEED ) && ( REG_PIOC_ODSR & 3 ) ) {
 if ( TOP_FAN_NB_PINS == 4 ) REG_PIOC_SODR |= 1<<3; // Démarrage des ventilateurs supérieurs.
 if ( ( statusTC0 & TC_SR_CPCS ) && ( TC0 -> TC_CHANNEL[0].TC_IMR & TC_IMR_CPCS ) ) {
 NVIC_EnableIRQ ( TC0_IRQn );
 TC_Start ( TC0, 0 ); // Démarrage des Timers si non démarrés !
 }
 if ( ( statusTC1 & TC_SR_CPCS ) && ( TC0 -> TC_CHANNEL[1].TC_IMR & TC_IMR_CPCS ) ) {
 NVIC_EnableIRQ ( TC1_IRQn );
 TC_Start ( TC0, 1 );
 }
 if ( ( statusTC6 & TC_SR_CPCS ) && ( TC2 -> TC_CHANNEL[0].TC_IMR & TC_IMR_CPCS ) ) {
 NVIC_EnableIRQ ( TC6_IRQn );
 TC_Start ( TC2, 0 );
 }
 }

 if ( GPU_FAN_NB_PINS == 4 ) {
 if ( ( REG_PWM_CDTY3 < MINIMAL_HDW_GPU_FAN_SPEED ) && ( !( REG_PIOC_ODSR & 9 ) ) ) REG_PIOC_CODR |= 1<<9; // Arrêt du ventilateur arrière.
 else if ( ( REG_PWM_CDTY3 >= MINIMAL_HDW_GPU_FAN_SPEED ) && ( REG_PIOC_ODSR & 9 ) ) REG_PIOC_SODR |= 1<<9; // Démarrage du ventilateur arrière.
 }

 if ( HDD_FAN_NB_PINS == 4 ) {
 if ( ( REG_PWM_CDTY1 < MINIMAL_HDW_HDD_FAN_SPEED ) && ( !( REG_PIOC_ODSR & 5 ) ) ) REG_PIOC_CODR |= 1<<5; // Arrêt des ventilateurs de disque dur.
 else if ( ( REG_PWM_CDTY1 >= MINIMAL_HDW_HDD_FAN_SPEED ) && ( REG_PIOC_ODSR & 5 ) ) REG_PIOC_SODR |= 1<<5; // Démarrage des ventilateurs de disque dur.
 }
//END

 read_SENSORS ();

...

For the PWM I have an other idea so perhaps that code can change.

Hi again,

Flag CLKSTA in TC status register TC_SR allow you to check if a TC channel is disabled, since when you stop a TC channel (TC_Stop()) you are disabling it. Maybe this is what you want.

Best.

I test this when I have time.
Tomorrow I print interesting part of then datasheet at my job.

Hi,
I test the flag CLKSTA, but... it's not really good.
When I start a timer it's OK, the flag is 0 since I stop the timer, and only after the flag is 1 and thats all.
When I restart timer CLKSTA is always 1.

Simple test...

#define NB_TRY 5
#define HORLOGE 42000000

uint32_t tc6ra;
uint32_t statusTC6;
int TC6Try_nb;
uint32_t _ticks_per_usecs = HORLOGE / 1000000;


void setup () {
 Serial.begin ( 115200 );

 REG_PIOC_PDR = 0x2000000;
 REG_PIOC_ABSR = REG_PIOC_ABSR | 0x2000000u;
 REG_PMC_PCER1 = REG_PMC_PCER1 | 0x2;

 TC_Configure ( TC2, 0,
       TC_CMR_TCCLKS_TIMER_CLOCK1 |
       TC_CMR_CPCTRG |
       TC_CMR_LDRA_FALLING |
       TC_CMR_ETRGEDG_FALLING |
       TC_CMR_ABETRG
 );
 
 TC_SetRC ( TC2, 0, 1200000 * _ticks_per_usecs );
 
 TC2->TC_CHANNEL[0].TC_IER = TC_IER_LDRAS;
 TC2->TC_CHANNEL[0].TC_IER = TC_IER_CPCS;
 
 NVIC_SetPriority ( TC6_IRQn, 1 );
 NVIC_ClearPendingIRQ ( TC6_IRQn );
 NVIC_EnableIRQ ( TC6_IRQn );
}

void loop () {
 TC_Start(TC2,0);
// NVIC_EnableIRQ(TC6_IRQn);
 Serial.print("ON\t");
 Serial.println((statusTC6 & TC_SR_CLKSTA), HEX);
 delay(3000);
 TC_Stop(TC2,0);
// NVIC_DisableIRQ(TC6_IRQn);
 TC2->TC_CHANNEL[0].TC_CCR = TC_CCR_CLKDIS;
 Serial.print("OFF\t");
 Serial.println((statusTC6 & TC_SR_CLKSTA), HEX);
 delay(3000);
}

void TC6_Handler () {
 statusTC6 = TC_GetStatus(TC2, 0);
 
 if ( ( statusTC6 & TC_SR_LDRAS ) && ( TC2 -> TC_CHANNEL[0].TC_IMR & TC_IMR_LDRAS ) )  {
 tc6ra = TC2->TC_CHANNEL[0].TC_RA;
 }
 
/* if ( ( statusTC6 & TC_SR_CPCS ) && ( TC2 -> TC_CHANNEL[0].TC_IMR & TC_IMR_CPCS ) ) {
 TC6Try_nb ++;
 tc6ra = 0;
 if ( TC6Try_nb >= NB_TRY ) {
 REG_PWM_CDTYUPD0 = TOP_FAN_STOP;
 NVIC_DisableIRQ ( TC6_IRQn );
 TC_Stop ( TC2, 0 );
 if ( DEBUG ) Serial.println ( ERRORMSG_TOP3_BLOCKED );
 TC6Try_nb = 0;
 }
 }*/
}

I miss something ?

[EDIT]
OK, I found an other way. I disable the timer in the PMC when I stop it, and I read the status when I want to test it.