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

    I stumbled upon this thread ( 3 years old -> cute ^^) while looking for fixes to drive the following screen correctly:­olor-128x160-SPI-TFT-LCD-Screen-Panel-re­place-OLED-for-Arduino/322569266920?ssPa­geName=STRK%3AMEBIDX%3AIT&_trksid=p20578­72.m2749.l2649

    The breakout uses an ST7735 driver, as @Jorgen's & as within the Espruino tutorial

    The issues:

    • not using the 'paletted' mode, I had "pixel artifacts" ( x & y offsets/borders with visuals glitches)
    • using the 'paletted' mode, I had text in italic ( ! ) & not aligned with the screen dimensions ( the part of "italic" text cut out on the left appeared on the right side of the screen)

    Many tries, but I couldn't get the above fixed without further modding the ILI9163 library ( fiddling with g.setRotation didn't fix the offset no the italic text :/ .. )
    After some time trying out stuff, I successfully fixed both issues ( yay ! ^^), and I think the fixes may as well be ported to other Espruino screen libraries ( to be as "generic" as possible while not increasing the libs drastically ;) )

    // Platform: EspruinoWiFi
    // pins used
    var screen_mosi_sda = A7;
    var screen_sck_scl = A6;
    var screen_dc = B1;
    var screen_cs = B13;
    var screen_rst = B10;
    var spi = new SPI();
    spi.setup({mosi: screen_mosi_sda, sck:screen_sck_scl});
    // -- modded module --
    var exports = {};
    // tweaks that fixed the offset wile not in 'palettes' mode, whatever the rotation ;)
    var colStart = 2;
    var rowStart = 3;
    // Nb: I added some commented out stuff ( from­7-ILI9163AN_V0_2.pdf )
    // but I don't have yet enough infos on what these do ( same as for some of @Jorgen's spa init stuff )
    function init(spi, dc, ce, rst, callback) {
      function cmd(c,d) {
        spi.write(c, ce);
        if (d!==undefined) {
          spi.write(d, ce);
      if (rst) {
      } 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
          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(0x29); // Display On
          cmd(0x2C); // reset frame ptr      
          if (callback) callback();
      } ,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, {
          //spi.write(0,x,0,x+1); // default
          //spi.write(0,y,0,y+1); // default
          //spi.write(0,x1,0,x2); // default
          //spi.write(0,y1,0,y2); // default
          //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)});
      // 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;
      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() {
        //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(0,0,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
        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
      init(spi, dc, ce, rst, callback);
      return g;
    // test code
    var colorPalette = new Uint16Array([BLACK, WHITE]); // instant refresh
    //var g = exports.connect(spi, screen_dc, screen_cs, screen_rst, function() { // works fine
    var g = exports.connectPaletted(colorPalette, spi, screen_dc, screen_cs, screen_rst, function() {
      g.setFont8x12(); // perfect size for 128x128 :)
      g.drawString("Troubles are fixed !", 2, 2);
      g.flip(); // needed when using 'paletted' mode

    From the above fixes, I don't know if a 'offset: { x: , y:}' could be added to an option object that could be passed while connecting to the screen ?

    This being said, I have few questions on the graphics module ( & I'd be glad to format & add the answers fetched to the graphics page ;) ):

    Q1: on screen colors & update speed for a "little bigger" chip, I was wondering If I could expect a nearly instant refresh for paletted ( & non paletted as well ? ) modes on this screen & the STM32F407VGT6 ( 1024kB flash, 192kB ram). 128*128*16bpp = 32768 bytes = 33kB -> ~159kB are ram left ? ( I bet this assumption is pretty naïve, since the amount of ram available to Espruino may not match the total amount of ram available or something of the same kind I'm not aware of ? .. )

    Q2: would a mode with 'non palettes zones' & other stuff 'palettes' be of any interest ?

    Q3: would a mod of the 'clear()' & 'flip()' methods accepting a rectangle x1,y1,x2,y2 be of any help ( to either clear or update only part of the screen ) ? ( then we could also have 'canvases' & do stuff like g.cnvs[0].clear(), .draw.. & maybe have x&y coords shifted by the top-left of such canvases - that may be rotated as well ? ;) )

    Q4: could 'dynamic italic text' be come a feature ? ( I'll add visuals from my tests to better illustrate this 'glitch-not-yet-a-feature' ;p )

    Q5: could an alternative behavior for the 'palettes' mode be handy ? ( if color is outa bounds, fallback to either black or white depending on a param passed or settings )

    Q6: best way to get 'instant drawing' for a (colored or not but likely ) logo ? I tried using setPixel() & timing the time taken to draw the entire screen with sort of a gradient ( see hacks in code below ), then just using fillPoly() & while the latter seems way more promising than loading setting every pixel, getting it into the right colors quickly drawn from an array is I think possible, yet I'm not sure yet how to achieve that ( by using E.mapInPlace() & Cie ? - I also wonder how loading an image is done - and I failed but 'll retry to getting the Espruino logo example loading as an image ;) )

    I currently have two possible scenarios of how to get that colored logo draw quickly ( 'll presumably happen on device boot or reboot - and may be used as a "background" while a loading bar is updated at the bottom - not sure how of much time a 'clearToBackground()' fcn would take ;p .. )


    • draw it, which 'll take some time ( setPixel(), fillPoly(), .. )
    • save buffer to a screen-size specific format ( uint16 array ? )
    • redraw it 'as fast as can do' by using E.mapInPlace() or any related helper


    • backlight off
    • draw logo ( not palleted mode - so we see it while it's being drawn, which may be cool depending on the logo ;p )
    • backlight on ( show logo )
    • transition to menu ( go back to using palettes mode as quick as can do, if possible not redrawing the screen while doing so - but we might as well backlight off ->connect() & then backlight on once menu is drawn )

    drawing to screen directly being slower than to memory then in-one-row to screen, I'm wondering how drawing from obj ( from an already painted in ArrayBuffer saved in flash ? ) to screen would preform ( I guess it'll take more time for more colors ) & how to minimize the ram used to do so ( if I get correctly the current paletted code, it send rows by rows what I'd like to load & send row-by-row ( to lessen ram used ), but I'm not sure how 'fillPoly()' is implemented & whether or not I may be able to just draw its points & then use 'ce.set' once all are drawn
    Also, I'm quite curious if saving then loading an array buffer to only the part of screen needed would be fast enough ..

    I'll try some stuff out & then post an update ;)

    here some hacks for the adventurous readers ;p

    // draw color tests before trying to fix the 'paletted' mode
    function drawColorGradient(){
      for(var x=0; x <= 127; x+=1){
      //var x=0;
        for(var y=0; y <= 127; y++){
          //g.setPixel(x, y, [0, x, y]);
          //g.setPixel(x, y, [0, 0.7, 0.7]); // hangs for more than 10 sec on espruino wifi & display nothing :/
          //g.setPixel(x, y, 0x53d); // R: WON'T WORK IF NOT PASSED AN INTEGER !
          // slowest hack ever ..
          //g.setColor(0, x/255, x/255); // don't know yt why it fails ..
          console.log('current color: ' + x/255 + ', ' + (y/255).toFixed(2));
          //g.setColor(0, x/255, y/255); // works fine
          //g.setColor(0, x*2/255, y*2/255); // works fine
          //g.setColor((x+y)/255, x*2/255, y*2/255); // works fine
          //g.setColor(1-(x+y)/255, x*2/255, y*2/255); // works fine
          //g.setColor(1-(x+y)/255, 1-(x*2/255), 1-(y*2/255)); // works fine
          //g.setColor(1-(x-y)/255, 1-(y*2/255), 1-(x*2/255)); // works fine
          g.setColor(1-(y-x)/255, y*2/255, 1-(x*2/255)); // works fine - & quite color full actually ;)
          //g.setColor(0, x/255, (y/255).toFixed(2)); // also works
          //g.setColor(0, 0.65, 0.92); // this works
          var color = g.getColor();
          g.setPixel(x, y, color);
      console.log('screen filed with colors !');
    // same as above but for paletted color
    function drawPaletteColorGradient(){
      var y = 20;
      for(var x=0; x <= colorPalette.length; x++){
        // slowest hack ever ..
        console.log('current color: ' + x/255 + ', ' + (y/255).toFixed(2));
        //g.setColor(1-(y-x)/255, y*2/255, 1-(x*2/255)); // works fine - & quite color full actually ;)
        //var color = g.getColor();
        //g.setPixel(x, y, color);
        //g.setPixel(x, y, colorPalette[x]);
        //g.drawLine(x, y, x, y+5, colorPalette[x]);
        var color = g.getColor();
        //g.drawLine(x, y, x, y+5, color);
        //g.setPixel(x, y, color);
        //g.setPixel(x, y, 0xF81F);
        g.setPixel(x, y, color);
        g.drawLine(x, y, x, y+5, color);
        g.drawLine(x, y+15, x, y+15, color);
      console.log('screen filed with colors !');
    // drawing the simplified logo
    / this is the kid of stuff I'd like drawn quickly in a row ;)
    function logo(xof, yof, sizeAdj){
      // Graphics.fillPoly(poly)
      // Graphics.drawPoly(poly, closed)
      //g.drawPoly([13.06,29.59 , 22.99,19.66 , 22.99,46.15 , 13.06,46.15], true);
      g.fillPoly([(xof+13.06)*sizeAdj,(yof+29.­59)*sizeAdj ,
                  (xof+22.99)*sizeAdj,(yof+19.66)*sizeAdj ,
                  (xof+22.99)*sizeAdj,(yof+46.15)*sizeAdj ,

Avatar for stephaneAG @stephaneAG started