diff --git a/Examples/CMakeLists.txt b/Examples/CMakeLists.txt
index c3c3925d..ab393e0f 100644
--- a/Examples/CMakeLists.txt
+++ b/Examples/CMakeLists.txt
@@ -14,6 +14,7 @@ add_subdirectory(PropWare_FullDuplexSerial)
add_subdirectory(PropWare_HD44780)
add_subdirectory(PropWare_I2C)
add_subdirectory(PropWare_I2CSlave)
+add_subdirectory(PropWare_Keypad)
add_subdirectory(PropWare_L3G)
add_subdirectory(PropWare_MAX6675)
add_subdirectory(PropWare_MCP2515)
diff --git a/Examples/PropWare_Keypad/CMakeLists.txt b/Examples/PropWare_Keypad/CMakeLists.txt
new file mode 100644
index 00000000..98210c2f
--- /dev/null
+++ b/Examples/PropWare_Keypad/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 3.3)
+find_package(PropWare REQUIRED)
+
+project(PropWareKeypad_Demo)
+
+create_simple_executable(${PROJECT_NAME} Keypad_Demo.cpp)
diff --git a/Examples/PropWare_Keypad/Keypad_Demo.cpp b/Examples/PropWare_Keypad/Keypad_Demo.cpp
new file mode 100644
index 00000000..ff806db8
--- /dev/null
+++ b/Examples/PropWare_Keypad/Keypad_Demo.cpp
@@ -0,0 +1,71 @@
+/**
+ * @file Keypad_Demo.cpp
+ *
+ * @author David Zemon
+ *
+ * @copyright
+ * The MIT License (MIT)
+ *
Copyright (c) 2013 David Zemon
+ *
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
+ * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
The above copyright notice and this permission notice shall be included in all copies or substantial portions
+ * of the Software.
+ *
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include
+#include
+
+using namespace PropWare;
+
+/**
+ * @example PropWare_Keypad/Keypad_Demo.cpp
+ *
+ * Read keys from a common 4x4 keypad to interact with a user
+ *
+ * @include PropWare_Keypad/CMakeLists.txt
+ */
+int main() {
+ Keypad::Key keys[] = {
+ Keypad::Key('1'), Keypad::Key('2'), Keypad::Key('3'), Keypad::Key('A'),
+ Keypad::Key('4'), Keypad::Key('5'), Keypad::Key('6'), Keypad::Key('B'),
+ Keypad::Key('7'), Keypad::Key('8'), Keypad::Key('9'), Keypad::Key('C'),
+ Keypad::Key('*'), Keypad::Key('0'), Keypad::Key('#'), Keypad::Key('D')
+ };
+ const Pin rowPins[] = {
+ Pin(Pin::P19),
+ Pin(Pin::P20),
+ Pin(Pin::P21),
+ Pin(Pin::P22),
+ };
+ const Pin columnPins[] = {
+ Pin(Pin::P26),
+ Pin(Pin::P25),
+ Pin(Pin::P24),
+ Pin(Pin::P23),
+ };
+
+ Keypad keypad(keys, rowPins, columnPins);
+
+ while (1) {
+ //for (auto key : keys) {
+ // if (keypad.is_pressed(key.get_character()))
+ // pwOut << key.get_character() << '\n';
+ //}
+
+ keypad.get_keys();
+ for (size_t i = 0; i < Utility::size_of_array(keys); ++i) {
+ if (0 == i % 4)
+ pwOut << '\n';
+ pwOut << (keys[i].get_state() ? keys[i].get_character() : ' ') << ' ';
+ }
+
+ waitcnt(100*MILLISECOND + CNT);
+ }
+}
diff --git a/PropWare/CMakeLists.txt b/PropWare/CMakeLists.txt
index 4217d38f..d004116a 100644
--- a/PropWare/CMakeLists.txt
+++ b/PropWare/CMakeLists.txt
@@ -12,6 +12,7 @@ set(PROPWARE_SOURCES
${CMAKE_CURRENT_LIST_DIR}/gpio/pin.h
${CMAKE_CURRENT_LIST_DIR}/gpio/port.h
${CMAKE_CURRENT_LIST_DIR}/gpio/simpleport.h
+ ${CMAKE_CURRENT_LIST_DIR}/hmi/input/keypad.h
${CMAKE_CURRENT_LIST_DIR}/hmi/input/scancapable.h
${CMAKE_CURRENT_LIST_DIR}/hmi/input/scanner.cpp
${CMAKE_CURRENT_LIST_DIR}/hmi/input/scanner.h
diff --git a/PropWare/hmi/input/keypad.h b/PropWare/hmi/input/keypad.h
new file mode 100644
index 00000000..87632684
--- /dev/null
+++ b/PropWare/hmi/input/keypad.h
@@ -0,0 +1,153 @@
+/**
+ * @file PropWare/hmi/input/keypad.h
+ *
+ * @author David Zemon
+ *
+ * @copyright
+ * The MIT License (MIT)
+ *
Copyright (c) 2013 David Zemon
+ *
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
+ * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
The above copyright notice and this permission notice shall be included in all copies or substantial portions
+ * of the Software.
+ *
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include
+#include
+
+namespace PropWare {
+
+class Keypad {
+ public:
+ class Key {
+ public:
+ friend class Keypad;
+
+ public:
+ Key(const char character)
+ : character(character),
+ state(false) {
+ }
+
+ char get_character() const {
+ return this->character;
+ }
+
+
+ bool get_state() const {
+ return this->state;
+ }
+
+ protected:
+ char character;
+ bool state;
+ };
+
+ public:
+ /**
+ * MAPSIZE is the number of rows (times 16 columns)
+ */
+ static const unsigned int DEFAULT_BOUNCE_TIME_MS = 10;
+
+ public:
+ /**
+ * Allows custom m_keymap, pin configuration, and keypad sizes
+ */
+ template
+ Keypad(Key keys[], const Pin (&rowPins)[ROW_SIZE], const Pin (&columnPins)[COLUMN_SIZE])
+ : m_keys(keys),
+ m_rowPins(rowPins),
+ m_columnPins(columnPins),
+ m_rows(ROW_SIZE),
+ m_columns(COLUMN_SIZE),
+ m_keyCount(this->m_rows * this->m_columns),
+ m_startTime(CNT - DEFAULT_BOUNCE_TIME_MS * MILLISECOND) {
+ this->set_debounce_time(Keypad::DEFAULT_BOUNCE_TIME_MS);
+ }
+
+ /**
+ * Populate the key list
+ */
+
+ bool is_pressed(const char character) {
+ this->get_keys();
+
+ for (uint_fast8_t i = 0; i < this->m_keyCount; i++)
+ if (character == this->m_keys[i].character) {
+ return this->m_keys[i].state;
+ }
+ return false;
+ }
+
+ bool get_keys() {
+ bool keyActivity = false;
+
+ // Limit how often the keypad is scanned. This makes the loop() run 10 times as fast.
+ if ((CNT - this->m_startTime) > this->m_debounceTime) {
+ this->scan_keys();
+ this->m_startTime = CNT;
+ }
+
+ return keyActivity;
+ }
+
+ /**
+ * Minimum debounceTime is 1 mS.
+ */
+ void set_debounce_time(const uint32_t debounceMs) {
+ if (!debounceMs)
+ this->m_debounceTime = MILLISECOND;
+ else
+ this->m_debounceTime = debounceMs * MILLISECOND;
+ }
+
+ private:
+
+ /**
+ * Hardware scan
+ */
+ void scan_keys() {
+ for (uint_fast8_t row = 0; row < this->m_rows; row++) {
+ this->m_rowPins[row].set_dir_in();
+ this->m_rowPins[row].high();
+ }
+
+ // bitMap stores ALL the keys that are being pressed.
+ for (uint_fast8_t column = 0; column < this->m_columns; ++column) {
+ // Begin column pulse output.
+ this->m_columnPins[column].low();
+ this->m_columnPins[column].set_dir_out();
+
+ for (uint_fast8_t row = 0; row < this->m_rows; ++row) {
+ const uint_fast8_t keyIndex = column * 4 + row;
+ this->m_keys[keyIndex].state = !this->m_rowPins[row].read();
+ }
+
+ // Set pin to high impedance input. Effectively ends column pulse.
+ this->m_columnPins[column].high();
+ this->m_columnPins[column].set_dir_in();
+ }
+ }
+
+ private:
+ Key *m_keys;
+ const Pin *m_rowPins;
+ const Pin *m_columnPins;
+ const uint_fast8_t m_rows;
+ const uint_fast8_t m_columns;
+ const uint_fast8_t m_keyCount;
+
+ uint32_t m_debounceTime;
+ volatile uint32_t m_startTime;
+};
+
+}