8

I'm trying to use hardware PWM on ATmega2560, using TC0 (an 8-bit timer), in Fast PWM mode. I need to dynamically adjust the duty cycle, and this includes zero duty cycle. However, this does not appear to be easy or even possible. Quoting the datasheet:

The extreme values for the OCR0A Register represents special cases when generating a PWM waveform output in the fast PWM mode. If the OCR0A is set equal to BOTTOM, the output will be a narrow spike for each MAX+1 timer clock cycle. Setting the OCR0A equal to MAX will result in a constantly high or low output (depending on the polarity of the output set by the COM0A1:0 bits).

So, setting OCR0A to 0 (=BOTTOM) will not actually result in zero duty cycle, and my tests confirm this. Some other approach needs to be taken.

First, I've taught about using the OCR0A=MAX special case as described in the quote above. Combined with temporarily switching to inverted mode, this will result in zero duty cycle. However, since the COM0A1:0 bits are not double buffered (and not synchronized with OCR0A), this may result in a glitch in the output, if the mode is switched while the output is high (it will be left high until the next overflow). It does not appear to matter what order the OCR0A change and the mode change is done, both may glitch.

I've also considered another solution, to turn off the PWM by setting COM0A1:0=0. This will immediately set the output to the value in the PORT register, which would be zero. But there's still the problem of going back from zero output to a non-zero duty cycle. From what I read in the datasheet, setting COM0A1:0 to re-enable the PWM will immediately switch the output pin to the output of the PWM, which may be an incorrect value until the next compare match or timer overflow. Hence, a glitch.

Inverting the PWM overall may be applicable, but then the problem just becomes achieving full duty cycle, with symmetric issues.

Note that it is not possible to leave the PWM waveform generation enabled while forcing the output of the pin via PORT, as explained in the datasheet:

The Compare Output mode (COM0x1:0) bits have two functions. The Waveform Generator uses the COM0x1:0 bits for defining the Output Compare (OC0x) state at the next Compare Match. Also, the COM0x1:0 bits control the OC0x pin output source.

There is no way to let the PWM run for a cycle or so and switch to it when it's ready - enabling PWM immediately forces the pin output.

UPDATE. The phase-correct (center-aligned) PWM mode does not have this issue, and in my case is acceptable. I have tried it and confirmed that it works for both zero and full duty cycle.

Ambroz Bizjak
  • 7,399
  • 1
  • 34
  • 44
  • I'm not aware of this device, so this is a generic suggestion, but is there a hardware difference in disabling the PWM by setting COM0A1:0=0 and disabling the timer interrupt that triggers the change in output? If so, I'm wondering whether you can leave the PWM enabled but disable the interrupt when output is low, allowing you to change the duty-cycle which will not be apparent until the timer interrupt is re-enabled? – Ed King May 25 '14 at 08:32
  • @EdKing Timer interrupts are not supposed to be involved, that's the whole idea of hardware PWM. The hardware itself updates the pin output, the software only changes the duty cycle. See my update in the question ad the end. – Ambroz Bizjak May 25 '14 at 08:40
  • Also, this probably applies to most ATmega chips, not just the 2560. – Ambroz Bizjak May 25 '14 at 08:41
  • 1
    Did you try phase-correct PWM? It doesn't have the spike problem. It is described a little further along in the datasheet. – uncleO May 25 '14 at 17:46
  • @UncleO Yeah, the phase-correct mode works, thankfully. – Ambroz Bizjak May 25 '14 at 21:46

4 Answers4

5

I just came through the same issue, but needed to stick with a Fast PWM setting on a atmega2560.

The solution I found was to temporarily put the output pin to normal port operation (i.e. set the COMnX1 and COMnX1 bits of the TCCRnA register to zero when a zero duty cycle is needed :

TCCR1A &= (0<<COM1A1) & (0<<COM1A1);

vince
  • 51
  • 1
  • 3
4

you have two options:

  1. if you use the fast pwm: you can use an inverted pwm. all you need to do is set the OC Pins to inverse mode and invert your pwm compare values. however - you now have glitches with 100% duty cycle

  2. use the phase correct pwm. draw back is that the maximum frequency is halved.

vlad_tepesch
  • 6,330
  • 1
  • 29
  • 67
0

In fast pwm mode, the duty cycle is: (OCRx+1)/(TOP+1) * 100% So you can never get to 0% in noninverting mode and never to 100% in inverting mode.

Ruudje
  • 1
0

There is another option, simply change pin direction to input like this:

DDRB &= ~(1<<PB1);

..when you want to achieve zero duty cycle. I'm aware this is some kind of a hack and sometimes it's not desired to perform such a thing, but anyway it works.

Adrian Adamczyk
  • 2,560
  • 4
  • 23
  • 41