Landtiger LPC1768 C BigLib 1
A self made, custom C library for the LandTiger board.
 
Loading...
Searching...
No Matches
touch.c
Go to the documentation of this file.
1#include "touch.h"
2#include "glcd.h"
3#include "power.h"
4
5#include <LPC17xx.h>
6#include <stdbool.h>
7#include <stdlib.h>
8
9#define TP_CS(a) ((a) ? (LPC_GPIO0->FIOSET = (1 << 6)) : (LPC_GPIO0->FIOCLR = (1 << 6)))
10#define TP_INT_IN (LPC_GPIO2->FIOPIN & (1 << 13))
11
12// Bit-frequency = PCLK / CPSR
13#define SPI_SPEED_4MHz 18 /* 4MHz */
14#define SPI_SPEED_2MHz 36 /* 2MHz */
15#define SPI_SPEED_1MHz 72 /* 1MHz */
16#define SPI_SPEED_500kHz 144 /* 500kHz */
17#define SPI_SPEED_400kHz 180 /* 400kHz */
18
19#define THRESHOLD 2
20
21// Bits in the SSP Status Register for checking the status of the SSP interface.
22// Refer to User Manual @ Table 373 for more info
23#define SSP_SR_RECV_FIFO_NOT_EMPTY 2 // 0 if the receive FIFO is empty, 1 if not
24#define SSP_SR_BUSY 4 // 0 if SSP is idle, 1 if it's sending/receiving data and/or SEND FIFO is not empty
25
26// Waits till the SSP interface is not busy anymore (& is 0)
27#define WAIT_IF_BUSY while (LPC_SSP1->SR & (1 << SSP_SR_BUSY))
28
29// Touch Panel registers for X, Y coordinates
30#define CHX 0x90
31#define CHY 0xd0
32
33// Math functions
34#define ABS(x) ((x) < 0 ? -(x) : (x))
35
36// TYPES
37
38typedef struct
39{
40 long double a_n, b_n, c_n, d_n, e_n, f_n, divider;
42
43// STATE VARIABLES
44
47
48_PRIVATE bool initialized = false, calibratated = false;
49
50// DELAY PRIVATE FUNCTIONS
51
54{
55 for (u32 i = 0; i < count; i++)
56 {
57 u8 us = 12;
58 while (us--)
59 ;
60 }
61}
62
63// SPI PRIVATE FUNCTIONS
64
66_PRIVATE inline void spi_set_speed(u8 speed)
67{
68 speed &= 0xFE; // Make sure that speed is even.
69 if (speed < 2)
70 speed = 2;
71
72 // Setting clock prescale register to the desired speed.
73 LPC_SSP1->CPSR = speed;
74}
75
80{
82 LPC_SSP1->DR = cmd; // Write command to the SSP1 data register (R/W)
84
85 // Controller should now respond with a byte. It will put it in the RX FIFO.
86 while (!(LPC_SSP1->SR & (1 << SSP_SR_RECV_FIFO_NOT_EMPTY)))
87 ; // Wait until RX FIFO is not empty
88
89 // If here, FIFO contains something, reading it
90 const u8 byte_returned = LPC_SSP1->DR;
91 return byte_returned;
92}
93
95_PRIVATE inline void spi_init(void)
96{
97 // Enabling power to SSP1 interface (Synchronous Serial Port), which provides SPI capability.
99
100 // P0.7 SCK, P0.8 MISO, P0.9 MOSI are SSP pins.
101 LPC_PINCON->PINSEL0 &= ~((3UL << 14) | (3UL << 16) | (3UL << 18)); // P0.7, P0.8, P0.9 cleared
102 LPC_PINCON->PINSEL0 |= (2UL << 14) | (2UL << 16) | (2UL << 18); // P0.7 SCK1, P0.8 MISO1, P0.9 MOSI1
103
104 // Setting clock speed for SSP1. 01 = CCLK
105 LPC_SC->PCLKSEL0 &= ~(3 << 20); /* PCLKSP0 = CCLK/4 (18MHz) */
106 LPC_SC->PCLKSEL0 |= (1 << 20); /* PCLKSP0 = CCLK (72MHz) */
107
108 // Register Table: User Manual @ Table 370/371
109 // CPOL = Clock Polarity / CPHA = Clock Phase
110 LPC_SSP1->CR0 = 0x0007; // 8Bit, CPOL=0, CPHA=0 (Control Register 0, clock rate, data size...)
111 LPC_SSP1->CR1 = 0x0002; // SSP1 enable, master (Control Register 1, master/slave...)
112
113 // SPI communication speed needs to be known and fixed.
115
116 // Wait, maybe touch panel is busy
118
119 // Consume eventual data in RX FIFO, so that we can start fresh.
120 while (LPC_SSP1->SR & (1 << SSP_SR_RECV_FIFO_NOT_EMPTY))
121 LPC_SSP1->DR;
122}
123
124// TOUCH CONTROLLER PRIVATE FUNCTIONS
125
129{
130 u16 buf, tmp;
131
132 tmp = spi_send_command(0x00);
133 buf = tmp << 8; // Read first 8 bits of the ADC result
134
135 delay_us(1); // Wait for the next byte to be ready
136
137 tmp = spi_send_command(0x00);
138 buf |= tmp; // Read the last 8 bytes of the ADC result
139
140 // ADC result is a 12-bit value transmitted as a 16 bit with 4 bits of padding.
141 // Need to discard those extra LSBs and return the 12-bit value (& 0xFFF).
142 return (buf >> 4) & 0xFFF;
143}
144
148{
149 TP_CS(0);
150 delay_us(1);
152 delay_us(1);
153
154 const u16 adc = ads7843_read_adc();
155 TP_CS(1);
156 return adc;
157}
158
162{
163 TP_CS(0);
164 delay_us(1);
166 delay_us(1);
167
168 const u16 adc = ads7843_read_adc();
169 TP_CS(1);
170 return adc;
171}
172
173// PRIVATE TOUCH FUNCTIONS
174
176_PRIVATE void get_xy(u16 *out_x, u16 *out_y)
177{
178 if (!out_x || !out_y)
179 return;
180
181 const u16 adx = ads7843_read_x();
182 delay_us(1);
183 const u16 ady = ads7843_read_y();
184 *out_x = adx;
185 *out_y = ady;
186}
187
189{
190 if (!out_tp_coords)
191 return false;
192
193 u16 tp_x, tp_y;
194 u16 buffer[2][9] = {{0}, {0}};
195
196 u8 count = 0;
197 while (!TP_INT_IN && count < 9)
198 {
199 get_xy(&tp_x, &tp_y);
200 buffer[0][count] = tp_x;
201 buffer[1][count] = tp_y;
202 count++;
203 }
204
205 if (count != 9)
206 return false;
207
208 int m0, m1, m2, temp[3];
209 temp[0] = (buffer[0][0] + buffer[0][1] + buffer[0][2]) / 3;
210 temp[1] = (buffer[0][3] + buffer[0][4] + buffer[0][5]) / 3;
211 temp[2] = (buffer[0][6] + buffer[0][7] + buffer[0][8]) / 3;
212 m0 = ABS(temp[0] - temp[1]);
213 m1 = ABS(temp[1] - temp[2]);
214 m2 = ABS(temp[2] - temp[0]);
215 if (m0 > THRESHOLD && m1 > THRESHOLD && m2 > THRESHOLD)
216 return false;
217
218 if (m0 < m1)
219 {
220 if (m2 < m0)
221 out_tp_coords->x = (temp[0] + temp[2]) / 2;
222 else
223 out_tp_coords->x = (temp[0] + temp[1]) / 2;
224 }
225 else if (m2 < m1)
226 out_tp_coords->x = (temp[0] + temp[2]) / 2;
227 else
228 out_tp_coords->x = (temp[1] + temp[2]) / 2;
229
230 temp[0] = (buffer[1][0] + buffer[1][1] + buffer[1][2]) / 3;
231 temp[1] = (buffer[1][3] + buffer[1][4] + buffer[1][5]) / 3;
232 temp[2] = (buffer[1][6] + buffer[1][7] + buffer[1][8]) / 3;
233
234 m0 = ABS(temp[0] - temp[1]);
235 m1 = ABS(temp[1] - temp[2]);
236 m2 = ABS(temp[2] - temp[0]);
237 if (m0 > THRESHOLD && m1 > THRESHOLD && m2 > THRESHOLD)
238 return false;
239
240 if (m0 < m1)
241 {
242 if (m2 < m0)
243 out_tp_coords->y = (temp[0] + temp[2]) / 2;
244 else
245 out_tp_coords->y = (temp[0] + temp[1]) / 2;
246 }
247 else if (m2 < m1)
248 out_tp_coords->y = (temp[0] + temp[2]) / 2;
249 else
250 out_tp_coords->y = (temp[1] + temp[2]) / 2;
251
252 return true;
253}
254
255// PRIVATE CALIBRATION FUNCTIONS
256
258
260{
261 // clang-format off
263 LCD_RECT2(x - 15, y - 15, {
264 .width = 30,
265 .height = 30,
266 .edge_color = LCD_COL_WHITE,
267 .fill_color = LCD_COL_NONE,
268 }),
269 LCD_LINE({
270 .from = {x, y - 7},
271 .to = {x, y + 7},
272 .color = LCD_COL_WHITE,
273 }),
274 LCD_LINE({
275 .from = {x - 7, y},
276 .to = {x + 7, y},
277 .color = LCD_COL_WHITE
278 }),
279 });
280
281 LCD_RMRender();
282 // clang-format on
283}
284
286{
288}
289
290_PRIVATE bool calc_calibration_matrix(TP_CalibrationMatrix *out_matrix, const LCD_Coordinate *const lcd_3points,
291 const TP_Coordinate *const tp_3points)
292{
293 if (!lcd_3points || !tp_3points || !out_matrix)
294 return false;
295
296 long double divider = ((tp_3points[0].x - tp_3points[2].x) * (tp_3points[1].y - tp_3points[2].y)) -
297 ((tp_3points[1].x - tp_3points[2].x) * (tp_3points[0].y - tp_3points[2].y));
298 if (divider == 0)
299 return false;
300
301 out_matrix->divider = divider;
302 out_matrix->a_n = ((lcd_3points[0].x - lcd_3points[2].x) * (tp_3points[1].y - tp_3points[2].y)) -
303 ((lcd_3points[1].x - lcd_3points[2].x) * (tp_3points[0].y - tp_3points[2].y));
304 out_matrix->b_n = ((tp_3points[0].x - tp_3points[2].x) * (lcd_3points[1].x - lcd_3points[2].x)) -
305 ((lcd_3points[0].x - lcd_3points[2].x) * (tp_3points[1].x - tp_3points[2].x));
306 out_matrix->c_n = (tp_3points[2].x * lcd_3points[1].x - tp_3points[1].x * lcd_3points[2].x) * tp_3points[0].y +
307 (tp_3points[0].x * lcd_3points[2].x - tp_3points[2].x * lcd_3points[0].x) * tp_3points[1].y +
308 (tp_3points[1].x * lcd_3points[0].x - tp_3points[0].x * lcd_3points[1].x) * tp_3points[2].y;
309 out_matrix->d_n = ((lcd_3points[0].y - lcd_3points[2].y) * (tp_3points[1].y - tp_3points[2].y)) -
310 ((lcd_3points[1].y - lcd_3points[2].y) * (tp_3points[0].y - tp_3points[2].y));
311 out_matrix->e_n = ((tp_3points[0].x - tp_3points[2].x) * (lcd_3points[1].y - lcd_3points[2].y)) -
312 ((lcd_3points[0].y - lcd_3points[2].y) * (tp_3points[1].x - tp_3points[2].x));
313 out_matrix->f_n = (tp_3points[2].x * lcd_3points[1].y - tp_3points[1].x * lcd_3points[2].y) * tp_3points[0].y +
314 (tp_3points[0].x * lcd_3points[2].y - tp_3points[2].x * lcd_3points[0].y) * tp_3points[1].y +
315 (tp_3points[1].x * lcd_3points[0].y - tp_3points[0].x * lcd_3points[1].y) * tp_3points[2].y;
316
317 return true;
318}
319
320// Calibration should not be an operation that the user should do,
321// hence it's been made private and called from the init function.
322bool calibrate(void)
323{
324 calibratated = false;
325
326 const LCD_Coordinate lcd_crosses[3] = {{45, 45}, {45, 270}, {190, 190}};
327 TP_Coordinate tp_crosses[3] = {{0}, {0}, {0}};
328
329 if (!LCD_IsInitialized())
330 return false;
331
333
334 LCD_ObjID text;
335 // clang-format off
336 LCD_OBJECT(&text, {
337 LCD_TEXT2(5, 10, {
338 .text = "Touch crosshairs to calibrate",
339 .font = LCD_DEF_FONT_SYSTEM,
340 .text_color = LCD_COL_WHITE,
341 .bg_color = LCD_COL_NONE,
342 }),
343 });
344 // clang-format on
345
346 LCD_RMRender();
347
348 for (u8 i = 0; i < 3; i++)
349 {
350#ifdef SIMULATOR
351 delay_us(1000 * 50);
352#else
353 delay_us(1000 * 500);
354#endif
355
356 draw_calibration_cross(lcd_crosses[i].x, lcd_crosses[i].y);
357 const TP_Coordinate *calib_coords = TP_WaitForTouch();
358 tp_crosses[i].x = calib_coords->x;
359 tp_crosses[i].y = calib_coords->y;
361 }
362
363 if (!calc_calibration_matrix(&current_calib_matrix, lcd_crosses, tp_crosses))
364 return false;
365
366 LCD_RMClear();
367 calibratated = true;
368 return calibratated;
369}
370
371// PUBLIC FUNCTIONS
372
373void TP_Init(bool skip_calibration)
374{
375 LPC_GPIO0->FIODIR |= (1 << 6); // P0.6 CS is output
376 LPC_GPIO2->FIODIR |= (0 << 13); // P2.13 TP_INT is input
377 TP_CS(1);
378
379 spi_init(); // Initialize SPI interface for touch panel
380 initialized = true;
381
382 if (skip_calibration)
383 return;
384
385 TP_Calibrate();
386 // assert(calibratated && initialized); // Should never fail.
387}
388
390{
391 return initialized;
392}
393
395{
396 return calibratated;
397}
398
399void TP_Calibrate(void)
400{
401 if (!initialized)
402 return;
403
404 while (!calibrate())
405 ;
406}
407
409{
410 if (!initialized)
411 return NULL;
412
413 static TP_Coordinate tp_coords;
414 while (!poll_touch(&tp_coords))
415 ;
416 return &tp_coords;
417}
418
420{
421 if (!initialized || !tp_point)
422 return NULL;
423
424 static LCD_Coordinate lcd_point;
426 if (matrix.divider == 0 || !tp_point)
427 return NULL;
428
429 lcd_point.x = ((matrix.a_n * tp_point->x) + (matrix.b_n * tp_point->y) + matrix.c_n) / matrix.divider;
430 lcd_point.y = ((matrix.d_n * tp_point->x) + (matrix.e_n * tp_point->y) + matrix.f_n) / matrix.divider;
431 return &lcd_point;
432}
433
434// TOUCH BUTTONS
435
437{
438 const LCD_Obj obj = {
439 .comps_size = 1,
440 .comps =
441 (LCD_Component[]){
443 .type = LCD_COMP_BUTTON,
444 .pos = pos,
445 .object.button = &button,
446 },
447 },
448 };
449
450 LCD_BBox bbox;
451 if (LCD_CalcBBoxForObject(&obj, &bbox) != LCD_ERR_OK)
452 {
453 return (TP_ButtonArea){0};
454 }
455
456 return (TP_ButtonArea){
457 .pos = pos,
458 .width = abs(bbox.bottom_right.x - bbox.top_left.x),
459 .height = abs(bbox.bottom_right.y - bbox.top_left.y),
460 };
461}
462
464{
465 if (!initialized)
466 return;
467
468 TP_Coordinate *touch_point;
469 do
470 {
471 touch_point = (TP_Coordinate *)TP_WaitForTouch();
472 } while (!TP_HasButtonBeenPressed(button, touch_point));
473}
474
475bool TP_HasButtonBeenPressed(TP_ButtonArea button, const TP_Coordinate *const touch_point)
476{
477 if (!touch_point)
478 return false;
479
480 // We should map the coordinates to the LCD coordinates, because the buttons pos
481 // is in LCD coordinates!
482 const LCD_Coordinate *touch_lcd_pos = TP_GetLCDCoordinateFor(touch_point);
483 if (!touch_lcd_pos)
484 return false;
485
486 const bool between_w = IS_BETWEEN_EQ(touch_lcd_pos->x, button.pos.x + 1, button.pos.x + button.width - 1);
487 const bool between_h = IS_BETWEEN_EQ(touch_lcd_pos->y, button.pos.y + 1, button.pos.y + button.height - 1);
488 return between_w && between_h;
489}
bool LCD_IsInitialized(void)
Checks if the LCD has been initialized.
Definition glcd.c:225
LCD_Error LCD_CalcBBoxForObject(const LCD_Obj *const obj, LCD_BBox *out_bbox)
Returns the bounding box of a temporary object.
Definition glcd.c:621
LCD_Error LCD_SetBackgroundColor(LCD_Color color, bool redraw_objects)
Sets the background color of the screen.
Definition glcd.c:264
LCD_Error LCD_RMRemove(LCD_ObjID id, bool redraw_underneath)
Removes an object from the render list by its ID. It also deallocates the memory used by that object ...
Definition glcd.c:362
LCD_Error LCD_RMClear(void)
Removes all visible and non-visible objects from the screen.
Definition glcd.c:395
LCD_Error LCD_RMRender(void)
Manually triggers an update of the screen. Useful when you want to add multiple objects at once,...
Definition glcd.c:421
@ LCD_ERR_OK
No error occurred.
Definition glcd_errors.h:8
#define LCD_LINE(...)
Definition glcd_macros.h:91
#define LCD_OBJECT(id,...)
Definition glcd_macros.h:44
#define LCD_TEXT2(x, y,...)
#define LCD_RECT2(x, y,...)
@ LCD_DEF_FONT_SYSTEM
Definition glcd_types.h:70
i32 LCD_ObjID
Definition glcd_types.h:195
@ LCD_COL_WHITE
Definition glcd_types.h:23
@ LCD_COL_NONE
Definition glcd_types.h:33
@ LCD_COL_BLACK
Definition glcd_types.h:24
@ LCD_COMP_BUTTON
Definition glcd_types.h:163
void POWER_TurnOnPeripheral(u8 bit)
Turns on a peripheral.
Definition power.c:25
@ POW_PCSSP1
Definition power_types.h:16
LCD_Coordinate top_left
Definition glcd_types.h:62
LCD_Coordinate bottom_right
Definition glcd_types.h:62
Used to store a drawable component of any type.
Definition glcd_types.h:168
Represents a generic object, made up of 1 or more basic components, that are rendered on the screen.
Definition glcd_types.h:189
u8 comps_size
Definition glcd_types.h:192
LCD_Coordinate pos
Definition touch.h:16
u16 height
Definition touch.h:17
u16 width
Definition touch.h:17
long double b_n
Definition touch.c:40
long double a_n
Definition touch.c:40
long double e_n
Definition touch.c:40
long double f_n
Definition touch.c:40
long double d_n
Definition touch.c:40
long double c_n
Definition touch.c:40
long double divider
Definition touch.c:40
_PRIVATE u16 ads7843_read_y(void)
Reads the Y coordinate from the touch panel.
Definition touch.c:161
_PRIVATE TP_CalibrationMatrix current_calib_matrix
Current calibration matrix.
Definition touch.c:46
_PRIVATE u16 ads7843_read_x(void)
Reads the X coordinate from the touch panel.
Definition touch.c:147
bool TP_IsCalibrated(void)
Definition touch.c:394
#define TP_INT_IN
Definition touch.c:10
_PRIVATE void draw_calibration_cross(u16 x, u16 y)
Definition touch.c:259
#define THRESHOLD
Definition touch.c:19
_PRIVATE bool poll_touch(TP_Coordinate *out_tp_coords)
Definition touch.c:188
#define CHX
Definition touch.c:30
_PRIVATE u8 spi_send_command(u8 cmd)
Writes a command to the ADS7843 trough SSP1 and returns the received byte.
Definition touch.c:79
_PRIVATE bool initialized
Definition touch.c:48
_PRIVATE void spi_init(void)
Initializes the SPI interface for ADS7843.
Definition touch.c:95
#define WAIT_IF_BUSY
Definition touch.c:27
#define CHY
Definition touch.c:31
_PRIVATE bool calc_calibration_matrix(TP_CalibrationMatrix *out_matrix, const LCD_Coordinate *const lcd_3points, const TP_Coordinate *const tp_3points)
Definition touch.c:290
_PRIVATE LCD_ObjID calib_cross_obj_id
Definition touch.c:257
_PRIVATE void get_xy(u16 *out_x, u16 *out_y)
Returns the X, Y coordinates of the touched point.
Definition touch.c:176
#define SSP_SR_RECV_FIFO_NOT_EMPTY
Definition touch.c:23
#define TP_CS(a)
Definition touch.c:9
_PRIVATE void delay_us(u32 count)
Delays for the specified number of microseconds.
Definition touch.c:53
bool TP_IsInitialized(void)
Definition touch.c:389
_PRIVATE u16 ads7843_read_adc(void)
Communicate (via SPI) with ADS7843 and read a 12-bit ADC (Analog to Digital Converter) value.
Definition touch.c:128
#define ABS(x)
Definition touch.c:34
TP_ButtonArea TP_AssignButtonArea(LCD_Button button, LCD_Coordinate pos)
Assigns a button area to the given button.
Definition touch.c:436
_PRIVATE void spi_set_speed(u8 speed)
Sets SPI speed to the specified value.
Definition touch.c:66
_PRIVATE bool calibratated
Definition touch.c:48
const TP_Coordinate * TP_WaitForTouch(void)
Blocks until a touch event is detected.
Definition touch.c:408
bool TP_HasButtonBeenPressed(TP_ButtonArea button, const TP_Coordinate *const touch_point)
Checks if the given button area has been pressed during the touch event which occurred at the given t...
Definition touch.c:475
void TP_WaitForButtonPress(TP_ButtonArea button)
Blocks until the given button area is pressed.
Definition touch.c:463
bool calibrate(void)
Definition touch.c:322
void TP_Calibrate(void)
Blocks until calibration is completed successfully.
Definition touch.c:399
const LCD_Coordinate * TP_GetLCDCoordinateFor(const TP_Coordinate *const tp_point)
Converts the touch panel coordinates to LCD coordinates.
Definition touch.c:419
_PRIVATE void delete_calibration_cross(void)
Definition touch.c:285
#define SPI_SPEED_500kHz
Definition touch.c:16
void TP_Init(bool skip_calibration)
Initializes the touch panel.
Definition touch.c:373
uint8_t u8
Definition types.h:8
#define _PRIVATE
Definition types.h:37
#define IS_BETWEEN_EQ(value, low, hi)
Definition types.h:23
uint16_t u16
Definition types.h:7
uint32_t u32
Definition types.h:6