• ADDED 2021/10/04: ...great help for coding the underlaying ILI9341 display controller

    The moment I did some basic setup, set some pixel and drew some lines, I knew that performance is a challenge... especially when driving the display over SPI... Therefore, graphics take a bit a back seat vs. simplification. ;-)

    Here some basic code:

    // Exploring 2.8" Color TFT Touch LCD by Pacman Game attempt 
    
    // [0r0] - [1v67] release / version info
    
    // basic setup: light on, SPI, graphics context
    B2.set();
    SPI1.setup({sck:B3, miso:B4, mosi:B5, baud: 1000000});
    var g = require("ILI9341").connect(SPI1, B6, B8, B7, function() {});
    
    var G = // Game
    { g: null  // Grapics
    , c: function() { this.g.clear(); } // clear
    , clrs:    // colors (6 bit color depth, 262K colors)
        [ 0    // black
        , 63 * (64*64) + 63 * (64) +  0 // yellow
        ,  0 * (64*64) +  0 * (64) + 63 // blue
        , 63 * (64*64) + 63 * (64) + 63 // white
        ]
    , clr: null // color index
    , sClr: function(cdx) {
        if (this.clr != this.clrs[cdx]) {
          this.clr = this.clrs[cdx];
          this.g.setColor(this.clr);
        }
      }
    , ini: function(g) {
        this.g = g;
        this.c();
      }
    };
    
    var P = // Pacman
    { g: null // Graphics
    , x: 0    // pos x
    , y: 0    // pos y
    , d: null // direction E, S, W, N phases start = 0, 4, 8, and 12) 
    , s: 0    // next state 0..15, show and hide states for mouth closed and open
              // advancing halft a cell with before showing
              // (0,4,8,12 = on cell center, mouth closed, facing E,S,W,N)
    , f: null // function for each state (0..15)
    , w: 10   // with
    , b:  6   // base
    , o:  8   // open
    , c:  3   // closed
    , sp: function() {}
    , se: function(i,m) {
      G.sClr(i);
      g.drawLine(this.x - this.b, this.y, this.x + this.b, this.y - m);
      g.drawLine(this.x - this.b, this.y, this.x + this.b, this.y + m);
     }
    , ss: function(i,m) {
      G.sClr(i);
      g.drawLine(this.x, this.y - this.b, this.x - m, this.y + this.b);
      g.drawLine(this.x, this.y - this.b, this.x + m, this.y + this.b);
     }
    , sw: function(i,m) {
      G.sClr(i);
      g.drawLine(this.x + this.b, this.y, this.x - this.b, this.y - m);
      g.drawLine(this.x + this.b, this.y, this.x - this.b, this.y + m);
     }
    , sn: function(i,m) {
      G.sClr(i);
      g.drawLine(this.x, this.y + this.b, this.x + m, this.y - this.b);
      g.drawLine(this.x, this.y + this.b, this.x - m, this.y - this.b);
     }
    , s0: function(s,x,y) {
      this.s = s;
      this.x = (s == 0) ? x - this.w : (s ==  8) ? x + this.w : x;
      this.y = (s == 4) ? y - this.w : (s == 12) ? y + this.w : y;
      this.f[this.s]();
    }
    , n: function() {
      this.f[this.s]();
      this.f[this.s]();
      }
    , ini: function(g) {
      this.g = g;
      var _this = this;
      this.f =
        [ function() { _this.x = _this.x + _this.w; 
                       _this.se(1,_this.c); _this.s =  1; }
        , function() { _this.se(0,_this.c); _this.s =  2; } // 1/2
        , function() { _this.x = _this.x + _this.w; 
                       _this.se(1,_this.o); _this.s =  3; }
        , function() { _this.se(0,_this.o); _this.s =  0; } // 1/1 E
        , function() { _this.y = _this.y + _this.w; 
                       _this.ss(1,_this.c); _this.s =  5; }
        , function() { _this.ss(0,_this.c); _this.s =  6; } // 1/2
        , function() { _this.y = _this.y + _this.w; 
                       _this.ss(1,_this.o); _this.s =  7; }
        , function() { _this.ss(0,_this.o); _this.s =  4; } // 1/1 S
        , function() { _this.x = _this.x - _this.w; 
                       _this.sw(1,_this.c); _this.s =  9; }
        , function() { _this.sw(0,_this.c); _this.s = 10; } // 1/2
        , function() { _this.x = _this.x - _this.w; 
                       _this.sw(1,_this.o); _this.s = 11; }
        , function() { _this.sw(0,_this.o); _this.s =  8; } // 1/1 W
        , function() { _this.y = _this.y - _this.w; 
                       _this.sn(1,_this.c); _this.s = 13; }
        , function() { _this.sn(0,_this.c); _this.s = 14; } // 1/2
        , function() { _this.y = _this.y - _this.w; 
                       _this.sn(1,_this.o); _this.s = 15; }
        , function() { _this.sn(0,_this.o); _this.s = 12; } // 1/1 N
      ];
     }
    };
    
    setTimeout(function(){
        G.ini(g);
        P.ini(g);
      },1500);
    
    var tt = 100;         // [ms] timout time for next phase, 4 phases per cell)
    var x0 = 20, y0 = 70; // left, top (left top corner of square w/ nmx cells) 
    var n, nmx = 10;      // n = cell, nmx = max cells (in x and y direction)
    
    // pacman walking E, S, W, N:
    function ne() { P.s0( 0,x0        ,y0        ); n0(); }
    function ns() { P.s0( 4,x0+nmx*P.w,y0        ); n0(); }
    function nw() { P.s0( 8,x0+nmx*P.w,y0+nmx*P.w); n0(); }
    function nn() { P.s0(12,x0        ,y0+nmx*P.w); n0(); }
    
    function n0() { n = 0; setTimeout("nx();",tt); }
    function nx() { 
      if (n < nmx) { n++;
        P.n();
        setTimeout("nx();",tt);
      } else {
        P.f[P.s]();
      }
     }
    

    To let Pacman walk E (x-axis), S (y-axis), W, or N, enter in IDE's command window (one at a time):

    • ne();
    • ns();
    • nw();
    • nn();

    [1of2] to be continued... Exploring 2.8" Color TFT Touch LCD by Pacman Game attempt

  • [2of2] continued Exploring 2.8" Color TFT Touch LCD by Pacman Game attempt

    Add this code to the initial one for having pacman continually walking around ins a square.

    // pacman walking around in a square (until w is set to false in cmd pane):
    var d, w;
    function r() {
      G.c();
      G.sClr(2);
      G.g.drawRect(
          x0 - (Math.round(P.w/2) + P.b) , y0 - (Math.round(P.w / 2) + P.b)
        , x0 + ((nmx + 1) * P.w   + 1  ) , y0 + ((nmx + 1) * P.w     + 1  )
        );
      G.g.drawRect(
          x0 + (Math.round(P.w/2) + P.b) , y0 + (Math.round(P.w / 2) + P.b)
        , x0 + ((nmx - 1) * P.w   - 1  ) , y0 + ((nmx - 1) * P.w     - 1  )
        );
      d = 0; // start direction (E) and ...
      P.s0(d,x0,y0); // start position
      n = 0;
      w = true;
      rx();
    }
    
    function rx() {
      if (n < nmx) { n++;
        P.n();
        setTimeout("rx();",tt);
      } else {
        P.f[P.s]();
        if (w) {
          d = (d < 12) ? d + 4 : 0;
          P.s0(d,P.x,P.y);
          n = 0;
          setTimeout("rx();",1);
        }
      }
     }
    

    In IDE's command / console window enter:

    • r(); // starts the walking in the NW corner
    • w = false; // stops the walking at the next corner



    PS: I used the 2.8" TFT LCD with Touchscreen Breakout Board w/MicroSD Socket from http://www.adafruit.com/products/1770 and wired according to http://www.espruino.com/ILI9341 Note that B2 goes to Lite on board (LED on module). See picture and watch (short, low-res pacmanvideo.mp4) movie...


    3 Attachments

  • Cool! What kind of speed does it update? Do you have a video?

    Not sure, but you might find that the drawImage functionality (with 1 bit graphics) is relatively speedy for stuff like the PacMan icon.

    It's a shame the bandwidth is so limited. Speed could definitely be improved if the driver wasn't written in pure JS, but even so it's never going to be amazing.

  • Was working on getting a video going... don't have the hang on focus yet... ;) ...will get there. Movie is now added.

    Regarding the speed: operation is event/timer driven. Operation is basically a continuous repetition of 4 phases:

    1. advance half a cell, draw two lines with small angle
    2. 'undraw' w/ background color what was drawn in previous phase
    3. advance half a cell, draw two lines with wide angle
    4. 'undraw' w/ background color what was drawn in previous phase

    The phases are optimized for the seamless, timer driven repetition in pairs of two with no logic in them what so over... all logic happens by 'direct' and indirect addressing... (invocation of functions - 1 per phase - stored in an array and called by a cycling index and index for next phase set in the function).

    Because of the optimization, a start and stop cycle exist that compensate where needed (for now only the stop):

    1. Start cycle - is a phase 1 (or 3) starting from a x/y position compensated by the 'advance half a cell'
    2. Enter repetition mode using a timeout which then executes either phase 2. and 3. or 4. and 1. and calls itself in the next timeout.
    3. Stop cycle - is the phase to remove the last drawing by the undraw, which is either a phase 2. or 4.

    Each phase sets the next phase index so that on timeout the next function in the cycle is called (indexes in this lists off by 1+):

    1. phase 1 sets index to 2.
    2. phase 2 sets index to 3.
    3. phase 3 sets index to 4.
    4. phase 4 sets index back to 1

    Since each direction - E, S, W, N - have all their own implementations, they have all their own cycles - but all phases are stored in the same array:

    • E. - 1, 2, 3, 4
    • S. - 5, 6, 7, 8
    • W. - 9, 10, 11, 12
    • N. - 13, 14, 15, 16

    In other words, every cycle's last phase sets the index for the next phase to the first phase of the cycle.

    The discussion 'where to draw the line' between high-level (language and device(s) agnostic) and low-level (language and device(s) dependent) application and driver implementation in the hybrid 'over all implementation' may take place in another post...

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Exploring 2.8" Color TFT Touch LCD by Pacman Game attempt

Posted by Avatar for allObjects @allObjects

Actions