Go Down

Topic: multiplexing voltage display (Read 809 times) previous topic - next topic

jessjess

Hi all, simple design that i have planned: to display onto three (3) 7-segment LED displays. the voltage of a variable power supply from 1.5-22VDC.

first problem i am having is that the numbers displayed are inverted, that is from the display:
  _        A
|_|   F, G, B
|_|.  E, D, C, DP

for the digit '1' B and C should be lit, however, A, D, E, F, G, DP are lit instead.

trying to test this, i have been using a code that i found:

Code: [Select]
/*
Using 2 7-segment displays with the 74HC595 shift registers
CC by-sa-nc 3.0
http://tronixstuff.wordpress.com
*/


int latchpin = 8; // connect to pin 12 on the '595
int clockpin = 12; // connect to pin 11 on the '595
int datapin = 11; // connect to pin 14 on the '595
float b = 0;
int c = 0;
float d = 0;
int e = 0;
int speed = 300; // used to control speed of counting
int segdisp[10] = {
3,159,37,13,153,73,65,27,1,9};
void setup()
{
pinMode(latchpin, OUTPUT);
pinMode(clockpin, OUTPUT);
pinMode(datapin, OUTPUT);
}
void loop()
{
//  Count up
for (int z=0; z<100; z++)
{
   digitalWrite(latchpin, LOW);
   shiftOut(datapin, clockpin, LSBFIRST, 0); // clears the right display
   shiftOut(datapin, clockpin, LSBFIRST, 0); // clears the left display
   digitalWrite(latchpin, LOW);
   if (z<10)
   {
     digitalWrite(latchpin, LOW);
     shiftOut(datapin, clockpin, LSBFIRST, segdisp[z]); // sends the digit down the serial path
     shiftOut(datapin, clockpin, LSBFIRST, 255); // sends a blank down the serial path to push the digit to the right
     digitalWrite(latchpin, HIGH);
   }
   else if (z>=10)
   {
     d=z%10; // find the remainder of dividing z by 10, this will be the right-hand digit
     c=int(d); // make it an integer, c is the right hand digit
     b=z/10; // divide z by 10 - the whole number value will be the left-hand digit
     e = int(b); // e is the left hand digit
     digitalWrite(latchpin, LOW); // send the digits down to the shift registers!
     shiftOut(datapin, clockpin, LSBFIRST, segdisp[c]);
     shiftOut(datapin, clockpin, LSBFIRST, segdisp[e]);
     digitalWrite(latchpin, HIGH);
   }
   delay(speed);
}
delay(2000);
//  Count down
for (int z=99; z>=0; z--)
{
   digitalWrite(latchpin, LOW);
   shiftOut(datapin, clockpin, LSBFIRST, 0); // clears the right display
   shiftOut(datapin, clockpin, LSBFIRST, 0); // clears the left display
   digitalWrite(latchpin, HIGH);
   if (z<10)
   {
     digitalWrite(latchpin, LOW);
     shiftOut(datapin, clockpin, LSBFIRST, segdisp[z]); // sends the digit down the serial path
     shiftOut(datapin, clockpin, LSBFIRST, 255); // sends a blank down the serial path to push the digit to the right
     digitalWrite(latchpin, HIGH);
   }
   else if (z>=10)
   {
     d=z%10; // find the remainder of dividing z by 10, this will be the right-hand digit
     c=int(d); // make it an integer, c is the right hand digit
     b=z/10; // divide z by 10 - the whole number value will be the left-hand digit
     e = int(b); // e is the left hand digit
     digitalWrite(latchpin, LOW); // send the digits down to the shift registers!
     shiftOut(datapin, clockpin, LSBFIRST, segdisp[c]);
     shiftOut(datapin, clockpin, LSBFIRST, segdisp[e]);
     digitalWrite(latchpin, HIGH);
   }
   delay(speed);
}

   delay(2000);
}


probably well known, counts from 0 through to 99 then backwards.

so, my first problem is i have the hardware set up correctly, so why are the numbers invertly lit?


Riva

Maybe the display is common anode/cathode instead of common cathode/anode (delete as applicable), really need a schematic and part numbers to be sure. As it lights the segments correctly (though inverted) I would think it's wired okay. Simple fix would be to invert the segment definitions from 3,159,37,13,153,73,65,27,1,9 to 252,96,218,242,102,182,190,228,254,246

dhenry

Quote
A, D, E, F, G, DP are lit instead.


Rather than sending a number, send the opposite of that number.

For example,  instead of

Code: [Select]
     shiftOut(datapin, clockpin, LSBFIRST, segdisp[z]); // sends the digit down the serial path

Use

Code: [Select]
     shiftOut(datapin, clockpin, LSBFIRST, ~segdisp[z]); // sends the digit down the serial path

jessjess

Beautiful. i didnt try the print the not number, because the first worked fine.

now that i have the physical printing semi-sorted, i moved onto the voltage calculation itself. it works perfectly fine, however, because i want to display to 0.1V, is there any method of 'rounding' that i could use to print a result (as seen by serial) of 2.55 to 2.6, and 2.54 to 2.5 for later integration onto the 7 segment displays?

here is the voltage snippit of coding


Code: [Select]
/*
result smoothing: http://www.arduino.cc/en/Tutorial/Smoothing
Voltage calculation: http://arduinotronics.blogspot.com.au/2011/03/monitoring-voltage-of-dc-battery-supply.html

*/

int VoltageIn = A5;

const int numReadings = 40;
int readings[numReadings];      // the readings from the analog input
int index = 0;                  // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average




void setup()
{
Serial.begin(9600); // initialize serial communication with computer:                 
  // initialize all the readings to 0:
  for (int thisReading = 0; thisReading < numReadings; thisReading++)
    readings[thisReading] = 0; 
}

void loop()
{
  total= total - readings[index];         // subtract the last reading:
  readings[index] = analogRead(VoltageIn);// read from the sensor: 
  total= total + readings[index];         // add the reading to the total:     
  index = index + 1;                      // advance to the next position in the array: 

  if (index >= numReadings)               // if we're at the end of the array...     
    index = 0;                            // ...wrap around to the beginning:                 
  average = total / numReadings;          // calculate the average:
  Serial.println(average);                // send it to the computer as ASCII digits
  delay(1);                               // delay in between reads for stability           


int val = analogRead(average); // read the value from the sensor
float volts = (average * 0.021965811965812); // calculate the ratio
Serial.println("Volts:");
Serial.println(volts); // print the value in volts
Serial.println("average value");
delay(400);
}

# note: the value or 0.0219... was calculated manually as a constant to alter the calculated value to the true voltage

jessjess

Hi all,
i managed to (re) find the wording for code i was after.
here is my code thus far.

Code: [Select]
/*
result smoothing: http://www.arduino.cc/en/Tutorial/Smoothing
Voltage calculation: http://arduinotronics.blogspot.com.au/2011/03/monitoring-voltage-of-dc-battery-supply.html

*/

int VoltageIn = A5;

const int numReadings = 40;
int readings[numReadings];      // the readings from the analog input
int index = 0;                  // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average

int latchpin = 8; // connect to pin 12 on the '595    GREEM
int clockpin = 12; // connect to pin 11 on the '595   BLUE
int datapin = 11; // connect to pin 14 on the '595    YELLOW
float b = 0;
int c = 0;
float d = 0;
int e = 0;
int speed = 500; // used to control speed of counting
int segdisp[10] = {
252,96,218,242,102,182,190,228,254,246};


void setup()
{
Serial.begin(9600); // initialize serial communication with computer:                 
  // initialize all the readings to 0:
  for (int thisReading = 0; thisReading < numReadings; thisReading++)
    readings[thisReading] = 0; 

pinMode(latchpin, OUTPUT);
pinMode(clockpin, OUTPUT);
pinMode(datapin, OUTPUT);

}

void loop()
{
  total= total - readings[index];         // subtract the last reading:
  readings[index] = analogRead(VoltageIn);// read from the sensor: 
  total= total + readings[index];         // add the reading to the total:     
  index = index + 1;                      // advance to the next position in the array: 

  if (index >= numReadings)               // if we're at the end of the array...     
    index = 0;                            // ...wrap around to the beginning:                 
  average = total / numReadings;          // calculate the average:
  Serial.println(average);                // send it to the computer as ASCII digits
  delay(1);                               // delay in between reads for stability           


int val = analogRead(average); // read the value from the sensor
float volts = (average * 0.021965811965812 * 0.94104308390023); // calculate the ratio
Serial.println("Volts:");
Serial.println(volts); // print the value in volts
Serial.println("");
Serial.println("");
int ones = int(volts);
int tenths = int((volts - ones) * 10);
Serial.println("voltage _test:");
Serial.println(ones);
Serial.println(tenths);
Serial.println("average value");
Serial.println("");

   digitalWrite(latchpin, LOW);
   shiftOut(datapin, clockpin, LSBFIRST, 0); // clears the right display
   shiftOut(datapin, clockpin, LSBFIRST, 0); // clears the left display
   shiftOut(datapin, clockpin, LSBFIRST, 0); // clears the left display
   digitalWrite(latchpin, LOW);


if (volts < 10)
{
  int tens = 0;
  int ones = int(volts);
  int tenths = int((volts - ones) * 10);

  digitalWrite(latchpin, LOW);
  shiftOut(datapin, clockpin, LSBFIRST, segdisp[tenths]); // clears the right display
  shiftOut(datapin, clockpin, LSBFIRST, segdisp[ones]); // clears the left display
  shiftOut(datapin, clockpin, LSBFIRST, segdisp[tens]); // clears the left display
  digitalWrite(latchpin, LOW);
 
 
}
else if (volts >= 10)
{
  int tens = int(volts / 10);
  int ones = int((volts - tens * 10));
  int tenths = int((volts - tens * 10 - ones) * 10);
 
  digitalWrite(latchpin, LOW);
  shiftOut(datapin, clockpin, LSBFIRST, segdisp[tenths]); // clears the right display
  shiftOut(datapin, clockpin, LSBFIRST, segdisp[ones]); // clears the left display
  shiftOut(datapin, clockpin, LSBFIRST, segdisp[tens]); // clears the left display
  digitalWrite(latchpin, LOW);
}

delay(5);    // to allow for a reset time

}


and i two questions. when writing to the '595s, there is a very annoying flicker between each value and re-print onto the 7 segments.
is there any way that that could be reduced?
and second. the relationship between the calculated voltage and true voltage should be a linear relationship, yes? i only ask because mine is not, i can have 1.2V calc == 1.2V measured, however bumping it up to 7.2V true gives me a fluctuating 7.3 - 7.4V measured

dhenry

Quote
there is a very annoying flicker b


Because your code does that.

What you should have done is to calculate the right segment information, send them to the hc595, and then  latch. This will cause the display to be updated instantaneously, no flickering at all. Like this:

Code: [Select]

  //get the right number to volts
  unsigned short voltsx10 = volts * 10; //convert volts to a fixed point integer
  tenths = voltsx10 % 10; voltsx10 /= 10;
  ones = voltsx10 %10; voltsx10 /=10;
  tens = voltsx10 %10;

  //check for blanks
  if (tens==0) tens = BLANK;
  if ((tens == BLANK) || (ones == 0) ones = BLANK;

  digitalWrite(latchpin, LOW);
  shiftOut(datapin, clockpin, LSBFIRST, segdisp[tenths]); // clears the right display
  shiftOut(datapin, clockpin, LSBFIRST, segdisp[ones]); // clears the left display
  shiftOut(datapin, clockpin, LSBFIRST, segdisp[tens]); // clears the left display
  digitalWrite(latchpin, LOW); 


This will blank out leading zeros and will not cause any flickering.

You really should avoid the use of floating point math also.

fungus

Might be a good idea to put the latch pin high every once in a while, too...

Latch should be a short pulse after the data is in place.
No, I don't answer questions sent in private messages (but I do accept thank-you notes...)

Go Up