forked from nodemcu/nodemcu-firmware
-
Notifications
You must be signed in to change notification settings - Fork 0
/
xpt2046.c
217 lines (178 loc) · 6.59 KB
/
xpt2046.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// Module for xpt2046
// by Starofall, F.J. Exoo
// used source code from:
// - https://github.com/spapadim/XPT2046/
// - https://github.com/PaulStoffregen/XPT2046_Touchscreen/
#include "module.h"
#include "lauxlib.h"
#include "platform.h"
// Hardware specific values
static const uint16_t CAL_MARGIN = 0; // Set to 0: up to the application
static const uint8_t CTRL_LO_DFR = 0b0011;
static const uint8_t CTRL_LO_SER = 0b0100;
static const uint8_t CTRL_HI_X = 0b1001 << 4;
static const uint8_t CTRL_HI_Y = 0b1101 << 4;
static const uint16_t ADC_MAX = 0x0fff; // 12 bits
// Runtime variables
static uint16_t _width, _height;
static uint8_t _cs_pin, _irq_pin;
static int32_t _cal_dx, _cal_dy, _cal_dvi, _cal_dvj;
static uint16_t _cal_vi1, _cal_vj1;
// Average pair with least distance between each
static int16_t besttwoavg( int16_t x , int16_t y , int16_t z ) {
int16_t da, db, dc;
int16_t reta = 0;
if ( x > y ) da = x - y; else da = y - x;
if ( x > z ) db = x - z; else db = z - x;
if ( z > y ) dc = z - y; else dc = y - z;
if ( da <= db && da <= dc ) reta = (x + y) >> 1;
else if ( db <= da && db <= dc ) reta = (x + z) >> 1;
else reta = (y + z) >> 1;
return reta;
}
// Checks if the irq_pin is down
static int isTouching() {
return (platform_gpio_read(_irq_pin) == 0);
}
// transfer 16 bits from the touch display - returns the recived uint16_t
static uint16_t transfer16(uint16_t _data) {
union { uint16_t val; struct { uint8_t lsb; uint8_t msb; }; } t;
t.val = _data;
t.msb = platform_spi_send_recv(1, 8, t.msb);
t.lsb = platform_spi_send_recv(1, 8, t.lsb);
return t.val;
}
// reads the value from the touch panel
static uint16_t _readLoop(uint8_t ctrl, uint8_t max_samples) {
uint16_t prev = 0xffff, cur = 0xffff;
uint8_t i = 0;
do {
prev = cur;
cur = platform_spi_send_recv(1, 8 , 0);
cur = (cur << 4) | (platform_spi_send_recv(1, 8 , ctrl) >> 4); // 16 clocks -> 12-bits (zero-padded at end)
} while ((prev != cur) && (++i < max_samples));
return cur;
}
// Returns the raw position information
static void getRaw(uint16_t *vi, uint16_t *vj) {
// Implementation based on TI Technical Note http://www.ti.com/lit/an/sbaa036/sbaa036.pdf
// Disable interrupt: reading position generates false interrupt
ETS_GPIO_INTR_DISABLE();
platform_gpio_write(_cs_pin, PLATFORM_GPIO_LOW);
platform_spi_send_recv(1, 8 , CTRL_HI_X | CTRL_LO_DFR); // Send first control int
*vi = _readLoop(CTRL_HI_X | CTRL_LO_DFR, 255);
*vj = _readLoop(CTRL_HI_Y | CTRL_LO_DFR, 255);
// Turn off ADC by issuing one more read (throwaway)
// This needs to be done, because PD=0b11 (needed for MODE_DFR) will disable PENIRQ
platform_spi_send_recv(1, 8 , 0); // Maintain 16-clocks/conversion; _readLoop always ends after issuing a control int
platform_spi_send_recv(1, 8 , CTRL_HI_Y | CTRL_LO_SER);
transfer16(0); // Flush last read, just to be sure
platform_gpio_write(_cs_pin, PLATFORM_GPIO_HIGH);
// Clear interrupt status
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(pin_num[_irq_pin]));
// Enable interrupt again
ETS_GPIO_INTR_ENABLE();
}
// sets the calibration of the display
static void setCalibration (uint16_t vi1, uint16_t vj1, uint16_t vi2, uint16_t vj2) {
_cal_dx = _width - 2*CAL_MARGIN;
_cal_dy = _height - 2*CAL_MARGIN;
_cal_vi1 = (int32_t)vi1;
_cal_vj1 = (int32_t)vj1;
_cal_dvi = (int32_t)vi2 - vi1;
_cal_dvj = (int32_t)vj2 - vj1;
}
// returns the position on the screen by also applying the calibration
static void getPosition (uint16_t *x, uint16_t *y) {
if (isTouching() == 0) {
*x = *y = 0xffff;
return;
}
uint16_t vi, vj;
getRaw(&vi, &vj);
// Map to (un-rotated) display coordinates
*x = (uint16_t)(_cal_dx * (vj - _cal_vj1) / _cal_dvj + CAL_MARGIN);
if (*x > 0x7fff) *x = 0;
*y = (uint16_t)(_cal_dy * (vi - _cal_vi1) / _cal_dvi + CAL_MARGIN);
if (*y > 0x7fff) *y = 0;
}
// Lua: xpt2046.init(cspin, irqpin, height, width)
static int xpt2046_init( lua_State* L ) {
_cs_pin = luaL_checkinteger( L, 1 );
_irq_pin = luaL_checkinteger( L, 2 );
_height = luaL_checkinteger( L, 3 );
_width = luaL_checkinteger( L, 4 );
// set pins correct
platform_gpio_mode(_cs_pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT );
setCalibration(
/*vi1=*/((int32_t)CAL_MARGIN) * ADC_MAX / _width,
/*vj1=*/((int32_t)CAL_MARGIN) * ADC_MAX / _height,
/*vi2=*/((int32_t)_width - CAL_MARGIN) * ADC_MAX / _width,
/*vj2=*/((int32_t)_height - CAL_MARGIN) * ADC_MAX / _height
);
// assume spi was inited before with a clockDiv of >=16
// as higher spi clock speed produced inaccurate results
// do first powerdown
platform_gpio_write(_cs_pin, PLATFORM_GPIO_LOW);
// Issue a throw-away read, with power-down enabled (PD{1,0} == 0b00)
// Otherwise, ADC is disabled
platform_spi_send_recv(1, 8, CTRL_HI_Y | CTRL_LO_SER);
transfer16(0); // Flush, just to be sure
platform_gpio_write(_cs_pin, PLATFORM_GPIO_HIGH);
return 0;
}
// Lua: xpt2046.isTouched()
static int xpt2046_isTouched( lua_State* L ) {
lua_pushboolean( L, isTouching());
return 1;
}
// Lua: xpt2046.setCalibration(a,b,c,d)
static int xpt2046_setCalibration( lua_State* L ) {
int32_t a = luaL_checkinteger( L, 1 );
int32_t b = luaL_checkinteger( L, 2 );
int32_t c = luaL_checkinteger( L, 3 );
int32_t d = luaL_checkinteger( L, 4 );
setCalibration(a,b,c,d);
return 0;
}
// Lua: xpt2046.xpt2046_getRaw()
static int xpt2046_getRaw( lua_State* L ) {
uint16_t x, y;
getRaw(&x, &y);
lua_pushinteger( L, x);
lua_pushinteger( L, y);
return 2;
}
// Lua: xpt2046.xpt2046_getPosition()
static int xpt2046_getPosition( lua_State* L ) {
uint16_t x, y;
getPosition(&x, &y);
lua_pushinteger( L, x);
lua_pushinteger( L, y);
return 2;
}
// Lua: xpt2046.xpt2046_getPositionAvg()
static int xpt2046_getPositionAvg( lua_State* L ) {
// Run three times
uint16_t x1, y1, x2, y2, x3, y3;
getPosition(&x1, &y1);
getPosition(&x2, &y2);
getPosition(&x3, &y3);
// Average the two best results
int16_t x = besttwoavg(x1,x2,x3);
int16_t y = besttwoavg(y1,y2,y3);
lua_pushinteger( L, x);
lua_pushinteger( L, y);
return 2;
}
// Module function map
static const LUA_REG_TYPE xpt2046_map[] = {
{ LSTRKEY( "isTouched"), LFUNCVAL(xpt2046_isTouched) },
{ LSTRKEY( "getRaw" ), LFUNCVAL(xpt2046_getRaw) },
{ LSTRKEY( "getPosition"), LFUNCVAL(xpt2046_getPosition)},
{ LSTRKEY( "getPositionAvg"), LFUNCVAL(xpt2046_getPositionAvg)},
{ LSTRKEY( "setCalibration"), LFUNCVAL(xpt2046_setCalibration)},
{ LSTRKEY( "init" ), LFUNCVAL(xpt2046_init) },
{ LNILKEY, LNILVAL }
};
NODEMCU_MODULE(XPT2046, "xpt2046", xpt2046_map, NULL);