use a led matrix 5x7 without driver chip?

Posted on
  • Hello, i'm playing with led matrix.
    I've bought some small 5x7 matrix and want to display some char on it with the espruino pico
    i do not use any hardware driver because i just need 12pins to solder on the board with small resistor to limit current... i did it on an arduino (with a different display but the same pb)

    I would like to use the powerfull of js to generate text.

    On the Arduino i've used a lib (sevensegment) to declare pin as cathode and anode to drive the display.
    Is there something similar to declare a list of pins to connect to a graphicArray? (hope i'm understandable)

    best regards
    éric


    1 Attachment

    • Capture d'écran 2018-12-17 10.54.40.png
  • Eric,

    surre, Espruino PICO has enough GPIO pins to do it. I Though strongly recommend that you use current limiting resistors (on column pins) because there is a limit in max current the chip can take...

    Take a look at Driving LED matrix with shift registers such as 74HC595. The shift register was intentionally used to use the least GPIO pins as possible. Instead of calculating and shifting out the bits, you calculate the bits in an integer and output it to an array of GPI pins in CharliePlexed fashion (see digitalWrite()).

    For example:

    //  row / col:        5    4    3    2    1    7    6  /  5    4    3    2    2
    //  matrixPins:       8    7   10    3    1    6    5     4    9    2   11   12
    var espruinoPins = [B13, B10,  B1,  A7,  A6,  A5,  A8,   B7,  B6,  B5,  B4,  B3];
    //  weight         2048 1024  512  256  128   64   32    16    8    4    2    1
    //                 ----------------------------------   -----------------------
    //                 ---------- to be set LOW ---------   --- to be set HIGH ----
    var leftTopLED = 4064 - (32) + (1); // 4033 = 0b1111000001
    digitalWrite(matrixPins,leftTopLED);
    digitalWrite(matrixPins,0b011111110000);­ // rigthBottom
    

    I like the pics you added while I was working on this post... Give me some more time to get something going for you... When you want to use the graphics buffer and Graphics object in Espruino, you declare a black and white (2 colors 7x5 matrix) and then read it out and process it row for row. With that you can use the built in or available fonts. If you go the direct mode as above, you have to get the font bit mapping yourself.

  • Hello Markus,

    I do not have a pinned pico, i've used a pico board and pin header on breadboard without soldering anything (crap work!!!)

    In an earlier version i was ligthting On dot by dot on the first line and it was working.
    but it seems to light on the led but i'm not sure the out is a line...
    with this one i'm a bit confuse how a graphic buffer and uint8Array work together!
    if i drawString("H") in my arrayBuffer, it should look like this

    10001  //17 -> #11
    10001  //17 -> #11
    10001  //17 -> #11
    11111  //31 -> #'1F'
    10001  //17 -> #11
    10001  //17 -> #11
    10001  //17 -> #11
    

    but the value from the graphicBuffer are not...
    if i add

    for (let i=0; i<a.length;i++) {
        console.log(a[i].toString(2));
      }
    

    my "brilliant" test :/

    let g = Graphics.createArrayBuffer(5,7,1);
    let a = new Uint8Array(g.buffer);
    
    var compteur=0;
    var row= [A6,A5,A8,B7,B6]; // my 5 col
    
    function sendData() {
      g.clear();
      g.drawString("H");
      digitalWrite(B4,0); // select first row
      for (let i=0; i<row.length;i++) {
        digitalWrite(row[i],a[i]);
      }
      digitalWrite(B4,1); // leave First row
      compteur++;
    }
    
    function onInit() {
      require("Font4x6").add(Graphics);
      pinMode(B4,"opendrain");
      for (let i=0; i<row.length;i++) {
        pinMode(row[i],"output");
      }
      digitalWrite(B4,1);
      setInterval(sendData, 1000);
    }
    

    é.


    1 Attachment

    • 2018-12-18 00.09.12-2.jpg
  • Eric, where did you dig up these retro red bubble display? ...not that I will ask your for your's, but just like to know, because I resorted to NS board with 12 digit display!

    Unfortunately, they are not red, were affordable, and working nicely: Retro Bubble Displays driven with 74HC595. Driving those directly - not using shift register - would be quite a number of GPIO pins to burn: 19 or 20 - exactly. PICO as 21... so it still would work.

    With this large 7 segment display - 12 digits - you might even get in a

                 _                    _
      |  _  |_| |_     |_|    _   _  |_ |   |
    |_| |_|  _| |_ |_| | |   | | |_| |_ |_   .
    

    8 resistors for the 7 segments and decimal point, and 12 digits...

  • ohhh i've tried to read the post on shift register and each time i try to read your code, i fall on my butt and have a headache (i am so far to be able to produce that, i am a 5years old kid in algorithmic...) respect

    é.

  • i've found them at sparkfun so time ago... they are from 1978's HP. but they are out of catalog
    https://www.sparkfun.com/products/retire­d/12710

  • if you search with "HP bubble display", there is again some pieces on ebay...
    HP QDSP-6064

  • Thanks for the picture... you got it in while I was compiling the post #4.

    I do not know how the dots are arranged in the buffer bytes... but it can be found out... You also may face the 'reverse' issue... but that's easy: you just put out a filled rectangle in 'white' and write in 'black'.

    Btw, I noticed two resistors going to same B7 PICO GPIO pin instead of B7 and A8. But I guess this will not solve all you problems...

  • Graphics w/ Buffer may actually work nicely into your hand: vertical bits 8 in one byte... so you play column by column...

  • oh it's just a parallaxe mistake, the resistor is plug in H9 for A8...

  • I think it should work.

    // crée le buffer graphique 8x8 en 1 bit
    let g = Graphics.createArrayBuffer(5,7,8);
    let a = new Uint8Array(g.buffer);
    
    var compteur=0;
    var row= [A6,A5,A8,B7,B6];
    var col=[B5,B4,B3,A7,B1,B10,B13];
    
    function sendData() {
      g.clear();
      g.setFont4x6();
      g.setBgColor(1);
      g.setColor(0);
      g.drawString("HELLO",5-(compteur%30),0);­
      
      for (let i=0; i<7*5; i+=5) {
        console.log(a[i],a[i+1],a[i+2],a[i+3],a[­i+4]);
      }
      console.log(" ----------- ");
      for (let j=0; j<col.length;j++) {
        digitalWrite(col[j],0);
        for (let i=0; i<row.length;i++) {
          digitalWrite(row[i],a[(i*5)+i]);LED1.tog­gle();
        }
        compteur++;
        digitalWrite(col[j],1);
      }
      
    }
    
    function onInit() {
      require("Font4x6").add(Graphics);
      
      for (let i=0; i<col.length;i++) {
        pinMode(col[i],"opendrain");
      }
      for (let i=0; i<row.length;i++) {
        pinMode(row[i],"output");
      }
      digitalWrite(B4,1);
      console.log("init affichage matrice 5x7 sans driver");
      console.log("mém. used: "+Math.round(process.memory().usage/proc­ess.memory().total*100)+"%");
      setInterval(sendData, 500);
    }
    
  • I'm not sure if column has to be open drain, because it is connecting to the anodes. Matter of fact you do not need to set any pin modes, because Espruino does automatic modes, and all are outputs.

    To figure out arrangements of bits in buffer, I create a 5(cols) x 7(rows) x 1(color) Graphics object and put a buffer over it which takes 8 bytes with last 5 bits unused. The drawings are rectangles of height 1, width 1, 2, 3 and 5 in rows 0 and 1. For each drawings the buffer is printed in the log.

    var g = Graphics.createArrayBuffer(5,7,1,{vertic­al_byte:false});
    var b = new Uint8Array(g.buffer);
        
    function onInit() {
      g.clear(); g.drawRect(0,0,0,0); // top row 1 dot (1st)
      console.log(b);
      g.clear(); g.drawRect(0,0,1,0); // top row 2 dots (1st and 2nd)
      console.log(b);
      g.clear(); g.drawRect(0,0,2,0); // top row 3 dots (1st, 2nd and 3rd)
      console.log(b);
      g.clear(); g.drawRect(0,0,4,0); // top row 5 dots (all)
      console.log(b);
      g.clear(); g.drawRect(0,1,0,1); // 2nd row 1 dot
      console.log(b);
      g.clear(); g.drawRect(0,1,1,1); // 2nd row 2 dots
      console.log(b);
      g.clear(); g.drawRect(0,1,2,1); // 2nd row 3 dots
      console.log(b);
      g.clear(); g.drawRect(0,1,4,1); // 2nd row 5 dots
      console.log(b);
    }
    
    setTimeout(onInit,1000);
    

    Log output - with some formatting - is as expected:

     ____                 _
    |  __|___ ___ ___ _ _|_|___ ___
    |  __|_ -| . |  _| | | |   | . |
    |____|___|  _|_| |___|_|_|_|___|
             |_| espruino.com
     2v00 (c) 2018 G.Williams
    >
    new Uint8Array([  1, 0, 0, 0, 0])
    new Uint8Array([  3, 0, 0, 0, 0])
    new Uint8Array([  7, 0, 0, 0, 0])
    new Uint8Array([ 31, 0, 0, 0, 0])
    new Uint8Array([ 32, 0, 0, 0, 0])
    new Uint8Array([ 96, 0, 0, 0, 0])
    new Uint8Array([224, 0, 0, 0, 0])
    new Uint8Array([224, 3, 0, 0, 0])
    > 
    

    We see that the 1st row - row 0 - uses the first five (5) LSB bits in first byte - byte 0. We also see that 2nd row - row 1 - straddles the first and second byte - byte 0 and 1 - with first three (3) bits in byte 0 and the remaining two (2) bits in byte 1.

    With this bit arrangement in the buffer it is difficult to write an algorithm that puts out the bits nicely... yes, we can do some 'streaming' to make sure we always - 7 times - have 5 bits per row to put out - with a lot of pushing and shoving around bits (shift, mask, shift, etc...).

    The Graphics class though allows us a vertical vs the horizontal bit arrangement... and we do not even really loose space: still 5 bytes, one for each column. Since we care about only one text row, it becomes very simple...

    The Graphics declaration is 8 bits per column (multiple of 8 in height):

    var g = Graphics.createArrayBuffer(5,8,1,{vertic­al_byte:true});
    

    And the respective console output is:

             |_| espruino.com
     2v00 (c) 2018 G.Williams
    >
    new Uint8Array([1, 0, 0, 0, 0])
    new Uint8Array([1, 1, 0, 0, 0])
    new Uint8Array([1, 1, 1, 0, 0])
    new Uint8Array([1, 1, 1, 1, 1])
    new Uint8Array([2, 0, 0, 0, 0])
    new Uint8Array([2, 2, 0, 0, 0])
    new Uint8Array([2, 2, 2, 0, 0])
    new Uint8Array([2, 2, 2, 2, 2])
    > 
    

    The buffer bytes are just perfect to be pushed out column by column...

    A separate algorithm just pushes the buffer out with a refresh rate defined by an interval. After all columns are put out, column selector has to be disabled to have not the last one be lit for the whole time between the refresh interval.

    I have a very simple 5x7 (actually only 5x3) RED LED matrix with row anodes and column cathodes from end 70's/early 80's that I used to demo and explain CharliePlexing... and I will use it again here - in the next post. LEDs were still a cost factor - at least for a student budget - dim lit, and some colors were not even on the market yet... ;-) see History of LED.

  • As @allObjects points out above it looks like you're better off using a Graphics instance that's 8 pixels high (with vertical byte). While it 'wastes' one pixel per column (which isn't much for 5 columns!) it means it's way easier to access the Graphics ArrayBuffer's data!

  • well i do not understand the interest to display in col vs in row! except to have a smaller ArrayBuffer.
    is it really faster?
    how to read each bit of col?
    I need to select each row and extract the bit from each byte stored in the buffer!!!
    i should missed something in the process... i'm lost

    for (let i=0; i<g.getWidth();i++) {
        console.log(b[i].toString(2));
    // export a rotated  display
    }
    
  • i do not understand why by this way vs by the other one.... :(
    don't understand how to extract dot on a line.
    because it seems easier to push out line by line... i'm lost

  • You can push out line by line, but if so, I'd suggest:

    let g = Graphics.createArrayBuffer(8,7,1);
    var b = new Uint8Array(g.buffer);
    for (let i=0; i<g.getHeight();i++) {
        console.log(b[i].toString(2));
    }
    

    Graphics 'packs' pixels as close together as they will go, so if you have a width of 5 then each row will get spread over your b array. If you have a width of 8 pixels then every element of b will represent a row, which is what you want.

    You can also use g.getPixel to get a single pixel out of Graphics - but it'll be a lot slower than accessing a whole row with b[i].

  • hello @Gordon,

    i have no predefined solution to switch on led on the matrix.
    i just want to solve my prob who is that i have a graphic arrayBuffer with a font drawing and want to HIGH or LOW the pin of the espruino to display the content of the buffer on the matrix.
    As i see in the datasheet of the matrix, it seems easier to pilot the display by line. but i haven't a good enough tech background to be sure of that. Your (@Gordon and @allObjects) attempts to explain are futile since I do not know how to switch from an arrayBuffer to a properly lit matrix with some digitalWrite()

    I ask you to forgive my gross ignorance. Shame on me.
    All seems so simple for you!
    not for me.
    can i DigitalWrite(*array of pins*, *array of value like the b[i].toString(2)*)?

    // pin of the matrix wired on the espruino
    const x= [A6,A5,A8,B7,B6];
    const y=[B5,B4,B3,A7,B1,B10,B13]; //to ground????
    
    let g = Graphics.createArrayBuffer(8,7,1);
    var b = new Uint8Array(g.buffer);
    for (let i=0; i<???;i++) {
    
       digitalWrite(???,???)
    }
    
  • can i DigitalWrite(array of pins, array of value like the b[i].toString(2))?

    Yes - digitalWrite([pin1,pin2,etc],b[i]) works great.

    I think the issue you have is that you have to 'scan' out the LEDs. You can't light them all at once, you have to light up just one row or column and then do them all in sequence.

    As a simple example, does this light up all the LEDs for you?

    const x= [A6,A5,A8,B7,B6];
    const y=[B5,B4,B3,A7,B1,B10,B13]; //to ground????
    
    function scan() {
    digitalWrite(y,0b1111111);  // all 1 turns all rows off
    for (let i=0; i<7;i++) {
       y[i].reset(); // row on
       digitalWrite(x,0b11111); // set column -> all on
       y[i].set();   // row off
    }
    }
    
    setInterval(scan, 20);
    
  • Eric, it all depends...

    I used the approach of putting out column by column because I know about some of your applications that implement one line of a running text... To finally implement that, it becomes extremely simple:

    Think about you want to run the text Hello! over and over *** smoothly *** - pixel by
    pixel column - through a display that can show two (2) characters only... in other words, the display has 10 columns of dots - or two 6x4 (fixed) font characters with one pixel space between characters.

    1. Define buffer for whole text plus 2 blanks at begin and end: (2+6+2)*5=50 cols
    2. Write the text once w/ leading and trailing blanks into buffer.
    3. Think about the 50 cols as a ring-buffer: after the last col, the first follows.
    4. Have a 'rotating' view port of 10 cols running over the ring of 40 cols.

    Regarding point 3: it is actually only 40 cols you shift thru, but with every position
    you show 10 cols. To make algorithm life simple - as shown at the end - that's what the
    leading and trailing spaces (blank characters) are for. So bear with me.

    I try to show what is going on in 'character graphics' below.

    After writing to the buffer with a 6x4 font and 1
    col character spacing, it looks like this (@=1,
    all others=0 bits):

      _0_  _1_  _2_  _3_  _4_  _5_  _6_  _7_  _8_  _9_  10 chars
     /   \/   \/   \/   \/   \/   \/   \/   \/   \/   \
     0123456789012345678901234567890123456789­0123456789 50 cols
     ----------@--@-@@@@-@----@-----@@---@---­----------  8 rows
     ----------@--@-@----@----@----@--@--@---­----------
     ----------@@@@-@@@@-@----@----@--@--@---­----------
     ----------@--@-@----@----@----@--@------­----------
     ----------@--@-@@@@-@@@@-@@@@--@@---@---­----------
     ----------------------------------------­----------
     ----------------------------------------­----------
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~­~~~~~~~~~~
    

    First you push out the first ten columns for n times -
    10 columns starting with column 0.
    This will display nothing... obviously:

      _0_  _1_  _2_  _3_  _4_  _5_  _6_  _7_  _8_  _9_ 
     /   \/   \/   \/   \/   \/   \/   \/   \/   \/   \
     0123456789012345678901234567890123456789­0123456789
    .----------. 
    |----------|--@-@@@@-@----@-----@@---@--­-----------
    |----------|--@-@----@----@----@--@--@--­-----------
    |----------|@@@-@@@@-@----@----@--@--@--­-----------
    |----------|--@-@----@----@----@--@-----­-----------
    |----------|--@-@@@@-@@@@-@@@@--@@---@--­-----------
    |----------|----------------------------­-----------
    |----------|----------------------------­-----------
    |~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~~~~~~~~~­~~~~~~~~~~~
    '----------'
                \
                 'Rotating' view port - begin @ col 0
    

    Then you push out n times 10 columns starting with col 1.
    This displays the left vertical of the H at the most
    right display column:

      _0_  _1_  _2_  _3_  _4_  _5_  _6_  _7_  _8_  _9_ 
     /   \/   \/   \/   \/   \/   \/   \/   \/   \/   \
     0123456789012345678901234567890123456789­0123456789
     .----------.
     |---------@|-@-@@@@-@----@-----@@---@---­----------
     |---------@|-@-@----@----@----@--@--@---­----------
     |---------@|@@-@@@@-@----@----@--@--@---­----------
     |---------@|-@-@----@----@----@--@------­----------
     |---------@|-@-@@@@-@@@@-@@@@--@@---@---­----------
     |----------|----------------------------­----------
     |----------|----------------------------­----------
     |~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~~~~~~~~~­~~~~~~~~~~
     '----------'
                \
                 'Rotating' view port - begin @ col 1
    

    And so fort...

      _0_  _1_  _2_  _3_  _4_  _5_  _6_  _7_  _8_  _9_ 
     /   \/   \/   \/   \/   \/   \/   \/   \/   \/   \
     0123456789012345678901234567890123456789­0123456789
      .----------.
     -|--------@-|@-@@@@-@----@-----@@---@---­----------
     -|--------@-|@-@----@----@----@--@--@---­----------
     -|--------@@|@-@@@@-@----@----@--@--@---­----------
     -|--------@-|@-@----@----@----@--@------­----------
     -|--------@-|@-@@@@-@@@@-@@@@--@@---@---­----------
     -|----------|---------------------------­----------
     -|----------|---------------------------­----------
     ~|~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~~~~~~~~­~~~~~~~~~~
      '----------'
                \
                 'Rotating' view port - begin @ col 2
    
      _0_  _1_  _2_  _3_  _4_  _5_  _6_  _7_  _8_  _9_ 
     /   \/   \/   \/   \/   \/   \/   \/   \/   \/   \
     0123456789012345678901234567890123456789­0123456789
       .----------.
     --|-------@--|-@@@@-@----@-----@@---@---­----------
     --|-------@--|-@----@----@----@--@--@---­----------
     --|-------@@@|-@@@@-@----@----@--@--@---­----------
     --|-------@--|-@----@----@----@--@------­----------
     --|-------@--|-@@@@-@@@@-@@@@--@@---@---­----------
     --|----------|--------------------------­----------
     --|----------|--------------------------­----------
     ~~|~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~~~~~~~­~~~~~~~~~~
       '----------'
                \
                 'Rotating' view port - begin @ col 3
    
      _0_  _1_  _2_  _3_  _4_  _5_  _6_  _7_  _8_  _9_ 
     /   \/   \/   \/   \/   \/   \/   \/   \/   \/   \
     0123456789012345678901234567890123456789­0123456789
        .----------.
     ---|------@--@|@@@@-@----@-----@@---@---­----------
     ---|------@--@|@----@----@----@--@--@---­----------
     ---|------@@@@|@@@@-@----@----@--@--@---­----------
     ---|------@--@|@----@----@----@--@------­----------
     ---|------@--@|@@@@-@@@@-@@@@--@@---@---­----------
     ---|----------|-------------------------­----------
     ---|----------|-------------------------­----------
     ~~~|~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~~~~~~­~~~~~~~~~~
        '----------'
                \
                 'Rotating' view port - begin @ col 4
    
      _0_  _1_  _2_  _3_  _4_  _5_  _6_  _7_  _8_  _9_ 
     /   \/   \/   \/   \/   \/   \/   \/   \/   \/   \          
     012345678 1 2345678 2 2345678 3 2345678 4 23456789
         .----------.                       .
     ----|----@--@-@|@@@-@----@-----@@---@--.­----------
     ----|----@--@-@|----@----@----@--@--@---­----------
     ----|----@@@@-@|@@@-@----@----@--@--@--.­----------
     ----|----@--@-@|----@----@----@--@------­----------
     ----|----@--@-@|@@@-@@@@-@@@@--@@---@--.­----------
     ----|----------|------------------------­----------
     ----|----------|-----------------------.­----------
     ~~~~|~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~~~~~­~~~~~~~~~~
         |0123456789|<--col 0..9 displayed  ^
         '----------'                       |
          ^      \                          |
          |       \                         m = 40 - 1
          |       'Rotating' view port - begin @ col 5
     0 .. p=5 .. m (=39) - display position p shifting thru
    

    I guess this is enough to show - and see - what is going on...

    With this setup, the algorithm becomes very very simple
    (b is the buffer):

    // "  HELLO!  " - 2+6+2=10 chars is 50 cols pixels with 
    // 6x4 font + 1 pixel char space displayed in 2x 7x5 matrix
    var n = 10; // times to 'scan' every frame that is pushed
    var m = 40; // m buffer col begins to shift thru (0..39)
    var w = 10; // w cols displayed at once (8x10 display)
    var p = -1, i, j, k, s = n - 1;
    
    function scan() {
      if (++s>=n) { 
        s = 0;
        if (++p>=m) {
          p = 0; // start over
        }
      }
      i = p - 1; k = -1;   // i byte to put out
      while (++k<w) { i++; // k col in view port
        // push b[i] out
      }
    }
    
    setInterval(scan,20);
    

    The lower n, the faster the text shifts thru...

    Conclusion: Single line text plus as many blanks before and
    after that can be displayed at once in the display defines
    the buffer size. So any text length can be displayed shifting
    thru the display.

    • m

    PS: Sorry, the show and tell on my almost 40 years old DIY LED matrix has to wait another post...

  • Finally 'almost final' working example with picture and clip of 7 seconds: testing the scan algorithm that puts out an Espruino Graphics buffer onto a LED matrix.

    The test uses the drawing of a sequence of rectangles (rs[]) to show the workings. Two independently and 'continuously' running loops do the job:

    1. Drawing rectangles one ofter the other - about every half second one (500 [ms]).
    2. Scanning the buffer and driving the leds - about a column every 3 [ms].

    'Continuously' is achieved by delayed self re-invocation at the end of each function after first invocation in onInit() using setTimeot() because that allows the pseudo-multi-threaded-ness of the application on a single threaded platform, such as JavaScript, and on top of that, Espruino, which has also to 'squeeze' in handling of hardware interrupts... (the display would anyway be only a part of the overall function of the whole system). There is the Espruino provide module that does charlie-plexing (http://www.espruino.com/CharlieplexI), ready-made for use, with one difference: it requires invocation of the Graphics.flip() method to transfer - in an Export-Transform-Load (ETL) manner - the Graphics buffer into a useful working buffer for the actual charlieplexing. Using the .flip() method has the advantage of separating the concerns of having a buffer to draw to for the Graphics object, and the scanning and driving w/ charlie-plexing the display ILED matrix. The disadvantage though is that the application has to initiate the transfer. Even though the latter is named as a disadvantage, it can be an advantage: only completely drawn frames are passed for display, because the application 'usually' knows - in a less interrupt / unpredictable situation - when it is done with drawing and has completed a frame.

    INTERESTING due to low-light condition: The second pic catches a transition between two drawings - shorter time scanning the 'outgoing' 4x3 rectangle and longer time of scanning begin of 'incoming' indented 3x3 rectangle - and the clearing of the display and drawing of the new rectangle in between. The transition is caught by the shutter time straddling the two drawings. That the display time of the outgoing drawing is shorter is noticeable by the first column being less bright (more dim) than the incoming drawing... :) . One should even be able to notice that the LED second from left and top is less bright than the other ones of the incoming rectangle, because the other ones are lit in both of the rectangles / frames... :O. It would be noticeable if LEDs would be properly aligned to project light in exact same direction (to the focal point), which they clearly do not... :/ sorry for that...

    With the low duty cycle and the diodes in the cathode lines I was not worried about the average forward voltage and current... about 2.6V from Espruino PIC on 2..2.2V red LEDs... - 'old' LEDs, not able to handle the today's regular 20mA (I have anyway not to worry about their life time hours). The diodes are there to protect these LEDS with their weak / low reverse voltage breakdown... Was thought just reading up on it and became aware that older manufacturing techniques yield actually better / higher reverse voltage breakdowns than newer ones... but when I bought them decades ago w/ student money, it was too expensive to take the risk... hahah). Also, I'm not worried about the LEDs taking down Espruino or 'over sinking' the STM32F4 chip: only max three (3) LEDs can be up at one time, way below the max of the chip's supply capability. For more about this particular DIY and other LED matrix' with close pictures, see conversation about Driving LED matrix with shift registers such as 74HC595.

    // M5x7mm0.js
    // shows continuous scaning and Charlie-Plexing LED Matrix
    // from Espruino Graphics buffer. Displays buffer content.
    
    // 5x7 (5x3) RED LED MATRIX w/ row anodes and columns cathods
    // Algorithm works a complete 7 pixels / rows tall display,
    // even though actual matrix hardware 'implmeents' / uses only
    // 3 2rows (Ignore the (5) diodes in the column / cathodes lines,
    // they would be resistors depending limiting the forward current).
    var rps = [ A5,  A8,  B7,  B6,  B5,  B4,  B3]; // row pins anodes
    var cps = [ A6,  A7,  B1, B10, B13]; // col pins csthodes
    
    var g = Graphics.createArrayBuffer(5,8,1,{vertic­al_byte:true});
    var b = new Uint8Array(g.buffer);
    
    // "  HELLO!  " - 2+6+2=10 chars is 50 cols pixels with 
    // 6x4 font + 1 pixel char space displayed in 2x 7x5 matrix
    var d =  3; // delay in ms
    var n = 10; // times to 'scan' every frame that is pushed
    var m = 40; // m buffer col begins to shift thru (0..39)
    var w = 10; // w cols displayed at once (8x10 display)
    
    // Overriding for test with rectangles
    // - static, no shifting, because m=0 -
    // and display port is only 5 cols wide.
    m = 0; w = 5;
    
    // setup scan start
    var p = -1, i, j, k, s = n - 1, c;
    // continually scan out the Graphics buffer 
    // in 'Charlie-Plexing LED matrix'-manner.
    function scan() { 
      if (++s>=n) { 
        s = 0;
        if (++p>=m) {
          p = 0; // start over
        }
      }
      i = p;  // i byte to put out
      k = -1; // k col in view port
      c = 30; // (2 ^ 5) -1; // ...11110
      while (++k<w) {
        digitalWrite(rps,b[i]);
        digitalWrite(cps,c);       
         i++; c = (c<<1) | 1;
        digitalWrite(cps,31);
      }
      if (d) { // keeps calling itself after d [ms]
        setTimeout(scan,d);
      }
    }
    
    var rs =    // defines sequence of rectangles
    [ [0,0,0,0] // top row 1 dot
    , [1,0,1,0] // top row 2nd dot
    , [2,0,2,0] // top row 3rd dot
    , [3,0,3,0] // top row 4th dot
    , [4,0,4,0] // top row 5th dot
    , [0,0,1,0] // top row 2 dots
    , [0,0,2,0] // top row 3 dots
    , [0,0,4,0] // top row 5 dots
    , [3,0,4,0] // top row last 2 dots
    , [0,1,0,1] // 2nd row 1 dot
    , [0,1,1,1] // 2nd row 2 dots 
    , [0,1,2,1] // 2nd row 3 dots
    , [0,1,4,1] // 2nd row 5 dots
    , [3,1,4,1] // 2nd row last 2 dots
    , [0,0,3,2] // real rectangle
    , [0,0,3,2] // real rectangle
    , [1,0,3,2] // real rectangle
    , [1,0,3,2] // real rectangle
    , [1,0,2,2] // real rectangle
    , [1,0,2,2] // real rectangle
    , [1,1,2,2] // real rectangle
    ];
    
    var rsc = rs.length; // rectangle cound (# of rects9
    var rx = -1;  // rect index to iterate thru rects []
    var rd = 500; // a new rectangle about every rd [ms]
    function drawRects() {
      if (++rx>=rsc) {
        rx = 0;
      }
      g.clear();
      g.drawRect.apply(g,rs[rx]);
      if (rd) { // keeps calling itself after rd [ms]
        setTimeout(drawRects,rd);
      }
    }
    
    function onInit() {
      scan();      // keeps calling itself after d [ms]
      drawRects(); // keeps calling itself after rd [ms]
    }
    
    setTimeout(onInit,500);
    

    3 Attachments

  • Eric, the

    interest to display in col vs in row

    is to make algorithm life easy, have less code to be executed, and thus a brighter display - more scans resulting in more 'ON'-time are possible in a given time.

    In computer science, Algorithm and Datastructure are complementing elements, like amount of memory and performance (execution time/speed), and same as in many other areas of engineering disciplines: one thrives on the cost of the other one and vice versa. But if a good balance can be struck, Résultats imprévus exceptionnels se manifestant! (...I hope I got that right - google helped me with the details... no kidding. It's 25+ years past my fluency).

    In engineering, there are areas of freedom and 'non-negotiable' ones, or - as I like to point out - life goes much easier not thinking in black boxes or only inside or only outside boxes: the freedom to budge or choose in one area is key to success in the or another one. In this case at hand - having 8 vertical bits store in a 1 byte - makes the algorithm simple, because all information for one scan cycle stays within one whole byte - no straddling or figuring out or complicated picking of individual bits out of many different bytes to put out in one scan cycle... ;-). If the LED matrix would be taller than 8 pixels, it would just be multiple bytes with a skew factor, such as the first, the 51st, the 101st,... with a buffer supporting a with of 50 pixels / columns.

    I hope this answers your thoughts.

    Btw, what does Mr BPP mean?.... now response required... just for curiosity, that can stay unanswered.

  • a next step...

    Pulled some TBC20-12/... from junk - bi-color green/red and orange 5x7 LED modules... and I did - for testing them before investing more work - drive them directly with PICO... When both LEDs are on, the color is an orange. Attached you find some clips and pictures.

    Below you find the code - which goes through each individual LED with given color. In console I then entered instant setInterval to indefinitely scroll through the colors while the display goes through all LEDs, one by one.

    // TBC20-12-test5.js
    // 5x7 red, green and orange dot display w/ common cathode (bi-color)
    // http://www.kingbrightusa.com/images/cata­log/SPEC/TBC20-12EGWA.pdf
    // cycle to all dots in selected color
    //
    // NOTE: use auto pinMode!
    //
    // --- Wiring:
    // Row R1..R7 Anodes - common, for all AND green and red rows
    // Col gC1..gC5 and rC1..rC5 green and rED Cathodes - ten (10)
    // GPIOs Espruibo PICO A# and B#
    //
    //   4    5    6    7    8    9   10   11   12  (13)
    //  B3   B4   B5   B6   B7   A8   B8   B9  A10  (A0)
    //   |    |    |    |    |    |    |    |    |  - direct connetion
    //  18   17   16   15   14   13   12   11   10    watch alignment
    // rC1   R3   R1   R2   R4  gC3  gC4  rC5  gC5
    //
    //
    // gC1  gC2  rC2  rC3   R6   R7  rC4   R5  NC?
    //   1    2    3    4    5    6    7    8    9   watch alignment
    //   :    :    :    :    :    :    :    :    : - wired from 'above'
    // B13  B10   B1   A7   A6   A5   A4   A3   A2  (A1)
    //  23   22   21   20   19   18   17   16   15  (1$)
    
    // --- Row / Col pin assignments (according to wiring, see picture)
    //
    //        R1  R2  R3  R4  R5  R6  R7
    var rps=[ B5, B6, B4, B7, A3, A6, A5]; // row pins - R1..R7
    //
    //       rC1 rC2 rC3 rC4 rC5 gC1 gC2 gC3 gC4 gC5
    var cps=[ B3, B1, A7, A4, B9,B13,B10, A8, B8,A10]; // col pins (r, g)
    
    // --- Hauskeeping
    var rMax=6; // Row maximum 0..6
    var cMax=4; // Col maximum 0..4
    var rpOn, cpOn; // previus row/col pin(s) on for switching off in next cycle
    var r=rMax, c=cMax; // row/col last - but not on - to start with first
    
    // --- Rum parameters - speed and color
    // can be change in console while running
    var t0=100;  // default and previous cycle time (see below)
    var t=t0;    // cycle time (>0) - cycle invokes itself after t [ms]
    var color=1; // Color 1=red, 2=green, 3=orange (red and green at same time)
    
    // core function: cycle:
    // - switch old of, increment and switch new on - col as well as row
    // - row concitional on column start over
    function cycle() {
      if (cpOn) digitalRead(cpOn); // 'switch' off (by making it/them input) 
      if (++c>cMax) { // start over w/ col 0
        c=0;
        if (rpOn) digitalRead(rpOn); // 'switch off' (by making it/them input) 
        if (++r>rMax) { // start over w/ row 0
          r=0;
        }
        rpOn=rps[r]; // row pin
        digitalWrite(rpOn,1); // pull anode up
      }
      cpOn // col pin(s)                 // select col pin(s) for color case:
        = (color==3) ? [cps[c],cps[c+5]] // both red and green creating orange
        : (color==2) ? cps[c+5]          // green
        : cps[c]                         // red (default)
        ;
      digitalWrite(cpOn,0); // pull cathode(s) down
      if (t>0) {
        setTimeout(cycle,t);
      } else {
        if (cpOn) digitalRead(cpOn); // 'switch' off (by making it/them input) 
        if (rpOn) digitalRead(rpOn); // 'switch off' (by making it/them input) 
      }
    }
    
    function h() { // halt
      t0=t;
      t=0;
    }
    function resume() { // resume / run
      t=t0;
      cycle();
    }
    function onInit() {
      cycle(); // auto start cycle onInit
    }
    
    setTimeout(onInit,500); // auto onInit while developing
    

    The first shot is taken with flash: you can barely see green in 6th row and 1st column on. I could increase the supply (Anode) voltage to 5 Volt, for example, but that would already require some driver... even if it is just a buffer... or level shifter. Second shot is taken with normal light and color can be seen better... of course, it is also red, which is easy to detect. Last shot is interesting: because taken in low light, the shutter picked up an outgoing and an incoming LED... ;O

    Putting multiples of these 5x7 LED modules into a panel requires lots of losts of lots of wiring... therefore, the post about running text out of an Espruino Graphics buffer will come a bit later.


    5 Attachments

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

use a led matrix 5x7 without driver chip?

Posted by Avatar for Mrbbp @Mrbbp

Actions