I'm trying to count pulses from a switch. The pulses will be coming from a flowmeter, one every few seconds. I'm using interrupts. I have digital pin 2 set as pullup and the switch grounds it, triggering the interrupt routine.
See my code below:
const byte LED = 13;
const byte BUTTON = 2;
int gals;
volatile int count;
// Interrupt Service Routine (ISR)
void switchPressed ()
{
if (digitalRead (BUTTON) == HIGH)
digitalWrite (LED, LOW);
else
digitalWrite (LED, HIGH);
Serial.println("LED is on, Gerry!"); //why this prints many times??
count++;
// Serial.println(count);
} // end of switchPressed
void setup ()
{
pinMode (LED, OUTPUT); // so we can update the LED
pinMode (BUTTON,INPUT_PULLUP);
// pinMode (BUTTON,INPUT);
// digitalWrite (BUTTON, HIGH); // internal pull-up resistor
attachInterrupt (digitalPinToInterrupt(BUTTON), switchPressed, RISING); // attach interrupt handler
Serial.begin(9600);
} // end of setup
void loop ()
{
count=0;
interrupts();
delay(5000);
String dataString = "";
dataString += "The count number is: ";
dataString += String(count);
Serial.println(dataString);
noInterrupts();
}
The problem I'm having is that I get more than one count (sometimes 3 or 4) every time I hit the switch.
I'm tempted to put some delay in the interrupt routine but I've read that it's not a wise thing to do.
You can't use the delay() function in an ISR but for this simple example you could add your own version (just a while() loop) and just return or test the pin again for an active level.
If this is going to be part of a "real" program you may have to get more creative eventually.
Thing is that an ISR for a button press is seldom required, because you have to poll a global variable (set by the ISR) anyway you may as well just poll the pin directly, but I admit that an ISR that provides a clean variable to read is nice.
Hardware debounce for noisy switches is always a good thing. Most flow meters have hall sensors and should not bounce.
There are two methods to debounce in software.
A) accept the first change reading as true, and ignore/block all changes after that for a period of time to avoid multiple readings due to bounce
B) verify that the reading is unchanged and stable for a period of time and accept it after the waiting period.
If you don't have a system subject to lot of electrical noise and false interrupts, than the first method will work fine, and is very easy to implement within the ISR.
void debounceISR()
{
static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = millis();
if (interrupt_time - last_interrupt_time > debounceInterval) //declare the debounce/block out interval in setup
{
//ISR code to execute)
}
last_interrupt_time = interrupt_time;
}
INPUT_PULLUP: The internal pullup resistor is equivalent to anything from 20K to 50K on an ATmega328P. This is rather weak, but is fine with switches or sensors connected on the same PCB or close to the input pin. For your application, I would recommend using an external 1K pullup resistor. The hardware debouncer in the link by groundfungus looks good, but most likely just using a 1K pullup to VCC with a 1µF cap from pin 2 to GND would be sufficient. In this case, the RC time constant would be 1ms.
When using a pullup resistor, it's usually best to use FALLING interrupt mode. This is because most sensors are open collector, open drain or just dry switch contact that is actively driven low. The signal switches low fast, the rise time will be dependent on the RC time constant. Another benefit of using FALLING mode is that the signal will be normally HIGH, thus ground noise should not be an issue.
Thanks everybody. I'm using cattledog's code with 500ms interval. Works like a charm.
dlloyd:
INPUT_PULLUP: The internal pullup resistor is equivalent to anything from 20K to 50K on an ATmega328P. This is rather weak, but is fine with switches or sensors connected on the same PCB or close to the input pin. For your application, I would recommend using an external 1K pullup resistor.
I don't understand. For my application (switch far away from PCB with a long cable) one would expect to have some resistance in the cable. So I would think that I want a relatively large resistance in the Pullup resistor. Why would I want to decrease it from 20-50K to only 1K?
As the resistance value increases, the signal becomes more susceptible to noise. Same effect with longer cables.
Also, some switches have a minimum current rating of 1mA. 1K will draw 5mA when the signal is switched low, providing a stronger, more stable switching waveform.
EDIT: Looks like the noise was on the rising edge only ... probably due to the weak pullup. You're signal would switch low quickly, but rise slowly.
If there's noise on both edges, software debouncing becomes more complex.
Luckily, the noise must have been on the rising edge only. If on both edges, software debouncing becomes more complex.
With noise on both edges, the debounce methods which accept a stable signal for a period of time are better. As dlloyd says, these methods are more difficult, especially with an interrupt rather than with a polled button press.
However, with the methods which ignore changes for an interval, a quick button press and release all within the debounce window, will result in any triggers on the back edge being blocked as well.
Since you are using a window of 500ms, that may be what's happening in your code.
Is the use of the button with debounce only to simulate a flow meter which is yet to arrive or be integrated, or do you have some sort of flow meter based on a reed switch which really bounces?
cattledog:
Is the use of the button with debounce only to simulate a flow meter which is yet to arrive or be integrated, or do you have some sort of flow meter based on a reed switch which really bounces?
Both. I have a flowmeter with a reed switch that bounces. But it's a large 6 inch flowmeter for agricultural use and it's a little cumbersome to have it on my desk, with a fan blowing air through it while I debug my code. To simulate the pulse I'm using a button switch.
If the flow meter incorporates a reed switch, it will have a strong magnet rotating inside. You can try to mount a digital hall sensor on its outside, that will provide a signal without bouncing.
This worked really well for me. Thanks for this string.
cattledog:
Hardware debounce for noisy switches is always a good thing. Most flow meters have hall sensors and should not bounce.
There are two methods to debounce in software.
A) accept the first change reading as true, and ignore/block all changes after that for a period of time to avoid multiple readings due to bounce
B) verify that the reading is unchanged and stable for a period of time and accept it after the waiting period.
If you don't have a system subject to lot of electrical noise and false interrupts, than the first method will work fine, and is very easy to implement within the ISR.
void debounceISR()
{
static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = millis();
if (interrupt_time - last_interrupt_time > debounceInterval) //declare the debounce/block out interval in setup
{
//ISR code to execute)
}
last_interrupt_time = interrupt_time;