Newsgroup: it.hobby.elettronica.digitale
Titolo: Temperatura e PIC

ciao
sto utilizzando un PIC16F88 con oscillatore interno a 8Mhz per leggere
la temperatura da un TMP36 utilizzando una VRef di 2.048 V.
Nonostante nel programma la temperatura venga letta ad intervalli
e poi calcolata la media, questa oscilla sempre (+/- 0.2°C che
poi è la risoluzione max che posso ottenere).
Per esempio:
17.0 / 16.8 / 17.2 / 17.0
im un arco di 10 secondi varierà un paio di volte.
L'alimentazione è stabilizzata a 3.3V, di condesatori ce ne sono...
Altri suggerimenti? lo schema è in : 
http://i1.tinypic.com/mlov9l.gif
oppure qui allegato in fidocad così come i sorgenti:
[FIDOCAD ]
LI 100 30 105 30
LI 110 35 115 35
LI 105 30 150 30
LI 110 35 100 35
MC 105 50 0 0 045
LI 100 40 105 40
LI 105 40 105 50
MC 110 40 1 0 170
LI 110 35 110 40
LI 110 50 105 50
SA 110 35
SA 105 50
LI 95 80 100 80
LI 95 90 100 90
RV 80 75 95 95
TY 81 93 5 3 90 1 0 * LM4040
TY 91 88 4 2 0 0 0 * A
TY 92 77 4 2 0 0 0 * C
LI 90 65 120 65
LI 120 65 120 35
LI 120 35 115 35
LI 105 65 105 70
LI 105 80 100 80
SA 105 65
LI 100 90 105 90
LI 150 30 155 30
LI 150 35 155 35
LI 150 40 155 40
LI 150 45 155 45
LI 150 50 155 50
LI 150 55 155 55
LI 150 60 155 60
LI 150 65 155 65
LI 150 70 155 70
LI 180 30 185 30
LI 180 35 185 35
LI 180 40 185 40
LI 180 45 185 45
LI 180 50 185 50
LI 180 55 185 55
LI 180 60 185 60
LI 180 65 185 65
LI 180 70 185 70
RV 155 25 180 75
TY 157 28 4 2 0 0 0 * RA2
TY 157 33 4 2 0 0 0 * RA3
TY 157 38 4 2 0 0 0 * RA4
TY 157 43 4 2 0 0 0 * RA5
TY 157 48 4 2 0 0 0 * VSS
TY 157 53 4 2 0 0 0 * RB0
TY 157 58 4 2 0 0 0 * RB1
TY 157 63 4 2 0 0 0 * RB2
TY 157 68 4 2 0 0 0 * RB3
TY 172 68 4 2 0 0 0 * RB4
TY 172 63 4 2 0 0 0 * RB5
TY 172 58 4 2 0 0 0 * RB6
TY 172 53 4 2 0 0 0 * RB7
TY 172 48 4 2 0 0 0 * VDD
TY 172 43 4 2 0 0 0 * RA6
TY 172 38 4 2 0 0 0 * RA7
TY 172 33 4 2 0 0 0 * RA0
EV 165 25 171 29
TY 172 28 4 2 0 0 0 * RA1
TY 166 58 5 3 90 1 0 * 16F88
MC 105 70 1 0 080
LI 105 80 125 80
LI 125 80 125 35
LI 125 35 150 35
SA 105 80
MC 135 35 1 0 170
MC 135 45 0 0 045
SA 135 35
MC 105 100 0 0 045
LI 105 90 105 100
TY 115 50 3 2 90 0 0 * 0.1u
TY 110 75 3 2 90 0 0 * 1k
TY 140 45 3 2 90 0 0 * 2.2u
LI 25 30 30 30
LI 25 35 30 35
LI 25 40 30 40
LI 25 45 30 45
RV 30 25 50 50
LI 50 30 55 30
LI 55 35 50 35
LI 55 40 50 40
LI 55 45 50 45
LI 95 30 100 30
LI 95 40 100 40
RV 80 25 95 45
TY 81 41 5 3 90 1 0 * TMP36
LI 95 35 100 35
TY 88 39 4 2 0 0 0 * GND
TY 88 28 4 2 0 0 0 * OUT
TY 89 34 4 2 0 0 0 * V+
TY 35 45 5 3 90 1 0 * MAX619
MC 35 55 0 0 170
MC 35 20 0 0 170
LI 55 45 55 55
LI 55 55 45 55
LI 35 55 25 55
LI 25 55 25 45
LI 25 30 25 20
LI 25 20 35 20
LI 45 20 55 20
LI 55 20 55 30
LI 55 35 60 35
LI 60 35 60 40
LI 60 40 55 40
LI 55 40 60 40
LI 60 40 70 40
LI 70 40 70 50
LI 70 50 105 50
SA 60 40
MC 10 65 0 0 460
LI 25 35 10 35
LI 10 35 10 65
MC 10 85 0 0 045
TY 15 85 4 2 90 1 0 * 3v(2x1.5)
MC 15 35 1 0 170
SA 15 35
MC 15 45 0 0 045
TY 15 35 3 2 0 0 0 * 10u
LI 25 40 20 40
LI 20 40 20 65
LI 20 65 30 65
MC 30 65 1 0 170
MC 30 75 0 0 045
LI 30 65 50 65
RV 50 60 65 70
LI 90 65 65 65
LI 60 70 60 85
MC 60 85 0 0 045
SA 30 65
TY 35 70 3 2 0 0 0 * 10u
LI 70 65 70 75
LI 70 85 70 90
MC 70 90 0 0 045
SA 70 65
MC 70 75 1 0 170
TY 70 75 3 2 0 0 0 * 1u
TY 52 61 3 2 0 0 0 * TC1263
TY 50 64 3 2 0 0 0 * I
TY 62 64 3 2 0 0 0 * O
TY 56 66 3 2 0 0 0 * GND
TY 40 15 3 2 0 0 0 * 0.22u
TY 30 55 3 2 0 0 0 * 0.22u
TY 85 90 3 2 90 0 0 * 2.048
TY 80 20 3 2 0 0 0 * 10mV/°C
TY 45 60 3 2 0 0 0 * 5V
TY 70 60 3 2 0 0 0 * 3.3v
LI 120 65 135 65
LI 135 65 135 80
LI 135 65 145 65
LI 145 65 145 80
MC 135 80 1 0 170
MC 145 80 1 0 170
MC 135 90 0 0 045
MC 145 90 0 0 045
TY 135 80 3 2 0 0 0 * 10u
TY 145 80 3 2 0 0 0 * 100n
SA 135 65
SA 120 65
PROGRAM TermoLight;
//{$DEFINE DEBUG_MODE}
USES
    EEpromLib_E_a;
Const
     ZonaTmpMax=$30;
     ZonaTmpMin=$20;
     MaxSteps=3;
     D7S:ARRAY[0..17] of
byte=($3f,$06,$5B,$4F,$66,$6D,$7D,$07,$7F,$6F,$77,$7C,$39,$37,$30,$40,$54,$76);
VAR
  StepCorrente:Byte;
  Tick_count : longint; absolute $20;
  TempValida : Byte; absolute $30;
  AppoDT:Byte;
  TmpLettaGradi:Integer;
  Segno:Byte;
  TkCnt:Byte;
  Tmp_Prec:Word;
  Tmp_Max:Word;
  Tmp_Min:Word;
FUNCTION mask(num: byte):byte;    // this function returns masks
begin
// for common cathode 7-seg. display
{  case num of
    0 : result := $3F;
    1 : result := $06;
    2 : result := $5B;
    3 : result := $4F;
    4 : result := $66;
    5 : result := $6D;
    6 : result := $7D;
    7 : result := $07;
    8 : result := $7F;
    9 : result := $6F;
    65: result := $77;  //10=A
    66: result := $7C;  //11=B
    67: result := $39;  //12=C
    77: result := $37; //M    //13
    73: result := $30; //I    //14
    45: result := $40; //-    //15
    78: result := $54; //n    //16
    88: result := $76; //X    //17
  end; //case end}
  result:=D7S[Num];
end;
FUNCTION TastoFn:Byte;
//pressione del tasto Function su PORT A bit 4, attivo alto per 1 ms;
//VAR OK:Byte;
Begin
 {Ok:=0;
  While Button(PORTA, 4, 1, 1) do
        Begin
             nop;
             Ok:=255;
        end;}
   Result:=Button(PORTA, 4, 75, 1)
   //Result:=Ok;
End;
FUNCTION TastoRes:Byte;
//pressione del tasto Reset su PORT A bit 5, attivo alto per 1 ms;
//VAR OK:Byte;
Begin
{  Ok:=0;
 While Button(PORTA, 5, 1, 1) do
        Begin
             nop;
             Ok:=255;
        end;}
   Result:=Button(PORTA, 5, 75, 1)
End;
PROCEDURE AbilitaDisplay(Num:Byte);
Begin
      //PortA.7:=1;
      //PortA.0:=1;
      //PortA.1:=1;
      PortA:=PortA OR $83;
       case num of
       1: PortA.1:=0;
       2: PortA.0:=0;
       3: PortA.7:=0;
      end;
end;
Procedure Scrivi7Seg(Quale,Cosa:Byte);
Begin
      PortB:=$00;
      PORTB:=Mask(Cosa);
      AbilitaDisplay(Quale);
    {$IF_DEF DEBUG_MODE}
    {$ELSE}
          // delay_ms(1);
    {$END_IF}
End;
PROCEDURE Scrivi(D_1,D_2,D_3:Byte);
Begin
     Scrivi7Seg(1,D_1);
     Scrivi7Seg(2,D_2);
     Scrivi7Seg(3,D_3);
End;
PROCEDURE WriteTempToEprom(Indirizzo:Byte; Tmp:Word);
Begin
  Eeprom_Write(Indirizzo,integer((Tmp SHR 8)) AND $FF);
  Delay_ms(20);
 Eeprom_Write(Indirizzo+1,byte(Tmp AND $FF)) ;
End;
FUNCTION LeggiTempFromEprom(Indirizzo:Byte):Word;
VAR B1,B2:Byte;
    OutT:Word;
Begin
     OutT:=0;
     B1:=EEprom_Read(Indirizzo);
     Delay_ms(20);
     B2:=EEprom_Read(Indirizzo+1);
     If (B1=$FF) AND (B2=$FF)
        then OutT:=0
      Else
       Begin
           Outt:=B1;
           //OutT:=OutT*$100;
           OutT:=OutT SHL 8;
           OutT:=OutT+B2;
       end;
     Result:=OutT;
ENd;
Procedure tmpWord_ToInt(TmpIn:Word; VAR OutT:Integer; VAR
OutSegno:Byte);
Var TmpLettaAppo:Integer;
Begin
     OutSegno:=0;
     Outt:=0;
      TmpLettaAppo:= (TmpIn SHL 1) ;
      //TmpLettaAppo:=((TmpIn *2)-480);
      If TmpLettaAppo<480 then
          Begin
                OutSegno:=$FF;
                TmpLettaAppo:=480-TmpLettaAppo;
//                TmpLettaAppo:=-1 * TmpLettaAppo;
          End
        Else
           TmpLettaAppo:=TmpLettaAppo-480;
      Outt:=TmpLettaAppo;
End;
Procedure DoLettura2;
Var Appo:Word;
Begin
     Appo:=adc_Read(2);
    // WriteTempToEprom(0,aPPo) ;
//     Appo:=(Tmp_Prec + Appo) div 2;
       Appo:=(Tmp_Prec + Appo) SHR 1;
    //attendo un po prima di calcolare mix e man
     If TempValida>=255 then
      Begin
       If Appo<Tmp_Min then
       Begin
           Tmp_Min:=Appo;
           WriteTempToEprom(ZonaTmpMin,aPPo) ;
       End;
       If Appo>Tmp_Max then
       Begin
           Tmp_Max:=Appo;
           WriteTempToEprom(ZonaTmpMax,aPPo) ;
       End;
      End;
   {  TmpLettaGradi:=((aPPo *2)-500);
      If TmpLettaGradi<0 then
          Begin
                Segno:=$FF;
                TmpLettaGradi:=-1 * TmpLettaGradi;
          End ;}
    tmpWord_ToInt(Appo,TmpLettaGradi,Segno);
    Tmp_Prec:=Appo;
End;
Procedure DisplayTemp(TinGradi:Integer; SegnoIn:Byte);
Var
   Appo:Word;
Begin
            Clearbit(TrisA,6) ;
            If SegnoIn=$FF then  PortA.6:=1 Else PortA.6:=0;
            delay_ms(2);
            PortA.6:=0;
            Setbit(TrisA,6) ;
            Appo:=integer(TinGradi div 100) mod 10;
            If appo<>0 then begin
              Scrivi7Seg(1,Appo);
              delay_ms(2);
            end;
            Appo:=integer(TinGradi div 10) mod 10;
            Scrivi7Seg(2,Appo);
            delay_ms(2);
            Appo:=TinGradi mod 10;
            Scrivi7Seg(3,Appo);
End;
Procedure DisplayMinMax(CheAddr:Byte);
Label FineProc,
      FineProcReset;
Var T:Integer;
    S:Byte;
    Appo:Word;
Begin
    S:=0;T:=0;
    Appo:=LeggitempFromEprom(CheAddr);
    tmpWord_ToInt( Appo,T,S);
  Repeat
           Appo:=0;
           Repeat
                 Appo:=APpo+1;
                 Scrivi7Seg(1,13);
                 //Scrivi7Seg(1,Ord('M'));
                 Delay_ms(2);
                 if (TastoFn=255) Then GoTo FineProc;
                 if CheAddr=ZonaTmpMin Then
                   Begin
                        //Scrivi7Seg(2,Ord('I'));
                        Scrivi7Seg(2,14);
                        Delay_ms(2);
                        //Scrivi7Seg(3,Ord('N'));
                        Scrivi7Seg(3,16);
                  End
                  Else
                   Begin
                      //Scrivi7Seg(2,Ord('A'));
                      Scrivi7Seg(2,10);
                      Delay_ms(2);
                      //Scrivi7Seg(3,Ord('X'));
                      Scrivi7Seg(3,17);
                    ENd;
                 Delay_ms(2);
           Until (Appo>=112);
           Appo:=0;
           Repeat
                 Appo:=APpo+1;
                 if (TastoFn=255) Then GoTo FineProc;
                 if (TastoRes=255) Then GoTo FineProcReset;
                 DisplayTemp(T,S);
           Until (Appo>=220);
  Until False;
FineProcReset:
    Nop;
    if CheAddr=ZonaTmpMin Then
       Begin
           WriteTempToEprom(ZonaTmpMin,1500) ;
           Tmp_Min:=1500;
       end;
    if CheAddr=ZonaTmpMax Then
       Begin
           WriteTempToEprom(ZonaTmpMax,0) ;
           Tmp_Max:=0;
       end;
       Appo:=0;
      Repeat
            Appo:=APpo+1;
            //Scrivi7Seg(1,Ord('-'));
            Scrivi7Seg(1,15);
            Scrivi7Seg(2,15);
            Scrivi7Seg(3,15);
      Until (Appo>=4095);
FineProc:
    Nop;
    //StepCorrente:=1;
end;
begin
     OPTION_REG := $80;
     OSCCON:=$70; //Internal 8Mhz clock
     Ansel:=$0C; //Input analogico da RA2/AN2 segnale, RA3 vref+
     ADCon0:=$D0; //clock a/d rc osc , Ch RA2/AN2
     ADCon1:=$A0; //right justified,RA3 vref+
     TrisB:=0; //porta B è output per display
     TrisA:=$3C; // Port A input tranne RA1/AN1 e Ra0/An0  e Ra7  e
ra6(segno)
     AbilitaDisplay(4);
     PortA.6:=0; //segno
     Tick_count:=0;
     CCP1Con:=0;
     TempValida:=0;
     Segno:=0;
     TmpLettaGradi:=0;
    Tmp_Prec:=Adc_Read(2);
     Tmp_Min:=LeggitempFromEprom( ZonaTmpMin);
     If (Tmp_MIN=$FFFF) Then Tmp_Min:=$05DC;
     Tmp_Max:=LeggitempFromEprom( ZonaTmpMax);
     If (Tmp_Max=$FFFF) Then Tmp_max:=$0;
     TkCnt:=128;
     TmpLettaGradi:=0;
     StepCorrente:=1;
     Repeat
          Tick_count:=Tick_count+1;
          If StepCorrente=1
            then
             begin
              {  AppoDT:=0;
                If TempValida>=254
                 then
                  Repeat
                   TkCnt:=3;
                   AppoDT:=AppoDT+1;
                   DisplayTemp(TmpLettaGradi,Segno);
                  Until AppoDt>=32
                Else  }
                DisplayTemp(TmpLettaGradi,Segno);
             end;
          If StepCorrente=2 then DisplayMinMax(ZonaTmpMin);
          If StepCorrente=3 then DisplayMinMax(ZonaTmpMax);
          If (TastoFn=255)
             Then
                 If StepCorrente=MaxSteps
                   THen StepCorrente:=1
                   Else StepCorrente:=StepCorrente+1;
          If Tick_Count=TkCnt then
           Begin
                DoLettura2;
                Tick_Count:=0;
                If TempValida<255 Then TempValida:=TempValida+1
           End;
          //PortB:=$00;
          //PortA:=PortA OR $83;
        While Button(PORTA, 4, 1, 1) do nop;
      Until False;
end.

poi è la risoluzione max che posso ottenere).
La tua sonda restituisce una variazione di 10mV/C, se il tuo convertitore è 
a 10 Bit hai una "sensibilità" di 0.2 °C, è un full range di 200 gradi. Puoi 
realizzare due soluzioni diverse:
1) se il full range è troppo ampio (per esempio stai misurando temperature 
che vanno da 0 a 30) allora puoi adattare il segnale in uscita dalla TMP36 
con un amplificatore ed ottenere una variazione maggiore di tensione a 
parità di variazione di temperatura.
2) se l'ADC riesce a campionare a frequenza abbastanza alta puoi utilizzare 
il filtro di media (che tu gia utilizzi) per aumentare la risoluzione del 
tuo ADC, tenendo presente che questo sistema funziona solo se ad alta 
frequenza (quindi intorno alla massima frequenza di campionamento) hai del 
rumore. Se raggiungi qualche centinaio di Hz stai tranquillo che puoi 
portare la tua risoluzione almeno a 14/16 bit. In questo secondo caso hai 
aumentato la dinamica del tuo convertitore e quindi la dinamica del sistema.
La seconda soluzione mi sembra quella piu interessante perchè non devi 
effettuare modifiche sul circuito e soprattutto perchè essendo la 
temperatura un segnale lento (si misura a bassa frequenza) il numero di 
medie che riesci a fare è molto alto e quindi la risoluzione può essere 
spinta.
Questa è solo un suggerimento pratico, una prova da fare "al volo" poi fai 
le tue considerazioni del caso.
Emiliano.

la temp può andare da -20 a 50 gradi...come quella ambientale
non ho ben capito però come posso "aumentare" la risoluzione dell'ADC:
il 16f88 ce l'ha a 10bit come hai detto

 il 16f88 ce l'ha a 10bit come hai detto
Buona lettura ....
http://folk.uio.no/perthi/alice/doc/oversampling
Ciao
Ema

 ha scritto nel messaggio 
news:1138369541....@g47g2000cwa.googlegroups.com...


 una maggiore risoluzione.
Con questo metodo riesci a filtrare i disturbi e rumore casuale presenti
nel segnale, ma non migliori la precisione complessiva propria dell'ADC.

 una maggiore risoluzione.
Secondo me questi discorsi (sovracampionare, mediare, ecc.) vanno bene
solo se stai facendo un campionamento vero e proprio, in cui hai un clock
preciso e campioni ad intervalli regolari. Allora puoi utilizzare tutta
la teoria del campionamento di Nyquist, considerando la banda del segnale
d'ingresso, utilizzando una frequenza di campionamento pari almeno al doppio,
o molto di più per sovracampionare e aumentare la precisione.
Con i uC non puoi essere così preciso nel campionamento ma, suppongo,
tu vada a campionare quando capita, in funzione di cosa sta facendo il
tuo programma. In questi casi, puoi soltanto sperare di cancellare qualche
disturbo casuale mediando... sei fortunato perchè la temperatura è un
segnale con banda molto stretta, quindi puoi campionare molto lentamente.
Ricordo che qualcuno riusciva a filtrare numericamente il disturbo della
50Hz, più o meno sempre presente, anche con un uC, andando a campionare
nell'interrupt di un timer che scatta ogni 20ms (1/0.02=50). In quel
modo dovresti andare a campionare quando il disturbo della 50Hz di rete
assume sempre lo stesso valore... ti produrrà un offset ma almeno sarà
costante.
Oppure puoi aumentare la frequenza di campionamento (diciamo a 5ms), sommare
quattro campioni e scartare gli ultimi due bit (dividi per 4). Così dovresti
pure riuscire a filtrare il rumore, oltre che renderlo costante.
Purtroppo mi sono imbattuto anche io in questi "ballonzolamenti" delle
misure campionate, anche per segnali molto lentamente variabili (temperatura)
ma non c'è stato verso. Condensatori qua e là, bit abbassamento della
risoluzione da 10 a 8 o meno bit, medie fatte via software. Tutti trucchi
sporchi che non centrano il problema...

pure riuscire a filtrare il rumore, oltre che renderlo costante.
Si può fare di meglio, leggi in proposito questo post di Franco
http://shorterlink.com/?0AYOA2
--
ciao
  Stefano


Ciao,
Pier.
www.piergm.com

 
http://shorterlink.com/?0AYOA2
E che c'č di diverso rispetto a quello che ho detto io?

>> Oppure puoi aumentare la frequenza di campionamento (diciamo a 5ms), sommare

E' sostanzialmente la stessa cosa, poi mi riferivo al post n.6, cioč al fatto
che in effetti fai un filtro FIR.
Tipicamente nei casi come quello esposto, basta un filtro che metta
degli zeri di trasmissione sulla frequenza della rete e sue armoniche
(nota che fare la media vuol dire fare un filtro FIR con funzione di
trasferimento di tipo sin(x)/x. 
--
ciao
  Stefano

 nel segnale, ma non migliori la precisione complessiva propria dell'ADC.
Su questo non sono daccordo, io parlavo di aumentare la risoluzione e quindi 
riuscire ad essere sensibili anche a variazioni inferiori (e anche di molto 
in certi casi) all'LSB.
Per quanto riguarda la precisione della misura dovresti indicarmi a cosa ti 
riferisci, in certi casi sovracampionando puoi miglirare alcuni parametri 
del tuo ADC.
Emiliano. 

 del tuo ADC.
Mi riferisco a tutti i tipi di errore di un ADC che, mediando,
non puoi eliminare.

non puoi eliminare.
Credo che sia sempre bene mediare un dato, oltre che per togliere il rumore di
rete anche per ridurre in termini statistici l'influenza del LSB che poi è il
limite del l'ADC.
--
ciao
  Stefano

 non puoi eliminare.
Ovviamente. :)
Ciao, Emiliano.