Arduino Forum

Using Arduino => Project Guidance => Topic started by: sj_777 on Jun 10, 2017, 11:22 am

Title: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: sj_777 on Jun 10, 2017, 11:22 am
Objective:

Generate PWM signals with:

a. Variable frequency
b. Variable duty cycle
c. Variable phase (90,180,270) - 2 signals: one normal and one shifted.

Board: Arduino Mega 2560

Currently i'm able to generate a. & b. at real time.
 
Need to incorporate a,b & c at real time.


Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: Grumpy_Mike on Jun 10, 2017, 01:35 pm
Quote
Currently i'm able to generate a. & b. at real time.
Great, post the code for that. Post it between code tags, you get them by pressing the </> icon.

Then we can suggest how to get what you want.
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: johnwasser on Jun 10, 2017, 03:51 pm
To get the phase differences you will probably need to use separate synchronized timers, initialized to different starting values.  For synchronization, use Timers 0,1, 3, 4, or 5 which share a prescaler.  For stopping the timers and resetting the prescaler while you initialize the separate timers, see: GTCCR - General Timer/Counter Control Register 
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: DrDiettrich on Jun 10, 2017, 10:16 pm
You can use two channels of the same timer, which determine the start of a pulse as required for the phase shift, and implement the different pulse widths in code.

Or you use two separate timers, which you start with different counter values for the phase shift, and let both create the PWM signals as usual. A reset of the prescaler is not normally required then, it's sufficient that both timers use the same prescaler rate/output, and TOP values, for the same signal frequency.
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: sj_777 on Jun 14, 2017, 07:44 pm
Great, post the code for that.
I used Arduino PWM Frequency Library (v5). Thanks to the developer who wrote the library.
(Download link: https://code.google.com/archive/p/arduino-pwm-frequency-library/downloads (https://code.google.com/archive/p/arduino-pwm-frequency-library/downloads))

Code: [Select]

#include <PWM.h>

int outputPin = 11;

//For testing
int freqInput = 100;          //10-10000 Hz (I wanted that range only)
int dutyCycleInput = 70;      //1-65535

void setup()
{
  InitTimersSafe();
}

void loop()
{
//Get inputs from either serial or through pot
//So we have Frequency and Duty cycle in variables 'freqInput' & 'dutyCycleInput' respectively in int
SetPinFrequencySafe(outputPin, freqInput );
pwmWriteHR(outputPin, dutyCycleInput );
}


Now i need to add phase shift as well.

Register level programming is also welcomed, if 'plug and play' is not available for all 3 needs.

Thank you johnwasser and DrDiettrich.
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: Grumpy_Mike on Jun 14, 2017, 09:02 pm
Thanks for posting the code and posting it correctly. However unfortunately I don't think you can get there from here.

First off I am not sure that you have full control of the duty cycle at all frequencies using this method. What you loose when you have full frequency control is that full duty cycle control is restricted to a few "spot" frequencies.

Quote
For synchronization, use Timers 0,1, 3, 4, or 5 which share a prescaler.
Note that the Arduino Uno only has three timers 0,1 & 2.

What Arduino are you using?

It might be possible to write a software driven PWM signal using the same principle as the servo libiary, where an interrupt routine is fired by a timer and the software counts interrupts and sets the outputs accordingly. This is processor intensive but look at the Shift PWM libiary for how that can work. I think you can only realistically get duty cycle control in the range 0 - 255.

What do you actually want to do, this looks like it could be a X-Y Problem (http://xyproblem.info/)
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: sj_777 on Jun 15, 2017, 09:14 pm
I tested it using DSO to find as much accuracy in frequency & duty cycle as required and that too in wide range of frequencies rather than "spots". (Please except minute latency)

I'm using Arduino Mega 2560 (as already specified), and the timers posted by johnwasser are correct for that board.

Leave the duty cycle resolution. Mega supports 16 bit timer. Even 0-256 is fine.

As clearly mentioned in objective, the purpose is the generation of variable frequency, duty cycle & delayed PWM signals.

Inputs:
1. Frequency
2. Duty Cycle
3. Phase Shuft (0,180,270 deg)

I used serial input through Arduino Android App which serve the dual purpose of power supply and input console that asks for these 3 parameters using serial monitor. :)

Process:
Generation of PWM signals using timers/counters available in Arduino Mega with the input spec given by user.

Ouput:
PWM signals with required spec as given by user.


Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: MasterT on Jun 16, 2017, 04:54 pm
I modified a PWM library, link to download below:

Example sketch supports phase shift between Timer 1 and Timer 3.


Code: [Select]
/*

 Test sketch for arduino PWM library - modified version.
 
 CLI - command line interface:

 d - debug, shift phase on regular time base interval, demo mode.
 f - set frequency,  "f100" - 100 Hz.
 l - set duty cycle, "l2000".
 p - set phase,      "p90".
 
 Direct Registers Manipulation:
 
 1. "a80" - set address
 2. "w10111" - write data, leading 0 could be ommited.
 3. "r" - read current registers.

 NOTE: Use direct registers programming VERY Carefully !!!
  
 created 16 June. 2017
 Anatoly Kuzmenko
 anatolyk69@gmail.com
 
 This example code is in the public domain.

 Works for Timer 1 & 3 so far:
 Pin   11   12       timer1
 Pin   2    3    5   timer3

 Timer-1 is primery clock generator, Timer-3 secondary, phase shifted clock.
 */

#include <PWM.h>

         String       in_String         =     "";        
         boolean      end_input         =  false;  
         uint8_t      adres_reg         =      0;        
         uint8_t      debug_osm         =      0;

         uint8_t      prime_pin         =     11;        
         uint8_t      secon_pin         =      2;        

         uint16_t     level_pwm         =  32767; // 1 -/- 65535
         uint16_t     phase_pwm         =     90; // degree 1 -/- 360
         uint32_t     freqn_pwm         =     35; // frequency in Hz

unsigned long         previousMillis    =      0;
const    long         interval          =    500;

void setup()
{
  Serial.begin(115200);
  in_String.reserve(200);

  InitTimersSafe();
}

void loop()
{
  int32_t  tempr = 0;
  char *   pEnd;

  if(debug_osm) {
    uint32_t currentMillis = millis();
    if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    phaseShift();
    }
  }
  
  serialEvent();

  if( end_input) {
    char cmd = in_String[0];
    in_String[0] = '+';
    
    if( cmd == 'd' ) {
      debug_osm = 1 - debug_osm;
      if(debug_osm) Serial.print(F("\nDebug aktiv."));
      else          Serial.print(F("\nDebug de-aktiv."));
      }
    
    if( cmd == 'f' ) {
      tempr = strtol( in_String.c_str(), &pEnd, 10);
      Serial.print(F("\n\tFreq: "));
      Serial.print(tempr, DEC);
      freqn_pwm = tempr;

      bool success1 = SetPinFrequencySafe(prime_pin, freqn_pwm);
      bool success2 = SetPinFrequencySafe(secon_pin, freqn_pwm);

      GTCCR |= ((1<<TSM)|(1<<PSRASY)|(1<<PSRSYNC));
      GTCCR &= ~(1<<TSM);

      if(success1 && success2) {
        pwmWriteHR(prime_pin, level_pwm);
        pwmWriteHR(secon_pin, level_pwm);
    
        pinMode(13, OUTPUT);
        digitalWrite(13, HIGH);  
        }
      }
    
    if( cmd == 'l' ) {
      tempr = strtol( in_String.c_str(), &pEnd, 10);
      Serial.print(F("\n\tLevel: "));
      Serial.print(tempr, DEC);
      level_pwm = tempr;
      pwmWriteHR(prime_pin, level_pwm);
      pwmWriteHR(secon_pin, level_pwm);
      }
      
    if( cmd == 'p' ) {
      tempr = strtol( in_String.c_str(), &pEnd, 10);
      Serial.print(F("\n\tPhase: "));
      Serial.print(tempr, DEC);
      phase_pwm = tempr;

      //GTCCR |= ((1<<TSM)|(1<<PSRASY)|(1<<PSRSYNC));
      GTCCR |= (1<<TSM);

      tempr  =  ICR3 * (phase_pwm /360.0) -1;
      if(tempr < 0) tempr = 0;
      TCNT1 = 0;
      TCNT3 = tempr;
 
      GTCCR &= ~(1<<TSM);    
      }
    
    if( cmd == 'a' ) {
      adres_reg = strtol( in_String.c_str(), &pEnd, 16);
      Serial.print(F("\n\tReg: "));
      Serial.print(adres_reg, HEX);
      Serial.print(F("\tvalue: "));
      tempr =   (*(uint8_t*)adres_reg);
      Serial.print(tempr, BIN);
      }
    if( cmd == 'r' ) {
      Serial.print(F("\n\tReg: "));
      Serial.print(adres_reg, HEX);
      Serial.print(F("\tvalue: "));
      tempr =   (*(uint8_t*)adres_reg);
      Serial.print(tempr, BIN);
      }
    if( cmd == 'w' ) {
      Serial.print(F("\n\tReg: "));
      Serial.print(adres_reg, HEX);
      Serial.print(F("\tvalue: "));
      tempr =   (*(uint8_t*)adres_reg);
      Serial.print(tempr, BIN);
      tempr = strtol( in_String.c_str(), &pEnd, 2);      
      (*(uint8_t*)adres_reg) = tempr;
      Serial.print(F("\tnew  value: "));
      tempr =   (*(uint8_t*)adres_reg);
      Serial.print( tempr, BIN);      
      }
      
          
    in_String = "";
    end_input= false;
  }
}

void serialEvent() {
  while (Serial.available()) {
    char inChar = (char)Serial.read();
    in_String += inChar;
    if (inChar == '\n') {
      end_input= true;
    }
  }
}

void phaseShift() {
static uint16_t phase_loc = 0;
       uint16_t tempr     = 0;
      
  //GTCCR |= ((1<<TSM)|(1<<PSRASY)|(1<<PSRSYNC));
  GTCCR |= (1<<TSM);
  //   GTCCR |= _BV (TSM);

      tempr =  ICR3 * (phase_loc /360.0) -1;
      if(tempr < 0) tempr = 0;

   TCNT1 = 0;
   TCNT3 = tempr;

  GTCCR &= ~(1<<TSM);
  //   GTCCR &= ~(_BV (TSM));
  
  phase_loc += 30;
  if(phase_loc > 360) phase_loc = 0;
}



Have fun.
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: sj_777 on Jun 20, 2017, 06:18 pm
I adapted it to my requirement and it works perfectly fine. Thank you very much MasterT.
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: jenovauh on Sep 06, 2017, 08:09 am
I modified a PWM library, you linked above, download:

https://drive.google.com/file/d/0Bw4tXXvyWtFVYWU5RElyeE9BdGc/view?usp=sharing (https://drive.google.com/file/d/0Bw4tXXvyWtFVYWU5RElyeE9BdGc/view?usp=sharing)

Example sketch supports phase shift between Timer 1 and Timer 3.

https://drive.google.com/file/d/0Bw4tXXvyWtFVZTlaYmQxMkFZeTQ/view?usp=sharing (https://drive.google.com/file/d/0Bw4tXXvyWtFVZTlaYmQxMkFZeTQ/view?usp=sharing)

The download links are no longer available?
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: MasterT on Sep 06, 2017, 02:50 pm
The download links are no longer available?
I uploaded modified pwm library on this forum, message #7, though it wouldn't rely on 3-rd party web-hosting.
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: kang2k on Oct 26, 2017, 02:24 pm
I uploaded modified pwm library on this forum, message #7, though it wouldn't rely on 3-rd party web-hosting.
Hello
This code available for Arduino nano ?
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: Grumpy_Mike on Oct 26, 2017, 02:39 pm
Quote
This code available for Arduino nano ?
The Nano and the Uno have the same chip so code suitable for one is suitable for both. The only exception is that their are two extra analogue inputs on the Nano.
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: kang2k on Oct 26, 2017, 04:29 pm
The Nano and the Uno have the same chip so code suitable for one is suitable for both. The only exception is that their are two extra analogue inputs on the Nano.
If selecet Nano - compiling error:

Generator:123: error: 'ICR3' was not declared in this scope

Generator:126: error: 'TCNT3' was not declared in this scope
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: MasterT on Oct 26, 2017, 04:37 pm
Arduino with AtMega328 doesn't have Timer3.  Example code was written for arduino Mega,  what OP  asked in message #0.
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: kang2k on Oct 26, 2017, 04:42 pm
Arduino with AtMega328 doesn't have Timer3.  Example code was written for arduino Mega,  what OP  asked in message #1.
Please help me.
What change in code for Atmega328 ?
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: MasterT on Oct 26, 2017, 04:54 pm
 AtMega328 has only 3 timers, though it would not be possible to generate 4 phase (0, 90, 180, 270) signal at all.
You may get 2 phases, implementing Timer-2 instead of Timer-3, but it's tricky since the hardware is completely different. 
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: Grumpy_Mike on Oct 26, 2017, 11:58 pm
Arduino with AtMega328 doesn't have Timer3.  Example code was written for arduino Mega,  what OP  asked in message #0.
Yes but kang2k is not the op, he hijacked the thread.
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: haroon46 on Nov 08, 2017, 07:43 pm
Is it possible to vary frequency and duty cycle for two pins in Mega 2560 using PWM library?
Here's my code. I am getting variable pulse in Pin 11, but Pin 5 is not showing any pulse signal. Any suggestion?

Code: [Select]
#include <PWM.h>

int outputPin = 11;
int outputPin1 = 5;
//For testing
int freqInput;          //10-10000 Hz (I wanted that range only)
int freqInput1 = 5500;
int dutyCycleInput = 32768;      //1-65535

void setup()
{
  InitTimersSafe();
  bool success = SetPinFrequencySafe(outputPin, freqInput);
  bool success2 = SetPinFrequencySafe(outputPin1, freqInput1); //added
    if(success) {
    pinMode(13, OUTPUT);
    digitalWrite(13, HIGH);   
  }
}

void loop()
{
  int sensorValue = analogRead(A0);
  Serial.println(sensorValue);
  freqInput = map(sensorValue,0,1023,500,5500);
  SetPinFrequencySafe(outputPin, freqInput );
  pwmWriteHR(outputPin, dutyCycleInput );
  SetPinFrequencySafe(outputPin1, freqInput1 );
  pwmWrite(outputPin1, 128);
}


Thanks in advance.
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: DrDiettrich on Nov 08, 2017, 09:25 pm
Check that pin 11 and 5 use a different timer, so that they can be set to individual frequencies.
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: haroon46 on Nov 09, 2017, 06:08 am
Is there any way to set to individual frequencies using this library?
Do you mean by specified timer initialising?
Code: [Select]
TIMER2.Setfrequency()
I have tried to control frequency in pin 12, which uses same timer in Mega 2560. But the frequency in both pins were not stable and fluctuates a lot.
Is there any way to control two pins with variable frequencies?
Thanks in advance.
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: DrDiettrich on Nov 09, 2017, 10:57 am
One timer must be used for every special frequency. Also the frequency should be set only when required, not with every iteration of loop().
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: Takiadin on Apr 30, 2018, 06:15 am
I adapted it to my requirement and it works perfectly fine. Thank you very much MasterT.
Could you please post the full code?
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: shp96 on Aug 20, 2018, 09:26 am
For an application I'm just interested in the frequency value and the duty cycle, not the phase. Could I use this code with Arduino UNO? I know this was written for a Mega, but I was wondering if some part could be used.

I used Arduino PWM Frequency Library (v5). Thanks to the developer who wrote the library.
(Download link: https://code.google.com/archive/p/arduino-pwm-frequency-library/downloads (https://code.google.com/archive/p/arduino-pwm-frequency-library/downloads))

Code: [Select]

#include <PWM.h>

int outputPin = 11;

//For testing
int freqInput = 100;          //10-10000 Hz (I wanted that range only)
int dutyCycleInput = 70;      //1-65535

void setup()
{
  InitTimersSafe();
}

void loop()
{
//Get inputs from either serial or through pot
//So we have Frequency and Duty cycle in variables 'freqInput' & 'dutyCycleInput' respectively in int
SetPinFrequencySafe(outputPin, freqInput );
pwmWriteHR(outputPin, dutyCycleInput );
}


Now i need to add phase shift as well.

Register level programming is also welcomed, if 'plug and play' is not available for all 3 needs.

Thank you johnwasser and DrDiettrich.
Title: Re: Generation of PWM signals with variable frequency & duty cycle & phase delays
Post by: johnwasser on Aug 20, 2018, 01:56 pm
For an application I'm just interested in the frequency value and the duty cycle, not the phase. Could I use this code with Arduino UNO? I know this was written for a Mega, but I was wondering if some part could be used.
It depends on the library.  What happens when you try to upload the sketch to your Arduino UNO?  If you don't get an error then it might just work.  Pin 11 is a PWM pin on the UNO so you don't even need to change it.