You are reading a single comment by @allObjects and its replies. Click here to read the full conversation.
  • Here it is, the calibration, resulting in a new module and a calibration function:

    // TFT24TRCportraitCalibr2.js
    //
    //  2.4" 240x320 262K Color TFT LCD ILI9341 on SPI1 with 
    //  Resistive Touch Screen Controller XPT2046 on SPI2
    //  using out of box ILI9341 and ADS7843 modules.
    //
    // Wiring:
    //  2.4"
    //  DSP                  PICO [<---USB
    //   1   VCC      red     3   3.3            shared
    //   2   GND      blk     1   GND            shared
    //   3   CS       blu     7   B6   CS        LCD
    //   4   RST      brn     9   A8   reset RST LCD
    //   5   D/C      grn     8   B7   D/C       LCD
    //   6   MOSI     ylw     6   B5   SPI1 MOSI LCD
    //   7   SCK      wht     4   B3   SPI1 SCK  LCD
    //   8   LED      org    18   A5   LED       LCD
    //   9   MISO     grn     5   B4   SPI1 MISO LCD
    //  10   T_CLK    wht    23   B13  SPI2 SCK  Touch
    //  11   T_CS     ylw    22   B10  CS        Touch
    //  12   T_MOSI   grn    25   B15  SPI2 MOSI Touch
    //  13   T_MISO   blu    24   B14  SPI2 MISO Touch
    //  14   T_IRQ    blk    21   B1   IRQ       Touch
    
    var dW = 240, dH = 320, dsp, dMod = require("ILI9341"),
        tSPI=SPI2, tCs = B10, tIrq = B1, 
        touch, tMod = // require("ADS7843");
    // future XPT2046 touchscreen controller
    // default calculation is a pass thru
    { calc: function(x, y, d) { return [x,y,d]; } // default calculation
    , connect: function(spi, cs, irq, callback, calc) {
        // overwrite default 'calculation': pass thru incl. raw values
        if (calc) { this.calc = calc; }
        // wake the controller up
        spi.send([0x90,0],cs);
        // look for a press
        var watchFunction = function() {
          var interval = setInterval(function () {
            if (!digitalRead(irq)) { // touch down
              var d = spi.send([0x90,0,0xD0,0,0], cs);
              callback.apply(undefined, this.calc(d[1]*256+d[2], d[3]*256+d[4], d));
            } else {
              callback();
              clearInterval(interval);
              interval = undefined;
              setWatch(watchFunction, irq, { repeat : false, edge: "falling" });
            }
          }.bind(this), 50);
        }.bind(this);
        setWatch(watchFunction, irq, { repeat : false, edge: "falling" });
        return this;
      }
    };
    
    // marker specs (lik dice face 5; top left to bottom right)
    var mrks =
        [{i:1, label:"topLeft    ", x: 20, y: 20, xOff: 14, yOff:-6}
        ,{i:2, label:"topRight   ", x:220, y: 20, xOff:-66, yOff:-6}
        ,{i:3, label:"center     ", x:120, y:160, xOff:-29, yOff:14}
        ,{i:4, label:"bottomLeft ", x: 20, y:300, xOff: 14, yOff:-6}
        ,{i:5, label:"bottomRight", x:220, y:300, xOff:-74, yOff:-6}
        ];
    var mSiz = 9; // half of marker size, (0.7 of font size)
    
    // print marker at x/y of size s with x/y label
    function mrkr(d,x,y,s,xOff,yOff) {
      dsp.setColor(1,1,1);
      dsp.drawRect(x-s,y-s,x+s,y+s);
      dsp.drawLine(x-s,y,x+s,y);
      dsp.drawLine(x,y-s,x,y+s);
      dsp.setFontVector(s / 0.7);
      dsp.drawString(x + " / " + y, x+xOff, y+yOff);
    }
    
    function average(arry) { // return avarage of array values
      return arry.reduce(
        function(p,c) { return p + c; },0) / arry.length; }
    
    function fInt(i,l) { return ("        " + i).substr(-l); }
    
    function logXY(m,xp,yp,l,d) {
      console.log(
          m.i
        , m.label
        , fInt(m.x,4), fInt(m.y,4)
        , fInt(m[xp],l), (d) ? "(" + fInt(m[xp] - m.x,4) + ")" : ""
        , fInt(m[yp],l), (d) ? "(" + fInt(m[yp] - m.y,4) + ")" : ""
        );
    }
    
    function handleAverage(seq,xs,ys) { // log x / y average
      var m = mrks[seq],
          x = Math.round(average(xs)), 
          y = Math.round(average(ys));
      m.xAvg = x; m.yAvg = y;
      logXY(m,"xAvg","yAvg",6);
      if (seq === 4) { // all in, calc calibration parms and calib with new calc function
        var xscs = [], // xscales = x deltas per pixel
            yscs = [], // yscales = y deltas per pixel
            xoss = [], // xoffsets 
            yoss = []; // yoffsets
        xscs.push((mrks[1].xAvg - mrks[0].xAvg) / (mrks[1].x - mrks[0].x)); // top x deltas
        xscs.push((mrks[2].xAvg - mrks[0].xAvg) / (mrks[2].x - mrks[0].x)); // top left - cent
        xscs.push((mrks[1].xAvg - mrks[2].xAvg) / (mrks[1].x - mrks[2].x)); // cent - top right
        xscs.push((mrks[4].xAvg - mrks[3].xAvg) / (mrks[4].x - mrks[3].x)); // bottom x deltas
        xscs.push((mrks[2].xAvg - mrks[3].xAvg) / (mrks[2].x - mrks[3].x)); // bot left - cent
        xscs.push((mrks[4].xAvg - mrks[2].xAvg) / (mrks[4].x - mrks[2].x)); // cent - top right
        console.log(xscs);
        yscs.push((mrks[3].yAvg - mrks[0].yAvg) / (mrks[3].y - mrks[0].y)); // left y deltas
        yscs.push((mrks[2].yAvg - mrks[0].yAvg) / (mrks[2].y - mrks[0].y)); // top left - cent
        yscs.push((mrks[3].yAvg - mrks[2].yAvg) / (mrks[3].y - mrks[2].y)); // cent - left bottom
        yscs.push((mrks[4].yAvg - mrks[1].yAvg) / (mrks[4].y - mrks[1].y)); // right y deltas
        yscs.push((mrks[2].yAvg - mrks[1].yAvg) / (mrks[2].y - mrks[1].y)); // top right - cent
        yscs.push((mrks[4].yAvg - mrks[2].yAvg) / (mrks[4].y - mrks[2].y)); // cent - bot right
        console.log(yscs);
        var xsc = average(xscs), 
            ysc = average(yscs);
        mrks.forEach(function(m) { 
            xoss.push(m.x - m.xAvg / xsc); 
            yoss.push(m.y - m.yAvg / ysc);
        });
        console.log(xoss);
        console.log(yoss);
        var xos = average(xoss), 
            yos = average(yoss);
        console.log("x / y scale", xsc, ysc);
        console.log("x / y offset", xos, yos);
        mrks.forEach(function(m){
          m.xc = Math.round((m.xAvg / xsc) + xos);
          m.yc = Math.round((m.yAvg / ysc) + yos);
          logXY(m,"xc","yc",4,true);
        });
        
        // calibrated calc function set in touch:
        // x / y scale   -121.5825         89.79285714285
        // x / y offset   258.33405300927 -18.53090446265
        touch.calc = function(yr, xr) { // xy raw
          return [Math.round(xr / xsc + xos) // / x scale per pixel + x offset
                 ,Math.round(yr / ysc + yos) // / y scale per pixel + y offset
                 ];
        };
        
        calibrated = true;
        console.log("---- calibrated, normal mode");
        console.log(" touch markers for validation.");
      }
    }
    
    var calibrated = false;
    
    function onInit() {
      A5.set();
      SPI1.setup({sck:B3, miso:B4, mosi:B5, baud: 1000000});
                       // spi, dc, cs, rst, callback
      dsp = dMod.connect(SPI1, B7, B6, A8, function() {
          dsp.clear();
          // print markers like dice face of 5
          mrks.forEach(function(m) {
            mrkr(dsp, m.x, m.y, mSiz, m.xOff, m.yOff); });
          console.log("---- in calibration mode:");
          console.log(" touch markers from top left to bottom right.");
          // setup touchscreen with callback averaging
          // x and y sequence for calibration of 
          // touchscreen to lcd display.
          SPI2.setup({sck:B13, miso:B14, mosi:B15, baud: 2000000});
          var seq = 0, xs = [], ys = [], x, y;
          touch = tMod.connect(
              // spi, cs, irq, callback, calc // landscape
              SPI2, B10, B1, function(m1, m2, rd) {  // portrait
                var xt, yt;
                if (calibrated) { // calibrated mode
                  xt = m1; yt = m2;
                  if (xt !== undefined) {
                    console.log(fInt(xt,4),fInt(yt,4));
                  }
                } else { // calibration mode
                  xt = m2; yt = m1;
                  if (xt !== undefined) {
                    // console.log(rd);
                    // collect x and y for averaging
                    xs.push(xt); ys.push(yt);
                  } else {
                    // log x and y avarages in console 
                    handleAverage(seq, xs, ys);
                    xs = []; ys = []; seq   = (seq +1) % 5;
                  }
                }
          });
      });
    }
    
    onInit();
    

    Related console output:

     1v86 Copyright 2016 G.Williams
    >echo(0);
    =undefined
    ---- in calibration mode:
     touch markers from top left to bottom right.
    1 topLeft       20   20  29148    3490
    2 topRight     220   20   4862    3495
    3 center       120  160  17118   16160
    4 bottomLeft    20  300  28996   28347
    5 bottomRight  220  300   4706   28424
    [ -121.43, -120.3, -122.56, -121.45, -118.78, -124.12 ]
    [ 88.775, 90.5, 87.05, 89.03214285714, 90.46428571428, 87.6 ]
    [ 260.01976284584, 260.03623188405, 260.95849802371, 258.76811594202, 258.75164690382 ]
    [ -19.25601574739, -19.31225645763, -21.76997549511, -18.85108263367, -19.71718957136 ]
    x / y scale -121.44 88.90357142857
    x / y offset 259.70685111989 -19.78130398103
    1 topLeft       20   20   20 (   0)   19 (  -1)
    2 topRight     220   20  220 (   0)   20 (   0)
    3 center       120  160  119 (  -1)  162 (   2)
    4 bottomLeft    20  300   21 (   1)  299 (  -1)
    5 bottomRight  220  300  221 (   1)  300 (   0)
    ---- calibrated, normal mode
     touch markers for validation.
      21  18
      23  17
      18  18
     222  19
     221  19
     223  20
     120 160
     121 160
     116 160
     121 159
      19 303
      20 303
      16 299
     220 304
     221 301
     221 299
     222 301
    > 
    

    The calibration goes as follows:

    • 30..53 provided a new module base on existing one that allows to set a customized calculation function
      - 30 shows the default - pass through function - which accepts the raw coordinates and the read data. Read data is pass just in case...
      - 41 shows the calculation invocation with the pre-calculation of the raw coordinates (same as known from existing modules: highByte * 256 + lowByte)
      - 48 and 49 are noteworthy where context of module / touch singleton is bound in order to use this.calc(...) function in nested anonymous watch and interval functions.
      - 176..187 raw values are collected while touching the calibration marks from top left to bottom right.
      - 97 starts the actual calibration calculations...
      - 98..117 calculates raw value per pixel based on known coordinates of the markers and measured values: scale value (raw value) / pixel = read value difference divided by pixel delta of the markers
      - 118..128 calculates the offset for each axis.
      - 126 and 127 show the calculated scale and offset values.
      - 128..132 calculates the new values from the initially read values (1..5 console output) with given scaling and offset and shows them in the console for verification (117..121 console output)
      - 134..141 sets the new calibrated calculation function in touch singleton
      - 143 switches into regular mode for verification of the calc function (touching/tapping the markers again shows new calculated values as expected.

    1 Attachment

About

Avatar for allObjects @allObjects started