TLC5940 simple led bar graph

I know this can’t be that hard, but being new to arduino, and really new to programming anything except my VCR, I’ve hit a brick wall. I successfully got my Uno to speak to an LCD display and show a very nice bar graph for a millivolt sensor on my bike, just a simple readout of throttle position. Not necessary, not life saving, just kinda fun. But the LCD can’t handle the weather, and I have a nice 16 red LED bar graph that works fine, so I figured it would be no problem to make the switch. But now I can’t figure this tlc5940 library out at all. Anyone with any input on where to start, or if you’ve made one before that I can dissect and learn from, I would greatly appreciate the info.
I’ve seen it done before, this the link to exactly what I’m trying to do, except with a sensor instead of a phototransistor-

I’ll put what I got to work for the LCD unit, maybe that will help.

Thanks

#include <LiquidCrystal.h>  // LiquidCrystal Library

int analogPin = 5; // The analog input from sensor

float d = 0;
float oldit = 0;

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

void setup() {
  lcd.begin(16, 2);  // will only need one line for LEDS
  lcd.setCursor(11, 0); // show what pin we're using for input
  lcd.println("LDI "); // ID info, not used with LEDS
  lcd.setCursor(15, 0); // for the top row caption, not used with LEDS
  lcd.print(analogPin);  // won't use for LEDS
}

void loop() {
  float num = analogRead(analogPin); // get our input
  float it = map(num, 0, 1023, 0, 1023);  // take our input and associate it to millivolt value, 5V/1024=4.8mv per block
  lcd.setCursor(0, 0); // print the block number we're using.
  lcd.print(num); // not used with LEDS

  if (it > oldit) { // check if the number changes to a higher number
    for (d = 0; it >= d; d++) { // count up from 0 to 15
    lcd.setCursor(d, 1); // start at the bottom left and work forward
    lcd.write(1023); // show a block on the screen
    }
  }

  if (it <= oldit) { // check if the number changed to a smaller number
    for (d = 15; it <= d; d--) { //count down from 15 to 0
    lcd.setCursor(d, 1); // start at the bottom right and work back
    lcd.write(1022); // show blank
    }
  }

  oldit = it;  
}

Is this just for elite programmers here? Is there somewhere else I should look or ask this question? No one seems to be willing to lend a hand to a novice, so where should I look? I also have the barGraph example working, but how can I convert that to the tlc5940? Thanks

No it is not just for elite programmers …

please use the # button to get proper tags around your code (modify you post, select the code part and press #)

simplified the loop in the code, replaced the floats by ints as floats should used with care.

#include <LiquidCrystal.h>  // LiquidCrystal Library

int analogPin = 5; // The analog input from sensor

int oldit = 0;

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

void setup() 
{
  lcd.begin(16, 2);        
  lcd.setCursor(11, 0);    
  lcd.println("LDI ");       
  lcd.setCursor(15, 0);   
  lcd.print(analogPin);    
}

void loop() 
{
  int raw = analogRead(analogPin); 
  int it = map(raw, 0, 1023, 0, 15); 

  lcd.setCursor(0, 0);  
  lcd.print(raw);       

  for (int led = 0; led < 15; led++)
  {
      lcd.setCursor(led, 1);    
      if (led <= it) lcd.write(1023);           
      else lcd.write(1022);           
  }
  
  delay(100); // optional; 
}

I changed the post above to show the code correctly. Interesting, that worked before but you were able to clean it up quite a bit. Thanks, I have to look into those changes and see why/how i should have done it in the first place. Of course, I'm still trying to figure out how to port that over to my tlc5940 and 16 segment bar graph, but I am very glad to learn anything new at this point, so much thanks!

Instead of using a TLC5940, use a shift register, the 74HC595. The TLC5940 is to expand the number of pwm outputs, not digital outputs. You will need two shift registers. The library is very, very simple. Here is a link for that http://bildr.org/2011/08/74hc595-breakout-arduino/

That is an interesting option, but I already have the tlc5940 on the board, it works with the examples from the tlc5940 library, and I like the PWM for nice dim LEDs for night viewing, and I've seen working examples on the web that do exactly what I want to do, basically like a VU meter. Since I already have all this together, I'd really like to see if i can get it to work. It's a great chance to see if I can figure out the coding, hopefully by looking at something someone else has already done and learn from it and make my project do exactly what I want. But thanks for the info, going to order some of those for a different project I have in mind, big rooftop display for next Christmas...

This might be what you are after.
I have not tested it but it compiles and should be close to what you want.

/*
analogue in to row of LEDs
  */

#include "Tlc5940.h"
int analogPin = 5; // The analog input from sensor
int lastRaw=0;

void setup()
{
  /* Call Tlc.init() to setup the tlc.
     You can optionally pass an initial PWM value (0 - 4095) for all channels.*/
  Tlc.init();
}


void loop()
{
    int raw = analogRead(analogPin);  
   if( abs(raw - lastRaw) > 60) { // only if there has been a change that affects the display
    Tlc.clear(); // clear old display
    for(int i=1; i<17; i++) { // now set the LEDs if they are below the threshold
      if((i * 60) < raw) Tlc.set(i-1, 4095); // turn LED on full
    }
      lastRaw = raw; // save for next time
          /* Tlc.update() sends the data to the TLCs.  This is when the LEDs will
       actually change. */
    Tlc.update();
  }
}

Fascinating, it's so simple once you put it down, but I went thru the data sheet, libraries, TI flow chart, and the "Demystifying the TLC5940" and still had no idea. Much thanks. Won't I still need to map the analogPin to a usable value, something like below? It compiles, but I see no way for the data to be interpreted? The (0, 1023) for output works for me because each step is 4.8mv (5v / 1024), and my sensor never goes over 80mv, so 16 LEDs is perfect for the first 16 out of 1024 possible returns.

  // map the result to a range from 0 to the number of LEDs:
  int ledLevel = map(analogPin, 0, 1023, 0, 1023);

That line just maps a value from 0 to 1023 to a value between 0 and 1023', in other words it dose nothing. Look at the referance defination of map() to see what you need to put in. If you have a restricted range of input values map them to convert that range into 0 to 1023. The code will map the full range to 16 LEDs so you can change the sums in the code that turns the LEDs on if you don't want to use the map function.

Ok, stayed up most of the night, mainly because I just really want to understand this and learn, so here's the question I haven't figured out- In the reference for analogRead it says it can do a resolution of 1024 separate values based on a 5v reference on the board. I used the map function on my LCD and didn't get a usable display until I used the above code that basically does nothing, but for some reason worked with my LCD and gave me one bargraph segment for each 4.8mv. I guess I stumbled on that one with luck, took a few days to get that to work back then. My problem is seeing where I can do that same basic thing here, to turn on one LED for each 4.8mv. Maybe I'm dense (probably, what 45 year old suddenly decides to try and learn this stuff, eh?), but I'm missing the place to set those parameters. I just don't see where the analogRead from pin5 gets sent to the tlc in a usable form. Compiles and uploads fine, but gives me no result on the LEDs, and I'm thinking its not getting enough resolution to light even one LED, ie:4.8mv is too small to light one segment. Now, lets see if that made any sense at all, LOL. Much thanks!

didn’t get a usable display until I used the above code that basically does nothing,

Is that this line?

int ledLevel = map(analogPin, 0, 1023, 0, 1023);

Well it is the same as saying:-

int ledLevel = analogPin;

But I think that line is in error anyway, analogPin is the number of the pin the voltage input is on. It is not the value of the voltage itself, you get that by using analogPin as the argument in the analogRead() statement.
value = analogRead(analogPin)

I just don’t see where the analogRead from pin5 gets sent to the tlc in a usable form.

The value is not sent to the TCL chip. It is used to see if to set or not a value in the Tlc buffer. This is just a lump of memory and you use Tlc.set() to set memory that will correspond to an LED once it is shifter in to it using the Tlc.update() call. Tlc.set() takes two parameters, the first one the LED number and the second one how bright that LED should be, with a value of 0 to 4095.
So what that code is doing is going through the LEDs one at a time and setting them to full brightness until a point is reached where the LEDs should be off, and it then sets no more on.
The Tlc.clear() call turns all the LEDs off, just before you go through the for loop turning them on.
The code that does this is :-

if((i * 60) < raw) Tlc.set(i-1, 4095); // turn LED on full

Now just a simple bit of maths here . The loop changes the value of i each time so each LED step will be 4095 / 17 = 60 ish.
The value of i starts at 1 but the numbering of the LEDs starts at zero so that is why we have i-1 as the LED number we want to change. As i ranges from 1 to 16 then we will potentially set the value of LEDs 0 to 15. The point where we stop turning LEDs on is when i * 60 becomes greater than the variable raw. If raw in the range 0 to 4095 then this will cover all the LEDs. If you don’t have that range then you use the map() function [ used correctly ] to stretch the range you do have to cover this range.
I hope this gives you a bit more of a handle about what is going on.

Ok, first off, you are great. I thank you alot for your help help, the explantion gave me a greater understanding of the code.
I couldn’t get it to work for me, so I went to the one part I never fully understood, which was the 4095/17 part. As far as I could figure from analogRead reference, it could be broken down at most the 1024 steps, not 4095. And I didn’t see where 17 was coming from. So, I decided to divide 1024/16, and 5 was closest, so I replaced the 60s in the sketch with 5s. That gave me a display, but it was lighting up 5 LEDs at a time, not usable. I figured if 5 lit five of them, I’d try 1s. (edit) I just realized that analog read already breaks it down into 1024 steps, not “can break it down into 1024 steps”. I thought I had to map that out. Duh (edit) That worked exactly as I wanted, 1 LED lights up with each 4.9mv input on analogPin. I don’t know if it should actually work that way, I’m reading more on it now, but it works. I also changed the 4095 for full brightness to 300 for dim LEDs, they’re just too bright at 4095. The only problem I have left is, for some reason the correct number of LEDs light up, but the next 2-3 flicker very dimly, doesn’t seem to be one at a time, always a couple of them flickering. Any idea on that, or anything else I may have done wrong?
Thanks!

/*
analogue in to row of LEDs
  */

#include "Tlc5940.h"
int analogPin = 5; // The analog input from sensor
int lastRaw=0;

void setup()
{
  /* Call Tlc.init() to setup the tlc.
     You can optionally pass an initial PWM value (0 - 4095) for all channels.*/
  Tlc.init();
}


void loop()
{
    int raw = analogRead(analogPin);  
   if( abs(raw - lastRaw) > 1) { // only if there has been a change that affects the display
    Tlc.clear(); // clear old display
    for(int i=1; i<16; i++) { // now set the LEDs if they are below the threshold
      if(i < raw) Tlc.set(i-1, 300); // turn LED on dim
    }
      lastRaw = raw; // save for next time
          /* Tlc.update() sends the data to the TLCs.  This is when the LEDs will
       actually change. */
    Tlc.update();
  }
}

but the next 2-3 flicker very dimly,

Two things:-

  1. if you are using analogue read the the reading is always likely to be plus or minus the least significant digit. That is simply how A/D converters work, so it could be noise on the input. It might be more stable if you amplified the signal before putting it into the analogue input. Ofcourse you will have to change the 1 to another number to cope with the bigger signal.

  2. Sometimes this can happen with insufficient power supply decoupling, you need at least a 0.1uF ceramic cap between power and ground on each chip you have.

You have been a great help, I have such a better understanding of this since yesterday. And you’re right, I decoupled almost everything else I’ve made before, but followed the TLC5940 wiring setup so closely that I never thought of the cap for it. Thanks! Now, if you could just email me some saveloys… LOL

where i can download Tlc5940 library because I have one but it can’t accept Tlc.init(); and more, can you help me