Forum: Mikrocontroller und Digitale Elektronik PIC16F877A ADC-Messung Messwertumrechnung


von Gerhard H. (oderlachs)


Lesenswert?

Hallo Freunde !
Ich möchte mal die Fachleute hier befragen, was ich falsch mache.
Mit einem PIC16F877A...(er ist nun mal auf meinem Entwicklerboard)
habe ich mich mit der ADC Messung befasst. Da auch einige PORTA I/O
mit dem LCD Anschluss fest verknüpft sind  steht mir als ADC  AN6 = RE1
 zur Verfügung.
Nun habe ich bei der Umrechnung nur die Brotpreise von Chicago 
rausbekommen und nicht etwa was ich als Messwert eingebe.
Da ich mit 10-Bit Auflösung bin ich nach folgender Formel gegangen:
1
Vref = 5V
2
Umess = (ADC_Wert/1023)*Vref

Liege ich mit meinen Gedanken da falsch ?? Ich dachte das bei 5V Ref und 
5V am Messeingang bei 10 Bit dann 1023 ausgegeben werden.

Aber irgendwie bekomme ich es nicht gebacken. Vielleicht spinnt auch 
sprintf bei der Umwandlung von float zu char.
Bei 5V  am ADC bekomme ich:  119,17 V,  am LCD angezeigt. Wer kann BITTE 
helfen ?
Hier der Code (in XC8 - C)
1
// adc_to_lcd.c
2
//
3
// CONFIG
4
#pragma config FOSC = XT        
5
#pragma config WDTE = OFF       
6
#pragma config PWRTE = OFF      
7
#pragma config BOREN = OFF      
8
#pragma config LVP = OFF        
9
#pragma config CPD = OFF        
10
#pragma config WRT = OFF        
11
#pragma config CP = OFF        
12
13
#include <xc.h>
14
#include <pic16f877a.h>
15
#include <stdio.h>
16
#include <stdlib.h>
17
#define _XTAL_FREQ 4000000  // XT
18
//#define _XTAL_FREQ 8000000  // HS
19
20
#define vref 5.000    /* Reference Voltage is 5V*/
21
#include "../lib/ol_pic_lcd204.h"
22
void ADC_Init()
23
{
24
   ADCON0 = 0x81;  // 0b10000001
25
   ADCON1 = 0x80;  // 0b10000000  //Vref+ = Vdd(5V), Vref- = Vss
26
}
27
28
unsigned int ADC_Read(unsigned char channel)
29
{
30
   if(channel > 7)
31
     return 0;
32
33
   ADCON0 &= 0xC5;
34
   ADCON0 |= channel << 3; // Eingang RE1 = AN6...0x30
35
   GO_nDONE = 1;
36
   while(GO_nDONE);
37
   return ((ADRESH<<8)+ADRESL);
38
}
39
40
void main()
41
{
42
   unsigned int a;
43
   ADC_Init();
44
   LCD_Init();
45
   char mw[10];  // String Messwert 
46
   do
47
   {
48
     a = ADC_Read(6);
49
     float fmw;  //float Messwert
50
   
51
   // Convert digital in analogen float Anzeigewert  
52
   fmw = ((float)a/1023) * (float)vref;
53
   
54
     sprintf(mw,"%.3f",fmw);  // 3 stellig hinterm komma
55
     LCD_Set_Cursor(1, 2);
56
     LCD_Write_String("Messwert = ");
57
     LCD_Set_Cursor(3, 6);
58
     LCD_Write_String(mw);
59
     LCD_Set_Cursor(3, 12);
60
     LCD_Write_String("V");
61
     
62
     __delay_ms(100);
63
   }while(1);
64
}
Schon mal Danke sage im Voraus .

Gerhard

von TK (Gast)


Lesenswert?

Hallo,

Du solltest Dich mal mit dem speziellen Timing für die ADC Erfassung
beschäftigen.
Dann ist es auch guter Stil bei der Umrechnung ZUERST die Multiplikation 
und dann erst die Division auszuführen (oder viele Klammern setzen! und 
die 1023 auch als float zu definieren => 1023.0).

Vielleicht hilfts ja

Gruß
TK

von Volker S. (vloki)


Lesenswert?

Gerhard H. schrieb:
> Vielleicht spinnt auch
> sprintf bei der Umwandlung von float zu char.

Lass das doch erst mal weg, bleib bei Integer und gib den Wert in mV 
aus.
(ADRESH<<8) ergibt eventuell auch immer NULL.

von Christian M. (Gast)


Lesenswert?

1
ADCON1 = 0x80;  // 0b10000000  //Vref+ = Vdd(5V), Vref- = Vss

Geht das bei Dir überhaupt? So hast Du den ganzen Port A als 
Analogeingänge, weil

Gerhard H. schrieb:
> Da auch einige PORTA I/O
> mit dem LCD Anschluss fest verknüpft sind  steht mir als ADC  AN6 = RE1
>  zur Verfügung.

Gruss Chregu

von Gerhard H. (oderlachs)


Lesenswert?

Christian M. schrieb:
> Geht das bei Dir überhaupt? So hast Du den ganzen Port A als
> Analogeingänge, weil

Hallo Christian!

Wie Du schon vielleicht beim Lesen meines Beitrages am Anfang entnehmen 
konntest, nutze ich gar nicht PortA für ADC, sondern PORT-E1.
PortA dient leider bei dem Enwicklerboard("EasyPIC-40") zu Steuerung des 
LCD oder 7seg-Anz_Display.
Das Datenblatt sagt wie es geht, darum ist ADCON1 binär 1000000 gesetzt.

Die Normalität ist ja das PortA mit dem ADC zu tun hat aber hier sind 
AN6 AN7 PORTE zugeordnet. Datenblatt ab Seite 127.

Ich musste auch erst knobeln, bis ich das herraus hatte. Die 
Codebespiele vom Boardhersteller sind alles andere als verwendbar...na 
ja zum Umstricken und abgucken... ;)
Aber ich werde nochmals duch Netzt wandern, ich werde ja nicht der 
"einzige PIC-ADC-Kunde" sein.
Danke aber, das Du Dir da Gedanken gemacht hast !

Gerhard

von Volker S. (vloki)


Lesenswert?

Gerhard H. schrieb:
> Aber ich werde nochmals duch Netzt wandern, ich werde ja nicht der
> "einzige PIC-ADC-Kunde" sein.

Aber vielleicht einer der wenigen, die so einen alten PIC verwenden.
Wenn die Gelegenheit mal günstig ist, dann kannst du den aber problemlos 
gegen einen aktuellen austauschen. Bei den PIC16 und PIC18 ist fast 
alles pinkompatibel!


Christian M. schrieb:
> Geht das bei Dir überhaupt? So hast Du den ganzen Port A als
> Analogeingänge, weil

Macht ja nichts, wenn die dann als Ausgänge verwendet werden...

von Gerhard H. (oderlachs)


Lesenswert?

So das Rätsel ist gelöst und zwar lag  es am Auslesen der Register 
ADRESH und ADRESL.

Hier nun richtig:
1
unsigned int ADC_Read(unsigned char channel)
2
{
3
   if(channel > 7)
4
     return 0;
5
   unsigned int adc_ret;
6
   adc_on();  
7
  
8
   ADCON0 |= channel << 3; // Eingang RE1 = AN6.. = 0x30
9
   __delay_ms(2);
10
   GO_nDONE = 1;
11
   while(GO_nDONE==1);
12
   adc_ret = ADRESH & 0x03;              // !!!!!!!
13
   adc_ret = ((adc_ret <<8) + (ADRESL)); // !!!!!!!
14
  adc_off();
15
   return adc_ret;
16
}

Es musste ja auch mit nem alten Pic16f877a gehen, denn zu irgendwas hat 
man die ja mal gefertigt. ;)

Muss mich nochmals mit dem Datenblatt und dem dicken PIC Buch befassen. 
Irgendwo muss ja was stehen, warum die beiden 8Bit-Register anders 
interpretiert werden müssen beim Auslesen.
Komisch  das dabei normale MSB-LSB Konvertierung versagt..

Allen nochmals herzlichen Dank !!!!

Gerhard

: Bearbeitet durch User
von Christian M. (Gast)


Lesenswert?

Gerhard H. schrieb:
> Es musste ja auch mit nem alten Pic16f877a gehen, denn zu irgendwas hat
> man die ja mal gefertigt. ;)

Wenn Du noch an Lager hast, ok. Hab auch noch ein paar. Der Ersatz ist 
der 16F193x, absolut pinkombatibel. Läuft intern mit PLL bis 32MHz, 
kostet nur die Hälfte, ist aber ein bisschen schwieriger zu 
konfigurieren...

Gruss Chregu

von W.S. (Gast)


Lesenswert?

Gerhard H. schrieb:
> Liege ich mit meinen Gedanken da falsch ?? Ich dachte das bei 5V Ref und
> 5V am Messeingang bei 10 Bit dann 1023 ausgegeben werden.
>
> Aber irgendwie bekomme ich es nicht gebacken. Vielleicht spinnt auch
> sprintf...

Da dreht sich mir der Magen um.

wenn du die PIC16F.. nicht in Assembler prigrammieren kannst, solltest 
du sie überhaupt nicht programmieren und stattdessen dir ein Raspberry 
zulegen.

So und nun zu den Details:
Für 5.0 Volt Referenz kriegst du einen Überlauf, falls du ebenso 5.0 
Volt anlegst. Der höchste ausgebbare Wert des ADC ist 1023 und das ist 
4.995117etc, also 1 LSB unter der Referenz. Sowas sollte ein Benutzer 
eines ADC mittlerweile gelernt haben.

Und wer auf einem PIC16F.. mit printf herumfuchtelt, über den kann ich 
nur verständnislos den Kopf schütteln - oder selbigen mit einem nicht 
stubenreinen Wort versehen.

Also: Weißt du nicht, wie man eine Zahl mit 5 multipliziert? Kopie 
machen, 2x nach links verschieben, dann Zahl nochmal draufaddieren. Wenn 
du den ADC linksbündig arbeiten läßt, hättest du damit bereits die erste 
Dezimalstelle. Für weitere Dezimalstellen wäre dann das Multiplizieren 
mit 10 angesagt.

Also stell dich nicht so an!

W.S.

von Volker S. (vloki)


Lesenswert?

Gerhard H. schrieb:
> Komisch  das dabei normale MSB-LSB Konvertierung versagt..

Das Detail, was du da vermutlich meinst, ist dass du jetzt einen 16Bit 
Wert 8 mal shiftest (adc_ret<<8).

Vorher war es ja nur das 8Bit Register (ARESH<<8). Wenn man ein 8Bit 
Register um 8 nach links schiftet, dann werden ja 8 Nullen von rechts 
nachgeschoben. Also stehen am Ende immer 8 Nullen in diesem Register 
drin. Das ist wie löschen ;-)

Volker S. schrieb:
> (ADRESH<<8) ergibt eventuell auch immer NULL.

: Bearbeitet durch User
von Gerhard H. (oderlachs)


Lesenswert?

Hallo Christian !

Ich kaufe nichts neues, ich habe "riesengrosse Vorratskisten"  voll und 
in der Winterzeit , wenn keine Gartenarbeit draussen ist , will man sich 
als Rentner die Zeit vertreiben.

Darum versuche ich mal so einige Beispiele mit PIC,AVR, STM usw..aus 
lange Weile und gespannt auf das Ergebnis mal durchzuprobieren...

Nu,r das das Progamm mir "Anodenspannung" anzeigte, anstatt was im 
bereich um die 4..5 Volt war mir jetzt doch ein wenig zu dumm....

In meinem Hobby-Alter kümmert es mich nicht mehr, ob da einige µV oder 
mV zu viel oder zu wenig sind, es geht ums Prinzip.

Es ist nur noch Hobby und so solls bleiben.

PICs habe ich verschiedene vom 20 bis 40 Pinner  na da will und möchte 
man doch mal mit probieren....
Zumal noch Sortierkästen mit diversen Sensoren, SD-Modulen, GSM-Modem 
und GPS-Rx  da sind , sowas möchte ich doch mal austesten.....

Somit vielen Dank für alle Hinweise, auch den anderen Usern

Gerhard

von Gerhard H. (oderlachs)


Lesenswert?

Danke Volker, ist ja auch logisch !!
....ich hatte das nicht gelesen weil ich noch beim Schreiben war....aber 
solche "Unfälle" machen schlauer...und man denkt nach.
Nun ja ich betrete diese Bühne ja erst seit kurzem etwas intensiever, 
das die programme anbelangt...sonst nur Led leuchten lassen am I/O Port

Gerhard

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.