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/datasheet/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)+colStart);
//spi.write(0,x1+1+colStart,0,twidth+colStart);
spi.write(0,x1+1+colStart,0,twidth+colStart);
spi.write(0x2B,dc);
//spi.write(0,y1+1,-2,(y2-y1)+1);
spi.write(0,y1+rowStart,0,theight+rowStart);
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);
});
Espruino is a JavaScript interpreter for low-power Microcontrollers. This site is both a support community for Espruino and a place to share what you are working on.
hi there !
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