Skip to main content
Arduino

Return to Answer

+ comments on updated code.
Source Link
Edgar Bonet
  • 45.1k
  • 4
  • 42
  • 81

Edit: Here are some comments on the updated version of your code.

The expression 1024 * 64 * 65536 / 62500 overflows on both multiplications. I suggest computing this as a float and assigning then to an integer.

The line

TIFR2 |= bit(TOV2); // clear the interrupt

is only useful when you poll the interrupt flag with interrupts disabled. As you are now using the interrupt, you do not need this: the flag is automatically cleared when the ISR starts executing.

If you want to toggle an output pin, you can do

PIND |= bit(4);

which is a single machine instruction and more efficient than PORTD ^= bit(4); (a read-modify-write sequence). Then, for monitoring the execution of the ISR on the scope, I would rather set and clear the pin on every ISR execution.

I tested your program with the ISR modified as this:

ISR(TIMER2_OVF_vect) {
 static uint32_t wave;
 const uint32_t incr = 1.0 * 64 * 256 * 65536 / F_CPU;
 PORTD |= bit(4);
 wave += incr;
 OCR2B = sinLUT[(wave >> 16) & 0x3f];
 PORTD &= ~bit(4);
}

Note that I lowered the frequency to 1 Hz, in order to better see the behavior on the scope. The output looks really clean to me. There are some glitches on the pin 4 pulses, which occasionally are late by about 6.2 μs. This is due to the Timer 0 overflow interrupt, which is used by the Arduino core for timekeeping. If you don't need the Arduino timekeeping functions, you can avoid these glitches by disabling that interrupt. However, since the ISR is never late by more than one PWM cycle, the glitches should have no effect on the PWM signal generation.


Edit: Here are some comments on the updated version of your code.

The expression 1024 * 64 * 65536 / 62500 overflows on both multiplications. I suggest computing this as a float and assigning then to an integer.

The line

TIFR2 |= bit(TOV2); // clear the interrupt

is only useful when you poll the interrupt flag with interrupts disabled. As you are now using the interrupt, you do not need this: the flag is automatically cleared when the ISR starts executing.

If you want to toggle an output pin, you can do

PIND |= bit(4);

which is a single machine instruction and more efficient than PORTD ^= bit(4); (a read-modify-write sequence). Then, for monitoring the execution of the ISR on the scope, I would rather set and clear the pin on every ISR execution.

I tested your program with the ISR modified as this:

ISR(TIMER2_OVF_vect) {
 static uint32_t wave;
 const uint32_t incr = 1.0 * 64 * 256 * 65536 / F_CPU;
 PORTD |= bit(4);
 wave += incr;
 OCR2B = sinLUT[(wave >> 16) & 0x3f];
 PORTD &= ~bit(4);
}

Note that I lowered the frequency to 1 Hz, in order to better see the behavior on the scope. The output looks really clean to me. There are some glitches on the pin 4 pulses, which occasionally are late by about 6.2 μs. This is due to the Timer 0 overflow interrupt, which is used by the Arduino core for timekeeping. If you don't need the Arduino timekeeping functions, you can avoid these glitches by disabling that interrupt. However, since the ISR is never late by more than one PWM cycle, the glitches should have no effect on the PWM signal generation.

Source Link
Edgar Bonet
  • 45.1k
  • 4
  • 42
  • 81

There are quite a few issues here.

First, you have to decide what channel you are going to use. You wrote that pin 11 is the B channel of Timer 2, but this is incorrect:

  • pin 11 = PB3 = OC2A = A channel of Timer 2
  • pin 3 = PD3 = OC2B = B channel of Timer 2

Next, you have to set the pin as output:

// either:
DDRB |= bit(PB3); // set pin 11 = PB3 = OC2A as output
// or:
DDRD |= bit(PD3); // set pin 3 = PD3 = OC2B as output

For enabling non-inverting PWM, you have to set either bit COM2A1 or COM2B1, depending on the channel you want. There is no reason to touch the bit COM2A0, which is for for the "toggle on compare match" mode (not what you want).

Regarding the timer mode, you have chosen mode 7, (fast PWM with TOP = OCR2A). The ability to set the TOP value is meant to enable frequency modulation, at the cost of loosing PWM capability on channel A. Again, not what you want. Instead, you want the timer to count all the way from 0x00 to 0xff, which is what mode 3 gives you.

Then, there is the code that waits for the compare match in order to update the PWM value at the right time. You are using the wrong bit: OCIE2B is an "interrupt enable" bit, which is used to enable the corresponding interrupt. If you enable the interrupt and have not defined a suitable interrupt service routine, then your program will crash (restart). You do not need interrupts here. Instead, you can simply test the "interrupt flag" OCF2A or OCF2B (depending on the channel) from register TIFR2.

There could be a race here: if the PWM value is very high, the interrupt flag will raise just before the start of the next PWM cycle, and you may end up updating the PWM value a bit too late. I would recommend instead monitoring the TOV2 (overflow) interrupt flag, which raises at the very beginning of a PWM cycle. Then you have a full cycle to update the PWM value.

AltStyle によって変換されたページ (->オリジナル) /