Mega timers for PWM -- getting B and C pins to toggle

I'm trying to use timers 3 and 4 of a Mega 2560 to generate PWM at 25 kHz. I am only able to get an output on the first pin ("A", e.g. pin 5 for timer 3) of each timer; nothing I've done has enabled the other pins.

Here's my setup function:

void setupTimer3() {
    // Set pins as outputs
    pinMode(2, OUTPUT);   // FAN1
    pinMode(3, OUTPUT);   // FAN2
    pinMode(5, OUTPUT);   // FAN3

    digitalWrite(2, HIGH);
    digitalWrite(3, HIGH);
    digitalWrite(5, HIGH);

    // Enable the PWM outputs OC3A, OC3B and OC3C on digital pins 5, 2 and 3   
   TCCR3A = _BV(COM3A1) | _BV(COM3B1) | _BV(COM3C1);   
    // Set phase and frequency correct PWM and prescaler of 0 on timer 3                                                     
    TCCR3B = _BV(WGM33) | _BV(CS30);                      
    
    // 320 = ~25 kHz
    ICR3 = 320;
    // set outputs to 50%
    OCR3A = 160;
    OCR3B = 160;
    OCR3C = 160;
}

This code is largely taken from MartinL's post at

The line TCCR3A = _BV(COM3A1) | _BV(COM3B1) | _BV(COM3C1) ought to enable all three pins, but only pin 5 actually outputs the PWM. (And with an equivalent function for timer 4, only pin 6 has an output.)

I'm doing something wrong, but don't know what. Any help would be appreciated!

Thanks,
John

The part you posted looks good to me. Perhaps there is something wrong in the parts you didn't post.

Thanks for confirming the code, John. I had commented out just about everything in my code that wasn't included in my post, but went back to completely remove everything except the setup function. That still had the same problem. So I got out another Mega board and lo and behold, it worked on all three channels.

It turns out I had not one, but two Megas that had partial hardware failure. The first board suffered a 5V-to-ground short when probing things, though it seemed to work OK afterwards. I'm not surprised that could have biffed some I/O, depending on just what shorted. [ Lesson learned -- put a piece of spaghetti over exposed 5V pins on headers! ]

So I got out another Mega, but this one suffered a 12V-to-ground short because of a part specification error (mine) on the shield -- a part spec'd for 9V was on the 12V power input, and it failed short. After correcting that, the board seemed to work fine in early testing. But it had the weird timer problem. Since the short was on the 12V side, I'm not sure why it affected the 5V Arduino components, but apparently it did. This was confusing, because the symptom was pretty much the same as on the first board.

I plugged the shield (minus shorted diode) into a new Mega, and everything is working as it should be. Your confirmation that the code looked OK got me to where I could check the hardware, so thanks!

Thanks!
John

You have chosen "inverting mode"; so, OC3A, OC3B and OC3C pins will initially remain at LOW states. Therefore, remove the following three lines from the sketch:

    digitalWrite(2, HIGH);
    digitalWrite(3, HIGH);
    digitalWrite(5, HIGH);

Also, I would suggest to include the following lines in the setup() function.

TCCR3A = 0;
TCCR3B = 0;

Test Sketch for 10 Hz in all three channels (tested)

void setup()
{
  Serial.begin(9600);
  setupTimer3();
}

void loop() {}

void setupTimer3()
{
  TCCR3A = 0;
  TCCR3B = 0;
  // Set pins as outputs
  pinMode(2, OUTPUT);   // FAN1
  pinMode(3, OUTPUT);   // FAN2
  pinMode(5, OUTPUT);   // FAN3

  TCCR3A = _BV(COM3A1) | _BV(COM3B1) | _BV(COM3C1);  //inverting mode
  TCCR3B = _BV(WGM33) | _BV(CS32); //prescale = 256
  
  ICR3 = 3125;//10 Hz PWM
  // set outputs to 50%
  OCR3A = 160;
  OCR3B = 160;
  OCR3C = 160;
}

To test connect a LED with a 2.2k series resistor at DPin-2, and then on DPin-3, and then on DPin-5. Observe that the LED blinks at 10 Hz.

Inverting-mode is when COMnx1 and COMnx0 are both set:
TCCR3A = _BV(COM3A1) | _BV(COM3A0) |_BV(COM3B1) | _BV(COM3B0) | _BV(COM3C1) | _BV(COM3C0);
The PWM mode is correctly set for normal PWM.

I don't think those three lines will cause a problem either way. Removing them would not hurt.

You are right.

Thanks for the replies, John and GolemMostafa! As mentioned above, the problem turned out to be an unfortunate sequence of self-inflicted hardware issues.

I included the digitalWrite lines as par of debugging; originally I didn't have them, and there's no need to include them. Since this is a fan controller running at 25 kHz, I don't think the starting phase is too important.

Regarding setting TCCR3A and TCCR3B, I'm not sure why they would need to be zeroed -- I am setting both from scratch with the two setup lines I had. (i.e., I'm not or-ing them with the existing values. It might be more obvious if I preset them to zero, though.

Thanks again!

As we are not sure whether these registers were manipulated or not by the init() function called by main() before starting the setup() function. So, let us reset them before loading new values by | (or) operation.

I'm sure you're correct from a non-obfuscation point of view, but unless I misunderstand the syntax, my setting lines didn't OR new values into the registers, they overwrote them ("=" instead of "|=" as the first operator).

This operation makes or only the referred bit and then assigns the new value to that bit position without affecting other bits. For example:

TCCR3B |= 1<<CS30;

The above code performs or (|) operation between 1 and CS30-bit of TCCR3B Register. The result of the operation is assigned at CS30-bit of TCCR3B Register.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.