SPI flash odd behaviour

Posted on
  • I have Espruino running on a DT28 smartwatch (NRF52832) with its display removed. I've soldered wires to the main board to run an e-ink display, which works fine. The watch has an 8MB SPI flash chip which I've compiled in and it works, but with power issues. My first pass I was getting nothing back with every first call (call .list() get nothing, call it again, get file list). This sounded like it needed to be woken up, so I added the flag 'DEFINES += -DSPIFLASH_SLEEP_CMD', # SPI flash needs to be explicitly slept and woken up and it that stopped the errors, but now my battery dies in about 2 days (as opposed to 10 days without flash at all). I don't have an easy way to measure the power draw other than battery life (a 200mAh which I charger for 2 hours).

    BTW: SPI flash shares NO GPIOs with the display. It DOES share CLK/DATA with the accelerometer, but I do not initialize or use that at all (yet).

    All thoughts appreciated. Board file is

    import pinutils;
    info = {
     'name' : "DT28 smartwatch",
     'boardname' : 'DT28', # visible in process.env.BOARD
     'default_console' : "EV_BLUETOOTH",
     'variables' : 2565, 
     'bootloader' : 1,
     'binary_name' : 'e_%v_dt28.hex',
     'build' : {
       'optimizeflags' : '-Os',
       'libraries' : [
       'makefile' : [
     'DEFINES+=-DCONFIG_GPIO_AS_PINRESET', # Allow the display MOSI pin to work
         'DEFINES+=-DNRF_BLE_GATT_MAX_MTU_SIZE=53­ -DNRF_BLE_MAX_MTU_SIZE=53', # increase MTU from default of 23
         'DEFINES += -DSPIFLASH_SLEEP_CMD', # SPI flash needs to be explicitly slept and woken up
         'DFU_SETTINGS=--application-version 0xff --hw-version 52 --sd-req 0x8C,0x91',
         'INCLUDE += -I$(ROOT)/libs/misc',c'
    save_code_pages = 20;
    chip = {
      'part' : "NRF52832",
      'family' : "NRF52",
      'package' : "QFN48",
      'ram' : 64,
     'flash' : 512,
      'speed' : 64,
      'usart' : 1,
      'spi' : 1,
      'i2c' : 1,
      'adc' : 1,
      'dac' : 0,
      'saved_code' : {
              'page_size' : 4096,
              #'address' : ((118 - save_code_pages) * 4096), # Bootloader at 0xF8000
              #'pages' : save_code_pages,
              'flash_available' : 512 - ((35 + 8 + 2)*4), # Softdevice 5.0  uses 35 pages of flash, bootloader 8, FS 2. Each page is 4 kb.
              'address' : 0x60000000, # put this in external spiflash (see below)
              'pages' : 2048, # 8MB of the 16MB external flash
    devices = {
     'BTN1' : { 'pin' : 'D29',  'pinstate' : 'IN_PULLDOWN'},
     'BTN2' : { 'pin' : 'D30',  'pinstate' : 'IN_PULLDOWN'},
     'BTN3' : { 'pin' : 'D31'},  # Fake button: bootloader wants THREE 
      #'VIBRATE' : { 'pin' : 'D22' }, # Pin negated in software
      #'LCD' : {  # unused for bootloader
     'SPIFLASH' : {
                'pin_sck' : 'D9',
                'pin_mosi' : 'D8',
                'pin_miso' : 'D7',
                'pin_cs' : 'D6',
                'size' : 8192*1024, # 16MB
                'memmap_base' : 0x60000000 # map into the address space (in software)
    # left-right, or top-bottom order
    board = {
      'left' : [ 'VDD', 'VDD', 'RESET', 'VDD','5V','GND','GND','','','D3','D4','­D28','D29','D30','D31'],
      'right' : [
         'D27', 'D26', 'D2', 'GND', 'D25','D24','D23', 'D22','D20','D19','',
      '_notes' : {
        'D6' : "Serial console RX",
        'D8' : "Serial console TX"
    board["_css"] = """
    [#board](https://forum.espruino.com/sear­ch/?q=%23board) {
      width: 528px;
      height: 800px;
      top: 0px;
     left : 200px;
      background-image: url(img/NRF52832DK.jpg);
    [#boardcontainer](https://forum.espruino­.com/search/?q=%23boardcontainer) {
      height: 900px;
    [#left](https://forum.espruino.com/searc­h/?q=%23left) {
        top: 219px;
        right: 466px;
    [#right](https://forum.espruino.com/sear­ch/?q=%23right) {
        top: 150px;
        left: 466px;
    .leftpin { height: 17px; }
    .rightpin { height: 17px; }
    def get_pins():
      pins = pinutils.generate_pins(0,31) # 32 General Purpose I/O Pins.
      pinutils.findpin(pins, "PD2", True)["functions"]["ADC1_IN0"]=0;
      pinutils.findpin(pins, "PD3", True)["functions"]["ADC1_IN1"]=0;
      pinutils.findpin(pins, "PD4", True)["functions"]["ADC1_IN2"]=0;
      pinutils.findpin(pins, "PD5", True)["functions"]["ADC1_IN3"]=0;
      pinutils.findpin(pins, "PD28", True)["functions"]["ADC1_IN4"]=0;
      pinutils.findpin(pins, "PD29", True)["functions"]["ADC1_IN5"]=0;
    pinutils.findpin(pins, "PD30", True)["functions"]["ADC1_IN6"]=0;
      pinutils.findpin(pins, "PD31", True)["functions"]["ADC1_IN7"]=0;
      # Make buttons and LEDs negated
      pinutils.findpin(pins, "PD29", True)["functions"]["NEGATED"]=0;
      pinutils.findpin(pins, "PD30", True)["functions"]["NEGATED"]=0;
      # everything is non-5v tolerant
      for pin in pins:
      return pins
  • That's an odd one - I just checked and SPIFLASH_SLEEP_CMD really should ensure that QSPI_STD_CMD_SLEEP is sent to the flash chip.

    Do you know what the flash chip in the watch actually is? Maybe it doesn't honour that command.

    Or... maybe when it goes to sleep it pulls the MISO pin low, and that then is fighting against the accelerometer and wasting power?

    I was just looking through the code, and in jshardware.c and jshResetPeripherals I can see:

    [#if](https://forum.espruino.com/search/­?q=%23if) defined(BANGLEJS_Q3) && !defined(SPIFLASH_SLEEP_CMD)
      spiFlashReset(); //SW reset

    So maybe you don't actually want to use SPIFLASH_SLEEP_CMD, but instead want to change that ifdef to enable it for your watch (not just the Bangle.js 2)?

  • Ah, so it wouldn't run anyway... cool. I'll try that. Right now I've manually sent the whole thing a sleep command, and it's been running for 9h on a 20 min charge, so cross fingers there.

    Thank you!

  • So maybe you don't actually want to use SPIFLASH_SLEEP_CMD, but instead want to change that ifdef to enable it for your watch (not just the Bangle.js 2)

    looks like that is some workaround for Q3 to do it 3 times but it is still there for other devices too

    @yngv126399 I still don't get why they share I2C and SPI pins for this watch as they had may other pins available. would make sense if the accelerometer would be configured as SPI too but it is not the case here. so it may make sense to keep the shared data pin floating when both flash and accelerometer communication is not in progress(?) then reconfiguring it before starting i2c or spi communication. But still, is the clock pin shared for clock for both spi and i2c? Then I don't know how the accelerometer is prevented from receiving spi communication and possibly misinterpreting that and messing data transfer for SPI on data pin - causing random errors (that you described in first post?). You can pull SPI CS high to let SPI flash not mess up I2C but what to do for the other way?

  • totally agree I don't know why they share those lines.. and this one actually shares data and clock with display, SPI flash AND accelerometer! However, as I've removed the display and the touchscreen, I've grabbed the touchscreen SDA/SCL for my e-ink display, so that's not shared.

    However, I'm not initializing the accelerometer at all, so I'm hoping it's not doing anything (yet). My intention was to use a very basic JS driver and simply ignore any requests if the SPI flash is low. Klunky, but it should do for now.

    Right now the watch has been running off charger for 19hours, but NRF.getBattery() is reporting 3.02 so not expecting it to last the night... this is with manually setting the SPI flash to sleep (current firmware isn't using it for Storage, but it is compiled in so I can get it via require("Flash"))

  • However, I'm not initializing the accelerometer at all, so I'm hoping it's not doing anything

    So most probably it sits on i2c bus trying to read i2c traffic when clock is pulsed and in very unlikely case may respond. For I2c the SDA pin is open drain with pull up, so only when it decides to send zero bits it will pull the shared data pin low and possibly mess up SPI communication randomly. However it is very very unlikely as the traffic would need to be valid i2c with matching address otherwise it should not do anything(?). In theory the safest would be to put it to deep sleep like the P8 touchscreen chip which must be then woken by interrupt pin to listen on i2c.

    What is more likely is the power drain if the last SPI bit is low = data pin driven is low with the i2c SDA pin having a pull up. This can be fixed by setting MOSI high or floating (=reconfigure as input) after spi transfer ends

    Same issue could be with SPI clock polarity if the clock is idle low as i2c clock is also open drain with pull up.
    see e.g. https://learn.sparkfun.com/tutorials/i2c­/i2c-at-the-hardware-level

  • @fanoush Lots of things to try, thank you. So far, it's still going (nearly 2 days now) so that's a good sign that it was simply the SPI flash not sleeping. But if I do choose to use it, I'll have to know some of these tricks.

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

SPI flash odd behaviour

Posted by Avatar for yngv126399 @yngv126399