Controlling a DC motor by PWM at constant speed

Hello !

I am trying to control a DC motor by PWM.
My motor have attached a brake to simulate a mechanical load.

So,

  • I set the constant speed by PWM ( for example 1200 rpm);
  • i pull the brake (softly) and the motor will slow-down it's speed (for example 800 rpm).

My controller needs to increase the PWM lenght so that even with the load (the brake) the motor should run at 1200 rpm.

Below is my code :

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 13, 6);

byte trigger;

unsigned long t0, t1;
float rpm;

int potPin = 0;
int transistorPin = 9;
int potValue = 0;

void setup()
{
pinMode(transistorPin, OUTPUT);
lcd.begin(20, 4);

attachInterrupt(0, rpm_fun, RISING); // senzorul HALL pe pin 2
t0 = 0;
t1 = 0;
trigger = 0;
lcd.setCursor(9,0);
lcd.print("----------");
lcd.setCursor(0, 3);
lcd.print("---------------- RPM");
lcd.setCursor(0, 1);
lcd.print("Turatie impusa=");
lcd.setCursor(0,2);
lcd.print("RPM acum=");
}

void loop()

{
potValue = analogRead(potPin) / 4;
analogWrite(transistorPin, potValue);

// incercare

//if ( potValue <= 150 && rpm < 2500 )potValue=254;

if (potValue >= 100&& rpm < 2200)
{
analogWrite(transistorPin,255 );
delay(2000);
}

// incercare

float g = 0;

if (trigger != 0) {
g = t1 - t0;
rpm = (1/g) * 60000000;
trigger = 0;

}

//-------- AFISARE PE LCD --------------------

delay(500);

int RPM=rpm;

lcd.setCursor(10,2);
lcd.print(RPM);
if ( RPM < 1000) lcd.print(' ');

lcd.setCursor(0,0);
lcd.print(potValue);
if ( potValue < 100 ) lcd.print(' ');

//-------- AFISARE PE LCD --------------------

}

void rpm_fun()
{
if (t0 == 0)
t0 = micros();
else {
t0 = t1;
t1 = micros();
trigger = 1;
}
}

I tried to do this :

if (potValue >= 100&& rpm < 2200)
{
analogWrite(transistorPin,255 );
delay(2000);
}

event without delay.
It work, but just for almoast a second, until it reached the deasired speed, and then the speed will slow again.

What should i do ?

The best way to do this is with a PID controller - take the difference between desired and actual speeds as the error value, let the PID control the PWM output.

Failing that a simple approach is to increase the PWM by 1 each time you sense the speed is too low, and decrease by 1 each time it is too slow, clunky but might be OK.

BTW that float rpm computation can be done faster like this:

    // incercare
 
  if (trigger != 0) 
  {
       rpm = 60000000UL / (t1 - t0) ;
       trigger = 0; 
  }

Floats aren't needed at all.

Hello MarkT and thank you for your replay !

Firstly i tried to use some PID algorytms but i faild because all the algorytms that i used where designed for relay output, like a thermostat applications, AND, the PID library form Arduino reference is not really PID because when that library don't use a "reaction" in the loop - so if you can't measure (or know) your actual RPM, you can't modify-it to your desired RPM.

Now i whant to use something like this

  • I have an array with few RPM , like 1000, 1500, 2000, 2500 rpm;
  • I start the motor at (for example) 2000 rpm;
  • I push the break -> and the motor speed will slow down (for example down to 1500);
  • from Arduino I increase the PWM lenght so even with the break, my motor will run at 2000 rpm

Regards!

Radu DAN

yo9hnf:
Firstly i tried to use some PID algorytms but i faild because all the algorytms that i used where designed for relay output, like a thermostat applications, AND, the PID library form Arduino reference is not really PID because when that library don't use a "reaction" in the loop - so if you can't measure (or know) your actual RPM, you can't modify-it to your desired RPM.

But you do have a way to measure RPM. I think you are misunderstanding the PID interface.

Now i whant to use something like this

  • I have an array with few RPM , like 1000, 1500, 2000, 2500 rpm;
  • I start the motor at (for example) 2000 rpm;
  • I push the break -> and the motor speed will slow down (for example down to 1500);
  • from Arduino I increase the PWM lenght so even with the break, my motor will run at 2000 rpm

If you take out all the unnecessary delays in your code, MarkT's suggestion will work.

int motorValue = 0;

void loop()
{                           
  unsigned int setRPM;

  potValue = analogRead(potPin);   
  setRPM = map(potValue, 0, 1023, 0, 2500);
  if (rpm < setRPM) {
       motorValue++;
  } else if (rpm > setRPM) {
       motorValue--;
 }
 motorValue = constrain(motorValue, 0, 255);
 analogWrite(transitorPin, motorValue);

  if (trigger) {
       rpm = 60000000UL / (t1 - t0);
       trigger = 0; 
  }
 
  //--------  AFISARE PE LCD  --------------------
 
  int RPM=rpm;
  lcd.setCursor(10,2);
  lcd.print(RPM);
  if ( RPM < 1000) lcd.print(' ');
 
  lcd.setCursor(0,0);
  lcd.print(potValue);
  if ( potValue < 100 ) lcd.print(' ');
 
 //--------  AFISARE PE LCD  -------------------- 
 
}

The only problem with this approach is that it will react somewhat slowly to large changes in either the motor load (brake) or the desired RPM. That's what PID gives you: the change in the output value is calculated so that the motor settles to the set point quickly, without thrashing or over shooting.