Conversion of DisplayModule's DmTouch to work with UniGraphics as display driver rather than their own DmTftLib.

Fork of DmTouch_UniGraphic by Whitworth-EN173-2016

DmTouch.cpp

Committer:
JLarkin
Date:
2016-01-21
Revision:
1:852bf4b0714f
Parent:
0:fd018d3e7dab

File content as of revision 1:852bf4b0714f:

/**********************************************************************************************
 Copyright (c) 2014 DisplayModule. All rights reserved.
 
 Redistribution and use of this source code, part of this source code or any compiled binary
 based on this source code is permitted as long as the above copyright notice and following
 disclaimer is retained.
 
 DISCLAIMER:
 THIS SOFTWARE IS SUPPLIED "AS IS" WITHOUT ANY WARRANTIES AND SUPPORT. DISPLAYMODULE ASSUMES
 NO RESPONSIBILITY OR LIABILITY FOR THE USE OF THE SOFTWARE.
 ********************************************************************************************/
// Tested with Xpt2046 and RA8875

/* Modified by John M. Larkin, Whitworth University, to remove Arduino code */
 
#include "DmTouch.h"
//#include "DmTouchCalibration.h"
 
#define MEASUREMENTS 10
 
// disp        - which display is used
// spiMode     - How to read SPI-data, Software, Hardware or Auto
// (JML) Modify to include cs and irq as input parameters
// (JML) Modify so doesn't assume Arduino shield
// (JML) Using with mbed so assume hardware SPI available
DmTouch::DmTouch(Display disp, PinName mosi, PinName miso, PinName clk, PinName cs, PinName irq)
{
    _disp = disp;
    _cs = cs;
    _irq = irq;
    _clk = clk;
    _mosi = mosi;
    _miso = miso;
    _hardwareSpi = true; 
    switch (disp) {
        // Display with 40-pin connector on top of adapter board
        case DmTouch::DM_TFT28_103:
        case DmTouch::DM_TFT24_104:  
            _width = 240;
            _height = 320;
            _touch_id = IC_2046;
            break;
            
        case DmTouch::DM_TFT50_111:  // or  DM_TFT50_112
          _width = 800;
          _height = 480;
          _hardwareSpi = true;
          _touch_id = IC_8875;
          break; 
 
        case DmTouch::DM_TFT28_105:
          _width = 240;
          _height = 320;
          _hardwareSpi = true;
          _touch_id = IC_2046;
          break;
 
        case DmTouch::DM_TFT35_107:
          _width = 320;
          _height = 240;      
          _hardwareSpi = true;
          _touch_id = IC_2046;
          break;
 
        case DmTouch::DM_TFT43_108:  // or DM_TFT43_110
          _width = 480;
          _height = 272;
          _hardwareSpi = true;
          _touch_id = IC_8875;
          break;     
      
        default:
          _width = 320;
          _height = 240;
          _hardwareSpi = true;
          _touch_id = IC_2046;
          break;
  }
  
  //setCalibrationMatrix(DmTouchCalibration::getDefaultCalibrationData(disp));
  setCalibrationMatrix(DmTouch::getDefaultCalibrationData(disp));               // Use new local version
 
  _samplesPerMeasurement = 3;
}
 
void DmTouch::init() {
  _pinCS = new DigitalOut(_cs);
  if (_hardwareSpi) {
    sbi(_pinCS, _bitmaskCS);
    _spi = new SPI((PinName)_mosi, (PinName)_miso, (PinName)_clk);
    _spi->format(8,0);
    _spi->frequency(2000000); // Max SPI speed    
  } else {
    _pinCLK = new DigitalOut(_clk);
    _pinMISO = new DigitalIn(_miso);
    _pinMOSI = new DigitalOut(_mosi);
    sbi(_pinCLK, _bitmaskCLK);
  }
 
  if (_irq != NC) { // We will use Touch IRQ
    enableIrq();
  }
}
 
void DmTouch::enableIrq() {
    _pinIrq = new DigitalIn((PinName)_irq);
    _pinIrq->mode(PullUp);  
    if(_touch_id == IC_8875) {
        // enable touch panel
        cbi(_pinCS, _bitmaskCS);
        spiWrite(0x80);
        spiWrite(0x70);
        sbi(_pinCS, _bitmaskCS);
 
        cbi(_pinCS, _bitmaskCS);
        spiWrite(0x00);
        spiWrite(0xB3);
        sbi(_pinCS, _bitmaskCS);
 
        // set auto mode
        cbi(_pinCS, _bitmaskCS);
        spiWrite(0x80);
        spiWrite(0x71);
        sbi(_pinCS, _bitmaskCS);
 
        cbi(_pinCS, _bitmaskCS);
        spiWrite(0x00);
        spiWrite(0x04);
        sbi(_pinCS, _bitmaskCS);
 
        // enable touch panel interrupt
        cbi(_pinCS, _bitmaskCS);
        spiWrite(0x80);
        spiWrite(0xF0);
        sbi(_pinCS, _bitmaskCS);
 
        cbi(_pinCS, _bitmaskCS);
        uint8_t temp;
        spiWrite(0x40);
        temp = spiRead();
        sbi(_pinCS, _bitmaskCS);
 
        cbi(_pinCS, _bitmaskCS);
        spiWrite(0x80);
        spiWrite(0xF0);
        sbi(_pinCS, _bitmaskCS);
 
        cbi(_pinCS, _bitmaskCS);
        spiWrite(0x00);
        spiWrite(temp | 0x04);
        sbi(_pinCS, _bitmaskCS);
 
        // Clear TP INT Status
        cbi(_pinCS, _bitmaskCS);
        spiWrite(0x80);
        spiWrite(0xF1);
        sbi(_pinCS, _bitmaskCS);
 
        cbi(_pinCS, _bitmaskCS);
        spiWrite(0x00);
        spiWrite(0x04);
        sbi(_pinCS, _bitmaskCS);
    } 
    else{
        cbi(_pinCS, _bitmaskCS);
        spiWrite(0x80); // Enable PENIRQ
        sbi(_pinCS, _bitmaskCS);
    }      
}
 
void DmTouch::spiWrite(uint8_t data) {
  if (_hardwareSpi) {
    _spi->write(data);
  }
  else {
    uint8_t count=0;
    uint8_t temp = data;
    delay(1);
    cbi(_pinCLK, _bitmaskCLK);
    for(count=0;count<8;count++) {
      if(temp&0x80) {
        sbi(_pinMOSI, _bitmaskMOSI);
      }
      else {
        cbi(_pinMOSI, _bitmaskMOSI);
      }
 
      temp=temp<<1;
 
      slow_pulse_low(_pinCLK, _bitmaskCLK);
    }
  }
}
 
uint8_t DmTouch::spiRead() {// Only used for Hardware SPI
  if (_hardwareSpi) {
    return _spi->write(0x00); // dummy byte to read
  } else {
    uint8_t count=0;
    uint8_t temp=0;
    cbi(_pinCLK, _bitmaskCLK);
    cbi(_pinMOSI, _bitmaskMOSI); // same as using 0x00 as dummy byte
    for(count=0;count<8;count++) {
 
      pulse_low(_pinCLK, _bitmaskCLK);
      temp = temp<<1;
      temp |= _pinMISO->read();
    }
    return temp;
  }
}
 
uint16_t DmTouch::readData12(uint8_t command) {
  uint8_t temp = 0;
  uint16_t value = 0;
 
  spiWrite(command); // Send command
  // We use 7-bits from the first byte and 5-bit from the second byte
  temp = spiRead();
  value = temp<<8;
  temp = spiRead();
  value |= temp;
  value >>=3;
  value &= 0xFFF;
  return value;
}
 
void DmTouch::readRawData(uint16_t &x, uint16_t &y) {
  if(_touch_id == IC_8875){
    uint16_t tx, ty;
    uint8_t temp;
 
    cbi(_pinCS, _bitmaskCS);
    spiWrite(0x80);
    spiWrite(0x72);
    sbi(_pinCS, _bitmaskCS);
    
    cbi(_pinCS, _bitmaskCS);
    spiWrite(0x40);
    tx = spiRead();     
    sbi(_pinCS, _bitmaskCS);
 
    cbi(_pinCS, _bitmaskCS);
    spiWrite(0x80);
    spiWrite(0x73);     
    sbi(_pinCS, _bitmaskCS);
 
    cbi(_pinCS, _bitmaskCS);
    spiWrite(0x40);
    ty = spiRead();     
    sbi(_pinCS, _bitmaskCS);
 
    cbi(_pinCS, _bitmaskCS);
    spiWrite(0x80);
    spiWrite(0x74); 
    sbi(_pinCS, _bitmaskCS);
 
    cbi(_pinCS, _bitmaskCS);
    spiWrite(0x40);
    temp = spiRead();       
    sbi(_pinCS, _bitmaskCS);
            
    tx <<= 2;
    ty <<= 2;
    tx |= temp & 0x03;              // get the bottom x bits
    ty |= (temp >> 2) & 0x03; // get the bottom y bits
            
    x = tx;
    y = ty;
            
    // Clear TP INT Status 
    cbi(_pinCS, _bitmaskCS);
    spiWrite(0x80);
    spiWrite(0xF1);     
    sbi(_pinCS, _bitmaskCS);
 
    cbi(_pinCS, _bitmaskCS);
    spiWrite(0x00);
    spiWrite(0x04); 
    sbi(_pinCS, _bitmaskCS);            
  }
  else{  
    cbi(_pinCS, _bitmaskCS);
    x = readData12(0xD0);
    y = readData12(0x90);
    sbi(_pinCS, _bitmaskCS);
  }
}
 
void DmTouch::readTouchData(uint16_t& posX, uint16_t& posY, bool& touching) {  
  uint16_t touchX, touchY;
  getMiddleXY(touchX,touchY);
  uint16_t screenX, screenY;
 
  posX = getDisplayCoordinateX(touchX, touchY);
  posY = getDisplayCoordinateY(touchX, touchY);
  if(_touch_id == IC_8875) {
    touching = isTouched() && (posX < _width && posY < _height);
  }
  else{
    touching = (posX < _width && posY < _height);
  }
  // Now account for screen orientation and return in "screen coordinates"
  switch(_orient) {
        case 0:
            screenX = posX;
            screenY = posY;
            break;
        case 1:
            screenX = posY;
            screenY = _width-posX;
            break;
        case 2:
            screenX = _width - posX;
            screenY = _height - posY;
            break;
        case 3:
            screenX = _height - posY;
            screenY = posX;
            break;
        default:
            screenX = posX;
            screenY = posY;
        }
    posX = screenX;
    posY = screenY;    
}
 
bool DmTouch::isSampleValid() {
  uint16_t sampleX,sampleY;
  readRawData(sampleX,sampleY);
  if (sampleX > 0 && sampleX < 4095 && sampleY > 0 && sampleY < 4095) {
    return true;
  } else {
    return false;
  }
}
 
bool DmTouch::isTouched() {
    if(_touch_id == IC_8875) {
        delay(1);
        if (!_pinIrq->read()) {
            // Clear TP INT Status
            cbi(_pinCS, _bitmaskCS);
            spiWrite(0x80);
            spiWrite(0xF1);
            sbi(_pinCS, _bitmaskCS);
 
            cbi(_pinCS, _bitmaskCS);
            spiWrite(0x00);
            spiWrite(0x04);
            sbi(_pinCS, _bitmaskCS);
            return true;
        } else {
            return false;
        }
    }
  return isSampleValid();
}
 
bool DmTouch::getMiddleXY(uint16_t &x, uint16_t &y) {
  bool haveAllMeasurements  = true;
  uint16_t valuesX[MEASUREMENTS];
  uint16_t valuesY[MEASUREMENTS];
  uint8_t nbrOfMeasurements = 0;
 
  for (int i=0; i<MEASUREMENTS; i++) {
    getAverageXY(valuesX[i], valuesY[i]);  
    nbrOfMeasurements++;
    if(_touch_id != IC_8875) {
      if (!isTouched()) {
        haveAllMeasurements = false;
        break;
      }
    }
  }
  if (haveAllMeasurements) {
    x = calculateMiddleValue(valuesX, nbrOfMeasurements);
    y = calculateMiddleValue(valuesY, nbrOfMeasurements);
  }
 
  return haveAllMeasurements;
}
 
void DmTouch::getAverageXY(uint16_t &x, uint16_t &y) {
  uint32_t sumX = 0;
  uint32_t sumY = 0;
  uint16_t sampleX,sampleY;
  readRawData(sampleX,sampleY);
 
  for (int i=0; i<_samplesPerMeasurement; i++) {
    readRawData(sampleX,sampleY);
    sumX += sampleX;
    sumY += sampleY;
  }
 
  x = (uint32_t)sumX/_samplesPerMeasurement;
  y = (uint32_t)sumY/_samplesPerMeasurement;
}
 
// Total number of samples = MEASUREMENTS * _samplesPerMeasurement
void DmTouch::setPrecison(uint8_t samplesPerMeasurement) {
  _samplesPerMeasurement = samplesPerMeasurement;
}
 
void DmTouch::setCalibrationMatrix(CalibrationMatrix calibrationMatrix) {
  _calibrationMatrix = calibrationMatrix;
}
 
void DmTouch::waitForTouch() {
  while(!isTouched()) {}
}
 
void DmTouch::waitForTouchRelease() {
  while(isTouched()) {}
}
 
uint16_t DmTouch::getDisplayCoordinateX(uint16_t x_touch, uint16_t y_touch) {
  uint16_t Xd;
  float temp;
  temp = (_calibrationMatrix.a * x_touch + _calibrationMatrix.b * y_touch + _calibrationMatrix.c) / rescaleFactor();
  Xd = (uint16_t)(temp);
  if (Xd > 60000) {
    Xd = 0;
  }
  return Xd;
}
 
uint16_t DmTouch::getDisplayCoordinateY(uint16_t x_touch, uint16_t y_touch) {
  uint16_t Yd;
  float temp;
  temp = (_calibrationMatrix.d * x_touch + _calibrationMatrix.e * y_touch + _calibrationMatrix.f) / rescaleFactor();
  Yd = (uint16_t)(temp);
  if (Yd > 60000) {
    Yd = 0;
  }
  return Yd;
}
 
uint16_t DmTouch::calculateMiddleValue(uint16_t values[], uint8_t count) {
  uint16_t temp;
 
  for(uint8_t i=0; i<count-1; i++) {
    for(uint8_t j=i+1; j<count; j++) {
      if(values[j] < values[i]) {
        temp = values[i];
        values[i] = values[j];
        values[j] = temp;
      }
    }
  }
 
  if(count%2==0) {
    return((values[count/2] + values[count/2 - 1]) / 2.0);
  } else {
    return values[count/2];
  }
}

// (JML) Add a function to set screen orientation to match UniGraphics display feature
void DmTouch::setOrientation(char orient) {
    _orient = orient%4;
}   


/* Moved the default function to DmTouch rather than DmTouchCalibration as interim measure
*/
CalibrationMatrix DmTouch::getDefaultCalibrationData(DmTouch::Display disp) {
  CalibrationMatrix calibrationMatrix = {0};
  switch (disp) {
    case DmTouch::DM_TFT28_103:
      calibrationMatrix.a = 67548;    //    63787;
      calibrationMatrix.b = -625;     //     -138;
      calibrationMatrix.c = -16854644;//-15921157;
      calibrationMatrix.d = 362;      //     -244;
      calibrationMatrix.e = 89504;    //    89313;
      calibrationMatrix.f = -14380636;//-10726623;
      break;
 
    case DmTouch::DM_TFT24_104:
      calibrationMatrix.a = -71855;
      calibrationMatrix.b = 2147;
      calibrationMatrix.c = 259719524;
      calibrationMatrix.d = -1339;
      calibrationMatrix.e = -91012;
      calibrationMatrix.f = 354268832;
      break;
 
    case DmTouch::DM_TFT28_105:
      calibrationMatrix.a = 65521;
      calibrationMatrix.b = -253;
      calibrationMatrix.c = -11813673;
      calibrationMatrix.d = -439;
      calibrationMatrix.e = 89201;
      calibrationMatrix.f = -10450920;
      break;
 
    case DmTouch::DM_TFT35_107:
      calibrationMatrix.a = 91302;    //    85984;
      calibrationMatrix.b = 817;      //      451;
      calibrationMatrix.c = -26296117;//-16494041;
      calibrationMatrix.d = -1877;    //     2308;
      calibrationMatrix.e = 73762;    //    65173;
      calibrationMatrix.f = -26384255;//-19179080;
      break;
    case DmTouch::DM_TFT43_108:   // or DM_TFT43_110
      calibrationMatrix.a = 541307;
      calibrationMatrix.b = -4288;
      calibrationMatrix.c = -36678732;
      calibrationMatrix.d = 2730;
      calibrationMatrix.e = 321714;
      calibrationMatrix.f = -31439472;    
      break;
    case DmTouch::DM_TFT50_111:   // or  DM_TFT50_112
      calibrationMatrix.a = 875894;
      calibrationMatrix.b = 1655;
      calibrationMatrix.c = -53695309;
      calibrationMatrix.d = -993;
      calibrationMatrix.e = 544421;
      calibrationMatrix.f = -41496753;                    
      break;   
    default:
      break;
  }
  return calibrationMatrix;
}