Arduino Mega 2560 - inverting PWM

Hello guys,

I am new in this community. I am Alessandro and i am passionate about electronics so sometimes I run some mini projects as hobbist.

I am actually going to command a stepper motor using two square waves of 50% duty cycle on the two pins of PULSE and DIRECTION.
For those i need 22kHz on the PULSE pin and 2Hz on the DIRECTION pin.

I decided to use my ARDUINO MEGA 2560 to provide PWM output through channel 10 (using Timer 2) to get 22kHz and through channel 6 (using Timer 4) to get 2Hz.

This is the code I used:

const byte PULSES = 10;  // Timer 2 "A" output: OC2A
const byte DIR = 6;  // Timer 4 "A" output: OC4A


void setup() {
pinMode (PULSES, OUTPUT);
pinMode (DIR, OUTPUT);
Serial.begin(9600);

 // set up Timer 2

int myEraserPulses = 7;             // this is 111 in binary and is used as an eraser
TCCR2B &= ~myEraserPulses;   // this operation (AND plus NOT),  set the three bits in TCCR2B to 0

TCCR2A = _BV(COM2A0) | _BV(WGM21) | _BV(WGM20);
TCCR2B = _BV(WGM22) | _BV(CS21);
OCR2A = 44; 
                           

// set up Timer 4
int myEraserDir = 7;             // this is 111 in binary and is used as an eraser
TCCR4B &= ~myEraserDir;   // this operation (AND plus NOT),  set the three bits in TCCR4B to 0 

TCCR4A = _BV(COM4A0)| _BV(WGM41) | _BV(WGM40); // CTC, toggle OC4A on Fast PWM 
TCCR4B = _BV(WGM43) | _BV(WGM42)| _BV(CS42);
OCR4A =  15625;          // 62500Hz/2Hz=31250    (15625--> 50%)

}  // end of setup

void loop() { }

I am actually able to achieve 22kHz on pin 10 but I am not able to get 2Hz on pin 6. I have been trying for several days to change the code but I haven't found the issue.

With this configuration there is no output on pin 6, while if I change the OCR4A into OCR4AH, IT WORKS!! (IN THE SENSE THAT I GET A PWM OUTPUT FROM PIN 6 BUT WITH A FREQUENCY OF 31kHz THAT WILL NOT CHANGE EVEN IF I CHANGE THE VALUE INSIDE OCR4AH)

Does anyone has an advice of what might be the problem?

Thanks a lot for your time.

Maybe nobody knows the answer?

Does anyone has an advice of what might be the problem?

I don't have a Mega, but when I adapted your code for Timer2 and Timer1 on a Uno it ran correctly.

There is some sort of bug in the avr core for the mega which effects multiple instance of pwm with analogWrite(). It's possible that it effects the code written at the register level as well.

I think there are some solutions proposed in the thread, and you might try implement one of them and see if it gets you output on two pins.

Solved.
My bad! I think I was using wrong the oscilloscope.

That's the code for who might need it:

const byte PULSES = 10;  // Timer 2 "A" output: OC2A
const byte DIR = 6;  // Timer 4 "A" output: OC4A


void setup() {
pinMode (PULSES, OUTPUT);
pinMode (DIR, OUTPUT);
Serial.begin(9600);

 // set up Timer 2
int myEraserPulses = 7;             // this is 111 in binary and is used as an eraser
TCCR2B &= ~myEraserPulses;   // this operation (AND plus NOT),  set the three bits in TCCR2B to 0

TCCR2A = _BV(COM2A0) | _BV(WGM21) | _BV(WGM20); // fast PWM
TCCR2B = _BV(WGM22) | _BV(CS21); // 16MHz/8=2MHz
OCR2A = 44; // 2MHz/22kHz=90.90 (50% duty--> 45)         

// set up Timer 4
int myEraserDir = 7;             // this is 111 in binary and is used as an eraser
TCCR4B &= ~myEraserDir;   // this operation (AND plus NOT),  set the three bits in TCCR4B to 0

TCCR4A = _BV(COM4A0)| _BV(WGM41) | _BV(WGM40); // fast PWM
TCCR4B = _BV(WGM43) | _BV(WGM42)| _BV(CS42)| _BV(CS40); // 16MHz/1024=15625Hz 
OCR4A =  3907;          // 62500Hz/2Hz=7812.5 (50% duty--> 3906.25) 

}  // end of setup

void loop() { }

D10S:
Solved.
My bad! I think I was using wrong the oscilloscope.

That's the code for who might need it:

const byte PULSES = 10;  // Timer 2 "A" output: OC2A

const byte DIR = 6;  // Timer 4 "A" output: OC4A

void setup() {
pinMode (PULSES, OUTPUT);
pinMode (DIR, OUTPUT);
Serial.begin(9600);

// set up Timer 2
int myEraserPulses = 7;            // this is 111 in binary and is used as an eraser
TCCR2B &= ~myEraserPulses;  // this operation (AND plus NOT),  set the three bits in TCCR2B to 0

TCCR2A = _BV(COM2A0) | _BV(WGM21) | _BV(WGM20); // fast PWM
TCCR2B = _BV(WGM22) | _BV(CS21); // 16MHz/8=2MHz
OCR2A = 44; // 2MHz/22kHz=90.90 (50% duty--> 45)

// set up Timer 4
int myEraserDir = 7;            // this is 111 in binary and is used as an eraser
TCCR4B &= ~myEraserDir;  // this operation (AND plus NOT),  set the three bits in TCCR4B to 0

TCCR4A = _BV(COM4A0)| _BV(WGM41) | _BV(WGM40); // fast PWM
TCCR4B = _BV(WGM43) | _BV(WGM42)| _BV(CS42)| _BV(CS40); // 16MHz/1024=15625Hz
OCR4A =  3907;          // 62500Hz/2Hz=7812.5 (50% duty--> 3906.25)

}  // end of setup

void loop() { }

It perfectly works now! Does someone know how can I invert the PWM output of channel 10? So what I mean is that now I have a HIGH-LOW 0-5 V at 2Hz with 50% duty cycle. What I wanna achieve is that the signal would be LOW where it is high and HIGH where it is low.
I know i can do it externally at the output with a NOT IC TTL, but there is a way I can do it by software?

analogWrite(pin, 10);

|--||--|____

analogWrite (pin, 245);
|-----------------||---------------|__|----

So a smaller # results in a short amount of High time.
Change to a bigger # (255 - 10), that has the effect of the same amount of time as Low time.
So effectively, the pulse is 'inverted'.

D10S:

Quote from: D10SDoes someone know how can I invert the PWM output of channel 10?

On Timer 2 you are using WGM 7 (WGM20, WGM21, & WGM22 set) which is one of the Fast PWM modes.
You are setting COM2A0 to 1 but not setting COM2A1 to 1. That is a RESERVED value and should NOT be used. What you SHOULD have done was set COM2A1 to 1 to enable PWM output.
To invert PWM on Timer2 OC2A you would set COM2A0 in TCCR2A as well a COM2A1.

Does someone know how can I invert the PWM output of channel 10? So what I mean is that now I have a HIGH-LOW 0-5 V at 2Hz with 50% duty cycle.

You are confused. Pin 10 is output OCR2A on timer2 of the Mega. It is running at 22KHz.

Using fast pwm with OCRxA as top value with a toggle at compare match at top makes it impossible to invert the output. Furthermore, what does it mean to invert the output of a 50% duty cycle.

The on/off periods must be in reference to some other starting point.

I am actually going to command a stepper motor using two square waves of 50% duty cycle on the two pins of PULSE and DIRECTION.
For those i need 22kHz on the PULSE pin and 2Hz on the DIRECTION pin.

This is an unusual application for the timers. Furthermore, you are working at the register level with settings you do not fully understand. Your choice of mode indicates that to me.

The stepper pulse does not need to be 50%, and it is not necessary to use a timer for 2Hz output. What are you really trying to achieve?

cattledog:
You are confused. Pin 10 is output OCR2A on timer2 of the Mega. It is running at 22KHz.

Using fast pwm with OCRxA as top value with a toggle at compare match at top makes it impossible to invert the output. Furthermore, what does it mean to invert the output of a 50% duty cycle.

The on/off periods must be in reference to some other starting point.

This is an unusual application for the timers. Furthermore, you are working at the register level with settings you do not fully understand. Your choice of mode indicates that to me.

The stepper pulse does not need to be 50%, and it is not necessary to use a timer for 2Hz output. What are you really trying to achieve?

Sorry for the confusion. I meant timer 4.

What I mean for inverting is: let's pick up 1 Hz as example. It will be High for 0.5 s and Low for the others 0.5 s . What I want by inverting is that the pwm would be first Low for 0.5 s and then High for the others 0.5 s

So right now in the moment i upload my sketch on arduino, it will generate from PIN 6 a square wave of 2 Hz into the DIR pin of my stepper motor. This will cause my motor (that has a driver I cannot access, I can only provide it with PULSES and DIR square waves) to reverse up and down with UP as first direction.
I guess that if I can invert the PWM it will start reversing with DOWN as first direction (THAT IS MY SCOPE).

johnwasser:
On Timer 2 you are using WGM 7 (WGM20, WGM21, & WGM22 set) which is one of the Fast PWM modes.
You are setting COM2A0 to 1 but not setting COM2A1 to 1. That is a RESERVED value and should NOT be used. What you SHOULD have done was set COM2A1 to 1 to enable PWM output.
To invert PWM on Timer2 OC2A you would set COM2A0 in TCCR2A as well a COM2A1.

I actually followed what was indicated on ATMega 2560 datasheet since I am using and Arduino Mega. I followed the table attached

I tried all the others combinations for Timer 4:
TCCR4A = _BV(COM4A1) | _BV(WGM41) | _BV(WGM40);
TCCR4A = _BV(COM4A0) | _BV(COM4A1) |_BV(WGM41) | _BV(WGM40);

but the only one that gives me a PWM as output is this one:
TCCR4A = _BV(COM4A0) |_BV(WGM41) | _BV(WGM40);

Try to move these two lines to the very end of setup and see if it helps.

pinMode (DIR, OUTPUT);
digitalWrite (DIR,LOW);

EDIT: Correct post for Timer4 instead of Timer2
I would set TCNT4 = 0 right before you start the timer by writing the setting into the prescaler.

// set up Timer 4
int myEraserDir = 7;             // this is 111 in binary and is used as an eraser
TCCR4B &= ~myEraserDir;   // this operation (AND plus NOT),  set the three bits in TCCR4B to 0

TCCR4A = _BV(COM4A0)| _BV(WGM41) | _BV(WGM40); // fast PWM
TCCR4B = _BV(WGM43) | _BV(WGM42);//| _BV(CS42)| _BV(CS40); // 16MHz/1024=15625Hz
OCR4A =  3907;          // 62500Hz/2Hz=7812.5 (50% duty--> 3906.25)
TCNT4 = 0;
TCCR4B |= _BV(CS42)| _BV(CS40); //start timer

The initially LOW output should toggle HIGH after the first match.
I still think there are easier ways to do what you want than to use timer output to control the DIR.

What I want to do is to invert Timer 4, 2 Hz.

Timer 2, 22 kHz is ok.

I made a mistake, but corrected my previous post for Timer4. Sorry :frowning:

cattledog:
I made a mistake, but corrected my previous post for Timer4. Sorry :frowning:

Thanks a lot for your help but it is actually not working.

A solution might be to put a logical inverter between the arduino output and the direction pin so it will actually invert the pwm. Of course it was better by firmware I think cause the TTL logic will introduce some delay.

Thanks a lot for your help but it is actually not working.

Explain what this means?

How are you determining that the initial period is HIGH and not LOW? Can you post the oscilloscope traces to show what you mean?

cattledog:
Explain what this means?

How are you determining that the initial period is HIGH and not LOW? Can you post the oscilloscope traces to show what you mean?

Yes, exactly. For the initial 0.5 s, the pwm is still HIGH

I switched to 1 Hz for simplicity, however the idea is the same.

const byte PULSES = 5;  // Timer 3 "A" output: OC3A --> 10 kHz
const byte DIR = 6;  // Timer 4 "A" output: OC4A --> 1 Hz
const byte home_switch=2; // Pin 9 connected to Home Switch (Photointerrupter)

#define encoderPinA 2 // encoder channel A
#define encoderPinB 3 // encoder channel B
volatile int counter =0; // pulses counter of the encoder
unsigned long time;
int incomingByte = 0;   // for incoming serial data

void setup() {
pinMode (PULSES, OUTPUT); // set the pulses pin as output
pinMode (DIR, OUTPUT);
pinMode(home_switch, INPUT_PULLUP); // set the photointerrupter as an input
pinMode(encoderPinA, INPUT); // set the encoder as an input
pinMode(encoderPinB, INPUT); // set the encoder as an input
attachInterrupt(digitalPinToInterrupt(encoderPinA), isr,RISING); //pulses counting
Serial.begin(9600);

 delay(2);  //
 
//  // set up Timer 3
int myEraserPulses = 7;             // this is 111 in binary and is used as an eraser
TCCR3B &= ~myEraserPulses;   // this operation (AND plus NOT),  set the three bits in TCCR2B to 0

TCCR3A = _BV(COM3A0)| _BV(WGM31) | _BV(WGM30); // fast PWM
TCCR3B = _BV(WGM33) | _BV(WGM32)| _BV(CS31); // 16MHz/8=2MHz 
OCR3A =  99;          // 2MHz/10kHz=200 (50% duty--> 100)      

//// set up Timer 4
int myEraserDir = 7;             // this is 111 in binary and is used as an eraser
TCCR4B &= ~myEraserDir;   // this operation (AND plus NOT),  set the three bits in TCCR4B to 0

TCCR4A = _BV(COM4A0)| _BV(WGM41) | _BV(WGM40); // fast PWM
TCCR4B = _BV(WGM43) | _BV(WGM42)| _BV(CS42)| _BV(CS40); // 16MHz/1024=15625Hz 
OCR4A =  7815;          // 15625Hz/1Hz=15625 (50% duty--> 7812.5) 
TCNT4 = 0;
TCCR4B |= _BV(CS42)| _BV(CS40); //start timer

}  // end of setup


void loop()
{
if (Serial.available() >= 0) {  // read the incoming byte:
  incomingByte = Serial.read();
 
  Serial.print("Encoder: ");
  Serial.print(counter);
  Serial.print("\t");
 
  Serial.print("Time: ");
  time = millis();
  Serial.println(time); //prints time since program started
 
if (incomingByte=='0') { //pressing 0 into the serial monitor to stop the program
   while(1) { }
}
}
}
//Interrupts
void isr(){

    int channel_A = digitalRead(encoderPinA);
    int channel_B = digitalRead(encoderPinB);
 
   if(channel_A == channel_B){
    counter++;
   }
   else{
    counter--;
   }       
}

The starting of the timer should be in the last line after TCNT4 is set to 0. Remove the CS bits from the line setting WGM in TCCR4B. You missed my commented section in the last post.

I also believe that because of how the timer output compare works on the next clock cycle after the match, it may be best to start at TCNT4 = 1. I have tested this with your code with a very long timer period with an led set on right at the end of setup, and the pwm pin output stays low initially.

Try this

//// set up Timer 4
int myEraserDir = 7;             // this is 111 in binary and is used as an eraser
TCCR4B &= ~myEraserDir;   // this operation (AND plus NOT),  set the three bits in TCCR4B to 0

TCCR4A = _BV(COM4A0)| _BV(WGM41) | _BV(WGM40); // fast PWM
//TCCR4B = _BV(WGM43) | _BV(WGM42)| _BV(CS42)| _BV(CS40); // 16MHz/1024=15625H
TCCR4B = _BV(WGM43) | _BV(WGM42); // 16MHz/1024=15625Hz
OCR4A =  7815;          // 15625Hz/1Hz=15625 (50% duty--> 7812.5)
TCNT4 = 1;
TCCR4B |= _BV(CS42)| _BV(CS40); //start timer

Sorry! I missed that part. However, it is still showing HIGH initially.

I missed this too:
pinMode (DIR, OUTPUT);
digitalWrite (DIR,LOW);

I have the "pinMode" command but why do do I need to add "digitalWrite (DIR,LOW);" ?

I have the "pinMode" command but why do do I need to add "digitalWrite (DIR,LOW);" ?

Not really necessary, as the default value is LOW. Sorry I can't be of more help.

cattledog:
Not really necessary, as the default value is LOW. Sorry I can't be of more help.

Thanks for the effort cattledog! Really appreciated.

Actually I don't get why setting both COM4A0 and COM4A1 to 1, it will not give me any PWM in output. According to the table of the ATMega2560 datasheet it should give me an inverted PWM!