PWM DC Motor control with Counter problem

This is my first Arduino project and hoping someone can help me out...

I am using an Aduino Uno and a Robot Power MegaMoto to drive a DC motor from a drill. I have a pot to control the speed and using a hall effect sensor to count revolutions.
The problem I am having is that it doesn't look like I am getting an accurate count. I am using the Serial Monitor to watch the count, PWM duty cycle, and the value from the pot.
The sketch is copied in this post as well as attached:
(hope someone can help...)

Thanks,
Lucas

/*
Coil Winder sketch
Potentiometer to control speed.
TLE4095 Hall Effect Sensor as a counter.
MegaMoto shield used to drive a DC Motor.
*/

int EnablePin = 8;
int duty;
int PWMPin = 11; // Timer2
int PWMPin2 = 3;
int potPin = A0;
int potValue = 0;
int count;

const byte CPin = 0; // analog input channel
int CRaw; // raw A/D value
float CVal; // adjusted Amps value

void setup(){
// initialize the digital pin as an output.
// Pin 13 has an LED connected on most Arduino boards:
count = 50;
pinMode(EnablePin, OUTPUT);
pinMode(PWMPin, OUTPUT);
pinMode(PWMPin2, OUTPUT);
setPwmFrequency(PWMPin, 8); //change Timer2 divisor to 8 gives 3.9kHz PWM freq
Serial.begin(9600);
attachInterrupt(0, magnet_detect, RISING);//Initialize the interrupt pin (Arduino Digital pin 2)
}
void loop()
{
// To drive the motor in H-bridge mode
// the power chip inputs must be opposite polarity
// and the Enable input must be HIGH

if (count > 0)
{
digitalWrite(EnablePin, HIGH); // Pin 8 high, enable motor driver
analogWrite(PWMPin2, 0); // Set Pin 3 low
potValue = analogRead(potPin); // Read voltage from pot wiper
duty = map(potValue, 0, 663, 0, 255);
analogWrite (PWMPin, duty);
Serial.print("count = ");
Serial.print(count);
Serial.print("\t potentiometer = " );
Serial.print(potValue);
Serial.print("\t motor = ");
Serial.println(duty);
// delay(2);
}
else
{
digitalWrite(EnablePin, LOW); // Pin 8 low, disable motor driver
}
}

/*

  • Divides a given PWM pin frequency by a divisor.
  • The resulting frequency is equal to the base frequency divided by
  • the given divisor:
    • Base frequencies:
  • o The base frequency for pins 3, 9, 10, and 11 is 31250 Hz.
  • o The base frequency for pins 5 and 6 is 62500 Hz.
    • Divisors:
  • o The divisors available on pins 5, 6, 9 and 10 are: 1, 8, 64,
  • 256, and 1024.
  • o The divisors available on pins 3 and 11 are: 1, 8, 32, 64,
  • 128, 256, and 1024.
  • PWM frequencies are tied together in pairs of pins. If one in a
  • pair is changed, the other is also changed to match:
    • Pins 5 and 6 are paired (Timer0)
    • Pins 9 and 10 are paired (Timer1)
    • Pins 3 and 11 are paired (Timer2)
  • Note that this function will have side effects on anything else
  • that uses timers:
    • Changes on pins 5, 6 may cause the delay() and
  • millis() functions to stop working. Other timing-related
  • functions may also be affected.
    • Changes on pins 9 or 10 will cause the Servo library to function
  • incorrectly.
  • Thanks to macegr of the Arduino forums for his documentation of the
  • PWM frequency divisors. His post can be viewed at:
  • http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1235060559/0#4
    */

void magnet_detect() //This function is called whenever a magnet/interrupt is detected
{
count--;
}
void setPwmFrequency(int pin, int divisor)
{
byte mode;
if(pin == 5 || pin == 6 || pin == 9 || pin == 10) { // Timer0 or Timer1
switch(divisor) {
case 1: mode = 0x01; break;
case 8: mode = 0x02; break;
case 64: mode = 0x03; break;
case 256: mode = 0x04; break;
case 1024: mode = 0x05; break;
default: return;
}
if(pin == 5 || pin == 6) {
TCCR0B = TCCR0B & 0b11111000 | mode; // Timer0
} else {
TCCR1B = TCCR1B & 0b11111000 | mode; // Timer1
}
} else if(pin == 3 || pin == 11) {
switch(divisor) {
case 1: mode = 0x01; break;
case 8: mode = 0x02; break;
case 32: mode = 0x03; break;
case 64: mode = 0x04; break;
case 128: mode = 0x05; break;
case 256: mode = 0x06; break;
case 1024: mode = 0x7; break;
default: return;
}
TCCR2B = TCCR2B & 0b11111000 | mode; // Timer2
}
}

CoilWinder.ino (3.82 KB)

setPwmFrequency(PWMPin, 8); //change Timer2 divisor to 8 gives 3.9kHz PWM freq

You set the pwm frequency to smiley face? That probably won't work.

Go read the "How to Use This Forum" post at the top of any board and you will learn how to post code correctly so that doesn't happen. You know, they tendency to read the manual first and write second is what separates the good coders from the failing ones.

lsburden:
The problem I am having is that it doesn't look like I am getting an accurate count.}

What count are you getting? What were you expecting? How do you know the actual number?

sorry about that...

/*
  Coil Winder sketch
  Potentiometer to control speed.
  TLE4095 Hall Effect Sensor as a counter.
  MegaMoto shield used to drive a DC Motor.
*/

int EnablePin = 8; 
int duty;
int PWMPin = 11;    // Timer2
int PWMPin2 = 3;
int potPin = A0;    
int potValue = 0;
int count;

const byte CPin = 0;  // analog input channel
int CRaw;      // raw A/D value
float CVal;    // adjusted Amps value

void setup(){              
  // initialize the digital pin as an output.
  // Pin 13 has an LED connected on most Arduino boards:
  count = 50;
  pinMode(EnablePin, OUTPUT);   
  pinMode(PWMPin, OUTPUT);   
  pinMode(PWMPin2, OUTPUT);  
  setPwmFrequency(PWMPin, 8);  //change Timer2 divisor to 8 gives 3.9kHz PWM freq
  Serial.begin(9600);
  attachInterrupt(0, magnet_detect, RISING);//Initialize the interrupt pin (Arduino Digital pin 2)
}
void loop()
{
    // To drive the motor in H-bridge mode
    // the power chip inputs must be opposite polarity
    // and the Enable input must be HIGH

    if (count > 0)
    {
    digitalWrite(EnablePin, HIGH);    // Pin 8 high, enable motor driver
    analogWrite(PWMPin2, 0);          // Set Pin 3 low 
    potValue = analogRead(potPin);    // Read voltage from pot wiper
    duty = map(potValue, 0, 663, 0, 255);
    analogWrite (PWMPin, duty);
    Serial.print("count = ");
    Serial.print(count);
    Serial.print("\t potentiometer = " );
    Serial.print(potValue);
    Serial.print("\t motor = ");
    Serial.println(duty);
   // delay(2);
    }
    else
    {
    digitalWrite(EnablePin, LOW);     // Pin 8 low, disable motor driver
    }
}
        
/*
 * Divides a given PWM pin frequency by a divisor.
 * 
 * The resulting frequency is equal to the base frequency divided by
 * the given divisor:
 *   - Base frequencies:
 *      o The base frequency for pins 3, 9, 10, and 11 is 31250 Hz.
 *      o The base frequency for pins 5 and 6 is 62500 Hz.
 *   - Divisors:
 *      o The divisors available on pins 5, 6, 9 and 10 are: 1, 8, 64,
 *        256, and 1024.
 *      o The divisors available on pins 3 and 11 are: 1, 8, 32, 64,
 *        128, 256, and 1024.
 * 
 * PWM frequencies are tied together in pairs of pins. If one in a
 * pair is changed, the other is also changed to match:
 *   - Pins 5 and 6 are paired (Timer0)
 *   - Pins 9 and 10 are paired (Timer1)
 *   - Pins 3 and 11 are paired (Timer2)
 * 
 * Note that this function will have side effects on anything else
 * that uses timers:
 *   - Changes on pins 5, 6 may cause the delay() and
 *     millis() functions to stop working. Other timing-related
 *     functions may also be affected.
 *   - Changes on pins 9 or 10 will cause the Servo library to function
 *     incorrectly.
 * 
 * Thanks to macegr of the Arduino forums for his documentation of the
 * PWM frequency divisors. His post can be viewed at:
 *   http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1235060559/0#4
 */

void magnet_detect() //This function is called whenever a magnet/interrupt is detected
{
  count--;
}
void setPwmFrequency(int pin, int divisor) 
{
  byte mode;
  if(pin == 5 || pin == 6 || pin == 9 || pin == 10) { // Timer0 or Timer1
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 64: mode = 0x03; break;
      case 256: mode = 0x04; break;
      case 1024: mode = 0x05; break;
      default: return;
    }
    if(pin == 5 || pin == 6) { 
      TCCR0B = TCCR0B & 0b11111000 | mode; // Timer0
    } else {
      TCCR1B = TCCR1B & 0b11111000 | mode; // Timer1
    }
  } else if(pin == 3 || pin == 11) {
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 32: mode = 0x03; break;
      case 64: mode = 0x04; break;
      case 128: mode = 0x05; break;
      case 256: mode = 0x06; break;
      case 1024: mode = 0x7; break;
      default: return;
    }
    TCCR2B = TCCR2B & 0b11111000 | mode; // Timer2
  }
}

Delta_G:
setPwmFrequency(PWMPin, 8); //change Timer2 divisor to 8 gives 3.9kHz PWM freq

You set the pwm frequency to smiley face? That probably won't work.

Go read the "How to Use This Forum" post at the top of any board and you will learn how to post code correctly so that doesn't happen. You know, they tendency to read the manual first and write second is what separates the good coders from the failing ones.

What count are you getting? What were you expecting? How do you know the actual number?

I am using the Serial Monitor to watch the count. The way I have the code written is that the count starts out at 50 then gets decremented. So, I am looking for a count-down - 50, 49, 48, etc. The problem is that I am seeing some numbers skipped - for example 50, 48, 47, 46, 44,...

I don't know if there is a code problem or just a Serial Monitor refresh rate. I have looked at the output of the hall effect sensor with a scope and don't see anything that might cause a false trigger.

Anyways, for the intended purpose I need to make sure the count accurate.

You're just printing the value of count out in loop. You don't think it is possible that the drill went two revolutions between two runs of that print line? Especially given that you are printing at a snails pace (9600 baud)

The motor is not running that fast. The motor is rated at 600rpm at 100% duty cycle.
Most of the count numbers displayed on the Serial Monitor show up more than once - 50, 50, 50, 49, 49, 49, 49, 47, 47, 46, 46, 46, 44, 44, 44,...

Possibly false triggers but I didn't see anything suspicious on the scope. I'll have to take another look.

So you recommend a faster baud rate? Like I said, this is my first project and cobbled together code from other sketches.

I considered doing all the loop operations within the function that the interrupt triggers.

lsburden:
I considered doing all the loop operations within the function that the interrupt triggers.

That would be a really bad idea. ISR should be fast fast fast. Get in, do something quick, and get out so the rest of the stuff on the board can run.

Ok, so I had a few minutes to take another look.

I tried doubling the baud rate and it messed up the Serial Monitor output so couldn't watch the count output. I either keep the baud and the turtles pace of 9600 or do more research to find the answer to a problem that I don't think is an issue because this motor is only rated for 600 rpm.

Looked at the hall effect sensor output again and I don't see anything that would indicate a false trigger. The hall effect sensor I am using (TLE4905) has an integrated schmitt trigger, so should be fine.

Is it possible that any inductive kick from the MegaMoto motor controller is corrupting input signals?

Hi
When you changed the baud rate in your code, did you change the baud rate in the monitor?
Is there a need for you to generate your own PWM output rather than use an Arduino analogWrite PWM signal?
Have you got the motor suppressed with 0.1uF cap and have you got the leads to the motor and leads from the Hall Device well away from each other?

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Tom.... :slight_smile:

TomGeorge:
Hi
When you changed the baud rate in your code, did you change the baud rate in the monitor?
Is there a need for you to generate your own PWM output rather than use an Arduino analogWrite PWM signal?
Have you got the motor suppressed with 0.1uF cap and have you got the leads to the motor and leads from the Hall Device well away from each other?

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Tom.... :slight_smile:

I did not change the baud rate in the monitor. Didn't know about that.
I am using the Arduino PWM output. It drives the MegaMoto shield. I am using a pot and the map function to control PWM (speed).
I will have to look at the MegaMoto schematic to see if there is any motor suppression.
I'll see what I can do to put a schematic together.

I have another idea I will hopefully get to tomorrow.

The MegaMoto schematic is attached.
D4 and C3 are placed across the motor.

MegaMoto-v1.5-schematic.pdf (82.7 KB)

int count;
volatile int count;

The value of count is changed within the ISR and it should be typed as volatile int (or byte if the windings are less than 255).

If you really do have a multi byte variable that you are accessing in the main program, it needs to be protected so that it it not accessed while the value is being changed.

noInterrupts();
copyCount = count;
interrupts();

 if (copyCount > 0)

I cobbled together this sketch from other sketched I found with related functions and applications.

I also used the "Arduino Programming Notebook" by Brian W. Evans (attached) as a reference. On page 14 it is described that an "int" as a number without decimal points and store a 16-bit value with a range of 32,767 to -32,768. So I fugured and "int" would be appropriate.
You think it needs to be a "volatile int"?
What do you mean by "ISR"?

arduino_notebook_v1-1.pdf (382 KB)

TomGeorge:
Hi
When you changed the baud rate in your code, did you change the baud rate in the monitor?
Is there a need for you to generate your own PWM output rather than use an Arduino analogWrite PWM signal?
Have you got the motor suppressed with 0.1uF cap and have you got the leads to the motor and leads from the Hall Device well away from each other?

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Tom.... :slight_smile:

I am looking at Fritzing to draw a schematic.

lsburden:
I am looking at Fritzing to draw a schematic.

Only use fritzy if you are using their schematic editor, not their picture graphic of component layout.
Prefer hand drawn if you do not have a CAD editor.
google expresspcb
Thanks.. Tom.. :slight_smile:

In an effort to troubleshoot, I did something that I was advised not to do.
I moved the serial print count to the function called by the interrupt.
I also commented out other serial print commands as I really am only concerned with count.

The count is now accurate but does not stop at zero...it keeps going into negative numbers. The loop is supposed to disable the motor driver when the count = 0. That said, it looks to me like the program may need a new approach.

I also changed count to a "volatile int" and increased the baud now that I know that I need to update the serial monitor baud too.

/*
  Coil Winder sketch
  Potentiometer to control speed.
  TLE4095 Hall Effect Sensor as a counter.
  MegaMoto shield used to drive a DC Motor.
*/

int EnablePin = 8; 
int duty;
int PWMPin = 11;    // Timer2
int PWMPin2 = 3;
int potPin = A0;    
int potValue = 0;
volatile int count;

const byte CPin = 0;  // analog input channel
int CRaw;      // raw A/D value
float CVal;    // adjusted Amps value

void setup(){              
  // initialize the digital pin as an output.
  // Pin 13 has an LED connected on most Arduino boards:
  count = 100;
  pinMode(EnablePin, OUTPUT);   
  pinMode(PWMPin, OUTPUT);   
  pinMode(PWMPin2, OUTPUT);  
  setPwmFrequency(PWMPin, 8);  //change Timer2 divisor to 8 gives 3.9kHz PWM freq
  Serial.begin(38400);
  attachInterrupt(0, magnet_detect, RISING);//Initialize the interrupt pin (Arduino Digital pin 2)
}
void loop()
{
    // To drive the motor in H-bridge mode
    // the power chip inputs must be opposite polarity
    // and the Enable input must be HIGH

    if (count > 0)
    {
    digitalWrite(EnablePin, HIGH);    // Pin 8 high, enable motor driver
    analogWrite(PWMPin2, 0);          // Set Pin 3 low 
    potValue = analogRead(potPin);    // Read voltage from pot wiper
    duty = map(potValue, 0, 663, 0, 255);
    analogWrite (PWMPin, duty);
   // Serial.print("count = ");
   // Serial.print(count);
   // Serial.print("\t potentiometer = " );
   // Serial.print(potValue);
   // Serial.print("\t motor = ");
   // Serial.println(duty);
   // delay(2);
    }
    else
    {
    digitalWrite(EnablePin, LOW);     // Pin 8 low, disable motor driver
    }
}
        
/*
 * Divides a given PWM pin frequency by a divisor.
 * 
 * The resulting frequency is equal to the base frequency divided by
 * the given divisor:
 *   - Base frequencies:
 *      o The base frequency for pins 3, 9, 10, and 11 is 31250 Hz.
 *      o The base frequency for pins 5 and 6 is 62500 Hz.
 *   - Divisors:
 *      o The divisors available on pins 5, 6, 9 and 10 are: 1, 8, 64,
 *        256, and 1024.
 *      o The divisors available on pins 3 and 11 are: 1, 8, 32, 64,
 *        128, 256, and 1024.
 * 
 * PWM frequencies are tied together in pairs of pins. If one in a
 * pair is changed, the other is also changed to match:
 *   - Pins 5 and 6 are paired (Timer0)
 *   - Pins 9 and 10 are paired (Timer1)
 *   - Pins 3 and 11 are paired (Timer2)
 * 
 * Note that this function will have side effects on anything else
 * that uses timers:
 *   - Changes on pins 5, 6 may cause the delay() and
 *     millis() functions to stop working. Other timing-related
 *     functions may also be affected.
 *   - Changes on pins 9 or 10 will cause the Servo library to function
 *     incorrectly.
 * 
 * Thanks to macegr of the Arduino forums for his documentation of the
 * PWM frequency divisors. His post can be viewed at:
 *   http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1235060559/0#4
 */

void magnet_detect() //This function is called whenever a magnet/interrupt is detected
{
  count--;
  Serial.print("\n count = ");
  Serial.print(count);
}
void setPwmFrequency(int pin, int divisor) 
{
  byte mode;
  if(pin == 5 || pin == 6 || pin == 9 || pin == 10) { // Timer0 or Timer1
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 64: mode = 0x03; break;
      case 256: mode = 0x04; break;
      case 1024: mode = 0x05; break;
      default: return;
    }
    if(pin == 5 || pin == 6) { 
      TCCR0B = TCCR0B & 0b11111000 | mode; // Timer0
    } else {
      TCCR1B = TCCR1B & 0b11111000 | mode; // Timer1
    }
  } else if(pin == 3 || pin == 11) {
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 32: mode = 0x03; break;
      case 64: mode = 0x04; break;
      case 128: mode = 0x05; break;
      case 256: mode = 0x06; break;
      case 1024: mode = 0x7; break;
      default: return;
    }
    TCCR2B = TCCR2B & 0b11111000 | mode; // Timer2
  }
}

schematic attached

CoilWinder.pdf (26.4 KB)

Looks like the latest version of my code works. The problem is that the motor doesn't stop rotating when PMW = 0 and enable. I need to factor this in and/or electric brake.