• @Mrbbp, I see that you use a controller (with 293 type, 4 x half-H bridge)... a lot of hardware, but it makes it possible to drive the coils w/ 12V and the logic with 3.3V (or 5V), and with the tons of diodes it is a pretty cool setup - and so is obviously the driving software.

    From my understanding, the shift register is there to feed the two 238 3-to-8 decoders, and the pulse is only there for the write (latch) into the shift register output buffers. Regarding the pulse: the shift register requires only a raising flank, and we get hat for free by using the built in SPI appliance and the nss (negative signal select) pin in the .write(). The nss pin you connect to the board's write input (which is the output register latch clock of the shift register) - NOTE: Im referring to the Version 2-5 of the driver board which you obviously have...

    To drive the row and column decoders, only 3 bits each are used for each nibble (half a byte, half of the shift register's 8 registers). The fourth bit is used for the data of the selected row and column, respectively. These 4th bits are always low and high or vice-versa for making a change. In other words, the byte you have to send (with SPI) looks like this:

    +---+---+---+---+---+---+---+---+--+ 
    |MSB|   |   |   |   |   |   |LSB|     <--- Most/Least significant bit
    | H | G | F | E | D | C | B | A |     <--- Shift Register Outputs QX
    |128|64 |32 |16 | 8 | 4 | 2 | 1 |     <--- bit weight in decimal
    | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |     <--- bit pos
    |   |   |   |   |   | 0 | 1 | 2 |     <--- col address bit (3x)
    |   |   |   |   |H/L|   |   |   |     <--- col data bit (1x)
    |   | 0 | 1 | 2 |   |   |   |   |     <--- row address bit (3x)
    |L/H|   |   |   |   |   |   |   |     <--- row data bit (1x)
    +---+---+---+---+---+---+---+---+--+
    | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 |64|  dec 164 flips row2/col1 pixel
    |   | row 2 dec |   | col 1 dec |  |   
    +---+---+---+---+---+---+---+---+--+
    | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 0 |44|  dec  44 flips row2/col1 pixel back
    |   | row 2 dec |   | col 1 dec |  |   
    +---+---+---+---+---+---+---+---+--+
    | 0 | x | x | x | 0 | x | x | x | 0|  dec   0 and 255 'de-power' the coils
    +---+---+---+---+---+---+---+---+--+          because BOTH of their ends
    | 1 | x | x | x | 0 | x | x | x | 1|  dec 255 are driven to same voltage,
    +---+---+---+---+---+---+---+---+--+          level either L or H; pref.
                                                  L, therefore preferably 0.
                                                  Altenative: nssPin.reset().
    Unit uses about 50..70/60..90mA idling and 250/350mA on active (9/12V).
    
    NOTE: Counting of rows and columns starts with 0.
    

    The idea of the flip dot display though is that it does not use current after setting a(ll) dot(s). Therefore, after updating the whole display, at least these row and column data bits have to be set to low. Depending the implementation, I recommend to do that even between pixel settings... In other words, the data has to be stable (latched) in the shift register output register buffer for a specified time... and you have to figure out how long that time is.

    I find it odd that the addresses are reverse (MSB shifting out first), but it is not that much a complication for the code. Regarding the max. invocation frequency, you may need to look if you can set one pixel after the other, because if you keep the values not stable for required time, the 'derived pulse' - the time between two pixel flips - is too short to actually have the pixel flipped... But if you de-power the coils after a sequence of settings, you may even choose a higher voltage - up to 36 volts according to the L293 spec - to 'pump' enough energy into the coils with a much shorter time of applying stable values (and before de-powering)!

    SPI1.setup({mosi:B5, sck:B3}); // SPI1, other SPIx can do too
    var nssPin = A0; // nss pin used for the latching
    function setPix(x,y,p) { // x(col),y(row),polarity 1/0 or true/false
       SPI1.write( (E.reverseByte(y | (x<<4))>>1) | ((!p) ? 128 : 8), nssPin);
    }
    

    That's all you need... the nssPin is connected to the write (RCLK) of the 595 shift register. The raising flank of the nssPin does just the right thing...

    Init your 3 pins - data, sclk, rclk - as output and set rclk high and you are just fine from the get go.

    If you loose flips, you may slow down the setPix() invocation frequency... To do that in the Espruino way, you do it event driven and in a producer/consumer pattern. You create a queue that is shared between producer and consumer. The producer - setPix() calculates and stores the values, and kicks the consumer off if it is the first entry in the queue. The consumer does the actual write to the device and works after initial kick off interval driven until everything written, including the de-powering value. The queue or buffer you implement as ring buffer to avoid memory management... (overflow detected, but not handled yet in the code).

    SPI1.setup({mosi:B5, sck:B3}); // SPI1, other SPIx can do too
    var nssPin = A0; // nss pin used for the latching
    var rbuf = new Uint8Array(512); // ring buffer w/ fill counter cnt...
    var cnt = rpt = wpt = 0; // ...and read/write buffer pointers
    function setPix(x,y,p,h) { // x(col),y(row),polarity 1/0 or true/false
      if (++cnt <= 512) {
         rbuf[wpt] = (E.reverseByte(y | (x<<4))>>1) | ((!p) ? 128 : 8); wpt++;
         if (cnt === 1) (setInterval(wriPix,1); // make/invoke it 'async'
      } else {
         cnt--;
         // throw error, decide what to do
      }
    }
    function wriPix() {
      var v = rbuf[rpt];
      SPI1.write(v, nssPin);
      if (cnt > 1) { // more to write
        setInterval(wriPix, 2);
        if (++rpt >= 512) rpt = 0; cnt--;
      } else if (v) { // was last one, de-power      
        setInterval(wriPix, 2);
        rbuf[rpt] = 0;   
      } else {
        if (++rpt >= 512) rpt = 0; cnt--;
      }
    }
    

    If you want to make the code 'safe' to prevent the coordinates not to interfere with the row and column data bits, use this expression which mask everything beyond 3 row and column address bits (works even when coordinates are not integers...):

    (E.reverseByte((y&7) | ((x&7)<<4))>>1) | ((!p) ? 128 : 8)
    

    Btw, choosing !p makes it 'color'-safe: 0 and just any thing else...

    PS: will test the code when I get a display... ;-)

About

Avatar for allObjects @allObjects started