You are reading a single comment by @stephaneAG and its replies. Click here to read the full conversation.
  • hi there !

    got it ;p
    For my early tests, it seems the benefits of line by line VS 16 of them is ok unless area modified is bigger than LCD_WIDTH/2 ( or a little more ).
    Also, I currently have no idea of how to optimize this using either inlineC or "compiled" :/ ( or just another way to do it in js )
    I stumbled upon this topic, but not sure at all if & which concepts could be useful here ..
    This being said, porting this to oled screens instead of colored ones may get quite handy for even faster updates :D

    Also, as a side subject, any idea of a hack to allow stuff already on screen of color other than black ( for ex ) to be all set to a particular color ?

    this being said, here's the code for partial flip :D

    /* black + 1x16bit color instant refresh on ST7735S
      ( https://www.displayfuture.com/Display/da­tasheet/controller/ST7735.pdf ? )
      LCD screen: 0.96" 80x160 RGB IPS 65K 4-wire SPI
      --> currently fixing & dev stuff on 1.44" 128x128 ;p
    */
    
    // Backlight is always on
    var DCpin = A4; //B0;
    var CSpin = B0;
    var RESpin = B1;
    
    var SCKpin = A5;
    var MOSIpin = A7;
    
    require("Font8x12").add(Graphics);
    
    // -- modded module --
    var exports = {};
    
    var LCD_WIDTH = 128;
    var LCD_HEIGHT = 128;
    
    var colStart = 2;
    var rowStart = 3;
    
    function init(spi, dc, ce, rst, callback) {
      function cmd(c,d) {
        dc.reset();
        spi.write(c, ce);
        if (d!==undefined) {
          dc.set();
          spi.write(d, ce);
        }
      }
      
      if (rst) {
        digitalPulse(rst,0,10);
      } else {
        cmd(0x01); //Software reset
      }
      setTimeout(function() {
        cmd(0x11); //Exit Sleep
        setTimeout(function() {
          cmd(0x26, 0x04); //Set Default Gamma
          //cmd(0xF2, 0x00) //E0h & E1h Enable/Disable ( from ILI9163AN datasheet - Laibo1.8" / CMO1.8" )
          cmd(0xB1, [0x0e,0x10]); //Set Frame Rate -> default
          //cmd(0xB1, [0x0C,0x14]); //Set Frame Rate ( from ILI9163AN datasheet - Laibo1.8" / CMO1.8" )
          cmd(0xC0, [0x08,0]); //Set VRH1[4:0] & VC[2:0] for VCI1 & GVDD
          //cmd(0xC0, [0x0C,0x05]); // Set VRH1[4:0] & VC[2:0] for VCI1 & GVDD ( from ILI9163AN datasheet - Laibo1.8" / CMO1.8" )
          cmd(0xC1, 0x05); //Set BT[2:0] for AVDD & VCL & VGH & VGL
          //cmd(0xC1, 0x02); //Set BT[2:0] for AVDD & VCL & VGH & VGL ( from ILI9163AN datasheet )
          cmd(0xC5, [0x38,0x40]); //Set VMH[6:0] & VML[6:0] for VOMH & VCOML
          //cmd(0xC5, [0x32,0x3B]); //Set VMH[6:0] & VML[6:0] for VOMH & VCOML ( from ILI9163AN datasheet - Laibo1.8" )
          ////cmd(0xC5, [0x29,0x43]); //Set VMH[6:0] & VML[6:0] for VOMH & VCOML ( from ILI9163AN datasheet - CMO1.8" )
          //cmd(0xC7, 0x40); // ??? ( from ILI9163AN datasheet )
          
          cmd(0x3a, 5); //Set Color Format, 5=16 bit,3=12 bit
          //cmd(0x36, 0xc8); //RGB - nope, on my screen its BRG -> produces artifacts
          cmd(0x36, 0xce); //RGB - nope, on my screen its BRG -> same, less artifacts
          
          cmd(0x2A,[0,0,0,LCD_WIDTH]); //Set Column Address R: digg if we can set the offset only here instead of within 3 fcns
          //cmd(0x2A,[0,0,0,0x7F]); //Set Column Address 0x7F = 127 ( from ILI9163AN datasheet )
          cmd(0x2B,[0,0,0,LCD_HEIGHT]); //Set Page Address R: digg if we can set the offset only here instead of within 3 fcns
          //cmd(0x2B,[0,0,0,0x9F]); //Set Page Address 0x9F = 135 ( from ILI9163AN datasheet )
          
          //cmd(0x36, 0xC0); // Set Scanning Direction ( from ILI9163AN datasheet - Laibo1.8" / CMO1.8" )
          
          cmd(0xB4, 0); // display inversion
          
          //cmd(0xB7, 0x00); // Set Source Output Direction ( from ILI9163AN datasheet - CMO1.8" )
          cmd(0xf2, 1); //Enable Gamma bit
          cmd(0xE0,[0x3f,0x22,0x20,0x30,0x29,0x0c,­0x4e,0xb7,0x3c,0x19,0x22,0x1e,0x02,0x01,­0x00]); 
          cmd(0xE1,[0x00,0x1b,0x1f,0x0f,0x16,0x13,­0x31,0x84,0x43,0x06,0x1d,0x21,0x3d,0x3e,­0x3f]);
          cmd(0x29); // Display On
          cmd(0x2C); // reset frame ptr      
          if (callback) callback();
        },20);
      } ,100);
    }
    // modded 'connect()' to fix the offset & pixel artifacts
    exports.connect = function(spi, dc, ce, rst, callback) {
      var g = Graphics.createCallback(LCD_WIDTH, LCD_HEIGHT, 16, {
        setPixel:function(x,y,c){
          ce.reset();
          spi.write(0x2A,dc);
          //spi.write(0,x,0,x+1); // default
          spi.write(0,x+colStart,0,x+colStart+1);
          spi.write(0x2B,dc);
          //spi.write(0,y,0,y+1); // default
          spi.write(0,y+rowStart,0,y+rowStart+1);
          spi.write(0x2C,dc);
          spi.write(c>>8,c);
          ce.set();
        },
        fillRect:function(x1,y1,x2,y2,c){
          ce.reset();
          spi.write(0x2A,dc);
          //spi.write(0,x1,0,x2); // default
          spi.write(0,x1+colStart,0,x2+colStart);
          spi.write(0x2B,dc);
          //spi.write(0,y1,0,y2); // default
          spi.write(0,y1+rowStart,0,y2+rowStart);
          spi.write(0x2C,dc);
          //spi.write({data:String.fromCharCode(c>­>8,c), count:(x2-x1+1)*(y2-y1+1)}); // default
          spi.write({data:String.fromCharCode(c>>8­,c), count:((x2+colStart)-(x1+colStart)+1)*((­y2+rowStart)-(y1+rowStart)+1)}); // USELESS ?! -> test with above
          ce.set();
        }
      });
      // quick & hacky fix
      g.flip = function() { /*prevents error during my quick tests*/ };
      init(spi, dc, ce, rst, callback);
      return g;
    };
    // modded 'connectPaletted()' to fix 'italic" text
    exports.connectPaletted = function(palette, spi, dc, ce, rst, callback) {
      //var bits = 8;
      /**/
      var bits;
      if (palette.length>16) bits=8;
      else if (palette.length>4) bits=4;
      else if (palette.length>2) bits=2;
      else bits=1;
      /**/
      var g = Graphics.createArrayBuffer(LCD_WIDTH, LCD_HEIGHT, bits, { msb:true, vertical_byte: false });
      g.flip = function() {
        ce.reset();
        spi.write(0x2A,dc);
        //spi.write(0,0,0,LCD_WIDTH); // default - otherwise, we had spi.write(0,x+colStart,0,x+colStart+1);
        //spi.write(0,2,0,LCD_WIDTH); // thx to @Jorgen who inspired this - italic but other orientation
        spi.write(0,1,0,LCD_WIDTH); // WORKS: made the trick for non-italic text drawn at once ! :D
        spi.write(0x2B,dc);
        spi.write(0,1,0,LCD_HEIGHT); // otherwise, we had spi.write(0,y+rowStart,0,y+rowStart+1);
        //spi.write(0,3,0,LCD_HEIGHT); // thx to @Jorgen who inspired this - italic but other orientation
        spi.write(0x2C,dc);
        var lines = 16; // size of buffer to use for un-paletting
        var a = new Uint16Array(LCD_WIDTH*lines); // default
        for (var y=0;y<LCD_HEIGHT;y+=lines) { // default
          E.mapInPlace(new Uint8Array(g.buffer, y*LCD_WIDTH*bits/8, a.length), a, palette, bits); // default
          spi.write(a.buffer);
        }
        ce.set();
      };
      
      /* ---- HUGE WIP: PARTIAL FLIP USING GETMODIFIED ---- */
      // wip usage: g.flipP(g.getModified())
      g.flipP = function(options) {
       // check if modified
      //var tmp = g.getModified();
      var tmp = g.getModified(true); // check & clear for update if so
      if(typeof tmp === "undefined"){
        // nothing to update: force ?
        console.log('nothing to flip');
      } else {
        console.log('flipping modified stuff ..');
        console.log(tmp);
        if(typeof options === "undefined") options = {};
        var y1 = options.y1 || tmp.y1 || 0;
        var y2 = options.y2 || tmp.y2 || LCD_HEIGHT;
        var x1 = options.x1 || tmp.x1 || 0;
        var x2 = options.x2 || tmp.x2 || LCD_WIDTH;
        // -- TMP OVERRIDE --
        //x1 = 0;
        //x2= 11;
        //x2 = LCD_WIDTH; // => COMMENT OUT FOR PARTIAL FLIP WITHOUT LINE CHUNKS
        //x2 = LCD_WIDTH/4;
        //x2 = LCD_WIDTH/2;
        var twidth = x2-x1;
        var theight = y2-y1;
        /*
        var tpxNum = twidth*theight;
        //var tbits = tpxNum*16/5; // may not be always tru ? ( found while trying to make sense of debug pokes values ..)
        //var tbits = tpxNum*bits/8; //same as tpxNum*16/8;
        var tbits = tpxNum*bits;
        //var tbits2 = tpxNum*(16-theight);
        var tbytes = tbits/8;
        console.log('area width: '+twidth+'\nheight: '+theight+'\npixels count: '+tpxNum+'\nbits / bytes: '+tbits+' / ' +tbytes); //+'\ntbits2: '+tbits2);
        */
        
        ce.reset();
        
        spi.write(0x2A,dc);
        //spi.write(0,x1+1,0,(x2-x1));
        //spi.write(0,x1+1+colStart,0,(x2-x1)+co­lStart);
        //spi.write(0,x1+1+colStart,0,twidth+col­Start);
        spi.write(0,x1+1+colStart,0,twidth+colSt­art);
        
        spi.write(0x2B,dc);
        //spi.write(0,y1+1,-2,(y2-y1)+1);
        spi.write(0,y1+rowStart,0,theight+rowSta­rt);
        
        spi.write(0x2C,dc);
        var lines = 1; // gives us 19 loops for the two squares to be drawn ( these totalize 20 in height ) & 256 bits buffer
        //var lines = 16; // => COMMENT OUT FOR PARTIAL FLIP WITHOUT LINE CHUNKS
        //var lines = 10; // => COMMENT OUT FOR PARTIAL FLIP WITHOUT LINE CHUNKS
        var a = new Uint16Array(twidth*lines);
        
        // debug bits counter
        //var bitsCntr = 0;
        
        for (var y=0;y<theight;y+=lines) { // shall stay as is !!
          //E.mapInPlace(new Uint8Array(g.buffer, y1*LCD_WIDTH*bits/8 + y*LCD_WIDTH*bits/8, a.length), a, palette, bits);
          E.mapInPlace(new Uint8Array(g.buffer, y1*LCD_WIDTH*bits/8 + y*LCD_WIDTH*bits/8+ x1*bits/8, a.length), a, palette, bits); // WORKS WITH lines=1
          
          spi.write(a.buffer);
          //bitsCntr += a.buffer.length;
          //console.log('y = '+y+' -> bitsCntr + ' + a.buffer.length + " = " + bitsCntr);
        }
        
        // ---- trying to optimize stuff via "compiled" ----
        /*
        //loopSendBufferChunks(a, x1, y1, theight, bits); // trying to get faster using "compiled" helper to loop over n lines, map & send over spi
        for (var y=0;y<theight;y+=lines) { // shall stay as is !!
          //E.mapInPlace(new Uint8Array(g.buffer, y1*LCD_WIDTH*bits/8 + y*LCD_WIDTH*bits/8, a.length), a, palette, bits);
          var arr = new Uint8Array(g.buffer, y1*LCD_WIDTH*bits/8 + y*LCD_WIDTH*bits/8+ x1*bits/8, a.length);
          mapAndSend(arr, a, bits, palette); // no gain whatsoever :/ ..
          //bitsCntr += a.buffer.length;
          //console.log('y = '+y+' -> bitsCntr + ' + a.buffer.length + " = " + bitsCntr);
        }
        */
        ce.set();
        //console.log('bits written ' + bitsCntr);
      }
      };
      
      
      init(spi, dc, ce, rst, callback);
      return g;
    };
    
    // compiled js helper to decrease transfer time ?
    function mapAndSend(arr, a, bits, palette){
      "compiled";
      E.mapInPlace(arr, a, palette, bits); // WORKS WITH lines=1
      spi.write(a.buffer);
    }
    function createNewArr(offset, length){
      return new Uint8Array(g.buffer, y1*LCD_WIDTH*bits/8 + y*LCD_WIDTH*bits/8+ x1*bits/8, a.length);
    }
    function loopSendBufferChunks(a, x1, y1, height, bits){
      "compiled";
      var sp = spi; // assign to local variable so no name lookup on each call
      for (var y=0;y<height;y+=lines) {
        //E.mapInPlace(new Uint8Array(g.buffer, y1*LCD_WIDTH*bits/8 + y*LCD_WIDTH*bits/8+ x1*bits/8, a.length), a, palette, bits);
        var arr = createNewArr(y1*LCD_WIDTH*bits/8 + y*LCD_WIDTH*bits/8+ x1*bits/8, a.length); // create new array externally to "compiled" code ?
        E.mapInPlace(arr, a, palette, bits);
        spi.write(a.buffer);
      }
    }
    
    // ---- test code ----
    var spi = new SPI();
    //spi.setup({mosi:MOSIpin /* sda */, sck:SCKpin /* scl */, baud: 1000000});
    spi.setup({mosi:MOSIpin /* sda */, sck:SCKpin /* scl */, baud: 16000000});
    //spi.setup({mosi:MOSIpin /* sda */, sck:SCKpin /* scl */, baud: 100000});
    //spi.setup({mosi:MOSIpin /* sda */, sck:SCKpin /* scl */});
    
    //var colorPalette = new Uint16Array([0, 0xf8, 0xe007, 0x1f00]); // black, red, green ,blue, finally ? ... YES !!
    // the following helper does the RGB888 -> GBRG565 mapping trick
    // wip fix: remapping the RGB565 as
    // GGGBBBBBRRRRRGGG: ( weird 'GBRG' ?)
    // G3,G2,G1,  B5,B4,B4,B2,B1,  R5,R4,R3,R2,R1,  G6,G5,G4
    //      LSB   MSB        LSB   MSB        LSB   MSB
    function rgb888To565t2(rgb888){
      return ((rgb888 & 0x1c00) << 3)|((rgb888 & 0xf8) << 5)|((rgb888 & 0xf80000) >> 16)|((rgb888 & 0xe000) >> 13);
    }
    var colMap = rgb888To565t2;
    var colorPalette = new Uint16Array([0, colMap(0xff0000), colMap(0x00ff00), colMap(0x0000ff)]); // black, red, green ,blue, finally ? ... YES !!
    
    //var g = exports.connect(spi, DCpin, CSpin, RESpin, function() { // works fine
    var g = exports.connectPaletted(colorPalette, spi, DCpin, CSpin, RESpin, function() {
      g.clear();
      g.flip(); // needed when using 'paletted' mode
      g.setFont8x12(); // perfect size for 128x128 :)
      g.setColor(1);
      g.drawString("should be red !", 2, 2);
      g.setColor(2);
      g.drawString("should be green !", 2, 20);
      g.setColor(3);
      g.drawString("should be blue !", 2, 40);
      g.flip(); // needed when using 'paletted' mode
      
      // from now, assume that nothing new was drawn
      g.setColor(3);
      //g.fillRect(0,10, 10, 20);
      g.fillRect(2,10, 12, 20);
      g.flip();
      
      g.getModified(true);
      g.flipP(); // 'll try clearing modified & flipping stuff if found anything -> should say 'nothing to update'
      setTimeout(function(){
        g.setColor(1);
        //g.setColor(1); // modifies stuff, even if its blanking it ..
        //g.fillRect(0,0, 10, 10);
        
        //testing y on flipP
        //g.fillRect(0,50, 10, 60);
        
        //testing x on flipP
        //g.setColor(2);
        //g.fillRect(10,50+10, 20, 60+10); // the +10 forces a second loop (20 > 16, the un-paletting buffer size )
        
        //g.fillRect(10+26,50+6, 20+26, 60+6);
        
        // benchmarking partial flip with lines chunks VS partial flip, to know when one becomes faster than the other ( both should be faster than flip ;) )
        //g.fillRect(0,50, LCD_WIDTH/4, 60); // 32px for 128px screen: 57ms for 'line chunk' version, 179.252ms for 16lines pflip
        //g.fillRect(0,50, LCD_WIDTH/3, 60); // 32px for 128px screen: 57ms for 'line chunk' version, 179.295ms for 16lines pflip, 113.227ms for 10lines pflip
        //g.fillRect(0,50, LCD_WIDTH/2, 60); // 32px for 128px screen: 83.032ms for 'line chunk' version, 113.348ms for 10lines pflip
        g.fillRect(0,50, LCD_WIDTH-1, 60); // 32px for 128px screen: 133.300ms for 'line chunk' version, 113.680ms for 10lines pflip
        
        //g.drawString("HELLO FLIP-P", 2, 30);
        //g.flip();
        var bFlipT = getTime();
        g.flipP(); // 'll try clearing modified & flipping stuff if found anything -> should update stuff CORRECTLY ? ..
        var aFlipT = getTime();
        console.log('partial flip took ' + (aFlipT-bFlipT)*1000+ 'ms');
        //console.log('partial flip took ' + (aFlipT-bFlipT)+ 's');
      },2000);
      
    });
    
About

Avatar for stephaneAG @stephaneAG started