Touch driver for companion boards (VKLCD50RTA & VKLCD70RT)
Diff: Touch.cpp
- 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 */