That works much better, thank you
Another somewhat interesting glitch has appeared...
on Pin3, I'm mapping the received RPM to a PWM value, which I feed into a 100uA meter via a large resistor. This gives me an approximate RPM in analogue form.
If I leave it unplugged, the RPMs work exactly as advertised. If I plug it in, the RPM goes crazy, typically reading 30,000+, maxing at 75000. Nothing is changing in the software...
Hardware wise, I'm using Pin2 for the Hall sensor, which is active low. The RPM_Sense triggers on a rising value, i.e. just after the magnet has passed under the sensor.
I'm using Pin3 for my PWM output, this is wired through a ~50K resistor into a moving-coil 100uA meter and back to gnd.
My code, currently, is as follows (some of the comments are out of date):
//-------------------------------------------------------------------------------------
// ArduECU version 0.01 alpha
// Compiled with Arduino v0018
//
// Thanks to the members of the arduino.cc forum for their assistance
// Many comments removed to fit sketch into forum posting box...
extern volatile unsigned long timer0_overflow_count; // Record the most recent timer ticks
volatile boolean ticks_valid; // Indicates a valid reading
volatile unsigned long ticks_per_rev; // Number of ticks counted in the current revolution
unsigned long msSinceRPMReading; // Number of mSec since the last rpm_sense (used to spot zero RPM)
float lastRPM, peakRPM; // Most recent RPM reading, and highest RPM recorded since last reset
const float __TICKS_TO_RPMS = 15e6; // Convert clock ticks to RPM by dividng ticks into this number
// The number will change if there are more magnets in an rpm
// (e.g. 2 magnets = 29296.875)
const unsigned int __WAIT_BEFORE_ZERO_RPM = 2000; // Number of mS to wait for an rpm_sense before assunming RPM = 0.
const int __REV_GAUGE_PIN = 3; // 100uA meter on this pin, with 50K current-limiting resistor
unsigned long msSinceSend; // mSec when the last data was sent to the serial port
const unsigned int __WAIT_BETWEEN_SENDS = 1000; // Number of mS to wait before sending a batch of data.
void setup()
{
Serial.begin(9600); // Initialise serial comms.
msSinceSend = millis(); // Data sent counter
attachInterrupt(0, rpm_sense, RISING); // RPM sense will cause an interrupt on pin2
msSinceRPMReading = 0; // If more than 2000 (i.e. 2 seconds),
// then RPMs can be assumed to be zero (< 15rpm
// at most, with a single magnet, no small IC
// can run that slowly).
lastRPM = 0; // Current RPM to zero
peakRPM = 0; // Max recorded RPM to zero
pinMode(__REV_GAUGE_PIN,OUTPUT); // Set pin 3 to be output
}
// ------------------------------------------------------------------------------------------------
// FUNCTION: RPM-SENSE
//
// Called whenever the RPM sense signal rises. In my setup, the hall effect switch is normally
// high, goes low when a south pole is introduced to the sensor, and rises back to high as the
// magnet goes away. Thus, the RPM sense is called on the trailing edge of the magnet.
//
// Version Date By Comment
// -------|-----------|---|------------------------------------------------------------
// 0.01a 26-May-2010 JAV Created (with an assist from BenF from the arduino.cc forum
//
// ------------------------------------------------------------------------------------------------
void rpm_sense()
{
static unsigned long pre_count; // Last timer0_overflow_count value
unsigned long ticks_now; //
ticks_now = timer0_overflow_count; // Read the timer
byte t = TCNT0;
if ((TIFR0 & _BV(TOV0)) && (t<255))
ticks_now++;
ticks_now = (ticks_now << 8) + t;
if (pre_count == 0) { // First time around the loop?
pre_count = ticks_now; // Yes - set the precount, don't use this number.
} else {
ticks_per_rev = ticks_now - pre_count; // No - calculate the number of ticks...
pre_count = ticks_now; // ...reset the counter...
ticks_valid = true; // ...and flag the change up.
}
}
void loop()
{
unsigned long thisMillis = millis(); // Read the time once
// Calculate RPM
if (ticks_valid) { // Only come here if we have a valid RPM reading
unsigned long thisTicks;
noInterrupts();
thisTicks = ticks_per_rev;
ticks_valid = false;
interrupts();
lastRPM = __TICKS_TO_RPMS / thisTicks; // Convert ticks to RPMs
ticks_valid = false; // Reset the flag.
msSinceRPMReading = thisMillis; // Set the time we read the RPM.
if (lastRPM > peakRPM)
peakRPM = lastRPM; // New peak RPM
} else {
// No tick this loop - is it more than X seconds since the last one?
if (thisMillis - msSinceRPMReading > __WAIT_BEFORE_ZERO_RPM) {
lastRPM = 0; // At least 2s since last RPM-sense, so assume zero RPMs
msSinceRPMReading = thisMillis; // Reset counter
}
}
// Calculate temperatures
// not implemented yet
// Is it time to send the data?
if (thisMillis < msSinceSend) // If thisMillis has recycled, reset it
msSinceSend = millis();
if (thisMillis - msSinceSend > __WAIT_BETWEEN_SENDS) {
// Yes: Build the serial output...
// For now, send everything plaintext. Maybe compression would be a good thing, later down the line...
Serial.print("uECUv0.01|"); // Send ID - ditch this for something smaller
Serial.print(lastRPM); // Current RPMs
Serial.print("|"); // Field Separator
Serial.print(peakRPM); // Peak RPMs
Serial.print("|"); // Field Separator
Serial.print(0); // Temperature #1 (water in)
Serial.print("|"); // Field Separator
Serial.print(0); // Temperature #2 (water out)
Serial.print("|"); // Field Separator
Serial.print(0); // Temperature #3 (exhaust gas)
// Debugging
// Serial.print("|"); // Field Separator
// Serial.print(ticks_per_rev); // clock ticks in the last rpm
// That'll do for bnow
Serial.println(); // EOL.
msSinceSend = millis(); // Reset the timer
if (Serial.available()) {
char cmd = Serial.read();
if (char('R') == cmd) {
peakRPM=0;
}
Serial.flush();
}
}
// Set the RPM gauge value in real-time...
if (lastRPM < 1000) {
// Sub-1000 RPMs, map to between 0-255
analogWrite(__REV_GAUGE_PIN,map(lastRPM,0,1000,0,255));
} else {
// If RPMs > 1000, write 255 (=meter FSD); but we've got bigger problems if the RPMs ever get this high...
analogWrite(__REV_GAUGE_PIN,255);
}
}
I've probably done something silly, but I don't understand why it only goes bonkers when the meter is actually plugged in.