Skip to content

Commit

Permalink
improved touch2046
Browse files Browse the repository at this point in the history
  • Loading branch information
TomSaw committed Sep 14, 2021
1 parent de16c0f commit 2ed0fbb
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 88 deletions.
154 changes: 111 additions & 43 deletions src/modm/driver/touch/touch2046.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// coding: utf-8
/*
* Copyright (c) 2021, Raphael Lehmann
* Copyright (c) 2021, Thomas Sommer
*
* This file is part of the modm project.
*
Expand All @@ -19,58 +20,112 @@

#include <array>
#include <tuple>
#include <modm/ui/graphic/display.hpp>
#include <modm/ui/shape/point.hpp>

namespace modm
{

/// \ingroup modm_driver_touch2046
struct touch2046 {
enum class Orientation {
Normal,
//...
enum class Control : uint8_t
{
START = Bit7, // 1: Marks the control byte

A2 = Bit6, // see enum class ChDiff / ChSingleEnd
A1 = Bit5,
A0 = Bit4,

MODE = Bit3, // see enum class Mode
REF = Bit2, // see enum class Reference

PD1 = Bit1, // see enum class PowerDown
PD0 = Bit0,
};
MODM_FLAGS8(Control);

// Valid when Control::MODE is 0
enum class ChDiff : uint8_t
{
Z1 = int(Control::A0) | int(Control::A1),
Z2 = int(Control::A2),
X = int(Control::A0) | int(Control::A2),
Y = int(Control::A0)
};
typedef Configuration<Control_t, ChDiff, 0b111'0000> ChDiff_t;

// Valid when Control::MODE is 1
enum class ChSingleEnd : uint8_t
{
TEMP0 = 0,
Y = int(Control::A0),
VBAT = int(Control::A1),
Z1 = int(Control::A0) | int(Control::A1),
Z2 = int(Control::A2),
X = int(Control::A0) | int(Control::A2),
AUX = int(Control::A0) | int(Control::A1),
TEMP1 = int(Control::A0) | int(Control::A1) | int(Control::A2)
};
typedef Configuration<Control_t, ChSingleEnd, 0b111'0000> ChSingleEnd_t;

enum class Mode : uint8_t
{
Res_12Bit = 0,
Res_8Bit = int(Control::MODE)
};
typedef Configuration<Control_t, Mode, 0b1, 3> Mode_t;

enum class Reference : uint8_t
{
Differential = 0,
SingleEnded = int(Control::REF)
};
typedef Configuration<Control_t, Reference, 0b1, 2> Reference_t;

enum class PowerDown : uint8_t
{
Auto = 0,
RefOff_AdcOn = int(Control::PD0),
RefOn_AdcOff = int(Control::PD1),
AlwaysOn = int(Control::PD0) | int(Control::PD1)
};
typedef Configuration<Control_t, PowerDown, 0b11, 0> PowerDown_t;


/**
* Calibration values are used to calculate touch point
* from raw values.
*
* \ref FactorX and \ref FactorY scaled by 1000000 to avoid float
* arithmetic. E.g. to get a factor of 0.75 \ref FactorX has to be
* set to 750'000.
*
* isTouched() = bool(Z > ThresholdZ)
*
* X = (rawX * FactorX / 1000000) + OffsetX
* limited to [0, MaxX]
* Y = (rawY * FactorY / 1000000) + OffsetY
* limited to [0, MaxY]
*
* Orientation (rotation, mirror) are applied after the
* above operations.
*/

struct Calibration
{
int32_t FactorX = 24;
int16_t OffsetX = 0;
int32_t FactorY = 24;
int16_t OffsetY = 0;
int32_t FactorX = 1'000'000;
int32_t FactorY = 1'000'000;
uint16_t MaxX = 240;
uint16_t MaxY = 320;
uint16_t ThresholdZ = 1500;
Orientation orientation = Orientation::Normal;
uint16_t ThresholdZ = 1000;
};
};

/**
* \ingroup modm_driver_touch2046
* \author Raphael Lehmann
* \author Raphael Lehmann, Thomas Sommer
*
* Datasheet TSC2046: https://www.ti.com/lit/ds/symlink/tsc2046.pdf
*/
template < class SpiMaster, class Cs>
template < class SpiMaster, class Cs, Resolution R>
class Touch2046 : public touch2046, public modm::SpiDevice< SpiMaster >, protected modm::NestedResumable<3>
{
public:
using Orientation = modm::graphic::Orientation;

/**
* Set calibration data
*
Expand All @@ -81,14 +136,6 @@ class Touch2046 : public touch2046, public modm::SpiDevice< SpiMaster >, protect
cal = calibration;
}

/**
* Get raw X, Y and Z values
*
* \return Position and intensity of touch point. Full int16_t range.
*/
modm::ResumableResult<std::tuple<uint16_t,uint16_t,uint16_t>>
getRawValues();

/**
* Is screen touched?
*
Expand All @@ -98,36 +145,57 @@ class Touch2046 : public touch2046, public modm::SpiDevice< SpiMaster >, protect
isTouched();

/**
* Get touch position
* Get touch position as tuple
*
* \return Position (X, Y) of touch point.
*/
modm::ResumableResult<std::tuple<uint16_t,uint16_t>>
getTouchPosition();

/**
* Get touch position as modm::shape::Point
*
* \return Point of touch point.
*/
modm::ResumableResult<modm::shape::Point>
getTouchPoint();

void setOrientation(Orientation orientation)
{ this->orientation = orientation; }

Orientation getOrientation() const
{ return orientation; }

private:
static constexpr uint8_t MeasureZ1 = 0xB1;
static constexpr uint8_t MeasureZ2 = 0xC1;
static constexpr uint8_t MeasureX = 0xD1;
static constexpr uint8_t MeasureY = 0x91;
static constexpr uint8_t Powerdown = 0b1111'1100;
static constexpr std::array<uint8_t, 17> bufferWrite = {
MeasureZ1, 0x00,
MeasureZ2, 0x00,
MeasureY, 0x00,
MeasureX, 0x00,
MeasureY, 0x00,
MeasureX, 0x00,
MeasureY, 0x00,
(MeasureX & Powerdown), 0x00,
0x00};
std::array<uint16_t, 9> bufferRead = {};
modm::ResumableResult<void>
updateZ();

modm::ResumableResult<void>
updateXY();

static constexpr Control_t Measure = Control::START | Mode_t(Mode::Res_12Bit)
| Reference_t(Reference::Differential) | PowerDown_t(PowerDown::RefOff_AdcOn);

static constexpr std::array<uint16_t, 8> bufferWrite = {
(Measure | ChDiff_t(ChDiff::Z1)).value,
((Measure | ChDiff_t(ChDiff::Z2)) & ~PowerDown_t::mask()).value,
(Measure | ChDiff_t(ChDiff::X)).value,
(Measure | ChDiff_t(ChDiff::Y)).value,
(Measure | ChDiff_t(ChDiff::X)).value,
(Measure | ChDiff_t(ChDiff::Y)).value,
(Measure | ChDiff_t(ChDiff::X)).value,
((Measure | ChDiff_t(ChDiff::Y)) & ~PowerDown_t::mask()).value
};
std::array<uint16_t, 7> bufferRead = {};

public:
uint16_t x = 0;
uint16_t y = 0;
uint16_t z = 0;

Calibration cal;

Orientation orientation = Orientation::Portrait90;
};

} // modm namespace
Expand Down
100 changes: 61 additions & 39 deletions src/modm/driver/touch/touch2046_impl.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// coding: utf-8
/*
* Copyright (c) 2021, Raphael Lehmann
* Copyright (c) 2021, Thomas Sommer
*
* This file is part of the modm project.
*
Expand All @@ -10,70 +11,91 @@
*/
// ----------------------------------------------------------------------------

#ifndef MODM_TOUCH2046_HPP
#error "Don't include this file directly, use 'touch2046.hpp' instead!"
#endif
#pragma once
#include "touch2046.hpp"

#include <modm/math/utils/endianness.hpp>

template < class SpiMaster, class Cs >
modm::ResumableResult<std::tuple<uint16_t,uint16_t,uint16_t>>
modm::Touch2046<SpiMaster, Cs>::getRawValues()
template<class SpiMaster, class Cs, Resolution R>
modm::ResumableResult<void>
modm::Touch2046<SpiMaster, Cs, R>::updateZ()
{
RF_BEGIN();

RF_WAIT_UNTIL(this->acquireMaster());
Cs::reset();

RF_CALL(SpiMaster::transfer(
bufferWrite.data(),
reinterpret_cast<uint8_t*>(bufferRead.data()) + 1,
17));
RF_CALL(SpiMaster::template transfer<16>(&bufferWrite[0], &bufferRead[0], 3));

z = 4095 + (modm::fromBigEndian(bufferRead[1]) >> 3)
- (modm::fromBigEndian(bufferRead[2]) >> 3);
z = 4095 + (bufferRead[1] >> 3) - (bufferRead[2] >> 3);

y = (modm::fromBigEndian(bufferRead[3]) >> 3)
+ (modm::fromBigEndian(bufferRead[5]) >> 3)
+ (modm::fromBigEndian(bufferRead[7]) >> 3);
if (this->releaseMaster())
Cs::set();

RF_END_RETURN();
}

template<class SpiMaster, class Cs, Resolution R>
modm::ResumableResult<void>
modm::Touch2046<SpiMaster, Cs, R>::updateXY()
{
RF_BEGIN();

RF_WAIT_UNTIL(this->acquireMaster());
Cs::reset();

x = (modm::fromBigEndian(bufferRead[4]) >> 3)
+ (modm::fromBigEndian(bufferRead[6]) >> 3)
+ (modm::fromBigEndian(bufferRead[8]) >> 3);
RF_CALL(SpiMaster::template transfer<16>(&bufferWrite[2], &bufferRead[0], bufferRead.size()));

if (this->releaseMaster()) {
if (this->releaseMaster())
Cs::set();
}

RF_END_RETURN(std::make_tuple(x, y, z));
x = (bufferRead[1] >> 3) + (bufferRead[3] >> 3) + (bufferRead[5] >> 3);
y = (bufferRead[2] >> 3) + (bufferRead[4] >> 3) + (bufferRead[6] >> 3);

static constexpr int scale_shift = 10;
x = std::clamp<int16_t>((((uint32_t)(x * cal.FactorX) >> scale_shift) + cal.OffsetX), 0, R.x);
y = std::clamp<int16_t>((((uint32_t)(y * cal.FactorY) >> scale_shift) + cal.OffsetY), 0, R.y);

RF_END_RETURN();
}

template < class SpiMaster, class Cs >
template<class SpiMaster, class Cs, Resolution R>
modm::ResumableResult<bool>
modm::Touch2046<SpiMaster, Cs>::isTouched()
modm::Touch2046<SpiMaster, Cs, R>::isTouched()
{
RF_BEGIN();
std::tie(std::ignore, std::ignore, z) = RF_CALL(getRawValues());
RF_CALL(updateZ());
RF_END_RETURN(z > cal.ThresholdZ);
}

template < class SpiMaster, class Cs >
modm::ResumableResult<std::tuple<uint16_t,uint16_t>>
modm::Touch2046<SpiMaster, Cs>::getTouchPosition()
template<class SpiMaster, class Cs, Resolution R>
modm::ResumableResult<modm::shape::Point>
modm::Touch2046<SpiMaster, Cs, R>::getTouchPoint()
{
RF_BEGIN();
RF_CALL(updateXY());

std::tie(x, y, std::ignore) = RF_CALL(getRawValues());

x = std::min<uint16_t>(
((static_cast<int32_t>(x * cal.FactorX) / 1'000'000)
+ cal.OffsetX),
cal.MaxX);
y = std::min<uint16_t>(
((static_cast<int32_t>(y * cal.FactorY) / 1'000'000)
+ cal.OffsetY),
cal.MaxY);
switch(orientation) {
case Orientation::Landscape0: RF_RETURN(Point(R.y - y, R.x - x));
case Orientation::Portrait90: RF_RETURN(Point(x, R.y - y));
case Orientation::Landscape180: RF_RETURN(Point(y, x));
case Orientation::Portrait270: RF_RETURN(Point(R.x - x, y));
}

// todo: orientation processing
RF_END();
}

RF_END_RETURN(std::make_tuple(x, y));
template<class SpiMaster, class Cs, Resolution R>
modm::ResumableResult<std::tuple<uint16_t, uint16_t>>
modm::Touch2046<SpiMaster, Cs, R>::getTouchPosition()
{
RF_BEGIN();
RF_CALL(updateXY());
// TODO evaluate orientation & modm::graphic::OrientationFlags::TopDown
if (orientation & modm::graphic::OrientationFlags::Portrait) {
RF_RETURN(std::make_tuple(x, y));
} else {
RF_RETURN(std::make_tuple(y, x));
}
RF_END();
}
Loading

0 comments on commit 2ed0fbb

Please sign in to comment.