From 19bbb186e72dae8f534ad8b3d398e96323d3ec28 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Thu, 2 Mar 2023 23:01:44 +0000 Subject: [PATCH] Create IO_TouchKeypad.h Driver for 16-pad capacitative key pad device (TTP229-B based). --- IO_TouchKeypad.h | 134 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 IO_TouchKeypad.h diff --git a/IO_TouchKeypad.h b/IO_TouchKeypad.h new file mode 100644 index 0000000..8984bce --- /dev/null +++ b/IO_TouchKeypad.h @@ -0,0 +1,134 @@ +/* + * © 2023, Neil McKechnie. All rights reserved. + * + * This file is part of DCC++EX API + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + +/* + * Driver for capacitative touch-pad based on the TTP229-B chip with serial + * (not I2C) output. The touchpad has 16 separate pads in a 4x4 matrix, + * numbered 1-16. The communications with the pad are via a clock signal sent + * from the controller to the device, and a data signal sent back by the device. + * The pins clockPin and dataPin must be local pins, not external (GPIO Expander) + * pins. + * + * To use, + * TouchKeypad::create(firstVpin, 16, clockPin, dataPin); + * + * NOTE: Most of these keypads ship with only 8 pads enabled. To enable all + * sixteen pads, locate the area of the board labelled P1 (four pairs of + * holes labelled 1 to 4 from the left); solder a jumper link between the pair + * labelled 3 (connected to pin TP2 on the chip). When this link is connected, + * the pins OUT1 to OUT8 are not used but all sixteen touch pads are operational. + * + * TODO: Allow a list of datapins to be provided so that multiple keypads can + * be read simultaneously by the one device driver and the one shared clock signal. + * As it stands, we can configure multiple driver instances, one for each keypad, + * and it will work fine. The clock will be driven to all devices but only one + * driver will be reading the responses from its corresponding device at a time. + */ + +#ifndef IO_TOUCHKEYPAD_H +#define IO_TOUCHKEYPAD_H + +#include "IODevice.h" + +class TouchKeypad : public IODevice { +private: + // Here we define the device-specific variables. + uint16_t _inputStates = 0; + VPIN _clockPin; + VPIN _dataPin; + +public: + // Static function to handle create calls. + static void create(VPIN firstVpin, int nPins, VPIN clockPin, VPIN dataPin) { + if (checkNoOverlap(firstVpin,nPins)) new TouchKeypad(firstVpin, nPins, clockPin, dataPin); + } + +protected: + // Constructor. + TouchKeypad(VPIN firstVpin, int nPins, VPIN clockPin, VPIN dataPin) { + _firstVpin = firstVpin; + _nPins = (nPins > 16) ? 16 : nPins; // Maximum of 16 pads per device + _clockPin = clockPin; + _dataPin = dataPin; + + addDevice(this); + } + + // Device-specific initialisation + void _begin() override { +#if defined(DIAG_IO) + _display(); +#endif + // Set clock pin as output, initially high, and data pin as input. + // Enable pullup on the input so that the default (not connected) state is + // 'keypad not pressed'. + ArduinoPins::fastWriteDigital(_clockPin, 1); + pinMode(_clockPin, OUTPUT); + pinMode(_dataPin, INPUT_PULLUP); // Force defined state when no connection + } + + // Device-specific read function. + int _read(VPIN vpin) { + if (vpin < _firstVpin || vpin >= _firstVpin + _nPins) return 0; + + // Return a value for the specified vpin. + return _inputStates & (1<<(vpin-_firstVpin)) ? 1 : 0; + } + + // Loop function to do background scanning of the keyboard. + // The TTP229 device requires clock pulses to be sent to it, + // and the data bits can be read on the rising edge of the clock. + // By default the clock and data are inverted (active-low). + // A gap of more than 2ms is advised between successive read + // cycles, we wait for 100ms between reads of the keyboard as this + // provide a good enough response time. + // Maximum clock frequency is 512kHz, so put a 1us delay + // between clock transitions. + // + void _loop(unsigned long currentMicros) { + + // Clock 16 bits from the device + uint16_t data = 0, maskBit = 0x01; + for (uint8_t pad=0; pad<16; pad++) { + ArduinoPins::fastWriteDigital(_clockPin, 0); + delayMicroseconds(1); + ArduinoPins::fastWriteDigital(_clockPin, 1); + data |= (ArduinoPins::fastReadDigital(_dataPin) ? 0 : maskBit); + maskBit <<= 1; + delayMicroseconds(1); + } + _inputStates = data; +#ifdef DIAG_IO + static uint16_t lastData = 0; + if (data != lastData) DIAG(F("KeyPad: %x"), data); + lastData = data; +#endif + delayUntil(currentMicros + 100000); // read every 100ms + } + + // Display information about the device, and perhaps its current condition (e.g. active, disabled etc). + void _display() { + DIAG(F("TouchKeypad Configured on VPins:%d-%d SCL=%d SDO=%d"), (int)_firstVpin, + (int)_firstVpin+_nPins-1, _clockPin, _dataPin); + } + + +}; + +#endif // IO_TOUCHKEYPAD_H \ No newline at end of file