Forum: Mikrocontroller und Digitale Elektronik ATmega8 ADC-Durchschnittsberechnung


von krusti (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich habe ein Problem mit meinem ADC und ATmega8. Ich will mit dem ADC 
eine Durchschnittsberechnung durchführen, um das Flackern auf meinen 
7-Segementen zu verringern. Da meine Versuche mit einer for-Schleife 
nicht fruchteten, habe ich aus dem Tutorial die ADC_Read_Avg genommen. 
Allerdings funktioniert auch das nicht. Meine Anzeigen, zeigen nun nur 
"8" an.

Ich habe mit dem AVR Simulator (Studio 6) eine Simulation durchgeführt 
und es scheint mir, als ob er bei
1
while (ADCSRA & (1<<ADSC) )
 hängen bleibt. Ich habe für den ADC viel aus dem Tutorial genommen aber 
genau diese while bei der Init ebenfalls weggelassen, weil es scheinbar 
an diesem Teil hing. Nach dem entfernen dieses Teils, lief alles rund.
Ich kann diesen Teil noch nicht so recht nachvollziehen, was dort 
passiert bzw. wann sich das ADSC ändert.

Mein ADC lief ursprünglich im Free-Mode, weil ich mich da noch nicht 
festlegen wollte, dies wird durch die Teile aus dem Tutorial ja zum 
Single-Mode gemacht, wenn ich das korrekt sehe.

Grüße krusti

von Karl H. (kbuchegg)


Lesenswert?

krusti schrieb:

> Ich habe mit dem AVR Simulator (Studio 6) eine Simulation durchgeführt
> und es scheint mir, als ob er bei
1
while (ADCSRA & (1<<ADSC) )
> hängen bleibt.

Das ist ein 'Problem' im Simulator der nun mal keine reale ADC Hardware 
hat.

> Ich habe für den ADC viel aus dem Tutorial genommen aber
> genau diese while bei der Init ebenfalls weggelassen, weil es scheinbar
> an diesem Teil hing.

Nicht wirklich.

> Nach dem entfernen dieses Teils, lief alles rund.

Im Simulator.
Aber auf dem µC brauchst du den Teil

> Ich kann diesen Teil noch nicht so recht nachvollziehen, was dort
> passiert bzw. wann sich das ADSC ändert.

Das ist eigentlich ganz einfach.
Du setzt das Bit um dem ADC zu signalisieren: Mach mal
Und wenn der ADC fertig ist, dann zieht er das Bit wieder auf 0 zurück.

Deshalb
1
  ADCSRA |= (1<<ADSC);            // mach mal
2
  while (ADCSRA & (1<<ADSC) ) {   // ich warte solange, bis du fertig bist
3
  }

> Mein ADC lief ursprünglich im Free-Mode, weil ich mich da noch nicht
> festlegen wollte,

Der Free Modus hat meistens keinen wirklichen Sinn. Zumindest solange, 
solange du in der Hauptschleife ein delay mit 500ms drinnen hast, kannst 
du den ADC auch dauernd neu starten und aufs Fertig werden warten. 
Zeitmässig sind das Peanuts in Relation zum delay.

Allerdings ist es keine sehr gute Idee, das Multilexing der Anzeige am 
selben Port zu machen, auf dem auch der ADC beheimatet ist. Der ADC hat 
es ganz gerne, wenn auf 'seinem' Port eher Ruhe herrscht. Wenn du da 
dauernd Pins umschaltest, dann ist das ... sagen wir mal ... eher 
suboptimal.

Dein Anzeige Multiplex ist getestet und für gut befunden? Ebenso die 
Zahlenausgabe?

: Bearbeitet durch User
von Pandur S. (jetztnicht)


Lesenswert?

Als Mittelwert verwendet man optimalerweise einen exponentiellen 
Mittelwert. Siehe auch : 
http://www.ibrtses.com/embedded/exponential.html

Der benoetigt nur eine einzelne Speicherstelle, und ist konfigurierbar

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

1
  ADCSRA |= (1<<ADEN) | (1<<ADSC) | (1<<ADFR)
Du hast ihn ja immer noch im Free Running Modus!

Dann brauchst du allerdings den ADC weder dauernd starten noch auf die 
Beendigung warten.

Nur ist das in deinem Fall eher suboptimal, weil du nie genau weisst, 
wann denn der ADC ein neues Ergebnis fertig hat. Es kann dir daher 
passieren, dass du in deiner ausleserei x mal den immer gleichen Wert 
ausliest, weil der ADC noch gar kein neues Ergebnis vorrätig hat sondern 
noch an der Arbeit ist.

Nimm den Free Run raus.
Free Run macht nur dann Sinn wenn
man ihn zb an einen Timer zur Triggerung koppelt
man die Ergebnisse per Interrupt abholt
man jeden Taktzyklus braucht

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Wie sieht deine Eingangsbeschaltung aus? Was hängt am Pin PC0?

von krusti (Gast)


Lesenswert?

Danke schon mal vorab für die Antworten.

Der 500ms delay ist nur zum Test drin um zu sehen, ob die Werte trotz 
flackern einigermassen stimmen.

An PC0 hängt derzeit meine Spannung, welche ich auf dem ADC geben will. 
Ein Labornetzteil. Später soll dort ein Drucksensor mit 0,5 bis 4,5V 
seinen Dienst tun.

Ich habe jetzt den ADC-Modus verändert. Die Init wie im Tutorial 
angepasst:
1
ADCSRA |= (1<<ADEN) | (1<<ADSC) | (1<<ADPS2) | (1<<ADPS0);
2
  
3
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
4
    also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
5
  ADCSRA |= (1<<ADSC);        //eine ADC-Wandlung
6
  while (ADCSRA & (1<<ADSC) )          // auf Abschluss der Konvertierung warten
7
  {}  
8
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
9
    Wandlung nicht übernommen. */
10
  (void) ADCW;

Und in der Main:
1
ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
2
    while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung warten
3
    }
4
    adcvalue = ADCW;
Das ADC auslesen werde ich später noch in eine eigene Funktion packen 
denke ich. Mal sehen was noch alles dazu kommt. Denke es kommt noch eine 
Maximalwertanzeige dazu, aber das ist ein anderes Thema

Woran es jetzt noch scheitert ist die Mittelwertbestimmung. Ich werde 
jetzt nochmal die Lösung aus dem Tutorial versuchen. Jetzt wo der ADC in 
Single zu laufen scheint.

von Karl H. (kbuchegg)


Lesenswert?

krusti schrieb:

> Ich werde
> jetzt nochmal die Lösung aus dem Tutorial versuchen.

Das einzige, was du an den Tutorialsroutinen anpassen musst ist die 
Einstellung der Referenzspannung auf deine Bedürfnisse und mglw. die 
Taktteiler des ADC an deine Prozessor-Taktfrequenz

Alles andere brauchst du nicht verändern und lässt es so wie es ist.

von Karl H. (kbuchegg)


Lesenswert?

Und ehe du da gross mit einer Umrechnung auf Volt künstelst (die 
ihrerseits fehlerhaft sein kann), lässt du dir den ADC Wert direkt auf 
deiner Anzeige ausgeben.
Da du nur eine 3 stellige Anzeige hast, lässt du dir eben die Hälfte des 
ADC Wertes ausgeben. Der ADC Wert kann nicht größer als 1023 sein, die 
Hälfte davon sind 511, dann passt das auf deine Anzeige.

Und für die Ausgabe machst du dir gleich mal eine Funktion, die den 
Kleinkram erledigt. Das willst du alles nicht in der Hauptschleife haben
1
int main()
2
{
3
....
4
5
    while(1)                        //Endlosschleife
6
    {
7
      adcvalue = ADC_Read_Avg(0, 5);
8
      outValue( adcValue / 2 );
9
10
      _delay_ms( 500 );
11
    }
12
}
13
14
15
void outValue( uint16_t wert )
16
{
17
  uint8_t hunderter, zehner, einer;
18
19
  if( value > 999 )   // sicherheitshalber
20
    value = 999;
21
22
  hunderter = wert / 100;
23
  zehner = wert / 10 % 10;
24
  einer = wert % 10;
25
26
  digit[0] = numbers[einer];
27
  digit[1] = numbers[zehner];
28
  digit[2] = numbers[hunderter];
29
}

das befreit dich davon, dauernd beim experimentieren darüber nachdenken 
zu müssen, wie die Aufbereitung für deine Anzeige funktioniert.
Du willst nach jedem ADC Wert 777 ausgeben, damit du erkennst wann die 
nächste Messung stattfindet? Kein Problem
1
    while(1)                        //Endlosschleife
2
    {
3
      adcvalue = ADC_Read_Avg(0, 5);
4
      outValue( adcValue / 2 );
5
6
      _delay_ms( 500 );
7
      outValue( 777 );
8
      _delay_ms( 100 );
9
    }
macht das.

Du willst deine 7_Segment Anzeige testen, weil dir das komisch vorkommt, 
dass da nur noch lauter 8 angezeigt werden.
Kein Problem:
1
    uint16_t i = 0;
2
3
    while(1)                        //Endlosschleife
4
    {
5
      outValue( i );
6
7
      i++;
8
      if( i == 200 )
9
        i = 0;
10
11
      _delay_ms( 500 );
12
    }
macht das.

Du musst dir selbst Hilfsfunktionen für Dinge schreiben, die förmlich 
danach schreien, dass du sie universell einsetzen kannst. Eine 
Zahlenausgabe ist genau so etwas. Braucht man immer. Daher macht man 
sich da ein für allemal eine Funktion dafür.

: Bearbeitet durch User
von krusti (Gast)


Angehängte Dateien:

Lesenswert?

Diese Hilfsfunktion ist eine tolle Sache. Aber lass ich die auch final 
drin? Ich muss ja später den ADC-Wert in eine Spannung wandeln bzw dann 
in einen Druck. Da bräuchte ich sie ja nicht mehr.

Ich habe noch nicht den Blick dafür zu sehen, wie ich etwas in 
Funktionen zusammenfassen kann und vor allem wie ich es universell 
gestallte.

Solche Universalfunktionen, wie mache ich das wenn ich diese in mehreren 
Projekten nutzen will? Speicher ich mir die irgendwo ab? Mach ich mich 
ein C-File auf welches ich dann zugreife?

Ich habe mein Programm nochmal angehängt, Stand jetzt ohne diese 
Hilfsvariablen aber mit einer Max-Wert Ausgabe und einem Reset (wo ich 
glaube das dieser entweder prellt oder ich ihn schaltungstechnisch 
falsch angeschlossen habe)

von Karl H. (kbuchegg)


Lesenswert?

krusti schrieb:
> Diese Hilfsfunktion ist eine tolle Sache. Aber lass ich die auch final
> drin?

Natürlich

> Ich muss ja später den ADC-Wert in eine Spannung wandeln

Und?
Dann wandelst du das eben und bereitest es so auf, dass du sie als 
Zahlenwert von 0 bis 999 an outValue übergeben kannst.

> bzw dann
> in einen Druck.

deto.

> Da bräuchte ich sie ja nicht mehr.

Oh, die wirst du immer wieder brauchen.

> Solche Universalfunktionen

Hier zb
1
//////////////////////////////////////////////////////////////////////////
2
void Berechnung_Max(uint16_t max_adcvalue)
3
{    
4
  uint16_t druck;                  //Variable für den anstehenden Druck
5
  uint8_t hunderter, zehner, einer;        //Variabel für den Stellen auf den 7-Segemnt
6
  
7
  druck = max_adcvalue * (V_REF / MAXVALUE);    //Berechnung des Druckes mit Hilfe der Respannung
8
                          //und der maximalen Anzahl an Tastwerten des ADC
9
  hunderter = druck / 10000;            //Berechnung der Hunderterstelle aud dem Display
10
  zehner = druck / 1000 % 10;            //Berechnung der Zehnerstelle auf dem Display
11
  einer = druck /100 % 10;            //Berechnung der Einerstelle auf dem Display
12
        
13
  digit [0] = numbers [einer];          //Übergabe der hinterlegten Zahl an das Digit
14
  digit [1] = numbers [zehner];          //Übergabe der hinterlegten Zahl an das Digit
15
  digit [2] = numbers [hunderter];        //Übergabe der hinterlegten Zahl an das Digit
16
}
vermischt du 2 Konzepte.
Das eine ist die Berechnung des maximalen Wertes. Das andere ist die 
Ausgabe. Das sind 2 verschiedene Dinge, die erst mal nichts miteinander 
zu tun haben.

, wie mache ich das wenn ich diese in mehreren
> Projekten nutzen will? Speicher ich mir die irgendwo ab? Mach ich mich
> ein C-File auf welches ich dann zugreife?

Im einfachsten Fall: ja
Die sammelt man zb in einem Verzeichnis, in dem sich im Laufe der Zeit 
viele derartige Funktionalitäten ansammeln. Man will ja nicht das Rad 
immer wieder neu erfinden.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

krusti schrieb:

> (wo ich
> glaube das dieser entweder prellt oder ich ihn schaltungstechnisch
> falsch angeschlossen habe)

Wie hast du sie angeschlossen?

Laut COde
1
    if ((PINB &TASTE_1))

ist das dann ein Taster, der beim Drücken gegen Vcc schliest.
Dann allerdings brauchst du einen externen Pulldown-Widerstand.

Drum schliesst man Taster normalerweise anders herum an, weil der AVR 
eingebaute Pullup Widerstände hat.
D.h der Taster stellt beim Drücken eine Verbindung nach Masse her.
Programmtechnisch ist das kein Problem. Dreht sich halt die ABfrage um.
1
    if (!(PINB &TASTE_1))

Aber wenn du in deiner Verschaltung keinen Pulldown Widerstand hast, 
dann hängt der Pin bei nicht gedrücktem Taster in der Luft. Ein in der 
Luft hängender EIngang hat aber mitnichten zuverlässig den Wert 0. Der 
kann je nach Tageszeit (und elektrischen Feldern rundum) wahlweise 0 
oder 1 sein. Je nachdem ob der Kühlschrank läuft oder nebenan der 
Staubsauger in Betrieb ist oder du auch nur mit deinen Händen zu nahe am 
Pin bist.

Lass dieses krampfhafte Festhalten an "ein gedrückter Taster muss 1 
liefern" bleiben.
Erstens ist das nur eine Konvention, die dir genau gesehen nicht viel 
bringt. Denn im Programm ist das umdrehen pipifax.
Zum anderen brauchst du dann ein externes Bauteil mehr, das du nicht 
benötigst, wenn du einfach von dieser Konvention ablässt.
Portpin auf Eingang schalten, Pullup Widerstand zuschalten und den 
Taster so verschalten, dass er beim Drücken eine Verbindung nach GND 
herstellt.

: Bearbeitet durch User
von krusti (Gast)


Lesenswert?

Danke für die vielen Tipps und die Hilfe. Da merkt man die 4 Jahre doch 
seit ich das letzte mal einen AVR in der Hand hatte. Das war noch zu 
Ausbildungszeiten, da war ich froh wenn es lief, da habe ich noch nicht 
so viel mit #define gearbeitet. Das es den Code aber doch verständlicher 
macht, will ich doch versuchen mir das anzueignen.

> vermischt du 2 Konzepte.
> Das eine ist die Berechnung des maximalen Wertes. Das andere ist die
> Ausgabe. Das sind 2 verschiedene Dinge, die erst mal nichts miteinander
> zu tun haben.

D.h. du würdest auch diesen Teil von mir trennen und in zwei Funktionen 
packen? Max-Ermittlung und die reine Ausgabe der ermittelten Werte für 
jedes Digit?

> Im einfachsten Fall: ja
> Die sammelt man zb in einem Verzeichnis, in dem sich im Laufe der Zeit
> viele derartige Funktionalitäten ansammeln. Man will ja nicht das Rad
> immer wieder neu erfinden.

Das heißt ich speicher mir jede Universalfunktion als c-File ab und 
binde die Datei in Studio mit ein. Oder mach ich copy-paste? Ich weiß 
nicht wie ich mehrere c-Files in einem Projekt miteinander verknüpfen 
könnte, falls es geht. Habe mich dazu aber auch noch nicht schlau 
gemacht.

von krusti (Gast)


Lesenswert?

Karl H. schrieb:

> Drum schliesst man Taster normalerweise anders herum an, weil der AVR
> eingebaute Pullup Widerstände hat.
> D.h der Taster stellt beim Drücken eine Verbindung nach Masse her.
> Programmtechnisch ist das kein Problem. Dreht sich halt die ABfrage um.

> Lass dieses krampfhafte Festhalten an "ein gedrückter Taster muss 1
> liefern" bleiben.
> Erstens ist das nur eine Konvention, die dir genau gesehen nicht viel
> bringt. Denn im Programm ist das umdrehen pipifax.

Ja was soll ich sagen, es ist schon etwas her bei mir, auch das letzte 
Platinendesign. Ich hab es total verpeilt und einen Widerstand in Reihe 
zum Schalter eingebaut. Ich denke ich wollte den Strom zum µC begrenzen. 
Keine Ahnung was mich da geritten hat. Auch warum ich nicht auf die Idee 
mit den Pull-Ups gekommen bin, verpeilt. Eigentlich dachte ich, ich weiß 
diese Basics. Nun gut, das nächste Mal denk ich bestimmt dran.
Ich bin dabei die Platine so zu bearbeiten, dass ich aus den 
Widerständen wenigstens einen Pull-Down mache. Das werde ich morgen 
testen.

von Karl H. (kbuchegg)


Lesenswert?

krusti schrieb:
> Danke für die vielen Tipps und die Hilfe. Da merkt man die 4 Jahre doch
> seit ich das letzte mal einen AVR in der Hand hatte. Das war noch zu
> Ausbildungszeiten, da war ich froh wenn es lief, da habe ich noch nicht
> so viel mit #define gearbeitet. Das es den Code aber doch verständlicher
> macht, will ich doch versuchen mir das anzueignen.
>
>> vermischt du 2 Konzepte.
>> Das eine ist die Berechnung des maximalen Wertes. Das andere ist die
>> Ausgabe. Das sind 2 verschiedene Dinge, die erst mal nichts miteinander
>> zu tun haben.
>
> D.h. du würdest auch diesen Teil von mir trennen und in zwei Funktionen
> packen? Max-Ermittlung und die reine Ausgabe der ermittelten Werte für
> jedes Digit?

Die reine Ausgabe hast du ja schon.
Ich setz mich doch nicht hin und mach den ganzen Kram mit den digits, 
wenn ich genausogut auch
1
void Berechnung_Max(uint16_t max_adcvalue)
2
{    
3
  uint16_t druck;                  //Variable für den anstehenden Druck
4
  
5
  druck = max_adcvalue * (V_REF / MAXVALUE);    //Berechnung des Druckes mit Hilfe der Respannung
6
  outValue( druck / 100 );
7
}
schreiben kann. Und genau genommen braucht auch die Variable keiner.
1
void Berechnung_Max(uint16_t max_adcvalue)
2
{    
3
  outValue( max_adcvalue * (V_REF / MAXVALUE) / 100 );
4
}
... aber lass sie ruhig drinn. Dir als Programmierer mag das helfen um 
zu erkennen was da passiert und der Compiler optimiert sie sowieso raus.

Bitte, bitte, bitte. Vergiss diesen Müll mit "ein Funktionsaufruf kostet 
aber Zeit".
Ja, er kostet Zeit. Der Prozessor hat da ein bischen was zu tun.
Und? Wen kümmerts? WIe schnell kannst du eine Anzeige ablesen? Wie viele 
Anzeige-Updates in der Minute kannst du verarbeiten. 3? 4? 5? Wenn du 
bei mehr als 5 sich verändernden WErten pro Sekunde noch mitkommst, zieh 
ich meinen Hut vor dir. Kein Mensch kann das. Ok, Juck Norris 
vielleicht.

D.h. in deiner Anwendung ist Zeit überhaupt kein Faktor. Dein µC macht 
bei 6Mhz über den Daumen ca vier einhalb MILLIONEN Befehle. Was denkst 
du was es den kümmert, wenn er für eine Runde durch die Hauptschleife 10 
Befehle mehr abarbeiten muss? Völlig uninteressant. Als Mensch wirst du 
das nie merken, dass er anstatt 5212 Updates pro Sekunde nur 5211 
zustande bringt. Das ist alles weit jenseits der 4 Updates, die dein 
Gehirn gerade noch so mit Ach und Krach registrieren kann.

> Das heißt ich speicher mir jede Universalfunktion

na ja. Nicht unbedingt jede. Man wird sie natürlich sinnvoll nach 
Themenkreisen sortiert in jeweilige C-Files stecken. Und sich natürlich 
ein Header File dazu bauen.

> Oder mach ich copy-paste? Ich weiß
> nicht wie ich mehrere c-Files in einem Projekt miteinander verknüpfen
> könnte, falls es geht. Habe mich dazu aber auch noch nicht schlau
> gemacht.

Du teilst dem Studio einfach mit, das dieses C-File auch mit zum Projekt 
gehören. Fertig. Im verwendenden Code noch das zugehörige Header File 
inkludieren und ab  geht die Luzi.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Wenn ich noch darf.

SChau. Das hier
1
  uint16_t druck;                  //Variable für den anstehenden Druck
ist ein völlig sinnloser Kommentar.
Wenn jemand nicht sieht, dass es sich hier um eine Variablendefinition 
handelt, dann soll er sich einen Blindenhund kaufen.
Die Variable hast du sinnigerweise 'druck' genannt. Was also wird wohl 
die Aufgabe dieser Variablen sein. Du wirst da sicherlich nicht die 
Anzahl der Pflaumenknödel von letzter Woche drinn speichern.

Was also erzählt mir der Kommentar, was ich nicht sowieso schon im Code 
sehe?
Nichts. Gar nichts.

Ein derartiger Kommentar ist sinnlos. Im besten Fall trägt er nichts zum 
Verständnis des Programmes bei. Im schlimmsten Fall ist er nur 
hinderlich, weil er falsch ist.

Kommentiere nicht Dinge, die ich sowieso im Code sehen kann. Ein
1
   i = sqrt( x * x + y * y );   // i berechnen
erzählt mir nichts. Dass hier i berechnet wird, das sehe ich im Code 
auch. Aber WAS wird da berechnet. WARUM wird es berechnet. Das sind 
Dinge, die ich um Code unter Umständen nicht sehen kann. OK, einen 
Pythagoras sollte man aus 5 Meter Entfernung erkennen können, damit kann 
man über das WAS streiten. Aber nicht über das WARUM.

In diesem Sinne
1
  druck = max_adcvalue * (V_REF / MAXVALUE);    //Berechnung des Druckes mit Hilfe der Respannung

Ach. Das hätte ich jetzt nicht gedacht, dass hier ein Druck berechnet 
wird. :-) Darum wird ja auch das Ergebnis der Berechnung an eine 
Variable namens 'druck' zugewiesen, weil hier die Anzahl der geparkten 
Autos bestimmt wird.
'Restspannung' (wohl ein Tippfehler). Welche Restspannung? Wo kommt da 
jetzt auf einemal eine Restspannung her? Im Code steht max_adcvalue. Da 
steckt ein 'max' wie Maximum drinnen. 'Rest' ist aber nicht dasselbe wie 
'Maximum'.

Siehst du wie verwirrend es sein kann, wenn der Kommentar so überhaupt 
nicht zur Anweisung passt, neben der er steht?
Und das ist noch ein harmloses Beispiel.

Ein Kommentar soll mir auf jeden Fall das WARUM erzählen. WIE das 
dann gemacht wird, das steht im Code direkt.

: Bearbeitet durch User
von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Karl H. schrieb:
> Welche Restspannung?

Ich vermute, im Kommentar sollte es RefSpannung heissen, und nicht 
Restspannung. Gemeint ist wohl VREF des ADC.

Noch ein bisschen was zur Mittelung. Es gibt viele Ansätze, einer der 
einfachsten ist einfach das simple Aufaddieren und Teilen durch die 
Anzahl der Messwerte.
1
uint16_t ADCValue;
2
ADCValue = (ADCValue + ADCW) / 2;
Hier wird der vorherige Wert aufaddiert und das Ergebnis durch 2 
geteilt, der Wert ist der Mittelwert aus der aktuellen und der 
vorherigen Messung. Hilft schon ein wenig.

: Bearbeitet durch User
von krusti (Gast)


Lesenswert?

Karl H. schrieb:
> krusti schrieb:
>> Das heißt ich speicher mir jede Universalfunktion
>
> na ja. Nicht unbedingt jede. Man wird sie natürlich sinnvoll nach
> Themenkreisen sortiert in jeweilige C-Files stecken. Und sich natürlich
> ein Header File dazu bauen.


Ok, das mit dem "auslagern" der Funktionen werde ich probieren, wenn das 
alles soweit fertig ist. Ich lass das alles jetzt erstmal noch in einer 
Datei drin. Und eine thematische Zuordnung muss ich mir dann auch noch 
einfallen lassen.

>Siehst du wie verwirrend es sein kann, wenn der Kommentar so überhaupt
>nicht zur Anweisung passt, neben der er steht?

Das mit den Kommentaren ist nicht so einfach, was da sinnvoll ist und 
was nicht. Aber ich werde versuchen die Frage nach dem warum mehr zu 
berücksichtigen.

Ich finde es echt cool, was ich jetzt doch neues gelernt habe in den 
letzten 24 Std. Das kann ich gar nicht so schnell umsetzen und 
verinnerlichen. Das werde ich mir in aller Ruhe nochmal anschauen 
müssen, gegebenfalls mehrmals, aber ich finds gut.

von krusti (Gast)


Lesenswert?

Matthias S. schrieb:
> Karl H. schrieb:
>> Welche Restspannung?
>
> Ich vermute, im Kommentar sollte es RefSpannung heissen, und nicht
> Restspannung. Gemeint ist wohl VREF des ADC.

Richtig, ein kleiner Tippfehler. Aber den Kommentar habe ich jetzt 
entfernt.

> Noch ein bisschen was zur Mittelung. Es gibt viele Ansätze, einer der
> einfachsten ist einfach das simple Aufaddieren und Teilen durch die
> Anzahl der Messwerte.uint16_t ADCValue;
> ADCValue = (ADCValue + ADCW) / 2;
> Hier wird der vorherige Wert aufaddiert und das Ergebnis durch 2
> geteilt, der Wert ist der Mittelwert aus der aktuellen und der
> vorherigen Messung. Hilft schon ein wenig.

Ich wollte es ja eigentlich so einfach mit einer for-Schleife 
realisieren. Aber das funktionierte nicht. Inzwischen weiß ich, dass das 
Problem am ADC lag und nicht an der for-Schleife. Trotzdem nutze ich 
weiter die ADC-Read-Funktion. Der Grund ist, dass ich dort gut noch mit 
der Anzahl der zu mittelnden Werte spielen kann.

von krusti (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich packe einfach nochmal diesen Thread wieder aus. Es ist zwar nicht 
mehr das gleiche Thema, aber noch das selbe Projekt.

Ich habe inzwischen die Anzige und den ADC soweit, dass ich die Spannung 
vom Labornetzzteil 1:1 auf den Anzeigen ausgeben kann. Nun will ich den 
Drucksenor einbinden, erstmal weiterhin in Form des Labornetzteils.

Problem ist, in meiner Berechnung geht irgendetwas schief oder ich habe 
einen Denkfehler drin. Es scheint als würden keine 3 Ziffern an meine 
Ausgabefunktion übergeben werden. Mit einem Taschenrechner bekomme ich 
eine 3stellige Zahl. Wenn ich in outValue eine Zahl eingebe (z.B. 123) 
wird diese sauber angezeigt, scheint also an der Berechnung zu liegen. 
Bei meinem berechneten Wert ist die hunderter Stelle total falsch. Das 
ist keine Zahl, sondern einfach wilde Zeichen. Die beiden letzten 
Stellen passen, zumindest optisch.

Ich habe auch mal auskommentiert drin, wie ich die Spannung auf die 
Anzeigen gebracht habe. Dort musste ich den Wert / 100 machen, weil 
sonst ähnliches passierte. Das konnte ich aber mit einem Taschenrechner 
nachvollziehen, beim jetztigen Problem wie gesagt nicht.

Ich habe schon mit den Datentypen gespielt, weil ich dachte ich habe 
einen Überlauf oder sonst etwas. Wäre dankbar für weitere Hilfe.

von Pandur S. (jetztnicht)


Lesenswert?

Ahhhhh. ADC Mitteln mit einer For Schleife....

Sowas macht man anders. Guck :

http://www.ibrtses.com/embedded/exponential.html

von Karl H. (kbuchegg)


Lesenswert?

krusti schrieb:

> Problem ist, in meiner Berechnung geht irgendetwas schief oder ich habe
> einen Denkfehler drin. Es scheint als würden keine 3 Ziffern an meine
> Ausgabefunktion übergeben werden. Mit einem Taschenrechner bekomme ich
> eine 3stellige Zahl. Wenn ich in outValue eine Zahl eingebe (z.B. 123)
> wird diese sauber angezeigt, scheint also an der Berechnung zu liegen.
> Bei meinem berechneten Wert ist die hunderter Stelle total falsch. Das
> ist keine Zahl, sondern einfach wilde Zeichen.

Ich hatte in der Funktion nicht ohne Grund am Anfang ein
1
....
2
3
   if( wert > 999 )
4
     wert = 999;
5
...

drinnen.

In deiner Berechnung kommt offensichtlich kein Wert zwischen 0 und 999 
raus sondern etwas mit mindestens einer Tausenderstelle.

von Karl H. (kbuchegg)


Lesenswert?

Oder D. schrieb:
> Ahhhhh. ADC Mitteln mit einer For Schleife....
>
> Sowas macht man anders.

Man kann es anders machen, man muss es aber nicht.
Rechenzeit spielt bei ihm keine Rolle.

von Karl H. (kbuchegg)


Lesenswert?

krusti schrieb:

> Mit einem Taschenrechner bekomme ich
> eine 3stellige Zahl.

Das reicht nicht.
In deiner Berechnung
1
  druck = (max_adcValue * V_REF * maxDruck) / ((V_REF-5000) * MAXVALUE);
darf kein einziges Zwischenergebnis den Wert 65535 übersteigen.

Also nochmal den Taschenrechner gezückt und die Berechnung noch einmal 
genau so durchspielen, wie sie auch vom AVR gerechnet werden. Jedes 
einzelne Zwischenergebnis kontrollieren.

von krusti (Gast)


Lesenswert?

Karl H. schrieb:
> krusti schrieb:
>
>> Mit einem Taschenrechner bekomme ich
>> eine 3stellige Zahl.
>
> Das reicht nicht.
> In deiner Berechnung  druck = (max_adcValue  V_REF  maxDruck) /
> ((V_REF-5000) * MAXVALUE);
> darf kein einziges Zwischenergebnis den Wert 65535 übersteigen.
>
> Also nochmal den Taschenrechner gezückt und die Berechnung noch einmal
> genau so durchspielen, wie sie auch vom AVR gerechnet werden. Jedes
> einzelne Zwischenergebnis kontrollieren.

Ja das habe ich dann auch festgestellt, als ich nach einer Pause 
nochmals ran bin. Habe dann max_adcValue und druck auf 64 bit 
umgestellt. Allerdings ist mein Speicher dann statt 20% ganze 77% 
belegt. Habe es jetzt mit float laufen. Scheint das selbe Ergebnis zu 
sein, nur eben wieder bei ca. 20% Speicherbelegung.
Wobei ich ja eigentlich extra mit Integer arbeiten wollte.

von Pandur S. (jetztnicht)


Lesenswert?

Was spricht denn gegen einen exponentiellen Average, auch Tiefpass 
genannt ? Der braucht genau eine Speicherstelle, etwas Schieben und 
Addieren. Dazu muss man nicht grad eine Library einbinden.

von Karl H. (kbuchegg)


Lesenswert?

krusti schrieb:

> Ja das habe ich dann auch festgestellt, als ich nach einer Pause
> nochmals ran bin. Habe dann max_adcValue und druck auf 64 bit
> umgestellt.

Hätte es gar nicht gebraucht.
Es hätte gereicht nur in dieser einen Berechnung zwischendurch einen 
oder zwei Zwischenschritte über long zu gehen. Zwischen 16 Bit und 64 
Bit ist noch viel Luft dazwischen.

Aber dazu muss man erst mal wissen, welche Berechnung bei 16 Bit 
überlaufen wird. Genau das wäre der Sinn der Übung gewesen, dir das 
bewusst zu machen. Einfach alle Variablen auf einen anderen Datentyp zu 
wechseln, war nicht der Sinn der Sache. Du fährst ja auch nicht mit dem 
LKW Brötchen holen.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Oder D. schrieb:
> Was spricht denn gegen einen exponentiellen Average, auch Tiefpass
> genannt ?

Das das jetzt nicht sein eigentliches Problem ist und sein Problem, dass 
er mit C-Grundlagen bezüglich Datentypen in Operationen hat, nicht löst.
Du verlagerst die Diskussion auf einen Nebenschauplatz, der momentan 
kein Problem darstellt.

: Bearbeitet durch User
von M. K. (sylaina)


Lesenswert?

Karl H. schrieb:
> Du fährst ja auch nicht mit dem
> LKW Brötchen holen.

Kommt drauf an… :D

von krusti (Gast)


Lesenswert?

Karl H. schrieb:
> Es hätte gereicht nur in dieser einen Berechnung zwischendurch einen
> oder zwei Zwischenschritte über long zu gehen. Zwischen 16 Bit und 64
> Bit ist noch viel Luft dazwischen.
>
> Aber dazu muss man erst mal wissen, welche Berechnung bei 16 Bit
> überlaufen wird. Genau das wäre der Sinn der Übung gewesen, dir das
> bewusst zu machen. Einfach alle Variablen auf einen anderen Datentyp zu
> wechseln, war nicht der Sinn der Sache. Du fährst ja auch nicht mit dem
> LKW Brötchen holen.
1
  uint16_t kraft;
2
  uint64_t temp1;
3
  uint32_t temp2;
4
  uint16_t druck;  
5
6
//  druck = (max_adcValue * V_REF * maxDruck) / ((V_REF-5000) * MAXVALUE);
7
  temp1 = (uint64_t) max_adcValue * V_REF * maxDruck;    //1024* 45855*250  = 11 738 880 000
8
  temp2 = (uint16_t) V_REF+5000;              //45855 + 5000    = 50 855
9
  temp2 = (uint32_t) temp2 * MAXVALUE;          //50855 * 1024    = 52 075 520
10
  druck = temp1 / temp2;                  //11738880000 / 52075520 = 225,..
11
  
12
//  kraft = (druck * D_KOLBEN * D_KOLBEN * M_PI)/4000;
13
  temp1 = (uint32_t) druck * D_KOLBEN * D_KOLBEN * M_PI;  //225 * 60 * 60 * Pi = 2 544 690,..
14
  kraft = temp1 / 4000;                  //2544690 / 4000  = 636,...(Nachkommastelle exisitier nicht)
15
   outValue(kraft);

Habe mich mal etwas schlau gemacht. Und habe es mal versucht. Habe mal 
die Ergebnisse mit dem maximalen ADC-Wert von 1024 berechnet und dazu 
geschrieben.

Ein Prblem ist aber weiterhin, dass der mein Programmspeicher mega 
überladen ist.
Program Memory Usage   :  5464 bytes   66,7 % Full
Data Memory Usage   :  271 bytes   26,5 % Full

Habe ich etwas falsch gemacht bei der Berechnung und den Datentypen?

von Walter T. (nicolas)


Lesenswert?

Oder D. schrieb:
> Als Mittelwert verwendet man optimalerweise einen exponentiellen
> Mittelwert

Dieser "exponnentielle Mittelwert" ist einfach ein gleitender Mittelwert 
über die N letzten Stellen. Keine Ahnung, warum der jetzt einen neuen 
Namen braucht.

von Karl H. (kbuchegg)


Lesenswert?

krusti schrieb:

>   druck = temp1 / temp2;                  //11738880000 / 52075520 =
> 225,..

Du solltest dir mal überlegen, ob du da die Berechnung nicht ein wenig 
zusammenkürzen willst. Es macht keinen Sinn, wenn du einen Bruch 
anschreibst, der theoretisch auf 5 Nachkommastellen genau ist, wenn du 
dann sowieso alle Nachkommastellen wegwirfst. 117000 / 520 ist immer 
noch genau genug. Und von da an kann man das Rad nach hinten aufzäumen 
und sich die notwendigen Konstanten für die Schritte davor bestimmen.


> Ein Prblem ist aber weiterhin, dass der mein Programmspeicher mega
> überladen ist.

67% ist nicht überladen. Das sind die 64 Bit Routinen, die 
zugegebenermassen nicht gerade die speichersparendsten sind.
-> 64 Bit willst du nicht verwenden wenn es nicht sein muss. Bei dir 
muss es nicht sein. Man kann die Berechnung auch so umformen und kürzen, 
dass zwischendurch keine so großen Zahlen entstehen und trotzdem hinten 
das richtige rauskommt.

von M. K. (sylaina)


Lesenswert?

Karl H. schrieb:
> Und von da an kann man das Rad nach hinten aufzäumen

Du zäumst ein Rad von hinten auf? Kenne den Spass ja nur mit dem Pferd, 
das Rad erfinden wir hier nur immer wieder neu ;) :D

von Purzel H. (hacky)


Lesenswert?

>Dieser "exponnentielle Mittelwert" ist einfach ein gleitender Mittelwert
über die N letzten Stellen. Keine Ahnung, warum der jetzt einen neuen
Namen braucht.


Nein. Ein gleitender Mittelwert, mittelt ueber die jeweils N letzten 
Werte. Dazu verwendet man einen Ringbuffer und geht pro neuem Wert 
einmal mit MAC (Multiply & Accumulate) drueber. Wenn der Controller 
diese Funktionalitaet nicht bietet, zB kein DSP ist, wuerd ich sowieso 
nicht in Betracht ziehen.


Der Name exponentieller Mittelwert kommt daher, dass die Gewichtung des 
jetzigen Wertes im Mittelwert mit der Zeit exponentiell abnimmt. Man 
kann zeigen, dass er identisch einem Tiefpass ist. Sehr schnell und 
effizient zu rechnen. Machen die Oszilloskope sogar in Echtzeit.

von krusti (Gast)


Lesenswert?

Ich habe endlich wieder ewtwas Zeit für meine Sache und habe mal 
versucht die Berechnung umzusetzen. Meine Speicherbelegung konnte ich 
wieder aug knapp 17% damit drücken. Es scheint im groben auch alles zu 
passen. Aber eben nur im Groben.
1
  uint16_t kraft;
2
  uint32_t temp1;
3
  uint32_t temp2;
4
  uint16_t druck;  
5
  
6
temp1 = (uint32_t) max_adcValue * V_REF * maxDruck;   //1024 * 458 * 250  = 117 248 000 (uint32_t)
7
  temp2 = (uint32_t) (450 - OFFSET) * MAXVALUE;      //+ oder -? (450-50)*1024 = 409 600
8
                              //      (450+50)*1024 = 512 000
9
  
10
  druck =(temp1 / temp2) - OFFSET;            //117 248 000 / 409 600  - 50= 236,..
11
                              //117 248 000 / 512 000 - 50 = 179
12
  
13
   outValue(druck);

Ich will ja, wie schon einmal erwähnt einen Drucksensor 0,5V - 4,5V 
verwenden. Somit habe ich die Formeln erarbeitet und umgestellt.

daraus habe ich dann eine große Formel gemacht:

Ich bin mir nicht sicher ob ich im Nenner + oder - machen muss. Bin mir 
bei Formelumstellen immer etwas unsicher. Denke aber es muss - sein.
Das Problem ist, dass meine 000 auf dem Display erst bei ca. 0,74V statt 
0,5V beginnt.

von Karl H. (kbuchegg)


Lesenswert?

krusti schrieb:

> Das Problem ist, dass meine 000 auf dem Display erst bei ca. 0,74V statt
> 0,5V beginnt.

Dann wird deine Gesamt-Formel wohl nicht stimmen.

Wenn das ein Polynom 4-ten Grades wäre, dann könnte ich das ja noch 
verstehen. Aber einfache UMformungen sollte man eigentlich schon können, 
wenn man Messsysteme programmieren will. Natürlich kann es schon mal 
sein, dass sich wo ein Vorzeichenfehler einschleicht, aber so Dinge wie 
Klammern ausmultiplizieren, Dinge aus einer Klammer herausheben und 
Terme von der rechten auf die linke Seite bringen (bzw. umgekehrt) 
dürfen keine Probleme machen.

Hinweis: dem Computer ist es erst mal egal, ob du gleich mit einer 
riesigen Formel kommst, oder ob du die Einzelteile einzeln errechnst und 
die Dinge nach und nach zusammensetzt.
Frei nach dem Muster: zusammenfassen kann man immer noch.
Aber wenn man die Einzelteile einzeln errechnet, dann kann man sich die 
auch ausgeben lassen und kontrollieren, ob die Ergebnisse plausibel 
sind. Soll heissen: Fängst du mit Einzelformeln an und kriegst du das 
richtige Ergebnis, dann kannst du mit deinem Programm auch deine ganze 
am Papier stattfindende Formelumformung bzw. Einsetzterei kontrollieren. 
Denn egal wie du eine Formel umformst, das Endergebnis muss ja dasselbe 
sein. :-)

: Bearbeitet durch User
von krusti (Gast)


Lesenswert?

Ich mache eine Beobachtung, wo ich nicht weiß ob ich da richtig liege.
Ich lasse den ADC-Wert auf dem Display anzeigen. Die Anzeige dort ist 
z.B. 50 Bit was einer Spannung von 0,22V entspricht. Der Multimeter 
zeigt aber 0,16V an, was ungefähr 37 oder 38 Bit enstprechen würde.

Ich habe meine Formel nochmal überprüft und denke, dass diese korrekt 
sein sollte. Wenn man dann die Bits in die Formel eintragen würde, wäre 
es ein Unterschied von 3 bar. Ist auf den gesamten Bereich nur 1% aber 
ich hätte es natürlich schon gerne genau. Also irgendwie kommt das 
Signal falsch rein. Meine Refspannung stimmt aber.

von Hubert G. (hubertg)


Lesenswert?

Wie sieht deine Referenzspannung aus?

von M. K. (sylaina)


Lesenswert?

krusti schrieb:
> Ich lasse den ADC-Wert auf dem Display anzeigen. Die Anzeige dort ist
> z.B. 50 Bit was einer Spannung von 0,22V entspricht. Der Multimeter
> zeigt aber 0,16V an, was ungefähr 37 oder 38 Bit enstprechen würde.

50 bit? Ja ne, is klar. Also 24 bit sind ja schon sehr sportlich aber 50 
bit?

krusti schrieb:
> Ich habe meine Formel nochmal überprüft und denke, dass diese korrekt
> sein sollte.

Auf keinen Fall. Du hast nie und nimmer 50 bit. Und auch keine 37 oder 
38 bit.

von Falk B. (falk)


Lesenswert?

@ krusti (Gast)

>ich habe ein Problem mit meinem ADC und ATmega8. Ich will mit dem ADC
>eine Durchschnittsberechnung durchführen, um das Flackern auf meinen
>7-Segementen zu verringern.

Wahrscheinlich sollte man das Problem erstmal in Hardware untersuchen.
Möglicherweise ist dort was faul.

>Ich habe endlich wieder ewtwas Zeit für meine Sache und habe mal
>versucht die Berechnung umzusetzen. Meine Speicherbelegung konnte ich
>wieder aug knapp 17% damit drücken.

Klingt doch super!

>Ich will ja, wie schon einmal erwähnt einen Drucksensor 0,5V - 4,5V
>verwenden. Somit habe ich die Formeln erarbeitet und umgestellt.

Das kann ja nicht so schwer sein.

>daraus habe ich dann eine große Formel gemacht:

>gesuchter Druck = \frac{ADCwert*RefSpannung*Max Druck-Offset Sensor}
>{MaxSpannung Sensor-+Offset Sensor*maximale Bit}

Die sieht komisch und falsch aus.

>Ich bin mir nicht sicher ob ich im Nenner + oder - machen muss.

???

> Bin mir
>bei Formelumstellen immer etwas unsicher. Denke aber es muss - sein.
>Das Problem ist, dass meine 000 auf dem Display erst bei ca. 0,74V statt
>0,5V beginnt.

Dann ist deine Formel falsch.

Mach es schrittweise und nicht in einer Formel.

1.) Spannung in mV berechen, siehe Festkommaartihmetik Dafür reichen 
32 Bit locker

2.) Damit den Druck ausrechnen. Offset abziehen und passend skalieren.

von Falk B. (falk)


Lesenswert?

@ krusti (Gast)

>Ich lasse den ADC-Wert auf dem Display anzeigen. Die Anzeige dort ist
>z.B. 50 Bit was einer Spannung von 0,22V entspricht.

Nein. Die Formulierung "50 Bit" ist falsch. Du hast 50 LSB, sprich 50 
Einheiten. Denn dein Messwert hat nur 10 Bit Breite.

> Der Multimeter
>zeigt aber 0,16V an, was ungefähr 37 oder 38 Bit enstprechen würde.

>Ich habe meine Formel nochmal überprüft und denke, dass diese korrekt
>sein sollte.

Ist es aber nicht. Ausserdem sehe ich bei dir keine Kalibrierung.

> Wenn man dann die Bits in die Formel eintragen würde, wäre
>es ein Unterschied von 3 bar. Ist auf den gesamten Bereich nur 1% aber
>ich hätte es natürlich schon gerne genau. Also irgendwie kommt das
>Signal falsch rein. Meine Refspannung stimmt aber.

Wie hast du das gemessen?

von krusti (Gast)


Lesenswert?

Falk B. schrieb:
> Nein. Die Formulierung "50 Bit" ist falsch. Du hast 50 LSB, sprich 50
> Einheiten. Denn dein Messwert hat nur 10 Bit Breite.
Ja ich weiß, die Formulierung war nicht ganz glücklich. Hätte ich besser 
sagen sollen ADC-Wert 50?

>> Der Multimeter
>>zeigt aber 0,16V an, was ungefähr 37 oder 38 Bit enstprechen würde.
>
>>Ich habe meine Formel nochmal überprüft und denke, dass diese korrekt
>>sein sollte.
>
> Ist es aber nicht. Ausserdem sehe ich bei dir keine Kalibrierung.
1
void init_ADC()
2
{
3
  
4
  ADCSRA |= (1<<ADEN) | (1<<ADSC) | (1<<ADPS2) | (1<<ADPS0);
5
  
6
  ADCSRA |= (1<<ADSC);          
7
  while (ADCSRA & (1<<ADSC) )        
8
  {}  
9
  (void) ADCW;
10
}

>> Wenn man dann die Bits in die Formel eintragen würde, wäre
>>es ein Unterschied von 3 bar. Ist auf den gesamten Bereich nur 1% aber
>>ich hätte es natürlich schon gerne genau. Also irgendwie kommt das
>>Signal falsch rein. Meine Refspannung stimmt aber.
>
> Wie hast du das gemessen?
Ich habe eine Excel-Liste erstellt, welche mir angibt, welchen ADC-Wert 
welche Spannung hat.
Auf meiner Anzeige lasse ich den ADC-Wert des µC ausgeben und 
kontrolliere die Spannung mit einem Multimeter. Dadurch habe ich eben 
einen Versatz festgestellt.

von krusti (Gast)


Lesenswert?

Hubert G. schrieb:
> Wie sieht deine Referenzspannung aus?
Wie genau? Schaltungstechnisch? Programmiertechnisch oder der 
Spannungswert?

von Falk B. (falk)


Lesenswert?

@ krusti (Gast)

>Ja ich weiß, die Formulierung war nicht ganz glücklich. Hätte ich besser
>sagen sollen ADC-Wert 50?

Ja.


>> Ist es aber nicht. Ausserdem sehe ich bei dir keine Kalibrierung.

>void init_ADC()

Das ist keine Kalibrirung, sagt ja schon der Name. Es wird nur der ADC 
eingestellt, neudeutsch initialisiert.

Kalibrieren heißt, den Fehler der Referenzspannung per "Mogelfaktor" 
auszugleichen.

>Ich habe eine Excel-Liste erstellt, welche mir angibt, welchen ADC-Wert
>welche Spannung hat.
>Auf meiner Anzeige lasse ich den ADC-Wert des µC ausgeben und
>kontrolliere die Spannung mit einem Multimeter.

Das ist schon mal gut.

> Dadurch habe ich eben
>einen Versatz festgestellt.

Dann hast du einen Fehler. Denn musst du erst einmal finden und 
beseitigen, ohne mit der Druckumrechung zu hantieren. Zuerst muss die 
Umrechung in Spannung fehlerfrei sein.

von Hubert G. (hubertg)


Lesenswert?

krusti schrieb:
>> Wie sieht deine Referenzspannung aus?
> Wie genau? Schaltungstechnisch? Programmiertechnisch oder der
> Spannungswert?

Mich würde die Schaltung insgesamt interessieren, speziell die 
Referenzspannung. Der gemessene Wert wäre auch wichtig.

Bei einem ADC-Wert von 50 und einer gemessenen Spannung von 0,16V, 
sollte die zurückgerechnete Referenzspannung 3,3V sein.
Da stellt sich schon die Frage was da stimmt, oder auch nicht.

von M. K. (sylaina)


Lesenswert?

Hubert G. schrieb:
> Bei einem ADC-Wert von 50 und einer gemessenen Spannung von 0,16V,
> sollte die zurückgerechnete Referenzspannung 3,3V sein.
> Da stellt sich schon die Frage was da stimmt, oder auch nicht.

3.3V wäre jetzt aber keine soo ungewöhnliche Spannung bei einem µC ;)

von Hubert G. (hubertg)


Lesenswert?

Michael K. schrieb:
> 3.3V wäre jetzt aber keine soo ungewöhnliche Spannung bei einem µC ;)

Es beißt sich nur etwas wenn der Sensor bis 4,5V ausgibt.

von M. K. (sylaina)


Lesenswert?

Hubert G. schrieb:
> Michael K. schrieb:
>> 3.3V wäre jetzt aber keine soo ungewöhnliche Spannung bei einem µC ;)
>
> Es beißt sich nur etwas wenn der Sensor bis 4,5V ausgibt.

Yo, Spannungsteiler sind ja auch soo komplizierte Schaltungen…also ich 
benutze auch oft 3.3V bei meinen µCs und hab die Sensoren dann ggf. an 
einem Spannungsteiler dran.

von Hubert G. (hubertg)


Lesenswert?

Michael K. schrieb:
> Hubert G. schrieb:
>> Michael K. schrieb:
>>> 3.3V wäre jetzt aber keine soo ungewöhnliche Spannung bei einem µC ;)
>>
>> Es beißt sich nur etwas wenn der Sensor bis 4,5V ausgibt.
>
> Yo, Spannungsteiler sind ja auch soo komplizierte Schaltungen…also ich
> benutze auch oft 3.3V bei meinen µCs und hab die Sensoren dann ggf. an
> einem Spannungsteiler dran.

Ist ja schön und richtig, aber dann sollte man nicht mit 4,5V rechnen.

von krusti (Gast)


Angehängte Dateien:

Lesenswert?

Um die ganzen Spekulationen auszuräumen habe ich mal einen Teil der 
Schaltung hier eingestellt.

Die Versorgung sind 5V, die Referenzspannung für den ADC habe ich auf 
4,585V eingestellt. Somit nutze ich den Sensor maximal aus, so dachte 
ich es mir zumindest.

Nur ist jetzt leider irgendetwas hardwareseitg mit meiner Platine 
passiert, sodass derzeit keine korrekte Funktion mehr da ist. Eventuell 
ist der ADC-Eingang hopps gegangen. Der Controller braucht plötzlich 
mehr Strom und wird warm. Da muss ich noch dahinter steigen was da 
passiert ist, hatte aber irgendwann keine Lust mehr.

von M. K. (sylaina)


Lesenswert?

krusti schrieb:
> Die Versorgung sind 5V, die Referenzspannung für den ADC habe ich auf
> 4,585V eingestellt. Somit nutze ich den Sensor maximal aus, so dachte
> ich es mir zumindest.

Hm, also so wie du das eingestellt hast, nur mit Widerständen, da 
könntest du den Kontroller auch gleich auf Vcc einstellen, dann bist du 
genauso genau in der Messung. Zumindest die Spule hättest du AVcc gönnen 
können.

von Hubert G. (hubertg)


Lesenswert?

AREF und AVCC wurden schon erwähnt.
Die Versorgungsspannung des Sensor sollte von der 5V Versorgung des 
Kontroller entkoppelt sein. Wenn der Ausgangswiderstand des Sensor mehr 
als 5k ist, sollte man einen 1n Kondensator vom ADC-Eingang nach GND 
schalten.
Es sollte weiters sichergestellt sein, das während der ADC-Messung keine 
weitere Aktivität auf den anderen Pin des PortC ist (Siebensegment).
Alles so Kleinigkeiten die aber die Genauigkeit der Messung wesentlich 
beeinflussen können.

von krusti (Gast)


Lesenswert?

Michael K. schrieb:
> Hm, also so wie du das eingestellt hast, nur mit Widerständen, da
> könntest du den Kontroller auch gleich auf Vcc einstellen, dann bist du
> genauso genau in der Messung.
Verstehe jetzt ehrlich gesagt nicht, was du mir damit sagen willst.
Ich weiß dass es nicht die eleganteste Lösung ist. Wie gesagt,ich wollte 
knapp über 4,5V kommen um den Bereich voll mitzunehmen. Soll ich das 
Umbauen?
Wenn ich die 5V nehm, dann verliere ich aber insgesamt 1V (0 - 0,5V und 
4,5V - 5V) an meine ADC den ich ja nicht nutzen kann.

>Zumindest die Spule hättest du AVcc gönnen
> können.
Ja das habe ich nicht beachtet. Versuche mir einen zu organisieren und 
einzubauen.
Bei einer Schlatung, welche schon Jahre her ist, ging es auch ohne (wohl 
damals auch schon nicht beachtet.)

von M. K. (sylaina)


Lesenswert?

krusti schrieb:
> Verstehe jetzt ehrlich gesagt nicht, was du mir damit sagen willst.
> Ich weiß dass es nicht die eleganteste Lösung ist. Wie gesagt,ich wollte
> knapp über 4,5V kommen um den Bereich voll mitzunehmen. Soll ich das
> Umbauen?
> Wenn ich die 5V nehm, dann verliere ich aber insgesamt 1V (0 - 0,5V und
> 4,5V - 5V) an meine ADC den ich ja nicht nutzen kann.

Du solltest zumindest noch eine Induktivität dazu packen gemäß 
Datenblattangabe damit dein Vcc auch stabiler wird. Eine 
Referenzspannung, die im Bereich von 10 K (z.B. zwischen 10°C und 20°C) 
noch auf 10 bit genau sein soll braucht mindestens eine Stabilität bzgl. 
Temperatur von 50 ppm. Versorgst du Vcc z.B. durch einen LM317 bist du 
vielleicht bei 100 ppm, das reicht nicht für 10 bit.
Und jetzt nur mal den Rest noch bedenken…also 5 mV ist nicht viel bei 
unterschiedlichen Belastungen kann man die schnell verlieren (LM317 hat 
meine ich 25 mV Genauigkeit, das reicht kaum noch für 8 bit). Deshalb 
solltest du dir da vielleicht noch Gedanken drum machen.

von Falk B. (falk)


Lesenswert?

@ krusti (Gast)

>Verstehe jetzt ehrlich gesagt nicht, was du mir damit sagen willst.
>Ich weiß dass es nicht die eleganteste Lösung ist. Wie gesagt,ich wollte
>knapp über 4,5V kommen um den Bereich voll mitzunehmen. Soll ich das
>Umbauen?

Du MUSST es umbauen! An AREF gehören 100nF gegen GND, sonst NIX!
Das dürfte auch der Grund für deinen zappelnden Messwert sein!

>Wenn ich die 5V nehm, dann verliere ich aber insgesamt 1V (0 - 0,5V und
>4,5V - 5V) an meine ADC den ich ja nicht nutzen kann.

Das ist weniger als 1 Bit Auflösung Verlust, das kann man 
vernachlässigen.

>>Zumindest die Spule hättest du AVcc gönnen
>> können.
>Ja das habe ich nicht beachtet. Versuche mir einen zu organisieren und
>einzubauen.

Es reichen ggf. auch 10-47 Ohm.

: Bearbeitet durch User
von M. K. (sylaina)


Lesenswert?

Falk B. schrieb:
> Du MUSST es umbauen! An AREF gehören 100nF gegen GND, sonst NIX!

Oder halt eine vernünftige Referenzspannungsquelle. ;)

Falk B. schrieb:
> Das ist weniger als 1 Bit Auflösung Verlust, das kann man
> vernachlässigen.

Deshalb auch meine Anmerkungen zur Stabilität der aktuellen 
Referenzspannungsquelle. Die wird wahrscheinlich nicht mal 8 bit 
genügen, geschweige denn 10 bit.

Falk B. schrieb:
> Es reichen ggf. auch 10-47 Ohm.

Das geht zur Not wahrscheinlich auch. Ich hab 10 uH Spulen die gleich 
groß wie Widerstände sind und preislich hat sich das auch nichts getan, 
daher setze ich immer die ein. ;)

: Bearbeitet durch User
von Axel R. (Gast)


Lesenswert?

mach doch die Referenzspannung 4.096 Volt (4096 mV). Das durch 10bit 
geteilt ergibt Vier(millivolt). Eine ADC-Einheit (Digit) sind dann exakt 
4mV.
Jetzt brauchst Du deinen ADC-Wert nur noch mal Vier nehmen und hast 
schonmal die Spannung in millivolt aus deinem Sensor. So einfach :)
Wenn Du die Sensorspannung nun noch mit einem Spannungsteiler viertelst 
(33K + 11K), sind deine Anzeigewerte des ADCs die Millivolts, die der 
Drucksensor liefert und du musst nichts mehr rechnen, um von den 
Anzeigeeinheiten auf deine Sensorspannung zu schließen.

Um das mit dem Druck jetzt richtig zu rechnen, hatte ich schon zu viele 
Cuba im Glas.

Gruß
Axel

von krusti (Gast)


Lesenswert?

Vielen Dank schon mal für die Hilfe. Irgendwie merke ich gerade, dass 
mein Aufbau nicht ganz so gut war wie gedacht bzw gehofft. Bin in der 
Schaltungsentwicklung zwar in den Basics ganz gut, bei solchen sachen 
mit Sensoren fehlt mir allerdings dann doch die Erfahrung.

Es kommt mir nachher nicht auf 100% Genauigkeit an aber wenn ich mit dem 
Netzteil, als Simulation des Sensors, schon 12 Digits daneben liege, ist 
eben was falsch.

Werde mir jetzt eine Induktivität besorgen und diese zwischen AVcc und 
Vcc hauen.

>Du MUSST es umbauen! An AREF gehören 100nF gegen GND, sonst NIX!
Damit ich das richtig verstehe, das gilt nur für den Fall, wenn ich die 
Vcc als Referenz nehme.

>Wenn der Ausgangswiderstand des Sensor mehr
>als 5k ist, sollte man einen 1n Kondensator vom ADC-Eingang nach GND
>schalten.
Der Widerstand beträgt laut Datenblatt max. 5k

>Es sollte weiters sichergestellt sein, das während der ADC-Messung keine
>weitere Aktivität auf den anderen Pin des PortC ist (Siebensegment).
Das werde ich ebenfalls versuchen umzusetzen.

>Deshalb auch meine Anmerkungen zur Stabilität der aktuellen
>Referenzspannungsquelle. Die wird wahrscheinlich nicht mal 8 bit
>genügen, geschweige denn 10 bit.
Sorry verstehe ich eventuell nicht. Soll heißen, mein aktueller Aufbau 
hat eine so schlechte Referenz bzw diese schwankt so sehr, dass damit 
keine geneue Messung möglich ist?

Es ist nicht so einfach bei mir noch einen weiteren Baustein einzubauen, 
der eine Referenzspannung erzeugen könnte.

Habe als Spannungsquelle den TSR 1-2450 von Traco genommen. Ich vermute 
mal, dass auch dies nicht die beste Wahl war?

Langsam verliere ich den Überblick und kapiere gar nichts mehr.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

krusti schrieb:
> Soll heißen, mein aktueller Aufbau
> hat eine so schlechte Referenz bzw diese schwankt so sehr, dass damit
> keine geneue Messung möglich ist?

So isses. Da du AREF direkt aus VCC generierst, welche mit den Spitzen 
des arbeitenden MC 'verseucht' ist, ist da nichts stabiles zu erwarten.

Sowohl AVCC als auch AREF sollten so entkoppelt sein, das Störungen auf 
VCC nicht bis zum ADC Teil durchkommen. Ein 10µF Elko an VCC und GND ist 
auch nicht dumm, direkt am MC.

> Habe als Spannungsquelle den TSR 1-2450 von Traco genommen. Ich vermute
> mal, dass auch dies nicht die beste Wahl war?

Das Dings ist eben ein Schaltnetzteil. Ob und wieweit Traco die Spitzen 
der Schaltvorgänge vom 5V Ausgang fernhält, verrät dir am besten ein 
Oszilloskop. Ein LC Filter jedenfalls kann auch hier helfen.

: Bearbeitet durch User
von M. K. (sylaina)


Lesenswert?

krusti schrieb:
>>Du MUSST es umbauen! An AREF gehören 100nF gegen GND, sonst NIX!
> Damit ich das richtig verstehe, das gilt nur für den Fall, wenn ich die
> Vcc als Referenz nehme.

Ja

krusti schrieb:
> Habe als Spannungsquelle den TSR 1-2450 von Traco genommen. Ich vermute
> mal, dass auch dies nicht die beste Wahl war?

Das Teil hat ne Restwelligkeit von 50 mV peak2peak. 8 bit auf 5 V 
gerechnet bedeutet, dass ein Count schon etwa 20 mV entspricht. Bei 7 
bit ist man bei knapp 40 mV und bei 6 bit bei etwa 78 mV, d.h. bzgl. der 
Restwelligkeit reicht dieser Regler grad mal für 6 bit. Die 
Lastabhängigkeit noch nicht berücksichtigt. Und die Temperaturstabilität 
beträgt bei dem Gerät auch "nur" 0,015%, also 150 ppm (genügt bei 10K 
Temperaturschwankung auch nur für 8 bit).

von Falk B. (falk)


Lesenswert?

@ krusti (Gast)

>>Du MUSST es umbauen! An AREF gehören 100nF gegen GND, sonst NIX!
>Damit ich das richtig verstehe, das gilt nur für den Fall, wenn ich die
>Vcc als Referenz nehme.

Nein, auch wenn man die interne Referenz nutzen will. Man kann auch eine 
externe Referenz an Aref anschließen, dann werden die 100nF aber auch 
gebraucht.

>Habe als Spannungsquelle den TSR 1-2450 von Traco genommen. Ich vermute
>mal, dass auch dies nicht die beste Wahl war?

Das Ding ist eine DRECKSCHLEUDER! Dort gibt es ordentlich HF-Störungen! 
Als ADC-Referenz sowie AVCC vollkommen ungeeignet. Man muss dort massiv 
filtern. Besser noch, man gewinnt die Versorgungsspannung aus einem 
Linearregler.

von M. K. (sylaina)


Lesenswert?

Falk B. schrieb:
> Das Ding ist eine DRECKSCHLEUDER! Dort gibt es ordentlich HF-Störungen!
> Als ADC-Referenz sowie AVCC vollkommen ungeeignet. Man muss dort massiv
> filtern. Besser noch, man gewinnt die Versorgungsspannung aus einem
> Linearregler.

Sehe ich auch so, und wenn man auf 10 bit will kann ein LT1021-5 
sinnvoll sein.

: Bearbeitet durch User
von krusti (Gast)


Lesenswert?

Ok, vielen Dank schon mal. Klingt alles sehr einleuchtend. Dass diese 
Faktoren mit Temperaturdrift und der Restwelligkeit so starke 
Auswirkungen haben, habe ich nicht bedacht. Wieder wichtige Grundlagen 
gelernt. Gerade wie ich so etwas berechne, war mir nicht klar. Wobei ich 
das mit dem ppm und der Temperatur im Zusammenhang mit den Bit noch 
nicht verstehe.

Dann wird es wohl das Beste sein, ich mache die Platine nochmals neu, 
mit dem LT1021-5. Reicht der dann auch um die ganze Schaltung zu 
treiben? Kann ich den quasi als Spannungsversorgung nutzen? Im 
Datenblatt steh was von gerade mal 1,2mA. Oder baue ich den quasi 
parallel zu meiner andern Spannungsversorgung ein, und Versorge den 
damit nur den ADC-Teil + Sensor mit Strom?

Trotzdem hätte ich gerne eine Referenzspannung nah an meinem Sensor von 
4,5 V. Um das ganze eben, wenn ich schon umbaue, möglichst genau zu 
machen.

von M. K. (sylaina)


Lesenswert?

krusti schrieb:
> Reicht der dann auch um die ganze Schaltung zu
> treiben?

Der LT1021-5 ist nur für Ströme bis 10 mA geeignet, braucht deine 
Schaltung mehr Strom würde ich an deiner Stelle den LT1021-5 nur für 
ARef benutzen (pass aber nicht die Spannung für ARef mittels 
Spannungsteiler auf, so würdest du alle Vorteile des LT1021-5 bzgl. 
Temperaturstabilität zunichte machen) und den Rest der Schaltung mittels 
L7805 bzw. LM317 versorgen. Auch ein Schaltregler wie der von dir schon 
gewählte von Traco ginge, bedenke aber, dass du für den noch einen 
LC-Filter vorsehen solltest wie er im Datenblatt beschrieben ist.

krusti schrieb:
> Trotzdem hätte ich gerne eine Referenzspannung nah an meinem Sensor von
> 4,5 V. Um das ganze eben, wenn ich schon umbaue, möglichst genau zu
> machen.

Dann baue dir dafür eine Referenzspannungsquelle, lies dir dafür z.B. 
das hier mal durch: http://sprut.de/electronic/referenz/index.htm

: Bearbeitet durch User
von krusti (Gast)


Angehängte Dateien:

Lesenswert?

Also habe die Scchaltung mal umgezeichnet. Ich stelle Sie mal im 
gesamten hoch. Es wäre nett, wenn man mir das jemand beurteilen könnte, 
damit ich nicht schon wieder einen Fehler einbaue. Außerdem lerne ich 
gerne was dazu.

Werde jetzt doch die 5V nehmen, weil es einfacher ist. Ich will nicht 
noch mehr Bauteile verbauen müssen.

Die Mosfets habe ich bei der Gelegenheit auch rein. Vorher waren 
Transistoren drin, es sind Logik-Mosfets.

von M. K. (sylaina)


Lesenswert?

N-Kanal Mosfets sollten gegen GND geschaltet sein, nicht gegen Vcc. 
Überlege dir einfach mal wie hoch die Gate-Spannung sein muss damit der 
Mosfet durchschalten kann ;)

Bei den Tastern sind die Widerstände unnötig, dafür könntest du da ein 
paar Kondensatoren vorsehen um einem Tastenprellen hardwaremäßig zu 
begegnen.

von Falk B. (falk)


Lesenswert?

@ krusti (Gast)

>Also habe die Scchaltung mal umgezeichnet. Ich stelle Sie mal im
>gesamten hoch. Es wäre nett, wenn man mir das jemand beurteilen könnte,
>damit ich nicht schon wieder einen Fehler einbaue. Außerdem lerne ich
>gerne was dazu.

C3 gehört direkt an das Reset-Pin!
Q1, Q2, Q3, Q4 und Q6 sind falsch! Dort braucht man P-Kanal MOSFETs und 
Source geht an +5V!

>Werde jetzt doch die 5V nehmen, weil es einfacher ist. Ich will nicht
>noch mehr Bauteile verbauen müssen.

Wenn der Spannungsregler sauber und halbwegs temperaturstabil ist, geht 
das auch.

>Die Mosfets habe ich bei der Gelegenheit auch rein. Vorher waren
>Transistoren drin, es sind Logik-Mosfets.

Was glaubst du sind MOSFETs?

von krusti (Gast)


Lesenswert?

Oh man, langsam wirds peinlich für mich. Seit ich den Sensor umsetzen 
will, gibt es nur noch Probleme und ich mache gefühlt ständig einen 
Schritt rückwärts.

Arbeite das erste mal mit MOSFETs. Habe mir jetzt schon extra MOSFETs 
organisiert, natürlich die Falschen. Dann lass ich jetzt doch erstmal 
die Transistoren drin. Die funktionierten ja (Ich weiß MOSFETs sind auch 
von der Art her Transistoren). Wollte eben eine Lösung realiseren wo ich 
meinen µC nicht noch unnötig belaste aber das ist jetzt meine kleinste 
Sorge. Ich organisiere mir jetzt noch eine Referenzspannung und 
LC-Filter für meinen Traco.

>Bei den Tastern sind die Widerstände unnötig, dafür könntest du da ein
>paar Kondensatoren vorsehen um einem Tastenprellen hardwaremäßig zu
>begegnen.
Aber hängen die dann nicht in der Luft? Ich ersetze einfach die 
Widerstände durch Kondensatoren.

Sorry, dass ich mich jetzt da so blöd anstelle. Ich bin langsam total 
verunsichert.

von M. K. (sylaina)


Lesenswert?

krusti schrieb:
> Aber hängen die dann nicht in der Luft? Ich ersetze einfach die
> Widerstände durch Kondensatoren.

Du kannst die Pull-Ups des Ports einschalten und die Taster gegen GND 
schalten lassen. Damit sparst du dir die Pull-Downs ;)

von krusti (Gast)


Lesenswert?

Michael K. schrieb:
> Sehe ich auch so, und wenn man auf 10 bit will kann ein LT1021-5
> sinnvoll sein.

Jetzt schaue ich mir gerade das Datenblatt des LT1021-5 an und sehe, 
dass LT1021B-5 und LT1021D-5 eine Schwankung von 100mVpp haben. Das wäre 
ja schlechter als beim Traco-Bauteil, sehe ich das richtig?

Müsste also ein LT1021C-5 nehmen, welcher nur 5mVpp hat. Den würde es 
aber nicht als SMD geben. Müsste ich also bei die DIL-Variante nehmen.

von Falk B. (falk)


Lesenswert?

@ krusti (Gast)

>Jetzt schaue ich mir gerade das Datenblatt des LT1021-5 an und sehe,
>dass LT1021B-5 und LT1021D-5 eine Schwankung von 100mVpp haben.

Niemals! Wo soll das stehen?

Im Gegenteil

Output Voltage Noise 10-1000Hz max. 3,5uV RMS!



> Das wäre
>ja schlechter als beim Traco-Bauteil, sehe ich das richtig?

Nein!

>Müsste also ein LT1021C-5 nehmen, welcher nur 5mVpp hat. Den würde es
>aber nicht als SMD geben. Müsste ich also bei die DIL-Variante nehmen.

Du machst das alles viel zu kompliziert für so ein bisschen 
ADC-Auslesen.

von dirk h. (Gast)


Lesenswert?


von krusti (Gast)


Lesenswert?

Falk B. schrieb:
> Niemals! Wo soll das stehen?
>
> Im Gegenteil
>
> Output Voltage Noise 10-1000Hz max. 3,5uV RMS!
>
Habe bei Output Voltage geschaut und mich davon irritieren lassen.

> Du machst das alles viel zu kompliziert für so ein bisschen
> ADC-Auslesen.
Ja, das Gefühl habe ich so langsam auch.

von M. K. (sylaina)


Lesenswert?

krusti schrieb:
> Müsste also ein LT1021C-5 nehmen, welcher nur 5mVpp hat. Den würde es
> aber nicht als SMD geben. Müsste ich also bei die DIL-Variante nehmen.

Ob DIL oder SMD ist wurscht, beide sind für deine Anwendung geeignet.

Falk B. schrieb:
> Du machst das alles viel zu kompliziert für so ein bisschen
> ADC-Auslesen

Es geht ja nicht ums auslesen sondern wie genau es sein soll ;)

von Christian M. (krusti)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich habe es nach einer Weile geschafft meine Platine umzubauen und habe 
statt Transitoren MOSFETs eingebaut und für den ADC eine 
Referenzspannungsquelle angeschlossen. Es paast zwar alles weiterhin 
nicht mit dem Labornetzteil und Multimeter überein aber meine Ergebnisse 
scheinen zu stimmen.
Programmtechnisch bin ich auch ein gutes Stuck voran gekommen. 
Hauptfunktion tut, kleine nebeneinbauten funktionieren auch.

Nun habe ich aber ein neues PRoblem, welches ich nicht derzeit nicht 
beheben kann.
Ich will bein meiner Anzeige eine Art Wartungsanzeige für die Mechanik 
einbauen. D.h. nach x  Tagen sollen die LED's blinken (1. Warnung). Nach 
weiteren x Tagen soll zusätzlich das Display blinken (möglichst 
gleichzeitig; 2. Warnung) Nach weiteren Tagen soll das Display eine 
Meldung bringen und nicht mehr zulassen, quasi Sperren. Der Zahle kann 
mit einer Tastenkombi zurückgesetzt werden.

Das Sperren und das Rücksetzen klappt einwandfrei. Mein Problem ist 
mehr, dass ich das blinken der LED's nicht schaffe. Die LED's (rot, 
grün) sollen auch Anzeigen ob der Bereich eingehalten wird.
1
void LEDmode(uint16_t xyz)
2
{
3
  switch (led_mode)
4
  {
5
  case 0:
6
    if ((LED_RANGE_MIN < xyz) && (xyz <= LED_RANGE_MAX))
7
    {
8
      PORTC &= ~LED_GREEN;
9
    }
10
    else
11
    {
12
      PORTC &= ~LED_RED;
13
    }
14
    break;
15
  case 1:
16
    if ((LED_RANGE_MIN < xyz) && (xyz <= LED_RANGE_MAX))
17
    {
18
      timer2_wert = 0;
19
      
20
      while (1){
21
        if (timer2_wert >= 200)
22
        {
23
          PORTC &= ~LED_GREEN;
24
          break;
25
        }
26
      }
27
      timer2_wert = 0;
28
      
29
      while (1){
30
        if (timer2_wert >= 200){
31
          PORTC &= ~LED_GREEN;
32
          break;
33
        }
34
      }
35
    }
36
    else
37
    {
38
      PORTC &= ~LED_RED;
39
    }
40
    break;
41
  }
42
}
Ich weiß ich habe dort eine while drin und das ist nicht so 
wünschenswert. -> Stichwort Multitasking. Mir fällt aber keine weitere 
Idee ein.
Alle Timer des ATmegas sind in Benutzung. 1 für die 7-Seg (Timer0 - 
8bit), 1 für meine Zählung der Arbeitstage (Timer1 - 16bit) und ein 
Universaltimer (Timer2 -8 bit) der immer 10 ms läuft. Ich baue mir 
daraus eben dann meine gewünschte Zeit.

Vernküft ist damit
1
void displayService()
2
{
3
  if (arbeitstag < ARBEITSTAGE)
4
  {
5
    led_mode = 0;
6
  }
7
  //Angenomme Zeit bis zu einem Monat überzogen
8
  else if (arbeitstag >= ARBEITSTAGE && arbeitstag < (ARBEITSTAGE + GRENZE_1))
9
  {
10
    //LED Anzeige soll blinken
11
    led_mode = 1;
12
  }
13
  //Angenomme Zeit über einen Monat überzogen
14
  else if (arbeitstag <= (ARBEITSTAGE + GRENZE_1) && arbeitstag < (ARBEITSTAGE + GRENZE_2))
15
  {
16
    //LED Anzeige + Anzeige soll blinken    
17
  }
18
  //Angenomme Zeit über zwei Monate überzogen
19
  else if (arbeitstag >= (ARBEITSTAGE + GRENZE_2))
20
  {
21
    //Elektronik sperren. Keine weitere Nutzung mehr möglich
22
     digit[2] = S;
23
     digit[1] = E;
24
     digit[0] = r;
25
    while (1){}
26
  }
27
}

Hänge aber den gesamten Code auch an, was inzwischen gut 500 Zeilen 
sind. Ich weiß es ist vllt nicht alles super programmiert. Bin eben kein 
gelernter Programmierer, lerne aber gern dazu. Meine Prgrammteile sind 
leider sehr statisch, nehme ich mal an und für andere spätere Projekte 
wohl nicht einfach nutzbar. Ich versuche aber eben viel mit #define zu 
machen, dass wenn ich etwas ändere, nicht der Ganze Code durchforstet 
werden muss. Mehr so in der Art OOP (so kenn ich es zumindest aus dem 
C#-Unterricht) wäre bestimmt nicht schlecht.

von Hubert G. (hubertg)


Lesenswert?

Du schaltest im case 1 die grüne Led immer nur aus.
Etwas Beschreibung wäre nicht schlecht.

von Karl H. (kbuchegg)


Lesenswert?

Christian M. schrieb:

> Alle Timer des ATmegas sind in Benutzung. 1 für die 7-Seg (Timer0 -
> 8bit), 1 für meine Zählung der Arbeitstage (Timer1 - 16bit) und ein
> Universaltimer (Timer2 -8 bit) der immer 10 ms läuft. Ich baue mir
> daraus eben dann meine gewünschte Zeit.

Und warum kann der Timer 2 dann nicht die Verwaltung der LED mitmachen?
So viel Arbeit ist das dann in der ISR auch wieder nicht, als das man 
das auf Biegen und Brechen da raushalten muss.
1
 ISR (TIMER2_COMP_vect)
2
 {
3
   /*Der Compare Interrupt Handler
4
   wird aufgerufen, wenn
5
   TCNT2 = OCR2 = 233-1
6
   ist (233 Schritte), d.h. genau alle 9,984ms
7
   */
8
   timer2_wert++ ;
9
 }

Da ist noch viel Spielraum. Wenn die ISR alle 10ms aufgerufen wird, dann 
ist es ein Kinderspiel, alle 500 ms eine LED umzuschalten.
1
uint8_t blinkCnt;
2
3
 ISR (TIMER2_COMP_vect)
4
 {
5
6
   /*Der Compare Interrupt Handler
7
   wird aufgerufen, wenn
8
   TCNT2 = OCR2 = 233-1
9
   ist (233 Schritte), d.h. genau alle 9,984ms
10
   */
11
   timer2_wert++ ;
12
13
14
   blinkCnt++;
15
   if( blinkCnt == 100 ) {
16
     blinkCnt = 0;
17
18
     if( blinkCnt < 50 )
19
       PORTC &= ~LED_RED;
20
     else
21
       PORTC |= LED_RED;
22
   }
23
 }

und das ganze natürlich nur dann, wenn die restlichen Programmteile sich 
auf einen ledMode geeinigt haben, der im Falle eines blinkens, wenn ich 
das recht sehe auf 1 stehen wird.
1
volatile uint8_t ledMode;
2
uint8_t blinkCnt;
3
4
 ISR (TIMER2_COMP_vect)
5
 {
6
7
   /*Der Compare Interrupt Handler
8
   wird aufgerufen, wenn
9
   TCNT2 = OCR2 = 233-1
10
   ist (233 Schritte), d.h. genau alle 9,984ms
11
   */
12
   timer2_wert++ ;
13
14
15
   if( ledMode == 1 ) {
16
     blinkCnt++;
17
     if( blinkCnt == 100 ) {
18
       blinkCnt = 0;
19
20
       if( blinkCnt < 50 )
21
         PORTC &= ~LED_RED;
22
       else
23
         PORTC |= LED_RED;
24
     }
25
   }
26
 }

und wenn wir schon dabei sind, dann kann in der ISR auch die Auswertung 
des ledMode für ALLE Led passieren. Die paar Taktzyklen hast du dort 
allemal, dass die Timer ISR die jeweiligen LED anhand des ledMode 
schaltet. Keine Angst.
Man könnte auch den ledMode Bitcodieren. Bit 0 steht grundsätzlich für 
'rot oder grün', Bit 1 steht für 'rote LED auf jeden Fall blinken 
lassen.
D.h. der ledMode kann 4 Werte annehmen
1
0  grün leuchtet
2
1  rot leuchtet
3
2  grün leuchtet und rot blinkt
4
3  rot blinkt

Bit 0 setzt oder löscht du zb in der main() je nach Ergebnis des ADC 
Vergleiches. Und Bit 1 setzt bzw. löscht du je nachdem ob dein 
Wartungszyklus abgelaufen ist oder nicht. Um den Rest kümmert sich dann 
die ISR. In einem gewissen Sinne genau identisch zur Ansteuerung der 
7-Segment Anzeige, bei der du auch ein paar Variablen hast, in die du 
das auszugebende Muster schreibst und die ISR kümmert sich um den Rest 
(und das könnte sogar ein und dieselbe ISR sein. Kein Mensch sagt, dass 
1 ISR auch nur 1 Aufgabe bearbeiten darf)
1
volatile uint8_t ledMode;
2
uint8_t blinkCnt;
3
4
 ISR (TIMER2_COMP_vect)
5
 {
6
7
   /*Der Compare Interrupt Handler
8
   wird aufgerufen, wenn
9
   TCNT2 = OCR2 = 233-1
10
   ist (233 Schritte), d.h. genau alle 9,984ms
11
   */
12
   timer2_wert++ ;
13
14
   if( !(ledMode & 0x01) )   // grün einschalten?
15
     PORTC &= ~LED_GREEN;
16
   else
17
     PORTC |= LED_GREEN;
18
19
   if( ledMode & 0x02 ) {     // rot soll also auf jeden Fall blinken
20
     blinkCnt++;
21
     if( blinkCnt == 100 ) {
22
       blinkCnt = 0;
23
24
       if( blinkCnt < 50 )
25
         PORTC &= ~LED_RED;
26
       else
27
         PORTC |= LED_RED;
28
     }
29
   }
30
31
   else {                    // rot ist nicht auf blinken geschaltet
32
                             // wie soll es stehen. Wenn grün aus ist
33
                             // dann muss rot ein sein
34
     if( ledMode & 0x01 )
35
       PORTC &= ~LED_RED;
36
     else
37
       PORTC |= LED_RED;
38
   }
39
 }

 Auch wenn es immer wieder heisst, eine ISR soll  so kurz wie möglich 
sein - das bedeutet nicht, dass man gar nichts in einer ISR machen darf. 
Das sieht zwar in C alles recht langatmig aus, aber in Maschinencode 
sind das pro ISR Durchlauf nur ein paar Zyklen. Nichts worüber man sich 
gross Gedanken machen muss, zumal in deinem Programm nichts wirklich 
zeitkritisch ist.
Das restliche Programm setzt den ledMode so wie es der Situation 
angemessen ist und die Timer-ISR realisiert das dann an den 
physikalischen LED. Auf die Art bleibt dann alles beisammen und du hast 
die LED Steuerung nur an einer einzigen Stelle im Code. Und da die ISR 
alle 10ms aufgerufen wird, realisieren die physikalischen LED das was du 
über ledMode eingestellt hast, spätestens nach 10ms und ein paar 
zerquetschten (je nachdem wie lange eine eventuelle andere ISR noch 
dazwischen gefunkt hat). Also schnell genug für einen menschlichen 
Benutzer, der das Umschalten als aus seiner Sicht 'sofort' einstufen 
wird.

: Bearbeitet durch User
von M. K. (sylaina)


Lesenswert?

Karl H. schrieb:
> volatile uint8_t ledMode;
> uint8_t blinkCnt;
>
>  ISR (TIMER2_COMP_vect)
>  {
>
>    /*Der Compare Interrupt Handler
>    wird aufgerufen, wenn
>    TCNT2 = OCR2 = 233-1
>    ist (233 Schritte), d.h. genau alle 9,984ms
>    */
>    timer2_wert++ ;
>
>    if( ledMode == 2 ) {
>      blinkCnt++;
>      if( blinkCnt == 100 ) {
>        blinkCnt = 0;
>
>        if( blinkCnt < 50 )
>          PORTC &= ~LED_RED;
>        else
>          PORTC |= LED_RED;
>      }
>    }
>  }

Geht auch einfacher:
1
volatile uint8_t ledMode;
2
//fehlt da nicht ein volatile?
3
volatile uint8_t blinkCnt;
4
5
 ISR (TIMER2_COMP_vect)
6
 {
7
8
   /*Der Compare Interrupt Handler
9
   wird aufgerufen, wenn
10
   TCNT2 = OCR2 = 233-1
11
   ist (233 Schritte), d.h. genau alle 9,984ms
12
   */
13
   timer2_wert++ ;
14
15
16
   if( ledMode == 2 ) {
17
     blinkCnt++;
18
     if( blinkCnt%50 == 0 ) {
19
       // Immer wenn blinkCnt ein Vielfaches von 50 ist, also ca. alle 500ms
20
       //blinkCnt auf 0 setzen, also immer nur wenn blinkCnt 50 ist
21
       blinkCnt = 0;
22
       //und die rote LED togglen/umschalten
23
       PORTC ^= (1 << LED_RED);
24
     }
25
   }
26
 }

von Karl H. (kbuchegg)


Lesenswert?

Michael K. schrieb:

> Geht auch einfacher:

viele Wege führen nach Rom.
ICh wollte die % Operation vermeiden und ihm einen Hinweis geben, dass 
das Blinken nicht unbedingt symetrisch sein muss. Auch 20% ein und 80% 
aus sind möglich um einen Kontrast zu schaffen. Aber das muss er selbst 
entscheiden.

>
1
volatile uint8_t ledMode;
2
> //fehlt da nicht ein volatile?
3
> volatile uint8_t blinkCnt;
4
>

nein. Wozu soll das volatile hier gut sein. der blinkCnt wird ja nur in 
der ISR verwendet. Man hätte ihn genausogut auch als static Variable in 
die ISR hineinziehen können.
1
 ISR (TIMER2_COMP_vect)
2
 {
3
   static uint8_t blinkCnt;
4
5
   ...

(aber dann wären wahrscheinlich wieder Fragen gekommen, was das static 
an dieser Stelle bedeutet :-)

: Bearbeitet durch User
von M. K. (sylaina)


Lesenswert?

Karl H. schrieb:
> viele Wege führen nach Rom.
> ICh wollte die % Operation vermeiden und ihm einen Hinweis geben, dass
> das Blinken nicht unbedingt symetrisch sein muss. Auch 20% ein und 80%
> aus sind möglich um einen Kontrast zu schaffen. Aber das muss er selbst
> entscheiden.

Auch wieder wahr ;)

von chris (Gast)


Lesenswert?

Michael K. schrieb:
> PORTC ^= (1 << LED_RED);

Das sollte man in einer ISR vermeiden, da nicht atomar!
Zumindest dann, wenn man auf den selben PORT auch wo anders zugreift 
(Hauptprogramm).

Wenn dann sollte man das mit
1
PINC = (1<<LED_RED);
machen.

von M. K. (sylaina)


Lesenswert?

chris schrieb:
> Das sollte man in einer ISR vermeiden, da nicht atomar!

Hm, da gabs bei mir zwar noch nie Problem mit aber OK, danke für den 
Tipp. Das war mir nicht bekannt.

von Christian M. (krusti)


Angehängte Dateien:

Lesenswert?

Hallo,

es ist wieder etwas Zeit vergangen und teilweise auch ein paar Nerven. 
Aber mein Projekt ist, zumindest programmiertechnisch beinahe fertig 
gestellt.
Allerdings habe ich noch zwei Probleme, welche ich nicht lösen kann.

Ich habe das Gefühle, wenn der ISP-Programmer nicht mehr steckt, dass 
falsche Werte stärker reinkommen als wenn ich den Programmer dran lasse. 
Wenn ich z.B. die Max-Hold-Funktion nutze, wandert der Max-Wert auf 
Dauer ohne den Programmer weiter hoch als mit dem Programmer.
Im "Live"-Modus schwankt die Zahl etwas mehr. Es ist nicht viel und ich 
habe die Aktualisierung der Anzeige durch eine Zeitfunktion etwas 
entstresst. Trotzdem würde mich interessieren warum es so ist.
Vermute mal es hat etwas mit meinen offenen Pins der ISP-Schnittstelle 
zu tun. Muss ich diese irgendwie am Controller speziell programmieren 
oder beschalten?

Zweites Problem, was mich mehr nervt:
Meine LED's flackern, also unabhängig vom Blinken, mit schneller aber 
sichtbarer Frequenz. Ich bin mir auch ziemlich sicher, dass es an PORTC 
= ~(1 << (stelle + 3)) liegt:
1
ISR (TIMER0_OVF_vect)
2
{
3
  /* Interrupt Aktion alle
4
  (6000000/64)/256 Hz = 366,2109375 Hz
5
  bzw. 1/366,2109375 s = 2,73 ms  */
6
  static uint8_t stelle;
7
  
8
  PORTC |= DIGIT_1 | DIGIT_2 | DIGIT_3;  //Anzeige ausschalten
9
    stelle++;
10
  if (stelle==3)
11
  {
12
    stelle = 0;
13
  }
14
  PORTD = digit[stelle];          //Entsprechendes Digit ausgeben
15
  
16
17
  if (dezimalPunkt == 0x01)  {
18
    PORTD &= ~DEZ_PUNKT;
19
  } 
20
  else  { 
21
    PORTD |= DEZ_PUNKT;                             
22
  }
23
  
24
  PORTC = ~(1 << (stelle + 3));                
25
  
26
  //7-Segmentanzeige blinkt
27
  if (blinkDisplay == 0x00)  {
28
    PORTC &= ~(DIGIT_1 & DIGIT_2 & DIGIT_3);  //Aus
29
  }
30
  else  {
31
    PORTC |= DIGIT_1 | DIGIT_2 | DIGIT_3;    //Ein
32
  }
33
}
Schließlich überschreibe ich ja auch meine LED's damit kurzzeitig. Aber 
ich habe diverse Bitmanipulationen probiert und finde einfach keine 
Lösung. Meist blieben die LED's dann aus. Vielleicht bin ich in diesem 
Punkt schon zu sehr verhirnt.

von Hubert G. (hubertg)


Lesenswert?

AREF auf AVCC legen ist eine schlechte Lösung. Du bekommst alle kleinen 
Spannungsschwankungen auf das Messergebnis übertragen. Besser ist AREG 
trennen und über 100n auf GND legen. In die Leitung zu AVCC eine kleine 
Induktivität, 10µ, einfügen.
Wenn man genau messen will, ist es auch schlecht auf PORTC 
Schaltfunktionen zu tätigen, da diese ebenfalls das Messergebnis 
beeinflussen.

von Christian M. (krusti)


Lesenswert?

Danke für die Antwort.

Im Datenblatt steht ja:
If the ADC is used, it should be connected to VCC  through a low-pass 
filter.
Also wenn ich es richtig verstehe, so?
5V-ADC----      5V--
         |          |
AREF------      VCC----10µH---AVCC
         |          |
       100nF       100nF (an jeden entsprechenden Pin)
         |          |
        GND        GND

Und jetzt wo ich es schreibe, merke ich, dass ich das selber schon mal 
im Datenblatt gesehen habe.

Ich weiß, dass am ADC keine Schaltfunktionen ausgeführt werden sollen, 
aber ich habe keine Pins mehr frei.
Außer es funktioniert folgender Gedanke. Ich will später einen 
SMD-Baustein verwenden, TQFP. Wenn ich mir die Pinbelegung anschaue gibt 
es zwei reine ADC-Ports. ADC6 und ADC7 an Pin 19 und 22. Sind diese dann 
von PortC getrennt und kann ich da mein Auslesen hin verlegen? Oder 
hängen diese ADC's trotzdem an PortC irgendwie mit dran?

Jetzt noch schnell eine Frage. Bisher hatte ich einen Mega8 drin, 
bekomme aber auf die schnelle als SMD nur einen Mega8A. Wenn ich das 
richtig recherchiert habe, sollte es da keine Probleme für meinen Code 
geben korrekt?

von Uwe (de0508)


Lesenswert?

Hallo Christian M. ,

an AREF liegt i.a.  nur einem 100nF Kondensator nach Masse.
Wenn ich wilklich mit dem ADC messen will, verbinde ich diesen Anschluss 
mit einer Referensopannungsquelle von 2,56V, 4,096V o.ä.

Aber niemals mit +5V der Vcc, das ist die Versorgungsspannung und diese 
ist nicht geeignet und es könnte zu Probleme mit der Internen ADC 
Referenzspannung kommen.

Ich habe noch einige neue TQFP atemga8, atmega88pa, atmega168  und 
atmega328P da.
Die ich zu meinem EK abgeben kann.

Die TQFP atmega162 kommen dann für dich sicherlich nicht in Frage?

: Bearbeitet durch User
von Christian M. (krusti)


Lesenswert?

Aber ich habe ja 2x 5V. Einmal zur Versorgung aller Bauteile. Und einmal 
extra eine externe Referenzspannung mit glatten 5V (LT1021).
Deswegen habe ich in dem kleinen Bild einen Post vorher ja einmal die 
Spannung 5V-ADC genannt und einmal 5V. War eventuell etwas verwirrend 
und misszuverstehen, gebe ich im Nachhinein zu.

Danke Uwe für dein Angebot. Habe aber einen Atmega8 gefunden als SMD. Es 
war nur eine kleine Frage ob ich auch den mega8A hätte direkt weiter 
nutzen können, vielmehr auch aus Neugier eine Frage nebenbei.

von M. K. (sylaina)


Lesenswert?

Christian M. schrieb:
> Im Datenblatt steht ja:
> If the ADC is used, it should be connected to VCC  through a low-pass
> filter.
> Also wenn ich es richtig verstehe, so?
> 5V-ADC----      5V--
>          |          |
> AREF------      VCC----10µH---AVCC
>          |          |
>        100nF       100nF (an jeden entsprechenden Pin)
>          |          |
>         GND        GND
>
> Und jetzt wo ich es schreibe, merke ich, dass ich das selber schon mal
> im Datenblatt gesehen habe.

An den ARef-Pin gehört lediglich eine Referenzspannung und ein 
Kondensator gegen GND, sonst nichts. Wenn also die 5V-ADC keine 
besonders stabilisierte Spannung darstellen haben die am ARef-Pin nicht 
zu suchen.

Da du ja als Referenzquelle ja einen LT1021 benutzt und dass die 5V-ADC 
darstellen ist das also so völlig in Ordnung.

von Christian M. (krusti)


Lesenswert?

Michael K. schrieb:
> Da du ja als Referenzquelle ja einen LT1021 benutzt und dass die 5V-ADC
> darstellen ist das also so völlig in Ordnung.

Danke, das wollte ich wissen. Hoffe das wird den ADC etwas beruhigen.

Dann bleibt nur noch das flackern der LED's übrig, was ich nicht gelöst 
bekomme.

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.