• Hurray! ...got it working!

    See updated version of module.

    Key to the solution is: Commands CANNOT be chained..., they all have to be concluded / committed individually by the chip select to go high!

    With that I just have increased my #memory (for very fast data) by 66%.

    // FRAMSPI_inline.js (c) muet.com
    
    // F RAM SPI Module
    
    var framspiModule = { connect: function(spi,cs,hd,wp) {
      pinMode(cs,"output"); cs.set();
      if (hd) { pinMode(hd,"output"); hd.set(); }
      if (wp) { pinMode(wp,"output"); wp.set(); }
    
      var fram = 
    { id: function() {
        cs.reset(); spi.send(0x9F);
        return spi.send(Uint8Array(9),cs);
      }
    , stat: function() {
        cs.reset(); spi.send(0x05);
        return spi.send(Uint8Array(1),cs);
      }
    , wen: function() { spi.send(0x06,cs); }
    , wdi: function() { spi.send(0x04,cs); }
    , read: function(a,n){
        cs.reset(); spi.send([0x03,a>>8,a]);
        return spi.send(new Uint8Array(n),cs);
     }
    , write: function(a,d){
        spi.send(0x06,cs); cs.reset();
        spi.send([0x02,a>>8,a]);
        spi.send(d); cs.set();
        return d.length;
      }
    };
      return fram;
    }
    };
    
    SPI2.setup({sck:B13, miso:B14, mosi:B15, baud: 18000000});
    
    var fram = framspiModule.connect(SPI2,A2);
    
    console.log(fram.id());
    
    function writes() {
    fram.write(  0,"FRAM@000");
    fram.write(  8,"FRAM@008");
    fram.write( 16,"FRAM@016");
    fram.write(128,"FRAM@128");
    fram.write(256,"FRAM@256");
    fram.write(512,"FRAM@512");
    }
    
    function reads() {
    console.log("@000:",fram.read(  0,  8));
    console.log("@008:",fram.read(  8,  8));
    console.log("@016:",fram.read( 16,  8));
    console.log("@128:",fram.read(128,  8));
    console.log("@256:",fram.read(256,  8));
    console.log("@512:",fram.read(512,  8));
    }
    
    ArrayBufferView.prototype.asString = function() {
     var s = "";
     this.forEach(function(d) { s+=String.fromCharCode(d); });  
     return s;
    };
    
    function hurray() {
      fram.write( 16,"Hurray! ...it works!");
      console.log(fram.read(16,20));
      console.log(fram.read(16,20).asString())­;
    }
      
    writes();
    reads();
    hurray();
    

    This is the output (see hurray() function):

     1v70 Copyright 2014 G.Williams
    >echo(0);
    new Uint8Array([127, 127, 127, 127, 127, 127, 194, 34, 0])
    @000: new Uint8Array([70, 82, 65, 77, 64, 48, 48, 48])
    @008: new Uint8Array([70, 82, 65, 77, 64, 48, 48, 56])
    @016: new Uint8Array([70, 82, 65, 77, 64, 48, 49, 54])
    @128: new Uint8Array([70, 82, 65, 77, 64, 49, 50, 56])
    @256: new Uint8Array([70, 82, 65, 77, 64, 50, 53, 54])
    @512: new Uint8Array([70, 82, 65, 77, 64, 53, 49, 50])
    new Uint8Array([72, 117, 114, 114, 97, 121, 33, 32, 46, 46, 46, 105, 116, 32, 119, 111, 114, 107, 115, 33])
    Hurray! ...it works!
    =undefined
    > 
    

    I set the SPI baud to 18MHz... does it really do it that fast? I could not find quickly what the limit is for the chip on the Espruino board running at 72Mhz.

    Initially, I could not even reliably read the device id and the status register. The first break through came when putting command, command parameter(s), and necessary clocking into a single array and make it an spi.send(), even though one would expect that a spi.write() would be the thing for commands and command parameter(s), because nothing should be returned for those. With known number of 0xFFs to the response one can live. For the id and status read this was sending:

    • [0x9F,0x00,0x00,0x00,0x00,0x00,0x00,0x00­,0x00,0x00]
    • [0x05,0x00]

    When using SPI.write() for the commands and parameters, the next SPI.send is returning first a 0xFF for each command and parameter byte, before the data, and leave stuff 'unread'... and subsequently 'read' by the next SPI.send()... just weird... there was no reasonable pattern to discover to discard reliably this unwanted, delayed read stuff... and adding more to the send to 'read' all made it worse: it prepended even more to the next 'read'. To not return unwanted information, I split sending of command and parameters from the clock ticking for receiving. The splitting requires the 'manual' handling of chip select outside of the SPI.send(), but can be kept inside latter for the last SPI.send().

    , id: function() {
        cs.reset(); spi.send(0x9F);
        return spi.send(Uint8Array(9),cs);
      }
    , stat: function() {
        cs.reset(); spi.send(0x05);
        return spi.send(Uint8Array(1),cs);
      }
    

    Next step was writing to the status register with the lesson learned.

    The write and read was a bit more complicated... This was the first working write:

    , wr0: function() {
        spi.send(0x06,cs);
        spi.send(  
          [0x02
          ,0x00,0x00
          ,0x40,0x30,0x031,0x032
          ]
         ,cs);
        spi.send(0x04,cs);
      }
    

    I first did a manual un-set of the write protection, and then do the write, and that made the read work. This told me that I cannot chain commands, like un-set write protection, then write, and finally set write protection again.

    , rd1: function(){
        var d = spi.send(
          [0x03
          ,0x00,0x00
          ,0x00,0x00,0x00,0x00
          ]
         ,cs);
        return d;
      }
    , rd2: function(a){
        var d = spi.send(
          [0x03
          ,a>>8,a
          ,0x00,0x00,0x00,0x00
          ]
         ,cs);
        return d;
      }
    

    From then on it was figuring out how to do it with the least statements, which unveiled another nifty corner: reset chip select and include the set of it in the send worked for all places except for the write. For the write I had to just do a simple send and then explicitly set chip select high.

    Trying to convert a Uint8Array to a string became a bit a detour... Espruino JS does not look at Uint8Array as an array that can be used in a Function.call(null,args) as args parameter as regular JS does (see http://forum.espruino.com/conversations/­258049). Resorting to beloved iteration and string concatenation in JS as Uint8Array asString() prototype extension gets it done.

    There are a few things left to look into. I was not able to use A5..A7 with A2 as chip select for the above writing sequence. First writes made it, but after a while it garbled. Could it be that B13..B15 are configured differently for SPI? ...like pulling up/down things? Also, actual speed has to be found out. But more important for now: the protocol.

    With 'hurray' going, it is now the time to think about a 'good' protocol for writing and reading of the FRAM in applications: a lean, concise protocol - not to fat and not to frugal . Questions for the protocol are: File System? ...may be not, since the amount of memory is small and filesystem overhead may be significant... read/write of data types? Strings, for example ( fram.substr(120,40 ) for 40 bytes starting with address 120,.... or 0 ending/delimited strings, or String objects with length at the beginning? ...reads and writes for every object type... or just limit to String with JSON for other things... Memory management with ribs... or even object-space w/ r/w of 'String objects' w/ 'simple' garbage collection.

    Object store w/ simple garbage collection: no reference for mark, just check for active / inactive indicator in the object list. MSBit is not used for address and can therefore be used in an object list held in a heap. Heap residing in one side of the memory and data in the opposite one could be a quite luxury solution - function-wise - and not too fat for the implementation. The protocol would be a simple CRU(D), with create returning a handle (address into object list/heap) for future access, update with automatically managing updates with a different size, and delete with setting the deletede / inactive indicator.

    Any protocol suggestions are welcome!

About

Avatar for allObjects @allObjects started