H753_CPP_SPI_Resistive_Touch_Screen_Controller 1.0
STM32H753 SPI resistive touchscreen (Adafruit 333 overlay + 5767 TSC2046 controller) driver and tutorial
Loading...
Searching...
No Matches
touchSelfTest.c
Go to the documentation of this file.
1
9#include "main.h"
10
11#ifdef TOUCH_SELFTEST_ENABLE
12
13#include "touchSelfTest.h"
14#include "entryPointCPP.hpp"
15#include "touchZones.h"
16#include "main.h"
17#include <math.h>
18#include <stdio.h>
19#include <string.h>
20
21extern SPI_HandleTypeDef hspi1;
22
23/* Control bytes for the four touch conversions (START, channel, 12-bit, differential). */
24static const uint8_t kControl[4] = { 0xD0, 0x90, 0xB0, 0xC0 };
25static const char *kControlName[4] = { "X", "Y", "Z1", "Z2" };
26
27/* Idle test: number of samples and spacing. */
28#define SELFTEST_IDLE_SAMPLES 25
29#define SELFTEST_IDLE_SPACING_MS 10
30
31/* Same touch criteria the main loop uses, so this test proves the loop won't fire when idle. */
32#define SELFTEST_PRESSURE_MIN 40.0f
33#define SELFTEST_PRESSURE_MAX 1500.0f
34
35static touchSelfTestReport_t report;
36
37static void resetReport(void) {
38 memset(&report, 0, sizeof(report));
39 report.passed = true;
40 report.failedTest = TOUCH_SELFTEST_NONE;
41}
42
43static bool fail(touchSelfTestId_t id, const char *fmt, ...);
44
45#include <stdarg.h>
46static bool fail(touchSelfTestId_t id, const char *fmt, ...) {
47 report.passed = false;
48 report.failedTest = id;
49 va_list args;
50 va_start(args, fmt);
51 vsnprintf(report.detail, sizeof(report.detail), fmt, args);
52 va_end(args);
53 return false;
54}
55
57static HAL_StatusTypeDef rawConversion(uint8_t cmd, uint8_t rx[3]) {
58 uint8_t tx[3] = { cmd, 0x00, 0x00 };
59 rx[0] = rx[1] = rx[2] = 0xAA; /* sentinel: shows if HAL never wrote rx */
60 HAL_GPIO_WritePin(TSC2046_CS_GPIO_Port, TSC2046_CS_Pin, GPIO_PIN_RESET);
61 HAL_StatusTypeDef st = HAL_SPI_TransmitReceive(&hspi1, tx, rx, 3, 100);
62 HAL_GPIO_WritePin(TSC2046_CS_GPIO_Port, TSC2046_CS_Pin, GPIO_PIN_SET);
63 return st;
64}
65
66/* Test 1: a raw HAL SPI transaction completes. */
67static bool testSpiTransfer(void) {
68 uint8_t rx[3];
69 HAL_StatusTypeDef st = rawConversion(kControl[0], rx);
70 if (st != HAL_OK) {
71 return fail(TOUCH_SELFTEST_SPI_TRANSFER,
72 "HAL SPI transfer failed (status %d, SPI error 0x%lX): check SPI1 setup and wiring",
73 (int)st, (unsigned long)HAL_SPI_GetError(&hspi1));
74 }
75 return true;
76}
77
78/* Test 2: response framing is what a live TSC2046 returns; channels are not all identical. */
79static bool testFraming(void) {
80 uint16_t value[4];
81 bool allIdentical = true;
82
83 for (int i = 0; i < 4; i++) {
84 uint8_t rx[3];
85 if (rawConversion(kControl[i], rx) != HAL_OK) {
86 return fail(TOUCH_SELFTEST_FRAMING, "SPI transfer failed reading %s", kControlName[i]);
87 }
88 /* Bit 7 of the first data byte is a leading null bit (always 0); the low 3 bits
89 * of the last byte are trailing padding (always 0). A stuck-high MISO breaks this. */
90 if ((rx[1] & 0x80) != 0 || (rx[2] & 0x07) != 0) {
91 return fail(TOUCH_SELFTEST_FRAMING,
92 "%s response %02X %02X %02X is not a valid frame: MISO stuck or floating, check VCC and wiring",
93 kControlName[i], rx[0], rx[1], rx[2]);
94 }
95 value[i] = (uint16_t)((((uint16_t)rx[1] << 8) | rx[2]) >> 3) & 0xFFF;
96 if (i > 0 && value[i] != value[0]) {
97 allIdentical = false;
98 }
99 }
100 if (allIdentical) {
101 return fail(TOUCH_SELFTEST_FRAMING,
102 "all four channels returned the same value (%u): MISO stuck low, chip not answering", value[0]);
103 }
104 return true;
105}
106
107/* NOTE: there is intentionally no temperature-sensor test here. The TSC2046's on-die
108 * temperature reading (TSC2046::readTemperatureC()) is not accurate enough to give a
109 * pass/fail limit -- it relies on a diode-difference measurement and a datasheet
110 * approximation that has not been verified against a real thermometer on this hardware --
111 * so a limit could fail a healthy board. "Is the chip answering" is covered by the
112 * framing test above. See the file header in touchSelfTest.h. */
113
114/* Test 3: with the panel untouched, no sample may look like a touch. */
115static bool testIdle(void) {
116 uint32_t phantom = 0;
117
118 for (int i = 0; i < SELFTEST_IDLE_SAMPLES; i++) {
119 int16_t x = 0, y = 0;
120 float pressure = 0.0f;
121 if (tsc2046GetPoint(&x, &y, &pressure)) {
122 bool looksTouched = isfinite(pressure) && pressure >= SELFTEST_PRESSURE_MIN &&
123 pressure < SELFTEST_PRESSURE_MAX && touchZoneFromPoint(x, y) != TOUCH_ZONE_NONE;
124 if (looksTouched) {
125 phantom++;
126 }
127 }
128 HAL_Delay(SELFTEST_IDLE_SPACING_MS);
129 }
130 report.idlePhantomTouches = phantom;
131 if (phantom > 0) {
132 return fail(TOUCH_SELFTEST_IDLE,
133 "%lu of %d idle samples looked like touches: hands on the panel, panel pressed or shorted, or flex cable mis-seated",
134 (unsigned long)phantom, SELFTEST_IDLE_SAMPLES);
135 }
136 return true;
137}
138
140 resetReport();
141
142 printf("Touch self-test (keep hands off the panel)...\r\n");
143
144 if (!tsc2046IsReady()) {
145 fail(TOUCH_SELFTEST_SPI_TRANSFER, "driver not ready: call initTSC2046() first");
146 printf(" FAIL: %s\r\n", report.detail);
147 return &report;
148 }
149
150 report.testsRun++;
151 if (!testSpiTransfer()) { printf(" [1/3] SPI transfer ........ FAIL: %s\r\n", report.detail); goto done; }
152 printf(" [1/3] SPI transfer ........ ok\r\n");
153
154 report.testsRun++;
155 if (!testFraming()) { printf(" [2/3] Response framing .... FAIL: %s\r\n", report.detail); goto done; }
156 printf(" [2/3] Response framing .... ok\r\n");
157
158 report.testsRun++;
159 if (!testIdle()) { printf(" [3/3] Idle, no touches .... FAIL: %s\r\n", report.detail); goto done; }
160 printf(" [3/3] Idle, no touches .... ok (%d samples)\r\n", SELFTEST_IDLE_SAMPLES);
161
162done:
163 printf("Self-test %s\r\n", report.passed ? "PASSED" : "FAILED (touch may not work; see above)");
164 return &report;
165}
166
168 return &report;
169}
170
171#endif /* TOUCH_SELFTEST_ENABLE */
C-callable entry points bridging CubeMX-generated main.c into the C++ TSC2046 driver.
bool tsc2046IsReady(void)
Reports whether initTSC2046() has run. Every other tsc2046* function below returns false without touc...
bool tsc2046GetPoint(int16_t *x, int16_t *y, float *pressure)
Reads the current touch position and pressure.
: Header for main.c file. This file contains the common defines of the application.
Result of the most recent touchSelfTestRunAll().
touchSelfTestId_t failedTest
First failing test, or NONE if passed.
char detail[96]
Human-readable reason for a failure.
bool passed
true if every test passed.
uint32_t idlePhantomTouches
Idle samples that looked like touches.
uint32_t testsRun
Tests that started running (pass or fail).
Startup self-test for the TSC2046 touch controller.
touchSelfTestId_t
Self-test identifiers.
const touchSelfTestReport_t * touchSelfTestRunAll(void)
Runs every self-test in order, stopping at the first failure, and prints progress and a PASS/FAIL ver...
const touchSelfTestReport_t * touchSelfTestGetReport(void)
Maps raw TSC2046 X/Y readings to the printed button zones on the panel overlay.
touchZone_t touchZoneFromPoint(int16_t x, int16_t y)
Classifies a raw touch position into a button zone.
Definition touchZones.c:35