Touch driver for companion boards (VKLCD50RTA & VKLCD70RT)

Revision:
0:0383b9f88d72
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Touch.cpp	Fri Nov 03 08:52:07 2017 +0000
@@ -0,0 +1,511 @@
+#include "Touch.hpp"
+
+
+
+namespace Vekatech {
+
+#define DISABLED	0
+#define LAST_SAMPLE	1
+#define ALL_SAMPLES	2
+
+//Debug is disabled by default
+#define TOUCH_DBG	DISABLED
+//#define MORE_DETAILS
+
+#ifndef FUNCTION_NAME
+//#define FUNCTION_NAME ""
+//#define FUNCTION_NAME __func__
+//#define FUNCTION_NAME __FUNCTION__
+#define FUNCTION_NAME __PRETTY_FUNCTION__
+#endif
+
+#if (TOUCH_DBG > DISABLED)
+#ifdef MORE_DETAILS
+	#define DBG(x, ...) printf("[DBG: %s:%s:%d] " x "\r\n", __FILE__, FUNCTION_NAME, __LINE__, ##__VA_ARGS__)
+#else
+	#define DBG(x, ...) printf("[DBG:] " x "\r\n", ##__VA_ARGS__)
+#endif
+#else
+	#define DBG(x, ...)
+#endif
+
+#if (TOUCH_DBG > LAST_SAMPLE)
+#ifdef MORE_DETAILS
+	#define DBG_MORE(x, ...) printf("[DBG_MORE: %s:%s:%d] " x "\r\n", __FILE__, FUNCTION_NAME, __LINE__,  ##__VA_ARGS__)
+#else
+	#define DBG_MORE(x, ...) printf("[DBG_MORE:] " x "\r\n", ##__VA_ARGS__)
+#endif
+#else
+	#define DBG_MORE(x, ...)
+#endif
+
+#if (TOUCH_DBG > ALL_SAMPLES)
+#ifdef MORE_DETAILS
+	#define DBG_ALL(x, ...) printf("[DBG_ALL: %s:%s:%d] " x "\r\n", __FILE__, FUNCTION_NAME, __LINE__,  ##__VA_ARGS__)
+#else
+	#define DBG_ALL(x, ...) printf("[DBG_ALL:] " x "\r\n", ##__VA_ARGS__)
+#endif
+#else
+	#define DBG_ALL(x, ...)
+#endif
+
+
+
+#define NEW_TOUCH_DATA	0x1
+
+const touch_config_t  STMPE811_cfg = {
+	"STMPE811", LCD_VDC5_CH0_PANEL, RESISTIVE,
+    {I_2_C, TPIIC_SDA, TPIIC_SCL, NC, NC, NC, NC, 100000},
+    {INT_ON_EDGE, FALLING_OR_ACTIVE_LO, TPIRQ_PIN}
+};
+
+osThreadId			TouchThreadID;
+
+/**************************************************************************//**
+ * @brief       Constructor of the Touch class
+ * @param[in]   pointer to Config structure (touch_config_t)
+******************************************************************************/
+//SPI(tp_cfg->interface.mosi, tp_cfg->interface.miso, tp_cfg->interface.sclk, tp_cfg->interface.ssel),
+Touch::Touch( const touch_config_t * tp_cfg ) : I2C(tp_cfg->interface.sda, tp_cfg->interface.scl), InterruptIn(tp_cfg->activity_irq.pin)
+{
+	if(tp_cfg == NULL)
+		touch_cfg = &STMPE811_cfg;
+	else
+		touch_cfg = tp_cfg;
+
+	x = y = z = 0;	  adc_x = adc_y = adc_z = 0;
+	xyz_data = screen_data; adc_data = raw_data;
+	last_xyz_idx = FIFO_DEPTH-1;
+
+	dot_thd = 0;
+	fraction = 0;
+	calib.data.flag = 0;
+} /* End of constructor */
+
+
+/**************************************************************************//**
+ * @brief       Destructor of the Touch class
+******************************************************************************/
+Touch::~Touch( )
+{
+	Thread::State S = thd.get_state();
+
+	disable_irq();
+
+	if((S != Thread::Inactive) && (S != Thread::Deleted))
+	{
+		thd.terminate();
+		thd.join();
+	}
+
+	touch_cfg = NULL;
+	xyz_data = NULL;
+	adc_data = NULL;
+}
+
+
+/**************************************************************************//**
+ * @brief       Touch controller initialization
+ * @retval      error code
+******************************************************************************/
+Touch::init_err_t Touch::Init( void )
+{
+	init_err_t tp_err;
+
+	if(touch_cfg->type == RESISTIVE)
+	{
+		tp_err = Clb_Setup();
+
+		if(tp_err != TOUCH_OK)
+			return tp_err;
+	}
+
+	if(touch_cfg->interface.type == I_2_C)
+		I2C::frequency(touch_cfg->interface.freq);
+	//else
+		//SPI::frequency(touch_cfg->interface.freq);
+
+	tp_err = Drv_Setup();
+
+	if(tp_err != TOUCH_OK)
+		return tp_err;
+	else
+	{
+		thd.start(callback(this, &Touch::Handle_touch));
+
+		if(touch_cfg->activity_irq.polarity == FALLING_OR_ACTIVE_LO)
+		{
+			rise(NULL);
+			fall(Irq_Alert);
+		}
+		else
+		{
+			rise(Irq_Alert);
+			fall(NULL);
+		}
+
+		enable_irq();
+	}
+
+	return tp_err;
+} /* End of method Init() */
+
+
+/**************************************************************************//**
+ * @brief       Set Calibration data
+ * @retval      error code
+******************************************************************************/
+Touch::init_err_t Touch::Clb_Setup()
+{
+	if(touch_cfg->name == "STMPE811")
+	{
+		#ifndef __USE_DEFAULT_CALIBRATION_DATA__
+			// extract calibration info from lcdpanel EEPROM
+			char adr = 0;
+
+			if( (touch_cfg->interface.sda == EEIIC_SDA) || (touch_cfg->interface.scl == EEIIC_SCL))
+			{	// lcdpanel EEPROM is on the same I2C channel no need to initialize a new one !
+				if(I2C::write(EE_CALIB_DEVICE_ADDR, (const char *)&adr, 1, true) != 0)
+					return TOUCH_INIT_ERR;
+				if(I2C::read(EE_CALIB_DEVICE_ADDR, (char*)calib.KX08, sizeof(calib.KX08)) != 0)
+					return TOUCH_INIT_ERR;
+			}
+			else
+			{	// lcdpanel EEPROM is on different I2C channel so initialize a new one !
+				I2C clb_eeprom(EEIIC_SDA, EEIIC_SCL);
+
+				clb_eeprom.frequency(100000);
+				if(clb_eeprom.write(EE_CALIB_DEVICE_ADDR, (const char *)&adr, 1, true) != 0)
+					return TOUCH_INIT_ERR;
+				if(clb_eeprom.read(EE_CALIB_DEVICE_ADDR, (char*)calib.KX08, sizeof(calib.KX08)) != 0)
+					return TOUCH_INIT_ERR;
+			}
+		#endif
+
+		if(calib.data.flag != 1)
+		{	// load default calibration info
+			unsigned char clb[] = {TPCALIBRATION_DATA};
+			memcpy(calib.KX08, clb, sizeof(clb));
+		}
+
+		return TOUCH_OK;
+	}
+	else
+		return TOUCH_UNSUPP_ERR;
+} /* End of method Clb_Setup() */
+
+
+/**************************************************************************//**
+ * @brief       Set Touch Controller settings
+ * @retval      error code
+******************************************************************************/
+Touch::init_err_t Touch::Drv_Setup()
+{
+	if(touch_cfg->name == "STMPE811")
+	{
+		unsigned char i, initdata[][2] = { INIT_DATA };
+
+		for(i=0; i<(sizeof(initdata)>>1); i++)
+		{
+			if(initdata[i][0] == INT_CTRL)
+			{	// reconfigure interrupt if needed
+				initdata[i][1] = 0x01;
+				initdata[i][1] |= (touch_cfg->activity_irq.trigger == INT_ON_EDGE)? 0x02 : 0x00;
+				initdata[i][1] |= (touch_cfg->activity_irq.polarity == RISING_OR_ACTIVE_HI)? 0x04 : 0x00;
+			}
+
+			if(initdata[i][0] == FIFO_TH)
+			{	// save threshold
+				if(initdata[i][1] < 2)
+					initdata[i][1] = 2;
+
+				dot_thd = initdata[i][1]-1;
+			}
+
+			if(initdata[i][0] == TSC_FRACT_Z)
+				fraction = initdata[i][1];	// save z precision
+
+			if ((I2C::write(STMPE811_DEVICE_ADDR, (const char *)&initdata[i][0], 2)) != 0)
+				return TOUCH_INIT_ERR;
+
+			while (I2C::write(STMPE811_DEVICE_ADDR, (const char *)initdata, 0) != 0);  // ACK polling
+		}
+
+		return TOUCH_OK;
+	}
+	else
+		return TOUCH_UNSUPP_ERR;
+}
+
+
+/**************************************************************************//**
+ * @brief       Get Status of the pen
+ * @retval     	true	: StylusDown
+ * 				false	: StyluUp
+******************************************************************************/
+bool Touch::Get_Pen_Status()
+{
+	if(touch_cfg->name == "STMPE811")
+	{
+		unsigned char pen = TSC_CTRL;
+
+	  	I2C::write(STMPE811_DEVICE_ADDR, (const char *)&pen, 1, true);
+	  	pen = 0;
+	  	I2C::read(STMPE811_DEVICE_ADDR, (char *)&pen, 1);
+
+	  	return (pen & TSC_STA)? true : false;
+	}
+	else
+		return false;
+} /* End of method Get_Pen_Status() */
+
+
+/**************************************************************************//**
+ * @brief       Get one sample of data
+ * @param[in]   * raw	: pointer to ring buffer to store the samples
+******************************************************************************/
+void Touch::Get_Data( unsigned long long * raw )
+{
+	if(touch_cfg->name == "STMPE811")
+	{
+		int idx = last_xyz_idx;
+		unsigned char  i, packed_sample[dot_thd * 4];
+		unsigned short raw_x, raw_y;
+		unsigned char  raw_z;
+
+		i = TSC_DATA_FIFO;
+		I2C::write(STMPE811_DEVICE_ADDR, (const char *)&i, 1, true);
+		I2C::read(STMPE811_DEVICE_ADDR, (char *)packed_sample, sizeof(packed_sample));
+		for(i=0; i<dot_thd; i++)
+		{
+			raw_x = (unsigned short)((packed_sample[(i*4)+0]<<4) | (packed_sample[(i*4)+1]>>4));
+			raw_y = (unsigned short)(((0x0F & packed_sample[(i*4)+1])<<8) | packed_sample[(i*4)+2]);
+			raw_z = packed_sample[(i*4)+3];
+
+			idx = ((idx+1) < FIFO_DEPTH)? idx+1 : 0;
+		    ((touch_raw_data_t*)raw)[idx].axis.x = raw_x;
+		    ((touch_raw_data_t*)raw)[idx].axis.y = raw_y;
+		    ((touch_raw_data_t*)raw)[idx].axis.z = raw_z;
+		}
+	}
+} /* End of method Get_Data() */
+
+
+/**************************************************************************//**
+ * @brief       Get all available samples of data
+ * @param[in]   * raw	: pointer to ring buffer to store the samples
+ * @retval      		  samples count
+******************************************************************************/
+int Touch::Get_Fifo( unsigned long long * raw )
+{
+	if(touch_cfg->name == "STMPE811")
+	{
+	  int idx = last_xyz_idx;
+	  unsigned char packets;
+
+	  packets = FIFO_SIZE;
+	  I2C::write(STMPE811_DEVICE_ADDR, (const char *)&packets, 1, true);
+	  packets = 0;
+	  I2C::read(STMPE811_DEVICE_ADDR, (char *)&packets, 1);
+	  if(packets)
+	  {
+		unsigned char  packed_sample[packets * 4];
+		unsigned short raw_x, raw_y, i;
+		unsigned char  raw_z;
+
+		raw_z = TSC_DATA_FIFO;
+		I2C::write(STMPE811_DEVICE_ADDR, (const char *)&raw_z, 1, true);
+		I2C::read(STMPE811_DEVICE_ADDR, (char *)packed_sample, packets*4);
+
+	    for(i=0; i<packets; i++)
+	    {
+	      raw_x = (unsigned short)((packed_sample[(i*4)+0]<<4) | (packed_sample[(i*4)+1]>>4));
+	      raw_y = (unsigned short)(((0x0F & packed_sample[(i*4)+1])<<8) | packed_sample[(i*4)+2]);
+	      raw_z = packed_sample[(i*4)+3];
+
+	      idx = ((idx+1) < FIFO_DEPTH)? idx+1 : 0;
+	      ((touch_raw_data_t*)raw)[idx].axis.x = raw_x;
+	      ((touch_raw_data_t*)raw)[idx].axis.y = raw_y;
+	      ((touch_raw_data_t*)raw)[idx].axis.z = raw_z;
+	    }
+
+	    return packets;
+	  }
+	  return 0;
+	}
+	else
+		return 0;
+} /* End of method Get_Fifo() */
+
+
+/**************************************************************************//**
+ * @brief       Coordinates Transfer function
+ * @param[in]   points		: number of samples which have to become meaningful
+******************************************************************************/
+void Touch::Get_XYZ( int points)
+{
+	if(touch_cfg->name == "STMPE811")
+	{
+		int i, idx;
+
+		for(i=0; i<points; i++)
+		{
+			idx = ((last_xyz_idx+1) < FIFO_DEPTH)? last_xyz_idx+1 : 0;
+			screen_data[idx].axis.x = (signed short)(calib.data.KX1*((signed short)raw_data[idx].axis.x)+calib.data.KX2*((signed short)raw_data[idx].axis.y)+calib.data.KX3+0.5);
+			screen_data[idx].axis.y = (signed short)(calib.data.KY1*((signed short)raw_data[idx].axis.x)+calib.data.KY2*((signed short)raw_data[idx].axis.y)+calib.data.KY3+0.5);
+			screen_data[idx].axis.z = ((float)(raw_data[idx].axis.z >> fraction)) + (((float)(raw_data[idx].axis.z & ((0x1<<fraction)-1)))*(1.0f/float(0x1<<fraction)));
+			last_xyz_idx = idx;
+			#ifdef MORE_DETAILS
+				DBG_MORE("TH_No: (%d)   X-> %d (%d), Y-> %d (%d), Z-> %f (%d), dots-> %d/%d", idx, screen_data[idx].axis.x, raw_data[idx].axis.x, screen_data[idx].axis.y, raw_data[idx].axis.y, screen_data[idx].axis.z, raw_data[idx].axis.z, i+1, points);
+			#else
+				DBG_MORE("TH_No: (%d)   X-> %d, Y-> %d, Z-> %f, dots-> %d/%d", idx, screen_data[idx].axis.x, screen_data[idx].axis.y, screen_data[idx].axis.z, i+1, points);
+			#endif
+		}
+
+		x = screen_data[last_xyz_idx].axis.x; adc_x = raw_data[last_xyz_idx].axis.x;
+		y = screen_data[last_xyz_idx].axis.y; adc_y = raw_data[last_xyz_idx].axis.y;
+		z = screen_data[last_xyz_idx].axis.z; adc_z = raw_data[last_xyz_idx].axis.z;
+			#ifdef MORE_DETAILS
+				DBG("S_No: (%d)   X-> %d (%d), Y-> %d (%d), Z-> %f (%d), dots-> %d", last_xyz_idx, x, adc_x, y, adc_y, z, adc_z, points);
+			#else
+				DBG("S_No: (%d)   X-> %d, Y-> %d, Z-> %f, dots-> %d", last_xyz_idx, x, y, z, points);
+			#endif
+	}
+} /* End of method Get_XYZ() */
+
+
+/**************************************************************************//**
+ * @brief       IRQ interrupt handler : indicates "New Touch Data available" which activates i2c data transfer in Handle_touch()
+******************************************************************************/
+void Touch::Irq_Alert()
+{
+	// Execute the time critical part first
+
+	// Then the rest can execute later in user context (and can contain code that's not interrupt safe).
+	osSignalSet(TouchThreadID, NEW_TOUCH_DATA);
+}
+
+
+/**************************************************************************//**
+ * @brief       Get index of the last sample in the ring buffer
+ * @retval      idx
+******************************************************************************/
+int Touch::Get_Last_Idx() { return last_xyz_idx; }
+
+/**************************************************************************//**
+ * @brief       Get dot collection threshold
+ * @retval      threshold
+******************************************************************************/
+int Touch::Get_Dot_thd() { return dot_thd; }
+
+/**************************************************************************//**
+ * @brief       Pull the new samples if new touch data is available
+******************************************************************************/
+void Touch::Handle_touch()
+{
+	bool Stylus = false;
+	bool Click_send = false;
+	unsigned short idx_on = 0;
+	unsigned char TP_IntStat = 0, rec[2];
+	int dots = 0;
+	osEvent evt;
+
+	TouchThreadID = thd.gettid();
+
+	while (true)
+	{
+		evt = thd.signal_wait(NEW_TOUCH_DATA);
+
+		if(evt.status == osEventSignal)
+		{
+			if(touch_cfg->name == "STMPE811")
+			{
+				TP_IntStat = INT_STA;
+				I2C::write(STMPE811_DEVICE_ADDR, (const char *)&TP_IntStat, 1, true);
+				TP_IntStat = 0;
+				I2C::read(STMPE811_DEVICE_ADDR, (char *)&TP_IntStat, 1);
+
+				if(TP_IntStat & INT_FIFO_TH)
+				{
+					Get_Data(&raw_data[0].dot);
+					Get_XYZ(dot_thd);
+					if(Stylus)
+					{
+						if(!Click_send)
+						{
+							MSG(EV_STYLUS_DOWN, idx_on, 0);
+							Click_send = true;
+						}
+						MSG(EV_STYLUS_HOLD, idx_on, last_xyz_idx);
+					}
+				}
+
+				if(TP_IntStat & INT_TOUCH_DET)
+				{
+					Stylus = Get_Pen_Status();
+					DBG("Pen: %s", (Stylus)? "DOWN" : "UP");
+
+					dots = Get_Fifo(&raw_data[0].dot);
+					if(dots)
+					{
+						Get_XYZ(dots);
+
+						if(Stylus)
+						{
+							int on = (last_xyz_idx+1) - dots;
+							idx_on = (on<0)? (FIFO_DEPTH-on) : on;
+							MSG(EV_STYLUS_DOWN, idx_on, 0);
+							Click_send = true;
+						}
+						else
+						{
+							if(!Click_send)
+							{
+								MSG(EV_STYLUS_DOWN, idx_on, 0);
+								Click_send = true;
+							}
+							MSG(EV_STYLUS_UP, idx_on, last_xyz_idx);
+						}
+					}
+					else
+					{
+						if(Stylus)
+						{
+							Click_send = false;
+							idx_on = (last_xyz_idx == (FIFO_DEPTH-1))? 0 : last_xyz_idx+1;
+						}
+						else
+						{
+							if(Click_send)
+								MSG( EV_STYLUS_UP, idx_on, last_xyz_idx);
+						}
+					}
+				}
+
+				if(TP_IntStat & INT_FIFO_OFLOW)
+				{
+					DBG("Overflow !!!");
+
+					rec[0] = FIFO_STA;
+					rec[1] = 0x01;		// Clear FIFO
+					I2C::write(STMPE811_DEVICE_ADDR, (const char *)rec, 2);
+
+					rec[1] = 0x00;		// Reset FIFO
+					I2C::write(STMPE811_DEVICE_ADDR, (const char *)rec, 2);
+				}
+
+				rec[0] = INT_STA;
+				rec[1] = TP_IntStat;
+				I2C::write(STMPE811_DEVICE_ADDR, (const char *)rec, 2);
+			}
+		}
+		else
+		{
+			DBG("Abnormal Touch Thread Status: 0x%X", evt.status);
+		}
+	}
+}
+
+} // namespace Vekatech
+
+/* End of file */