Library for detecting button clicks, doubleclicks and long press pattern on a single button.

Dependents:   OneButton_Example ABBlind

Revision:
1:fdf67f893a5c
Parent:
0:62d614796022
diff -r 62d614796022 -r fdf67f893a5c OneButton.cpp
--- a/OneButton.cpp	Wed Aug 31 10:09:43 2016 +0000
+++ b/OneButton.cpp	Thu Sep 01 09:21:00 2016 +0000
@@ -1,3 +1,6 @@
+// Modifyed to support mbed environment, rewrite the state machine.
+// by Zibin Zheng <znbin@qq.com>
+
 // -----
 // OneButton.cpp - Library for detecting button clicks, doubleclicks and long press pattern on a single button.
 // This class is implemented for use with the Arduino environment.
@@ -5,45 +8,45 @@
 // This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
 // More information on: http://www.mathertel.de/Arduino
 // -----
-// Changelog: see OneButton.h
-// -----
+
 
 #include "OneButton.h"
 
+
 // ----- Initialization and Default Values -----
 
-OneButton::OneButton(PinName pin, int activeLow)
+OneButton::OneButton(PinName pin, bool active_level)
 {
 	btn_pin = new DigitalIn((PinName)pin);      // sets the MenuPin as input
-	_pin = pin;
+
+	timer = new Timer;
+	timer->start();
 
-	_clickTicks = 300;        // number of millisec that have to pass by before a click is detected.
+	_clickTicks = 400;        // number of millisec that have to pass by before a click is detected.
 	_pressTicks = 800;       // number of millisec that have to pass by before a long button press is detected.
+	_debounceTicks = 10;
 
 	_state = 0; // starting with state 0: waiting for button to be pressed
 	_isLongPressed = false;  // Keep track of long press state
 
-	if (activeLow) {
-		// button connects ground to the pin when pressed.
-		_buttonReleased = HIGH; // notPressed
-		_buttonPressed = LOW;
-//		digitalWrite(pin, HIGH);   // turn on pullUp resistor
-		btn_pin->mode(PullUp);
+	button_level = (bool)(btn_pin->read());
+	active = active_level;
+
+	if (active_level) {
+		// button connects VCC to the pin when pressed.
+		btn_pin->mode(PullDown);
 
 	} else {
-		// button connects VCC to the pin when pressed.
-		_buttonReleased = LOW;
-		_buttonPressed = HIGH;
-	} // if
+		// turn on pullUp resistor
+		btn_pin->mode(PullUp);
+	}
 
-
+	_clickFunc = NULL;
+	_pressFunc = NULL;
 	_doubleClickFunc = NULL;
-	_pressFunc = NULL;
 	_longPressStartFunc = NULL;
 	_longPressStopFunc = NULL;
 	_duringLongPressFunc = NULL;
-	
-	startMillis ();
 } // OneButton
 
 
@@ -106,73 +109,86 @@
 	return _isLongPressed;
 }
 
+/**
+  * @brief  Button driver core function, driver state machine.
+  * @param  handle: the button handle strcut.
+  * @retval None
+  */
 void OneButton::tick(void)
 {
-	// Detect the input information
-//	int buttonLevel = digitalRead(_pin); // current button signal.
-	int buttonLevel = btn_pin->read(); // current button signal.
-	unsigned long now = millis(); // current (relative) time in msecs.
+	int read_gpio_level = btn_pin->read(); // current button signal.
 
-	// Implementation of the state machine
-	if (_state == 0) { // waiting for menu pin being pressed.
-		if (buttonLevel == _buttonPressed) {
-			_state = 1; // step to state 1
-			_startTime = now; // remember starting time
-		} // if
+	/*------------button debounce handle---------------*/
+	if(ticks != timer->read_ms()) { //1ms scan again.
+		if(read_gpio_level != button_level) {
+			//continue read 'debounce_cnt' times same new level change
+			if(++_debounce_cnt >= _debounceTicks) {
+				button_level = read_gpio_level;
+				_debounce_cnt = 0;
+			}
 
-	} else if (_state == 1) { // waiting for menu pin being released.
+		} else { //leved not change ,counter reset.
+			_debounce_cnt = 0;
+		}
+	}
 
-		if ((buttonLevel == _buttonReleased) && ((unsigned long)(now - _startTime) < _debounceTicks)) {
-			// button was released to quickly so I assume some debouncing.
-			// go back to state 0 without calling a function.
-			_state = 0;
+	//current (relative) time in msecs.
+	ticks = (uint16_t)(timer->read_ms());
 
-		} else if (buttonLevel == _buttonReleased) {
-			_state = 2; // step to state 2
+	/*-----------------State machine-------------------*/
+	switch (_state) {
+	case 0:
+		if(button_level == active) {	//start press
+			if(_clickFunc) _clickFunc();
+			timer->reset();
+			_state = 1;
+		}
+		break;
 
-		} else if ((buttonLevel == _buttonPressed) && ((unsigned long)(now - _startTime) > _pressTicks)) {
-			_isLongPressed = true;  // Keep track of long press state
-			if (_pressFunc) _pressFunc();
-			if (_longPressStartFunc) _longPressStartFunc();
-			if (_duringLongPressFunc) _duringLongPressFunc();
-			_state = 6; // step to state 6
+	case 1:
+		if(button_level != active) { //released
+			_state = 2;
 
-		} else {
-			// wait. Stay in this state.
-		} // if
+		} else if(ticks > _pressTicks) {
+			if(_longPressStartFunc) _longPressStartFunc();
+			_isLongPressed = 1;
+			_state = 5;
+		}
+		break;
 
-	} else if (_state == 2) { // waiting for menu pin being pressed the second time or timeout.
-		if ((unsigned long)(now - _startTime) > _clickTicks) {
-			// this was only a single short click
-			if (_clickFunc) _clickFunc();
-			_state = 0; // restart.
+	case 2:
+		if(ticks > _clickTicks) {	//released
+			//press event
+			if(_pressFunc) _pressFunc();	//press event
+			_state = 0;	//reset
 
-		} else if (buttonLevel == _buttonPressed) {
-			_state = 3; // step to state 3
-		} // if
+		} else if(button_level == active) { //press again
+			if(_clickFunc) _clickFunc();
+			_state = 3;
+		}
+		break;
 
-	} else if (_state == 3) { // waiting for menu pin being released finally.
-		if (buttonLevel == _buttonReleased) {
-			// this was a 2 click sequence.
-			if (_doubleClickFunc) _doubleClickFunc();
-			_state = 0; // restart.
-		} // if
+	case 3:	//repeat press pressing
+		if(button_level != active) {	//double releasd
+			//double click event
+			if(_doubleClickFunc) _doubleClickFunc();
+			_state = 0;
+		}
+		break;
 
-	} else if (_state == 6) { // waiting for menu pin being release after long press.
-		if (buttonLevel == _buttonReleased) {
-			_isLongPressed = false;  // Keep track of long press state
+	case 5:
+		if(button_level == active) {
+			//continue hold trigger
+			if(_duringLongPressFunc) _duringLongPressFunc();
+
+		} else { //releasd
 			if(_longPressStopFunc) _longPressStopFunc();
-			_state = 0; // restart.
-		} else {
-			// button is being long pressed
-			_isLongPressed = true; // Keep track of long press state
-			if (_duringLongPressFunc) _duringLongPressFunc();
-		} // if
-
-	} // if
-} // OneButton.tick()
-
+			_isLongPressed = 0;
+			_state = 0; //reset
+		}
+		break;
+	}
+}
 
 // end.
 
-