12F675 - 0..100V digital voltmeter, ... [Solved]

General discussion on mikroPascal.
Post Reply
Author
Message
chimimic
Posts: 178
Joined: 29 Sep 2007 14:35
Location: France
Contact:

12F675 - 0..100V digital voltmeter, ... [Solved]

#1 Post by chimimic » 30 Oct 2008 16:34

Hi to all,

I actually work on a simple digital voltmeter allowing measurement from 00.0V to +99.9V, trough a 1/20 voltage divider. For this, I use one of the ADC contained in the 12F675 (full scale 0..5V), and transmit measure result via 3 shift registers, each driving one seven seg displays.

All works fine for getting analog input value, and for displaying the two first number of the real analog value.

But I don't find how display the third number. I tried with "mod" operator, searched examples on the web, and tried with "7seg_display.ppp" sample provided with MikroPascal (this last one works for an "0000..9999" integer value but not for a real value). After a full day searching on how to dow, I can't get good result for this last number.

The following schematic show what is done for 36,57 V measurement : the two first number are "3" and "6", that is OK. Now, I try to display the last number, that should be "5" or "6" if rounded (no matter for me).

Image
(click on image for full size)

Can someone help me, please ?
My code is following :

Code: Select all

program Voltmetre_005a;

var
  ArrOut : Array[3] of byte;  // one byte per 7 seg display
  wIn: word;

procedure Init();
begin
  INTCON := %00000000;
  IOC := 0;
  T1CON := %00000001;
  OPTION_REG := %10000001;

  TRISIO := %00000000;
  TRISIO := %00001011;
  TRISIO.0 := 1;
  TRISIO.1 := 0;
  TRISIO.2 := 0;
  TRISIO.3 := 1;  // always input
  TRISIO.4 := 0;
  TRISIO.5 := 0;

  CMCON := %00000111;   // comparators OFF

  ANSEL := %00100001;  // AN0 only enabled (last 4 bits are enable bits {AN3:AN0}
  ANSEL.ANS0 := 1;     // set as analog input
  ANSEL.ANS1 := 0;     // set as digital I/O
  ANSEL.ANS2 := 0;     // set as digital I/O
  ANSEL.ANS3 := 0;     // set as digital I/O

  ADCON0 := %00000001;
  ADCON0.ADFM  := 1;  // left justified, 1 = right justified
  ADCON0.VCFG  := 0;  // voltage ref is VDD
  ADCON0.ADON  := 1;  // turn on ADC module
  ADCON0.ADCS0 := 1;  // |
  ADCON0.ADCS1 := 0;  // | ADCS = 100 = Fosc/4
  ADCON0.ADCS2 := 0;  // |

end;

procedure Out_UpdateAll;
const
  iDelayData = 10;  // 10 us
var
  iDisp, iOut: byte;
  Data: boolean;
begin
  {
  GP2 = DATA
  GP4 = CLOCK
  GP5 = STROBE
  }
  // set Data to low level
  ClearBit(GPIO, 2);
  // set Clock to low level
  ClearBit(GPIO, 4);
  // set Strobe to false, to avoid new output values propagate through latches
  ClearBit(GPIO, 5);
  // send 3 x 7 seg displays data
  for iDisp := 0 to 2 do
  begin
    for iOut := 7 downto 0 do
    begin
      Data := ArrOut[iDisp].iOut;
      if Data then
        SetBit(GPIO, 2)
      else
        ClearBit(GPIO, 2);
      Delay_us(iDelayData);
      SetBit(GPIO, 4);   // clock high
      Delay_us(iDelayData);
      ClearBit(GPIO, 4); // clock low
      Delay_us(iDelayData);
    end;
  end;
  // set Strobe to high, to allow new output values propagate through latches
  SetBit(GPIO, 5);
  // set Data to low level
  ClearBit(GPIO, 2);
  Delay_us(iDelayData);
end;

function Mask(iVal: byte): byte;
begin
  case iVal of
    0 : result  := %00111111;
    1 : result  := %00000110;
    2 : result  := %01011011;
    3 : result  := %01001111;
    4 : result  := %01100110;
    5 : result  := %01101101;
    6 : result  := %01111101;
    7 : result  := %00000111;
    8 : result  := %01111111;
    9 : result  := %01101111
    else result := %01000000;
  end;
end;

procedure ValToDisp(iVal: word);
var
  iDigit: byte;
  rVolt: real;
begin
  // iVal = 0 for Vin = 0V - iVal = 1023 for Vin = +100V
  // rVolt := (100 / 1024) * iVal;
  rVolt := 0.0976562 * iVal;
  // decimal part
  iDigit := (rVolt div 10) mod 10;  // works
  ArrOut[2] := Mask(iDigit);
  // unit part
  iDigit := (rVolt) mod 10;         // works
  ArrOut[1] := Mask(iDigit);
  // fract part
  iDigit := ???;                    // don't know how to do
  ArrOut[0] := Mask(iDigit);
end;

// main program
begin
  Init;
  while true do
  begin
    wIn := ADC_Read(0);
    ValToDisp(wIn);
    Out_UpdateAll;
    delay_ms(100);
  end;
end.
Thanks in advance for any help,

best regards,
Remy
Last edited by chimimic on 30 Oct 2008 23:29, edited 1 time in total.

John Ferrell
Posts: 111
Joined: 19 Sep 2006 21:21
Location: Julian NC, US
Contact:

#2 Post by John Ferrell » 30 Oct 2008 21:32

The old fashioned way to do this is keep all calculations in tenths of a volt and mark the decimal point only in the display.
Another example would be computing in US currency (dollars and cents). If all calculations are made in cents simply shifting the decimal point will handle the conversion to dollars and cents.
John Ferrell W8CCW

kjm
Posts: 70
Joined: 06 Jul 2006 22:17

Re: 12F675 - 0..100V digital voltmeter, how display fract pa

#3 Post by kjm » 30 Oct 2008 21:52

Code: Select all


procedure ValToDisp(iVal: word);
var
  iDigit: byte;
  rVolt: real;  NOT NEED real
  rVolt : word;
begin
  // iVal = 0 for Vin = 0V - iVal = 1023 for Vin = +100V
  // rVolt := (100 / 1024) * iVal; -----> rVolt := ( iVal * 1000) / 1024;
  rVolt := 0.0976562 * iVal;
  // decimal part
  iDigit := (rVolt div 10) mod 10;  // works ----> iDigit := rVolt div 100;
  ArrOut[2] := Mask(iDigit);
  // unit part
  iDigit := (rVolt) mod 10; // works -----> iDigit := (rVolt div 10) mod 10;
  ArrOut[1] := Mask(iDigit);
  // fract part
  iDigit := ???;  // don't know how to do ----> iDigit := rVolt mod 10;
  ArrOut[0] := Mask(iDigit);
end;

// main program
begin
  Init;
  while true do
  begin
    wIn := ADC_Read(0);
    ValToDisp(wIn);
    Out_UpdateAll;
    delay_ms(100);
  end;
end.
:wink:

chimimic
Posts: 178
Joined: 29 Sep 2007 14:35
Location: France
Contact:

#4 Post by chimimic » 30 Oct 2008 23:26

Hi john and kjm,

thanks for your help !

kjm > I already tried this approch, by bividing iVal by 102,4 rather than by 1024, to get an initial value 10 times bigger, and used the same code as you (the same as shown in example provided with MikroPascal), with rVolt declared as real. I retried with your code but the same thing appear : displayed value are totally wrong.

So I modified declaration for rVolt, to make it as word as you suggested. But result is the same : wrong displayed values.

Hum... Found problem :
when iVal = 1000, then
(iVal * 1000) / 1024 = (1.000.000) / 1024
that can't be contained in a single word, but can in a dWord.

following code now works perfectly :

Code: Select all

procedure ValToDisp(iVal: word);
var
  iDigit: byte;
  rVolt: dword;
begin
  // iVal = 0 for Vin = 0V - iVal = 1023 for Vin = +100V
  rVolt := (iVal * 1000) / 1023;
  //rVolt := 0.9775171 * iVal;
  // decimal part
  iDigit := rVolt div 100;
  ArrOut[2] := Mask(iDigit);
  // unit part
  iDigit := (rVolt div 10) mod 10;
  ArrOut[1] := Mask(iDigit);
  // fract part
  iDigit := rVolt mod 10;
  ArrOut[0] := Mask(iDigit);
end;
Updated schematic and code on my web site :
Voltmetre 005a and Voltmetre 005b (in French)

Thanks again for your help :D
Remy

Post Reply

Return to “mikroPascal General”