mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-12-24 05:11:24 +01:00
134 lines
4.8 KiB
C
134 lines
4.8 KiB
C
|
/*
|
||
|
* © 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 <https://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* 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
|