Avatar for jeffmer

jeffmer

Member since Apr 2020 • Last active Nov 2020
  • 6 conversations
  • 73 comments

Most recent activity

  • in Projects
    Avatar for jeffmer

    A small update - I have now got double buffering to work with SPI flash. The solution is to introduce a simple spin lock which blocks any attempt to do a flash read until the DMA transfer has finished. I used the existing macro WAIT_UNTIL for the spin lock so that it can timeout if a DMA interrupt goes missing.

  • in Projects
    Avatar for jeffmer

    Many thanks, I assume, I flash the upgrade zip first and then I can flash normal Espruino builds as in BOARD=P8-SDK12 RELEASE=1 make ?

  • in Projects
    Avatar for jeffmer

    Having thought about it, I think the current implementation is probably the best you can do as for the reasons below, it is difficult/impossible to use double buffering. In addition, I think enabling and disabling the HW SPI is quite legitimate - it already happens to switch from DMA SPI to normal SPI to read single bytes. For some graphics screens, you have to do something similar to adjust to lower the bus speed for the touch controller if shares the display bus.

    Double Buffering

    My attempt at this - which works well when not using SPI flash - failed with a SPI timeout for - I think - the following reason. The driver starts a DMA operation to flush a buffer full of pixels and then returns to drawImage which accesses SPI flash to read the color palette. This read stops the DMA transfer with no completion interrupt so when it comes to try and flush the next buffer you get the SPI timeout.

    I do not think that there is a general solution- other than synchronous single buffering - to stopping the CPU accessing the SPI flash in the time available during the DMA transfer. I tried blocking the SPI flash read while the DMA is in operation but this causes a sort of deadlock - #drawImage cannot fill the next buffer because it is waiting for a spi flash read which cannot complete because it is waiting for the bus to be released.

    I have persevered with the driver as I would like to run different apps with different graphics requirements - you can see the result in the video below.

    https://youtu.be/cHj_lXSNEv0

    @fanoush - Many thanks for your invaluable help with this - looking forward to softdevice+bootloader upgrade package to move to SDK12 as I need the secure connection to connect my phone.

  • in Projects
    Avatar for jeffmer

    I think that would be worth investigating as I am not convinced that my current implementation is very robust. I assume the Bangle uses SW SPI as its the same NRF5x jshardware.c implementation. Is there any reason why you do not use HW SPI with DMA?

  • in Projects
    Avatar for jeffmer

    Thanks for your advice. After a little bit of a struggle, I have got my driver to work with the SPIFLASH board description. I added an enable/disable function:

    /** enable/disable SPI - needed for shared SPI pins between flash and device */
    void jshSPIEnable(IOEventFlags device, bool enable);
    

    to jshardware.c. You can see it here]: (https://github.com/jeffmer/Espruino/blob­/master/targets/nrf5x/jshardware.c) . I also put a weak implementation in jshardware_common.c. I am not sure if @Gordon will think this is something that might be included in the main repository. It would seem to be needed if you want to share SPI pins.

    Your board description only allocates 96 pages of the 4 Megabyte spi flash - is there any reason why we cannot use the whole 4M as with the Bangle?

  • in Projects
    Avatar for jeffmer

    The performance when I rebuilt with DMA enabled was:

    P8: fillRect 81ms, drawImage 154ms.

    which is much more reasonable. However, I have now implemented double buffering which with two 60 pixel (120 byte ) buffers gives:

    P8: fillRect 85ms, drawImage 111ms.

    There is a tradeoff in that smaller buffers allow more overlap but increase the time to draw the rectangle - important for fast g.clear().

    I would now like to get the driver to work with SPI Flash which I guess requires some care with the chip select pins?

  • in Projects
    Avatar for jeffmer

    Brilliant! Many thanks for your help. Its not enabled in targetlibs/nrf5x_11/nrf52_config/nrf_drv­_config.h. Will enable there, add to board file and rebuild.

    And thanks for clarifying SDK vs SoftDevice routines for me.

  • in Projects
    Avatar for jeffmer

    Thanks to the clear instructions, I had no problem building the P8 package. I also tested your driver which gives an impressive result of 73ms for the fill rectangle test - fill_time(). Admittedly, this is 12-bit but it would still be less than 100ms for 16-bit while the best that my lcd_spi_unbuf driver can do is 237ms.

    I accept your arguments about single pixel operations, however this performance difference is not caused by pixel operations, since both drivers are sending large buffers via EasyDMA. The difference seems to be caused by the fact that you have used a direct implementation of EasyDMA while the Espruino jshSPISendMany uses SoftDevice routines - including interrupt handling. Your implementation - similar to @atc1441's ATCWatch fastspi module does not use either the SoftDevice or interrupts. I am surprised that the Nordic routines seem to incur such a large overhead.

    BTW. Both Espruino and the ATCwatch module have workarounds for the 1 byte EasyDMA transfer bug while your implementation does not - yet it seems to work OK?

  • in Projects
    Avatar for jeffmer

    Did you test also g.flip of my driver? should be below 100ms for full 240x240 screen.

    I did not get around to testing it as I realised it was faster which makes me believe that it should be possible to speed up lcd_spi_unbuf which has the advantage of flexibility in terms of screen buffering.

    Many thanks for the information on building - I will have a go and get back to you. Look forward to SDK12 as I think the Apple ANCS widget requires secure connections.

  • in Projects
    Avatar for jeffmer

    Thanks, I managed to get my P8 flashed with Espruino with no problems. I did the following measurements for the Lcd_spi_unbuf driver.

    I compared the time taken to fill a 240 x 160 pixel rectangle with the time to draw a 240 x 160 1 bit image. I include the results for Bangle and ESP32 (T-watch for comparison.

    RESULTS

    Bangle: fillRect 11ms, drawImage 66ms.

    ESP32: fillRect 44ms, drawImage 82ms.

    P8: fillRect 256ms, drawImage 331ms.

    The speed is not great, however, its is worth noting that the issue is not rendering palleted images but it is simply getting pixels sent to the driver. There are at least two improvements I can think of:

    1) The buffer at 128 (256 bytes) is nearly exactly the wrong size as the implementation of spiSendMany uses EasyDMA with a maximum transfer size of 255 bytes i.e. the current buffer size causes two transfers, the second of 1 byte. I would like to try a buffer size of 240 (480 bytes).

    2) spiSendMany is currently used synchronously in spiSendMany so it would be interesting to try double buffering to speed things up.

    I would really like to be able to build the firmware to test this and also to add a Bluetooth hack to support my ANCS widget. I see that you have made the board description public but I would guess that you also need the other files (bootloader etc) to build it?

    Thanks agian for making the package available - the P8 has a bright display and its good to experiment with the touchscreen. I found it really easy to transfer the apps I have been running on the T-watch to the P8. Will share when I clean things up.

    function time_fill(){
        g.setColor(0x07E0);
        var time= Date.now();
        g.fillRect(0,40,239,199);
        g.flip();
        time = Math.floor(Date.now()-time);
        console.log("Time to Draw Rectangle: "+time+"ms");
    }
    
    var pal1color = new Uint16Array([0x0000,0xF100]);
    var buf = Graphics.createArrayBuffer(240,160,1,{ms­b:true});
    buf.setColor(1);
    buf.fillRect(0,0,239,159);
    
    function time_image(){
        var time= Date.now();
        g.drawImage({width:240,height:160,bpp:1,­buffer:buf.buffer, palette:pal1color},0,40);
        g.flip();
        time = Math.floor(Date.now()-time);
        console.log("Time to Draw Image: "+time+"ms");
    }
    
Actions