Write by Register and Increase Frequency PWM TIO x - Pins 2 to 5 and 10 to 13

Hello,
In my project the DUE will pilot up to 6 three-phase frequency inverters controlling 6 220v tri-geared motors.
Each geared motor is connected to a crank rod system controlled in position by a 360 ° potentiometer. The normal working stroke will be + 60 ° / 0 ° / -60 °

For the drive control part (out of the internally managed position feedback loop in the Due, I need for each motor to:
1 ADC input (no problem at the moment)
2 digital outputs to control the direction of rotation at the input of the drives (subject treated on the post By the registers how to prohibit to have 2 digital pins at the same time in ON ? - Arduino Due - Arduino Forum )
)
1 PWM output having a high frequency so that at the output of second level RC filtering the signal is as continuous and pure as possible

In order to have the fastest possible PID management, I want to use the registers of the SAM AT91SAM 3X8E of the DUE

The PWM output will be used as speed reference at the drive input
Specifications for PWM outputs:
PWM 12 bits with a change of duty through the registers - How?
Maximum frequency usable in PWM 12 bits - What is it?

6 Pins among these 8 pins PWM Timer are destined in my project to the speed setpoint:
Inverter speed setpoint 1: Pin 2 (TIOA0)
Inverter speed setpoint 2: Pin 3 (TIOA7)
Inverter speed setpoint 3: Pin 4 (TIOB6)
Inverter speed setpoint 4: Pin 5 (TIOA6)
Inverter speed setpoint 5: Pin 10 (TIOB7)
Inverter speed setpoint 6: Pin 11 (TIOA8)
In reserve for extension: Pin 12 (TIOB8) and Pin 13 (TIOB0)

These 6 pins will be interfaced to adapt the voltage and output power with 2 OP amplifier TLV274IN or LM324N

About the 8 TIOx PWMs (pins 2 to 5 and pins 10 to 13), how often can they be operated in a sustainable way if you want to use a 12-bit PWM.
PWM TIOx Maxi: 10Khz in 12 bits? I am currently getting 10KHz in 12 bits by changing variant.h

1) Can you go higher in frequency without CPU heating?

2) How does changing variant.h happen? (10khz or more!)

3) How to vary the duty of PWM TIOx through the registers?
As for the PWM Lx and PWM Hx?
Purpose: Decrease response time by avoiding using digitalWrite (pin, value) ?

Thank you for all your valuable information that you will bring!

Note:
I had modified variant.h to have 40kHz in 12 bits, it worked well only a few hours before the deterioration of the Due CPU

Bonjour,
Dans mon projet la DUE pilotera jusqu’à 6 variateurs de fréquences triphasé commandant 6 motoréducteurs 220v tri.
Chaque motoréducteur est relié à un système bielle manivelle contrôlé en position par un potentiomètre à 360°. La course de travail normale sera +60° /0° / -60°

Pour la partie de commande du variateur (hors de la boucle de feedback de position géré en interne dans la Due, j’ai besoin pour chaque moteur de :
1 Entrée ADC (aucun problème pour l’instant)
2 Sorties digitales afin de commander le sens de rotation à l’entrée des variateurs (sujet traité sur le post By the registers how to prohibit to have 2 digital pins at the same time in ON ? - Arduino Due - Arduino Forum )
1 Sortie PWM ayant une fréquence élevée afin qu’en sortie du filtrage RC de second niveau le signal soit le plus continu et pur possible

Afin d’avoir une gestion du PID la plus rapide possible, je souhaite utiliser les registres du SAM AT91SAM 3X8E de la DUE

La sortie PWM servira de consigne de vitesse en entrée variateur
Cahier des charges pour les sorties PWM :
PWM 12 bits avec un changement de duty en passant par les registres – Comment ?
Fréquence maximale utilisable en PWM 12 bits – Quelles est-elle ?

6 Pins parmi ces 8 pins PWM Timer sont destinées dans mon projet à la consigne de vitesse:
Consigne de vitesse du variateur 1 : Pin 2 (TIOA0)
Consigne de vitesse du variateur 2 : Pin 3 (TIOA7)
Consigne de vitesse du variateur 3 : Pin 4 (TIOB6)
Consigne de vitesse du variateur 4 : Pin 5 (TIOA6)
Consigne de vitesse du variateur 5 : Pin 10 (TIOB7)
Consigne de vitesse du variateur 6 : Pin 11 (TIOA8)
En réserve pour extension : Pin 12 (TIOB8) et Pin 13 (TIOB0)

Ces 6 pins seront interfacées pour adapter la tension et la puissance de sortie avec 2 amplificateur OP TLV274IN ou LM324N

A propos des 8 PWM x (pin 2 à 5 et pin de 10 à 13) , quelle est la fréquence à laquelle on peut les faire fonctionner de façon durable si l'on souhaite utilisé un PWM en 12 bits.

PWM TIOx Maxi : 10Khz en 12 bits ? J’obtiens actuellement 10KHz en 12 bits par la modification de variant.h
1) Peux-t-on aller plus haut en fréquence sans échauffement du CPU ?

2) Comment se passe de la modification de variant.h ? (10khz ou plus !)

3) Comment faire varier le duty des PWM TIOx en passant par les registres?
Comme pour les PWMLx et PWMHx ?
But : Diminuer le temps de réponse en évitant de passer par digitalWrite(pin, value) ?

Merci pour toutes vos informations précieuse que vous allez m'apporter !

Nota:
J'avais modifié variant.h pour avoir du 40kHz en 12 bits , cela a bien fonctionné que quelques heures avant la détérioration du CPU de la Due

Hello everyone !

About the 8 TIOx PWMs (pins 2 to 5 and pins 10 to 13), how to vary the duty of PWM TIOx through the registers?

Bonjour à tous !

A propos des 8 TIOx PWM (broches 2 à 5 et broches 10 à 13), comment faire varier le duty de PWM TIOx en utilisant les registres?

Hi there Tfou57,

Have a look to library pwm_lib: GitHub - antodom/pwm_lib: This is a C++ library to abstract the use of the eight hardware PWM channels available on Arduino DUE's Atmel ATSAM3X8E microcontroller.

It is a specific library for DUE's ATSAM3X8E pwm channels. I think it is exactly what you need.

Have a look to the examples coming with pwm_lib. I hope they are clear enough.
If in doubt, just ask me.

:grin: Thank you for your reply,
Is your "antodom / pwm_lib" not only suitable for PWMHx and PWMLx pins?
I thought to reserve these pines for a use described in the post By the registers how to prohibit to have 2 digital pins at the same time in ON ? - Arduino Due - Arduino Forum
At the level of this post, I block because I do not get stable signals! ...

I go back on the subject that I am looking for in the frequency increase of PWM TIO without modifying variant.h

This is the increase in PWM frequencies remaining available, the 8 PWM TIOx (pins 2 to 5 and pins 10 to 13).
How to use your library on these PWM TIOx pins?

:grin: Merci de votre réponse,
Votre "antodom/pwm_lib" n'est-elle pas uniquement adaptée aux pins PWMHx et PWMLx ?
Je pensais réserver ces pins à un usage décrit dans le post By the registers how to prohibit to have 2 digital pins at the same time in ON ? - Arduino Due - Arduino Forum
Au niveau de ce post , je bloque car je n'obtient pas de signaux stables !...

Je reviens sur le sujet que je recherche au niveau de l'augmentation de fréquence des PWM TIO sans modifier variant.h

Il s'agit de l'augmentation de fréquences des PWM restant disponible, les 8 PWM TIOx (pins 2 à 5 et pins 10 à 13).
Comment utiliser votre bibliothèque sur ces pin PWM TIOx ?

Hi there @Tfou57,

With pwm_lib you can change the PWM frequency and duty dynamically without modifying variant.h. I would advice you to familiarize with the library starting with the examples, specially, basic_test.ino and changing_period_test.ino.

Have a look to the library documentation, basically, file README.md. Better see it on github directly at pwm_lib/README.md at master · antodom/pwm_lib · GitHub. There, you can see which specific pins you can use for PWM output using the eight channels available in the ATSAM3X8E and using pwm_lib.

I hope it helps.

antodom:
There, you can see which specific pins you can use for PWM output using the eight channels available in the ATSAM3X8E and using pwm_lib.

Hello,
Except for the misunderstanding, the 8 channels (PWMLx and PWMHx) which I thought I would reserve for interlocking the digital pins that I plan to use to control the direction of rotation of the frequency inverters Tri.

For the frequency inverter setpoint, I have only the PWM TIOA0, PWM TIOA7, PWM TIOA6, PWM TIOB6, PWM TIOB7, PWM TIOA8, PWM TIOB8 and PWM TIOB0.

For the speed setpoint, as I need a 12 bit resolution of the PWM, I think the Maxi frequency not to exceed is 10 Khz.

Bonjour,
Sauf erreur de compréhension il s'agit des 8 canaux (PWMLx et PWMHx) que je pensais réserver pour faire l'interverrouillage des pins digitales que je prévois d'utiliser pour commander le sens de rotation des variateurs de fréquence Tri.
Pour la consigne de fréquence des variateurs de fréquence , il ne me reste donc que les PWM TIOA0 ,PWM TIOA7,PWM TIOA6, PWM TIOB6, PWM TIOB7,PWM TIOA8,PWM TIOB8 et PWM TIOB0.
Pour la consigne de vitesse , comme il me faut une résolution de 12 bits des PWM, je pense que la fréquence Maxi à ne pas dépasser est 10 Khz.

Hi again @Tfou57,

I am afraid to say that I do not have a specific example that does exactly what you want, sorry for that.

As I commented previously, read the documentation and have a look to the examples, specifically basic_test.ino. Try it with just one PWM output and check that you can get to the frequencies you want. Have a look to changing_period_test.ino to see how to use pwm_lib to changing dynamically the frequency of your PWM signal. When you get to that point, try to solve your problem using all PWM outputs available.

As to PWMHx and PWMLx pins, those are the only pins you can use with the ATSAM3X8E PWM channels. Have a look to Table 38-2 in ATSAM3X8E datasheet. TIOAx and TIOBx pins are specific for the TC (Timer Counter) module and its nine TC channels. The PWM channels are independent from those ones. You can also generate PWM signals with the TC channels, but this is another history. pwm_lib is only referred to ATSAM3X8E PWM channels.

PWM channels in ATSAM3X8E have a resolution of 16 bits, and, from my own experience, you can get easily to 10 kHz using them with the library.

I hope it helps.

̶@ Antodom
̶In my application, I need to compare the current position of the actuators with respect to their calculated target position.
To compare these two data, I was thinking of using the highest common native resolution between the ADC and the PWM outputs

Since the native resolution of the ADC is limited to 12 bits, I did not see the advantage of using PWMLx and PWMHx in 16 bits.
Table 43.1 of data sheet ATSAM3X8E. (Analog-to-Digital Converter (ADC))

:grin: If the ADC reading had been in 16 bits, the use of the 16-bit PWM would have been of interest.
Creating a 16-bit ADC by putting bits 13 to 16 at zero, does not interest me because I would gain nothing in position reader actuator.

I chose the TIOX PWMs because their 12 bits are sufficient for the frequency setpoint accuracy of my three-phase frequency inverters.
This allowed me to keep my PWMLx and PWMHx in order to be able to switch the direction of rotation of the three-phase frequency inverters with a "Dead Time" when I could see a stable signal! ... ( >:( unstable duty observed oscilloscope).

@ Antodom
Dans mon application, j’ai besoin de comparer la position actuelle des actionneurs par rapport à leur position cible calculée.
Pour comparer ces deux données , je pensais utiliser la résolution native la plus élevée commune entre l’ADC et les sorties PWM

Comme la résolution native de l’ADC est limitée à 12bits, je ne voyais pas l’intérêt d’utiliser des PWMLx et PWMHx en 16 bits.
Tableau 43.1 de la fiche technique ATSAM3X8E. (Analog-to-Digital Converter (ADC))

:grin: Si la lecture ADC avait été en 16 bits, l’utilisation des PWM 16 bits auraient eu un intérêt.
Créer un ADC 16 bits en mettant les bits 13 à 16 à zéro, ne m’intéresse pas car je ne gagnerais rien en lecture de position actionneur.

J’avais choisis les PWM TIOX car leurs 12 bits suffissent à la précision de consigne de fréquence de mes variateurs de fréquences triphasés.
Cela me permettait de garder mes PWMLx et PWMHx afin de pouvoir réaliser une commutation des sens de rotation des variateurs de fréquence triphasé avec un « Dead Time » quand j’arriverais à voir un signal stable ! … ( >:( duty instable observé l’oscilloscope).

Hi again @Tfou57,

Sorry, but I do not really understand what you want to do, my fault :expressionless:

I do not understand why you are using the ADC module to get the feedback of an actuator, except if the position information is in analogical terms. Neither I do understand what a control PWM output signal has to do with an actuator position, except if you are in open loop, which I imagine it is not case, as you want to read the actuator position for feedback control.

Usually, to get an actuator position, or better an actuator velocity, you use a encoder which gives you ticks per turn, if your actuator is a motor. You should read this with the capture facilities available on ATSAM3X8E's TC channels.

If your actuator is a servo and you have an angular sensor which provides an analogical signal for the servo position, I can see why you need the ADC input, but I keep not seeing the relation with the PWM output you are talking about.

Definitely I am missing something.

Best.

antodom:
Hi again @Tfou57,

I am afraid to say that I do not have a specific example that does exactly what you want, sorry for that.

As I commented previously, read the documentation and have a look to the examples, specifically basic_test.ino. Try it with just one PWM output and check that you can get to the frequencies you want. Have a look to changing_period_test.ino to see how to use pwm_lib to changing dynamically the frequency of your PWM signal. When you get to that point, try to solve your problem using all PWM outputs available.

As to PWMHx and PWMLx pins, those are the only pins you can use with the ATSAM3X8E PWM channels. Have a look to Table 38-2 in ATSAM3X8E datasheet. TIOAx and TIOBx pins are specific for the TC (Timer Counter) module and its nine TC channels. The PWM channels are independent from those ones. You can also generate PWM signals with the TC channels, but this is another history. pwm_lib is only referred to ATSAM3X8E PWM channels.

PWM channels in ATSAM3X8E have a resolution of 16 bits, and, from my own experience, you can get easily to 10 kHz using them with the library.

I hope it helps.

I try to answer your questions as simply as possible
My final project, is a dynamic simulator for games.
My actuators are three-phase 220V geared motors. At the end of the reduction shaft a connecting rod / crank system tilts the dynamic simulator
The operating range of the connecting rod at the shaft output of the gearmotor is only 120 °
The Feedback control is achieved simply by a potentiometer with a mechanical stroke of 360 ° and an electric stroke of 340 °
The connection between the feedback potentiometer and the shaft output of the gear unit is reduced in order to use a maximum of the electrical stroke of the potentiometer.

The telemetry of the games is extracted by software like SimTools, X-Sim ....
These software sends serial link to the Arduino of the position instructions resulting from the telemetry of the games.
The position setpoint for each of the actuators is calculated by the Arduino according to the position instructions received by the USB link (SimTools software)
For each movement, the Arduino compares the current position with respect to the target position and manages the PID by controlling the three-phase frequency inverters

Using a test ADC code for A0 and A1, I get by the registers values ​​in 12 bits

Increasing the resolution of PWMs beyond 12 bits does not really make sense in terms of accuracy if I can not get 16-bit ADC

Is there a way to have a real ADC in 16 bits?

If the ADC goes into 16 bits, using 16-bit PWMs with increasing frequency using your library makes sense

void setup() 
{
  ADC->ADC_MR |= 0x80; // Mode FREERUN
  ADC->ADC_CR=2; // Demarrage convertisseur
  ADC->ADC_CHER=0xC0; // Activation voies 6 et 7 (A0 et A1)
  Serial.begin(115200);
}

void loop() 
{
Serial.print("A0--> ");
  Serial.print(ADC->ADC_CDR[7]);    //AD7
 Serial.print("     A1--> "); 
  Serial.println(ADC->ADC_CDR[6]);  //AD6
}

Je essayer de répondre à vos questions le plus simplement possible
Mon projet final, est un simulateur dynamique pour les jeux.
Mes actionneurs sont des motoréducteurs 220v triphasé. En bout d’arbre réducteur un système bielle / manivelle incline le simulateur dynamique
La plage de travail de la bielle en sortie d’arbre du motoréducteur est uniquement de 120°
Le contrôle Feedback est réalisé simplement par un potentiomètre avec une course mécanique de 360° et une course électrique de 340°
La liaison entre le potentiomètre de feedback et la sortie d’arbre du réducteur est démultipliée afin d’utiliser un maximum de la course électrique du potentiomètre.

La télémétrie des jeux est extraite par des logiciels comme SimTools , X-Sim … .
Ces logiciels envoient par liaison série à l’Arduino des consignes de positions issue de la télémétrie des jeux.
La consigne de position de chacun des actionneurs est calculée par l’Arduino en fonction des consignes de position reçu par la liaison USB (logiciel SimTools )
Pour chaque mouvement, l’Arduino compare la position actuelle par rapport à la position cible et gère le PID en commandant les variateurs de fréquence triphasés

A l’aide d’un code ADC d’essai pour A0 et A1, j’obtiens par les registres des valeurs en 12 bits

Augmenter la résolution des PWM au-delà de 12 bits n’a pas vraiment de sens en termes de précision si je n’arrive à avoir une lecture ADC en 16 bits

Y-a-t-il moyen d’avoir une vraie ADC en 16 bits ?

Si l’ADC passe en 16 bits, l’utilisation des PWM en 16 bits avec une augmentation de fréquence à l’aide de votre bibliothèque prend un sens.

Hi again @Tfou57,

According to ATSAM3X8E's data sheet the ADC inputs use 12 bits, so that's what you can reach with the hardware. You should map those 12 bits values to the range you are using for your target signal, and assume you will lose those 4 bits of accuracy.

Best.

Thank you,
But in my application, the resolution of the ADC takes precedence over the resolution of the PWM because 12 bits is sufficient for the setpoint of my drives.

Merci,
Mais dans mon application , la résolution de l'ADC est prioritaire par rapport à la résolution du PWM car 12 bits suffit pour la valeur de consigne de mes variateurs.

You won't get 16-bit PWM resolution at a frequency of 10kHz, as the resolution is determined by the value in your PWM Channel Period Register (PWM_CPRDx):

Resolution = log(PWM_CPRDx + 1) / log(2)

So if we're running the PWM channel timer at 84MHz, to get a 10kHz signal with single slope PWM:

PWM_CPRDx = 84MHz / 10kHz - 1 = 8399

Plugging this into the resolution formula we get:

Resolution = log(8399 + 1) / log(2) = 13.036 = 13 bit resolution

Maximum frequency usable in PWM 12 bits - What is it?

Rearranging the resolution formula to find the PWM_CPRDx register value for 12-bits:

PWM_CPRDx = 10^(12 * log(2)) - 1 = 4095

This then gives a PWM frequency of:

Maximum PWM frequency (at 12-bit resolution) = 84MHz / (4095 + 1) = 20508Hz = 20.508kHz

Thanks @MartinL for these details :grin:
I understand better why my previous Arduino Due card did not hold in 12 bits 40khz in continuous use for a few hours.
I will therefore modify variant.h to have 20.508kHz in 12 bits

How to vary the duty of PWM TIOx through the registers?
Purpose: Avoid using digitalWrite (pin, value) to decrease the response time of PWM TIOx during a duty change

:grinning: Thank you for your help … !

Merci @MartinL pour ces précisions :grin:
Je comprends mieux pourquoi ma précédente carte Arduino Due n’a pas tenu en 12 bits 40khz en usage continu pendant quelques heures.
Je vais donc modifier variant.h pour avoir 20.508kHz en 12 bits

Comment faire varier le duty des PWM TIOx en passant par les registres?
But : Eviter d’utiliser digitalWrite(pin, value) pour diminuer le temps de réponse des PWM TIOx lors d’un changement de duty

:grinning: Merci de votre aide … !

Hi Tfou57,

There's no need to modify your "variant.h" file.

How to vary the duty of PWM TIOx through the registers?

The example I provided was for the Due's 8 channel PWM Controller.

The TC timer counter channels however are only capable of being clocked at 42MHz, so at 12-bit resolution your maximum fequency will be halved to 10.254kHz.

Personally, I prefer using the Due's PWM controller over its TC timers, as it offers advanced PWM features. The TC timers on the other hand have only the most basic PWM functionality.

Anyway, here's the code that generates 10.254kHz PWM signals, at 12-bit resolution on pins D2, D3, D4, D5, D10, D11, D12 and D13 using the TC timers:

// Output 50% duty cycle PWM at 10.254Hz (12-bit resolution) on digital pins D2, D3, D4, D5, D10, D11, D12 and D13, 
// using TC0, TC6, TC7 and TC8
void setup() 
{
  REG_PMC_PCER0 |= PMC_PCER0_PID27;                 // Enable peripheral TC0 (TC0 Channel 0)
  REG_PIOB_ABSR |= PIO_ABSR_P27 | PIO_ABSR_P25;     // Switch the multiplexer to peripheral B for TIOA0 and TIOB0
  REG_PIOB_PDR |= PIO_PDR_P27 | PIO_PDR_P25;        // Disable the GPIO on the corresponding pins
  
  REG_PMC_PCER1 |= PMC_PCER1_PID33;                 // Enable peripheral TC6 (TC2 Channel 0)
  REG_PIOC_ABSR |= PIO_ABSR_P26 | PIO_ABSR_P25;     // Switch the multiplexer to peripheral B for TIOA6 and TIOB6
  REG_PIOC_PDR |= PIO_PDR_P26 | PIO_PDR_P25;        // Disable the GPIO on the corresponding pins

  REG_PMC_PCER1 |= PMC_PCER1_PID34;                 // Enable peripheral TC7 (TC2 Channel 1)
  REG_PIOC_ABSR |= PIO_ABSR_P29 | PIO_ABSR_P28;     // Switch the multiplexer to peripheral B for TIOA7 and TIOB7
  REG_PIOC_PDR |= PIO_PDR_P29 | PIO_PDR_P28;        // Disable the GPIO on the corresponding pins

  REG_PMC_PCER1 |= PMC_PCER1_PID35;                 // Enable peripheral TC8 (TC2 Channel 2)
  REG_PIOD_ABSR |= PIO_ABSR_P8 | PIO_ABSR_P7;       // Switch the multiplexer to peripheral B for TIOA8 and TIOB8
  REG_PIOD_PDR |= PIO_PDR_P8 | PIO_PDR_P7;          // Disable the GPIO on the corresponding pins

  REG_TC0_CMR0 = TC_CMR_BCPC_SET |                  // Set TIOB on counter match with RC0
                 TC_CMR_ACPC_SET |                  // Set TIOA on counter match with RC0
                 TC_CMR_BCPB_CLEAR |                // Clear TIOB on counter match with RB0
                 TC_CMR_ACPA_CLEAR |                // Clear TIOA on counter match with RA0
                 TC_CMR_WAVE |                      // Enable wave mode
                 TC_CMR_WAVSEL_UP_RC |              // Count up with automatic trigger on RC compare
                 TC_CMR_EEVT_XC0 |                  // Set event selection to XC0 to make TIOB an output
                 TC_CMR_TCCLKS_TIMER_CLOCK1;        // Set the timer clock to TCLK1 (MCK/2 = 84MHz/2 = 42MHz)
  
  REG_TC2_CMR0 = TC_CMR_BCPC_SET |                  // Set TIOB on counter match with RC0
                 TC_CMR_ACPC_SET |                  // Set TIOA on counter match with RC0
                 TC_CMR_BCPB_CLEAR |                // Clear TIOB on counter match with RB0
                 TC_CMR_ACPA_CLEAR |                // Clear TIOA on counter match with RA0
                 TC_CMR_WAVE |                      // Enable wave mode
                 TC_CMR_WAVSEL_UP_RC |              // Count up with automatic trigger on RC compare
                 TC_CMR_EEVT_XC0 |                  // Set event selection to XC0 to make TIOB an output
                 TC_CMR_TCCLKS_TIMER_CLOCK1;        // Set the timer clock to TCLK1 (MCK/2 = 84MHz/2 = 42MHz)

  REG_TC2_CMR1 = TC_CMR_BCPC_SET |                  // Set TIOB on counter match with RC0
                 TC_CMR_ACPC_SET |                  // Set TIOA on counter match with RC0
                 TC_CMR_BCPB_CLEAR |                // Clear TIOB on counter match with RB0
                 TC_CMR_ACPA_CLEAR |                // Clear TIOA on counter match with RA0
                 TC_CMR_WAVE |                      // Enable wave mode
                 TC_CMR_WAVSEL_UP_RC |              // Count up with automatic trigger on RC compare
                 TC_CMR_EEVT_XC0 |                  // Set event selection to XC0 to make TIOB an output
                 TC_CMR_TCCLKS_TIMER_CLOCK1;        // Set the timer clock to TCLK1 (MCK/2 = 84MHz/2 = 42MHz)

  REG_TC2_CMR2 = TC_CMR_BCPC_SET |                  // Set TIOB on counter match with RC0
                 TC_CMR_ACPC_SET |                  // Set TIOA on counter match with RC0
                 TC_CMR_BCPB_CLEAR |                // Clear TIOB on counter match with RB0
                 TC_CMR_ACPA_CLEAR |                // Clear TIOA on counter match with RA0
                 TC_CMR_WAVE |                      // Enable wave mode
                 TC_CMR_WAVSEL_UP_RC |              // Count up with automatic trigger on RC compare
                 TC_CMR_EEVT_XC0 |                  // Set event selection to XC0 to make TIOB an output
                 TC_CMR_TCCLKS_TIMER_CLOCK1;        // Set the timer clock to TCLK1 (MCK/2 = 84MHz/2 = 42MHz)
  
  REG_TC0_RA0 = 2047;                               // Load the RA0 register
  REG_TC0_RB0 = 2047;                               // Load the RB0 register
  REG_TC0_RC0 = 4095;                               // Load the RC0 register
  
  REG_TC2_RA0 = 2047;                               // Load the RA0 register
  REG_TC2_RB0 = 2047;                               // Load the RB0 register
  REG_TC2_RC0 = 4095;                               // Load the RC0 register

  REG_TC2_RA1 = 2047;                               // Load the RA1 register
  REG_TC2_RB1 = 2047;                               // Load the RB1 register
  REG_TC2_RC1 = 4095;                               // Load the RC1 register

  REG_TC2_RA2 = 2047;                               // Load the RA2 register
  REG_TC2_RB2 = 2047;                               // Load the RB2 register
  REG_TC2_RC2 = 4095;                               // Load the RC2 register

  NVIC_SetPriority(TC0_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC0 to 0 (highest) 
  NVIC_EnableIRQ(TC0_IRQn);         // Connect TC0 to Nested Vector Interrupt Controller (NVIC)
 
  NVIC_SetPriority(TC6_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC6 to 0 (highest) 
  NVIC_EnableIRQ(TC6_IRQn);         // Connect TC6 to Nested Vector Interrupt Controller (NVIC)
  
  NVIC_SetPriority(TC7_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC7 to 0 (highest) 
  NVIC_EnableIRQ(TC7_IRQn);         // Connect TC7 to Nested Vector Interrupt Controller (NVIC)
 
  NVIC_SetPriority(TC8_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC8 to 0 (highest) 
  NVIC_EnableIRQ(TC8_IRQn);         // Connect TC8 to Nested Vector Interrupt Controller (NVIC)

  REG_TC0_IER0 |= TC_IER_CPCS;      // Enable interrupts for TC0 (TC0 Channel 0)
  REG_TC2_IER0 |= TC_IER_CPCS;      // Enable interrupts for TC6 (TC2 Channel 0)
  REG_TC2_IER1 |= TC_IER_CPCS;      // Enable interrupts for TC7 (TC2 Channel 1)
  REG_TC2_IER2 |= TC_IER_CPCS;      // Enable interrupts for TC8 (TC2 Channel 2)
  
  REG_TC0_CCR0 = TC_CCR_SWTRG | TC_CCR_CLKEN;       // Enable the timer TC0
  REG_TC2_CCR0 = TC_CCR_SWTRG | TC_CCR_CLKEN;       // Enable the timer TC6
  REG_TC2_CCR1 = TC_CCR_SWTRG | TC_CCR_CLKEN;       // Enable the timer TC7
  REG_TC2_CCR2 = TC_CCR_SWTRG | TC_CCR_CLKEN;       // Enable the timer TC8
}

void loop() {}

void TC0_Handler()                // ISR TC0 (TC0 Channel 0)
{
  if (REG_TC0_SR0 & TC_SR_CPCS)   // Check for RC compare condition
  {
    // Update the duty cycle here...
    REG_TC0_RA0 = 1024;
    REG_TC0_RB0 = 1024; 
  }
}

void TC6_Handler()                // ISR TC6 (TC2 Channel 0)
{
  if (REG_TC2_SR0 & TC_SR_CPCS)   // Check for RC compare condition
  {
    // Update the duty cycle here...
    REG_TC2_RA0 = 1024;
    REG_TC2_RB0 = 1024;
  }
}

void TC7_Handler()                // ISR TC7 (TC2 Channel 1)
{
  if (REG_TC2_SR1 & TC_SR_CPCS)   // Check for RC compare condition
  {
    // Update the duty cycle here...
    REG_TC2_RA1 = 1024;
    REG_TC2_RB1 = 1024;
  }
}

void TC8_Handler()                // ISR TC8 (TC2 Channel 2)
{
  if (REG_TC2_SR2 & TC_SR_CPCS)   // Check for RC compare condition
  {   
    // Update the duty cycle here...
    REG_TC2_RA2 = 1024;
    REG_TC2_RB2 = 1024;
  }
}

I've included the ISRs to enable a new duty cycle to be loaded into the RA and RB registers at the beginning of the timer cycle. If you change the RA and RB during the cycle you might get glitches on your PWM outputs.