Using 4 PWM`s issue

Hello, I use arduino for a few years, and so far I could use it without big issues, but there is one which is causing me headakes.

I'm controlling to motors using h bridge, so I need to control 4 pwm, two will be zero and the other two between zero and 255.

Each motor uses two pwm, each pwm controls the speed of a direction, so I can never have values grater than zero for the same motor. I have testes and confirmed it works.

I'm using arduino nano, 328. I tried many different ports, by the manual I can use 3, 5, 6, 9, 10, and 11.

In header:

#define MOTOR_A_PWM1 3
#define MOTOR_A_PWM2 5
#define MOTOR_B_PWM1 6
#define MOTOR_B_PWM2 9

In setup:

pinMode(MOTOR_A_PWM11, OUTPUT);
pinMode(MOTOR_A_PWM2, OUTPUT);
pinMode(MOTOR_B_PWM1, OUTPUT);
pinMode(MOTOR_B_PWM2, OUTPUT);

analogWrite(MOTOR_A_PWM1, LOW);
analogWrite(MOTOR_A_PWM2, LOW);
analogWrite(MOTOR_B_PWM1, LOW);
analogWrite(MOTOR_B_PWM2, LOW);

And the speed, I work from -250 to 250, to know what direction the motor must go. When the speed changes I call:

int absSpeed = abs(speed);
Serial.println(absSpeed);
if (speed > 0)
{
analogWrite(MOTOR_A_PWM1, 0);
analogWrite(MOTOR_B_PWM2, 0);
analogWrite(MOTOR_A_PWM2, absSpeed);
analogWrite(MOTOR_B_PWM1, absSpeed);
delay(100); //I added, changed values and removed this line to try but no lucky
}
else
{
analogWrite(MOTOR_A_PWM2, 0);
analogWrite(MOTOR_B_PWM1, 0);
analogWrite(MOTOR_A_PWM1, absSpeed);
analogWrite(MOTOR_B_PWM2, absSpeed);
delay(100);
}

So, let's go to the problem. When the speed is grater than zero (driving forward) both pwm's work. When I try to go backwards it goes for about 3 steps and briks the arduino.

I took everything out, let the arduino without any connection, and just tested the pwm output with a led, I still have the same issue.

I tried another arduino, brand new, the same happens. So it must be a programming issue. A limitation I never faced before, I really don't know...

Thank you very much for reading this far, if you have any ideas of what it could be, please reply.

Thiago

what if speed==0

Then all ports will be zero, which is ok, it will stop both motors.

if (speed > 0)

maybe u mean

if(speed>=0){

ur routine never specifies what to do if speed==0

it falls into 'else'. The if looks for > and else in other hand is <=

thiagoribeiro81:
I took everything out, let the arduino without any connection, and just tested the pwm output with a led, I still have the same issue.

So where's the whole compilable code then?

And please use the code tags next time.

It briks, the arduino stops working, it stops serial connection, RF connections, everything.

I have a few tests that points up the problem to the pwms:

  1. If I comment one of the lines, let's say the MOTOR B PWM2 into the ELSE. It works just fine, but as I don't change the output the motor or led wouldn't work.

  2. Then you may figure that there is a problem with motor B or the ports, I tried many ports and also, if I comment all analogWrite of MOTOR A, the motor B will work.

So it appears to be an issue having 4 pwm signals been changed oftenly.

Sorry for my bad english.

Thank you for trying to help guys.

Okay, let me rephrase my last post:

Post a whole sketch!

That we can read and compile. NOT snippets of where you think the problem is. You may reduce the sketch to where the problem is as long as the sketch still compiles and has the same error.

Using analogWrite will never ever just "brick" the Arduino

#include <VirtualWire.h>

byte message[VW_MAX_MESSAGE_LEN];    // Armazena as mensagens recebidas
byte msgLength = VW_MAX_MESSAGE_LEN; // Armazena o tamanho das mensagens

#define MOTOR_A_PWM1 6
#define MOTOR_A_PWM2 9
#define MOTOR_B_PWM1 10
#define MOTOR_B_PWM2 11

void setup()   
{
  Serial.begin(115200);
  TCCR0B = TCCR0B & 0b11111000 | 0x02 ;
  
  vw_set_rx_pin(8);     // Define o pino 5 do Arduino como entrada 
                        //de dados do receptor
  vw_setup(3000);       // Bits por segundo
  vw_rx_start();        // Inicializa o receptor


  pinMode(MOTOR_A_PWM1, OUTPUT);
  pinMode(MOTOR_A_PWM2, OUTPUT);
  pinMode(MOTOR_B_PWM1, OUTPUT);
  pinMode(MOTOR_B_PWM2, OUTPUT);
  
  analogWrite(MOTOR_A_PWM1, LOW);
  analogWrite(MOTOR_A_PWM2, LOW);
  analogWrite(MOTOR_B_PWM1, LOW);
  analogWrite(MOTOR_B_PWM2, LOW);

  delay(500);
  
  Serial.println("Car engine is ready...");
}

void loop()
{
  uint8_t message[VW_MAX_MESSAGE_LEN];    
  uint8_t msgLength = VW_MAX_MESSAGE_LEN; 
 
  if (vw_get_message(message, &msgLength)) // Non-blocking
  {
    processMessage(message, msgLength);
    Serial.print("Recebido: ");
    for (int i = 0; i < msgLength; i++)
    {
      Serial.write(message[i]);
    }
    Serial.println();
  }

  updateMotors();
}

int direction = 1;
int speed = 0;
int targetSpeed = 0;

void processMessage(byte msg[], int size)
{
  //Serial.print("Processing: ");
  //Serial.println(msg[0]);
  switch(msg[0])
  {
    case 'v':
      int vertical = (msg[1] - '0') * 20;
      targetSpeed = vertical;
      Serial.println(targetSpeed);
      break;
  }
}

void updateMotors()
{
  bool changed = false;
  if (speed > targetSpeed)
  {
    speed-=5;
    changed = true;
  }
  if (speed < targetSpeed)
  {
    speed+=5;
    changed = true;
  }

  if (changed)
  {
    int absSpeed = abs(speed);
    //Serial.println(absSpeed);
    if (speed > 0)
    {
      delay(200);
      analogWrite(MOTOR_A_PWM1, 0);
      analogWrite(MOTOR_B_PWM2, 0);
      analogWrite(MOTOR_A_PWM2, absSpeed);
      analogWrite(MOTOR_B_PWM1, absSpeed);
      delay(200);
    }
    else
    {
      delay(200);
      analogWrite(MOTOR_A_PWM2, 0);
      analogWrite(MOTOR_B_PWM1, 0);
      analogWrite(MOTOR_A_PWM1, absSpeed);
      analogWrite(MOTOR_B_PWM2, absSpeed);
      delay(200);
    }
  }
  
}

Alright, I don't see anything wrong with the PWM part. But I see other flaws.

TCCR0B = TCCR0B & 0b11111000 | 0x02 ;

Changing the timer0 speed will seriously influence timing issues because it changes millis(). A lot of code relies on millis()... Also, VW messes with timer1.

The biggest is the reading of the VW.

void processMessage(byte msg[], int size)
{
  //Serial.print("Processing: ");
  //Serial.println(msg[0]);
  switch(msg[0])
  {
    case 'v':
      int vertical = (msg[1] - '0') * 20;
      targetSpeed = vertical;
      Serial.println(targetSpeed);
      break;
  }
}

You never check the length. You get weird errors when you only receive 1 byte :wink:

  bool changed = false;
  if (speed > targetSpeed)
  {
    speed-=5;
    changed = true;
  }
  if (speed < targetSpeed)
  {
    speed+=5;
    changed = true;
  }

Not really wrong but keep in mind, it only works because you always set speed in multiples of 20 which is a multiple of 5. If target speed is 11 and current speed is 10 you'll end with a weird oscillation... Keep that in mind if you change the speed setting part of the code...

You want to avoid the delay() all together. Your code is seriously blocking... See BlinkWithoutDelay example sketch.

And some general advice, using define for pin definition not recommended by Arduino. The const keyword is preferred, it's a bit more versatile for the compiler. So:

const byte MotorA1Pin = 6;
const byte MotorA2Pin = 9;
const byte MotorB1Pin = 10;
const byte MotorB2Pin = 11;

I think you use something like a L298? Then it's better to PWM the Enable line. It has tree advantages:

  1. You only need one PWM pin per motor
  2. You use the same PWM pin for forwards and backwards
  3. You don't try to "Fast motor stop" the motor when the pwm is off...

You're messing with TCCR0B, so delay() is going to be wrong, or maybe just hang up the system completely if it results in the millis ISR never getting called (i can't read TCCR0B by sight, so I don't know off hand what you're telling it to do).

You also may not have 4 available PWM pins.

VW takes over timer 1, so you can't use those PWM pins, and you've done something to timer 0 - you may only have two PWM pins available (3 and 11, on timer 2).

What are you doing to timer0 anyway, and why? I don't see anything else in your sketch that explains why you're doing that?

Ow, wow, I always assumed the delay() function was based around NOPs or something... But yeay, it relies om timer0 because it uses micros().

void delay(unsigned long ms)
{
	uint16_t start = (uint16_t)micros();

	while (ms > 0) {
		yield();
		if (((uint16_t)micros() - start) >= 1000) {
			ms--;
			start += 1000;
		}
	}
}

He just changes the speed of timer0. He sets the prescaler to 8 instead of 64, so all the timing stuff is 8 times to fast. As far as PWM, only works faster.

For timer1, it's indeed used by VW. No idea if it only changes timing or makes PWM unusable....

Hello septillion, thank you for all info.

Please disconsider the timer0 method, I just added this today morning in order to try to know if the arduino was still able to run timer0 when appearing that it had briked. I had a method that made the pin 13 blink. It is not in the code I posted anymore. I didn realised that changing it would implicate in so many other functions, trying to solve a problem I caused so many others. Thank you for showing me that!

You're right, the message code was much more complex, I still have its backup, I just simplified to eliminate possibilities. As you said, the way it is now is counting on luck :slight_smile:

Also the addition of delay together the changes in the pwm values I added after reading a post that it would help keep at list one cicle between changes. I tried to delay between the functions, in the beginning, in the end, all combinations but it didnt work. I agree with you.

I tried to clean the code with all the suggestions, but no luck yet :frowning:

About the steps, it was set to step++ at first, but the motors were taking too long to get to the target speed, then I changed to go 5 by 5 steps, the controller always send multiples of 10, always, it uses the raw data - raw data % 10.

Itś pretty weird, one direction works pretty fine, the other works for a few steps and the arduino stops. As DrAzzy said I believe the arduino has many pwm ports, but it's not really able to use all of them at the same time.

Thank you very very much for all the replies, if you have any other suggestion please send back. Thanks again!! :slight_smile:

the _BV routine works best i think for setting up the timers...
such as..
TCCR2A = _BV(COM2A1) | _BV(COM2A0) | _BV(COM2B1) | _BV(WGM20);
whatevr works for ur need

also--
why not simplify ur pwm routine--

void updateMotors(){absSpeed=abs(speed);
if(speed>targetSpeed){speed=-5; //or whatever
//do pwms
}
else(speed=+=5;
//do pwms
}