Generating 1 MHZ PWM on Due

I need to be able to create a >1 MHZ Signal using the Arduino due. I have very little experience with Arduinos, but I read the article at

Using the following code:

void setup() {
pinMode(3, OUTPUT);
TCCR2A = 0x23;
TCCR2B = 0x09;
OCR2A = 3;
OCR2B = 1;
}

void loop () {

}

You have several ways to generate a PWM pulse:

  • For a fixed 50% duty cycle, you can use PMC_PCKx up to 480 MHz
  • For different duty cycles, you can use either Timer Counters (section 36 of Sam3x datasheet), or PWMHx/Lx (section 38 of Sam3x datasheet).

See below a snippet to generate a 1 MHz PWM with a 50% duty cycle:

/******************************************************************************/
/***               1MHz/ 50% Duty cycle PWM thru TIOA0                      ***/
/******************************************************************************/

void setup() {

/*************  Timer Counter 0 Channel 0 to generate PWM pulses thru TIOA0  ************/
  PMC->PMC_PCER0 |= PMC_PCER0_PID27;                      // Timer Counter 0 channel 0 IS TC0, TCO power ON
  PMC->PMC_PCER0 |= PMC_PCER0_PID12;                      // PIOB power ON, page 38
  
  PIOB->PIO_PDR |= PIO_PDR_P25;
  PIOB->PIO_ABSR |= PIO_ABSR_P25;                        // PB25 is driven by the TC, peripheral type B, page 858

  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 = 42;  //<*********************  Frequency = (Mck/2)/TC_RC  = 1 MHz
  TC0->TC_CHANNEL[0].TC_RA = 21;  //<********************   Duty cycle = (TC_RA/TC_RC) * 100 = 50 %

  TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC0 counter and enable

}

void loop() {
 
}

ard_newbie:
You have several ways to generate a PWM pulse:

  • For a fixed 50% duty cycle, you can use PMC_PCKx up to 480 MHz
  • For different duty cycles, you can use either Timer Counters (section 36 of Sam3x datasheet), or PWMHx/Lx (section 38 of Sam3x datasheet).

See below a snippet to generate a 1 MHz PWM with a 50% duty cycle:

/******************************************************************************/

/***              1MHz/ 50% Duty cycle PWM thru TIOA0                      /
/
***************************************************************************/

void setup() {

/*************  Timer Counter 0 Channel 0 to generate PWM pulses thru TIOA0  ************/
  PMC->PMC_PCER0 |= PMC_PCER0_PID27;                      // Timer Counter 0 channel 0 IS TC0, TCO power ON
  PMC->PMC_PCER0 |= PMC_PCER0_PID12;                      // PIOB power ON, page 38
 
  PIOB->PIO_PDR |= PIO_PDR_P25;
  PIOB->PIO_ABSR |= PIO_ABSR_P25;                        // PB25 is driven by the TC, peripheral type B, page 858

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 = 42;  //<*********************  Frequency = (Mck/2)/TC_RC  = 1 MHz
  TC0->TC_CHANNEL[0].TC_RA = 21;  //<********************  Duty cycle = (TC_RA/TC_RC) * 100 = 50 %

TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC0 counter and enable

}

void loop() {

}

I have so many questions. I have a tiny bit of experience with atmega avr, however none when it comes to the Atsam ARM. (Using a DUE)

You said up to 480 MHz frequency could be generated by PMC_PCKx, did you mean 48MHz? And how come you didn't use this method for the example of 1Mhz PWM?

Would setting the counters this way disable the functions delay() and millis()? In Atmega88p these functions are controlled by TC0 and so changing anything with TC0 would disable them, however, changing the other timers has no influence on these functions. Is it the same with my DUE as well?

In fact, PMC_PCKx allows you to output one of the internal clocks divided by a prescaler, and re-reading the datasheet, the maximum frequency output can be 480 MHz/2 = 240 MHz. Note that 480 MHz is the frequency needed by USB 2.0 High speed (see page 552, Sam3x datasheet).

To output a 1 MHz frequency, it should be more convenient to use a Timer Counter or a PWM channel. In either cases, search in the DUE sub forum for example sketches, or use TC-lib or PWM-lib from antodom.

Hi there,

The Arduino DUE's ATSAM3X8E micro-controller has its own PWM channels, in order to generate PWM signals without using any of the timers available in the micro-controller. Thus, there is no any conflicts with millis() or delay().

For generating a 1 MHz PWM signal, you can use pwm_lib (available at 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.). This is a library which allows you to genereate PWM signals using those channels, concretely the eight PMW channels available in the ATSAM3X8E. Have a look to the documentation and the examples which come with the library. This is an open source library, so you are also free to have a look to the code, if what you prefer is doing things at low level.

I hope it helps.

ard_newbie:
In fact, PMC_PCKx allows you to output one of the internal clocks divided by a prescaler, and re-reading the datasheet, the maximum frequency output can be 480 MHz/2 = 240 MHz. Note that 480 MHz is the frequency needed by USB 2.0 High speed (see page 552, Sam3x datasheet).

To output a 1 MHz frequency, it should be more convenient to use a Timer Counter or a PWM channel. In either cases, search in the DUE sub forum for example sketches, or use TC-lib or PWM-lib from antodom.

Hey thanks for your reply. I am aware of prescalers, but isn't the main clock (and the other internal clocks) of the DUE clocked at 84Mhz max?

antodom:
Hi there,

The Arduino DUE's ATSAM3X8E micro-controller has its own PWM channels, in order to generate PWM signals without using any of the timers available in the micro-controller. Thus, there is no any conflicts with millis() or delay().

For generating a 1 MHz PWM signal, you can use pwm_lib (available at 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.). This is a library which allows you to genereate PWM signals using those channels, concretely the eight PMW channels available in the ATSAM3X8E. Have a look to the documentation and the examples which come with the library. This is an open source library, so you are also free to have a look to the code, if what you prefer is doing things at low level.

I hope it helps.

Thanks Ill have a look!

Hi there
Sorry if I missed something obvious but while reading pwm_lib I could not figure out how to specify a phase shift when using several pwm pins
Is it possible or do I need to code something specifics?

Thanks a lot
JF

Whenever you need several PWM outputs with the same frequency but phase shifted, the PWM synchro feature is a good option.

Search in the DUE sub forum for example sketches with PWM synchro.

Thanks a lot!
I found your example and I think I can start from here.

I guess I will have to make some change if I want a the best possible resolution at >1 Mhz but at last I know how to make the synchro now.

Hi there @jf_in_vt,

pmw_lib do not abstract the possibility of synchronizing several PWM signals. This capability is present in ATSAM3X8E's PWM modules, but until now, I have not seen the necessity to add this functionality to the library. I will have a look to it.

Best.

thanks
that will be neat
In the meantime I will try to do something on my own.
but I am lazy (are we not all..?) and i hate to reinvent the wheel :slight_smile:

ps I am also considering using C2000 microcontrollers instead
. they are a definite overkill for my application and I have never used them so I will have to face a steep learning curve.. which I am reluctant to do if I can avoid it.

JF