100kHz PWM

Hi, i want to make a 100kHz PWm with my Due. I think it is almost as that: Due PWM frequency - Arduino Due - Arduino Forum.

I only want to increase and than decrease the PWM.
At the moment i stuck at 10kHz with my code. Have some an idea what i can change to get the 100kHz?

#include <DueTimer.h>
int i = 0;
int j = 10;
boolean flag = false; 

void isr(){
i=i+2;
j=j-2;
if(i==10)
{ flag= !flag;
  i=0;
  j=10;
  }
}

void setup(){
pinMode(3,OUTPUT);
  Timer1.attachInterrupt(isr);
  Timer1.start(10);
}

void loop(){
  
programm();
}

void programm(){
  while(flag==false){
digitalWrite(3,HIGH);
delayMicroseconds(i);
digitalWrite(3,LOW);
delayMicroseconds(j);
  }
    while(flag==true){
digitalWrite(3,HIGH);
delayMicroseconds(j);
digitalWrite(3,LOW);
delayMicroseconds(i);
  }
}

Hi gamer1,

To get a 100kHz PWM signal using the examples given in the linked topic, just change the REG_PWM_CPRD0 register from 2100 to 840 and the REG_PWM_CDTY0 register from 1050 to 420.

REG_PWM_CPRD0 = 840;         // Set the PWM frequency 84MHz/100kHz = 840
REG_PWM_CDTY0 = 420;         // Set the PWM duty cycle 50% (840/2=420)

Hi there @gamer1,

You can easily generate a 100 kHz PWM signal using library pwm_lib. The library is available here:

Have a look to the exampes, and ask me if you have any question.

I hope it helps.

MartinL:
Hi gamer1,

To get a 100kHz PWM signal using the examples given in the linked topic, just change the REG_PWM_CPRD0 register from 2100 to 840 and the REG_PWM_CDTY0 register from 1050 to 420.

REG_PWM_CPRD0 = 840; // Set the PWM frequency 84MHz/100kHz = 840
REG_PWM_CDTY0 = 420; // Set the PWM duty cycle 50% (840/2=420)[/code]

Thanks MartinL, your change is good the problem i have is that i don't have a know i have to increase and than decrease the PWM
, I have used delayMicroseconds() but it isn't a nice Signal and to Slow with 80 uS between the first and the next increase.

// Output a 1000kHz PWM waveform at a resolution of 11-bits on pin DAC1 (PWML0)
void setup() {
  // PWM Set-up on pin: DAC1
  REG_PMC_PCER1 |= PMC_PCER1_PID36;                     // Enable PWM
  REG_PIOB_ABSR |= PIO_ABSR_P16;                        // Set PWM pin perhipheral type A or B, in this case B
  REG_PIOB_PDR |= PIO_PDR_P16;                          // Set PWM pin to an output
  REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);      // Set the PWM clock rate to 84MHz (84MHz/1)
  REG_PWM_CMR0 = PWM_CMR_CPRE_CLKA;                     // Enable single slope PWM and set the clock source as CLKA
REG_PWM_CPRD0 = 840;         // Set the PWM frequency 84MHz/100kHz = 840
REG_PWM_CDTY0 = 0;         // Set the PWM duty cycle 50% (840/2=420)
  REG_PWM_ENA = PWM_ENA_CHID0;                          // Enable the PWM channel     
}

void loop() {
  
delayMicroseconds(10);
  REG_PWM_CDTY0 = 100;
delayMicroseconds(10);
  REG_PWM_CDTY0 = 300;
delayMicroseconds(10);
  REG_PWM_CDTY0 = 500;
delayMicroseconds(10);
  REG_PWM_CDTY0 = 800;
 delayMicroseconds(10);
  REG_PWM_CDTY0 = 500;
delayMicroseconds(10);
  REG_PWM_CDTY0 = 300;
  delayMicroseconds(10);
  REG_PWM_CDTY0 = 100;
  delayMicroseconds(10); 
 REG_PWM_CDTY0 = 0;
   }

Hi gamer1,

If you're changing the duty cycle, use the buffered duty cycle register, this will update the PWM only on the next timer cycle and avoids glitches appearing on your waveform.

REG_PWM_CDTYUPD0 = 420;         // Set the PWM duty cycle 50% (840/2=420)

okay the Signalform is now really good but still to slow with 80 uS between the first and the next increase.

#include <DueTimer.h>
boolean a = true;
int i = 0;

// Output a 1000kHz PWM waveform at a resolution of 11-bits on pin DAC1 (PWML0)
void setup() {
  // PWM Set-up on pin: DAC1
  REG_PMC_PCER1 |= PMC_PCER1_PID36;                     // Enable PWM
  REG_PIOB_ABSR |= PIO_ABSR_P16;                        // Set PWM pin perhipheral type A or B, in this case B
  REG_PIOB_PDR |= PIO_PDR_P16;                          // Set PWM pin to an output
  REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);      // Set the PWM clock rate to 84MHz (84MHz/1)
  REG_PWM_CMR0 = PWM_CMR_CPRE_CLKA;                     // Enable single slope PWM and set the clock source as CLKA
REG_PWM_CPRD0 = 800;         // Set the PWM frequency 84MHz/100kHz = 840
REG_PWM_CDTY0 = 0;         // Set the PWM duty cycle 50% (840/2=420)
  REG_PWM_ENA = PWM_ENA_CHID0;                          // Enable the PWM channel     
Timer1.attachInterrupt(isr);
  Timer1.start(10);
}

void loop() {

      
   }

   
   void isr()
   {
    
    if (a==true)
    { i=i+200;
REG_PWM_CDTYUPD0 = i;
    if(i==800)
    {
      a=false;
      }
    }
    if (a==false)
    { i=i-200;
    REG_PWM_CDTYUPD0 = i;
    if(i==0)
    {a=true;}
    }
    }

Hi gamer1,

If you're aiming to change the duty cycle every PWM timer period? Then you could try enabling either the PWM compare match or compare update interrupts, (update interrupt is commented out in the code below).

The compare match interrupt occurs whenever the counter matches the duty cycle register's value, while the compare update interrupt occurs whenever the counter matches the period register (at the end of the cycle). The interrupts call the PWM controller's PWM_Handler() interrupt service routine (ISR).

// Output a 100kHz PWM waveform at a resolution of 11-bits on pin DAC1 (PWML0)
void setup() {
  // PWM Set-up on pin: DAC1
  REG_PMC_PCER1 |= PMC_PCER1_PID36;                     // Enable PWM
  REG_PIOB_ABSR |= PIO_ABSR_P16;                        // Set PWM pin perhipheral type A or B, in this case B
  REG_PIOB_PDR |= PIO_PDR_P16;                          // Set PWM pin to an output
  REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);      // Set the PWM clock rate to 84MHz (84MHz/1)
  REG_PWM_CMR0 = PWM_CMR_CPRE_CLKA;                     // Enable single slope PWM and set the clock source as CLKA
  REG_PWM_CPRD0 = 840;                                  // Set the PWM frequency 84MHz/100kHz = 840
  REG_PWM_CDTY0 = 0;                                    // Set the PWM duty cycle 0%
  REG_PWM_IER2 = PWM_IER2_CMPM0;                        // Enable interrupt for comparison match channel 0
  //REG_PWM_IER2 = PWM_IER2_CMPU0;                        // Enable interrupt for comparison update channel 0
  REG_PWM_ENA = PWM_ENA_CHID0;                          // Enable the PWM channel     
}

loop (){}

void PWM_Handler()                                      // ISR Handler for the PWM controller
{
  if (REG_PWM_ISR2 & PWM_ISR2_CMPM0)                    // Check if the comparison match channel 0 interrupt has been triggered
  {
     // Add your code here...
  }
  /*if (REG_PWM_ISR2 & PWM_ISR2_CMPU0)                    // Check if the comparison update channel 0 interrupt has been triggered
  {
     // Add your code here...
  }*/
}

If you're aiming to change the duty cycle every PWM timer period?

Yes, thats what i'm trying to do. But with your new code i'm out.

So my first Questens how do come in void PWM_Handler()?
Doesn't we need a decleration/conjunction or something in the setup.

So the next Questions is are you trying to the Step that are written on Page 988 of the Sam?
http://www.atmel.com/Images/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf

Hi gamer1,

The PWM_Handler() is the interrupt service routine for the PWM controller. This function is called everytime an enabled PWM controller interrupt is triggered. The PWM controller's PWM Interrupt Enable Register 2 (REG_PWM_IER2) on page 1019 of the datasheet can control which interrupts are enabled.

If the Comparison Match Channel 0 bit (CMPM0) bit is set, the PWM_Handler() function is called on every rising edge of the PWM pulse. Conversely, when the Comparison Update Chanel 0 bit (CMPU0) is set the PWM_Handler() function is called on the falling edge at the end of the cycle.

On entering the PWM_Handler() function we need to test for which interrupt has been triggered. We do this by bitwise ANDing our interrupt of interest with the PWM Interrupt Status Register:

if (REG_PWM_ISR2 & PWM_ISR2_CMPM0)                    // Check if the comparison match channel 0 interrupt has been triggered
{
   // Add your code here...
}

Reading the REG_PWM_ISR2 register automatically clears the interrupt flags.

As you're using the buffered duty cycle update register (REG_PWM_CDTYUPD0), that buffers the duty cycle and only updates the PWM output on the next timer cycle; it's therefore possible to trigger the PWM_Handler() function to load duty cycle update register with a new value on the current timer cycle, ready to be output on the next. This way you avoid having to synchronize your PWM controller with the TC timer (or timer library).

The definition for the PWM_Handler() function can be found in the "sam3x8e.h" file located (on my Windows machine at least) at:

C:\Users\Computer\AppData\Local\Arduino15\packages\arduino\tools\CMSIS\4.0.0-atmel\Device\ATMEL\sam3xa\include\sam3x8e.h

Also in this directory you'll find the "instance" and "component" directories. In these there's a "pwm.h" file that contains the PWM Controller's definitions for its registers and their bitfields. This is also where the register definitions for all the other Due's peripheral registers are held.

The step you mention on page 988 of the SAM3X datasheet is to do with synchronizing the PWM channels to each other and won't really affect you unless you're planning on using more than one channel.