Go Down

Topic: Changing Arduino Zero PWM Frequency (Read 70995 times) previous topic - next topic

CoCoZero

Hello everyone,

First I'd like to thank you all for every information you shared on this forum (special thanks to MartinL for all the well explained information he already has provided).

I am facing some issues with my code. My goal is to control 4 motors using PWM, in order to keep the basic functions of ARDUINO I understood that i needed to let the TCC0 as it is. That is why I decided to use only TCC1 and TCC2.
With information I picked up on this forum I developped this code below
   
Code: [Select]

// pins 2|5 control motor 1 with pwm on PIN 3
// pins 7|8 control motor 2 with pwm on PIN 4 
// pins 6|9 control motor 3 with pwm on PIN 11     
// pins 10|12 control motor 4 with pwm on PIN 13

REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                    GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                     GCLK_GENCTRL_GENEN |         // Enable GCLK4
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                     GCLK_GENCTRL_ID(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Enable the port multiplexer for the 4 PWM channels: timer TCC1 and TCC2 outputs
  PORT->Group[g_APinDescription[3].ulPort].PINCFG[g_APinDescription[3].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[4].ulPort].PINCFG[g_APinDescription[4].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[11].ulPort].PINCFG[g_APinDescription[11].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[13].ulPort].PINCFG[g_APinDescription[13].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC timers to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[4].ulPort].PMUX[g_APinDescription[4].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F; // To connect TCC1 to PIN 3 and PIN 4
  PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg = PORT_PMUX_PMUXO_E | PORT_PMUX_PMUXE_E; // To connect TCC2 to pin 11 and pin 13

  // Feed GCLK4 to TCC0 and TCC1
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC0 and TCC1
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TCC0_TCC1;   // Feed GCLK4 to TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Feed GCLK4 to TCC2 (and TC3)
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC2 (and TC3)
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TCC2_TC3;    // Feed GCLK4 to TCC2 (and TC3)
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization


  /*TCC_WAVE_POL(0xF) is for CC3 | CC2 | CC1 | CC0
      TCC1 and TCC2 only have CC0 and CC1 so we need to use
      TTC_WAVE_POL(0x3)
      By default in dual slope mode (without reversing the polarity), as you go from 0 ==> CCx and CCx ==> 0 the WO(x) timer output goes low, while anything from CCx ==> PER and PER ==> CCx the output is high. Note CCx = counter compare.
  */
  // Dual slope PWM operation: timers countinuously count up to PER register value then down 0
  REG_TCC1_WAVE |= TCC_WAVE_POL(0x3) |         // Reverse the output polarity on all TCC1 outputs
                   TCC_WAVE_WAVEGEN_DSBOTH;    // Setup dual slope PWM on TCC1
  while (TCC1->SYNCBUSY.bit.WAVE);               // Wait for synchronization

  // Dual slope PWM operation: timers countinuously count up to PER register value then down 0
  REG_TCC2_WAVE |= TCC_WAVE_POL(0x3) |         // Reverse the output polarity on all TCC2 outputs
                   TCC_WAVE_WAVEGEN_DSBOTH;    // Setup dual slope PWM on TCC2
  while (TCC2->SYNCBUSY.bit.WAVE);               // Wait for synchronization

  // Each timer counts up to a maximum or TOP value set by the PER register,
  // this determines the frequency of the PWM operation:
  REG_TCC1_PER = 1200;         // Set the frequency of the PWM on TCC1 to 25kHz
  while (TCC1->SYNCBUSY.bit.PER);                // Wait for synchronization
  REG_TCC2_PER = 1200;         // Set the frequency of the PWM on TCC1 to 25kHz
  while (TCC2->SYNCBUSY.bit.PER);                // Wait for synchronization


  // Set the PWM signal to output 50% duty cycle
  REG_TCC1_CCB0 = 600;         // TCC1 CC1 - on D3
  while (TCC1->SYNCBUSY.bit.CCB0);                // Wait for synchronization
  REG_TCC1_CCB1 = 600;         // TCC1 CC2 - on D4
  while (TCC1->SYNCBUSY.bit.CCB1);                // Wait for synchronization
  REG_TCC2_CCB0 = 600;         // TCC2 CC1 - on D11
  while (TCC2->SYNCBUSY.bit.CCB0);                // Wait for synchronization
  REG_TCC2_CCB1 = 600;         // TCC2 CC2 - on D13
  while (TCC2->SYNCBUSY.bit.CCB1);                // Wait for synchronization

  // Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the outputs
  REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC1 output
  while (TCC1->SYNCBUSY.bit.ENABLE);              // Wait for synchronization

  // Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the outputs
  REG_TCC2_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC2 output
  while (TCC2->SYNCBUSY.bit.ENABLE);              // Wait for synchronization


   // pins 2|5 control motor 1 with pwm on PIN 3
   // pins 7|8 control motor 2 with pwm on PIN 4 
   // pins 6|9 control motor 3 with pwm on PIN 11     
   // pins 10|12 control motor 4 with pwm on PIN 13

  I commande the motors using bloc of code like :

Code: [Select]

  digitalWrite(2, LOW);
  digitalWrite(7, HIGH);
  digitalWrite(5, HIGH);
  digitalWrite(8, LOW);
  digitalWrite(6, LOW);
  digitalWrite(10, HIGH);
  digitalWrite(9, HIGH);
  digitalWrite(12, LOW);


 My problem is that PIN 12 seems to be in HIGH state whether or not I use the commande digitalWrite(12, LOW);.
  First I have seen that my code never initialize the PIN mode to output so I tried to use the commande pinMode(12, OUTPUT);
  It worked for the PIN 12 but it seems to disable the PWM each time the programme uses the commande digitalWrite(12, LOW);
  My guess is that I have somehow connected the PIN 12 with the pin 11 so maybe the error comes from the line :
 
  PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg = PORT_PMUX_PMUXO_E | PORT_PMUX_PMUXE_E;
 
  I can't be sure of that because I don't realy understand how this line works...
  Does someone has an idea of how to disconnect PIN 11 and PIN 12 ?
 
  Thank you in advance!
  Corentin

MartinL

Hi CoCoZero,

The timing functions delay(), millis() and micros() use the systick timer on the SAMD21, so it's possible to use TCC0 in addition to TCC1 and TCC2.

Unlike the Uno and other AVR based Arduinos it's usually not necessary to set the pin to an output before using the pins for PWM. However, if you need to pull the outputs low as soon as possible during initialisation, then it's easiest just to set the GPIO ports directly:

Code: [Select]
REG_PORT_DIRSET0 =  PORT_PA08 | PORT_PA09 | PORT_PA16 | PORT_PA17;     // Configure the motor channels as outputs                  
REG_PORT_OUTCLR0 = PORT_PA08 | PORT_PA09 | PORT_PA16 | PORT_PA17;      // Set the motor channels low (0V)

MartinL

Hi Corentin,

Quote
My guess is that I have somehow connected the PIN 12 with the pin 11 so maybe the error comes from the line :
Not sure what the issue is, but digital pin 12 isn't connected in any way to pins 11 and 13.

Digital pins 11 and 13 are a pair (PA16 and PA17), as are pins 10 and 12 (PA18 and PA19).

CoCoZero

Hi MartinL,

Thank you for your quick answer and your explanation about the TCC0 timer, it could be useful later, but now that I've something almost working with TCC1 and TCC2 I will try to keep it that way.

Anyway, after your remark I check the pin 12 on my arduino and on my diferent shileds and I found out my last shield is responsible for the problem...

Once again thank you for your answer !

Miruta22

Hi, I've been looking for information on generating several PWMs with different sources and I don't find anything about that.

1.Could you generate 3 PWM, one of 15Khz, another of 10Khz and the last of 1Khz without any disturbances between them? I need the most precision possible using arduino.

2.Could it be done with different clock sources?

It is the first MKRzero arduino and I still do not know it.

Thanks for help.

Regards!

MartinL

Hi Miruta22,

If you require the highest PWM resolution possible on all three channels then it's necessary to 3 separate timers: TCC0, TCC1 and TCC2. The timers operate independently from one another.

The following code for the MKRZero outputs 15kHz on D2, 10kHz on D3 and 1kHz on the MOSI pin with a 50% duty-cycle:

Code: [Select]
// Output 15kHz, 10kHz and 1kHz PWM on timers TCC0, TCC1 and TCC2 respectively
void setup()
{
  GCLK->GENDIV.reg = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                     GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  GCLK->GENCTRL.reg = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                      GCLK_GENCTRL_GENEN |         // Enable GCLK4
                      GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                      GCLK_GENCTRL_ID(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  // Enable the port multiplexer for the 3 PWM channels: timer TCC0 outputs
  const uint8_t pwmPins[] = { 2, 3, MOSI };
  for (uint8_t i = 0; i < 3; i++)
  {
     PORT->Group[g_APinDescription[pwmPins[i]].ulPort].PINCFG[g_APinDescription[pwmPins[i]].ulPin].bit.PMUXEN = 1;
  }
  // Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[2].ulPort].PMUX[g_APinDescription[2].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_E;
  PORT->Group[g_APinDescription[MOSI].ulPort].PMUX[g_APinDescription[MOSI].ulPin >> 1].reg |= PORT_PMUX_PMUXE_E;

  // Feed GCLK4 to TCC0 and TCC1
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC0 and TCC1
                      GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                      GCLK_CLKCTRL_ID_TCC0_TCC1;   // Feed GCLK4 to TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  // Feed GCLK4 to TCC2 and TC3
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC2 and TC3
                      GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                      GCLK_CLKCTRL_ID_TCC2_TC3;    // Feed GCLK4 to TCC2 and TC3
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  TCC0->PER.reg = 3199;        // Set the frequency of the PWM on TCC0 to 15kHz
  while (TCC0->SYNCBUSY.bit.PER);
  TCC1->PER.reg = 4799;        // Set the frequency of the PWM on TCC1 to 10kHz
  while (TCC1->SYNCBUSY.bit.PER);
  TCC2->PER.reg = 47999;       // Set the frequency of the PWM on TCC2 to 1kHz
  while (TCC2->SYNCBUSY.bit.PER);
 
  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
  TCC0->CC[3].reg = 1599;       // TCC0 CC3 - 50% duty cycle on D3
  while (TCC0->SYNCBUSY.bit.CC3);
  TCC1->CC[0].reg = 2399;       // TCC1 CC0 - 50% duty cycle on D2
  while (TCC1->SYNCBUSY.bit.CC0);
  TCC2->CC[0].reg = 23999;      // TCC2 CC0 - 50% duty cycle on MOSI
  while (TCC2->SYNCBUSY.bit.CC0);
 
  TCC0->CTRLA.bit.ENABLE = 1;                     // Enable the TCC0 counter
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
  TCC1->CTRLA.bit.ENABLE = 1;                     // Enable the TCC1 counter
  while (TCC1->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
  TCC2->CTRLA.bit.ENABLE = 1;                     // Enable the TCC2 counter
  while (TCC2->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { }

chuqdd

Hi, MartinL,

I need to generate 100KHz (50% duty cycle is fine for me) on D5 pin of Arduino MKR1300, on which D5 is PB11 of SAMD21G18A

I've tried fumbling after a few code examples in this forum, but with no success.

Wondering if you can provide me any guidance on this?

Thank you very much

chuqdd

Hi, MartinL,


I now get 100KHz on D5 (PB11?) of MKR1300 using the following modified code, but also get 25KHz on D6 (PA20?)

how to get rid of the unwanted 25KHz on D6(PA20?)?

Thank you very much




// Output 250kHz PWM on timer TCC0 (6-bit resolution)
void setup()
{
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                    GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                     GCLK_GENCTRL_GENEN |         // Enable GCLK4
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                     GCLK_GENCTRL_ID(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Enable the port multiplexer for the digital pin D7
//  PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[5].ulPort].PINCFG[g_APinDescription[5].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC0 timer to digital output D7 - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
//  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;
  PORT->Group[g_APinDescription[4].ulPort].PMUX[g_APinDescription[4].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;

  // Feed GCLK4 to TCC0 and TCC1
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC0 and TCC1
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TCC0_TCC1;   // Feed GCLK4 to TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Dual slope PWM operation: timers countinuously count up to PER register value then down 0
  REG_TCC0_WAVE |= TCC_WAVE_POL(0xF) |         // Reverse the output polarity on all TCC0 outputs
                    TCC_WAVE_WAVEGEN_DSBOTH;    // Setup dual slope PWM on TCC0
  while (TCC0->SYNCBUSY.bit.WAVE);               // Wait for synchronization

  // Each timer counts up to a maximum or TOP value set by the PER register,
  // this determines the frequency of the PWM operation:
  REG_TCC0_PER = 240;         // Set the frequency of the PWM on TCC0 to 250kHz
  while (TCC0->SYNCBUSY.bit.PER);                // Wait for synchronization
 
  // Set the PWM signal to output 50% duty cycle
//  REG_TCC0_CC3 = 48;         // TCC0 CC3 - on D7
//  while (TCC0->SYNCBUSY.bit.CC3);                // Wait for synchronization
  REG_TCC0_CC1 = 120;         // TCC0 CC3 - on D7
  while (TCC0->SYNCBUSY.bit.CC1);                // Wait for synchronization
 
  // Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { }

chuqdd

Hi, MartinL,


Please disregard my previous post, the 25KHz signal on D6 is due to my shield.

Thanks


evi7538

The pin mapping for the TCC0, TCC1 and TCC2 are as follows:

REG_TCC0_CCB0 - digital output D2 (Zero Pro/M0 Pro/M0 - digital pin D4)
REG_TCC0_CCB1 - digital output D5
REG_TCC0_CCB2 - digital output D6
REG_TCC0_CCB3 - digital output D7
REG_TCC1_CCB0 - digital output D4 (Zero Pro/M0 Pro/M0 - digital pin D2)
REG_TCC1_CCB1 - digital output D3
REG_TCC2_CCB0 - digital output D11
REG_TCC2_CCB1 - digital output D13
Hi Martin,

I'm using MKR1000 and the pin map seems to be different. I need to drive PWM on pin PA8 which seems to use register TCC0 CCB0 for duty cycle and correspond to D11 Arduino port for MKR1000, but I can't get any PWM signal out on PA8. (But I can toggle the PA8 with direct port manipulation)

I did these modifications to your code to direct PWM to PA11:

  // Enable the port multiplexer for the digital pin PA8
  PORT->Group[g_APinDescription[11].ulPort].PINCFG[g_APinDescription[11].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC0 timer to digital output D0 - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg = PORT_PMUX_PMUXE_F;

  // Set the PWM signal to output 50% duty cycle
  REG_TCC0_CCB0 = pulsewidth;         // TCC0 CCB0 - on PA8

Is there a way to set the PORT command with generic SAMD21 pin numbers instead of using g_APinDescription() and Arduino pin numbers? The Arduino pin/port mapping is constantly changing between board version and it's very confusing


Thanks!


MartinL

Hi evi7538,

To configure port pin PA08 without referencing Arduino pins:

First enable the port multiplexer:

Code: [Select]
PORT->Group[PORTA].PINCFG[8].bit.PMUXEN = 1;
...then switch the port multplexer to the correct peripheral. In this case the port is even (port eight) and the TCC0/W[0] is on switch E (TC/TCC), (I/O Multiplexing and Considerations table in the SAMD21 datasheet):

Code: [Select]
PORT->Group[PORTA].PMUX[8 >> 1].reg |= PORT_PMUX_PMUXE_E;
If you prefer the PORTA definition can be replaced by 0, and likewise PORTB with 1.

evi7538

#191
Jan 26, 2019, 06:36 pm Last Edit: Jan 26, 2019, 06:45 pm by evi7538
Martin, thank you for quick response. I also just realized that I need to use TCC2 for PA8 (is this correct?). Still no luck. Here is my code.

Do I need to enable the pin with pinMode(11, OUTPUT) for PWM?

Any help is much appreciated!!!


Code: [Select]


 pinMode(11, OUTPUT);

  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                    GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                     GCLK_GENCTRL_GENEN |         // Enable GCLK4
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                     GCLK_GENCTRL_ID(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

 PORT->Group[PORTA].PINCFG[8].bit.PMUXEN = 1;
 PORT->Group[PORTA].PMUX[8 >> 1].reg |= PORT_PMUX_PMUXE_E;

  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TCC2_TC3;   // Feed GCLK4 to TCC2 and TC3
   while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Dual slope PWM operation: timers countinuously count up to PER register value then down 0
  REG_TCC2_WAVE |= TCC_WAVE_POL(0xF) |         // Reverse the output polarity on all TCC0 outputs
                    TCC_WAVE_WAVEGEN_DSBOTH;    // Setup dual slope PWM on TCC0
  while (TCC2->SYNCBUSY.bit.WAVE);               // Wait for synchronization

   REG_TCC2_PER = period;         // Set the frequency of the PWM on TCC to 250kHz
  while (TCC2->SYNCBUSY.bit.PER);                // Wait for synchronization
  
  // Set the PWM signal to output 50% duty cycle
  REG_TCC2_CCB0 = pulsewidth;         // TCC2 CCB0 - on PA8
  while (TCC2->SYNCBUSY.bit.CC3);                // Wait for synchronization
  
  // Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the outputs
  REG_TCC2_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC output
  while (TCC2->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
  


MartinL

#192
Jan 26, 2019, 07:22 pm Last Edit: Jan 26, 2019, 07:59 pm by MartinL
Timer TCC1 on channel 0 (CC0/CCB0) is the other timer available on port PA08, it's on peripheral switch position F:

Code: [Select]
PORT->Group[PORTA].PMUX[8 >> 1].reg |= PORT_PMUX_PMUXE_F;
Just change TCC2 to TCC1 and set the counter compare and counter compare buffered registers to CC0 and CCB0 respectively.

You'll also need to connect the TCC1 timer to the generic clock, by changing the GCLK_CLKCTRL_ID_TCC2_TC3 register bitfield to GCLK_CLKCTRL_ID_TCC0_TCC1 instead:

Code: [Select]
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC
                   GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                   GCLK_CLKCTRL_ID_TCC0_TCC1;   // Feed GCLK4 to TCC0 and TCC1
while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

Unlike the AVR Arduino boards, on the SAMD21 it isn't necessary to use the pinMode() function to activate the pin as an output, so this line can be deleted.

evi7538

WOW, it actually works now, thank you so much, Martin!

manuelx10

Hi,

I am looking to migrate my project from Arduino MEGA2560 to MKR ZERO.
My sketch controls 3 motor driver ICs and does it via 3 separate pins per driver IC, and sets up the pins  to 20KHz via timers, and then directly modify the duty cycle in the program to adjust the speed.

Right now i have this for my MEGA2560:

Code: [Select]


  // Timer 4 (TCCR4) configuration controls pins 6, 7, and 8.
  // PWM frequency calculation: [ 16MHz / 1 (prescaler) / 2 (phase-correct) / 400 (top) = 20kHz ]
  int eightOnes = 255;          // Equivalent to 11111111 in binary
  TCCR3A &= ~eightOnes;         // Set the eight bits in register to 0
  TCCR3B &= ~eightOnes;         // Set the eight bits in register to 0
  ICR3 = (F_CPU/20000)/2;       // Top = 400 = (16Mhz/20Khz)/2
  TCCR3A = _BV(COM3A1)          // pin 6 - non-inverted PWM output   
         | _BV(COM3B1)          // pin 7 - non-inverted PWM output
         | _BV(COM3C1)          // pin 8 - non-inverted PWM output
         | _BV(WGM31);          // mode 10: phase correct PWM with ICR4 as Top
  TCCR3B = _BV(WGM33)
         | _BV(CS30);           // Prescaler = 1     



and control the duty cycle by updating OCR3A, OCR3B, OCR3C:

Code: [Select]


// speed = range between -400 to 400
OCR3A = speed
OCR3B = speed



Anyone could help with a solution or could point me the right direction? thanks!

Go Up