• The P8 smartwatch is good one to hack and put Espruino onto it. It can be updated without taking apart and is quite cheap ans has full touchscreen and 240x240 ST7789 display.

    Here is demo of Espruino build with display driver implemented as Inline C code.


    While there is already support for this display for banglejs, that driver is not SPI based. There is another one for previous F5 watch here but that one has framebuffer as static variables. I tried to update it to banglejs driver style but the whole "1. build espruino, 2. flash to watch, 3. see it crashing , 4. repeat" cycle was too slow so I first tried to prototype it in javascript and inline C and see ho far I could go with this.

    This uses DMA in similar way as the spilcd driver but was written from scratch without using nordic SDK to learn about ST7789 and nrf52 EasyDMA and SPI hardware (first version just bitbanged SPI RXD and TXD registers without using DMA).

    BTW it is interesting how much JS interpreter is slower, try to use g.jsflip and see the difference, the code is otherwise the same. While Espruino performance is otherwise OK here it really struggles.

    SDK14 and SDK11 based builds are here with some instructions.

    Oh and BTW thanks for cube animation, it was stolen from pixl.js conference badge code

  • Very impressive indeed. How does the speed of the SPI display compare with the 8-bit parallel Bangle version?

  • I don't know as I did not see both next to each other, but previously I thought 8Mbps SPI with 240x240 must be pathetic (see this forum post and the video there) but this is not so bad.

    There is no DMA for 8-bit parallel mode so the code must do both at once - palette conversion and/or scaling and sending to display. Wth DMA you can compute first block of pixels in advance and then both can run in parallel. With larger blocks than just few bytes the DMA could amost reach maximum theoretical 8Mbps speed. So e.g filling 240x240 in 12 bits (1.5bytes per pixel) takes 96ms when using dma in 24byte blocks.

    Even if there is mode when the DMA does not stop and can send several buffers (or same buffer) repeatedly on its own automatically, for some reason letting it loop over 6 byte buffers or 24byte buffers in such mode is a difference. With just the 6 byte buffer in code above it it was still like 125ms ~= same as sending it without DMA, and the 3byte buffer was even slower.

  • Also this was first meant as quick prototype to try various methods of sending data quickly, I am a bit surprised it can be probably used as standalone driver as is. But of course proper way is to merge it with driver code https://github.com/espruino/Espruino/blo­b/master/libs/graphics/lcd_st7789_8bit.c­ and possibly just change macros/methods here to put same data into some buffer and trigger DMA when buffer is filled istead of using 8bit mode.

  • The rotating cube looks quite fast - fast enough to use as you note. With hardware supported SPI, I have previously managed to get quite good performance for a relatively simple Arduino based ILI9341 driver - see.

  • ILI9341 driver - see.

    Oh, nice, thank you for linking that, the canvas idea with different smaller areas of different bit depth is interesting and could save memory. I think that is what @Gordon was adding to Espruino in recent days? Then you don't need to have whole framebuffer (28K in my case for 16 color 4bit one) and could blit such areas directly. E.g. for fonts there is otherwise no other way than to draw each pixel separately which is very slow as seen in the simple driver example here - that one was very painful to see on P8.

    Not sure what it will do with memory fragmentation to create such (relatively large) memory blocks dynamically. With one framebuffer you create it once on the beginning. And with around 300 variables free I am experiencing issues to get small flat strings created, this gets triggered sometimes unless I put the code to flash.

    The rotating cube looks quite fast

    Oh, that one is even fully drawn in javascript running via setInterval() so that the device is still responsive.

  • Yes, Gordon’s createArrayBuffer and drawImage with palettes are exactly the same mechanism. Actually you can usually get away with one or two buffers and move them around to write at different times. My gpsnav app uses only one buffer which is used every 200ms to draw the compass display and once a second for numerical data display.

    Combining javascript and compiled C in your driver is really neat, I was going to ask to see it!

  • That looks really cool! Actually I believe there is already code to handle ST7789 via SPI (with DMA!) in Espruino, since it was used on the ID205 that I'd been considering for Bangle.js: https://github.com/espruino/Espruino/blo­b/master/libs/graphics/lcd_spilcd.c

    However, that uses an offscreen buffer - which worked on the nRF52840, but is probably not so great on the nRF52832 :)

    It'd be really cool to pull something like this into Espruino itself - actually sending individual pixels but via DMA looks really interesting performance-wise.

    There are some hacks you can steal from the existing 8 bit driver too - like detecting if you're sending to X,Y then X+1,Y and not sending new XY coordinates each time.

  • Just a followup. I also got the 16bit mode working and it is visibly slower. 30ms more is really visible difference. Also rewrote the framebuffer/palette lookup reading code so that I can have 3-bit 8 color mode (pixels not aligned to byte boundary). Unfortunately Espruino does not support that, learned after it was already done. After adding bpp=3 to isValidBPP here https://github.com/espruino/Espruino/blo­b/master/libs/graphics/jswrap_graphics.c­#L181 it almost works, just have some wrong thin blank vertical stripes where the byte boundary is crossed. will check how easily it can be fixed.

    Also as for colors I tried some dynamic palette for bpp=1 mode so I could change colors between flips - to have multiple colors with only 1 bit framebuffer. And it can work like that for many things without a need for full 2 or 4 bpp. With 1bpp the first cube rotating screen and then second shape fill screen look almost same as before. Only random lines show some nice ZX Spectrum style color artifacts :-)

    Current code is still here also briefly described how one could self host the compiler https://github.com/fanoush/ds-d6/wiki/Es­pruino-Inline-C if you want to modify it.

    It is actually pretty nice environment with inline c, one could upload code quickly without even touching the flash memory and try anything freely. With watchdog enabled bugs are harmless. Thinking about exporting unused interrupt vectors from Espruino as this is currently clear limitation of inline C - I cannot hook into SPI interrupt in the driver now. Something like E.set/getInterruptVector(intNo) would do. Any vector unused by current espruino build could be exported like that.

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

Espruino on P8 smartwatch - ST7789 display driver in Inline C

Posted by Avatar for fanoush @fanoush