├── docs
├── html.zip
├── Messung.xlsx
├── noisy_touch.png
├── normal_touch.png
├── Ausgleichsgerade.xlsx
├── noisy_touch_with_fir.png
└── mainpage.dox
├── TFT_eFirFilter.h
├── README.md
├── library.properties
├── keywords.txt
├── examples
├── generic
│ ├── edit_calibation
│ │ ├── TMenu.h
│ │ ├── MenuCounter.h
│ │ ├── MenuCounter.cpp
│ │ ├── edit_calibation.ino
│ │ └── TMenu.cpp
│ ├── raw
│ │ └── raw.ino
│ └── calibrate
│ │ └── calibrate.ino
└── TFT_eSPI
│ └── Conways_Life
│ └── Conways_Life.ino
├── TFT_eTouchGesture.h
├── TFT_eTouchGesture.cpp
├── TFT_eTouchBase.inl
├── TFT_eTouch.h
├── TFT_eTouchUser.h
├── TFT_eTouch.inl
├── TFT_eTouchBase.h
└── TFT_eTouchBase.cpp
/docs/html.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/achillhasler/TFT_eTouch/HEAD/docs/html.zip
--------------------------------------------------------------------------------
/TFT_eFirFilter.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/achillhasler/TFT_eTouch/HEAD/TFT_eFirFilter.h
--------------------------------------------------------------------------------
/docs/Messung.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/achillhasler/TFT_eTouch/HEAD/docs/Messung.xlsx
--------------------------------------------------------------------------------
/docs/noisy_touch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/achillhasler/TFT_eTouch/HEAD/docs/noisy_touch.png
--------------------------------------------------------------------------------
/docs/normal_touch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/achillhasler/TFT_eTouch/HEAD/docs/normal_touch.png
--------------------------------------------------------------------------------
/docs/Ausgleichsgerade.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/achillhasler/TFT_eTouch/HEAD/docs/Ausgleichsgerade.xlsx
--------------------------------------------------------------------------------
/docs/noisy_touch_with_fir.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/achillhasler/TFT_eTouch/HEAD/docs/noisy_touch_with_fir.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TFT_eTouch
2 | generic touch support for resistive chips
3 |
4 | unzip the Doxygen docu in docs for additional documentation
5 |
--------------------------------------------------------------------------------
/library.properties:
--------------------------------------------------------------------------------
1 | name=TFT_eTouch
2 | version=0.6.0
3 | author=Achill Hasler
4 | maintainer=Achill Hasler
5 | sentence=A library for restitive touchscreens for the Arduino IDE
6 | paragraph=Supports TFT displays that have a Adafruit compatible display driver.
7 | category=Display
8 | url=https://github.com/achillhasler/TFT_eTouch
9 | architectures=*
10 | includes=TFT_eTouch.h TFT_eTouchBase.h
11 |
--------------------------------------------------------------------------------
/keywords.txt:
--------------------------------------------------------------------------------
1 | #######################################
2 | # Syntax Coloring Map For TFT_eTouch
3 | #######################################
4 |
5 | #######################################
6 | # Datatypes (KEYWORD1)
7 | #######################################
8 |
9 | TFT_eTouch KEYWORD1
10 | TFT_eTouchBase KEYWORD1
11 | Calibation KEYWORD1
12 | Measure KEYWORD1
13 | TouchPoint KEYWORD1
14 |
15 | #######################################
16 | # Methods and Functions (KEYWORD2)
17 | #######################################
18 |
19 | init KEYWORD2
20 | getCalibration KEYWORD2
21 | getUserCalibration KEYWORD2
22 | setCalibration KEYWORD2
23 | readCalibration KEYWORD2
24 | writeCalibration KEYWORD2
25 | calibration KEYWORD2
26 | getXY KEYWORD2
27 | getRZ KEYWORD2
28 | getRaw KEYWORD2
29 | transform KEYWORD2
30 | setMeasure KEYWORD2
31 | setValidRawRange KEYWORD2
32 | setMeasureWait KEYWORD2
33 | getMeasureWait KEYWORD2
34 | setRXPlate KEYWORD2
35 | getRXPlate KEYWORD2
36 | setRZThreshold KEYWORD2
37 | getRZThreshold KEYWORD2
38 | setAcurateDistance KEYWORD2
39 | getAcurateDistance KEYWORD2
40 |
--------------------------------------------------------------------------------
/examples/generic/edit_calibation/TMenu.h:
--------------------------------------------------------------------------------
1 | #ifndef T_MENU_H
2 | #define T_MENU_H
3 |
4 | //
5 | // TMenu.h
6 | //
7 | // (C) Copyright Achill Hasler 2019.
8 | //
9 | // Distributed under the Boost Software License, Version 1.0.
10 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt
11 | //
12 |
13 | #define TEST_RAW_INTERFACE
14 |
15 | #include
16 | #include "MenuCounter.h"
17 |
18 | class TMenu
19 | {
20 | TFT_eTouch& touch_;
21 |
22 | MenuCounter mc_[4];
23 | uint8_t offset_;
24 | #ifdef TEST_RAW_INTERFACE
25 | TFT_eTouchBase::Measure raw_;
26 | TFT_eTouchBase::TouchPoint tp_;
27 | #else
28 | int16_t x_;
29 | int16_t y_;
30 | uint16_t rz_;
31 | #endif
32 | bool touched_;
33 | bool cursor_visible_;
34 |
35 |
36 | void draw();
37 | void info(uint8_t offset);
38 |
39 | public:
40 | TMenu(TFT_eTouch& touch, uint8_t offset = 18);
41 |
42 | TFT_Driver& tft();
43 |
44 | EventType pen_up();
45 | EventType pen_down();
46 |
47 | void init();
48 | #ifdef TEST_RAW_INTERFACE
49 | void update(const TFT_eTouchBase::Measure& raw);
50 | #else
51 | void update(int16_t x, int16_t y, uint16_t rz);
52 | #endif
53 | void refresh();
54 | };
55 |
56 | #endif // T_MENU_H
57 |
--------------------------------------------------------------------------------
/examples/generic/edit_calibation/MenuCounter.h:
--------------------------------------------------------------------------------
1 | #ifndef MENU_COUNTER_H
2 | #define MENU_COUNTER_H
3 |
4 | //
5 | // TMenuCounter.h
6 | //
7 | // (C) Copyright Achill Hasler 2019.
8 | //
9 | // Distributed under the Boost Software License, Version 1.0.
10 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt
11 | //
12 |
13 | #include
14 |
15 | typedef enum
16 | {
17 | none,
18 | changed,
19 | calibrate,
20 | store
21 | } EventType;
22 |
23 | class TMenu;
24 |
25 | class MenuCounter
26 | {
27 | TMenu* menu_;
28 | uint8_t fw_;
29 | int16_t x_p_;
30 | int16_t y_p_;
31 | bool x_grow_;
32 |
33 | char ch_;
34 | EventType ch_signal_;
35 | EventType signal_;
36 |
37 | uint16_t* val_;
38 |
39 | uint8_t cnt_step_;
40 |
41 | uint8_t activ_;
42 | uint8_t last_activ_;
43 |
44 | void draw_move(int16_t x_pos, int16_t y_pos, bool flag);
45 |
46 | public:
47 | MenuCounter(uint8_t fw = 20);
48 |
49 | void oner(TMenu* menu);
50 | TFT_Driver& tft() const;
51 |
52 | void pos(int16_t x_p, int16_t y_p, bool horizontal)
53 | { x_p_ = x_p; y_p_ = y_p; x_grow_ = horizontal; }
54 |
55 | void cmd(char ch, EventType event = none)
56 | { ch_ = ch; ch_signal_ = event; }
57 |
58 | void val(uint16_t& val)
59 | { val_ = &val; }
60 |
61 | void fw(uint8_t val) { fw_ = val; }
62 | uint8_t fw() const { return fw_; }
63 |
64 | EventType signal() { EventType ret = signal_; signal_ = none; return ret; }
65 |
66 | void pen_up();
67 |
68 | void update(int16_t x, int16_t y, uint16_t rz);
69 | void draw();
70 | };
71 |
72 | #endif // MENU_COUNTER_H
73 |
--------------------------------------------------------------------------------
/TFT_eTouchGesture.h:
--------------------------------------------------------------------------------
1 | #ifndef TFT_E_TOUCH_GESTURE_H
2 | #define TFT_E_TOUCH_GESTURE_H
3 |
4 | //
5 | // TFT_eTouchGesture.h
6 | //
7 | // (C) Copyright Achill Hasler 2019.
8 | //
9 | // Distributed under the Boost Software License, Version 1.0.
10 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt
11 | //
12 | //
13 | // See TFT_eTouch/docs/html/index.html for documentation.
14 | //
15 |
16 | #include
17 |
18 | /**
19 | * @brief Gesture support for touch
20 | */
21 | class TFT_eTouchGesture
22 | {
23 | struct FilteredMeasure
24 | {
25 | uint8_t x; ///< raw x measure reduced to 8 bit (ca 213..3881)/16
26 | uint8_t y; ///< raw y measure reduced to 8 bit (ca 222..3929)/16
27 | uint8_t z1; ///< raw z1 measure reduced to 8 bit (ca 29..1748)/16
28 | uint8_t z2; ///< raw z2 measure reduced to 8 bit (ca 963..3989)/16
29 | uint8_t rz; ///< calculated resitor value < 0xff when tuched, 0xff when not tuched.
30 | uint16_t ms;
31 | FilteredMeasure() : rz(0xff) {}
32 | void operator=(const TFT_eTouchBase::Measure& raw);
33 | };
34 |
35 | public:
36 | typedef enum
37 | {
38 | none,
39 | stay,
40 | move,
41 | wipe,
42 | zoom_in,
43 | zoom_out
44 | } Action;
45 |
46 | /**
47 | * @brief constructor
48 | * @param size stored values
49 | */
50 | TFT_eTouchGesture(uint16_t size);
51 | ~TFT_eTouchGesture();
52 |
53 | /**
54 | * You have to feed this instance with new measure in a constant timeframe.
55 | * @brief set measure
56 | * @param raw last readed touch measure
57 | */
58 | void set(const TFT_eTouchBase::Measure& raw);
59 |
60 | /**
61 | * On pen up reset must call.
62 | * @brief reset
63 | */
64 | void reset();
65 |
66 | /**
67 | * Get the action. When Action move or wipe is returned the angle is valid.
68 | * @brief get action
69 | * @param angle in grad
70 | * @return actual action
71 | */
72 | Action get(int16_t& angle);
73 |
74 | private:
75 | FilteredMeasure* data_;
76 |
77 | uint16_t size_;
78 | uint16_t next_;
79 | uint16_t used_;
80 | };
81 |
82 |
83 |
84 | //#include
85 |
86 | #endif // TFT_E_TOUCH_GESTURE_H
87 |
--------------------------------------------------------------------------------
/TFT_eTouchGesture.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // TFT_eTouchGesture.cpp
3 | //
4 | // (C) Copyright Achill Hasler 2019.
5 | //
6 | // Distributed under the Boost Software License, Version 1.0.
7 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt
8 | //
9 | //
10 | // See TFT_eTouch/docs/html/index.html for documentation.
11 | //
12 |
13 | #include
14 |
15 | void TFT_eTouchGesture::FilteredMeasure::operator=(const TFT_eTouchBase::Measure& raw)
16 | {
17 | x = raw.x >> 4;
18 | y = raw.y >> 4;
19 | z1 = raw.z1 >> 4;
20 | z2 = raw.z2 >> 4;
21 | if (raw.rz == 0xffff) rz = 255;
22 | else if (raw.rz > (254<<4)) rz = 254;
23 | else rz = raw.rz >> 4;
24 | ms = millis();
25 | }
26 |
27 | TFT_eTouchGesture::TFT_eTouchGesture(uint16_t size)
28 | : data_(0)
29 | , size_(size)
30 | , next_(0)
31 | , used_(0)
32 | {
33 | data_ = new FilteredMeasure[size];
34 | }
35 |
36 | TFT_eTouchGesture::~TFT_eTouchGesture()
37 | {
38 | delete data_;
39 | }
40 |
41 | void TFT_eTouchGesture::set(const TFT_eTouchBase::Measure& raw)
42 | {
43 | data_[next_++] = raw;
44 | if (next_ >= size_) next_ = 0;
45 | if (used_ < size_) used_++;
46 | }
47 |
48 | void TFT_eTouchGesture::reset()
49 | {
50 | next_ = 0;
51 | used_ = 0;
52 | }
53 | #define compare_ind(i1, i2) \
54 | if (data_[i1].x < data_[i2].x) xneg=false; \
55 | if (data_[i1].x > data_[i2].x) xpos=false; \
56 | if (data_[i1].y < data_[i2].y) yneg=false; \
57 | if (data_[i1].y > data_[i2].y) ypos=false
58 |
59 | TFT_eTouchGesture::Action TFT_eTouchGesture::get(int16_t& angle)
60 | {
61 | TFT_eTouchGesture::Action ret = none;
62 | if (used_ < size_) return ret;
63 | uint16_t ind = (size_ + next_ - 1) % size_;
64 | // int32_t dist = sqrt(pow((data_[ind].x - data_[next_].x), 2) + pow((data_[ind].y - data_[next_].y), 2));
65 | // float dist = std::hypotf(data_[ind].x - data_[next_].x, data_[ind].y - data_[next_].y);
66 | float dist = ::hypotf(data_[ind].x - data_[next_].x, data_[ind].y - data_[next_].y);
67 |
68 | bool xneg=true, xpos=true, yneg=true, ypos=true;
69 | for (int i = next_; i < size_ - 1; i++) {
70 | compare_ind(i, i+1);
71 | }
72 | if (next_ != 0) {
73 | compare_ind(size_-1, 0);
74 | }
75 | for (int i = 0; i < next_ - 2; i++) {
76 | compare_ind(i, i+1);
77 | }
78 | return ret;
79 | }
80 | #undef compare_ind
81 |
82 | /*
83 | none,
84 | stay,
85 | move,
86 | wipe,
87 | zoom_in,
88 | zoom_out
89 |
90 | TFT_eTouchBase::Measure* data_;
91 |
92 | uint16_t size_;
93 | uint16_t next_;
94 | uint16_t used_;
95 | */
96 |
--------------------------------------------------------------------------------
/TFT_eTouchBase.inl:
--------------------------------------------------------------------------------
1 | #ifndef TFT_E_TOUCH_BASE_INL
2 | #define TFT_E_TOUCH_BASE_INL
3 |
4 | //
5 | // TFT_eTouchBase.inl
6 | //
7 | // (C) Copyright Achill Hasler 2019.
8 | //
9 | // Distributed under the Boost Software License, Version 1.0.
10 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt
11 | //
12 | //
13 | // See TFT_eTouch/docs/html/index.html for documentation.
14 | //
15 |
16 | // public inline members
17 | uint16_t TFT_eTouchBase::getRZ() const
18 | {
19 | return raw_.rz;
20 | }
21 |
22 | void TFT_eTouchBase::setCalibration(const Calibation& data)
23 | {
24 | calibation_ = data;
25 | }
26 |
27 | TFT_eTouchBase::Calibation& TFT_eTouchBase::calibration()
28 | {
29 | return calibation_;
30 | }
31 |
32 | void TFT_eTouchBase::setMeasure(uint8_t drop_first, bool z_once, bool z_first, bool z_local_min, uint8_t count)
33 | {
34 | drop_first_measures_ = drop_first;
35 | z_once_measure_ = z_once;
36 | z_first_measure_ = z_first;
37 | z_local_min_measure_ = z_local_min;
38 |
39 | count_measure_ = count;
40 | }
41 |
42 | #ifdef TOUCH_USE_AVERAGING_CODE
43 | void TFT_eTouchBase::setAveraging(bool averaging, bool ignore_min_max)
44 | {
45 | averaging_measure_ = averaging;
46 | ignore_min_max_measure_ = ignore_min_max;
47 | }
48 | #endif // end TOUCH_USE_AVERAGING_CODE
49 |
50 | void TFT_eTouchBase::setValidRawRange(uint16_t min, uint16_t max)
51 | {
52 | raw_valid_min_ = min; // raw measure minimum value from x, y, z1 and z2 (otherwise it is not touched)
53 | raw_valid_max_ = max; // raw measure maximum value
54 | }
55 |
56 |
57 | void TFT_eTouchBase::setMeasureWait(uint16_t ms)
58 | {
59 | measure_wait_ms_ = ms;
60 | }
61 | uint16_t TFT_eTouchBase::getMeasureWait() const
62 | {
63 | return measure_wait_ms_;
64 | }
65 |
66 |
67 | void TFT_eTouchBase::setRXPlate(uint16_t ohm)
68 | {
69 | rx_plate_ = ohm;
70 | }
71 | uint16_t TFT_eTouchBase::getRXPlate() const
72 | {
73 | return rx_plate_;
74 | }
75 |
76 | void TFT_eTouchBase::setRZThreshold(uint16_t ohm)
77 | {
78 | rz_threshold_ = ohm;
79 | }
80 | uint16_t TFT_eTouchBase::getRZThreshold() const
81 | {
82 | return rz_threshold_;
83 | }
84 |
85 | void TFT_eTouchBase::reset()
86 | {
87 | #ifdef TOUCH_FILTER_TYPE
88 | # ifdef TOUCH_X_FILTER
89 | x_filter_->reset();
90 | # endif
91 | # ifdef TOUCH_Y_FILTER
92 | y_filter_->reset();
93 | # endif
94 | # ifdef TOUCH_Z_FILTER
95 | z1_filter_->reset();
96 | z2_filter_->reset();
97 | # endif
98 | #endif
99 | }
100 |
101 |
102 | bool TFT_eTouchBase::valid()
103 | {
104 | return raw_.rz < rz_threshold_;
105 | }
106 |
107 | #ifdef TOUCH_USE_USER_CALIBRATION
108 | void TFT_eTouchBase::setAcurateDistance(uint16_t raw_difference)
109 | {
110 | acurate_difference_ = raw_difference;
111 | }
112 | uint16_t TFT_eTouchBase::getAcurateDistance() const
113 | {
114 | return acurate_difference_;
115 | }
116 | #endif // TOUCH_USE_USER_CALIBRATION
117 |
118 | // private inline members
119 |
120 | bool TFT_eTouchBase::is_touched()
121 | {
122 | return raw_.rz != 0xffff;
123 | }
124 |
125 | bool TFT_eTouchBase::in_range(uint16_t measure)
126 | {
127 | return (measure >= raw_valid_min_) && (measure <= raw_valid_max_);
128 | }
129 |
130 | void TFT_eTouchBase::spi_start()
131 | {
132 | spi_.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0));
133 | digitalWrite(cs_, LOW);
134 | }
135 |
136 | void TFT_eTouchBase::spi_end()
137 | {
138 | digitalWrite(cs_, HIGH);
139 | spi_.endTransaction();
140 | }
141 |
142 | #endif // TFT_E_TOUCH_BASE_INL
143 |
--------------------------------------------------------------------------------
/TFT_eTouch.h:
--------------------------------------------------------------------------------
1 | #ifndef TFT_E_TOUCH_H
2 | #define TFT_E_TOUCH_H
3 |
4 | //
5 | // TFT_eTouch.h
6 | //
7 | // (C) Copyright Achill Hasler 2019.
8 | //
9 | // Distributed under the Boost Software License, Version 1.0.
10 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt
11 | //
12 | //
13 | // See TFT_eTouch/docs/html/index.html for documentation.
14 | //
15 |
16 | #include
17 |
18 | /**
19 | * @brief touch support for tft
20 | * @param T used display driver, must be Adafuit compatible
21 | */
22 | template
23 | class TFT_eTouch : public TFT_eTouchBase
24 | {
25 | public:
26 | /**
27 | * Create instance with defaults.
28 | *
29 | * Display and touch mostly use the same spi bus (SPI). This will by configured by the tft driver on tft.begin() and
30 | * the touch driver use this configuration.
31 | * When not you have to look that your used spi parameter get configured elsewhere bevor you call touch.begin().
32 | * @brief constructor
33 | * @param tft used display, must be Adafuit compatible
34 | * @param cs_pin touch chip select line processor pin
35 | * @param penirq_pin touch penirq line processor pin. 0xff disable touch interrupt
36 | * @param spi used spi bus, configured by the tft driver
37 | */
38 | TFT_eTouch(T& tft, uint8_t cs_pin, uint8_t penirq_pin = 0xff, SPIClass& spi = SPI);
39 |
40 | /**
41 | * Initialize the processor pins and initialize interrupt handling.
42 | * @brief init touch
43 | */
44 | void init();
45 |
46 | /**
47 | * Initialize the processor pins and initialize interrupt handling.
48 | * @brief begin touch
49 | */
50 | void begin();
51 |
52 | /**
53 | * Get Adafuit compatible display driver.
54 | * @brief get display
55 | * @return used display
56 | */
57 | T& tft();
58 |
59 | #ifdef TOUCH_USE_USER_CALIBRATION
60 | /**
61 | * Calculate the calibration values from 4 measures. User has to hit the blue target.
62 | * If it is get red try it again til it is green and next target is shown.
63 | * At the end a configuration string will be printed out to Serial.
64 | * @brief get calibration from user
65 | * @param data display coordinate of touch
66 | * @param xy_off display coordinate of touch
67 | * @return false on error otherwise data is set.
68 | */
69 | bool getUserCalibration(Calibation& data, uint8_t xy_off);
70 | #endif
71 |
72 | /**
73 | * Tranfom a raw touch measure into display position.
74 | * The value tp is only set if the function returns true.
75 | * @brief raw to display
76 | * @param raw touch data
77 | * @param tp display data
78 | * @return true when raw data valid
79 | */
80 | bool transform(const Measure& raw, TouchPoint& tp);
81 |
82 | /**
83 | * Get display position of touch.
84 | * The values x and y are only set if the function returns true.
85 | * @brief display position
86 | * @param x display coordinate of touch
87 | * @param y display coordinate of touch
88 | * @return true when display is tuched
89 | */
90 | bool getXY(int16_t& x, int16_t& y);
91 |
92 | /**
93 | * Get display position and touched area of touch.
94 | * The value tp is only set if the function returns true.
95 | * @brief touch display values
96 | * @param tp touch
97 | * @return true when display is tuched
98 | */
99 | bool get(TouchPoint& tp);
100 |
101 | #ifdef TOUCH_USE_PENIRQ_CODE
102 | # ifdef ESP32
103 | static void IRAM_ATTR cb_isr_touch_fnk();
104 | # else
105 | static void cb_isr_touch_fnk();
106 | # endif
107 | #endif // end TOUCH_USE_PENIRQ_CODE
108 |
109 | private:
110 | #ifdef TOUCH_USE_USER_CALIBRATION
111 | /**
112 | * Show one calibration target and get the raw touch values for that point.
113 | * The Target get red when not accurate and function returns false.
114 | * othewise the target get green and function returns true.
115 | *
116 | * If it is allways get red try a other configuration. see setAcurateDistance()
117 | * @brief one calibration target
118 | * @param point target, when the function returns true touch parameters are set.
119 | * @return true when accurate
120 | */
121 | bool handleTouchCalibrationTarget(CalibrationPoint& point);
122 | #endif
123 |
124 | T& tft_; ///< the given display driver
125 | #ifdef TOUCH_USE_PENIRQ_CODE
126 | static TFT_eTouch* isr_instance_; ///< one instance used for interrupt handling
127 | #endif // end TOUCH_USE_PENIRQ_CODE
128 | };
129 |
130 | #include
131 |
132 | #endif // TFT_E_TOUCH_H
133 |
--------------------------------------------------------------------------------
/examples/generic/edit_calibation/MenuCounter.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // TMenuCounter.cpp
3 | //
4 | // (C) Copyright Achill Hasler 2019.
5 | //
6 | // Distributed under the Boost Software License, Version 1.0.
7 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt
8 | //
9 |
10 | #include "TMenu.h"
11 |
12 | void MenuCounter::draw_move(int16_t x_pos, int16_t y_pos, bool flag)
13 | {
14 | if (x_grow_) {
15 | const int16_t xm = x_pos + fw_*8/16;
16 | const int16_t xa = x_pos + fw_*4/16;
17 | const int16_t xe = x_pos + fw_*12/16;
18 | if (flag) {
19 | // up
20 | const int16_t ym = y_pos + fw_*4/16;
21 | tft().drawLine(xa, y_pos, xm, ym, TFT_WHITE);
22 | tft().drawLine(xe, y_pos, xm, ym, TFT_WHITE);
23 | }
24 | else {
25 | // down
26 | const int16_t ym = y_pos - fw_*4/16;
27 | tft().drawLine(xa, y_pos, xm, ym, TFT_WHITE);
28 | tft().drawLine(xe, y_pos, xm, ym, TFT_WHITE);
29 | }
30 | }
31 | else {
32 | const int16_t ym = y_pos + fw_*8/16;
33 | const int16_t ya = y_pos + fw_*4/16;
34 | const int16_t ye = y_pos + fw_*12/16;
35 | if (flag) {
36 | // right
37 | const int16_t xm = x_pos + fw_*4/16;
38 | tft().drawLine(x_pos, ya, xm, ym, TFT_WHITE);
39 | tft().drawLine(x_pos, ye, xm, ym, TFT_WHITE);
40 | }
41 | else {
42 | // left
43 | const int16_t xm = x_pos - fw_*4/16;
44 | tft().drawLine(x_pos, ya, xm, ym, TFT_WHITE);
45 | tft().drawLine(x_pos, ye, xm, ym, TFT_WHITE);
46 | }
47 | }
48 | }
49 |
50 |
51 | MenuCounter::MenuCounter(uint8_t fw)
52 | : menu_(0)
53 | , fw_(fw)
54 | , cnt_step_(12)
55 | , activ_(255)
56 | , last_activ_(255)
57 | , ch_signal_(none)
58 | , signal_(none)
59 | , val_(0)
60 | {}
61 |
62 | void MenuCounter::oner(TMenu* menu)
63 | {
64 | menu_ = menu;
65 | }
66 |
67 | TFT_Driver& MenuCounter::tft() const
68 | {
69 | return menu_->tft();
70 | }
71 |
72 | void MenuCounter::pen_up()
73 | {
74 | activ_ = 255;
75 | if (last_activ_ < 5) draw();
76 | }
77 |
78 | void MenuCounter::update(int16_t x, int16_t y, uint16_t rz)
79 | {
80 | static uint32_t last_update = 0;
81 |
82 | bool inside = false;
83 | if ((x > x_p_) && (y > y_p_)) {
84 | x -= x_p_;
85 | y -= y_p_;
86 | if (x_grow_) {
87 | if ((x < (5 * fw_)) && (y < (1 * fw_))) inside = true;
88 | }
89 | else {
90 | if ((x < (1 * fw_)) && (y < (5 * fw_))) inside = true;
91 | }
92 | }
93 | if (!inside) {
94 | activ_ = 255;
95 | if (signal_ != none) {
96 | signal_ = none;
97 | draw();
98 | }
99 | return;
100 | }
101 | uint8_t ind;
102 | if (x_grow_) {
103 | ind = x / fw_;
104 | }
105 | else {
106 | ind = y / fw_;
107 | }
108 | if (last_update + 500 < millis()) {
109 | last_update = millis();
110 | uint16_t val = *val_;
111 | // Serial.printf("update %d val: %p %d -> ", ind, val_, val);
112 | switch (ind) {
113 | case 0: // decrement var
114 | if (val - cnt_step_ > 200) val -= cnt_step_;
115 | break;
116 | case 1:
117 | if (val > 200) val--;
118 | break;
119 | case 2:
120 | signal_ = ch_signal_;
121 | break;
122 | case 3:
123 | if (val < 4000) val++;
124 | break;
125 | case 4:
126 | if (val + cnt_step_ < 4000) val += cnt_step_;
127 | break;
128 | default:
129 | return;
130 | }
131 | if (ind != 2) {
132 | signal_ = changed;
133 | *val_ = val;
134 | // Serial.println(val);
135 | }
136 | }
137 | activ_ = ind;
138 | }
139 |
140 | void MenuCounter::draw()
141 | {
142 | uint16_t i;
143 |
144 | if (activ_ == 2) tft().setTextColor(TFT_WHITE, TFT_BLUE);
145 |
146 | #ifdef BASIC_FONT_SUPPORT
147 | #define CH_X_OFF 8
148 | #define CH_Y_OFF 7
149 | #else
150 | #define CH_X_OFF 6
151 | #define CH_Y_OFF 3
152 | #endif
153 |
154 | if (x_grow_) {
155 | if (last_activ_ < 5 && activ_ != last_activ_) {
156 | tft().fillRect(x_p_ + 1 + last_activ_ * fw_, y_p_ + 1, fw_-1, fw_-1, TFT_BLACK);
157 | }
158 | for (i = 0; i <= 5; i++) {
159 | tft().drawFastVLine(x_p_ + i * fw_, y_p_ + 1, fw_ - 1, TFT_WHITE);
160 | }
161 | tft().drawFastHLine(x_p_, y_p_, 5 * fw_ + 1, TFT_WHITE);
162 | tft().drawFastHLine(x_p_, y_p_ + fw_, 5 * fw_ + 1, TFT_WHITE);
163 | if (activ_ < 5) {
164 | tft().fillRect(x_p_ + 1 + activ_ * fw_, y_p_ + 1, fw_-1, fw_-1, TFT_BLUE);
165 | }
166 | draw_move(x_p_, y_p_ + fw_*4/16, true);
167 | draw_move(x_p_, y_p_ + fw_*8/16, true);
168 |
169 | draw_move(x_p_ + fw_, y_p_ + fw_*6/16, true);
170 |
171 | tft().setCursor(x_p_ + 2*fw_ + CH_X_OFF, y_p_ + CH_Y_OFF);
172 | tft().print(ch_);
173 |
174 | draw_move(x_p_ + 3*fw_, y_p_ + fw_*10/16, false);
175 |
176 | draw_move(x_p_ + 4*fw_, y_p_ + fw_*8/16, false);
177 | draw_move(x_p_ + 4*fw_, y_p_ + fw_*12/16, false);
178 | }
179 | else {
180 | if (last_activ_ < 5 && activ_ != last_activ_) {
181 | tft().fillRect(x_p_ + 1, y_p_ + 1 + last_activ_ * fw_, fw_-1, fw_-1, TFT_BLACK);
182 | }
183 | for (i = 0; i <= 5; i++) {
184 | tft().drawFastHLine(x_p_ + 1, y_p_ + i * fw_, fw_ - 1, TFT_WHITE);
185 | }
186 | tft().drawFastVLine(x_p_, y_p_, 5 * fw_ + 1, TFT_WHITE);
187 | tft().drawFastVLine(x_p_ + fw_, y_p_, 5 * fw_ + 1, TFT_WHITE);
188 | if (activ_ < 5) {
189 | tft().fillRect(x_p_ + 1, y_p_ + 1 + activ_ * fw_, fw_-1, fw_-1, TFT_BLUE);
190 | }
191 |
192 | draw_move(x_p_ + fw_*8/16, y_p_, false);
193 | draw_move(x_p_ + fw_*12/16, y_p_, false);
194 |
195 | draw_move(x_p_ + fw_*10/16, y_p_ + fw_, false);
196 |
197 | tft().setCursor(x_p_ + CH_X_OFF, y_p_ + 2*fw_ + CH_Y_OFF);
198 | tft().print(ch_);
199 |
200 | draw_move(x_p_ + fw_*6/16, y_p_ + 3*fw_, true);
201 |
202 | draw_move(x_p_ + fw_*4/16, y_p_ + 4*fw_, true);
203 | draw_move(x_p_ + fw_*8/16, y_p_ + 4*fw_, true);
204 | }
205 | tft().setTextColor(TFT_WHITE, TFT_BLACK);
206 |
207 | last_activ_ = activ_;
208 | }
209 |
--------------------------------------------------------------------------------
/docs/mainpage.dox:
--------------------------------------------------------------------------------
1 | /**
2 | @brief Documentation file for TFT_eTouch
3 | @author Achill Hasler
4 | @file mainpage.dox
5 | */
6 |
7 | // we have to list all sample programs to show these in doxygen
8 | /**
9 | @example raw.ino Touch raw data example
10 | @example edit_calibation.ino Calibration example for TFT_eSPI driver
11 | @example calibrate.ino Calibration example for TFT_eSPI driver (obsolete use edit_calibation.ino)
12 | @example Conways_Life.ino Application example for TFT_eSPI driver (compare TFT_eTouch with integrated Touch in TFT_eSPI)
13 | */
14 |
15 | /**
16 | @mainpage TFT_eTouch
17 | @section intro Intro
18 | Touchscreen
19 | - interface: spi & i2c & analog
20 | - type: resistive & capacitive
21 |
22 | This library supports resistive touchscreen chips with spi interface.
23 |
24 | @section config Config
25 | @subsection display Supported display
26 | The template parameter must be a Adafuit compatible display driver with followed interface
27 | @code
28 | class T {
29 | public:
30 | int16_t height(void) const;
31 | int16_t width(void) const;
32 | uint8_t getRotation(void) const;
33 |
34 | void fillScreen(uint32_t color);
35 | void drawPixel(uint32_t x, uint32_t y, uint32_t color);
36 | void drawCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color);
37 | void fillCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color);
38 | void drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color);
39 | void drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color);
40 |
41 | void setCursor(int16_t x, int16_t y);
42 | void setTextSize(uint8_t size);
43 | void setTextColor(uint16_t fgcolor, uint16_t bgcolor);
44 | void print(..); // from Print
45 | void println(..); // from Print
46 |
47 | // when defined BASIC_FONT_SUPPORT follow function may be not avalible
48 | void setTextFont(uint8_t font);
49 | void drawString(..)
50 | };
51 | @endcode
52 |
53 | @subsection chips Supported resistive chips
54 | - ADS7846 https://www.ti.com/lit/ds/symlink/ads7846.pdf
55 | - TSC2046 https://www.ti.com/lit/ds/symlink/tsc2046.pdf
56 | - XPT2064 https://www.buydisplay.com/download/ic/XPT2046.pdf
57 | - ADS7843 https://www.ti.com/lit/ds/symlink/ads7843.pdf
58 | - ADS7845 http://www.ti.com/lit/ds/symlink/ads7845.pdf
59 | - AD7873 https://www.analog.com/media/en/technical-documentation/data-sheets/ad7873.pdf
60 | - AD7843 https://www.analog.com/media/en/technical-documentation/data-sheets/ad7843.pdf
61 |
62 | @subsection raw Raw value definition
63 | All measured values (X, Y, Z1, Z2) has 12bit resolution, range is 0 .. 4095, bud valid range is 25 .. 4000.
64 |
65 | You can adjust the valid range with TFT_eTouchBase::setValidRawRange().
66 | @subsection time Time for next fetch
67 | You can adjust the time between measures with TFT_eTouchBase::setMeasureWait(). As long the time is not reached, you get the same value from TFT_eTouchBase::getRaw(), TFT_eTouch::getXY() and TFT_eTouch::get().
68 |
69 | @subsection algo Fetch algorithm
70 | There are two variants in sbaa036.pdf (Figure 10 and 11 page 8). You can wait til measure is same last measure (Figure 10) or taking the n conversation (Figure 11) or averaging n measures.
71 | When you averaging you can also ignore min and max measure.
72 |
73 | You can adjust the algorithm with TFT_eTouchBase::setMeasure() and TFT_eTouchBase::setAveraging().
74 |
75 | If this is still noisy you can have a try with FIR filter. When using FIR there is a large delay.
76 |
77 | @subsection calibrate Calibrate
78 | There is a document from TI slyt277.pdf whitch describes calibration. In my callibration i use only offset and scaling correction no rotation correction.
79 | You can get a new calibation (TFT_eTouchBase::Calibation) with TFT_eTouch::getCalibration(). A existing calibration can be set with
80 | TFT_eTouchBase::setCalibration().
81 |
82 | @section todo To Do
83 | @subsection read_write_cal calibation persistent
84 | We have to write a function to read/write the calibation from EPROM. (Atmel & Teensy missing) ESP32 & ESP8266 done.
85 | bool TFT_eTouchBase::readCalibration(), bool TFT_eTouchBase::writeCalibration().
86 |
87 | @subsection analog analog Touch with X+, X-, Y+, Y-
88 | Support analog Touch.
89 |
90 | @subsection gesture gesture recognition
91 | Next will be gesture recognition. stay, wipe left, wipe right, wipe up and wipe down. (with one ore two fingers)
92 | If its possible zoom in / out. (only prepered)
93 |
94 | @section reference Reference
95 | @subsection pdf External documentation
96 | - http://www.ti.com/lit/an/sbaa036/sbaa036.pdf
97 | - http://www.ti.com/lit/an/slyt277/slyt277.pdf
98 |
99 | @subsection arduino Existing touch classes for Arduino
100 | - https://github.com/adafruit/Adafruit_STMPE610
101 | - https://github.com/PaulStoffregen/XPT2046_Touchscreen
102 | - https://github.com/adafruit/Touch-Screen-Library (analog Touch)
103 |
104 | using:
105 | - https://gist.github.com/CelliesProjects/99a56121e7c8a7cb00d5b46270e74b75
106 |
107 | @subsection linux Linux kernel sources
108 | - {linux_scr}/include/linux/spi/ads7846.h
109 | - {linux_scr}/drivers/drivers/input/touchscreen/ads7846.c
110 |
111 | - {linux_scr}/drivers/staging/fbtft
112 |
113 | @section history History
114 | - v0.4.0 First draft working with TFT_eSPI < 1.4.0
115 | - v0.5.0 tested with Tennsy 3.1, ESP32 added load save Configuration, added struct interface for raw & tft values, increasing speed when not touched.
116 | @subsection patch 1.4.0 < TFT_eSPI < 1.4.8 (latest TFT_eSPI lib has this patch)
117 | - add static getSPIinstance() function, for getting spi bus used by touchscreen.
118 | - when default config, (VSPI Port) do not create a new var, use the SPI var exported by SPI.h
119 | Header file TFT_eSPI.h
120 | @code
121 | void getSetup(setup_t& tft_settings); // Sketch provides the instance to populate
122 | +#ifndef ESP32_PARALLEL
123 | + static SPIClass& getSPIinstance();
124 | +#endif
125 |
126 | int32_t cursor_x, cursor_y, padX;
127 | @endcode
128 | Sourcefile TFT_eSPI.cpp
129 | @code
130 | #if defined (ESP32)
131 | #if !defined (ESP32_PARALLEL)
132 | #ifdef USE_HSPI_PORT
133 | SPIClass spi(HSPI);
134 | #else // use default VSPI port
135 | +- SPIClass& spi = SPI; // use the predefined SPI variable which is defined by SPI.h
136 | #endif
137 | #endif
138 | #else // ESP8266
139 | +- SPIClass& spi = SPI; // use the predefined SPI variable which is defined by SPI.h
140 | #endif
141 |
142 | +#ifndef ESP32_PARALLEL
143 | +SPIClass& TFT_eSPI::getSPIinstance() { return spi; }
144 | +#endif
145 |
146 | @endcode
147 |
148 | - v0.6.0 tested with Tennsy 3.1, ESP32, ESP8266, Arduino NANO.
149 | - ESP8266 support
150 | - added FIR filter
151 |
152 | */
153 | EOF
154 |
--------------------------------------------------------------------------------
/TFT_eTouchUser.h:
--------------------------------------------------------------------------------
1 | #ifndef TFT_E_TOUCH_USER_H
2 | #define TFT_E_TOUCH_USER_H
3 |
4 | //
5 | // TFT_eTouchUser.h
6 | //
7 | // (C) Copyright Achill Hasler 2019.
8 | //
9 | // Distributed under the Boost Software License, Version 1.0.
10 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt
11 | //
12 | //
13 | // See TFT_eTouch/docs/html/index.html for documentation.
14 | //
15 |
16 | /** @def TFT_ETOUCH_CS
17 | * Define the chip select pin for the touch chip.
18 | */
19 | #if defined (TEENSYDUINO) // with ILI9341
20 | # define TFT_ETOUCH_CS 15
21 | #elif defined (ESP8266) // with ILI9341
22 | # define TFT_ETOUCH_CS D1 // Chip select pin (T_CS)
23 | #elif 1
24 | # define TFT_ETOUCH_CS 3
25 | #else
26 | # define TFT_ETOUCH_CS SS
27 | #endif
28 |
29 | /** @def TFT_ETOUCH_PIRQ
30 | * Define the irq pin for the touch chip.
31 | */
32 | #if 0
33 | # define TFT_ETOUCH_PIRQ 6
34 | #elif 0 //defined (ESP8266) // with ILI9341
35 | # define TFT_ETOUCH_PIRQ D0 // is touched signal pin (T_IRQ) is high when touched
36 | #elif 0
37 | # define TFT_ETOUCH_PIRQ 1
38 | #else
39 | # define TFT_ETOUCH_PIRQ 255
40 | #endif
41 |
42 | // include used TFT driver
43 | #if 0 // using Adafuit lib ILI9341
44 | #include
45 | typedef Adafruit_ILI9341 TFT_Driver;
46 |
47 | # define TFT_DC 4
48 | # define TFT_CS 3
49 | # define TFT_RST 17 // on SoftwareReset use -1, and connect pin to 3.3V
50 |
51 | #elif defined (TEENSYDUINO) // with ILI9341
52 | # include // Hardware-specific TFT library
53 | typedef ILI9341_t3 TFT_Driver;
54 |
55 | # define TFT_DC 20 // Data Command control pin
56 | # define TFT_CS 21 // Chip select pin
57 | # define TFT_RST 2 // on SoftwareReset use 255, and connect pin to 3.3V
58 | # define TFT_MOSI 7 // MasterOut SlaveIn pin (DOUT)
59 | # define TFT_SCLK 14 // SPI clock (SCK)
60 | # define TFT_MISO 12 // MasterIn SlaveOut pin (DIN)
61 | # define TFT_BL 3 // Backlight pin must have PWM for analogWrite if set
62 |
63 | #else // ESP_PLATFORM with eSPI
64 | #include // Hardware-specific TFT library
65 | typedef TFT_eSPI TFT_Driver;
66 | // manage your TFT setup in TFT_eSPI UserSetup.h
67 |
68 | # ifdef TOUCH_CS
69 | #error undef TOUCH_CS in TFT_eSPI UserSetup.h for using TFT_eTouch
70 | # endif
71 |
72 | #endif
73 |
74 | #ifndef TFT_ETOUCH_PIRQ
75 | # define TFT_ETOUCH_PIRQ 255
76 | #else
77 | # if (TFT_ETOUCH_PIRQ != 255)
78 | // if pin is set and valid we have to use penirq code
79 | # define TOUCH_USE_PENIRQ_CODE
80 | # endif
81 | #endif
82 |
83 | /** @def TOUCH_USE_PENIRQ_CODE
84 | * If this define is set the penrirq code is used.
85 | */
86 | // undefine this to save progmem if you not have penirq or not using it
87 | #ifndef TOUCH_USE_PENIRQ_CODE
88 | //#define TOUCH_USE_PENIRQ_CODE
89 | #endif
90 |
91 | /** @def TOUCH_USE_AVERAGING_CODE
92 | * If this defined is set the averaging option is available.
93 | */
94 | // undefine this to save progmem if you not using averaging
95 | #define TOUCH_USE_AVERAGING_CODE
96 |
97 | /** @def TOUCH_USE_USER_CALIBRATION
98 | * If this defined is set the member function getUserCalibration() is available.
99 | */
100 | // undefine this to save progmem if you don't use getUserCalibration() anymore
101 | #define TOUCH_USE_USER_CALIBRATION
102 |
103 | /** @def TOUCH_USE_GESTURE
104 | * If this defined is set the gesture interface is available. (work in progress)
105 | */
106 | // undefine this to save progmem if you don't use gesture interface anymore
107 | //#define TOUCH_USE_GESTURE
108 |
109 | /** @def TOUCH_USE_SIMPE_TARGET
110 | * If this defined is set the function getUserCalibration() show simple target.
111 | */
112 | // define this to save progmem, simple target wont write outside dispay coordinates (undefine it looks nicer, bud can core on some dispay drivers)
113 | #define TOUCH_USE_SIMPE_TARGET
114 |
115 | /** @def TOUCH_USE_DIFFERENTIAL_MEASURE
116 | * If this defined is set the 'Differential Measure' mode is used. (SER/DFR low)
117 | * When undefined 'Single Ended Measure' mode is active. (SER/DFR high)
118 | */
119 | #define TOUCH_USE_DIFFERENTIAL_MEASURE
120 |
121 | /** @def TOUCH_DEFAULT_CALIBRATION
122 | * This is the used touch configuration. If it's match to your configuration, you can disable TOUCH_USE_USER_CALIBRATION.
123 | */
124 | #define TOUCH_DEFAULT_CALIBRATION { 272, 3749, 3894, 341, 0 }
125 |
126 | /** @def TOUCH_FILTER_TYPE
127 | * If this defined is set the touch driver filter raw data with a fir filter,
128 | * define additional TOUCH_X_FILTER, TOUCH_Y_FILTER, TOUCH_Z_FILTER for the value to filter
129 | * Window type:
130 | * - 1: Hamming
131 | * - 2: Hanning
132 | * - 3: Blackmann
133 | */
134 | //#define TOUCH_FILTER_TYPE 1
135 |
136 | /** @def BASIC_FONT_SUPPORT
137 | * If this defined is set the tft driver lacks setTextFont() and drawString().
138 | */
139 | //#define BASIC_FONT_SUPPORT
140 |
141 | /** @def TOUCH_SERIAL_DEBUG
142 | * If this defined is set the library show info to Serial.
143 | */
144 | // define this to see additional output on Serial
145 | #define TOUCH_SERIAL_DEBUG
146 |
147 | /** @def TOUCH_SERIAL_CONVERSATION_TIME
148 | * If this defined is set the library show time of one conversation in microsecond to Serial.
149 | * When TOUCH_SERIAL_DEBUG_FETCH is defined you got additional time from the print call!
150 | * When defined, getUserCalibration() won't work.
151 | */
152 | // define this to see converation time on Serial for fetching raw values
153 | //#define TOUCH_SERIAL_CONVERSATION_TIME
154 |
155 | /** @def TOUCH_SERIAL_DEBUG_FETCH
156 | * If this defined is set the library show ctrl command of invalid read measure to Serial.
157 | */
158 | // define this to see if X, Y, Z1 or Z2 measure is out of range when not touched
159 | //#define TOUCH_SERIAL_DEBUG_FETCH
160 |
161 |
162 | #ifdef TOUCH_FILTER_TYPE
163 | #include
164 | // undefine a filter or change N, N must be even (default 12), T must be uint16_t whitch is default
165 | #define TOUCH_X_FILTER FirFilter<20>
166 | #define TOUCH_Y_FILTER TOUCH_X_FILTER
167 | //#define TOUCH_Z_FILTER FirFilter<>
168 | #endif
169 |
170 | #ifndef TOUCH_DEFAULT_CALIBRATION
171 | #define TOUCH_DEFAULT_CALIBRATION { 300, 3700, 300, 3700, 2 }
172 | #endif
173 |
174 | #if defined (_ILI9341_t3H_) || defined (_ADAFRUIT_ILI9341H_)
175 | // color used by TFT_eTouch
176 | #define TFT_BLACK ILI9341_BLACK
177 | #define TFT_BLUE ILI9341_BLUE
178 | #define TFT_GREEN ILI9341_GREEN
179 | #define TFT_RED ILI9341_RED
180 | #define TFT_WHITE ILI9341_WHITE
181 |
182 | // missing setTextFont, drawString
183 | #ifndef BASIC_FONT_SUPPORT
184 | #define BASIC_FONT_SUPPORT
185 | #endif
186 | #endif
187 |
188 |
189 | #ifdef DOXYGEN
190 | // we set all for getting documentation
191 | #define TOUCH_USE_PENIRQ_CODE
192 | #define TOUCH_USE_AVERAGING_CODE
193 | #define TOUCH_USE_USER_CALIBRATION
194 | #define TOUCH_USE_SIMPE_TARGET
195 | #define TOUCH_USE_GESTURE
196 | #define TOUCH_USE_DIFFERENTIAL_MEASURE
197 | #define BASIC_FONT_SUPPORT
198 | #define TOUCH_SERIAL_DEBUG
199 | #define TOUCH_SERIAL_CONVERSATION_TIME
200 | #define TOUCH_SERIAL_DEBUG_FETCH
201 | #define TOUCH_FILTER_TYPE 1
202 | #endif
203 |
204 | #endif // TFT_E_TOUCH_USER_H
205 |
--------------------------------------------------------------------------------
/examples/generic/raw/raw.ino:
--------------------------------------------------------------------------------
1 | /**
2 | Sketch to see raw touch values.
3 |
4 | The sketch has been tested with compatible ADS7846 chip on:
5 | - ESP32 (TFT_eSPI, Adafruit_ILI9341)
6 | - ESP8322 (TFT_eSPI)
7 | - Tensy 3.1 (ILI9341_t3)
8 | - Arduino Nano (Adafruit_ILI9341)
9 | */
10 |
11 | #include
12 | #include
13 |
14 | //------------------------------------------------------------------------------------------
15 |
16 | #define TFT_ROTATION 1
17 | //#define MINMAX
18 | #define WITH_DISPLAY // some Displays make touch signal noisy
19 | //#define SECOND_SPI_PORT
20 |
21 | //------------------------------------------------------------------------------------------
22 | #ifdef SECOND_SPI_PORT
23 | #define TFT_ETOUCH_SCK 14
24 | #define TFT_ETOUCH_MISO 12
25 | #define TFT_ETOUCH_MOSI 13
26 |
27 | SPIClass hSPI(HSPI);
28 | #endif
29 | //------------------------------------------------------------------------------------------
30 |
31 | #ifdef WITH_DISPLAY
32 | # ifdef _ADAFRUIT_ILI9341H_
33 | Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
34 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ);
35 |
36 | # elif defined (_ILI9341_t3H_)
37 | ILI9341_t3 tft(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
38 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ);
39 |
40 | # elif defined (_TFT_eSPIH_)
41 | TFT_eSPI tft;
42 | # ifdef SECOND_SPI_PORT
43 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ, hSPI);
44 | # else
45 | # ifdef ESP32_PARALLEL
46 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ, SPI);
47 | # else
48 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ, TFT_eSPI::getSPIinstance());
49 | # endif
50 | # endif
51 | # else
52 | # error definition missing in TFT_eTouchUser.h
53 | # endif
54 | #else
55 |
56 | # ifdef SECOND_SPI_PORT
57 | TFT_eTouchBase touch(TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ, hSPI);
58 | # else
59 | TFT_eTouchBase touch(TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ, SPI);
60 | # endif
61 |
62 | #endif
63 |
64 | void setup() {
65 | Serial.begin(115200);
66 | delay(2000);
67 |
68 | #ifdef SECOND_SPI_PORT
69 | // hSPI.begin(); // ESP32 default SCK: 14, MISO: 12, MOSI: 13, SS :15.
70 | hSPI.begin(TFT_ETOUCH_SCK, TFT_ETOUCH_MISO, TFT_ETOUCH_MOSI, TFT_ETOUCH_CS);
71 | #endif
72 | #ifdef ESP32_PARALLEL
73 | SPI.begin();
74 | #endif
75 | #ifdef WITH_DISPLAY
76 | tft.begin();
77 | touch.init();
78 | #else
79 | #ifdef TEENSYDUINO
80 | SPI.setMOSI(TFT_MOSI);
81 | SPI.setMISO(TFT_MISO);
82 | SPI.setSCK(TFT_SCLK);
83 | #else
84 | // raw test on ESP32 support only default SPI bus (MISO, MOSI & SCLK from pins_arduino.h)
85 | #endif
86 | touch.init(true);
87 | #endif
88 | while (!Serial) ; // wait for Arduino Serial Monitor
89 | // touch.setMeasure(0, false, true, false, 3); // constructor defaults (take third measure, start with z axis)
90 | // touch.setAveraging(false, false); // constructor defaults (without averaging)
91 |
92 | // touch.setMeasure(0, true, true, false, 1); // Differential mode fastest (each axis read only once, may work)
93 | // touch.setAcurateDistance(25); // in this mode acurate distance must be higher for getUserCalibration (default 10)
94 |
95 | // touch.setMeasure(1, true, true, false, 1); // Differential mode faster (take second z bud first x, y. may work)
96 | // touch.setAcurateDistance(25); // in this mode acurate distance must be higher for getUserCalibration (default 10)
97 |
98 | // touch.setMeasure(10, false, false, true, 14); // slowest (drop 10 and additional 16 measures. averaging 14 with min max value removed)
99 | // touch.setAveraging(true, true);
100 |
101 | // when noisy
102 | //touch.setMeasure(1, false, true, true, 3); // z first, drop 1 and additional 5 measures. averaging 3 with min max value removed
103 | //touch.setAveraging(true, true);
104 | //touch.setAcurateDistance(100); // or even higher for getUserCalibration (default 10)
105 | // or
106 | touch.setAcurateDistance(80); // and define TOUCH_FILTER_TYPE in TFT_eTouchUser.h
107 |
108 | // untouched: 35 us touched: 95 us (good choice for starting)
109 | touch.setMeasure(0, false, true, false, 2); // z first, take 2'th z,x,y
110 | /* if you get output like that when calling getUserCalibration, you have a noisy touch signal
111 | acurate on point[4, 4](233, 371) dx: 287 dy: 27 > 10 nok
112 | acurate on point[4, 4](283, 365) dx: 192 dy: 17 > 10 nok
113 | acurate on point[4, 4](349, 399) dx: 192 dy: 16 > 10 nok
114 | acurate on point[4, 4](334, 400) dx: 204 dy: 11 > 10 nok
115 | */
116 |
117 |
118 | // touch.setMeasure(); // defaults (accurate all axis, start with x)
119 | // touch.setMeasure(1, true, true, true); // z with local min, acurate x,y
120 |
121 | // touch.setValidRawRange(10, 4085);
122 | // touch.setRXPlate(1000);
123 | // touch.setRZThreshold(1000*3);
124 |
125 | #ifdef WITH_DISPLAY
126 | tft.setRotation(TFT_ROTATION);
127 | delay(1);
128 | tft.fillScreen(TFT_BLACK);
129 | #ifdef BASIC_FONT_SUPPORT
130 | const char* str;
131 | str = "Touch raw test!";
132 | int16_t len = strlen(str) * 6;
133 | tft.setCursor((tft.width() - len)/2, tft.height()/2);
134 | tft.print(str);
135 | #else
136 | tft.drawCentreString("Touch raw test!", tft.width()/2, tft.height()/2, 2);
137 | #endif
138 | #endif
139 | }
140 |
141 | void loop(void) {
142 | static uint32_t last_update = 0;
143 | if (last_update + touch.getMeasureWait() > millis()) return;
144 | last_update = millis();
145 | uint16_t x = 0, y = 0, z1 = 0, z2 = 0, rz = 0; // To store the touch coordinates
146 | if (!touch.getRaw(x, y, z1, z2, rz)) return;
147 |
148 | #ifdef MINMAX
149 | static uint16_t count = 0;
150 | static uint16_t x_min = 0xffff, y_min = 0xffff, z1_min = 0xffff, z2_min = 0xffff, rz_min = 0xffff;
151 | static uint16_t x_max = 0, y_max = 0, z1_max = 0, z2_max = 0, rz_max = 0; // To store the touch coordinates
152 | if (x < x_min) x_min = x; if (x > x_max) x_max = x;
153 | if (y < y_min) y_min = y; if (y > y_max) y_max = y;
154 | if (z1 < z1_min) z1_min = z1; if (z1 > z1_max) z1_max = z1;
155 | if (z2 < z2_min) z2_min = z2; if (z2 > z2_max) z2_max = z2;
156 | if (rz < rz_min) rz_min = rz; if (rz > rz_max) rz_max = rz;
157 | if (count++ > 100) {
158 | count = 0;
159 | Serial.print("x (min, max),y (min, max),z1 (min, max),z2 (min, max),rz (min, max) = (");
160 | Serial.print(x_min);
161 | Serial.print(",");
162 | Serial.print(x_max);
163 | Serial.print("),(");
164 | Serial.print(y_min);
165 | Serial.print(",");
166 | Serial.print(y_max);
167 | Serial.print("),(");
168 | Serial.print(z1_min);
169 | Serial.print(",");
170 | Serial.print(z1_max);
171 | Serial.print("),(");
172 | Serial.print(z2_min);
173 | Serial.print(",");
174 | Serial.print(z2_max);
175 | Serial.print("),(");
176 | Serial.print(rz_min);
177 | Serial.print(",");
178 | Serial.print(rz_max);
179 | Serial.println(")");
180 | }
181 | #else
182 | Serial.print("x,y,z1,z2,rz = ");
183 | Serial.print(x);
184 | Serial.print(",");
185 | Serial.print(y);
186 | Serial.print(",");
187 | Serial.print(z1);
188 | Serial.print(",");
189 | Serial.print(z2);
190 | Serial.print(",");
191 | Serial.println(rz);
192 | #endif
193 | // delay(touch.getMeasureWait() + 1); // wait a little bit longer, for getting new measure values
194 | }
195 |
--------------------------------------------------------------------------------
/examples/generic/edit_calibation/edit_calibation.ino:
--------------------------------------------------------------------------------
1 | /**
2 | Sketch to generate the setup() calibration values, these are reported
3 | to the Serial Monitor.
4 |
5 | The sketch has been tested on the ESP32 with compatible ADS7846 chip and TFT_eSPI driver.
6 | define TEST_RAW_INTERFACE in TMenu.h for raw test.
7 | */
8 |
9 | #include
10 | #include
11 |
12 | #include "TMenu.h"
13 |
14 | //------------------------------------------------------------------------------------------
15 |
16 | #define TFT_ROTATION 1
17 |
18 | //------------------------------------------------------------------------------------------
19 | #ifdef _ADAFRUIT_ILI9341H_
20 | Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
21 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ);
22 |
23 | #elif defined (_ILI9341_t3H_)
24 | ILI9341_t3 tft(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
25 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ);
26 |
27 | #elif defined (_TFT_eSPIH_)
28 | TFT_eSPI tft;
29 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ, TFT_eSPI::getSPIinstance());
30 |
31 | #else
32 | # error definition missing in TFT_eTouchUser.h
33 | #endif
34 |
35 | TMenu menue(touch);
36 |
37 | //------------------------------------------------------------------------------------------
38 | #define CALIBRATION_FILE "/TFT_eTouch.cal"
39 |
40 | void setup() {
41 | Serial.begin(115200);
42 | delay(2000);
43 |
44 | tft.begin();
45 | touch.init();
46 | while (!Serial) ; // wait for Arduino Serial Monitor
47 |
48 | #if 0
49 | // untouched: 35 us touched: 136 us
50 | touch.setMeasure(0, false, true, false, 3); // constructor defaults (take third measure, start with z axis)
51 | touch.setAveraging(false, false); // constructor defaults (without averaging)
52 |
53 | #elif 0
54 | // untouched: 35 us touched: 56 us
55 | touch.setMeasure(0, true, true, false, 1); // Differential mode fastest (each axis read only once, may work)
56 | #ifdef TOUCH_USE_USER_CALIBRATION
57 | touch.setAcurateDistance(25); // in this mode acurate distance must be higher for getUserCalibration (default 10)
58 | #endif
59 |
60 | #elif 0
61 | // untouched: 35 us touched: 77 us
62 | touch.setMeasure(1, true, true, false, 1); // Differential mode faster (take second z bud first x, y. may work)
63 | #ifdef TOUCH_USE_USER_CALIBRATION
64 | touch.setAcurateDistance(25); // in this mode acurate distance must be higher for getUserCalibration (default 10)
65 | #endif
66 |
67 | #elif 0
68 | // untouched: 35 us touched: d 190 max 600 us
69 | touch.setMeasure(1, true, true, true); // z with local min, acurate x,y, 35 us tot d 200 max 600
70 |
71 | #elif 0
72 | // untouched: 35 us touched: d 220 max 600 us
73 | touch.setMeasure(0, false, true); // z first, acurate z,x,y
74 |
75 | #elif 0
76 | // untouched: 35 us touched: d 190 max 600 us
77 | touch.setMeasure(2, true, true, false); // z first 3end, acurate x,y
78 |
79 | #elif 0
80 | // untouched: 35 us touched: 95 us
81 | touch.setMeasure(1, true, true, false, 2); // z first, take 2'th z,x,y
82 |
83 | #elif 0
84 | // untouched: 35 us touched: 95 us (good choice for starting, when not noisy)
85 | touch.setMeasure(0, false, true, false, 2); // z first, take 2'th z,x,y
86 |
87 | #elif 0
88 | // when noisy
89 | touch.setMeasure(0, false, true, true, 2); // z first, no drop and additional 4 measures. averaging 2 with min max value removed
90 | touch.setAveraging(true, true);
91 | #ifdef TOUCH_USE_USER_CALIBRATION
92 | touch.setAcurateDistance(100); // or even higher for getUserCalibration (default 10)
93 | #endif
94 |
95 | #elif 0
96 | // when noisy
97 | touch.setMeasure(1, false, true, true, 3); // z first, drop 1 and additional 5 measures. averaging 3 with min max value removed
98 | touch.setAveraging(true, true);
99 | #ifdef TOUCH_USE_USER_CALIBRATION
100 | touch.setAcurateDistance(100); // or even higher for getUserCalibration (default 10)
101 | #endif
102 |
103 | #elif 0
104 | // when noisy, untouched: 35 us touched: 334 us
105 | touch.setMeasure(3, false, true, false, 3); // drop 3 averaging 3 (+2 min max)
106 | touch.setAveraging(true, true);
107 | #ifdef TOUCH_USE_USER_CALIBRATION
108 | touch.setAcurateDistance(100); // or even higher for getUserCalibration (default 10)
109 | #endif
110 |
111 | #elif 0
112 | // when noisy, untouched: 35 us touched: 1302 us
113 | touch.setMeasure(10, false, true, true, 14); // slowest (drop 10(..255) and additional 16 measures. averaging 14 with min max value)
114 | touch.setAveraging(true, true);
115 | #ifdef TOUCH_USE_USER_CALIBRATION
116 | touch.setAcurateDistance(100); // or even higher for getUserCalibration (default 10)
117 | #endif
118 |
119 | #elif 1
120 | // when noisy, set a fast measure and define TOUCH_FILTER_TYPE in TFT_eTouchUser.h
121 | touch.setMeasure(0, false, true, false, 2); // z first, take 2'th z,x,y
122 | #ifdef TOUCH_USE_USER_CALIBRATION
123 | touch.setAcurateDistance(64); // in this mode acurate distance must be higher for getUserCalibration (default 10)
124 | #endif
125 |
126 | #else
127 | // default: untouched: 35 us touched: 136 us (take third measure, start with z axis)
128 | #endif
129 |
130 | touch.setRXPlate(1000);
131 | touch.setRZThreshold(1000*3);
132 | // touch.setMeasureWait(10);
133 |
134 | // SPIFFS.remove(CALIBRATION_FILE);
135 | if (!touch.readCalibration(CALIBRATION_FILE)) {
136 | #ifdef TOUCH_USE_USER_CALIBRATION
137 | TFT_eTouchBase::Calibation cal;
138 | touch.getUserCalibration(cal, 4);
139 | touch.setCalibration(cal);
140 | #else
141 | Serial.printf("Calibration not readed %s take default configuration %s\n", CALIBRATION_FILE, TOUCH_DEFAULT_CALIBRATION);
142 | #endif
143 | }
144 |
145 | tft.setRotation(TFT_ROTATION);
146 | menue.init(); // after rotation
147 | }
148 |
149 | //------------------------------------------------------------------------------------------
150 |
151 | void loop(void) {
152 | static bool was_touched = false;
153 | static uint32_t last_touch = 0;
154 |
155 | if (last_touch + touch.getMeasureWait() > millis()) return;
156 | last_touch = millis();
157 |
158 | #ifdef TEST_RAW_INTERFACE
159 | TFT_eTouchBase::Measure raw;
160 | if (touch.getRaw(raw)) {
161 | menue.update(raw);
162 | #else
163 | int16_t x, y; // screen coordinates
164 | if (touch.getXY(x, y)) {
165 | menue.update(x, y, touch.getRZ());
166 | #endif
167 | if (!was_touched) {
168 | was_touched = true;
169 | Serial.println("pen down");
170 | // we have pen down
171 | menue.pen_down();
172 | }
173 | menue.refresh();
174 | // Serial.println("pen activ");
175 | }
176 | else {
177 | if (was_touched) {
178 | was_touched = false;
179 | Serial.print("pen up, ");
180 | // we have pen up
181 | switch (menue.pen_up()) {
182 | case changed:
183 | Serial.println("sig changed");
184 | break;
185 | case calibrate: {
186 | #ifdef TOUCH_USE_USER_CALIBRATION
187 | Serial.println("sig calibrate");
188 | TFT_eTouchBase::Calibation cal;
189 | touch.getUserCalibration(cal, 4);
190 | touch.setCalibration(cal);
191 | menue.init();
192 | #else
193 | Serial.println("sig calibrate disabled");
194 | #endif
195 | }
196 | break;
197 | case store:
198 | Serial.println("sig store");
199 | touch.writeCalibration(CALIBRATION_FILE);
200 | break;
201 | case none:
202 | default:
203 | Serial.println("sig none");
204 | break;
205 | }
206 | menue.refresh();
207 | }
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/examples/generic/edit_calibation/TMenu.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // TMenu.cpp
3 | //
4 | // (C) Copyright Achill Hasler 2019.
5 | //
6 | // Distributed under the Boost Software License, Version 1.0.
7 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt
8 | //
9 |
10 | #include "TMenu.h"
11 |
12 | TMenu::TMenu(TFT_eTouch& touch, uint8_t offset)
13 | : touch_(touch)
14 | , offset_(offset)
15 | #ifndef TEST_RAW_INTERFACE
16 | , x_(0)
17 | , y_(0)
18 | , rz_(0)
19 | #endif
20 | , touched_(false)
21 | , cursor_visible_(false)
22 | {
23 | }
24 |
25 | TFT_Driver& TMenu::tft()
26 | {
27 | return touch_.tft();
28 | }
29 |
30 | EventType TMenu::pen_up()
31 | {
32 | EventType ret = none;
33 | touched_ = false;
34 | touch_.reset();
35 | for (uint8_t i = 0; i < 4 ; i++) {
36 | ret = mc_[i].signal();
37 | mc_[i].pen_up();
38 | if (ret > changed) break;
39 | }
40 | #ifdef TEST_RAW_INTERFACE
41 | tp_.rz++;
42 | #else
43 | rz_++; // change a littlte bit, for hideing cursor in next refresh call
44 | #endif
45 | return ret;
46 | }
47 |
48 | EventType TMenu::pen_down()
49 | {
50 | touched_ = true;
51 | return none;
52 | }
53 |
54 | void TMenu::init()
55 | {
56 | TFT_eTouchBase::Calibation& calibation = touch_.calibration();
57 | uint8_t d_rot = (4 + tft().getRotation() - calibation.rel_rotation) % 4;
58 |
59 | // Serial.printf("d_rot %d\n", d_rot);
60 |
61 | for (uint8_t i = 0; i < 4 ; i++) {
62 | mc_[i].oner(this);
63 | }
64 | // pos from d_rot == 0
65 | mc_[0].pos(offset_, (tft().height() - 5 * mc_[0].fw()) / 2, false);
66 | mc_[1].pos((tft().width() - 5 * mc_[1].fw()) / 2, offset_, true);
67 | mc_[2].pos(tft().width() - 1 - offset_ - mc_[2].fw(), (tft().height() - 5 * mc_[2].fw()) / 2, false);
68 | mc_[3].pos((tft().width() - 5 * mc_[3].fw())/2, tft().height() - 1 - offset_ - mc_[3].fw(), true);
69 |
70 | mc_[(4 + 0 - d_rot) % 4].cmd('X', none); // do nothing
71 | mc_[(4 + 0 - d_rot) % 4].val(calibation.x0);
72 |
73 | mc_[(4 + 1 - d_rot) % 4].cmd('Y', none); // do nothing
74 | mc_[(4 + 1 - d_rot) % 4].val(calibation.y0);
75 |
76 | mc_[(4 + 2 - d_rot) % 4].cmd('S', store); // store calibation on pen up
77 | mc_[(4 + 2 - d_rot) % 4].val(calibation.x1);
78 |
79 | mc_[(4 + 3 - d_rot) % 4].cmd('C', calibrate); // calibrate on pen up
80 | mc_[(4 + 3 - d_rot) % 4].val(calibation.y1);
81 |
82 | // Clear the screen
83 | tft().fillScreen(TFT_BLACK);
84 |
85 | #ifdef BASIC_FONT_SUPPORT
86 | tft().setTextSize(1);
87 | char* str;
88 | str = "Touch screen!";
89 | int16_t len = strlen(str) * 6;
90 | tft().setCursor((tft().width() - len)/2, tft().height()/2 - 20);
91 | tft().print(str);
92 |
93 | str = "C: calibrate";
94 | len = strlen(str) * 6;
95 | tft().setCursor((tft().width()-len)/2 , tft().height()/2 - 0);
96 | tft().print(str);
97 |
98 | str = "S: store";
99 | len = strlen(str) * 6;
100 | tft().setCursor((tft().width()-len)/2 , tft().height()/2 + 20);
101 | tft().print(str);
102 |
103 | str = "(on pen up)";
104 | len = strlen(str) * 6;
105 | tft().setCursor((tft().width()-len)/2 , tft().height()/2 + 40);
106 | tft().print(str);
107 | #else
108 | tft().setTextFont(2);
109 | tft().setTextDatum(TC_DATUM);
110 | tft().drawString("Touch screen!", tft().width()/2, tft().height()/2 - 20, 2);
111 | tft().drawString("C: calibrate", tft().width()/2, tft().height()/2 - 0, 2);
112 | tft().drawString("S: store", tft().width()/2, tft().height()/2 + 20, 2);
113 | tft().drawString("(on pen up)", tft().width()/2, tft().height()/2 + 40, 2);
114 | tft().setTextDatum(TL_DATUM);
115 | #endif
116 |
117 | refresh();
118 | }
119 |
120 | void TMenu::draw()
121 | {
122 | for (uint8_t i = 0; i < 4 ; i++) {
123 | mc_[i].draw();
124 | }
125 | }
126 |
127 | void TMenu::info(uint8_t offset)
128 | {
129 | #ifdef TEST_RAW_INTERFACE
130 | tft().setCursor(offset, offset);
131 | tft().printf("Pos: %d, %d, %d ", tp_.x, tp_.y, tp_.rz);
132 |
133 | if (tp_.rz > touch_.getRXPlate()*174/100) tft().println("Pen ");
134 | else if (tp_.rz >= touch_.getRXPlate()*66/100) tft().println("Finger ");
135 | else if (tp_.rz > touch_.getRXPlate()*15/100) tft().println("2 Finger ");
136 | else tft().println("+ 2 Finger ");
137 |
138 | tft().setCursor(offset, tft().getCursorY());
139 | tft().printf("Raw: %d, %d, %d,%d \n", raw_.x, raw_.y, raw_.z1, raw_.z2);
140 | #else
141 | uint16_t xr;
142 | uint16_t yr;
143 | uint16_t z1;
144 | uint16_t z2;
145 | uint16_t rz;
146 | touch_.getRaw(xr, yr, z1, z2, rz);
147 |
148 | tft().setCursor(offset, offset);
149 | tft().printf("Pos: %d, %d, %d ", x_, y_, rz_);
150 | if (rz_ > touch_.getRZThreshold()*58/100) tft().println("Pen ");
151 | else if (rz_ >= touch_.getRZThreshold()*22/100) tft().println("Finger ");
152 | else if (rz_ > touch_.getRZThreshold()*5/100) tft().println("2 Finger ");
153 | else tft().println("+ 2 Finger ");
154 |
155 | tft().setCursor(offset, tft().getCursorY());
156 | tft().printf("Raw: %d, %d, %d,%d (%s) \n", xr, yr, z1, z2, (rz == rz_) ? "same" : "new" );
157 |
158 | #endif
159 | tft().setCursor(offset, tft().getCursorY());
160 | TFT_eTouchBase::Calibation& calibation = touch_.calibration();
161 | tft().printf("Cal: %d,%d, %d,%d, %d \n", calibation.x0, calibation.x1, calibation.y0, calibation.y1, calibation.rel_rotation);
162 | }
163 |
164 | #ifdef TEST_RAW_INTERFACE
165 | void TMenu::update(const TFT_eTouchBase::Measure& raw)
166 | {
167 | if (touch_.transform(raw, tp_)) {
168 | raw_ = raw;
169 | for (uint8_t i = 0; i < 4 ; i++) {
170 | mc_[i].update(tp_.x, tp_.y, tp_.rz);
171 | }
172 | }
173 | }
174 | #else
175 | void TMenu::update(int16_t x, int16_t y, uint16_t rz)
176 | {
177 | x_ = x;
178 | y_ = y;
179 | rz_ = rz;
180 |
181 | for (uint8_t i = 0; i < 4 ; i++) {
182 | mc_[i].update(x, y, rz);
183 | }
184 | }
185 | #endif
186 |
187 |
188 | void TMenu::refresh()
189 | {
190 | const uint8_t cursor_size = 8, cursor_hole = 3;
191 | bool same_pos = false;
192 |
193 | #ifdef TEST_RAW_INTERFACE
194 | static TFT_eTouchBase::TouchPoint last;
195 |
196 | if (last.x == tp_.x && last.y == tp_.y) {
197 | if (last.rz == tp_.rz) return;
198 | same_pos = true;
199 | }
200 |
201 | if (cursor_visible_ && (!same_pos || !touched_)) {
202 | tft().drawFastHLine(last.x - cursor_size + 1, last.y, cursor_size - cursor_hole, TFT_BLACK);
203 | tft().drawFastHLine(last.x + cursor_hole, last.y, cursor_size - cursor_hole, TFT_BLACK);
204 | tft().drawFastVLine(last.x, last.y - cursor_size + 1, cursor_size - cursor_hole, TFT_BLACK);
205 | tft().drawFastVLine(last.x, last.y + cursor_hole, cursor_size - cursor_hole, TFT_BLACK);
206 | tft().drawPixel(last.x, last.y, TFT_BLACK);
207 | }
208 |
209 | draw();
210 | if (!touched_) {
211 | cursor_visible_ = false; // hide cursor
212 | return;
213 | }
214 |
215 | info(50);
216 |
217 | if (!same_pos) {
218 | tft().drawFastHLine(tp_.x - cursor_size + 1, tp_.y, cursor_size - cursor_hole, TFT_WHITE);
219 | tft().drawFastHLine(tp_.x + cursor_hole, tp_.y, cursor_size - cursor_hole, TFT_WHITE);
220 | tft().drawFastVLine(tp_.x, tp_.y - cursor_size + 1, cursor_size - cursor_hole, TFT_WHITE);
221 | tft().drawFastVLine(tp_.x, tp_.y + cursor_hole, cursor_size - cursor_hole, TFT_WHITE);
222 | tft().drawPixel(tp_.x, tp_.y, TFT_WHITE);
223 | }
224 | last = tp_;
225 | #else
226 | static int16_t vx = 10000, vy = 10000, vz = 10000;
227 |
228 | if (vx == x_ && vy == y_) {
229 | if (vz == rz_) return;
230 | same_pos = true;
231 | }
232 | if (cursor_visible_ && (!same_pos || !touched_)) {
233 | tft().drawFastHLine(vx - cursor_size + 1, vy, cursor_size - cursor_hole, TFT_BLACK);
234 | tft().drawFastHLine(vx + cursor_hole, vy, cursor_size - cursor_hole, TFT_BLACK);
235 | tft().drawFastVLine(vx, vy - cursor_size + 1, cursor_size - cursor_hole, TFT_BLACK);
236 | tft().drawFastVLine(vx, vy + cursor_hole, cursor_size - cursor_hole, TFT_BLACK);
237 | tft().drawPixel(vx, vy, TFT_BLACK);
238 | }
239 |
240 | draw();
241 | if (!touched_) {
242 | cursor_visible_ = false; // hide cursor
243 | return;
244 | }
245 |
246 | info(50);
247 |
248 | if (!same_pos) {
249 | tft().drawFastHLine(x_ - cursor_size + 1, y_, cursor_size - cursor_hole, TFT_WHITE);
250 | tft().drawFastHLine(x_ + cursor_hole, y_, cursor_size - cursor_hole, TFT_WHITE);
251 | tft().drawFastVLine(x_, y_ - cursor_size + 1, cursor_size - cursor_hole, TFT_WHITE);
252 | tft().drawFastVLine(x_, y_ + cursor_hole, cursor_size - cursor_hole, TFT_WHITE);
253 | tft().drawPixel(x_, y_, TFT_WHITE);
254 | }
255 | vx = x_; vy = y_; vz = rz_;
256 | #endif
257 | cursor_visible_ = true;
258 | }
259 |
--------------------------------------------------------------------------------
/examples/generic/calibrate/calibrate.ino:
--------------------------------------------------------------------------------
1 | /**
2 | Sketch to generate the setup() calibration values, these are reported
3 | to the Serial Monitor.
4 |
5 | The sketch has been tested with compatible ADS7846 chip on:
6 | - ESP32 (TFT_eSPI, Adafruit_ILI9341)
7 | - ESP8322 (TFT_eSPI)
8 | - Tensy 3.1 (ILI9341_t3)
9 | - Arduino Nano (Adafruit_ILI9341)
10 | */
11 |
12 | #include
13 | #include
14 |
15 | //------------------------------------------------------------------------------------------
16 |
17 | #define TFT_ROTATION 1
18 |
19 | #define DEFAULT_CALIBRATION
20 | #define MAX_CALIBRATION 1 /* 0 .. 4 set it to 0 when callData ok */
21 |
22 | //------------------------------------------------------------------------------------------
23 |
24 | # ifdef _ADAFRUIT_ILI9341H_
25 | Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
26 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ);
27 |
28 | # elif defined (_ILI9341_t3H_)
29 | ILI9341_t3 tft(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
30 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ);
31 |
32 | # elif defined (_TFT_eSPIH_)
33 | TFT_eSPI tft;
34 | # ifdef ESP32_PARALLEL
35 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ, SPI);
36 | # else
37 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ, TFT_eSPI::getSPIinstance());
38 | # endif
39 | # else
40 | # error definition missing in TFT_eTouchUser.h
41 | # endif
42 |
43 |
44 | void start_screen()
45 | {
46 | // Clear the screen
47 | tft.fillScreen(TFT_BLACK);
48 |
49 | #ifdef BASIC_FONT_SUPPORT
50 | tft.setTextSize(1);
51 | const char* str;
52 | str = "Touch screen!";
53 | int16_t len = strlen(str) * 6;
54 | tft.setCursor((tft.width() - len)/2, tft.height()/2 - 20);
55 | tft.print(str);
56 |
57 | str = "R: rotate, C: calibrate";
58 | len = strlen(str) * 6;
59 | tft.setCursor((tft.width()-len)/2 , tft.height()/2);
60 | tft.print(str);
61 |
62 | str = "open serial monitor to see calibration";
63 | len = strlen(str) * 6;
64 | tft.setCursor((tft.width()-len)/2 , tft.height()/2 + 20);
65 | tft.print(str);
66 | #else
67 | tft.drawCentreString("Touch screen!", tft.width()/2, tft.height()/2 - 20, 2);
68 | tft.drawCentreString("R: rotate, C: calibrate", tft.width()/2, tft.height()/2, 2);
69 | tft.drawCentreString("open serial monitor to see calibration", tft.width()/2, tft.height()/2 + 20, 2);
70 | #endif
71 | tft.setCursor(0, 0);
72 |
73 | #ifdef BASIC_FONT_SUPPORT
74 | tft.setTextSize(2);
75 | #define CHAR_XSIZE_PIX (2 * 6)
76 | #else
77 | tft.setTextFont(2);
78 | tft.setTextSize(1);
79 | #define CHAR_XSIZE_PIX 10
80 | #endif
81 | tft.setTextColor(TFT_WHITE, TFT_BLUE);
82 | tft.print("R");
83 | tft.setCursor(tft.width() - CHAR_XSIZE_PIX, 0);
84 | tft.print("C");
85 | tft.setTextColor(TFT_WHITE, TFT_BLACK);
86 | #ifdef BASIC_FONT_SUPPORT
87 | tft.setTextSize(1);
88 | #endif
89 | }
90 |
91 | void show_cursor(int16_t x, int16_t y)
92 | {
93 | // this is not a real cursor, it does not store the background under the cursor
94 | static bool visible = false;
95 | static int16_t vx = 10000, vy = 10000, vz = 10000;
96 |
97 | const uint8_t size = 8, hole = 3;
98 | uint16_t rz = touch.getRZ();
99 | bool same_pos = false;
100 | if (vx == x && vy == y) {
101 | if (vz == rz) return;
102 | same_pos = true;
103 | }
104 | if (visible && !same_pos) {
105 | tft.drawFastHLine(vx - size + 1, vy, size - hole, TFT_BLACK);
106 | tft.drawFastHLine(vx + hole, vy, size - hole, TFT_BLACK);
107 | tft.drawFastVLine(vx, vy - size + 1, size - hole, TFT_BLACK);
108 | tft.drawFastVLine(vx, vy + hole, size - hole, TFT_BLACK);
109 | tft.drawPixel(vx, vy, TFT_BLACK);
110 | }
111 | if (x < 0 || y < 0) {
112 | visible = false; // coordinate invalid, hide cursor
113 | return;
114 | }
115 | tft.setCursor(50, 50);
116 | tft.print(x);
117 | tft.print(",");
118 | tft.print(y);
119 | tft.print(",");
120 | tft.print(rz);
121 | tft.println(" ");
122 |
123 | if (rz > touch.getRXPlate()*174/100) tft.println("Pen ");
124 | else if (rz > touch.getRXPlate()*66/100) tft.println("Finger ");
125 | else if (rz > touch.getRXPlate()*15/100) tft.println("2 Finger ");
126 | else tft.println("+ 2 Finger ");
127 | /*
128 | if (rz > touch.getRZThreshold()*58/100) tft.println("Pen ");
129 | else if (rz >= touch.getRZThreshold()*22/100) tft.println("Finger ");
130 | else if (rz > touch.getRZThreshold()*5/100) tft.println("2 Finger ");
131 | else tft.println("more then 2 Finger");
132 | */
133 | if (!same_pos) {
134 | tft.drawFastHLine(x - size + 1, y, size - hole, TFT_WHITE);
135 | tft.drawFastHLine(x + hole, y, size - hole, TFT_WHITE);
136 | tft.drawFastVLine(x, y - size + 1, size - hole, TFT_WHITE);
137 | tft.drawFastVLine(x, y + hole, size - hole, TFT_WHITE);
138 | tft.drawPixel(x, y, TFT_WHITE);
139 | }
140 | vx = x; vy = y; vz = rz;
141 | visible = true;
142 | }
143 |
144 | //------------------------------------------------------------------------------------------
145 |
146 | void setup() {
147 | Serial.begin(115200);
148 | delay(2000);
149 |
150 | #ifdef ESP32_PARALLEL
151 | SPI.begin();
152 | #endif
153 | tft.begin();
154 | touch.init();
155 | while (!Serial) ; // wait for Arduino Serial Monitor
156 | // touch.setMeasure(0, false, true, false, 3); // constructor defaults (take third measure, start with z axis)
157 | // touch.setAveraging(false, false); // constructor defaults (without averaging)
158 |
159 | // touch.setMeasure(0, true, true, false, 1); // Differential mode fastest (each axis read only once, may work)
160 | // touch.setAcurateDistance(25); // in this mode acurate distance must be higher for getUserCalibration (default 10)
161 |
162 | // touch.setMeasure(1, true, true, false, 1); // Differential mode faster (take second z bud first x, y. may work)
163 | // touch.setAcurateDistance(25); // in this mode acurate distance must be higher for getUserCalibration (default 10)
164 |
165 | // touch.setMeasure(10, false, false, true, 14); // slowest (drop 10 and additional 16 measures. averaging 14 with min max value removed)
166 | // touch.setAveraging(true, true);
167 |
168 | // when noisy
169 | //touch.setMeasure(1, false, true, true, 3); // z first, drop 1 and additional 5 measures. averaging 3 with min max value removed
170 | //touch.setAveraging(true, true);
171 | //touch.setAcurateDistance(100); // or even higher for getUserCalibration (default 10)
172 | // or
173 | touch.setAcurateDistance(80); // and define TOUCH_FILTER_TYPE in TFT_eTouchUser.h
174 |
175 | // untouched: 35 us touched: 95 us (good choice for starting)
176 | touch.setMeasure(0, false, true, false, 2); // z first, take 2'th z,x,y
177 | /* if you get output like that when calling getUserCalibration, you have a noisy touch signal
178 | acurate on point[4, 4](233, 371) dx: 287 dy: 27 > 10 nok
179 | acurate on point[4, 4](283, 365) dx: 192 dy: 17 > 10 nok
180 | acurate on point[4, 4](349, 399) dx: 192 dy: 16 > 10 nok
181 | acurate on point[4, 4](334, 400) dx: 204 dy: 11 > 10 nok
182 | */
183 |
184 |
185 | // touch.setMeasure(); // defaults (accurate all axis, start with x)
186 | // touch.setMeasure(1, true, true, true); // z with local min, acurate x,y
187 |
188 | // touch.setValidRawRange(10, 4085);
189 |
190 | touch.setRXPlate(1000/3);
191 | touch.setRZThreshold(1000);
192 |
193 | #ifndef DEFAULT_CALIBRATION
194 | TFT_eTouchBase::Calibation calibation = { 265, 3790, 264, 3850, 2 }; // x and y axes have same direction on touch & display
195 | // TFT_eTouchBase::Calibation calibation = { 3790, 265, 3850, 264, 0 }; // same as above
196 | // TFT_eTouchBase::Calibation calibation = { 272, 3749, 3894, 341, 0 }; // y axes have oposite direction
197 |
198 | // Calibrate the touch screen and retrieve the scaling factors
199 | for (int rot = 0; rot < MAX_CALIBRATION; rot++) {
200 | // Set the rotation before we calibrate
201 | tft.setRotation(TFT_ROTATION + rot % 4);
202 | touch.getUserCalibration(calibation, 4);
203 | }
204 | touch.setCalibration(calibation);
205 | #endif
206 | tft.setRotation(TFT_ROTATION);
207 | delay(1);
208 | start_screen();
209 | }
210 |
211 | //------------------------------------------------------------------------------------------
212 |
213 | void loop(void) {
214 | static uint32_t last_update = 0;
215 | if (last_update + touch.getMeasureWait() > millis()) return;
216 | last_update = millis();
217 |
218 | static bool is_touched = false;
219 | int16_t x, y; // screen coordinates
220 | if (touch.getXY(x, y)) {
221 | if (!is_touched) { // we have pen down!
222 | is_touched = true;
223 | }
224 | if (y < 10) {
225 | if (x < 10) {
226 | show_cursor(-1, -1); // hide cursor
227 | uint8_t rot = tft.getRotation();
228 | rot = (rot + 1) % 4;
229 | tft.setRotation(rot);
230 | start_screen();
231 | return;
232 | }
233 | if (x > tft.width() - 10) {
234 | TFT_eTouchBase::Calibation calibation;
235 | show_cursor(-1, -1); // hide cursor
236 | touch.getUserCalibration(calibation, 4);
237 | touch.setCalibration(calibation);
238 | start_screen();
239 | return;
240 | }
241 | }
242 | show_cursor(x, y);
243 | }
244 | else {
245 | if (is_touched) { // we have pen up!
246 | is_touched = false;
247 | show_cursor(-1, -1); // hide cursor
248 | touch.reset();
249 | }
250 | }
251 |
252 | }
253 |
--------------------------------------------------------------------------------
/TFT_eTouch.inl:
--------------------------------------------------------------------------------
1 | #ifndef TFT_E_TOUCH_INL
2 | #define TFT_E_TOUCH_INL
3 |
4 | //
5 | // TFT_eTouch.inl
6 | //
7 | // (C) Copyright Achill Hasler 2019.
8 | //
9 | // Distributed under the Boost Software License, Version 1.0.
10 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt
11 | //
12 | //
13 | // See TFT_eTouch/docs/html/index.html for documentation.
14 | //
15 |
16 | #ifdef TOUCH_USE_PENIRQ_CODE
17 | template
18 | TFT_eTouch* TFT_eTouch::isr_instance_ = 0;
19 |
20 | template
21 | #ifdef ESP32
22 | void IRAM_ATTR TFT_eTouch::cb_isr_touch_fnk()
23 | #else
24 | void TFT_eTouch::cb_isr_touch_fnk()
25 | #endif
26 | {
27 | TFT_eTouch* touch_ptr = isr_instance_;
28 | if (touch_ptr) touch_ptr->update_allowed_ = true;
29 | }
30 | #endif // end TOUCH_USE_PENIRQ_CODE
31 |
32 |
33 | template
34 | TFT_eTouch::TFT_eTouch(T& tft, uint8_t cs_pin, uint8_t penirq_pin, SPIClass& spi)
35 | : TFT_eTouchBase(cs_pin, penirq_pin, spi)
36 | , tft_(tft)
37 | {
38 | }
39 |
40 | template
41 | void TFT_eTouch::begin()
42 | {
43 | init();
44 | }
45 |
46 | template
47 | void TFT_eTouch::init()
48 | {
49 | TFT_eTouchBase::init(false);
50 | #ifdef TOUCH_USE_PENIRQ_CODE
51 | if (penirq_ != 0xff) {
52 | pinMode(penirq_, INPUT);
53 | attachInterrupt(digitalPinToInterrupt(penirq_), cb_isr_touch_fnk, FALLING);
54 | isr_instance_ = this;
55 | }
56 | #endif // end TOUCH_USE_PENIRQ_CODE
57 | }
58 |
59 | template
60 | T& TFT_eTouch::tft()
61 | {
62 | return tft_;
63 | }
64 |
65 | #ifdef TOUCH_USE_USER_CALIBRATION
66 | template
67 | bool TFT_eTouch::getUserCalibration(Calibation& data, uint8_t xy_off)
68 | {
69 | uint8_t user_r = tft_.getRotation();
70 | uint8_t touch_r = 255;
71 |
72 | tft_.fillScreen(TFT_BLACK);
73 | tft_.setCursor(50, 50);
74 | #ifndef BASIC_FONT_SUPPORT
75 | tft_.setTextFont(2);
76 | #endif
77 | tft_.setTextSize(1);
78 | tft_.setTextColor(TFT_WHITE, TFT_BLACK);
79 |
80 | tft_.println("Touch blue target to green");
81 |
82 | #ifndef BASIC_FONT_SUPPORT
83 | tft_.setTextFont(1);
84 | #endif
85 | tft_.println();
86 |
87 | if (xy_off < 4) xy_off = 4;
88 |
89 | CalibrationPoint cal_pos[4]; // get clockwise 4 values
90 | cal_pos[0].set(xy_off, xy_off); // Top Left
91 | cal_pos[1].set(tft_.width() - 1 - xy_off, xy_off); // Top Right
92 | cal_pos[2].set(tft_.width() - 1 - xy_off, tft_.height() - 1 - xy_off); // Down Right
93 | cal_pos[3].set(xy_off, tft_.height() - 1 - xy_off); // Down Left
94 | for (int i = 0; i < 4; i++) {
95 | uint8_t try_cnt = 4;
96 | reset(); // clear FIR filter when used
97 | while (try_cnt > 0 && !handleTouchCalibrationTarget(cal_pos[i])) {
98 | reset();
99 | delay(500);
100 | try_cnt--;
101 | }
102 | }
103 |
104 | #ifdef TOUCH_SERIAL_DEBUG
105 | if (Serial) {
106 | Serial.print("p0 "); cal_pos[0].print();
107 | Serial.print("p1 "); cal_pos[1].print();
108 | Serial.print("p2 "); cal_pos[2].print();
109 | Serial.print("p3 "); cal_pos[3].print();
110 | }
111 | #endif
112 |
113 | for (int i = 0; i < 4; i++) {
114 | if (cal_pos[i].touch_x < 1000 && cal_pos[i].touch_y < 1000) {
115 | // zerro of touch, is touch_y inverted ?
116 | if (cal_pos[(i+4-1) % 4].touch_x < 1000) {
117 | // not inverted
118 | touch_r = i;
119 | }
120 | else {
121 | touch_r = (i + 1) % 4;
122 | }
123 | break;
124 | }
125 | }
126 |
127 | #ifdef TOUCH_SERIAL_DEBUG
128 | if (Serial) {
129 | Serial.print("Rotation: tft ");
130 | Serial.print(user_r);
131 | Serial.print(" touch ");
132 | Serial.println(touch_r);
133 | }
134 | #endif
135 |
136 | int16_t d_xs = cal_pos[1].scr_x - cal_pos[0].scr_x;
137 | int16_t d_xt = (cal_pos[(touch_r + 1) % 4].touch_x + cal_pos[(touch_r + 2) % 4].touch_x)/2 - (cal_pos[touch_r + 0].touch_x + cal_pos[(touch_r + 3) % 4].touch_x)/2;
138 |
139 | #ifdef TOUCH_SERIAL_DEBUG
140 | if (Serial) {
141 | Serial.print("dx ");
142 | Serial.print(d_xs);
143 | Serial.print(", ");
144 | Serial.print(d_xt);
145 | }
146 | #endif
147 |
148 | int16_t x0 = cal_pos[0].scr_x - d_xs * (cal_pos[touch_r + 0].touch_x+cal_pos[(touch_r + 3) % 4].touch_x) / (d_xt * 2);
149 | #ifdef TOUCH_SERIAL_DEBUG
150 | if (Serial) {
151 | Serial.print(", ");
152 | Serial.println(x0);
153 | }
154 | #endif
155 |
156 | data.x0 = -x0 * d_xt / d_xs;
157 | data.x1 = (tft_.width() - 1 - x0) * d_xt / d_xs;
158 |
159 | int16_t d_ys = cal_pos[3].scr_y - cal_pos[0].scr_y;
160 | int16_t d_yt = (cal_pos[(touch_r + 2) % 4].touch_y + cal_pos[(touch_r + 3) % 4].touch_y)/2 - (cal_pos[touch_r + 0].touch_y + cal_pos[(touch_r + 1) % 4].touch_y)/2;
161 |
162 | #ifdef TOUCH_SERIAL_DEBUG
163 | if (Serial) {
164 | Serial.print("dy ");
165 | Serial.print(d_ys);
166 | Serial.print(", ");
167 | Serial.print(d_yt);
168 | }
169 | #endif
170 |
171 | int16_t y0 = cal_pos[0].scr_y - d_ys * (cal_pos[touch_r + 0].touch_y+cal_pos[(touch_r + 1) % 4].touch_y) / (d_yt * 2);
172 |
173 | #ifdef TOUCH_SERIAL_DEBUG
174 | if (Serial) {
175 | Serial.print(", ");
176 | Serial.println(y0);
177 | }
178 | #endif
179 |
180 | data.y0 = -y0 * d_yt / d_ys;
181 | data.y1 = (tft_.height() - 1 - y0) * d_yt / d_ys;
182 |
183 | data.rel_rotation = (user_r + touch_r) % 4;
184 |
185 | #ifdef TOUCH_SERIAL_DEBUG
186 | if (Serial) {
187 | Serial.print("x: "); Serial.print(data.x0); Serial.print(" "); Serial.print(data.x1);
188 | Serial.print(" y: "); Serial.print(data.y0); Serial.print(" "); Serial.print(data.y1);
189 | Serial.print(" rot "); Serial.println(data.rel_rotation);
190 | }
191 | #endif
192 |
193 | if (Serial) {
194 | Serial.printf("#define TOUCH_DEFAULT_CALIBRATION { %i, %i, %i, %i, %i }\n",
195 | data.x0, data.x1, data.y0, data.y1, data.rel_rotation);
196 | }
197 |
198 | return true;
199 | }
200 | /*
201 | acurate on p[4, 4] is 100, dx 292, dy 8
202 | acurate on p[4, 4] is 100, dx 268, dy 12
203 | acurate on p[4, 4] is 100, dx 273, dy 11
204 | acurate on p[4, 4] is 100, dx 294, dy 5
205 | acurate on p[4, 4] is 100, dx 38, dy 16
206 | acurate on p[315, 4] is 100, dx 43, dy 81
207 | acurate on p[315, 235] is 100, dx 152, dy 113
208 | acurate on p[315, 235] is 100, dx 189, dy 129
209 | acurate on p[315, 235] is 100, dx 93, dy 68
210 | acurate on p[4, 235] is 100, dx 54, dy 7
211 | #define TOUCH_DEFAULT_CALIBRATION { 263, 3765, 3873, 326, 0 }
212 | */
213 |
214 | #endif // TOUCH_USE_USER_CALIBRATION
215 |
216 | template
217 | bool TFT_eTouch::transform(const Measure& raw, TouchPoint& tp)
218 | {
219 | if (raw.rz != 0xffff) {
220 | uint8_t d_rot = (4 + tft_.getRotation() - calibation_.rel_rotation) % 4;
221 |
222 | int16_t dx = calibation_.x1 - calibation_.x0;
223 | int16_t dy = calibation_.y1 - calibation_.y0;
224 |
225 | int16_t xs=0, xs_max = tft_.width() - 1;
226 | int16_t ys=0, ys_max = tft_.height() - 1;
227 |
228 | switch (d_rot) {
229 | case 0:
230 | xs = (raw.x - calibation_.x0) * xs_max / dx;
231 | ys = (raw.y - calibation_.y0) * ys_max / dy;
232 | break;
233 | case 1:
234 | xs = (raw.y - calibation_.y0) * xs_max / dy;
235 | ys = ys_max - ((raw.x - calibation_.x0) * ys_max / dx);
236 | break;
237 | case 2:
238 | xs = xs_max - ((raw.x - calibation_.x0) * xs_max / dx);
239 | ys = ys_max - ((raw.y - calibation_.y0) * ys_max / dy);
240 | break;
241 | case 3:
242 | xs = xs_max - ((raw.y - calibation_.y0) * xs_max / dy);
243 | ys = (raw.x - calibation_.x0) * ys_max / dx;
244 | break;
245 | }
246 | if (xs < 0) xs = 0;
247 | if (xs > xs_max) xs = xs_max;
248 |
249 | if (ys < 0) ys = 0;
250 | if (ys > ys_max) ys = ys_max;
251 |
252 | tp.set(xs, ys, raw.rz);
253 | return true;
254 | }
255 | tp.rz = 0xffff;
256 | return false;
257 | }
258 |
259 | template
260 | bool TFT_eTouch::getXY(int16_t& x, int16_t& y)
261 | {
262 | update(false);
263 | if (valid()) {
264 | TouchPoint tp;
265 | transform(raw_, tp);
266 | x = tp.x;
267 | y = tp.y;
268 |
269 | return true;
270 | }
271 | return false;
272 | }
273 |
274 | template
275 | bool TFT_eTouch::get(TouchPoint& tp)
276 | {
277 | update(false);
278 | if (valid()) {
279 | return transform(raw_, tp);
280 | }
281 | return false;
282 | }
283 |
284 | // private
285 | #ifdef TOUCH_USE_USER_CALIBRATION
286 |
287 | #ifdef TOUCH_USE_SIMPE_TARGET
288 | template
289 | bool TFT_eTouch::handleTouchCalibrationTarget(CalibrationPoint& point)
290 | {
291 | tft_.fillRect(point.scr_x - 4, point.scr_y - 4, 9, 9, TFT_BLUE);
292 | tft_.fillCircle(point.scr_x, point.scr_y, 4, TFT_WHITE);
293 | tft_.fillCircle(point.scr_x, point.scr_y, 2, TFT_BLUE);
294 |
295 | bool acurate = acurateCalibrationTarget(point);
296 |
297 | tft_.fillRect(point.scr_x - 4, point.scr_y - 4, 9, 9, acurate ? TFT_GREEN : TFT_RED);
298 | tft_.fillCircle(point.scr_x, point.scr_y, 4, TFT_WHITE);
299 | tft_.fillCircle(point.scr_x, point.scr_y, 2, acurate ? TFT_GREEN : TFT_RED);
300 |
301 | // wait pen up
302 | waitPenUp();
303 | delay(500);
304 |
305 | return acurate;
306 | }
307 | #else // !TOUCH_USE_SIMPE_TARGET
308 | template
309 | bool TFT_eTouch::handleTouchCalibrationTarget(CalibrationPoint& point)
310 | {
311 | const uint8_t size = 10, hole = 3;
312 |
313 | tft_.fillCircle(point.scr_x, point.scr_y, 10, TFT_WHITE);
314 | tft_.fillCircle(point.scr_x, point.scr_y, 9, TFT_BLUE);
315 | tft_.fillCircle(point.scr_x, point.scr_y, 4, TFT_WHITE);
316 | tft_.fillCircle(point.scr_x, point.scr_y, 2, TFT_BLACK);
317 | if (point.scr_x < size || point.scr_y < size || point.scr_x > tft_.width() - size || point.scr_y > tft_.height() - size) {
318 | // fillCircle won't work if not full visible
319 | tft_.drawCircle(point.scr_x, point.scr_y, 3, TFT_WHITE);
320 | tft_.drawCircle(point.scr_x, point.scr_y, 4, TFT_WHITE);
321 | tft_.drawCircle(point.scr_x, point.scr_y, 5, TFT_BLUE);
322 | tft_.drawCircle(point.scr_x, point.scr_y, 6, TFT_BLUE);
323 | tft_.drawCircle(point.scr_x, point.scr_y, 7, TFT_BLUE);
324 | tft_.drawCircle(point.scr_x, point.scr_y, 8, TFT_BLUE);
325 | tft_.drawCircle(point.scr_x, point.scr_y, 9, TFT_BLUE);
326 | tft_.drawCircle(point.scr_x, point.scr_y, 10, TFT_WHITE);
327 | }
328 | tft_.drawFastHLine(point.scr_x - size + 1, point.scr_y, size - hole, TFT_WHITE);
329 | tft_.drawFastHLine(point.scr_x + hole, point.scr_y, size - hole, TFT_WHITE);
330 | tft_.drawFastVLine(point.scr_x, point.scr_y - size + 1, size - hole, TFT_WHITE);
331 | tft_.drawFastVLine(point.scr_x, point.scr_y + hole, size - hole, TFT_WHITE);
332 |
333 | bool acurate = acurateCalibrationTarget(point);
334 |
335 | tft_.fillCircle(point.scr_x, point.scr_y, 9, acurate ? TFT_GREEN : TFT_RED);
336 | tft_.fillCircle(point.scr_x, point.scr_y, 4, TFT_WHITE);
337 | tft_.fillCircle(point.scr_x, point.scr_y, 2, TFT_BLACK);
338 | if (point.scr_x < size || point.scr_y < size || point.scr_x > tft_.width() - size || point.scr_y > tft_.height() - size) {
339 | tft_.drawCircle(point.scr_x, point.scr_y, 5, acurate ? TFT_GREEN : TFT_RED);
340 | tft_.drawCircle(point.scr_x, point.scr_y, 6, acurate ? TFT_GREEN : TFT_RED);
341 | tft_.drawCircle(point.scr_x, point.scr_y, 7, acurate ? TFT_GREEN : TFT_RED);
342 | tft_.drawCircle(point.scr_x, point.scr_y, 8, acurate ? TFT_GREEN : TFT_RED);
343 | tft_.drawCircle(point.scr_x, point.scr_y, 9, acurate ? TFT_GREEN : TFT_RED);
344 | }
345 | tft_.drawFastHLine(point.scr_x - size + 1, point.scr_y, size - hole, TFT_WHITE);
346 | tft_.drawFastHLine(point.scr_x + hole, point.scr_y, size - hole, TFT_WHITE);
347 | tft_.drawFastVLine(point.scr_x, point.scr_y - size + 1, size - hole, TFT_WHITE);
348 | tft_.drawFastVLine(point.scr_x, point.scr_y + hole, size - hole, TFT_WHITE);
349 | // wait pen up
350 | waitPenUp();
351 | delay(500);
352 |
353 | return acurate;
354 | }
355 | #endif // TOUCH_USE_SIMPE_TARGET
356 | #endif // TOUCH_USE_USER_CALIBRATION
357 |
358 | #endif // TFT_E_TOUCH_INL
359 |
360 |
--------------------------------------------------------------------------------
/TFT_eTouchBase.h:
--------------------------------------------------------------------------------
1 | #ifndef TFT_E_TOUCH_BASE_H
2 | #define TFT_E_TOUCH_BASE_H
3 |
4 | //
5 | // TFT_eTouchBase.h
6 | //
7 | // (C) Copyright Achill Hasler 2019.
8 | //
9 | // Distributed under the Boost Software License, Version 1.0.
10 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt
11 | //
12 | //
13 | // See TFT_eTouch/docs/html/index.html for documentation.
14 | //
15 |
16 | #include
17 | #include
18 |
19 | #if ARDUINO < 10600
20 | #error "Arduino 1.6.0 or later (SPI library) is required"
21 | #endif
22 |
23 | #define TFT_ETOUCH_VERSION "0.6.0"
24 |
25 | #include
26 |
27 | #ifdef TOUCH_USE_GESTURE
28 | class TFT_eTouchGesture;
29 | #endif
30 |
31 | /**
32 | * @brief touch support base
33 | */
34 | class TFT_eTouchBase
35 | {
36 | public:
37 | /**
38 | * These are the calibration values for the touchscreen.
39 | *
40 | * When x0 < x1 the touch x axis is in same direction as the tft axis.
41 | *
42 | * when y1 < y0 the touch y axis is in oposite direction as the tft axis.
43 | *
44 | * The calibration values { 250, 3800, 260, 3850, 2 } and { 3800, 250, 3850, 260, 0 } are identical.
45 | * @brief touch calibation
46 | */
47 | struct Calibation
48 | {
49 | uint16_t x0; ///< x touch value of first display pixel
50 | uint16_t x1; ///< x touch value of last display pixel
51 | uint16_t y0; ///< y touch value of first display pixel
52 | uint16_t y1; ///< y touch value of last display pixel
53 |
54 | uint8_t rel_rotation; ///< relative rotation clockwise from display to touch
55 | };
56 |
57 | /**
58 | * This struct hold one raw touch measure.
59 | *
60 | * @brief touch raw value
61 | */
62 | struct Measure
63 | {
64 | uint16_t x; ///< raw x measure range 0..4095 (12 bit) valid (ca 213..3881)
65 | uint16_t y; ///< raw y measure range 0..4095 (12 bit) valid (ca 222..3929)
66 | uint16_t z1; ///< raw z1 measure range 0..4095 (12 bit) valid (ca 29..1748)
67 | uint16_t z2; ///< raw z2 measure range 0..4095 (12 bit) valid (ca 963..3989)
68 | uint16_t rz; ///< calculated resitor value from raw x, z1 and z2 measure, value range (0..rx_plate_*10) when tuched, 0xffff when not tuched.
69 | Measure() : rz(0xffff) {}
70 | };
71 |
72 | /**
73 | * This struct hold one touch measure with tft coordinates.
74 | *
75 | * @brief tft touch value
76 | */
77 | struct TouchPoint
78 | {
79 | int16_t x; ///< TFT x position
80 | int16_t y; ///< TFT y position
81 | uint16_t rz; ///< resitance of Touch in ohm
82 |
83 | TouchPoint() : x(0), y(0), rz(0xffff) {}
84 | /// set display position and pressure
85 | inline void set(int16_t _x, int16_t _y, uint16_t _rz)
86 | { x = _x; y = _y; rz = _rz; }
87 | };
88 |
89 | /**
90 | * Create instance with defaults.
91 | *
92 | * Display and touch must use the same spi bus.
93 | * @brief constructor
94 | * @param cs_pin touch chip select line processor pin
95 | * @param penirq_pin touch penirq line processor pin. 0xff disable touch interrupt
96 | * @param spi used bus
97 | */
98 | TFT_eTouchBase(uint8_t cs_pin, uint8_t penirq_pin = 0xff, SPIClass& spi = SPI);
99 |
100 | /**
101 | * Initialize the processor cs pin.
102 | * @brief init cs pin
103 | * @param spi_init init spi bus to with default MOSI MISO and SCK
104 | */
105 | void init(bool spi_init);
106 |
107 | /**
108 | * Get raw position of touch. These values are in orientation of the touchscreen.
109 | *
110 | * The values x, y, z1, z2 and rz are only set if the function returns true.
111 | * @brief touch position
112 | * @param x touch coordinate
113 | * @param y touch coordinate
114 | * @param z1 touch value for calculating rz
115 | * @param z2 touch value for calculating rz
116 | * @param rz calculated pressure in ohm
117 | * @return true when display is tuched
118 | */
119 | bool getRaw(uint16_t& x, uint16_t& y, uint16_t& z1, uint16_t& z2, uint16_t& rz);
120 |
121 | /**
122 | * Get raw position of touch. These values are in orientation of the touchscreen.
123 | * With TFT_eTouch::transform() you get the display coordinates from the raw measure.
124 | * The value is only set if the function returns true.
125 | * @brief raw touch position
126 | * @param raw value to set
127 | * @return true when display is tuched
128 | */
129 | bool getRaw(Measure& raw);
130 |
131 | /**
132 | * Wait in this function as long touchscreen is touched. If you must know the touch coordinates while waiting pen up,
133 | * use following code:
134 | @code
135 | while (touch.getXY(x, y)) {
136 | // do something with display coordinates
137 | delay(touch.getMeasureWait() - time used);
138 | }
139 | // or
140 | while (touch.getRaw(x, y, z1, z2, rz)) {
141 | // do something with raw touch coordinates
142 | delay(touch.getMeasureWait() - time used);
143 | }
144 | @endcode
145 | * @brief wait for pen up
146 | */
147 | void waitPenUp();
148 |
149 | /**
150 | * Get last calculated RZ value. When display not touched the value is 0xffff.
151 | * As more finger touch the display as lower is this value
152 | * @brief last pressure
153 | * @return last RZ measure in ohm
154 | */
155 | inline uint16_t getRZ() const;
156 |
157 | /**
158 | * Set the calibration for this touchscreen.
159 | * @brief set calibration
160 | * @param data touch calibration for display
161 | */
162 | inline void setCalibration(const Calibation& data);
163 |
164 | /**
165 | * Get the calibration for this touchscreen.
166 | * @brief get calibration
167 | * @return data touch calibration for display
168 | */
169 | inline Calibation& calibration();
170 |
171 | /**
172 | * Read the calibration for this touchscreen from SPIFLASH or EPROM.
173 | * @brief read calibration
174 | * @param descr filename in flash or adr in eprom
175 | * @return true when data was readed, otherwise data is invalid
176 | */
177 | bool readCalibration(const char* descr);
178 |
179 | /**
180 | * Write the actual calibration for this touchscreen to SPIFLASH or EPROM.
181 | * @brief write calibration
182 | * @param descr filename in flash or adr in eprom
183 | * @return true when data was written, otherwise calibration is not stored
184 | */
185 | bool writeCalibration(const char* descr);
186 |
187 | /**
188 | * Set the measure strategie.
189 | *
190 | * Base decicion
191 | * - do i wait until measure is same as measure bevore?
192 | * This is the Figure 10 implmentation from sbaa036.pdf. Set count = 0
193 | * - when not take the n'th measurement?
194 | * This is the Figure 11 implmentation from sbaa036.pdf. Set setAveraging(false) & count = n (0
196 | * Set averaging=true & count = n (0
199 | * When measure of X gives a value in_range() bud Z1 not (touchscreen not touched), its better to start with Z1 (z_first = true)
200 | *
201 | * There are some combinations to now
202 | * - drop_first=0 when count=0 | count>0 & averaging=false
203 | * drop_first=n will be ignored by X and Y measure, Z1 and Z2 measure ignore this when z_once=false.
204 | * - z_once=true
205 | * This set z_first to true, the first Z1 and Z2 measurement will be used when drop_first=0. (otherwise, the drop_first+1 measurement)
206 | * - z_local_min=true
207 | * In Formula of RZ = RX-plate * x/4096 * (Z2/Z1 -1); Z1 is the divisor. When Z1 raise RX shrink. This mean we measure Z1 as long it grow for getting the local minimum of RZ.
208 | *
209 | *
210 | * @brief set measure
211 | * @param drop_first ignore first n measures
212 | * @param z_once read z1 and z2 only once. When this flag is true z_first must be also true.
213 | * @param z_first when true start measure with Z1 otherwise with X
214 | * @param z_local_min when true, get local minimum of RZ
215 | * @param count how many values used by averaging or witch measure is taken. 0 means read until measure is equal last measure
216 | */
217 | inline void setMeasure(uint8_t drop_first = 0, bool z_once = false, bool z_first = false, bool z_local_min = false, uint8_t count = 0);
218 |
219 |
220 | #ifdef TOUCH_USE_AVERAGING_CODE
221 | /**
222 | * Set averaging mode.
223 | *
224 | * @param averaging when true averaging count measure, otherwise take the count measure as valid measure (if count > 0)
225 | * @param ignore_min_max when true, ignore min and max value on averaging
226 | */
227 |
228 | inline void setAveraging(bool averaging, bool ignore_min_max = false);
229 | #endif
230 |
231 | /**
232 | * Set valid measure range. We are using the 12bit interface from the chip. Value range is 0 .. 4095, but when measure values near 0 or 4095 indicate that there is no touch.
233 | * @brief valid measure range
234 | * @param min minimal valid measure
235 | * @param max maximal valid measure
236 | */
237 | inline void setValidRawRange(uint16_t min, uint16_t max);
238 |
239 | /**
240 | * Set the waiting time between two measure. If set to 0 every measure (getRaw(), TFT_eTouch::getXY()) will invoke the chip.
241 | * Otherwise you get the last measure values as long waiting time is not reached.
242 | * @brief set waiting time
243 | * @param ms waiting time in miliseconds between measures
244 | */
245 | inline void setMeasureWait(uint16_t ms);
246 | /**
247 | * @brief get waiting time
248 | * @return waiting time between measures in miliseconds
249 | */
250 | inline uint16_t getMeasureWait() const;
251 |
252 | /**
253 | * Set the resistace of the X-plate. The value can be between 300 and 1200. It is used for calculating RZ. If you change this value you have to change also the RZ threshold.
254 | * @brief set RX-plate
255 | * @param ohm RX-plate value
256 | */
257 | inline void setRXPlate(uint16_t ohm);
258 |
259 | /**
260 | * @brief get RX-plate
261 | * @return RX-plate in ohm
262 | */
263 | inline uint16_t getRXPlate() const;
264 |
265 | /**
266 | * Set the pressure threshold. If the calculation of RZ is less then this threshold then we have a touch. The value depend on RX-plate and must be also changed when RX-plate is chaged.
267 | *
268 | * Good value is tree time of RX-plate.
269 | *
270 | * Do not set this threshold to 0xffff, this value indicate that there is no touch.
271 | * @brief set threshold
272 | * @param ohm RZ threshold value
273 | */
274 | inline void setRZThreshold(uint16_t ohm);
275 | /**
276 | * @brief get threshold
277 | * @return RZ threshold value in ohm
278 | */
279 | inline uint16_t getRZThreshold() const;
280 |
281 | /**
282 | * If you use TOUCH_FILTER_TYPE, you have to call this function on pen-up. Otherwise when TOUCH_FILTER_TYPE is not defined, this function does nothing.
283 | * @brief reset touch buffer
284 | */
285 | inline void reset();
286 |
287 | #ifdef TOUCH_USE_USER_CALIBRATION
288 | /**
289 | * If you stay with the pen on the touchscreen the X and Y measure will change a little bit. This value describe the
290 | * tolarable difference of (max-min) from 16 measures. @sa TFT_eTouch::handleTouchCalibrationTarget()
291 | * @brief set acurate difference
292 | * @param raw_difference max difference of target measure
293 | */
294 | inline void setAcurateDistance(uint16_t raw_difference);
295 | /**
296 | * @brief get acurate difference
297 | * @return acurate difference
298 | */
299 | inline uint16_t getAcurateDistance() const;
300 |
301 | protected:
302 | /**
303 | * The user has to touch at scr_x and scr_y, the touch coordinate stored then in touch_x and touch_y.
304 | * @brief measure point
305 | */
306 | struct CalibrationPoint {
307 | int16_t scr_x; ///< x target display position
308 | int16_t scr_y; ///< y target display position
309 | int16_t touch_x; ///< x target touch position measured
310 | int16_t touch_y; ///< y target touch position measured
311 |
312 | /// set target display position
313 | inline void set(int16_t x, int16_t y)
314 | { scr_x = x; scr_y = y; }
315 |
316 | /// print to Serial
317 | void print();
318 | };
319 |
320 | /**
321 | * @brief measure CalibrationPoint
322 | * @param point target, to measure.
323 | * @return true when accurate
324 | */
325 | bool acurateCalibrationTarget(CalibrationPoint& point);
326 |
327 | #else
328 | protected:
329 | #endif // TOUCH_USE_USER_CALIBRATION
330 |
331 | /**
332 | * Fetch all raw touch values in time when only_z1 is false.
333 | * After the call check (rz != 0xffff) if true, then we have a touch.
334 | *
335 | * or
336 | *
337 | * Fetch only z1 in time when only_z1 is true.
338 | * After the call check (raw z1 > 0) if true, then we have a touch.
339 | * @brief update raw in time
340 | * @param only_z1 when true fetch only z1, otherwise x, y, z1, z2 and calculate RZ
341 | */
342 | void update(bool only_z1);
343 |
344 | inline bool valid(); ///< goes true when tuched (RZ < RZ threshold)
345 |
346 | SPIClass& spi_; ///< used spi bus. display and touch must use the same bus
347 | uint8_t cs_; ///< chip select pin
348 | uint8_t penirq_; ///< penirq pin
349 |
350 | Calibation calibation_; ///< used callibration for transforming touch measure into display pixels
351 | Measure raw_; ///< last touch measure
352 |
353 | #ifdef TOUCH_USE_GESTURE
354 | TFT_eTouchGesture* recognize_;
355 | #endif
356 |
357 | #ifdef TOUCH_USE_PENIRQ_CODE
358 | volatile bool update_allowed_; ///< goes true when penirq happend
359 | #endif // end TOUCH_USE_PENIRQ_CODE
360 |
361 | private:
362 | inline bool is_touched(); ///< goes true when tuched (RZ != 0xffff)
363 | inline bool in_range(uint16_t measure); ///< mesure between raw_valid_min_ and raw_valid_max_?
364 | inline void spi_start(); ///< reserve spi bus and select chip
365 | inline void spi_end(); ///< deselect chip and leave spi bus
366 | /// @sa update()
367 | void fetch_raw(bool only_z1); ///< fetch raw values
368 |
369 | uint8_t drop_first_measures_; ///< ignore first n measures
370 | bool z_once_measure_; ///< measure Z1 & Z2 only once (do not averaging)
371 | bool z_first_measure_; ///< begin with Z1 & Z2 then X Y. when false X Y Z1 Z2
372 | bool z_local_min_measure_; ///< read z1 until value grow
373 | uint8_t count_measure_; ///< averaging/use n measure. when 0 read until measure is equal previous measure
374 | #ifdef TOUCH_USE_AVERAGING_CODE
375 | bool averaging_measure_; ///< when true, averaging count_measure_, when false take the count_measure_ as valid measure (if count_measure_ > 0)
376 | bool ignore_min_max_measure_; ///< when averaging do 2 measure more for dropping min max value in average result
377 | #endif // end TOUCH_USE_AVERAGING_CODE
378 |
379 | uint16_t raw_valid_min_; ///< raw measure minimum value for x, y, z1 and z2 (otherwise it is not touched)
380 | uint16_t raw_valid_max_; ///< raw measure maximum value
381 |
382 | uint32_t last_measure_time_us_; ///< last measure time in microseconds
383 | uint16_t measure_wait_ms_; ///< waiting time in miliseconds between measures
384 |
385 | uint16_t rx_plate_; ///< Resitor value in ohm of x plate (300)
386 | uint16_t rz_threshold_; ///< when RZ < RZ threshold we have a valid touch (default 3*RX-plate)
387 |
388 | #ifdef TOUCH_USE_USER_CALIBRATION
389 | uint16_t acurate_difference_; ///< tolerable noise on X and Y measure for same point
390 | #endif // TOUCH_USE_USER_CALIBRATION
391 |
392 | #ifdef TOUCH_FILTER_TYPE
393 | # ifdef TOUCH_X_FILTER
394 | TOUCH_X_FILTER *x_filter_;
395 | # endif
396 | # ifdef TOUCH_Y_FILTER
397 | TOUCH_Y_FILTER *y_filter_;
398 | # endif
399 | # ifdef TOUCH_Z_FILTER
400 | TOUCH_Z_FILTER *z1_filter_;
401 | TOUCH_Z_FILTER *z2_filter_;
402 | # endif
403 | #endif
404 | };
405 |
406 | #include
407 |
408 | #ifdef TOUCH_USE_GESTURE
409 | #include
410 | #endif
411 |
412 | #endif // TFT_E_TOUCH_BASE_H
413 |
--------------------------------------------------------------------------------
/TFT_eTouchBase.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // TFT_eTouchBase.cpp
3 | //
4 | // (C) Copyright Achill Hasler 2019.
5 | //
6 | // Distributed under the Boost Software License, Version 1.0.
7 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt
8 | //
9 | // See TFT_eTouch/docs/html/index.html for documentation.
10 | //
11 |
12 | #if (defined (ESP8266) || defined (ESP32)) && !defined (ESP_PLATFORM)
13 | #define ESP_PLATFORM
14 | #endif
15 |
16 | #ifdef ESP_PLATFORM
17 | #include
18 | #ifdef ESP32
19 | #include
20 | #endif
21 | #endif
22 |
23 | #include
24 |
25 | TFT_eTouchBase::TFT_eTouchBase(uint8_t cs_pin, uint8_t penirq_pin, SPIClass& spi)
26 | : spi_(spi)
27 | , cs_(cs_pin)
28 | , penirq_(penirq_pin)
29 | //, raw_.x(0), raw_.y(0)
30 | //, raw_z1_(0), raw_z2_(0)
31 | //, rz_(0xffff)
32 | #ifdef TOUCH_USE_PENIRQ_CODE
33 | , update_allowed_(true)
34 | #endif // end TOUCH_USE_PENIRQ_CODE
35 | , drop_first_measures_(0)
36 | , z_once_measure_(false)
37 | , z_first_measure_(true)
38 | , z_local_min_measure_(false)
39 | , count_measure_(3)
40 | #ifdef TOUCH_USE_AVERAGING_CODE
41 | , averaging_measure_(false)
42 | , ignore_min_max_measure_(false)
43 | #endif // end TOUCH_USE_AVERAGING_CODE
44 |
45 | , raw_valid_min_(25), raw_valid_max_(4000)
46 | , last_measure_time_us_(0), measure_wait_ms_(5)
47 | , rx_plate_(1000/3)
48 | , rz_threshold_(1000)
49 | #ifdef TOUCH_USE_USER_CALIBRATION
50 | , acurate_difference_(10)
51 | #endif // TOUCH_USE_USER_CALIBRATION
52 | {
53 | calibation_ = TOUCH_DEFAULT_CALIBRATION;
54 |
55 | #ifdef TOUCH_FILTER_TYPE
56 | # ifdef TOUCH_X_FILTER
57 | x_filter_ = new TOUCH_X_FILTER(TOUCH_FILTER_TYPE);
58 | if (!x_filter_ && Serial) {
59 | Serial.println("coult not allocate x_filter");
60 | }
61 | # endif
62 | # ifdef TOUCH_Y_FILTER
63 | y_filter_ = new TOUCH_Y_FILTER(TOUCH_FILTER_TYPE);
64 | if (!y_filter_ && Serial) {
65 | Serial.println("coult not allocate y_filter");
66 | }
67 | # endif
68 | # ifdef TOUCH_Z_FILTER
69 | z1_filter_ = new TOUCH_Z_FILTER(TOUCH_FILTER_TYPE);
70 | z2_filter_ = new TOUCH_Z_FILTER(TOUCH_FILTER_TYPE);
71 | if ((!z1_filter_ || !z2_filter_) && Serial) {
72 | Serial.println("coult not allocate z_filter");
73 | }
74 | # endif
75 | #endif
76 | }
77 |
78 | void TFT_eTouchBase::init(bool spi_init)
79 | {
80 | if (spi_init) spi_.begin();
81 | pinMode(cs_, OUTPUT);
82 | digitalWrite(cs_, HIGH);
83 |
84 | #ifdef ESP_PLATFORM
85 | // check file system
86 | if (!SPIFFS.begin()) {
87 | if (Serial) Serial.println("formating SPIFFS file system");
88 |
89 | SPIFFS.format();
90 | if (!SPIFFS.begin()) {
91 | if (Serial) Serial.println("formating SPIFFS file system failed, check Tools/Flash size: != no SPIFFS");
92 | }
93 | }
94 | #endif
95 | }
96 |
97 |
98 | bool TFT_eTouchBase::getRaw(uint16_t& x, uint16_t& y, uint16_t& z1, uint16_t& z2, uint16_t& rz)
99 | {
100 | update(false);
101 | if (is_touched()) {
102 | x = raw_.x;
103 | y = raw_.y;
104 | z1 = raw_.z1;
105 | z2 = raw_.z2;
106 | rz = raw_.rz;
107 | return true;
108 | }
109 | return false;
110 | }
111 |
112 | bool TFT_eTouchBase::getRaw(Measure& raw)
113 | {
114 | update(false);
115 | if (is_touched()) {
116 | raw = raw_;
117 | return true;
118 | }
119 | return false;
120 | }
121 |
122 | void TFT_eTouchBase::waitPenUp()
123 | {
124 | if (!is_touched()) return;
125 |
126 | while (raw_.z1 != 0) {
127 | delay(measure_wait_ms_ + 1);
128 | update(true);
129 | }
130 | }
131 |
132 |
133 | bool TFT_eTouchBase::readCalibration(const char* descr)
134 | {
135 | bool ret = false;
136 | #ifdef ESP_PLATFORM
137 | if (SPIFFS.exists(descr)) {
138 | File calfile = SPIFFS.open(descr, "r");
139 | if (calfile) {
140 | Calibation data;
141 | if (calfile.readBytes((char *)&data, sizeof(data)) == sizeof(data)) {
142 | ret = true;
143 | calibation_ = data;
144 | if (Serial) Serial.printf("Calibration: %s x %u, %u, y %u, %u, r %u\n", descr, calibation_.x0, calibation_.x1, calibation_.y0, calibation_.y1, calibation_.rel_rotation);
145 | }
146 | else if (Serial) Serial.println("Calibration file read error");
147 | calfile.close();
148 | }
149 | else if (Serial) Serial.println("Calibration file open for read error");
150 | }
151 | #else
152 | if (Serial) Serial.println("TFT_eTouchBase::readCalibration() not implemented");
153 | #endif
154 | return ret;
155 | }
156 |
157 |
158 | bool TFT_eTouchBase::writeCalibration(const char* descr)
159 | {
160 | bool ret = false;
161 | #ifdef ESP_PLATFORM
162 | File calfile = SPIFFS.open(descr, "w");
163 | if (calfile) {
164 | ret = calfile.write((const unsigned char *)&calibation_, sizeof(calibation_)) == sizeof(calibation_);
165 | calfile.close();
166 | }
167 | if (!ret && Serial) {
168 | Serial.print("Calibration file write error, ");
169 | if (calfile) {
170 | Serial.println("cant write, size mismatch");
171 | }
172 | else {
173 | Serial.println("cant open");
174 | }
175 | }
176 | #else
177 | if (Serial) Serial.println("TFT_eTouchBase::writeCalibration() not implemented");
178 | #endif
179 | if (!ret && Serial) {
180 | Serial.printf("#define TOUCH_DEFAULT_CALIBRATION { %i, %i, %i, %i, %i }\n",
181 | calibation_.x0, calibation_.x1, calibation_.y0, calibation_.y1, calibation_.rel_rotation);
182 | }
183 | return ret;
184 | }
185 |
186 |
187 | void TFT_eTouchBase::update(bool only_z1)
188 | {
189 | #ifdef TOUCH_USE_PENIRQ_CODE
190 | if (!update_allowed_) return;
191 | #else
192 | // cant query PENIRQ for LOW, but when all measures in range then dispay is touched
193 | #endif // end TOUCH_USE_PENIRQ_CODE
194 |
195 | uint32_t now = micros();
196 | if ( now - last_measure_time_us_ < measure_wait_ms_*1000 ) return;
197 | last_measure_time_us_ = now;
198 |
199 | fetch_raw(only_z1);
200 |
201 | #ifdef TOUCH_USE_PENIRQ_CODE
202 | if (penirq_ != 0xff) {
203 | if (only_z1) update_allowed_ = raw_.z1 > 0;
204 | else update_allowed_ = valid();
205 | }
206 | #endif // end TOUCH_USE_PENIRQ_CODE
207 |
208 | #ifdef TOUCH_FILTER_TYPE
209 | if (!only_z1 && raw_.rz != 0xffff) {
210 | bool empty = false;
211 | uint16_t val;
212 | # ifdef TOUCH_X_FILTER
213 | val = x_filter_->next(raw_.x);
214 | if (val == 0) {
215 | empty = true;
216 | # ifdef TOUCH_SERIAL_DEBUG
217 | if (Serial) {
218 | Serial.print(x_filter_->filled());
219 | Serial.print('/');
220 | Serial.print(x_filter_->size());
221 | Serial.print(" x filter fill ");
222 | Serial.println(raw_.x);
223 | }
224 | # endif
225 | }
226 | else raw_.x = val;
227 | # endif
228 | # ifdef TOUCH_Y_FILTER
229 | val = y_filter_->next(raw_.y);
230 | if (val == 0) {
231 | empty = true;
232 | # ifdef TOUCH_SERIAL_DEBUG
233 | if (Serial) {
234 | Serial.print(y_filter_->filled());
235 | Serial.print('/');
236 | Serial.print(y_filter_->size());
237 | Serial.print(" y filter fill ");
238 | Serial.println(raw_.y);
239 | }
240 | # endif
241 | }
242 | else raw_.y = val;
243 | # endif
244 | # ifdef TOUCH_Z_FILTER
245 | val = z1_filter_->next(raw_.z1);
246 | if (val == 0) empty = true;
247 | else raw_.z1 = val;
248 |
249 | val = z2_filter_->next(raw_.z2);
250 | if (val == 0) empty = true;
251 | else raw_.z2 = val;
252 | # endif
253 | if (empty) {
254 | raw_.rz = 0xffff;
255 | }
256 | }
257 | #endif
258 |
259 | #ifdef TOUCH_SERIAL_CONVERSATION_TIME
260 | if (Serial) Serial.printf("TFT_eTouchBase::update() %d microseconds\n", micros() - last_measure_time_us_);
261 | #endif
262 | }
263 |
264 |
265 | // Differential Measure (SER/DFR low)
266 | #define X_MEASURE_DFR 0b11010001 // 0b11010011 also works
267 | #define Y_MEASURE_DFR 0b10010001 // 0b10010011
268 | #define Z1_MEASURE_DFR 0b10110001
269 | #define Z2_MEASURE_DFR 0b11000001
270 |
271 | #define OFF_MEASURE 0b10010000
272 |
273 | // Single Ended Measure (SER/DFR high)
274 | #define X_MEASURE_SER 0b11010100
275 | #define Y_MEASURE_SER 0b10010100
276 | #define Z1_MEASURE_SER 0b10110101
277 | #define Z2_MEASURE_SER 0b11000101
278 |
279 | #ifdef TOUCH_USE_DIFFERENTIAL_MEASURE
280 | #define X_MEASURE X_MEASURE_DFR
281 | #define Y_MEASURE Y_MEASURE_DFR
282 | #define Z1_MEASURE Z1_MEASURE_DFR
283 | #define Z2_MEASURE Z2_MEASURE_DFR
284 | #else
285 | #define X_MEASURE X_MEASURE_SER
286 | #define Y_MEASURE Y_MEASURE_SER
287 | #define Z1_MEASURE Z1_MEASURE_SER
288 | #define Z2_MEASURE Z2_MEASURE_SER
289 | #endif
290 |
291 | void TFT_eTouchBase::fetch_raw(bool only_z1)
292 | {
293 | bool has_touch = true;
294 | uint16_t data1, data2;
295 | uint8_t ctrl = X_MEASURE; // X-POSITION Measure
296 | uint8_t drop_cnt = drop_first_measures_;
297 |
298 | if (z_first_measure_ && !z_once_measure_) {
299 | ctrl = Z1_MEASURE; // Z1-POSITION Measure
300 | }
301 | spi_start();
302 |
303 | if (z_once_measure_) {
304 | z_first_measure_ = true; // then we do it first
305 | spi_.transfer(Z1_MEASURE); // Z1 Measure
306 | while (has_touch && drop_cnt-- > 0) {
307 | data2 = (spi_.transfer16(Z1_MEASURE) >> 3) & 0x0fff;
308 | if (!in_range(data2)) {
309 | has_touch = false;
310 | raw_.z1 = 0;
311 | }
312 | }
313 | drop_cnt = drop_first_measures_;
314 | if (has_touch) {
315 | if (z_local_min_measure_) { // read z1 until grows
316 | data2 = 0;
317 | do {
318 | data1 = data2;
319 | data2 = (spi_.transfer16(Z1_MEASURE) >> 3) & 0x0fff;
320 | } while (data1 < data2);
321 | }
322 | if (only_z1) raw_.z1 = (spi_.transfer16(OFF_MEASURE) >> 3) & 0x0fff;
323 | else raw_.z1 = (spi_.transfer16(Z2_MEASURE) >> 3) & 0x0fff; // Z2 Measure
324 | if (!in_range(raw_.z1)) {
325 | has_touch = false;
326 | raw_.z1 = 0;
327 | }
328 | else {
329 | if (only_z1) {
330 | spi_end();
331 | return;
332 | }
333 | while (drop_cnt-- > 0) spi_.transfer16(Z2_MEASURE);
334 | drop_cnt = drop_first_measures_;
335 | raw_.z2 = (spi_.transfer16(ctrl) >> 3) & 0x0fff; // X Measure
336 | if (!in_range(raw_.z2)) {
337 | has_touch = false;
338 | }
339 | }
340 | }
341 | }
342 | else {
343 | if (only_z1) ctrl = Z1_MEASURE;
344 | spi_.transfer(ctrl); // X or Z1 Measure
345 | raw_.z1 = 0;
346 | }
347 |
348 | #ifdef TOUCH_USE_AVERAGING_CODE
349 | if (!averaging_measure_) {
350 | ignore_min_max_measure_ = false;
351 | }
352 | #endif // end TOUCH_USE_AVERAGING_CODE
353 |
354 | while (has_touch) {
355 | if (count_measure_ == 0) {
356 | // Figure 10
357 | data1 = 0xffff;
358 | do {
359 | data2 = data1;
360 | data1 = (spi_.transfer16(ctrl) >> 3) & 0x0fff; // X, Y, Z1 or Z2 Measure
361 | } while (in_range(data1) && data1 != data2); // wait until stable
362 | }
363 | else {
364 | // Figure 11 or averaging
365 | #ifdef TOUCH_USE_AVERAGING_CODE
366 | uint16_t min = 0xffff, max = 0;
367 | data2 = count_measure_;
368 | if (averaging_measure_) {
369 | while (has_touch && drop_cnt-- > 0) {
370 | data1 = (spi_.transfer16(ctrl) >> 3) & 0x0fff;
371 | if (!in_range(data1)) {
372 | has_touch = false;
373 | }
374 | }
375 | drop_cnt = drop_first_measures_;
376 | if (!has_touch) break;
377 | if (ignore_min_max_measure_) {
378 | data2 += 2;
379 | }
380 | if (data2 > 16) { // 16 is max! (data1 is 16bit one sum value 12bit, 4bit's for 16 Values)
381 | data2 = 16;
382 | if (ignore_min_max_measure_) count_measure_ = 14;
383 | else count_measure_ = 16;
384 | }
385 | }
386 | #else
387 | data2 = count_measure_;
388 | #endif // end TOUCH_USE_AVERAGING_CODE
389 | data1 = 0;
390 | uint8_t next_ctrl = ctrl;
391 | while (data2--) {
392 | if (data2 == 0) { // last measure of axis, switch ctrl to next
393 | if (ctrl == X_MEASURE) {
394 | next_ctrl = Y_MEASURE; // Y-POSITION Measure
395 | }
396 | else if (ctrl == Y_MEASURE) {
397 | if (z_first_measure_) next_ctrl = OFF_MEASURE;
398 | else next_ctrl = Z1_MEASURE; // Z1-POSITION Measure
399 | }
400 | else if (ctrl == Z1_MEASURE) {
401 | if (!z_local_min_measure_) { // then later
402 | next_ctrl = Z2_MEASURE; // Z2-POSITION Measure
403 | }
404 | else if (only_z1) ctrl = OFF_MEASURE;
405 | }
406 | else if (ctrl == Z2_MEASURE) { // Z2 Measure done
407 | if (!z_first_measure_) next_ctrl = OFF_MEASURE;
408 | else next_ctrl = X_MEASURE; // X-POSITION Measure
409 | }
410 | #ifdef TOUCH_USE_AVERAGING_CODE
411 | if (!averaging_measure_) {
412 | data1 = (spi_.transfer16(next_ctrl) >> 3) & 0x0fff; // take n'th measure of X, Y, Z1 or Z2
413 | }
414 | #else
415 | data1 = (spi_.transfer16(next_ctrl) >> 3) & 0x0fff; // take n'th measure of X, Y, Z1 or Z2
416 | #endif // end TOUCH_USE_AVERAGING_CODE
417 | }
418 | #ifdef TOUCH_USE_AVERAGING_CODE
419 | if (averaging_measure_) {
420 | uint16_t data = (spi_.transfer16(next_ctrl) >> 3) & 0x0fff; // X, Y, Z1 or Z2 Measure
421 | data1 += data;
422 | if (ignore_min_max_measure_) {
423 | if (min > data) min = data;
424 | if (max < data) max = data;
425 | }
426 | }
427 | else {
428 | // dummy read when not n'th measure
429 | if (data2 > 0) {
430 | // spi_.transfer16(next_ctrl);
431 | data1 = (spi_.transfer16(next_ctrl) >> 3) & 0x0fff;
432 | if (!in_range(data1)) {
433 | data2 = 0;
434 | // has_touch = false;
435 | }
436 | }
437 | }
438 | #else
439 | else {
440 | // dummy read when not n'th measure
441 | // spi_.transfer16(next_ctrl);
442 | data1 = (spi_.transfer16(next_ctrl) >> 3) & 0x0fff;
443 | if (!in_range(data1)) {
444 | data2 = 0;
445 | }
446 | }
447 | #endif // end TOUCH_USE_AVERAGING_CODE
448 | }
449 | #ifdef TOUCH_USE_AVERAGING_CODE
450 | if (averaging_measure_) {
451 | if (ignore_min_max_measure_) {
452 | data1 -= (min + max);
453 | }
454 | data1 /= count_measure_;
455 | }
456 | #endif // end TOUCH_USE_AVERAGING_CODE
457 | }
458 |
459 | if (!in_range(data1)) {
460 | has_touch = false;
461 | }
462 | else if (ctrl == X_MEASURE) { // X Measure done
463 | raw_.x = data1;
464 | ctrl = Y_MEASURE; // Y-POSITION Measure
465 | }
466 | else if (ctrl == Y_MEASURE) { // Y Measure done
467 | raw_.y = data1;
468 | if (z_first_measure_) break;
469 | ctrl = Z1_MEASURE; // Z1-POSITION Measure
470 | }
471 | else if (ctrl == Z1_MEASURE) { // Z1 Measure done
472 | if (z_local_min_measure_) { // read z1 until grows
473 | if (raw_.z1 >= data1) {
474 | ctrl = Z2_MEASURE; // Z2-POSITION Measure
475 | if (count_measure_ > 0) {
476 | if (!only_z1) spi_.transfer16(ctrl); // dummy read of last z1, because next transfer16() must return z2
477 | }
478 | }
479 | }
480 | else {
481 | ctrl = Z2_MEASURE; // Z2-POSITION Measure
482 | }
483 | raw_.z1 = data1;
484 | if (only_z1 && ctrl == Z2_MEASURE) {
485 | if (count_measure_ == 0) {
486 | spi_.transfer16(OFF_MEASURE); // set power down mode
487 | }
488 | spi_end();
489 | return;
490 | }
491 | }
492 | else if (ctrl == Z2_MEASURE) { // Z2 Measure done
493 | raw_.z2 = data1;
494 | if (!z_first_measure_) break;
495 | ctrl = X_MEASURE; // X-POSITION Measure
496 | }
497 | }
498 | if (count_measure_ == 0 || !has_touch) {
499 | spi_.transfer16(OFF_MEASURE); // set power down mode
500 | }
501 | spi_end();
502 |
503 | #ifdef TOUCH_SERIAL_DEBUG_FETCH //
504 | if (!has_touch) {
505 | if (Serial) {
506 | Serial.print("raw measure out of range value: ");
507 | Serial.print(data1);
508 | Serial.print(" ctrl: 0x");
509 | Serial.println(ctrl, BIN);
510 | }
511 | }
512 | #endif
513 | if (has_touch && raw_.z1 > 0) { // if z1 is 0 we get a division by 0 exception!
514 | if (raw_.z1 >= raw_.z2) raw_.rz = 0; // more then 2 Finger
515 | // else raw_.rz = (uint16_t)((((int32_t)rx_plate_ * raw_.z2 / raw_.z1) * raw_.x / 4096) - (int32_t)rx_plate_ * raw_.x / 4096);
516 | else raw_.rz = (uint16_t)(((((int32_t)rx_plate_ * raw_.z2 / raw_.z1) * raw_.x) - (int32_t)rx_plate_ * raw_.x) / 4096);
517 | // Formula from ADS7846 pdf: R_TOUCH = Rx-plate * X-Position/4096 * (Z2/Z1 - 1) ; bud work only with float bud i prefer speed
518 | // else raw_.rz = (uint16_t)((float)rx_plate_ * raw_.x / 4096.0 * ((float)raw_.z2 / raw_.z1 - 1.0));
519 |
520 | // Serial.print("rz: "); Serial.print((uint16_t)((((int32_t)rx_plate_ * raw_.z2 / raw_.z1) * raw_.x / 4096) - (int32_t)rx_plate_ * raw_.x / 4096));
521 | // Serial.print(", "); Serial.print((uint16_t)(((((int32_t)rx_plate_ * raw_.z2 / raw_.z1) * raw_.x) - (int32_t)rx_plate_ * raw_.x) / 4096));
522 | // Serial.print(", "); Serial.print((uint16_t)((float)rx_plate_ * raw_.x / 4096.0 * ((float)raw_.z2 / raw_.z1 - 1.0))); Serial.print(" ");
523 | }
524 | else raw_.rz = 0xffff; // indicate 'no touch'
525 | }
526 |
527 |
528 | // --
529 | #ifdef TOUCH_USE_USER_CALIBRATION
530 | void TFT_eTouchBase::CalibrationPoint::print()
531 | {
532 | #ifdef TOUCH_SERIAL_DEBUG
533 | if (Serial) {
534 | Serial.print("x,y scr: ");
535 | Serial.print(scr_x);
536 | Serial.print(",");
537 | Serial.print(scr_y);
538 | Serial.print(" touch: ");
539 | Serial.print(touch_x);
540 | Serial.print(",");
541 | Serial.println(touch_y);
542 | }
543 | #endif
544 | }
545 |
546 | bool TFT_eTouchBase::acurateCalibrationTarget(CalibrationPoint& point)
547 | {
548 | bool acurate = false;
549 | reset(); // empty FIR filter when used
550 | delay(500);
551 | uint8_t cnt = 0;
552 | uint16_t max_x = 0, max_y = 0;
553 | uint16_t min_x = 0xffff, min_y = 0xffff;
554 | uint16_t sum_x = 0, sum_y = 0;
555 | uint16_t org_wait = getMeasureWait();
556 | setMeasureWait(0);
557 | while (cnt < 16) {
558 | update(false);
559 | if (valid()) {
560 | sum_x += raw_.x; sum_y += raw_.y;
561 | if (max_x < raw_.x) max_x = raw_.x;
562 | if (max_y < raw_.y) max_y = raw_.y;
563 | if (min_x > raw_.x) min_x = raw_.x;
564 | if (min_y > raw_.y) min_y = raw_.y;
565 | cnt++;
566 | }
567 | else {
568 | delay(1); // wait for the first touch (ESP8266 crash when not waiting)
569 | }
570 | }
571 | setMeasureWait(org_wait);
572 | point.touch_x = (sum_x - max_x - min_x) / 14;
573 | point.touch_y = (sum_y - max_y - min_y) / 14;
574 |
575 | #ifdef TOUCH_SERIAL_DEBUG
576 | if (Serial) {
577 | Serial.printf("acurate on point[%i, %i](%i, %i) dx: %i dy: %i", point.scr_x, point.scr_y, point.touch_x, point.touch_y, max_x - min_x, max_y - min_y);
578 | if ( (max_x - min_x <= getAcurateDistance()) && (max_y - min_y <= getAcurateDistance()) ) {
579 | acurate = true;
580 | Serial.printf(" ok\n");
581 | }
582 | else {
583 | Serial.printf(" > %i nok\n", getAcurateDistance());
584 | }
585 | }
586 | else {
587 | if ( (max_x - min_x <= getAcurateDistance()) && (max_y - min_y <= getAcurateDistance()) ) {
588 | acurate = true;
589 | }
590 | }
591 | #else
592 | if ( (max_x - min_x <= getAcurateDistance()) && (max_y - min_y <= getAcurateDistance()) ) {
593 | acurate = true;
594 | }
595 | #endif
596 | return acurate;
597 | }
598 |
599 | #endif // TOUCH_USE_USER_CALIBRATION
600 |
--------------------------------------------------------------------------------
/examples/TFT_eSPI/Conways_Life/Conways_Life.ino:
--------------------------------------------------------------------------------
1 | // The Game of Life, also known simply as Life, is a cellular automaton
2 | // devised by the British mathematician John Horton Conway in 1970.
3 | // https://en.wikipedia.org/wiki/Conway's_Game_of_Life
4 |
5 | // touching the corner
6 | // ul: reset this sample
7 | // ur: goto next sample
8 | // ll: speedup animation
9 | // lr: slowdown animation
10 | // touch outside corner will set a cell
11 |
12 | // this sample use touch implemention from TFT_eSPI when TOUCH_CS is defined in User_Setup.h otherwiese TFT_eTouch is used.
13 |
14 | #include
15 | #include
16 |
17 | #include // Hardware-specific library
18 | #ifndef TOUCH_CS
19 | #include
20 | #endif
21 |
22 | TFT_eSPI tft; // Invoke custom library
23 |
24 | #ifndef TOUCH_CS
25 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, 0xff, TFT_eSPI::getSPIinstance());
26 | #endif
27 |
28 | #define CELL_SIZE 2 // cell in pixel can be 1
29 | #define ROTATION 1
30 |
31 | #ifdef TOUCH_CS
32 | #define CALIBRATION_FILE "/TouchCalibData4"
33 | #else
34 | #define CALIBRATION_FILE "/TFT_eTouch.cal"
35 | #endif
36 |
37 | #define BYTES(bits) (((bits) + 7) / 8)
38 |
39 | class LifeData {
40 | uint16_t x_grid_;
41 | uint16_t y_grid_;
42 | uint8_t* data_; // [XBYTES * GRIDY];
43 | public:
44 | LifeData(TFT_eSPI& tft, uint8_t cell_size)
45 | : x_grid_(tft.width()/cell_size)
46 | , y_grid_(tft.height()/cell_size)
47 | , data_(new uint8_t[BYTES(x_grid_) * y_grid_])
48 | {}
49 |
50 | ~LifeData()
51 | {
52 | if (data_) delete data_;
53 | }
54 |
55 | inline void clear()
56 | {
57 | memset(data_, 0, BYTES(x_grid_) * y_grid_);
58 | }
59 |
60 | inline void operator=(const LifeData& d)
61 | {
62 | memcpy(data_, d.data_, BYTES(x_grid_) * y_grid_);
63 | }
64 |
65 | void fill(uint8_t border);
66 | void pattern(uint8_t type, uint16_t x_off, uint16_t y_off);
67 |
68 | inline void set_pos(int16_t x, int16_t y, bool set)
69 | {
70 | if ((x >= x_grid_) || (y >= y_grid_)) return;
71 | uint8_t x_byte = x / 8, x_bit = x % 8;
72 | if (set) data_[x_byte + y * BYTES(x_grid_)] |= _BV(x_bit);
73 | else data_[x_byte + y * BYTES(x_grid_)] &= ~_BV(x_bit);
74 | }
75 |
76 | inline bool get_pos(int16_t x, int16_t y) const
77 | {
78 | if ((x >= x_grid_) || (y >= y_grid_)) return false;
79 | uint8_t x_byte = x / 8, x_bit = x % 8;
80 | return (data_[x_byte + y * BYTES(x_grid_)] & _BV(x_bit)) != 0;
81 | }
82 |
83 | uint8_t neighbors(int16_t x, int16_t y) const;
84 | void print() const;
85 | };
86 |
87 |
88 | class Life {
89 | TFT_eSPI& tft_;
90 | uint8_t cell_size_;
91 | LifeData grid_, nextgrid_;
92 | uint8_t generation_delay_, border_;
93 |
94 | void draw();
95 | void compute();
96 | uint8_t touched();
97 | public:
98 | Life(TFT_eSPI& tft_, uint8_t cell_size = 3, uint8_t border = 255)
99 | : tft_(tft)
100 | , cell_size_(cell_size)
101 | , grid_(tft_, cell_size)
102 | , nextgrid_(tft_, cell_size)
103 | , generation_delay_(0)
104 | , border_(border)
105 | { }
106 |
107 | void begin();
108 | void step();
109 | };
110 |
111 | void LifeData::pattern(uint8_t type, uint16_t x_off, uint16_t y_off)
112 | {
113 | switch(type) {
114 | case 0: // Lightweight spaceship going right (LWSS)
115 | set_pos(1 + x_off, 1 + y_off, true);
116 | set_pos(1 + x_off, 3 + y_off, true);
117 | set_pos(2 + x_off, 4 + y_off, true);
118 | set_pos(3 + x_off, 4 + y_off, true);
119 | set_pos(4 + x_off, 4 + y_off, true);
120 | set_pos(5 + x_off, 4 + y_off, true);
121 | set_pos(5 + x_off, 3 + y_off, true);
122 | set_pos(5 + x_off, 2 + y_off, true);
123 | set_pos(4 + x_off, 1 + y_off, true);
124 | break;
125 | case 1: // Lightweight spaceship going left (LWSS)
126 | set_pos(6 - 1 + x_off, 1 + y_off, true);
127 | set_pos(6 - 1 + x_off, 3 + y_off, true);
128 | set_pos(6 - 2 + x_off, 4 + y_off, true);
129 | set_pos(6 - 3 + x_off, 4 + y_off, true);
130 | set_pos(6 - 4 + x_off, 4 + y_off, true);
131 | set_pos(6 - 5 + x_off, 4 + y_off, true);
132 | set_pos(6 - 5 + x_off, 3 + y_off, true);
133 | set_pos(6 - 5 + x_off, 2 + y_off, true);
134 | set_pos(6 - 4 + x_off, 1 + y_off, true);
135 | break;
136 | case 2: // Middleweight spaceship going right (MWSS)
137 | set_pos(7 - 4 + x_off, 1 + y_off, true);
138 | set_pos(7 - 2 + x_off, 2 + y_off, true);
139 | set_pos(7 - 6 + x_off, 2 + y_off, true);
140 | set_pos(7 - 1 + x_off, 3 + y_off, true);
141 | set_pos(7 - 1 + x_off, 4 + y_off, true);
142 | set_pos(7 - 6 + x_off, 4 + y_off, true);
143 | set_pos(7 - 1 + x_off, 5 + y_off, true);
144 | set_pos(7 - 2 + x_off, 5 + y_off, true);
145 | set_pos(7 - 3 + x_off, 5 + y_off, true);
146 | set_pos(7 - 4 + x_off, 5 + y_off, true);
147 | set_pos(7 - 5 + x_off, 5 + y_off, true);
148 | break;
149 | case 3: // Middleweight spaceship going left (MWSS)
150 | set_pos(4 + x_off, 1 + y_off, true);
151 | set_pos(2 + x_off, 2 + y_off, true);
152 | set_pos(6 + x_off, 2 + y_off, true);
153 | set_pos(1 + x_off, 3 + y_off, true);
154 | set_pos(1 + x_off, 4 + y_off, true);
155 | set_pos(6 + x_off, 4 + y_off, true);
156 | set_pos(1 + x_off, 5 + y_off, true);
157 | set_pos(2 + x_off, 5 + y_off, true);
158 | set_pos(3 + x_off, 5 + y_off, true);
159 | set_pos(4 + x_off, 5 + y_off, true);
160 | set_pos(5 + x_off, 5 + y_off, true);
161 | break;
162 | case 4: // Heavyweight spaceship going right (HWSS)
163 | y_off += 2;
164 | set_pos(8 - 1 + x_off, 1 + y_off, true);
165 | set_pos(8 - 2 + x_off, 1 + y_off, true);
166 | set_pos(8 - 3 + x_off, 1 + y_off, true);
167 | set_pos(8 - 4 + x_off, 1 + y_off, true);
168 | set_pos(8 - 5 + x_off, 1 + y_off, true);
169 | set_pos(8 - 6 + x_off, 1 + y_off, true);
170 | set_pos(8 - 1 + x_off, 2 + y_off, true);
171 | set_pos(8 - 7 + x_off, 2 + y_off, true);
172 | set_pos(8 - 1 + x_off, 3 + y_off, true);
173 | set_pos(8 - 2 + x_off, 4 + y_off, true);
174 | set_pos(8 - 7 + x_off, 4 + y_off, true);
175 | set_pos(8 - 4 + x_off, 5 + y_off, true);
176 | set_pos(8 - 5 + x_off, 5 + y_off, true);
177 | break;
178 | case 5: // Heavyweight spaceship going left (HWSS)
179 | y_off += 2;
180 | set_pos(1 + x_off, 1 + y_off, true);
181 | set_pos(2 + x_off, 1 + y_off, true);
182 | set_pos(3 + x_off, 1 + y_off, true);
183 | set_pos(4 + x_off, 1 + y_off, true);
184 | set_pos(5 + x_off, 1 + y_off, true);
185 | set_pos(6 + x_off, 1 + y_off, true);
186 | set_pos(1 + x_off, 2 + y_off, true);
187 | set_pos(7 + x_off, 2 + y_off, true);
188 | set_pos(1 + x_off, 3 + y_off, true);
189 | set_pos(2 + x_off, 4 + y_off, true);
190 | set_pos(7 + x_off, 4 + y_off, true);
191 | set_pos(4 + x_off, 5 + y_off, true);
192 | set_pos(5 + x_off, 5 + y_off, true);
193 | break;
194 | case 6: // Glider going down right
195 | set_pos(3 + x_off, 1 + y_off, true);
196 | set_pos(1 + x_off, 2 + y_off, true);
197 | set_pos(3 + x_off, 2 + y_off, true);
198 | set_pos(2 + x_off, 3 + y_off, true);
199 | set_pos(3 + x_off, 3 + y_off, true);
200 | break;
201 | case 7: // Glider going down left
202 | set_pos(4 - 3 + x_off, 1 + y_off, true);
203 | set_pos(4 - 1 + x_off, 2 + y_off, true);
204 | set_pos(4 - 3 + x_off, 2 + y_off, true);
205 | set_pos(4 - 2 + x_off, 3 + y_off, true);
206 | set_pos(4 - 3 + x_off, 3 + y_off, true);
207 | break;
208 | case 8: // Glider going up right
209 | set_pos(3 + x_off, 4 - 1 + y_off, true);
210 | set_pos(1 + x_off, 4 - 2 + y_off, true);
211 | set_pos(3 + x_off, 4 - 2 + y_off, true);
212 | set_pos(2 + x_off, 4 - 3 + y_off, true);
213 | set_pos(3 + x_off, 4 - 3 + y_off, true);
214 | break;
215 | case 9: // Glider going up left
216 | set_pos(4 - 3 + x_off, 4 - 1 + y_off, true);
217 | set_pos(4 - 1 + x_off, 4 - 2 + y_off, true);
218 | set_pos(4 - 3 + x_off, 4 - 2 + y_off, true);
219 | set_pos(4 - 2 + x_off, 4 - 3 + y_off, true);
220 | set_pos(4 - 3 + x_off, 4 - 3 + y_off, true);
221 | break;
222 | case 10: // Blinker
223 | set_pos(1 + x_off, 2 + y_off, true);
224 | set_pos(2 + x_off, 2 + y_off, true);
225 | set_pos(3 + x_off, 2 + y_off, true);
226 | break;
227 | case 11: // Toad
228 | set_pos(3 + x_off, 1 + y_off, true);
229 | set_pos(1 + x_off, 2 + y_off, true);
230 | set_pos(4 + x_off, 2 + y_off, true);
231 | set_pos(1 + x_off, 3 + y_off, true);
232 | set_pos(4 + x_off, 3 + y_off, true);
233 | set_pos(4 + x_off, 4 + y_off, true);
234 | break;
235 | case 12: // Beacon
236 | set_pos(1 + x_off, 1 + y_off, true);
237 | set_pos(2 + x_off, 1 + y_off, true);
238 | set_pos(1 + x_off, 2 + y_off, true);
239 | set_pos(2 + x_off, 2 + y_off, true);
240 | set_pos(3 + x_off, 3 + y_off, true);
241 | set_pos(4 + x_off, 3 + y_off, true);
242 | set_pos(3 + x_off, 4 + y_off, true);
243 | set_pos(4 + x_off, 4 + y_off, true);
244 | break;
245 | case 13: // Pulsar
246 | x_off++;
247 | y_off++;
248 | set_pos(3 + x_off, 1 + y_off, true);
249 | set_pos(4 + x_off, 1 + y_off, true);
250 | set_pos(5 + x_off, 1 + y_off, true);
251 | set_pos(9 + x_off, 1 + y_off, true);
252 | set_pos(10 + x_off, 1 + y_off, true);
253 | set_pos(11 + x_off, 1 + y_off, true);
254 | set_pos(1 + x_off, 3 + y_off, true);
255 | set_pos(6 + x_off, 3 + y_off, true);
256 | set_pos(8 + x_off, 3 + y_off, true);
257 | set_pos(13 + x_off, 3 + y_off, true);
258 | set_pos(1 + x_off, 4 + y_off, true);
259 | set_pos(6 + x_off, 4 + y_off, true);
260 | set_pos(8 + x_off, 4 + y_off, true);
261 | set_pos(13 + x_off, 4 + y_off, true);
262 | set_pos(1 + x_off, 5 + y_off, true);
263 | set_pos(6 + x_off, 5 + y_off, true);
264 | set_pos(8 + x_off, 5 + y_off, true);
265 | set_pos(13 + x_off, 5 + y_off, true);
266 | set_pos(3 + x_off, 6 + y_off, true);
267 | set_pos(4 + x_off, 6 + y_off, true);
268 | set_pos(5 + x_off, 6 + y_off, true);
269 | set_pos(9 + x_off, 6 + y_off, true);
270 | set_pos(10 + x_off, 6 + y_off, true);
271 | set_pos(11 + x_off, 6 + y_off, true);
272 | set_pos(3 + x_off, 8 + y_off, true);
273 | set_pos(4 + x_off, 8 + y_off, true);
274 | set_pos(5 + x_off, 8 + y_off, true);
275 | set_pos(9 + x_off, 8 + y_off, true);
276 | set_pos(10 + x_off, 8 + y_off, true);
277 | set_pos(11 + x_off, 8 + y_off, true);
278 | set_pos(1 + x_off, 9 + y_off, true);
279 | set_pos(6 + x_off, 9 + y_off, true);
280 | set_pos(8 + x_off, 9 + y_off, true);
281 | set_pos(13 + x_off, 9 + y_off, true);
282 | set_pos(1 + x_off, 10 + y_off, true);
283 | set_pos(6 + x_off, 10 + y_off, true);
284 | set_pos(8 + x_off, 10 + y_off, true);
285 | set_pos(13 + x_off, 10 + y_off, true);
286 | set_pos(1 + x_off, 11 + y_off, true);
287 | set_pos(6 + x_off, 11 + y_off, true);
288 | set_pos(8 + x_off, 11 + y_off, true);
289 | set_pos(13 + x_off, 11 + y_off, true);
290 | set_pos(3 + x_off, 13 + y_off, true);
291 | set_pos(4 + x_off, 13 + y_off, true);
292 | set_pos(5 + x_off, 13 + y_off, true);
293 | set_pos(9 + x_off, 13 + y_off, true);
294 | set_pos(10 + x_off, 13 + y_off, true);
295 | set_pos(11 + x_off, 13 + y_off, true);
296 | break;
297 |
298 | case 14: // Pentadecathlon v (period 15)
299 | x_off += 3;
300 | y_off += 3;
301 | set_pos(2 + x_off, 1 + y_off, true);
302 | set_pos(2 + x_off, 2 + y_off, true);
303 | set_pos(1 + x_off, 3 + y_off, true);
304 | set_pos(3 + x_off, 3 + y_off, true);
305 | set_pos(2 + x_off, 4 + y_off, true);
306 | set_pos(2 + x_off, 5 + y_off, true);
307 | set_pos(2 + x_off, 6 + y_off, true);
308 | set_pos(2 + x_off, 7 + y_off, true);
309 | set_pos(1 + x_off, 8 + y_off, true);
310 | set_pos(3 + x_off, 8 + y_off, true);
311 | set_pos(2 + x_off, 9 + y_off, true);
312 | set_pos(2 + x_off, 10 + y_off, true);
313 | break;
314 |
315 | case 15: // Pentadecathlon h (period 15)
316 | x_off += 3;
317 | y_off += 3;
318 | set_pos(3 + x_off, 1 + y_off, true);
319 | set_pos(8 + x_off, 1 + y_off, true);
320 | set_pos(1 + x_off, 2 + y_off, true);
321 | set_pos(2 + x_off, 2 + y_off, true);
322 | set_pos(4 + x_off, 2 + y_off, true);
323 | set_pos(5 + x_off, 2 + y_off, true);
324 | set_pos(6 + x_off, 2 + y_off, true);
325 | set_pos(7 + x_off, 2 + y_off, true);
326 | set_pos(9 + x_off, 2 + y_off, true);
327 | set_pos(10 + x_off, 2 + y_off, true);
328 | set_pos(3 + x_off, 3 + y_off, true);
329 | set_pos(8 + x_off, 3 + y_off, true);
330 | break;
331 |
332 | case 16: // The R-pentomino
333 | set_pos(2 + x_off, 1 + y_off, true);
334 | set_pos(3 + x_off, 1 + y_off, true);
335 | set_pos(1 + x_off, 2 + y_off, true);
336 | set_pos(2 + x_off, 2 + y_off, true);
337 | set_pos(2 + x_off, 3 + y_off, true);
338 | break;
339 |
340 | case 17: // Diehard
341 | set_pos(7 + x_off, 1 + y_off, true);
342 | set_pos(1 + x_off, 2 + y_off, true);
343 | set_pos(2 + x_off, 2 + y_off, true);
344 | set_pos(2 + x_off, 3 + y_off, true);
345 | set_pos(6 + x_off, 3 + y_off, true);
346 | set_pos(7 + x_off, 3 + y_off, true);
347 | set_pos(8 + x_off, 3 + y_off, true);
348 | break;
349 |
350 | case 18: // infinite Line
351 | y_off -= 1;
352 | set_pos(1 + x_off, 1 + y_off, true);
353 | set_pos(2 + x_off, 1 + y_off, true);
354 | set_pos(3 + x_off, 1 + y_off, true);
355 | set_pos(4 + x_off, 1 + y_off, true);
356 | set_pos(5 + x_off, 1 + y_off, true);
357 | set_pos(6 + x_off, 1 + y_off, true);
358 | set_pos(7 + x_off, 1 + y_off, true);
359 | set_pos(8 + x_off, 1 + y_off, true);
360 | set_pos(10 + x_off, 1 + y_off, true);
361 | set_pos(11 + x_off, 1 + y_off, true);
362 | set_pos(12 + x_off, 1 + y_off, true);
363 | set_pos(13 + x_off, 1 + y_off, true);
364 | set_pos(14 + x_off, 1 + y_off, true);
365 | set_pos(18 + x_off, 1 + y_off, true);
366 | set_pos(19 + x_off, 1 + y_off, true);
367 | set_pos(20 + x_off, 1 + y_off, true);
368 | set_pos(27 + x_off, 1 + y_off, true);
369 | set_pos(28 + x_off, 1 + y_off, true);
370 | set_pos(29 + x_off, 1 + y_off, true);
371 | set_pos(30 + x_off, 1 + y_off, true);
372 | set_pos(31 + x_off, 1 + y_off, true);
373 | set_pos(32 + x_off, 1 + y_off, true);
374 | set_pos(33 + x_off, 1 + y_off, true);
375 | set_pos(35 + x_off, 1 + y_off, true);
376 | set_pos(36 + x_off, 1 + y_off, true);
377 | set_pos(37 + x_off, 1 + y_off, true);
378 | set_pos(38 + x_off, 1 + y_off, true);
379 | set_pos(39 + x_off, 1 + y_off, true);
380 | break;
381 |
382 | case 19: // infinite 5x5
383 | set_pos(1 + x_off, 1 + y_off, true);
384 | set_pos(2 + x_off, 1 + y_off, true);
385 | set_pos(3 + x_off, 1 + y_off, true);
386 | set_pos(5 + x_off, 1 + y_off, true);
387 | set_pos(1 + x_off, 2 + y_off, true);
388 | set_pos(4 + x_off, 3 + y_off, true);
389 | set_pos(5 + x_off, 3 + y_off, true);
390 | set_pos(2 + x_off, 4 + y_off, true);
391 | set_pos(3 + x_off, 4 + y_off, true);
392 | set_pos(5 + x_off, 4 + y_off, true);
393 | set_pos(1 + x_off, 5 + y_off, true);
394 | set_pos(3 + x_off, 5 + y_off, true);
395 | set_pos(5 + x_off, 5 + y_off, true);
396 | break;
397 |
398 | default:
399 | break;
400 | }
401 | }
402 |
403 | void LifeData::fill(uint8_t border)
404 | {
405 | int x_off = x_grid_/2;
406 | int y_off = y_grid_/2;
407 | clear();
408 | switch(border) {
409 | case 255: // The R-pentomino
410 | pattern(16, x_off, y_off);
411 | break;
412 | case 254: // Diehard
413 | pattern(17, x_off-x_off/2, y_off-y_off/2);
414 | pattern(17, x_off, y_off-y_off/2);
415 | pattern(17, x_off+x_off/2, y_off-y_off/2);
416 |
417 | pattern(17, x_off-x_off/2, y_off);
418 | pattern(17, x_off, y_off);
419 | pattern(17, x_off+x_off/2, y_off);
420 |
421 | pattern(17, x_off-x_off/2, y_off+y_off/2);
422 | pattern(17, x_off, y_off+y_off/2);
423 | pattern(17, x_off+x_off/2, y_off+y_off/2);
424 | break;
425 | case 253: // infinite Line
426 | y_off -= 1;
427 | pattern(18, 6, y_off);
428 | break;
429 | case 252: // infinite 5x5
430 | x_off = x_grid_*4/5;
431 | y_off += y_off/2;
432 | pattern(19, x_off, y_off);
433 | break;
434 | case 251: // spaceships
435 | pattern(0, x_off, 3);
436 | pattern(1, x_off, 18 + 3);
437 | pattern(2, x_off, y_off - 18/2);
438 | pattern(3, x_off, y_off + 18/2);
439 | pattern(4, x_off, y_grid_ - 18 - 10);
440 | pattern(5, x_off, y_grid_ - 10);
441 | break;
442 | case 250: // Glider
443 | pattern(6, x_off, y_grid_/2);
444 | pattern(6, x_off + 13, y_grid_/2);
445 | pattern(6, x_off + 2*13, y_grid_/2);
446 | pattern(6, x_off + 3*13, y_grid_/2);
447 | pattern(6, x_off + 4*13, y_grid_/2);
448 | pattern(6, x_off + 5*13, y_grid_/2);
449 | pattern(6, x_off + 6*13, y_grid_/2);
450 | pattern(6, x_off + 7*13, y_grid_/2);
451 | pattern(7, x_off - 13, y_grid_/2);
452 | pattern(7, x_off - 2*13, y_grid_/2);
453 | pattern(7, x_off - 3*13, y_grid_/2);
454 | pattern(7, x_off - 4*13, y_grid_/2);
455 | pattern(7, x_off - 5*13, y_grid_/2);
456 | pattern(7, x_off - 6*13, y_grid_/2);
457 | pattern(7, x_off - 7*13, y_grid_/2);
458 | pattern(7, x_off - 8*13, y_grid_/2);
459 | pattern(8, x_off, y_grid_/2 - 13);
460 | pattern(8, x_off + 13, y_grid_/2 - 13);
461 | pattern(8, x_off + 2*13, y_grid_/2 - 13);
462 | pattern(8, x_off + 3*13, y_grid_/2 - 13);
463 | pattern(8, x_off + 4*13, y_grid_/2 - 13);
464 | pattern(8, x_off + 5*13, y_grid_/2 - 13);
465 | pattern(8, x_off + 6*13, y_grid_/2 - 13);
466 | pattern(8, x_off + 7*13, y_grid_/2 - 13);
467 | pattern(9, x_off - 13, y_grid_/2 - 13);
468 | pattern(9, x_off - 2*13, y_grid_/2 - 13);
469 | pattern(9, x_off - 3*13, y_grid_/2 - 13);
470 | pattern(9, x_off - 4*13, y_grid_/2 - 13);
471 | pattern(9, x_off - 5*13, y_grid_/2 - 13);
472 | pattern(9, x_off - 6*13, y_grid_/2 - 13);
473 | pattern(9, x_off - 7*13, y_grid_/2 - 13);
474 | pattern(9, x_off - 8*13, y_grid_/2 - 13);
475 | break;
476 | case 249: // Oscillators
477 | pattern(10, x_off-20, y_off-20);
478 | pattern(11, x_off, y_off-20);
479 | pattern(12, x_off+20, y_off-20);
480 |
481 | pattern(13, 0, 0);
482 | pattern(14, x_off-10, y_off);
483 | pattern(15, x_off+10, y_off);
484 | break;
485 | case 248: // Random
486 | border = y_grid_ / 4;
487 | default:
488 | if (2 * border > y_grid_ - 40) return;
489 | if (2 * border > x_grid_ - 40) return;
490 | for (int16_t x = border; x < x_grid_-border; x++) {
491 | for (int16_t y = border; y < y_grid_-border; y++) {
492 | if (random(5) == 1) set_pos(x, y, true);
493 | }
494 | }
495 | }
496 | }
497 |
498 | void LifeData::print() const
499 | {
500 | Serial.print("dim: ");
501 | Serial.print(x_grid_);
502 | Serial.print(" / ");
503 | Serial.println(y_grid_);
504 | for (int16_t x = 0; x < x_grid_; x++) {
505 | #if 1
506 | for (int16_t y = 0; y < y_grid_; y++) {
507 | Serial.print("grid[");
508 | Serial.print(x);
509 | Serial.print(", ");
510 | Serial.print(y);
511 | Serial.print("] ");
512 | Serial.print(get_pos(x, y));
513 | Serial.print(" ");
514 | Serial.println(neighbors(x,y));
515 | }
516 | #else
517 | Serial.print("grid[");
518 | Serial.print(x);
519 | Serial.print("] ");
520 |
521 | for (int16_t y = 0; y < y_grid_; y++) {
522 | Serial.print("[");
523 | Serial.print(get_pos(x, y));
524 | Serial.print(" ");
525 | Serial.print(neighbors(x,y));
526 | Serial.print("]");
527 | }
528 | Serial.println("");
529 | #endif
530 | }
531 | }
532 |
533 | uint8_t LifeData::neighbors(int16_t x, int16_t y) const
534 | {
535 | uint8_t neighbors = 0;
536 |
537 | uint16_t xpos = (x + 1) % x_grid_;
538 | uint16_t xneg = (x + x_grid_ - 1) % x_grid_;
539 |
540 | uint16_t ypos = (y + 1) % y_grid_;
541 | uint16_t yneg = (y + y_grid_ - 1) % y_grid_;
542 |
543 | if (get_pos(xneg, y) ) neighbors++;
544 | if (get_pos(xneg, yneg)) neighbors++;
545 | if (get_pos(x, yneg)) neighbors++;
546 | if (get_pos(xpos, yneg)) neighbors++;
547 | if (get_pos(xpos, y) ) neighbors++;
548 | if (get_pos(xpos, ypos)) neighbors++;
549 | if (get_pos(x, ypos)) neighbors++;
550 | if (get_pos(xneg, ypos)) neighbors++;
551 |
552 | return neighbors;
553 | }
554 |
555 | void Life::draw()
556 | {
557 | uint16_t color = TFT_WHITE;
558 | for (int16_t x = 0; x < tft_.width()/cell_size_; x++) {
559 | for (int16_t y = 0; y < tft_.height()/cell_size_; y++) {
560 | if (grid_.get_pos(x, y) != nextgrid_.get_pos(x, y)) {
561 | if (nextgrid_.get_pos(x, y)) color = TFT_WHITE;
562 | else color = TFT_BLACK;
563 | tft_.fillRect(cell_size_ * x, cell_size_ * y, cell_size_, cell_size_, color);
564 | }
565 | }
566 | }
567 | grid_ = nextgrid_;
568 | }
569 |
570 | void Life::compute()
571 | {
572 | nextgrid_.clear();
573 | for (int16_t x = 0; x < tft_.width()/cell_size_; x++) {
574 | for (int16_t y = 0; y < tft_.height()/cell_size_; y++) {
575 | bool active = grid_.get_pos(x, y);
576 | uint8_t neighbors = grid_.neighbors(x, y);
577 | if (active) {
578 | if (neighbors >= 2 && neighbors <= 3) {
579 | // stay active
580 | nextgrid_.set_pos(x, y, true);
581 | }
582 | }
583 | else if (neighbors == 3) {
584 | // get active
585 | nextgrid_.set_pos(x, y, true);
586 | }
587 | }
588 | }
589 | }
590 |
591 | uint8_t Life::touched()
592 | {
593 | const int offset = 20;
594 | #ifdef TOUCH_CS
595 | uint16_t x, y;
596 | if (tft_.getTouch(&x, &y)) {
597 | #else
598 | int16_t x, y;
599 | if (touch.getXY(x, y)) {
600 | #endif
601 | if (x < offset) {
602 | if (y < offset) return 2; // left upper corner
603 | else if (y > tft_.height() - offset) return 4; // left lower corner
604 | }
605 | if (x > tft_.width() - offset) {
606 | if (y < offset) return 5; // right upper corner
607 | else if (y > tft_.height() - offset) return 3; // right lower corner
608 | }
609 | nextgrid_.set_pos(x/cell_size_, y/cell_size_, true);
610 | return 1; // anywhere
611 | }
612 | return 0; // nowhere
613 | }
614 |
615 | void Life::begin()
616 | {
617 | // Display a simple splash screen
618 | tft_.fillScreen(TFT_BLACK);
619 | tft_.setTextSize(2);
620 | tft_.setTextColor(TFT_WHITE);
621 | tft_.setCursor(35, 5);
622 | tft_.println(F("Arduino"));
623 | tft_.setCursor(35, 35);
624 | tft_.println(F("Conway's"));
625 | tft_.setCursor(35, 65);
626 | tft_.println(F("Game of Life"));
627 |
628 | tft_.setCursor(35, 95);
629 | tft_.println(F("Touch corners"));
630 |
631 | tft_.setCursor(35, 125);
632 | switch(border_) {
633 | case 255: // The R-pentomino
634 | tft_.println(F("The R-pentomino"));
635 | break;
636 | case 254: // Diehard
637 | tft_.println(F("Diehard"));
638 | break;
639 | case 253: // infinite Line
640 | tft_.println(F("infinite Line"));
641 | break;
642 | case 252: // infinite 5x5
643 | tft_.println(F("infinite 5x5"));
644 | break;
645 | case 251: // spaceships
646 | tft_.println(F("Spaceships"));
647 | break;
648 | case 250: // Glider
649 | tft_.println(F("Glider"));
650 | break;
651 | case 249: // Oscillators
652 | tft_.println(F("Oscillators"));
653 | break;
654 | case 248: // Random
655 | tft_.println(F("Random"));
656 | break;
657 | default:
658 | tft_.println(F("Border"));
659 | break;
660 | }
661 | delay(2000);
662 |
663 | tft_.fillScreen(TFT_BLACK);
664 | grid_.clear();
665 | nextgrid_.fill(border_);
666 | }
667 |
668 | void Life::step()
669 | {
670 | draw();
671 | compute();
672 | switch(touched()) {
673 | case 0: // nowhere
674 | break;
675 | case 1: // anywhere
676 | break;
677 | case 2: // left upper corner
678 | begin();
679 | break;
680 | case 3: // right lower corner
681 | if (generation_delay_ < 255) generation_delay_++;
682 | break;
683 | case 4: // left lower corner
684 | if (generation_delay_ > 0) generation_delay_--;
685 | break;
686 | case 5: // right upper corner
687 | if (border_ >= 248) {
688 | if (--border_ < 248) {
689 | border_ = 255;
690 | }
691 | begin();
692 | }
693 | break;
694 | default: break;
695 | }
696 | delay(generation_delay_ * 20);
697 | }
698 |
699 | Life* life = 0;
700 |
701 |
702 | void setup()
703 | {
704 | Serial.begin(115200);
705 | delay(2000);
706 | //Set up the display
707 | tft.init();
708 | tft.setRotation(ROTATION);
709 |
710 |
711 | #ifndef TOUCH_CS
712 | touch.init();
713 |
714 | // untouched: 35 us touched: 136 us
715 | // touch.setMeasure(0, false, true, false, 3); // constructor defaults (take third measure, start with z axis)
716 | // touch.setAveraging(false, false); // constructor defaults (without averaging)
717 |
718 | // untouched: 35 us touched: 56 us
719 | // touch.setMeasure(0, true, true, false, 1); // Differential mode fastest (each axis read only once, may work)
720 |
721 | // untouched: 35 us touched: 77 us
722 | // touch.setMeasure(1, true, true, false, 1); // Differential mode faster (take second z bud first x, y. may work)
723 |
724 | // untouched: 35 us touched: d 190 max 600 us
725 | // touch.setMeasure(1, true, true, true); // z with local min, acurate x,y, 35 us tot d 200 max 600
726 |
727 | // untouched: 35 us touched: d 220 max 600 us
728 | // touch.setMeasure(0, false, true); // z first, acurate z,x,y
729 |
730 | // untouched: 35 us touched: d 190 max 600 us
731 | // touch.setMeasure(2, true, true, false); // z first 3end, acurate x,y
732 |
733 | // untouched: 35 us touched: 95 us
734 | // touch.setMeasure(1, true, true, false, 2); // z first, take 2'th z,x,y
735 |
736 | // untouched: 35 us touched: 95 us
737 | touch.setMeasure(0, false, true, false, 2); // z first, take 2'th z,x,y
738 |
739 | // untouched: 35 us touched: 1302 us
740 | // touch.setMeasure(10, false, true, true, 14); // slowest (drop 10(..255) and additional 16 measures. averaging 14 with min max value)
741 | // touch.setAveraging(true, true);
742 |
743 | // untouched: 35 us touched: 334 us
744 | // touch.setMeasure(3, false, true, false, 3); // drop 3 averaging 3 (+2 min max)
745 | // touch.setAveraging(true, true); // averaging on (+2 min max)
746 |
747 | #endif
748 |
749 | tft.fillScreen(TFT_WHITE);
750 |
751 | tft.setCursor(20, 0, 2);
752 | tft.setTextColor(TFT_BLACK, TFT_WHITE);
753 | tft.setTextSize(1);
754 | tft.println("calibration run");
755 |
756 | //SPIFFS.remove(CALIBRATION_FILE);
757 |
758 | // check if calibration file exists
759 | #ifdef TOUCH_CS
760 | uint16_t cal_data[5];
761 | bool has_cal_file = false;
762 |
763 | // check file system
764 | if (!SPIFFS.begin()) {
765 | Serial.println("formating file system");
766 |
767 | SPIFFS.format();
768 | SPIFFS.begin();
769 | }
770 |
771 | if (SPIFFS.exists(CALIBRATION_FILE)) {
772 | File f = SPIFFS.open(CALIBRATION_FILE, "r");
773 | if (f) {
774 | if (f.readBytes((char *)cal_data, 10) == 10) has_cal_file = true;
775 | if (has_cal_file) {
776 | Serial.printf("Calibration: x %u, %u, y %u, %u, opt %u\n", cal_data[0], cal_data[1], cal_data[2], cal_data[3], cal_data[4]);
777 | }
778 | else Serial.println("Calibration File read error");
779 | f.close();
780 | }
781 | }
782 | if (has_cal_file) {
783 | // calibration data valid
784 | tft.setTouch(cal_data);
785 | }
786 | else {
787 | // data not valid. recalibrate
788 | tft.calibrateTouch(cal_data, TFT_WHITE, TFT_RED, 15);
789 | // store data
790 | File f = SPIFFS.open(CALIBRATION_FILE, "w");
791 | if (f) {
792 | f.write((const unsigned char *)cal_data, 10);
793 | f.close();
794 | }
795 | }
796 | #else
797 | if (!touch.readCalibration(CALIBRATION_FILE)) {
798 | #ifdef TOUCH_USE_USER_CALIBRATION
799 | TFT_eTouchBase::Calibation calibation; // = { 265, 3790, 264, 3850, 2 }; // x and y axes have same direction on touch & display
800 | touch.getUserCalibration(calibation, 4);
801 | touch.setCalibration(calibation);
802 | touch.writeCalibration(CALIBRATION_FILE);
803 | #else
804 | Serial.printf("Calibration not readed %s take default configuration. Store a valid configuration with eTouch_edit\n", CALIBRATION_FILE);
805 | touch.calibration() = { 265, 3790, 264, 3850, 2 };
806 | #endif
807 | }
808 | #endif
809 |
810 | tft.fillScreen(TFT_BLACK);
811 | life = new Life(tft, CELL_SIZE);
812 | life->begin();
813 | }
814 |
815 | void loop() {
816 | life->step();
817 | }
818 |
--------------------------------------------------------------------------------