Combine two PWM outputs

Go To Last Post
13 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi guys,

is it possible to have two independent PWM outputs on an AVR, let's say MEGA2560, with different duty cycles that can be triggered in sequence?

To give you an example, both outputs have the same base frequency of 100Khz, first PWM output has a duty of 30% on and 70% off. The second output will be triggered after the end of the first pulse on it's on duty cycle.. let's say 50% on and 50% off.

My thought is this:
Let's say, at 100Khz.. it's a total period of 10uS for both outputs, 5uS goes to first output and 5uS to second output. The first output with a duty cycle of 30% must stay HIGH for 1.5uS and then 3.5uS LOW then second output goes HIGH again for 2.5uS (50% duty) and off for 2.5uS.

Is this approach correct? If so, the problem is that i can't make it work in software with a timer and interrupt because it's too slow for these timings.

Can you thought of any way to implement this sequence using hardware PWM?

thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lets step back and have a look at the big picture. What is it you want to do? Then we can look at the implementation.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's certainly possible.

Each PWM cycle can be programmed individually. By using the output compare and overflow interrupts for a particular timer, you can set the duty cycle for the next PWM cycle. Keep in mind that at high frequencies, your processor loading may be high. If you are using "C", it may even be higher due to pushing and popping lots of registers.

A processor like the M2560 has several PWM modes. The specific implementation will vary depending on which mode you want to use. Read the section on the 16 bit timer/counter.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

If you are using "C", it may even be higher due to pushing and popping lots of registers.

[Don't start on this chestnut again. If >>you<< have chosen to use a toolchain that is not known for producing skinny ISRs, then indeed there might be some save/restore overhead that affects the "fast ISR" goal. However, that should not be a blanket "C" statement; more rather aimed at the toolchain(s) in question.]

Getting back to the OP's needs: An Xmega with port-DMA would seem to be ideal, especially if one could dedicate a complete I/O port to the task. Xmega E-series has that "logic block" in it.

It is unclear from the description above what this second input is supposed to do. Fire a one-shot pulse at the falling edge of the first pulse? Then what is this talk of 50% duty cycle? Does it keep firing or not? Can it extend into the next cycle of the primary? Once it is understood, then perhaps one can noodle with AVR8 possibilities.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello again,

it's just a task that was requested from me to do, don't know exactly where it will be used.

The request is to have two pwm outputs that each one will have independent duty cycle and the second output will follow the first output and not overlap it.

Take a look at the attached image, don't take care of the Variable time space between the two pulses, i will take care of this by re-calculating the given duty cycle.

I am just trying to figure out how to sync the two pwm outputs so the second pulse will run after the end period of the first one and never overlap.

Hope the attachments helps!

Thank you

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

?

The initial post has fixed parameters.

Then the image shows variable parameters, and an additional "variable" gap in-between the two pulse trains.

As noted above, I would stop thinking of the second signal as a PWM signal and think of it as a one-shot (mono-stable), triggered by the falling edge of the primary signal.

If you need the "variable" time interval, then think of cascading two monostables. Primary signal's falling edge triggers monostable one. Monostable one's falling edge triggers the second monostable, (the second signal).

Also, as noted, the Xmega E series has the internal, routable, combinational logic, and the Event System, both of which might help in this project; as well as its 32 MHz clock.

JC

Last Edited: Sun. Jul 28, 2013 - 04:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So the envelope has 4 cases: 1KHz rep rate at 1% and 99% duty, and 300KHz rep rate at 1% to 99% duty cycle. Specifying the duty in percent means I can use top as 100? This lets the OCR value be in percent. A 20MHz AVR can generate a 100 count pwm at a 200KHz rep rate. So I guess a 32MHz xmega would be able to get to 300KHz. Ask the boss how tight the specs are,

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The diagram helps to make things clearer.

Anyway, what it appears to be is [close to] "complementary outputs with dead-time generation". Perhaps see if the datasheet of AT90PWMn family will fit the bill, or other AVR8 models with dead-time generation.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Besides the AT90PWMn, it would appear that ATMEGAnnM1 family has dead-time. Xmegas have DTI/Dead-Time Insertion.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is requested to use MEGA2560 so i have to go with this.. i was thinking if there is just a way to "time shift" a second pwm channel using the same frequency.

Is this possible somehow?

Also how can i trigger the second output using the first in PWM mode as DocJS mention?

I have done all this using software approach and a 1uS timer interrupt counting... but with all the overhead i can't go higher that 10-20Khz, that's why i am looking a way to use hardware PWM.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is one of my interpretations of your first post, so it's not exactly what you want. It shows how you can use two timers at the same frequency with a fixed time shift. Maybe it can be changed to something you can use. And maybe one of the Phase and Frequency correct modes can be useful for this. The code is for atmega1284p.

#include 

int main(void)
{
    // Stop all synchronous timers
    GTCCR = (1<<TSM) | (1<<PSRSYNC);

    // Timer1, mode 14 with ICR1 as TOP, OCA non inverting
    DDRD |= 1<<5;
    ICR1 = 79;     // 100kHz with F_CPU 8MHz
    // 30% duty for the first half is 15% duty cycle
    OCR1A = 79 * 3/10 * 1/2;
    TCCR1A = (1<<COM1A1) | (1<<WGM11);
    TCCR1B = (1<<WGM13)|(1<<WGM12) | (1<<CS10);
    // Start at half time
    TCNT1 = 79/2 + 1;
    
    // Timer3, mode 14, OCA non inverting
    DDRB |= 1<<6;
    ICR3 = 79;
    // 50% duty cycle in the second half period
    OCR3A = 79 * 1/2 * 1/2; 
    TCCR3A = (1<<COM3A1) | (1<<WGM31);
    TCCR3B = (1<<WGM33)|(1<<WGM32) | (1<<CS30);
    TCNT3 = 0;

    // Release timers
    GTCCR = 0;

    while(1)
    {
    }
}

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok. I throw in a Phase and Frequency Correct version too.

    // Stop all synchronous timers
    GTCCR = (1<<TSM) | (1<<PSRSYNC);

    // Timer1, mode 8 PnFC with ICR1 as TOP, OCA non inv.
    DDRD |= 1<<5;
    ICR1 = 40;     // 100kHz with F_CPU 8MHz
    // 30% duty cycle
    OCR1A = 40 * 3/10;
    TCCR1A = (1<<COM1A1);
    TCCR1B = (1<<WGM13) | (1<<CS10);
    // Start at half time
    TCNT1 = 40;
    
    // Timer3, mode 8 PnFC, OCA non inverting
    DDRB |= 1<<6;
    ICR3 = 40;
    // 50% duty cycle
    OCR3A = 40 * 1/2; 
    TCCR3A = (1<<COM3A1);
    TCCR3B = (1<<WGM33) | (1<<CS30);
    TCNT3 = 0;

    // Release timers
    GTCCR = 0;

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

snigelen wrote:
Ok. I throw in a Phase and Frequency Correct version too.
    // Stop all synchronous timers
    GTCCR = (1<<TSM) | (1<<PSRSYNC);

    // Timer1, mode 8 PnFC with ICR1 as TOP, OCA non inv.
    DDRD |= 1<<5;
    ICR1 = 40;     // 100kHz with F_CPU 8MHz
    // 30% duty cycle
    OCR1A = 40 * 3/10;
    TCCR1A = (1<<COM1A1);
    TCCR1B = (1<<WGM13) | (1<<CS10);
    // Start at half time
    TCNT1 = 40;
    
    // Timer3, mode 8 PnFC, OCA non inverting
    DDRB |= 1<<6;
    ICR3 = 40;
    // 50% duty cycle
    OCR3A = 40 * 1/2; 
    TCCR3A = (1<<COM3A1);
    TCCR3B = (1<<WGM33) | (1<<CS30);
    TCNT3 = 0;

    // Release timers
    GTCCR = 0;

That looks very promising! Thank you very much snigelen, i will try this myself and let you know!

Thank you very much for your kind help!