Driving a multiplexed display: the other way

I bought a number of these small 12-segment [Red/Green bargraph displays.

They’re neat. But they’re multiplexed, having 14 pins. [Here’s the datasheet.

My plan is to use of one of these as indicators, like fluid or battery voltage level displays. The end goal is to make a library for these, although given the variety of modes I’d like to do, that could be a very interesting project:

  • - All red, ascending from left to right
  • - All red, ascending from right to left
  • - All red, middle marker, used for something like a compass, where left and right markers indicate how far off you are from "middle"
  • - Individual red digit control; i.e. turn on digit 4, 8 and 10 at will
  • - All of the above, but in green
  • - All of the above, but in yellow
  • - Middle marker style, but with green in the middle, yellow, then finally red towards the edges
  • - Five green digits from the left, four yellow, and three red in an ascending fashion like a voltage level
  • - ibid, but reversed from right to left
  • That’s what comes to mind.

    I’m using a TPIC6B595 ([datasheet) to drive the cathodes (grounds) of the LEDs, three transistors to drive the anodes (voltage +) on the display.

    A picture of my setup is shown below. The code on the µC is the project from Beginning Arduino, Project 17: binary counter with the 74HC595. A video of the whole thing working is [here, and a picture [here.

    Here’s my basic idea: A potentiometer will be hooked up to A0 which will be the input data source. I then I have to figure out how to send bytes out to the shift register that enable the display modes I’d like to do above.

    How should I proceed?](Imgur: The magic of the Internet)](http://www.youtube.com/watch?v=D3NFDuPy_58)](http://www.adafruit.com/datasheets/tpic6b595.pdf)](http://www.adafruit.com/datasheets/tpic6b595.pdf)](Bi-Color (Red/Green) 12-LED Bargraph : ID 459 : Adafruit Industries, Unique & fun DIY electronics and kits)

    I found Sparkfun’s example code they supplied with their 30-LED bargraph and it has some random output that it pushes out to the shift register.

    const int LAT = 9;  // Latch
    const int SIN = 11;  // Serial data input (to bargraph)
    const int CLK = 12;  // Clock
    
    // Default pin assignments for Mega Arduinos
    // to use, uncomment the below lines, and comment the above lines
    
    //const int LAT = 53;  // Latch
    //const int SIN = 51;  // Serial data input (to bargraph)
    //const int CLK = 52;  // Clock
    
    void setup()
    // Runs once upon reboot
    {
      // Set pins to output
      pinMode(LAT,OUTPUT);
      pinMode(SIN,OUTPUT);
      pinMode(CLK,OUTPUT);
    }
    
    void loop()
    // Runs continuously after setup() ends
    {
      // This example code will shift random 1s or 0s into the bargraph, and toggle the
      // LAT pin on every bit so we can see the data sliding down the shift registers.
      // (Note that you can't do this with the library or SPI interface, as it sends 8 bits at a time!)
    
      // To clock data into the Bargraph, set SIN to the state you want, and transition CLK L to H.
    
      // To transfer the internal shift register data to the output pins (LEDs), transition LAT L to H.
    
      digitalWrite(LAT,LOW); // Make LAT low
    
      // Put a random bit (1 or 0) into the shift register:
    
      digitalWrite(CLK,LOW); // Make CLK low
      digitalWrite(SIN,random(2)); // Put a random 0 or 1 on SIN (this is your input data)
      digitalWrite(CLK,HIGH); // Make CLK high - this loads SIN into the shift register
    
      // Normally we would  do the above 32 times to fill all the shift registers with the data you want,
      // then toggle the LAT pin to make it visible. But we'll do that here for EACH bit we clock in,
      // to show the data flowing through the shift registers:
    
      digitalWrite(LAT,HIGH);
    
      delay(500); // Short delay so we can see the data, try making it faster or slower!
    }
    

    Less weird colors being produced this time.

    I have a feeling Me’n Mac might become interested in this at some point… :mrgreen:

    Not much I can add really. There’s just a fair amount of head scratching to implement all the modes you want. One thing to consider up front is whether you want to extend the shift register’s “length” and put the 3 bits controlling the anodes into the bitstream in order to free up 3 pins.

    Well I did previously chop the transistors off the circuit and they are now just driven right off the +5 rail. I thought about it for a little bit, and then it made little sense to have the anodes driven by the 328P.

    I do know that the ascending “VU type display” will require some kind of “for” loop.

    compass indicator…thats cool

    So now that I’ve got some time freed up from finishing another project I came back to this. It’s a little harder than you might think at first.

    The display is broken up into three blocks, each containing four Red/Green LEDs. If I want to make an “ascending” “filler” mode where it shows, say the “capacity” of something, then I have to drive it fast enough to show the first and second blocks while the third might be the one that has “activity”. A standard 10 digit bargraph doesn’t have this problem because it only has 10 digits (10 bit shift register, problem solved), it is not multiplexed and it doesn’t have to worry about two colors at once.

    So if I want to make one of these behave like a volume indicator, liquid capacity indicator, or what have you, I have to figure out how to do it. The only problem is I lack the programmerese to do so.

    I do know that I need three global variables, one for each block. The code would then read the value of the first block, shift it out, then read the second block, shift it out, then the last block as prior. The general idea is probably make it a library, where you’d just have to feed it a value, either from a µC or something else like a pot and it would be taken care of.

    Suggestions…

    Your link to the datasheet for the display goes to the wrong place (a B595). It sounds like the anodes from each “block” are tied together and then the cathodes are common btw the blocks ? The display has 4 R control lines, 4 G control lines and 3 anode (or “block”) control lines ? A diagram or the datasheet would be helpful.

    Your scheme (I’m guessing) then has the R/G on/off data for a single “block” stored in the B595. You enable whatever LEDs are supposed to be on via the transistor (a PNP to +V). Then you shift the data for the next “block” into the B595 and change the transistor. Wash, rinse, repeat, block by block. Your question is then how do you best store the data and clock it out to the B595 and control the pins for the PNPs. And how do you map the input data into color and number of LEDs to be on. And then from that information into the data to be sent to the B595 and transistors ?

    The datasheet for the display [is here. I’m using a TPIC6B595 for driving the cathodes because a regular 74HC595 won’t work because the TPIC sinks current instead of sourcing it, and the TPIC can sink 150mA per pin, which is more than enough for two LEDs on per digit.

    There are 8 cathode lines, and three anode lines. Each anode line controls one “block” of four digits. Four cathode lines control the red LEDs “per that block” and four cathode lines are for the green lines.

    I’m working on an animated GIF which will show all the modes I’d like to do. Basically I’m trying to write a library.](http://www.adafruit.com/datasheets/BL-AR12Z3010DUG.pdf)

    With knowing all the intended uses of this let me say I’d start with the basics (which you may already have figured out per your video).

    A single display gives me 2 dimensions to play with; one of magnitude and one of color. Magnitude determines how many LEDs will be on (or if there’s 1, where it’ll be) and color is obvious, though I’m not sure how best to use it. So I’d start by remapping the input signal into 13 levels of magnitude; 0 - 12 and store that in an variable … Mag.

    Then, depending on the display mode, I’d use Mag and the color info (in some way I don’t know right now) to create a 2 dimensional array of single bit values. This would store the on/off info for the entire display.

    Then I’d have a “driver” that translates that array into the bits to be clocked into the B595’s and the transistor control lines.

    Why would I segregate the task this way ? For me it divides the job into parts that I can understand, rather that trying to do it all in 1 swell foop. Also I might think that others might find a way around the B595s and use another hardware implementation to do the task. Replacing the driver portion only allows that. Lastly, and perhaps most importantly to your implementation, I see that you should (to do it “right”) have a “background” set of processing and an interrupt driven portion (the driver) that allows however long it takes to determine the LED on/off states w/o affecting the multiplexing times. Think of it this way … you want all the “blocks” to be of the same brightness. That means the LEDs for any one “block” must be on for the same amount of time as any other “block”. To me that screams using a hardware timer to do the driver portion. The rest of the code runs as fast, or as slow, as it needs to … independent of the LED update times … which needs to happen fast enough not to be seen flickering, and equally so the brightness doesn’t vary “block” to “block” (unless you want it to).

    Ohkay, rendering done. Just not professionally :mrgreen:

    Center Spread Type 1: Notice how the two middle ones light up, this is used when “you’re in the middle”. Possible uses: like a compass, direction indicator, robot path line follower…

    http://i.imgur.com/Vp9antw.gif

    Center Spread Type 2: Individual illumination.

    http://i.imgur.com/0K8DFQW.gif

    Filler mode, Left to right, Red Yellow Green. I expect this one to be somewhat popular.

    http://i.imgur.com/nx0ZKAN.gif

    Same as last, reversed.

    http://i.imgur.com/7CCbSbU.gif

    Filler mode, Full RYG Left>Right, which shows 12 green then 12 yellow and finally 12 red. Meant for precision, basically a “stretched” version of the previous two models.

    http://i.imgur.com/5ccFNtV.gif

    Same as previous, but reversed direction:

    http://i.imgur.com/IrAoPWX.gif

    Filler Mode Green only, Red only and Yellow only with left>right and right<left variations:

    http://i.imgur.com/XnLg6HS.gif http://i.imgur.com/UfA2NDM.gif

    http://i.imgur.com/2FfwUhO.gif http://i.imgur.com/Bh04hRS.gif

    http://i.imgur.com/1xjKQv5.gif http://i.imgur.com/hRJ2QqA.gif

    Similar to last one, but individual illumination. Saves battery? I guess you could just use a “Check Status” button if you were concerned about drawing lots of current. Also have a reverse direction but I goofed the order in the gif maker. Oops. You get it though.

    http://i.imgur.com/e6ub8DR.gif

    Reverse filler mode. This one starts out as full green then goes back to yellow and red. Good for a battery indicator of sorts, or a water capacity tank indicator, whatever.

    http://i.imgur.com/EVDVa7V.gif

    Looks like a long list of case statements.

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

    Two questions ;

    1. where does the data come from ? A/D, serial port, ???

    2. how does the user set the “mode” ?

    Well, you’d “pick” whichever mode you’d like when writing the code, and it’s “fixed” from there. It doesn’t change from say, Reverse Filler Mode to Center Spread Type 1.

    A couple of days ago I was fiddling around with [this code, and instead of shifting out the curb variable, shifting out a B00001111 or something like that instead. I’m sure it can be done with hex values too.

    Here’s what I think the basis for this project is: Three byte variables, one for each “block” of four digits (the first four binary MSB, BXXXX are mapped to green as I’ve found, and B0000XXXX or the last four LSBs are mapped to red.) and just an array for each byte variable. All the colors, direction, etc are all made up in the array, i.e. {B11001100, B11101110, B11111111} and so on.

    I just don’t know if I’m on the right track or not, and “how to write it”. I don’t know how to “parse an array and decide which value to choose” (although I did pick up tips from that last PWM RGB LED mixer project). I’m doing the work on figuring out all the possible “modes” for this display, and isolate the “common ones” (note how the Filler Mode Red Yellow Green is the same thing, basically, as the Filler Mode Yellow only), as I suspect there’ll be only like two or three code “variants”, the rest is just a matter of changing binary values.

    I’m not sure how the data input is supposed to be supplied. It’d be nice for a library (or just a function) where the value could be “fed” into it, just like when I call the lcd.print() function, it’s all taken care of. That way it’s “portable”. I wouldn’t know – I know just enough of this stuff to get myself into trouble, not out of it :mrgreen:

    Could it be related or similar to the barGraph library found in the Arduino IDE?](lucidtronix.com - This website is for sale! - lucidtronix Resources and Information.)

    I finally get what you want to do. :oops:

    Never written a library before but how hard can it be … it’s only software. :? :pray:

    Start with this :

    http://arduino.cc/en/Hacking/LibraryTutorial

    To use in a sketch you’d create an instance of your new library, LEDbargraph(), which would specify which pins were connected to the SR and which to the anode transistors and possibly how many segments and whether the device is single color, R/G, or R/G/B (do they even exist).

    Then the code would call the function name and pass it the data to be graphed and display mode (and ??).

    My first thought is to write a sketch that will eventually become the .cpp file in the library. Get the display displaying the way you want and then figure out how to write the header file and make it all into a library.

    Mee_n_Mac:
    I finally get what you want to do. :oops:

    Never written a library before but how hard can it be … it’s only software. :? :pray:

    Nothing could go wrong then, right? http://apolyton.net/images/smilies/bored.gif

    I think we ought focus on how to display what we want first, and then we’ll figure out how to make it into a library. I don’t have a clue how to start, other than by cloning/modifying other people’s code. :mrgreen: Did the lucidtronix link offer anything useful?

    whether the device is single color, R/G, or R/G/B (do they even exist).

    Not that I know of. Mouser has a weird bicolor 10 segment display that has more pins than this does. Betlux is the manufacturer of this display and they have a orange/green model available (why, I don’t know…why not blue/red or something).

    Here’s that weird one: [page, [datasheet; there is [this one and [this other one with [datasheet for both. The first one is customizable but has 30 pins and I’ll wager a non-breadboard/prototyping board friendly pinout; the last two are pre-defined colors.

    I think this is looking somewhat closer.

    Maybe. After the last element of the array has been done for the last block (block 3), there is some “junk” that gets shifted out. Each block is written twice instead of once, which is probably something to do with the ShiftOut function.

    More help needed. :mrgreen:

    // This project is to map an analog pot to a corresponding value of LEDs via a
    // shift register. This is Stage 1 of the 12-LED Bargraph Display Project. 
    // Based on Project 17 of the Beginning Arduino book and barGraph by Tom Igoe
    
    // VERSION 0.1 BETA TESTING PHASE: Initial goofing off
    
    int latchPin = 4; // Latch / RCK / Pin 12 (74HC595 equivalent) / ST_CP / (RCK Pin 12 TPIC6B595N)
    int clockPin = 12; // Clock / SCK / Pin 11 (74HC595 equivalent) / SH_CP / SRCK Pin 13 (TPIC6B595N)
    int dataPin = 11; // Data / SER / Pin 14 (74HC595 equivalent) / DS / SER IN Pin 3 (TPIC6B595N)
    
    /* The display is broken up into three blocks of four digits each, with two colors per digit.
    Assuming the angled corner indicator is oriented to the lower left, Block 1 is digits A through D,
    Block 2 is digits E through H, and Block 3 is digits I through L. The anodes are switched on manually, 
    then the array is parsed for the appropiate value to send to the shift register. The following defines 
    what pins the anodes are activated on.*/
    
    int block1Anode = 10;
    int block2Anode = 6;
    int block3Anode = 9;
    
    /* These are the arrays for each of the blocks. They are binary values, the first four digits from left
    to right correspond to a green value, the last four are mapped to the red values.*/
    
    byte block1[] = {B00001111, B11110000, B11111111, B00000000};
    byte block2[] = {B00001111, B11110000, B11111111, B00000000};
    byte block3[] = {B00001111, B11110000, B11111111, B00000000};
    
    void setup() {
      pinMode(latchPin, OUTPUT); // set all pins to output mode
      pinMode(clockPin, OUTPUT);
      pinMode(dataPin, OUTPUT);
    }
    
    void loop() {
      block1Func();
      block2Func();
      block3Func();
    }
    
    void block1Func() {
      digitalWrite(10, HIGH);
      for (int i = 0; i < 8; i++) {
        digitalWrite(latchPin, LOW);
        shiftOut(block1[i]);
        digitalWrite(latchPin, HIGH);
        delay(1000);
      }
      digitalWrite(10, LOW);
    }
    
    void block2Func() {
      digitalWrite(6, HIGH);
      for (int i = 0; i < 8; i++) {
        digitalWrite(latchPin, LOW);
        shiftOut(block2[i]);
        digitalWrite(latchPin, HIGH);
        delay(1000);
      }
      digitalWrite(6, LOW);
    }
    
    void block3Func() { 
      digitalWrite(9, HIGH);
      for (int i = 0; i < 8; i++) {
        digitalWrite(latchPin, LOW);
        shiftOut(block3[i]);
        digitalWrite(latchPin, HIGH);
        delay(1000);
      }
      digitalWrite(9, LOW);
    }
    
    void shiftOut(byte dataOut) {
      
      // Shift out 8 bits on the LSB first, on rising edge of clock
      boolean pinState;
      digitalWrite(dataPin, LOW); // clear out shift register to make ready for data input
      digitalWrite(clockPin, LOW);
          for (int i=0; i<=7; i++) { // for each bit in dataOut send out a bit
              digitalWrite(clockPin, LOW); // set clockPin to LOW before sending bit
              if ( dataOut & (1<<i) ) { pinState = HIGH; }  // if the value of DataOut and (logical AND) a bitmask are true, set pinState to 1 (HIGH)
              else { pinState = LOW; }
        
      // sets dataPin to HIGH or LOW depending on pinState
      digitalWrite(dataPin, pinState); // send bit out on rising edge of clock
      digitalWrite(clockPin, HIGH); }
      
      digitalWrite(clockPin, LOW);  // stop shifting data out
    }
    

    ](HDSP-4830, HDSP-4840, HDSP-4850, HDSP-4832, HDSP-4836, HLCP-J100 10-Element Bar Graph Array Data Sheet)](http://www.mouser.com/ProductDetail/Avago-Technologies/HDSP-4832/?qs=sGAEpiMZZMvkC18yXH9iIsSFKp4VzurG0vAKNM%252bu9zY%3D)](http://www.mouser.com/ProductDetail/Avago-Technologies/HDSP-4836/?qs=sGAEpiMZZMvkC18yXH9iIsSFKp4VzurGtAmc4KGL6bw%3D)](http://www.mouser.com/ds/2/216/DC10EGWA-189864.pdf)](http://www.mouser.com/ProductDetail/Kingbright/DC10EGWA/?qs=sGAEpiMZZMvkC18yXH9iIoSkehDuVhEVlPrqt1C5PXQ%3D)

    32768:
    Did the lucidtronix link offer anything useful?

    To be honest I just skimmed it over. It's more concerned with the detail of getting bits out to the SR and anode control lines. That part is easy and would be a portion of the driver I've mentioned. I'll get back to that later. Right now I have a snippet of code that is a start on a [[function](http://arduino.cc/en/Reference/FunctionDeclaration) that is intended to eventually become the .cpp part of your [[library](http://arduino.cc/en/Hacking/LibraryTutorial) (after many additions to implement the other display modes). As such it's not complete nor will I say it correctly implements the processing needed for the 2 display modes is purports to do. But it should give you an inkling of my thoughts. That is there's a layer of processing that takes the data passed to it (along with the display mode) and turns that into (in this case) 2 variables that will be used by the driver to set the SR and anode control lines. Have a look and ask questions re: the big picture or the details ... that will save me from typing a long explanation that's not needed.
    void LEDbargraph(long data, int mode){
      int Mag;
      unsigned int LEDs_on
      unsigned int GLEDs_working
      unsigned int RLEDs_working
      //reset all LEDs to be off in the LED "working" registers
      LEDs_on = 0x00;
      GLEDs_working = 0x00;
      RLEDs_working = 0x00;
      //switch to the appropriate processing dependent on mode
      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, inLow, inHigh, 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;
        case 2:
          //this mode is the processing for the center spread 2 mode
          //this is a stooopid version for use as a starting point
          //determine where in the range of 12 LEDs the data lies
          Mag = int(map(data, inLow, inHigh, 1, 12));
          //set that LED to be on in the working register
          LEDs_on = 1 << (Mag);  //shifts a single 1 to the proper position
          //now map which LED is on
          //since we're doing only a single color for now
          GLEDs_working = LEDs_on;
        break;
        default:
          //this is where you should never end up
          //put some error code and msg here
        break;
      }
      //all the above coding has set up the R and G working registers
      //now it's time to copy that info into the registers used by the driver
      GLEDs = GLEDs_working;
      RLEDs = RLEDs_working;
      //now get driver running on an interrupt basis, if not already running
      //this part is TBD
    }
    

    So the above function would be called in a sketch that has done all the pin definitions and setup needed for the driver to talk to the SR and display anodes. That functionality would normally happen when you instantiate the library, like setting up an LCD display by putting this in the sketch:

    #include <LiquidCrystal.h>
    LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2);
    

    So, for the moment, let me assume there’s a sketch that sets up those pin numbers, etc, etc. It might then create some artificial data by adding 10 to the input data on every loop and then calling the bargraph function. The bargraph function (so far) computes 2 variables (GLEDs, RLEDs) whose bits indicate which green and red (orange) LEDs should be on. Now it’s time for a driver to use those variables to set the anode control lines and shift out bits to the SR (B595). The mechanics of that processing are largely covered in the stuff you’ve seen so far. What I’ve not seen covered, nor have quite figured out, is how to make that happen on a non-blocking basis.

    Consider what must happen for the display to look as you want. For any single static display there are 3 “writes” that are happening on a continuous basis at some high enough rate. The 1’st write sets 1 anode pin to on (the others off) and shifts the proper 4 R and 4 G bits into the SR. That turns on a block. A little later in time this occurs again but for block 2. And then again for block 3. And then that cycle repeats. But if the display isn’t just to be stuck, other processing must be allowed to run as well (so new data can be acquired and sent to the display processing). The proper way to do this is to setup an interrupt and have that interrupt run the driver. It then runs every X msec (fast enough not to see any flickering) and picks up the display data from the aforementioned 2 variables. It cycles through the block by block processing and keeps the display updated. The driver itself perhaps only take a few hundred usecs to run and do it’s thing out of the X msec, leaving the remaining time for other processing to run. The way to make this happen is to use a timer to trigger the interrupt. A base Arduino has 3 timers and they are all used for various other functions, ie - timer1 is used to implement the millis() function. So I need to find that list of functions vs timers that I’ve seen before and see which timer is least likely to conflict with a bargraph display usage. Then we can use that timer for the driver interrupt.](http://arduino.cc/en/Hacking/LibraryTutorial)](http://arduino.cc/en/Reference/FunctionDeclaration)

    I ran across this library while looking at what standard Arduino use what hardware timers. Perhaps using it will preclude using a timer and prevent any interference btw your new library and any existing (or future) ones. The main limitation seems to be a 1 msec resolution, which for your usage isn’t a drawback (I think). Let’s start with an assumption that each block needs to be updated at 100 times/sec otherwise the block will appear to flicker. That means 3.3333 msec btw each block (1 → 2, 2 ->3, 3 → 1) so that the time btw block 1 updates is 10 msec. So you could set the timer to call the driver function (only) every 3 msec and all might look good.

    http://playground.arduino.cc/code/timer

    http://www.doctormonk.com/2012/01/ardui … brary.html

    Well I know how have (at this moment) one Block that is counting up in binary in green, red and yellow (more of an orangish hue).

    Any more ideas, suggestions? I’m not going away :mrgreen:

    EDIT: Just saw your post Mac. I’ll process it and see what I come up with. I didn’t catch that the page count had already rolled over to page 2 and I had the post reply window open for awhile. Oops :oops:

    Here’s a test sketch to be used to develop all the code portions that will eventually be used to create your library.

    It creates a test signal, a ramp from 0 - 1023. It then calls the bargraph function (using mode 1 at the moment). It also sets up the timer function which should result in the “driver” function being run every 3 msec. ATM the driver is incomplete but, as you’ve gotten a display to work, you should be able to write the missing portion.

    Again, as an overview, the graphing function takes data** and display mode as arguments and turns those into 2 unsigned integers, RLEDs and GLEDs. Each bit in these variables indicates whether the corresponding R/G LED should be on or off. For the moment, a 1 indicates the LED should be on. And of course for a 12 segment display, only the least significant 12 bits are used. **at present I couldn’t figure a good way to graph every possible range of data so the bargraph function requires that the data to be passed to it be scaled btw 0 and 100 (as in %) for mode 1 and -100 to +100 for mode 2. Maybe later, after all the display modes have been coded, a better implementation can be devised ? And FWIW right now only the G LEDs are used. Again this can be changed to be like the GIFs you posted, but for now … keep it simple.

    The driver function should run (once you’ve installed the timer library from the above link) every 3 msec. It will (when you’ve finished it) turn on the appropriate anode pin for the display block, create and 8 bit byte containing the 4 R and 4 G LED states for the block to be written to (this cycle). It will then shift that byte out to the B595 SR. And then increment a counter so next time the driver is called, it’ll update the next display block.

    code deleted ... see next post
    

    Seeing as I had a free hour … I corrected a silly oversight in getting the printout to work and took my cut at implementing the 1’st part of the driver code, the part that takes the data from GLEDs and RLEDs and puts that into the B595 SR for block1. You can see that and do blocks 2 and 3.

    //this sketch is a testbed for code intended to be made into a library
    //for displaying data on a multisegment LED bargraph. It will serve as
    //a wrapper around a function and driver that control the display
    
    //for the time being this code will use a timer function to multiplex
    //the display "blocks" timing
    #include "Timer.h"
    Timer blockTimer;
    
    //the present implementation of the display uses 3 pins for anode control
    //and a B595 SR for R/G LED cathode control. These are the pins used
    const byte anode1Pin = ??;
    const byte anode2Pin = ??;
    const byte anode3Pin = ??;
    const byte SRsclkPin = ??;
    const byte SRrclkPin = ??;
    const byte SRdataPin = ??;
    //create an array of the anode pins for use by the driver
    const byte anodePins[] = {
      anode1Pin, anode2Pin, anode3Pin};
    
    //now declare the other constants used
    
    
    //and declare the variables used
    volatile unsigned int GLEDs = 0;
    volatile unsigned int RLEDs = 0;
    volatile int block = 0;
    volatile byte SRdata = 0;
    unsigned long dataTime = 0;
    unsigned long msgTime = 0;
    long testData = 0;
    long scaledData = 0;
    
    
    void setup(){
      //start up serial port for debugging
      Serial.begin(38400);
      //start up the timer driven processing to run every 3 msec
      blockTimer.every(3, LEDdriver);
    
    }
    
    void loop(){
      //for test purposes lets create an input that runs from 0 to 1023
      if(dataTime - millis() > 200){
        testData =+ 10;
        dataTime = millis();
      }
      if(testData > 1023){
        testData = 0;
      }
      //send the data value to the serial monitor so we canb see what it is
      if(msgTime - millis() > 1000){
        Serial.print("The data is presently : ");
        Serial.println(testData);
        msgTime = millis();
      }
      //rescale the data to be btw 0 and 100% before sending it to the bar graph
      scaledData = map(testData, 0, 1023, 0, 100);
      //now call the bar graphing function using mode = 1
      LEDbargraph(scaledData, 1);
    }
    
    void LEDbargraph(long data, int mode){
      int Mag;
      unsigned int LEDs_on;
      unsigned int GLEDs_working;
      unsigned int RLEDs_working;
      //reset all LEDs to be off in the LED "working" registers
      LEDs_on = 0x00;
      GLEDs_working = 0x00;
      RLEDs_working = 0x00;
      //call the timer update routine
      blockTimer.update();
      //switch to the appropriate processing dependent on mode
      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;
      case 2:
        //this mode is the processing for the center spread 2 mode
        //this is a stooopid version for use as a starting point
        //determine where in the range of 12 LEDs the data lies
        Mag = int(map(data, -100, +100, 1, 12));
        //set that LED to be on in the working register
        LEDs_on = 1 << (Mag);  //shifts a single 1 to the proper position
        //now map which LED is on
        //since we're doing only a single color for now
        GLEDs_working = LEDs_on;
        break;
      default:
        //this is where you should never end up
        //put some error code and msg here
        break;
      }
      //all the above coding has set up the R and G working registers
      //now it's time to copy that info into the registers used by the driver
      GLEDs = GLEDs_working;
      RLEDs = RLEDs_working;
    }
    
    void LEDdriver(){
      //this driver code will take the R/G data words and update the LED display
      //block every time it's called (at ~3 msec intervals).
    
      //based upon the block to be updated, activate it's anode control pin
      //and turn the other anodes off
      //it's ASSUMED here that a logic 1 turns on the anode
      for(int i = 0; i < 3; i++){
        digitalWrite(anodePins[i], LOW);
      }
      digitalWrite(anodePins[block], HIGH);
      //now it's time to shift out the data for the block into the SR
      //using only the 2 words that contain the on/off bits, RLEDs, GLEDs
      //and picking the bits from them and packing them into the SRdata byte
      switch (block + 1){
        case 1:
        //this code will setup the SR byte for block = 0
        //the 4 LSBs of GLEDs and RLEDs are used to set up the leftmost block (1)
        //so extract these LSBs using the bitwise AND function and then shift them
        //into their proper locations (assumed here to have G LEDs in lower 4 bits)
        //so do the R LED data bit first
        SRdata = SRdata | (0x000F & RLEDs);
        //now left shift the lower 4 bits into the upper 4 bits
        SRdata = SRdata << 4;
        //now mask the 4 bits from the GLEDs and OR them into the SR byte
        SRdata = SRdata | (0x000F & GLEDs);
        //the above word holds all the bits in their proper places to be shifted out
        //to the B595 except that the polarity is wrong. The B595 needs a 0 to turn on
        //the LED. So finally invert each bit, 0 -> 1, 1 -> 0
        SRdata = ~SRdata;
        //finally shift the data out and into the B595. ASSUME it's MSB 1st ???
        shiftOut(SRdataPin, SRsclkPin, MSBFIRST, SRdata);
        //and perhaps the R clock pin needs a pulse ... or not. add code below if needed
        
        break;
        case 2:
        //this code will setup the SR byte for block = 1
        SRdata = ?????
        break;
        case 3:
        //this code will setup the SR byte for block = 2
        SRdata = ?????
        break;
      }
      //now clock the SRbyte out to the B595 SR
      
      //lastly increment the block number so the next pass will update the 
      //next block of the display
      block++;
      if(block > 2){
        block = 0
      }
    }
    

    BTW the driver code above makes certain assumptions as to how the B595 is wired up to the display. Some of these can be changed easily (in software), others … not so much. So what’s important is that the B595 be wired such that all the G LEDs are consecutive and in order and likewise for the R LEDS. By this I mean that the bit for the least significant LED is wired to the pin that is the LSB (for that color). And the next most significant LED is wired to the pin for the next most significant bit. Look at what the code does and it should be apparent what’s needed for wiring.