EFM32 Gecko Software Documentation  efm32g-doc-5.1.2
touch.c
Go to the documentation of this file.
1 /**************************************************************************/
17 #include <stdlib.h>
18 #include <string.h>
19 #include "em_device.h"
20 #include "em_adc.h"
21 #include "em_gpio.h"
22 #include "em_cmu.h"
23 #include "touch.h"
24 #include "calibrate.h"
25 #include "bsp.h"
26 #ifndef TOUCH_WITHOUT_STORE
27 #include "i2cspm.h"
28 #include "eeprom.h"
29 #endif
30 
32 #define CALIBRATION_MAGIC_NUMBER 0xCA71B4A7
33 #define CALIBRATION_EEPROM_OFFSET 0x80
34 
36 /* ADC inputs connected to touchpad */
37 #define ADC_X adcSingleInpCh1
38 #define ADC_Y adcSingleInpCh4
40 /* touchpad PIOs */
41 #define TOUCH_X1_PORT gpioPortD
42 #define TOUCH_X1_PIN 5
43 #define TOUCH_X2_PORT gpioPortD
44 #define TOUCH_X2_PIN 4
45 #define TOUCH_Y1_PORT gpioPortD
46 #define TOUCH_Y1_PIN 3
47 #define TOUCH_Y2_PORT gpioPortD
48 #define TOUCH_Y2_PIN 1
51 typedef enum
52 { TOUCH_INIT,
53  TOUCH_CHECK_PRESS,
54  TOUCH_MEASURE_X,
55  TOUCH_MEASURE_Y } TOUCH_State_TypeDef;
56 
59 static volatile TOUCH_State_TypeDef touch_state = TOUCH_INIT;
61 volatile TOUCH_Pos_TypeDef newpos;
62 volatile TOUCH_Pos_TypeDef current_pos;
63 static TOUCH_Pos_TypeDef pos;
64 
65 static void (*upcall)(TOUCH_Pos_TypeDef *) = 0;
66 
67 uint32_t touch_ignore_move;
71 MATRIX calibrationMatrix = { 103800, 2048, -8184704, -384, 102144, -16424640, 287650 };
72 
74 #ifndef TOUCH_WITHOUT_STORE
75 /**************************************************************************/
91 static uint32_t touch_CountChecksum(uint32_t magic, uint32_t *data, uint32_t len)
92 {
93  unsigned long checksum = magic;
94 
95  while (len--)
96  {
97  if (checksum & 0x80000000)
98  {
99  checksum <<= 1;
100  checksum |= 1;
101  }
102  else checksum <<= 1;
103  checksum += *data;
104  data++;
105  }
106  return(checksum);
107 }
108 
110 static void touch_LoadCalibration(void)
111 {
113  uint32_t temp, checksum;
114  int count;
115  MATRIX new_matrix;
116 
117 #if !defined( BSP_STK )
119 #endif
120 
121  /* Initialize I2C driver, using standard rate. Devices on DK itself */
122  /* supports fast mode, but in case some slower devices are added on */
123  /* prototype board, we use standard mode. */
124  I2CSPM_Init(&i2cInit);
125  count = EEPROM_Read(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET, (uint8_t*) &temp, sizeof(temp));
126  count += EEPROM_Read(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET + 4, (uint8_t*) &new_matrix, sizeof(new_matrix));
127  if (count == sizeof(new_matrix) + 4)
128  {
129  if (temp == CALIBRATION_MAGIC_NUMBER)
130  {
131  checksum = touch_CountChecksum(temp, (uint32_t*) &new_matrix, sizeof(new_matrix) / 4);
132  count = EEPROM_Read(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET + 4 + sizeof(new_matrix), (uint8_t*) &temp, sizeof(temp));
133  if (temp == checksum)
134  { /* looks like calibration table is valid */
135  ADC_IntDisable(ADC0, ADC_IF_SINGLE); /* we need to disable ADC interrupt to avoid current_pos structure update for a while */
136  memcpy(&calibrationMatrix, &new_matrix, sizeof(calibrationMatrix));
138  }
139  }
140  }
141 }
142 
144 static void touch_StoreCalibration(void)
145 {
146  int count;
147  uint32_t temp = CALIBRATION_MAGIC_NUMBER, checksum;
148  checksum = touch_CountChecksum(CALIBRATION_MAGIC_NUMBER, (uint32_t*) &calibrationMatrix, sizeof(calibrationMatrix) / 4);
149  count = EEPROM_Write(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET, (uint8_t*) &temp, sizeof(temp));
150  count += EEPROM_Write(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET + 4, (uint8_t*) &calibrationMatrix, sizeof(calibrationMatrix));
151  count += EEPROM_Write(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET + 4 + sizeof(calibrationMatrix), (uint8_t*) &checksum, sizeof(checksum));
152 }
153 #endif
154 
156 /***************************************************************************/
164 {
165  POINT old_pos, new_pos;
166 
167  if (pos->pen)
168  {
169  old_pos.x = pos->adcx;
170  old_pos.y = pos->adcy;
171  if (getDisplayPoint(&new_pos, &old_pos, &calibrationMatrix) == OK)
172  {
173  if (new_pos.x >= 0) pos->x = new_pos.x;
174  else pos->x = 0;
175  if (new_pos.y >= 0) pos->y = new_pos.y;
176  else pos->y = 0;
177  }
178  }
179  else
180  {
181  pos->x = 0;
182  pos->y = 0;
183  }
184 }
185 
186 /***************************************************************************/
191 {
192  if (upcall)
193  upcall((TOUCH_Pos_TypeDef*) &current_pos);
194 }
195 
196 /***************************************************************************/
204 {
205  int result = 0;
206  int diff, a, b;
207  if (newpos.pen && !current_pos.pen) result = 1; /* pen down */
208  a = current_pos.x;
209  b = newpos.x;
210  diff = a - b;
211  if (abs(diff) > (int) touch_ignore_move) result = 1; /* move in X axis */
212  a = current_pos.y;
213  b = newpos.y;
214  diff = a - b;
215  if (abs(diff) > (int) touch_ignore_move) result = 1; /* move in Y axis */
216  return result;
217 }
218 
219 /***************************************************************************/
224 void ADC0_IRQHandler(void)
225 {
226  switch (touch_state)
227  {
228  case TOUCH_INIT: /* enter this state if touch panel is not pressed */
233  sInit.input = ADC_Y;
234  sInit.reference = adcRefVDD;
235  sInit.resolution = adcResOVS;
236  sInit.acqTime = adcAcqTime128; /* used to slow down */
238  {
239  touch_state = TOUCH_MEASURE_Y;
244  sInit.input = ADC_X;
245  sInit.acqTime = adcAcqTime16; /* pressed, so speed-up */
246  }
247  ADC_InitSingle(ADC0, &sInit);
248  break;
249  case TOUCH_CHECK_PRESS: /* checks if touch panel is still pressed */
251  {
252  touch_state = TOUCH_MEASURE_Y;
257  sInit.input = ADC_X;
258  sInit.acqTime = adcAcqTime16; /* pressed, so speed-up */
259  ADC_InitSingle(ADC0, &sInit);
260  current_pos.pen = newpos.pen;
261  TOUCH_RecalculatePosition(&newpos);
262  if (newpos.pen)
263  {
264  int call_upcall = TOUCH_StateChanged();
265  if (call_upcall)
266  {
267  current_pos.x = newpos.x;
268  current_pos.y = newpos.y;
269  }
270  current_pos.adcx = newpos.adcx;
271  current_pos.adcy = newpos.adcy;
272  current_pos.pen = 1;
273  if (call_upcall) TOUCH_CallUpcall();
274  }
275  newpos.pen = 1;
276  }
277  else
278  {
279  touch_state = TOUCH_INIT;
280  newpos.pen = 0;
281  current_pos.pen = 0;
283  }
284  break;
285  case TOUCH_MEASURE_Y: /* touch panel pressed, measure Y position */
286  newpos.adcy = (ADC_DataSingleGet(ADC0) + 31) >> 6; /* reduce ADC resolution to 10-bits */
287  GPIO_PinModeSet(TOUCH_Y1_PORT, TOUCH_Y1_PIN, gpioModePushPull, 0); /* to avoid overflow in calibration routines */
291  sInit.input = ADC_Y;
292  ADC_InitSingle(ADC0, &sInit);
293  touch_state = TOUCH_MEASURE_X;
294  break;
295  case TOUCH_MEASURE_X: /* touch panel pressed, measure X position */
296  newpos.adcx = (ADC_DataSingleGet(ADC0) + 31) >> 6;
301  sInit.input = ADC_Y;
302  ADC_InitSingle(ADC0, &sInit);
303  touch_state = TOUCH_CHECK_PRESS;
304  break;
305  default: touch_state = TOUCH_INIT;
306  }
309 }
310 
311 /***************************************************************************/
318 int TOUCH_IsBusy(void)
319 {
320  if( (touch_state == TOUCH_INIT) )
321  {
323  }
324 
325  if( (touch_state == TOUCH_CHECK_PRESS) )
326  {
327  return TOUCH_BUSY_CHECK;
328  }
329 
330  return(TOUCH_BUSY_SCAN);
331 }
332 
333 /***************************************************************************/
341 {
343 #ifndef TOUCH_WITHOUT_STORE
344  touch_LoadCalibration();
345 #endif
348  init.prescale = ADC_PrescaleCalc(config->frequency, 0);
349  touch_ignore_move = config->ignore;
350  init.ovsRateSel = config->oversampling;
351  ADC_Init(ADC0, &init);
353  sInit.input = ADC_Y;
354  sInit.reference = adcRefVDD;
355  sInit.resolution = adcResOVS;
356  ADC_InitSingle(ADC0, &sInit);
358  touch_state = TOUCH_INIT;
359  NVIC_ClearPendingIRQ(ADC0_IRQn);
360  NVIC_EnableIRQ(ADC0_IRQn);
363 }
364 
365 /***************************************************************************/
373 {
374  ADC_IntDisable(ADC0, ADC_IF_SINGLE); /* we need to disable ADC interrupt to avoid current_pos structure update for a while */
375  pos.pen = current_pos.pen;
376  pos.x = current_pos.x;
377  pos.y = current_pos.y;
378  pos.adcx = current_pos.adcx;
379  pos.adcy = current_pos.adcy;
381 
382  return &pos;
383 }
384 
385 /***************************************************************************/
393 {
394  upcall = new_upcall;
395 }
396 
397 
398 /***************************************************************************/
410 int TOUCH_CalibrationTable(POINT * displayPtr, POINT * screenPtr)
411 {
412  int result;
413  result = setCalibrationMatrix(displayPtr, screenPtr, &calibrationMatrix);
414 #ifndef TOUCH_WITHOUT_STORE
415  if (result == OK)
416  touch_StoreCalibration();
417 #endif
418  return(result);
419 }
Clock management unit (CMU) API.
void TOUCH_Init(TOUCH_Config_TypeDef *config)
Initialize touch panel driver.
Definition: touch.c:340
uint16_t x
Definition: touch.h:48
Board support package API definitions.
#define TOUCH_X1_PORT
Definition: touch.c:41
#define TOUCH_Y1_PIN
Definition: touch.c:46
#define ADC_X
Definition: touch.c:37
void I2CSPM_Init(I2CSPM_Init_TypeDef *init)
Initalize I2C peripheral.
Definition: i2cspm.c:39
int EEPROM_Write(I2C_TypeDef *i2c, uint8_t addr, unsigned int offset, uint8_t *data, unsigned int len)
Write data to EEPROM.
Definition: eeprom.c:191
ADC_AcqTime_TypeDef acqTime
Definition: em_adc.h:993
#define TOUCH_Y2_PORT
Definition: touch.c:47
#define ADC_INITSINGLE_DEFAULT
Definition: em_adc.h:1049
__STATIC_INLINE void ADC_IntClear(ADC_TypeDef *adc, uint32_t flags)
Clear one or more pending ADC interrupts.
Definition: em_adc.h:1194
CMSIS Cortex-M Peripheral Access Layer for Silicon Laboratories microcontroller devices.
#define ADC_INIT_DEFAULT
Definition: em_adc.h:836
__STATIC_INLINE uint32_t ADC_DataSingleGet(ADC_TypeDef *adc)
Get single conversion result.
Definition: em_adc.h:1096
ADC_SingleInput_TypeDef input
Definition: em_adc.h:1009
uint16_t y
Definition: touch.h:49
#define TOUCH_Y2_PIN
Definition: touch.c:48
Definition: bsp.h:122
EEPROM driver for 24AA024 (2Kbit) EEPROM device on the DK.
__STATIC_INLINE void ADC_IntEnable(ADC_TypeDef *adc, uint32_t flags)
Enable one or more ADC interrupts.
Definition: em_adc.h:1233
Touch panel driver prototypes and definitions.
void ADC_Init(ADC_TypeDef *adc, const ADC_Init_TypeDef *init)
Initialize ADC.
Definition: em_adc.c:371
#define TOUCH_X1_PIN
Definition: touch.c:42
TOUCH_State_TypeDef
Definition: touch.c:51
void GPIO_PinModeSet(GPIO_Port_TypeDef port, unsigned int pin, GPIO_Mode_TypeDef mode, unsigned int out)
Set the mode for a GPIO pin.
Definition: em_gpio.c:269
int TOUCH_StateChanged(void)
Check if cursor state changed (down or move)
Definition: touch.c:203
Definition: bsp.h:126
General Purpose IO (GPIO) peripheral API.
I2C simple poll-based master mode driver for the DK/STK.
uint16_t adcx
Definition: touch.h:50
#define TOUCH_X2_PIN
Definition: touch.c:44
#define I2CSPM_INIT_DEFAULT
Definition: i2cspm.h:73
void ADC0_IRQHandler(void)
Interrupt handler is executed with frequency ~28Hz when panel is not pressed and with frequency ~140H...
Definition: touch.c:224
void CMU_ClockEnable(CMU_Clock_TypeDef clock, bool enable)
Enable/disable a clock.
Definition: em_cmu.c:1453
ADC_Res_TypeDef resolution
Definition: em_adc.h:1002
void TOUCH_RecalculatePosition(volatile TOUCH_Pos_TypeDef *pos)
Convert ADC readings into XY position.
Definition: touch.c:163
uint8_t prescale
Definition: em_adc.h:822
uint8_t pen
Definition: touch.h:52
__STATIC_INLINE void ADC_IntDisable(ADC_TypeDef *adc, uint32_t flags)
Disable one or more ADC interrupts.
Definition: em_adc.h:1211
void ADC_InitSingle(ADC_TypeDef *adc, const ADC_InitSingle_TypeDef *init)
Initialize single ADC sample conversion.
Definition: em_adc.c:854
#define ADC0
#define ADC_IF_SINGLE
Definition: efm32g_adc.h:517
#define TOUCH_X2_PORT
Definition: touch.c:43
int EEPROM_Read(I2C_TypeDef *i2c, uint8_t addr, unsigned int offset, uint8_t *data, unsigned int len)
Read data from EEPROM.
Definition: eeprom.c:126
int TOUCH_IsBusy(void)
Check status of the touch panel.
Definition: touch.c:318
int BSP_PeripheralAccess(BSP_Peripheral_TypeDef perf, bool enable)
DK Peripheral Access Control Enable or disable access to on-board peripherals through switches and SP...
Definition: bsp_dk_3201.c:461
void TOUCH_RegisterUpcall(TOUCH_Upcall_TypeDef *new_upcall)
Register upcall which will be call every position or state change.
Definition: touch.c:392
uint16_t adcy
Definition: touch.h:51
uint8_t ADC_PrescaleCalc(uint32_t adcFreq, uint32_t hfperFreq)
Calculate prescaler value used to determine ADC clock.
Definition: em_adc.c:1025
TOUCH_Pos_TypeDef * TOUCH_GetPos(void)
Returns current touch position and state.
Definition: touch.c:372
__STATIC_INLINE void ADC_Start(ADC_TypeDef *adc, ADC_Start_TypeDef cmd)
Start scan sequence and/or single conversion.
Definition: em_adc.h:1318
#define TOUCH_Y1_PORT
Definition: touch.c:45
uint32_t frequency
Definition: touch.h:57
Analog to Digital Converter (ADC) peripheral API.
#define I2C0
ADC_Ref_TypeDef reference
Definition: em_adc.h:999
#define EEPROM_DVK_ADDR
Definition: eeprom.h:40
ADC_OvsRateSel_TypeDef ovsRateSel
Definition: em_adc.h:802
#define _ADC_IF_MASK
Definition: efm32g_adc.h:516
void( TOUCH_Upcall_TypeDef)(TOUCH_Pos_TypeDef *)
Definition: touch.h:70
uint8_t ignore
Definition: touch.h:58
#define ADC_Y
Definition: touch.c:38
__STATIC_INLINE unsigned int GPIO_PinInGet(GPIO_Port_TypeDef port, unsigned int pin)
Read the pad value for a single pin in a GPIO port.
Definition: em_gpio.h:781
void TOUCH_CallUpcall(void)
Function that calls registered upcall.
Definition: touch.c:190
int TOUCH_CalibrationTable(POINT *displayPtr, POINT *screenPtr)
Set calibration table.
Definition: touch.c:410
ADC_OvsRateSel_TypeDef oversampling
Definition: touch.h:59
MATRIX calibrationMatrix
Definition: touch.c:71