NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Project Help and Ideas » Simple Bar Graph

January 18, 2011
by cmelia
cmelia's Avatar

I need some help creating a simple one line bar graph using black squares indicating the ADC reading. Let's say no blocks for 0 and 20 blocks for 1024. All the other code I found online required additional libraries which I am not quite ready for. Thanks in advance.

January 18, 2011
by bretm
bretm's Avatar

Something like this?

int16_t x = (readAdc() * 20) / 1024;
lcd_home();
int8_t i;

for (i = 0; i < x; i++)
{
    lcd_write_byte(0xFF); // block
}

for (; i < 20; i++)
{
    lcd_write_byte(0x20); // space
}
January 18, 2011
by cmelia
cmelia's Avatar

Thanks for the quick reply. The code seems very logical but I cant get it to work. I wrote it to line 4 and it dimly highlighted lines one and three when running. Have you tried this? Thanks!!

February 25, 2011
by SpaceGhost
SpaceGhost's Avatar

I'm having trouble figuring this one out too... Same results as cmelia.

February 26, 2011
by SpaceGhost
SpaceGhost's Avatar

I've played around with this for a while today, and this is what I have. It's pretty close to working right - problem is, the first "bar" of the bargraph comes in at about 52 of the ADC value (which is what I would expect), but on the first line of the LCD & last (20th) position.

#define F_CPU 14745600

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

// PIN DEFINITIONS:
//
// PC0 -- temperature sensor analog input

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {
  // read from ADC, waiting for conversion to finish
  // (assumes someone else asked for a conversion.)
  // wait for it to be cleared

  while(ADCSRA & (1<<ADSC)) {
    // do nothing... just hold your breath.
  }
  // bit is cleared, so we have a result.

  // read from the ADCL/ADCH registers, and combine the result
  // Note: ADCL must be read first (datasheet pp. 259)
  uint16_t result = ADCL;
  uint16_t temp = ADCH;
  result = result + (temp<<8);

  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);

  return result;
}

double sampleToFahrenheit(uint16_t sample) {
  // conversion ratio in DEGREES/STEP:
  // (5000 mV / 1024 steps) * (1 degree / 10mV)
  //    ^^^^^^^^^^^      ^^^^^^^^^^
  //     from ADC         from LM34
  return sample * (5000.0 / 1024.0 / 10.0);  
}

int main() {
  // start up the LCD
  lcd_init();
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_home();

  // start up the Analog to Digital Converter
  adc_init();

  // start up the serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

  // holder variables for temperature data
  uint16_t last_sample = 0;
  double this_temp;
  double temp_avg;
  uint8_t i;

   int16_t x;
   lcd_line_three();

   int8_t j;

  while(1) {
    // take 100 samples and average them!
    temp_avg = 0.0;
    for(i=0; i<100; i++) {
      last_sample = adc_read();
      this_temp = sampleToFahrenheit(last_sample);

      // add this contribution to the average
      temp_avg = temp_avg + this_temp/100.0;
    }

    x = (adc_read() * 20) / 1024;

      for (j = 0; j < x; j++)  // new code

      lcd_write_byte(0xff); // block

      for (;j < 20; j++)

      lcd_write_byte(0xa0); // space

    // write message to LCD
    lcd_home();
    lcd_write_string(PSTR("ADC: "));
    lcd_write_int16(last_sample);
    lcd_write_string(PSTR(" of 1024    "));

    // write message to serial port
    printf_P(PSTR("%.2f degrees F\r\n"), temp_avg);
  }

  return 0;
}

This is of course a modified version of the tempsensor project. I incorporated the bargraph code (attempted anyway) and removed the temperature information. I am using a 2k trimmer pot with a nice thumbwheel in place of the LM34.

When the ADC value hits 100 the first bar jumps down to line three, after which the second bar appears (following the first) at ADC value 104 as expected.

I understand (I think) that the first bar gets "pushed" there when line one "grows" a space, after the ADC increases from a two digit number to a three digit number...

However I am unable to get the bargraph to begin on line three - even by adding another space after "of 1042"...

The rest of the bargraph works fine after the second bar appears - the graph increments left or right across line three according to the rotation of the potentiometer, until...

After the bar reaches position 19, at 20 (full scale) the first bar on line three "disappears."

I believe the issue is all about getting the bargraph to start (@ ADC value 52) on line three instead of one. The problem may be obvious, but I'm sure missing it!

  • An interesting note: The "space" bretm used (0x20, from the data sheet) seems to have been the problem with why cmelia and myself were getting the two dark "lines" over the LCD when the program was running (and not working). I used the other "space" shown on the data sheet (0xa0), and that one appears to be better suited for this display.

One last thing I think I should mention: When loading the code I get a "warning" message - unused variable 'lcd_stream' (referring to line #66). Not sure what to do here either... What to replace it with, or whether to delete it? Could this be part of the problem?

It was fun getting this far with it, but now I'm kinda stuck!

Post a Reply

Please log in to post a reply.

Did you know that you can connect to certain car computers via the OBD-II port with a microcontroller? Learn more...