A number of people have been asking for a version of the DualVNH5019MotorShield library that can do 20kHz PWM frequencies with the Arduino Mega. To address this, we have made a modified version of our DualVNH5019MotorShield library that lets you use the Dual VNH5019 Motor Driver Shield with an Arduino Mega to drive motors with a 20kHz PWM. Using the 20kHz PWM should eliminate the whining sound heard when PWMing at lower frequencies, like the Arduino’s standard PWM frequency of 500Hz. This library uses pins 11 and 12 of the Arduino, which are connected to one of the Mega’s 16bit timers, timer 1.
To use this library you will need to either make some hardware modifications, or use the driver without plugging it in as a shield. As mentioned above, M1PWM and M2PWM were moved to pins 11 and 12 respectively. In addition, M2EN/DIAG was moved from pin 12 on the Arduino to pin 5. All other connections between the board and shield are the same.
Hardware modifications can be made by cutting traces on the shield and running a wire from a different Arduino pin to the appropriate trace, as explained in the user’s guide.
The new version of the library should be compatible with code written to use the old version, but the include statement will need to be changed.
DualVNH5019MotorShieldMega.h
#ifndef DualVNH5019MotorShield_h
#define DualVNH5019MotorShield_h
#include <Arduino.h>
class DualVNH5019MotorShield
{
public:
// CONSTRUCTORS
DualVNH5019MotorShield(); // Default pin selection.
DualVNH5019MotorShield(unsigned char INA1, unsigned char INB1, unsigned char EN1DIAG1, unsigned char CS1,
unsigned char INA2, unsigned char INB2, unsigned char EN2DIAG2, unsigned char CS2); // User-defined pin selection.
// PUBLIC METHODS
void init(); // Initialize TIMER 1 (or timer 3 for mega) set the PWM to 20kHZ.
void setM1Speed(int speed); // Set speed for M1.
void setM2Speed(int speed); // Set speed for M2.
void setSpeeds(int m1Speed, int m2Speed); // Set speed for both M1 and M2.
void setM1Brake(int brake); // Brake M1.
void setM2Brake(int brake); // Brake M2.
void setBrakes(int m1Brake, int m2Brake); // Brake both M1 and M2.
unsigned int getM1CurrentMilliamps(); // Get current reading for M1.
unsigned int getM2CurrentMilliamps(); // Get current reading for M2.
unsigned char getM1Fault(); // Get fault reading from M1.
unsigned char getM2Fault(); // Get fault reading from M2.
private:
unsigned char _INA1;
unsigned char _INB1;
#if defined(__AVR_ATmega168__)|| defined(__AVR_ATmega328P__)
static const unsigned char _PWM1 = 9;
static const unsigned char _PWM2 = 10;
#endif
#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
static const unsigned char _PWM1 = 11;
static const unsigned char _PWM2 = 12;
#endif
unsigned char _EN1DIAG1;
unsigned char _CS1;
unsigned char _INA2;
unsigned char _INB2;
unsigned char _EN2DIAG2;
unsigned char _CS2;
};
#endif
DualVNH5019MotorShieldMega.cpp
#include "DualVNH5019MotorShieldMega.h"
// Constructors ////////////////////////////////////////////////////////////////
DualVNH5019MotorShield::DualVNH5019MotorShield()
{
//Pin map
_INA1 = 2;
_INB1 = 4;
_EN1DIAG1 = 6;
_CS1 = A0;
_INA2 = 7;
_INB2 = 8;
_EN2DIAG2 = 5;
_CS2 = A1;
}
DualVNH5019MotorShield::DualVNH5019MotorShield(unsigned char INA1, unsigned char INB1, unsigned char EN1DIAG1, unsigned char CS1,
unsigned char INA2, unsigned char INB2, unsigned char EN2DIAG2, unsigned char CS2)
{
//Pin map
//PWM1 and PWM2 cannot be remapped because the library assumes PWM is on timer1
_INA1 = INA1;
_INB1 = INB1;
_EN1DIAG1 = EN1DIAG1;
_CS1 = CS1;
_INA2 = INA2;
_INB2 = INB2;
_EN2DIAG2 = EN2DIAG2;
_CS2 = CS2;
}
// Public Methods //////////////////////////////////////////////////////////////
void DualVNH5019MotorShield::init()
{
// Define pinMode for the pins and set the frequency for timer1.
pinMode(_INA1,OUTPUT);
pinMode(_INB1,OUTPUT);
pinMode(_PWM1,OUTPUT);
pinMode(_EN1DIAG1,INPUT);
pinMode(_CS1,INPUT);
pinMode(_INA2,OUTPUT);
pinMode(_INB2,OUTPUT);
pinMode(_PWM2,OUTPUT);
pinMode(_EN2DIAG2,INPUT);
pinMode(_CS2,INPUT);
#if defined(__AVR_ATmega168__)|| defined(__AVR_ATmega328P__)
// Timer 1 configuration
// prescaler: clockI/O / 1
// outputs enabled
// phase-correct PWM
// top of 400
//
// PWM frequency calculation
// 16MHz / 1 (prescaler) / 2 (phase-correct) / 400 (top) = 20kHz
TCCR1A = 0b10100000;
TCCR1B = 0b00010001;
ICR1 = 400;
#endif
#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// Mega board specific stuff here - assumes assigning timer3, using pins 3 &5
TCCR1A = 0b10100000; // Mega pin 5
TCCR1B = 0b00010001; // Mega pin 3
ICR1 = 400;
#endif
}
// Set speed for motor 1, speed is a number betwenn -400 and 400
void DualVNH5019MotorShield::setM1Speed(int speed)
{
unsigned char reverse = 0;
if (speed < 0)
{
speed = -speed; // Make speed a positive quantity
reverse = 1; // Preserve the direction
}
if (speed > 400) // Max PWM dutycycle
speed = 400;
#if defined(__AVR_ATmega168__)|| defined(__AVR_ATmega328P__)
OCR1A = speed;
#elif defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// Mega board specific stuff here - assumes assigning timer3, using pins 3 &5
OCR1A = speed;
#else
analogWrite(_PWM1,speed * 51 / 80); // default to using analogWrite, mapping 400 to 255
#endif
if (reverse)
{
digitalWrite(_INA1,LOW);
digitalWrite(_INB1,HIGH);
}
else
{
digitalWrite(_INA1,HIGH);
digitalWrite(_INB1,LOW);
}
}
// Set speed for motor 2, speed is a number betwenn -400 and 400
void DualVNH5019MotorShield::setM2Speed(int speed)
{
unsigned char reverse = 0;
if (speed < 0)
{
speed = -speed; // make speed a positive quantity
reverse = 1; // preserve the direction
}
if (speed > 400) // Max
speed = 400;
#if defined(__AVR_ATmega168__)|| defined(__AVR_ATmega328P__)
OCR1B = speed;
#elif defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// Mega board specific stuff here - assumes assigning timer3, using pins 3 &5
OCR1B = speed;
#else
analogWrite(_PWM2,speed * 51 / 80); // default to using analogWrite, mapping 400 to 255
#endif
if (reverse)
{
digitalWrite(_INA2,LOW);
digitalWrite(_INB2,HIGH);
}
else
{
digitalWrite(_INA2,HIGH);
digitalWrite(_INB2,LOW);
}
}
// Set speed for motor 1 and 2
void DualVNH5019MotorShield::setSpeeds(int m1Speed, int m2Speed)
{
setM1Speed(m1Speed);
setM2Speed(m2Speed);
}
// Brake motor 1, brake is a number between 0 and 400
void DualVNH5019MotorShield::setM1Brake(int brake)
{
// normalize brake
if (brake < 0)
{
brake = -brake;
}
if (brake > 400) // Max brake
brake = 400;
digitalWrite(_INA1, LOW);
digitalWrite(_INB1, LOW);
#if defined(__AVR_ATmega168__)|| defined(__AVR_ATmega328P__)
OCR1A = brake;
#elif defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// Mega board specific stuff here - assumes assigning timer3, using pins 3 &5
OCR1A = brake;
#else
analogWrite(_PWM1,brake * 51 / 80); // default to using analogWrite, mapping 400 to 255
#endif
}
// Brake motor 2, brake is a number between 0 and 400
void DualVNH5019MotorShield::setM2Brake(int brake)
{
// normalize brake
if (brake < 0)
{
brake = -brake;
}
if (brake > 400) // Max brake
brake = 400;
digitalWrite(_INA2, LOW);
digitalWrite(_INB2, LOW);
#if defined(__AVR_ATmega168__)|| defined(__AVR_ATmega328P__)
OCR1B = brake;
#elif defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// Mega board specific stuff here - assumes assigning timer3, using pins 3 &5
OCR1B = brake;
#else
analogWrite(_PWM2,brake * 51 / 80); // default to using analogWrite, mapping 400 to 255
#endif
}
// Brake motor 1 and 2, brake is a number between 0 and 400
void DualVNH5019MotorShield::setBrakes(int m1Brake, int m2Brake)
{
setM1Brake(m1Brake);
setM2Brake(m2Brake);
}
// Return motor 1 current value in milliamps.
unsigned int DualVNH5019MotorShield::getM1CurrentMilliamps()
{
// 5V / 1024 ADC counts / 144 mV per A = 34 mA per count
return analogRead(_CS1) * 34;
}
// Return motor 2 current value in milliamps.
unsigned int DualVNH5019MotorShield::getM2CurrentMilliamps()
{
// 5V / 1024 ADC counts / 144 mV per A = 34 mA per count
return analogRead(_CS2) * 34;
}
// Return error status for motor 1
unsigned char DualVNH5019MotorShield::getM1Fault()
{
return !digitalRead(_EN1DIAG1);
}
// Return error status for motor 2
unsigned char DualVNH5019MotorShield::getM2Fault()
{
return !digitalRead(_EN2DIAG2);
}
DualVNH5019MotorShieldMega.zip (3.06 KB)