Forum: Mikrocontroller und Digitale Elektronik C167 analoge Schnittstelle


von Mike Müller (Gast)


Lesenswert?

Hi,

ich bin gerade dabei beim Keil XC 167 Board den AD Wanlder einzustellen.
Allerdings gibt er immer 0 aus!:(

Ich schreib euch den Code mal hier rein. Sry das der Code so ausführlich 
ist, aber ich bin in der Materie MC erst seid 1 Woche!



/*/**********************************************************/
/*                                                        */
/*      AD Wandler                           */
/*                                                        */
/**********************************************************/

#include <reg167.h>
#include <stdio.h>
#include <math.h>
#define VREF 5                      /*  ADC Voltage Reference 
*/
/*********************************************************************** 
*******/
/***************************      MAIN PROGRAM 
***************************/
/*********************************************************************** 
*******/
 unsigned int adcwert;
 unsigned int adcberechnet;
 sfr  ALTSEL0P3 = 0xF126;    // Umleitung des Seriellen Ports   !!!




void main (void)
{
/*
 Konfiguration der seriellen Schnittstellen
*/
P3 |= 0x0400;    // Port 3.10 = 1 zur Ausgabe benutzen
DP3 |= 0x0400;    // Port 3.10 für Datenausgabe konfigurieren
DP3 &= 0xF7FF;    // Port 3.11=0 für Datenausgabe konfigurieren
ALTSEL0P3 |= 0x0400;    // Umleiten COM auf SubD9 Schnittstelle     !!!

S0TIC = 0x0080;    // Transmit Interruptflag setzen
S0RIC = 0x0000;   // Recieve Interruptflag löschen
S0RBUF =
S0CON =0x8011;     //           Serial Channel 0 Control    0x8011 = 1 0 
0 0 0 0 0 0 0 0 0 1 0 001
/*
Wird alles in S0CON definiert

S0M = 001 ;    // asynchron 8-bit Daten
S0STP = 0;    // Anzahl der Stopbits -> eins
S0REN = 1;      // Freigabe des Empfängers auf frei setzen
S0PEN = 0;    // Freigabe der Paritätsprüfung auf freigegeben setzen
S0FEN = 0;    // Freigabe der Rahmenprüfung auf freigegeben setzen
S0OEN = 0;    // Freigabe der Überlaufsprüfung auf freigegeben setzen
S0PE = 0;    // Fehlermarke Paritätsprüfung -> Bit durch Befehl löschen
S0FE = 0;    // Fehlermarke Rahmenprüfung -> Bit durch Befehl löschen
S0OE = 0;    // Fehlermarke Überlaufsprüfung -> Bit durch Befehl löschen
S0ODD = 0;    // Auswahl der Parität -> gerade
S0BRS = 0;    // Baudratenauswahl -> Teiler durch zwei
S0LB = 0;    // Freigabe Rückführung auf nein
S0R = 1;     // Freigabe Baudgenerator -> Takt freigegeben
*/
S0BG = 0x04;   // Baudrate auf 19200 setzen     20MHz
S0TBIC =1 ;     // Daten nach Schieberegister

/*
AD Wandler
*/

ADCON = 0x0111; //    A/D Wandler Control     00 00 0 0 1 0 0 01 0001 = 
0x0111

/*
Wird alles in ADCON definiert

ADCH = 0001;    // Port 5.1 als Startkanal
ADM = 01;       // ein Kabal, fortlaufende Messung
ADST = 0;      // Startet die Wandlung sobalds im Programm auf 1 gesetzt 
wird
ADBSY = 0;      // wird während der Wandlung von der Steuerung auf 1 
gesetzt
ADWR = 1;      //  wartet bis daten gelesen sind 1 = ja
ADCIN = 0;      // keine Einzelmessungen einfügen
ADCRQ = 0;
ADSTC = 00;      // Sample Zeit 00= 1 Takt
ADCTC = 00;     // 00 = 24 Takte
*/

/* Test ob  Serielle Schnittstelle funktioniert
while(1)
{printf("Hey Test!\n");
}
*/
while(1)
{
 ADST =1;     // startet den wandler
   while(ADBSY);
  {}

  adcwert = (ADDAT & 0x3ff) >>2;
  adcberechnet = (adcwert*5)/(1024*1000);



  printf("%i", adcberechnet);
  printf("Hi");




}


}

von Guido B. (guido-b)


Lesenswert?

Mike Müller schrieb:
> adcwert = (ADDAT & 0x3ff) >>2;
>   adcberechnet = (adcwert*5)/(1024*1000);

Da bleibt halt nicht viel übrig. Wahrscheinlich soll die
Tausend nicht in den Nenner?

von Mike Müller (Gast)


Lesenswert?

Hi,

da hast du natürlich recht... da man bei 10Bit ja ca 5mV als Abstufung 
hat -> ergo muss ich die 1000 nach oben ziehen! Richtig!

allerdings spuckt der ADU jezt sehr komische Werte aus

Wert=50;Wert=46;Wert=57;Wert=9;Wert=0;Wert=0;Wert=0;Wert=0;Wert=0;Wert=0 
;Wert=0;Wert=0;Wert=0;Wert=0;Wert=0;Wert=0;Wert=0;Wert=0;Wert=0;Wert=9;W 
ert=48;Wert=23;Wert=42;Wert=8;Wert=47;Wert=51;Wert=56;Wert=55;Wert=60;We 
rt=60;Wert=15;Wert=0;Wert=29;Wert=23;Wert=26;Wert=5;Wert=29;Wert=29;Wert 
=29;Wert=14;Wert=49;Wert=14;Wert=10;Wert=59;Wert=35;Wert=6;Wert=40;Wert= 
1;Wert=41;Wert=5


Heißt doch eigentlich, dass meine Eingangsspannung nicht konstant ist 
oder? Allerdings benutz ich ein Netzteil ... müsst also theoretisch 
stimmen

von Guido B. (guido-b)


Lesenswert?

Hast du Uref an VCC angeschlossen und entkoppelt?

Mir fehlt noch die Umschaltung von P5.1 zum Analogeingang.
Diese erfolgt über P5DIDIS (= 1).

von Mike Müller (Gast)


Lesenswert?

Guido B. schrieb:
> Hast du Uref an VCC angeschlossen und entkoppelt?

Also ich habe ein Netzteil mit 4 Ausgängen, je 2+ und 2-
Einmal habe ich + und - als Versorgungsspannung mit 6V eingestellt und 
die anderen habe ich via Pinstecker zum einen + auf den Pin 5.1 und - 
auf GND gesteckt. Das müsste doch so passen?


> Mir fehlt noch die Umschaltung von P5.1 zum Analogeingang.
> Diese erfolgt über P5DIDIS (= 1).

Ja das mit P5.1 habe ich noch gemacht einfach P5 = 0x0000 -> Alle als 
Eingang

Also der ADC gibt jezt auch Werte in Abhängigkeit von der SPG aus. 
Allerdings mit einem Faktor von 4 zu klein -> bei 5V bekomm ich 1,2 als 
Ausgabe bei 2,5V -> 0,64 ... bei 0,02V (Netzteil lässt sich nicht auf 0V 
stellen) 0,06 -> Ich habe jezt einfach das Ergebnis mit dem Faktor 4 
multipliziert so habe ich eine proportionalität von 5v ausgehend da der 
Faktor aber nicht 100% konstant ist habe ich wenn ich z.B 0,02V 
einstelle einen zu großen Wert.
Woran könnte die Ungenauigkeit liegen?

Zum Anderen möchte ich jezt noch ein digitales FIR Filter -> hat dazu 
jemand noch etwas das weiterhilft? Wie Programmiert man so einen Filter?

von Guido B. (guido-b)


Lesenswert?

Wundert mich, dass es klappt ohne P5DIDIS zu setzen.

Mike Müller schrieb:
> adcwert = (ADDAT & 0x3ff) >>2

Da teilst du durch 4, vllt. ist das der Fehler.

Du willst den AD-Wert durch 1024 teilen (ich würde ev.
vorher 1 addieren) und dann mal 1000 nehmen. Dazu würde ich
mal 2000 nehmen und durch 2048 (16  16  8) teilen. Um
die Bitverluste zu miniieren wpürde ich das in 3 Schritten
machen: Mal 20, durch 16. Nochmal mal 20 und durch 16. Mal
5 und durch 8.

von Mike Müller (Gast)


Lesenswert?

Guido B. schrieb:
> Wundert mich, dass es klappt ohne P5DIDIS zu setzen.
>

Für was wird denn P5DIDIS verwendet? Setzt er P5 als analogen Eingang?

> Mike Müller schrieb:
>> adcwert = (ADDAT & 0x3ff) >>2
>
> Da teilst du durch 4, vllt. ist das der Fehler.

Inwiefern teile ih da durch 4? Hier lese ich doch nur die Daten von 
ADDAT aus und Schiebe 2 Bits raus sodass ich von den 10Bit des ADW auf 
8Bit komme
Bitte um Erklärung :)

>
> Du willst den AD-Wert durch 1024 teilen (ich würde ev.
> vorher 1 addieren) und dann mal 1000 nehmen. Dazu würde ich
> mal 2000 nehmen und durch 2048 (16  16  8) teilen. Um
> die Bitverluste zu miniieren wpürde ich das in 3 Schritten
> machen: Mal 20, durch 16. Nochmal mal 20 und durch 16. Mal
> 5 und durch 8.

Also folgendes

adcwert = (ADDAT & 0x3ff) >>2;// 1111111111  = 0x3ff -> 10bit von 0-9
adcberechnet = (adcwert*5.0*20.0)/(16.0);
adcberechnet = (adcwert*20.0) / (16.0);
adcberechnet = (adcwert*5.0) / (8.0);


So meinst du das oder?

von Guido B. (guido-b)


Lesenswert?

Mike Müller schrieb:
> Für was wird denn P5DIDIS verwendet? Setzt er P5 als analogen Eingang?

Siehe User-Manual:

"For pins that shall be used as analog inputs it is recommended to
disable the digital input stage via register P5DIDIS (see
description below). This avoids undesired cross currents and
switching noise while the (analog) input signal level is
between VIL and VIH."

Mike Müller schrieb:
> Inwiefern teile ih da durch 4? Hier lese ich doch nur die Daten von
> ADDAT aus und Schiebe 2 Bits raus sodass ich von den 10Bit des ADW auf
> 8Bit komme
> Bitte um Erklärung :)

Aus dem Maximalwert von 1023 wird durch das Schieben 255. Du hast also
durch 4 dividiert. Außerdem reduziert dieses Schieben die Auflösung
auf ca. 20 mV, ich würde es weglassen.

Mike Müller schrieb:
> So meinst du das oder?

Etwa. Bei der Umrechnung musst du darauf achten, dass der Uint nicht
überläuft, der Wert also kleiner 65536 bleibt, und dass beim
Dividieren keine Stellen durch Divisionsreste verloren gehen.

Mike Müller schrieb:
> adcberechnet = (adcwert*5.0*20.0)/(16.0);

Das kann einen Überlauf ergeben und sollte daher werden zu:
1
 adcberechnet = (adcwert*5*10)/(8);

Lass die Dezimalpunkte weg, nicht dasss der Compiler sie in den
falschen Hals bekommt.

Der Rest müsste stimmen, ich würde zu adcwert noch 1 addieren, so
dass der Wert zwischen 1 und 1024 liegt.

von Mike Müller (Gast)


Lesenswert?

Hi,

achso man schiebt ja um 8bit zu erhalten verringert dadurch aber 
natürlich auch die max. zahl wegen 2^8 < 2^10 .. :D


Variante 1:

adcwert =(ADDAT & 0x3ff);
adcberechnet = (adcwert*5) / (1024)


So klappts -> Wenn ich 2,5V einstelle bekomm ich 2,5-2,64 raus.

Variante2:

adcwert = (ADDAT & 0x3ff)
adcberechnet = (adcwert*5.0*10.0)/(8.0);
adcberechnet = (adcwert*20.0) / (16.0);
adcberechnet = (adcwert*5.0) / (8.0);

Passt nicht ganz ;) Das Komma is um 2 Verschoben -> bei dem Schitt mit 
*20 wird auch recht schnell ein Überlauf erzeugt.. :)


Ich habs gerade mal durchgerehnet und mit Variante 1 & 2 kommt fast das 
selbe raus.

Bei Variante 1 kann man sich halt sicher sein, dass nie ein Überlauft 
erzeugt wird, da 1023*5 < 65k

von Mike Müller (Gast)


Lesenswert?

Sry für den Doppelpost aber hab noch eine wichtige Frage.

Aktuell benutze ich ja ein Netzteil das gibt ja Gleichstrom.. was mach 
ich denn wenn ich einen Wechselstrom anlege - dann schwankt ja mein Wert 
andauernd - wie kann ich das realisieren?

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.