Touch driver for companion boards (VKLCD50RTA & VKLCD70RT)
Touch.cpp
- Committer:
- tvendov
- Date:
- 2017-11-03
- Revision:
- 0:0383b9f88d72
File content as of revision 0:0383b9f88d72:
#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 */