Driving a multiplexed display: the other way

I’ve been a little busy on another side project but I’m still thinking about how to implement this.

Having the “host” code manage the data to push over to the library has advantages, while handling all the data in the library could stifle management. Not sure.

I’ll have to talk to someone I know about whether to use software timers or hardware interrupts for this type of thing. Flexibility and adapability is key, thus I think keeping it as simple as possible is probably best.

The software timer method (as used above) requires that you place a this piece of code in the loop().

blockTimer.update();

In the test code, I’ve put it in the bar graphing function. This line is then used to run the driver.

blockTimer.every(3, LEDdriver);

My best guess is that when the first box above is executed it simply calls milli() and checks for the time elapsed since the functioned named in the second box was last called. When that time exceeds the amount specified, it calls whatever function it’s been told to call again, in this case the LEDdriver() function. What this means though is that the end user has to insert the code in the first box above somewhere in his sketch and it has to be called more often than update time. My thinking is that places too much of a dependency on the end user for library type usage. Heck, there’s no way to ensure that placement anywhere in some user’s loop() will get it to check the elapsed time in under 3 msecs. That leaves the display update timing to the tender mercies of future unknown sketches.

It’s more reliable IMO to program up a hardware timer and have it run an interrupt as part of the code that runs when you instantiate the library function. Then warn the user in the library notes that it uses timerX (probably 2) and that it conflicts with other functions like tone(), etc, etc.

That means the above code is using what I think is the wrong method … but it should work well enough for the test code to develop all the other displays you wanted. Then, at the end, it can be swapped over to a HW timer.

FWIW another course is to avoid having the software involved in the multiplexing of the blocks. You could instead use HW to do that. I think a [7219 could be configured to run the display and that would mean the code only has to write to the IC whenever you want the display to change. The IC does all the multiplexing. Saves on pins used as well. And there may be a less complex IC that does the same function for fewer LEDs than does the 7219/7221. I’ve not looked.

Even if you were to go w/a 7219, the code above wouldn’t change all that much. The test sketch would look more like the final library version as the driver portion would be just another function called from the graphing function. The driver portion would change but all the tricky code in LEDbargraph() would remain the same. Which is one reason why I set it up the way I have.](LED Display Driver (8-Digit) - MAX7219CNG - COM-09622 - SparkFun Electronics)

That is what I was thinking of. The µC does work pretty quick on simple code so I don’t think it needs ultra-fast responsiveness. According to some developer of old software (think: 1984, 1985 ish), a delay of 10ms from the new information (like opening a menu, closing a dialog box, …) won’t really be noticable by the user, and 20ms is fast enough that they won’t complain much. More than that and “it’s slow!” begins to pop up. (It was something to do about optimizing code for computer platforms, and incidentally enough I can’t find the webpage anymore.)

Here’s 2 GIFs of the same 3 “blocks”. Pretend they are your mutiplexed display and you want all the block to appear to be on all the time. The 1’st GIF shows a 100 msec period btw block-block updates. The second is 20 msec (or as fast as your browser can show). In the first it’s clear that the 3 block are being switched on and off. In the latter there’s some flickering but it appears mostly on (though dimmer). I generally use 100 Hz (10 msec for the whole display, 3.3 msec btw the 3 blovks here) as my min update rate so as to not see flickering.

Click on to animate the GIF.

How do you have the display wired up to the Arduino and to the B595 ?

Do you “grok” how I did the block1 driver code ?

FWIW here’s one possible way to use a 7219 to drive this type of display.

(click on to open)

Mee_n_Mac:
How do you have the display wired up to the Arduino and to the B595 ?

Like this, I don’t have a schematic ready. http://i.imgur.com/mOmOr2d.jpg

The 6B595 is not wired the same as a regular 74HC595. On a 74HC595, the Clock/SCK/SH_CP/SRCK is Pin 11, but it’s 13 on the 6GB595. The Latch/RCK/ST_CP/RCK is 12 on the 74HC595 and it’s the same on the 6B595. The Data/SER/DS/SER IN is Pin 14 on the 74HC595 but it’s Pin 3 on the 6B595.

Block1 driver code – I kind of get it yes. I spoke to someone I know that should be able to inject more input on this subject on Wednesday and said he’ll take a look at it this weekend.

When you mentioned the MAX7219 (or MAX7221) you’re probably right. I do have a 7219 on hand (only one :mrgreen: they’re kind of expensive).

EDIT: I just had an idea. We can “spread out” the multiplexing time over the area of the display. Say we’re currently drawing this image here on the display:

http://i.imgur.com/2UWqMKV.png

We can draw the greens on on the first block, then the reds on the third block, greens on the second block, reds on the first block, reds on the third block, and then reds in the second block, or something like that. Then again if the multiplexing is fast enough from L->R then it probably won’t matter, but I might have to crank down the resistance on the resistors (I’m using 220Ω right now) to get a brighter image. Not sure on that one, I’ll parse the datasheet and figure out what is “okay” for a duty cycle.

32768:
… I’ll parse the datasheet and figure out what is “okay” for a duty cycle.

You won't find that in any datasheet. It's a matter of not having any 1 (or 2) block(s) off for so long that the human can detect that it's off. And that's completely separate from what colors are on.

The B595 SR holds the color data. Four of it’s outputs control the R LEDs and the other 4 control the green LEDs. You only have to load the SR up with the proper data to make whatever color you want happen. The “driver” loads up that appropriate bits for each block and loads the SR. The LEDbargraph() function is where what colors are to be on is determined. ATM just to keep things simple (to understand and to debug), it only calculates the green LEDs. Extending that to turn on other colors isn’t that hard. Let’s look at mode1 (as it is), which presently takes a 0 to 100 % input, and turns on 0 to 12 LEDs, presently only the green ones.

  switch (mode){
    case 1:
    //this is the processing the will setup the left to right filling
    //first determine how many LEDs need to be on
    Mag = int(map(data, 0, 100, 0, 12));
    if(Mag > 0){
      for(int i = 1; i <= Mag; i++){
        LEDs_on += 1 << (i-1);
      }
    }
    //now turn on/off the colors as dictated by this mode
    //right now we're doing a single color
    GLEDs_working = LEDs_on;
    break;

Let’s assume for the moment that my code correctly (ha!) determines the variable LEDs_on. This variable is a bitwise map of which LEDs need to be on. The lsb corresponds to the least significant LED. A 1 in any bit position indicates that LED should be on. But now the question is … which LEDs, the green and/or the red, should be on ? Above (again KISS) it’s all green and so the in-process working variable, GLEDs_working, is just a copy of LEDs_on.

But now let’s make it like your original GIF. The least significant 5 LEDs, if on, are green. The next 4 LEDS, if on, are yellow. And the 3 remaining LED, if on, are red. No doubt there are many ways to do this, here’s just one. See if you can find a more efficient way. Let me use masking to copy those bits that are supposed to be on from LEDS_on to both working variables for G and R LEDs. Know that bitwise AND’ing of the mask copies only those bits that are a 1 in both the source and the mask.

http://arduino.cc/en/Reference/BitwiseAnd

    //for green or yellow the G LED must be on so allow the bottom 9 bits to be on if needed
    GLEDs_working = LEDs_on & 0x01FF ;
    //for yellow or red the R LEDS must be on 
    //so allow the top 7 (out of 12) bits to be on if needed
    RLEDs_working = LEDs_on & 0x0FE0 ;

Later on in the function the working variables are copied into the variables GLEDs and RLEDs which the driver uses to configure the B595 SR.

FWIW I’ve typed the Hex equivalent (I think in hex better than in binary) of the masks but if you convert hex to binary you can see how the mask does what it does and why I bothered to have a variable called LEDs_on.

0x01FF = 0000 0001 1111 1111 b

0x0FE0 = 0000 1111 1110 0000 b

Recall that the upper 4 bits in the 16 bit word aren’t used in your 12 LED case.

Update: Still waiting for my friend to report back. I’ll assume he’s still bogged down with C# work.

When we do get further updates, we’ll promptly resume.

I think that if you want to truely understand embedded programming, this is the time to dig in and play with the details until you do understand them. In this case, just the “driver” code, and lets simplify it for the moment. Forget how the driver gets to run, just assume that it will run every 3 msec. Forget how the LED blocks 2 and 3 get illuminated, just concentrate on how block1 does. What must happen to make any 1 R or any 1 G LED become illuminated ?

The answer is the block1 anode must be turned on somehow. OK, there will be some code to do that. Then there must be a logic 0 loaded into the B595 SR, in 1 of the 8 bit positions that it can hold, such that that bit position maps to the LED you want on. That’s a matter of both programming and how the SR is wired to the display. The wiring dictates the meaning of the bit positions in the SR. To make programming easy, wire the SR so the 4 R LEDs correspond to 4 consecutive bits in the SR, either positions 0-3 or 4-7; it makes only a small difference in programming. Naturally the G LEDs go in the other 4 bits. So the code must load the SR in the order mandated by the hardware wiring. You could figure out what bit patterns are needed for every possible combination of R and G LEDs but simplify it a just figure out those bits, 0-7, that must be loaded into the SR to make the 12 displays you want. Of course the block1 programming is only the least significant (or leftmost) 4 R+G LEDs.

Then it becomes a small leap to understand how to code a function that can take the bits in RLEDs and GLEDs and turn those into the patterns you just listed. To make that leap read the prior links on masking (bitwise logical AND function) and shifting. No friend needed, just some time to understand the very low level bit by bit shuffling that’s needed.