F439_CPP_SPI_RA8875_TFT_LCD_04 1.0
STM32F439 SPI RA8875 7-inch TFT display with FRAM-backed touch calibration, built on the STM32_GFX, STM32_RA8875 and STM32_MB85RS64V libraries
Loading...
Searching...
No Matches
touchCalibration.cpp
Go to the documentation of this file.
1
9#include <stdio.h>
10#include "main.h"
11#include "touchCalibration.hpp"
12
13#ifdef TOUCH_CALIBRATION_ENABLE
14
15/* Defined in main.c, together with HAL_UART_RxCpltCallback() and HAL_UART_ErrorCallback() */
16extern "C" {
18extern volatile uint8_t uartRxChar;
20extern volatile uint8_t uartKeyPressed;
21}
22
23namespace {
24
26const int CALIBRATION_ATTEMPTS = 3;
27
29const uint16_t TARGET_RADIUS = 5;
30
32const uint32_t HOLD_MS = 300;
33
35const uint32_t SETTLE_MS = 100;
36
38const uint32_t RELEASE_GAP_MS = 100;
39
41const uint16_t ACCEPTED_COLOUR = RA8875_GREEN;
42
59void capturePress(Adafruit_RA8875 *display, uint16_t x, uint16_t y, uint16_t radius, tsPoint_t *point) {
60 uint16_t rx = 0;
61 uint16_t ry = 0;
62
63 // Throw away anything left over from before, so an old touch is never used
64 while (display->touched()) {
65 display->touchRead(&rx, &ry);
66 HAL_Delay(5);
67 }
68
69 for (;;) {
70 // Wait for the pen to go down
71 while (!display->touched()) {
72 HAL_Delay(1);
73 }
74
75 const uint32_t pressStart = HAL_GetTick();
76 uint32_t lastReading = pressStart;
77 int64_t sumX = 0;
78 int64_t sumY = 0;
79 int32_t count = 0;
80 bool accepted = false;
81
82 // Read for as long as readings keep arriving
83 while ((HAL_GetTick() - lastReading) < RELEASE_GAP_MS) {
84 if (display->touched()) {
85 display->touchRead(&rx, &ry);
86 const uint32_t now = HAL_GetTick();
87 lastReading = now;
88 if ((rx != 0 || ry != 0) && (now - pressStart) >= SETTLE_MS) {
89 sumX += rx;
90 sumY += ry;
91 count++;
92 }
93 if (!accepted && (now - pressStart) >= HOLD_MS) {
94 accepted = true;
95 display->drawCircle(x, y, radius + 4, ACCEPTED_COLOUR);
96 display->drawCircle(x, y, radius + 5, ACCEPTED_COLOUR);
97 }
98 } else {
99 HAL_Delay(1);
100 }
101 }
102
103 if (accepted && count > 0) {
104 point->x = (int32_t) (sumX / count);
105 point->y = (int32_t) (sumY / count);
106 return;
107 }
108 // A short tap, or no usable reading: ignore it and wait for a proper press
109 }
110}
111
113const uint8_t KEY_ESC = 0x1B;
114
130bool waitForSpaceOrEsc(UART_HandleTypeDef *uart) {
131 uartKeyPressed = 0;
132
133 // Cancel any receive left over from an earlier call and drop stale characters
134 HAL_UART_AbortReceive(uart);
135 __HAL_UART_FLUSH_DRREGISTER(uart);
136
137 printf(" Press the space bar to show the next target, or Esc to redo this one\r\n");
138
139 const HAL_StatusTypeDef status = HAL_UART_Receive_IT(uart, (uint8_t *) &uartRxChar, 1);
140 if (status != HAL_OK) {
141 printf("Could not start receiving from the UART (HAL status %d)\r\n", (int) status);
142 Error_Handler();
143 return true;
144 }
145
146 while (uartKeyPressed == 0) {
147 HAL_Delay(1);
148 }
149 return uartKeyPressed != KEY_ESC;
150}
151
160tsPoint_t renderCalibrationScreen(Adafruit_RA8875 *display, uint16_t x, uint16_t y, uint16_t radius) {
161 display->fillScreen(RA8875_WHITE);
162 display->drawCircle(x, y, radius, RA8875_RED);
163 display->drawCircle(x, y, radius + 2, 0x8410); // 50% gray
164
165 tsPoint_t point = { 0, 0 };
166 capturePress(display, x, y, radius, &point);
167 return point;
168}
169
170} // namespace
171
172bool touchCalibrationCompute(const tsPoint_t *display, const tsPoint_t *screen, tsMatrix_t *matrix) {
173 if (display == nullptr || screen == nullptr || matrix == nullptr) {
174 return false;
175 }
176
177 matrix->Divider = ((screen[0].x - screen[2].x) * (screen[1].y - screen[2].y)) -
178 ((screen[1].x - screen[2].x) * (screen[0].y - screen[2].y));
179
180 if (matrix->Divider == 0) {
181 return false;
182 }
183
184 matrix->An = ((display[0].x - display[2].x) * (screen[1].y - screen[2].y)) -
185 ((display[1].x - display[2].x) * (screen[0].y - screen[2].y));
186
187 matrix->Bn = ((screen[0].x - screen[2].x) * (display[1].x - display[2].x)) -
188 ((display[0].x - display[2].x) * (screen[1].x - screen[2].x));
189
190 matrix->Cn = (screen[2].x * display[1].x - screen[1].x * display[2].x) * screen[0].y +
191 (screen[0].x * display[2].x - screen[2].x * display[0].x) * screen[1].y +
192 (screen[1].x * display[0].x - screen[0].x * display[1].x) * screen[2].y;
193
194 matrix->Dn = ((display[0].y - display[2].y) * (screen[1].y - screen[2].y)) -
195 ((display[1].y - display[2].y) * (screen[0].y - screen[2].y));
196
197 matrix->En = ((screen[0].x - screen[2].x) * (display[1].y - display[2].y)) -
198 ((display[0].y - display[2].y) * (screen[1].x - screen[2].x));
199
200 matrix->Fn = (screen[2].x * display[1].y - screen[1].x * display[2].y) * screen[0].y +
201 (screen[0].x * display[2].y - screen[2].x * display[0].y) * screen[1].y +
202 (screen[1].x * display[0].y - screen[0].x * display[1].y) * screen[2].y;
203
204 return true;
205}
206
207bool touchCalibrationApply(const tsMatrix_t *matrix, const tsPoint_t *raw, tsPoint_t *display) {
208 if (matrix == nullptr || raw == nullptr || display == nullptr || matrix->Divider == 0) {
209 return false;
210 }
211
212 display->x = ((matrix->An * raw->x) + (matrix->Bn * raw->y) + matrix->Cn) / matrix->Divider;
213 display->y = ((matrix->Dn * raw->x) + (matrix->En * raw->y) + matrix->Fn) / matrix->Divider;
214 return true;
215}
216
217bool touchCalibrationRun(Adafruit_RA8875 *display, UART_HandleTypeDef *uart, tsMatrix_t *matrix) {
218 if (display == nullptr || uart == nullptr || matrix == nullptr) {
219 return false;
220 }
221
222 const int32_t w = display->width();
223 const int32_t h = display->height();
224
225 // Three targets: 10% over and 10% down, 50% over and 90% down, 90% over and 50% down
226 tsPoint_t displayPoints[3] = { { w / 10, h / 10 }, { w / 2, h - h / 10 }, { w - w / 10, h / 2 } };
227 tsPoint_t touchPoints[3];
228
229 for (int attempt = 1; attempt <= CALIBRATION_ATTEMPTS; attempt++) {
230 printf("Touch calibration, attempt %d of %d\r\n", attempt, CALIBRATION_ATTEMPTS);
231
232 for (int i = 0; i < 3; i++) {
233 touchPoints[i] = renderCalibrationScreen(display, (uint16_t) displayPoints[i].x,
234 (uint16_t) displayPoints[i].y, TARGET_RADIUS);
235 printf(" point %d: display %ld,%ld raw %ld,%ld\r\n", i + 1, (long) displayPoints[i].x,
236 (long) displayPoints[i].y, (long) touchPoints[i].x, (long) touchPoints[i].y);
237 if (i < 2 && !waitForSpaceOrEsc(uart)) {
238 // Esc: throw this touch away and show the same target again
239 printf(" point %d discarded, touch it again\r\n", i + 1);
240 i--;
241 }
242 }
243
244 display->fillScreen(RA8875_WHITE);
245
246 if (touchCalibrationCompute(displayPoints, touchPoints, matrix)) {
247 return true;
248 }
249 printf(" the three touches lie on one line, calibration is not usable\r\n");
250 }
251 return false;
252}
253
254#endif /* TOUCH_CALIBRATION_ENABLE */
#define RA8875_GREEN
Green Color.
#define RA8875_WHITE
White Color.
#define RA8875_RED
Red Color.
Class that stores state and functions for interacting with the RA8875 display controller.
boolean touchRead(uint16_t *x, uint16_t *y)
void fillScreen(uint16_t color)
void drawCircle(int16_t x, int16_t y, int16_t r, uint16_t color)
uint16_t height(void)
boolean touched(void)
uint16_t width(void)
: Header for main.c file. This file contains the common defines of the application.
int32_t y
int32_t x
int32_t Divider
volatile uint8_t uartRxChar
The one character the interrupt receive waits for (see main.c).
Definition main.c:87
bool touchCalibrationRun(Adafruit_RA8875 *display, UART_HandleTypeDef *uart, tsMatrix_t *matrix)
Runs the interactive calibration: draws three targets one at a time, waits for the user to touch each...
volatile uint8_t uartKeyPressed
Set by HAL_UART_RxCpltCallback() in main.c: 0 = none yet, otherwise ' ' or 0x1B (Esc).
Definition main.c:89
bool touchCalibrationApply(const tsMatrix_t *matrix, const tsPoint_t *raw, tsPoint_t *display)
Converts one raw touch reading into a display pixel position.
bool touchCalibrationCompute(const tsPoint_t *display, const tsPoint_t *screen, tsMatrix_t *matrix)
Calculates the calibration matrix from three known display points and the raw touch readings taken at...
Three-point touch-screen calibration for the RA8875's resistive panel.