ShiftOut question for Hannio flipDot Display

Posted on
  • Hello @Gordon

    I've bought a flipdot display controled by a shift register.
    There is an arduino lib to control it...
    i find the code to display one pixel with arduino:

    void FlipDot_5x7::displayPixel(int16_t x, int16_t y, boolean color) {
            // DR R0 R1 R2 DC C0 C1 C2
            uint8_t data = color != _invert;
            data |= y << 1;
            data |= (color == _invert) << 4;
            data |=  x << 5;
            shiftOut(_data, _clock, LSBFIRST, data);

    it needs a LATCH pin more for working, i've added it in the js code
    i've rewrited the code for espruino like this

    var DATA_PIN = A8;
    var CLOCK_PIN = B6;
    var LATCH_PIN = B4;
    var compteur=0;
    var aff= 0;
    var _invert = false;
    function displayPixel(x, y, color) {
            // DR R0 R1 R2 DC C0 C1 C2
            var data = color != _invert;
            data |= y << 1;
            data |= (color == _invert) << 4;
            data |=  x << 5;
            shiftOut(DATA_PIN, {clk:CLOCK_PIN}, data);
            //console.log("data:", data.toString(2));
    function onInit() {
      setInterval(function() {
        displayPixel(aff%5, Math.floor(aff/5)%7, compteur%2);
        compteur ++;
        if (compteur%2 ===0) {aff++;}
      }, 500);

    it does not work...
    if i generate a random number for data

    data = Math.round(Math.random()*255);

    dots flip sometime...
    i suppose it's about format of my byte...

    Luca from Hannio send me the format for the byte

    the data has to be xxxCyyyc where xxx is the binary representation of
    the x coord (and yyy for the y). c is the color, C is the inverted
    value. Thus, for example, x = 2, y = 3, c = 1 would be 010 0 011 1.

    Where i'm wrong?

    best regards


  • In the anonymous function in your oninit() code I would expect the displayPixel() first and then the digitalPulse() for the latching (flank) and (combined?) the output enable for a particular time (apply the power to the coil)... - in other words, flip the lines 28 and 29, and allow a much longer pulse...

    What type of shift register are you using?

    Btw, you can save yourself some work by using MC / Eespruino built-in SPI (serial peripheral interface) appliance: assuming, there are two shif registers, one for x (0..5) and the other one for y (0..6) and the shift registers are in series - first x, then y - then you have an easy game:

    // SPI1 and ports for Espruino Pico / Wifi and 74HC595 shift registers
    SPI1.setup({mosi:B5, sck:B3}); // SPI1, other SPIx can do too
    var dnss = A0; // nss pin used for the latching, 
    var  pls = A1; // for the _OE (neg output enable) 
    var dat = new Uint8Array(2);
    function setPxl(x,y,p) { // polarity 0,1
      x=Math.floor(x); y=Math.floor(y); // optional
      x=(x>4)?4:(x<0)?0:x; y=(y>6)?6:(y<0)?0:y; // optional
      d[0]=Math.pow(2,y); d[1]=Math.pow(2,x); d[p]^=0xFF;
      SPI1.write(d,dnss); digialPulse(pls,1,10);
  • There is 2 ic 74hc238 who are decoding a 3bit adress and convert to 8bits for the shift register 74hc595 (i suppose) for l293d
    I do not have direct access to shift register.
    But perhaps i'm wrong on the way that's works.
    Or in a different way.
    The 595 is a stronger version of standard shift register...

    Here the git page of the project (lib and diagramm)

    In the arduino lib, the latch time is 500 microseconds.
    I've tried with more time but no better results...

    An other question who is alway confuse me on the espruino.
    I want to use the usb for debugging purpose,
    The display is powered in 12v and convert a line to 5v to power the mcu.
    i need to power the espruino from this line.
    On which pin do i need to clip the power (i'm not soldering at the moment)
    On the pin near the gnd one or the next pin (third) or on the ViN pin?
    I'm sad cause i've burned two display with an incorrect power connection when changing clip position from the mcu.

  • If i power the espruino with the 12v, i plug on the 3rd pin (gnd pin side)?
    Apologies for this basic question, but the page is not lighting for my english abilities 😞

  • Hi - which Espruino board are you using? I'm pretty sure the 3rd pin won't be right (that's usually the 3.3v pin) - I think if you do that you could cause some serious damage!

    I'd also make sure that GND of all boards is connected together - it can often be a big cause of problems if that isn't connected.

    In terms of software, what you're doing looks good. The latch is often active low though, so I'd try:

        displayPixel(aff%5, Math.floor(aff/5)%7, compteur%2);

    Or potentially the problem is that digitalPulse is asyncronous? It will be working while you are writing data using displayPixel. I'm not sure if that's your intention? If not:

        setTimeout(function() {
          displayPixel(aff%5, Math.floor(aff/5)%7, compteur%2);
        }, 2);

    Might help?

    Also, I believe there are some issues with shiftOut:

    You could try this?

    shiftOut(DATA_PIN, {clk:CLOCK_PIN, repeat:8}, [E.reverseByte(data)]);

    Or, as @allObjects says, you can use the SPI object. But you'll still need reverseByte

  • @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 nssPin 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
    | 0 | x | x | x | 0 | x | x | x | 0|  dec 255 are driven to same voltage,
    +---+---+---+---+---+---+---+---+--+          level either L or H; pref.
                                                  L, therefore preferably 0.
    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) ? 64 : 128), 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) ? 64 : 128); wpt++;
         if (cnt === 1) (setInterval(wriPix,1); // make/invoke it 'async'
      } else {
         // 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) ? 64 : 128)

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

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

  • @allObjects: Whao, i'm impressed! Amazing!
    Have to take time to test your code.
    Thanks for the great explanation.
    I've got the version with the silkscreen error on pcb (inverted LAT and 5V pins).
    Sorry i just understand the use of espruino spi.write() - data,clk, latch pins

    @Gordon, sorry, i've forgotted to write: i use a pico board unpinned.

    The arduino lib is great but for english writer, no accents! :/
    Want to combine with the custom font of espruino.

  • @Mrbbp no problem :) You want to connect your input voltage to what's shown as BAT_IN here:

    Ideally just the 5v signal from the board though if you can, rather than 12v.

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

    ...was a hint looking for a sponsor... ;) - and kind of says: sorry, have (had) no chance to test... of course, I could make a dry test: print the bits I'm passing to SPI1.write(...)``, but that part I actually already use successfully.

    I'm tinkering along on a different display 'project' as possible 'sub-project' or 'explorative project' for an other, larger project. Im tinkering with a retro display: National Semiconductor 12 digit 7-segment, red LED Bubble Display NS A5120A (see attached pic) - as found in many pocket calculators in lat 60/early 70 - about $2K a piece was HP's first usable/scientific pocket calculator.

    I use 3 595 shift register in series. The last one sources the 7-segment plus decimal point anodes, and the 12 bit of the 16 bits of the other two drain the common cathodes for each digit... since the shift register drains a measly 6ma on 5V, I'm already in trouble with just 595 and may need to add 12 transistors to reliably drive all segments plus decimal point of a single digit all at once... another 2 chips of ULN2703 or 12 plain transistors w/ 12 Base resistors (the issue is obviously not the driving of the cathodes, - otherwise you could not see the digit lit - but more so the reliable shifting on higher speeds to 'hide' the multiplexing from the slow eye. The heavy drain load makes the latching register not to behave nicely anymore...).

    The setup you see in the picture uses an Espruino-Wifi, which is more or less a Pico w/ onboard ESP8266 (latter here not in use right now even though the blue LED dims...). Also you see that only the drains of tje first 3 digits' are hooked - hidden under the white-blue-striped segment wires (Ran out of common cathode resistors from my reuse-pile... or rusty nails I keep for ever... ;) the dismay of many around me...). I avoid the E.byteReversed() in digit's common anode driving, so think reversed. You see only 1 digit displayed because the digits are time multiplexed with very low frequency.

    The SPI1.write(...) that produces 'this' output is equal to:

    //          Segments   12 Digits, 1st is 'on' (=0=drains)           
    //          for 5 on     -------   ---1 
    //          595 right  middle    left
    //          latches    latches   latches
    //          1st byte   2nd byte  3rd byte
    //          receives   receives  receives
    //          data last  data 2nd  data 1st

    Regarding the SPI write with nss pin passed: SPI slaves requires the slave select line to go and stay low - hence called - nss (negative ss) in order to make a slave look at data on the bus (MOSI - Master Out Slave In wire) and detect when the transmission transaction is completed: ssn goes high. You can control ssn by yourself separately, such as B0.reset() (assuming ssn connected to B0), then do multiple sends with spi.write(aSingleByteOrAnArrayOfBytes); and conclude the 'transaction' with B0.set(). For convenience, @Gordon implemented the spi.write() with snn *in addition to accepting an array of bytes and not just a single byte - lest to talk about multiple pins - so that when you have all of your things to send ready-made in one byte array, you just need one call - which makes thing just crazy fast, because it is executed 'down deep in the belly' of Espruine all at once rather than in piece meal, spoon fed...

    1 Attachment

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

ShiftOut question for Hannio flipDot Display

Posted by Avatar for Mrbbp @Mrbbp