I have adopted an old car instrument cluster and I have gotten the air-core meter (the speedometer, and tachometer) working with the Arduino Duemilanove using PWM and an h-bridge.
I am using the circuit described here: [1] , with some minor Arduino side pin changes (to keep the other PWM pins free):
1IN is attached to pin 2 on the Arduino (same as the schematic).
2IN is attached to pin 4 on the Arduino.
3IN is attached to pin 7 on the Arduino.
4IN is attached to pin 8 on the Arduino.
The "sine" coil is attached to the green (1OUT to SIN+) and orange (2OUT to SIN-) motor wires and the "cosine" coil to the purple (3OUT to COS+) and red (4OUT to COS-) motor wires.
It seems to work very well for the most part.
But as I increment over the 360 degrees of the meter, at the 0, 90, 180, and 270 degree points, the needle appears hesitate for a significant fraction of a second (over the course of several steps).
I am calculating the sine and cosine of the angle I want the meter to be at, multiplying that by 255 (the 100% duty cycle or 5 volts), and set the polarity of the coil depending on the sign of the result. I can drive the meter fairly smoothly through 360 degrees without any problem, except the hesitations at/near those angles.
I thought the h-bridge chip might be too slow in switching polarity, but if I am reading the datasheet [2] correctly (though I may not be), it should have a switching speed of up to about 5 kHz. That should be far faster than I would expect a human to be able to perceive. Is the L293D really that fast?
I have also tried making the code that changes the polarity pins faster (by exchanging the digitalWrite for PORTD and PORTB commands) based on my reading it should be executing in microseconds, but it didn't seem to make a noticeable difference.
I have tried using the Timer1 module ([3]) to increase the resolution of the analog outputs to 1023, but the problem still persists.
I have searched a variety of web sites, and while I find references to driving air-core movements using microcontrollers and PWM, no one seems to have the problem I am describing. All the searches seem to indicate I am doing the right thing. But perhaps I do not know the right terms to use in my googling.
I suspect it is because the polarity reversal is too slow at those transition points, either because of some software silliness I am doing, or because the L293D can't switch the polarity as fast as the Arduino can set the pins. But I don't know how to check that. Even if it was the case, how could I make the polarity reversal happen faster?
Any ideas what I am doing wrong?
[1] Link in next post
[2] Link in next post
[3] Link in next post
Here is the code I am using:
//the mathematical constant pi, because we are using radians instead of degrees.
// 360 degrees = 2*pi, 180 = pi, 90 = pi/2, etc.
const float pi = 3.14159;
const float stepSize = 0.01; //This is the step size for incrementing the angle (in radians)
//this is the maximum resolution of the PWM output.
const int maxAnalogRes = 255;
/* Define the pins the meter is connected to. We need 1 PWM to vary the voltage in
* each coil, and two more pins to reverse the polarity in that coil. So a total
* of 6 pins (3 per coil).
*/
const int aircore1SinPin = 9; //this controls the voltage to the "sine" coil
const int aircore1CosPin = 10; //this controls the voltage to the "cosine" coil
const int aircore1SinDirPin1 = 2; //these two control the polarity to the "sine" coil
const int aircore1SinDirPin2 = 4;
const int aircore1CosDirPin1 = 7; //these two control the polarity to the "cosine" coil
const int aircore1CosDirPin2 = 8;
// milliseconds between each angular step.
int delayTime = 40;
void setup()
{
//Set the pins to OUTPUT
pinMode(aircore1SinPin, OUTPUT);
pinMode(aircore1CosPin, OUTPUT);
pinMode(aircore1SinDirPin1, OUTPUT);
pinMode(aircore1SinDirPin2, OUTPUT);
pinMode(aircore1CosDirPin1, OUTPUT);
pinMode(aircore1CosDirPin2, OUTPUT);
// Set the initial direction as "forward". Of course it could be "reverse" depending on which lead you connected :-).
digitalWrite(aircore1SinDirPin1, HIGH);
digitalWrite(aircore1SinDirPin2, LOW);
digitalWrite(aircore1CosDirPin1, HIGH);
digitalWrite(aircore1CosDirPin2, LOW);
}
void loop() // run over and over again
{
//Rotate 0 through 360 degrees (OK, really 2*pi radians, but who cares?)
for(float i = 0; i < (2*pi); i = i + stepSize){
setMeterPosition(aircore1SinPin, aircore1SinDirPin1, aircore1SinDirPin2, aircore1CosPin, aircore1CosDirPin1, aircore1CosDirPin2, i);
delay(delayTime); // waits for delayTime milliseconds
}
//Rotate 360 through 0 degrees
for(float i = (2*pi); i > 0; i = i - stepSize){
setMeterPosition(aircore1SinPin, aircore1SinDirPin1, aircore1SinDirPin2, aircore1CosPin, aircore1CosDirPin1, aircore1CosDirPin2, i);
delay(delayTime); // waits for delayTime milliseconds
}
}
// setMeterPosition() - put it at the angle in radians
void setMeterPosition(int sinPin, int sinDirPin1, int sinDirPin2, int cosPin, int cosDirPin1, int cosDirPin2, float pos){
// Calculate the voltage on the PWM pins based on the angle we want
float sinCoilValue = maxAnalogRes*sin(pos);
float cosCoilValue = maxAnalogRes*cos(pos);
// change the polarity of the coil depending on the sign of the voltage level
if (sinCoilValue<=0) {
digitalWrite(sinDirPin1, LOW); //Change to LOW if using relays
digitalWrite(sinDirPin2, HIGH); //comment this line out if using relays
} else {
digitalWrite(sinDirPin1, HIGH); //Change to HIGH if using relays
digitalWrite(sinDirPin2, LOW); //comment this line out if using relays
}
if (cosCoilValue<=0) {
digitalWrite(cosDirPin1, LOW); //Change to LOW if using relays
digitalWrite(cosDirPin2, HIGH); //comment this line out if using relays
} else {
digitalWrite(cosDirPin1, HIGH); //Change to HIGH if using relays
digitalWrite(cosDirPin2, LOW); //comment this line out if using relays
}
// take the absolute value because analogWrite doesn't take negatives
sinCoilValue = abs(sinCoilValue);
cosCoilValue = abs(cosCoilValue);
analogWrite(sinPin, sinCoilValue);
analogWrite(cosPin, cosCoilValue);
}