Forum: Mikrocontroller und Digitale Elektronik LDR am AD-Wandler


von Karl (Gast)


Lesenswert?

Hallo zusammen,

ich möchte gerne mit dem Mega8 eine analoge Spannung messen und an den
LEDs des STK500 ausgeben.

Mit einem Poti zwischen Ground und 5V und der Mittelabzapfung am Port C
Pin2 klappt das hervorragend. Ich kann durch Drehen des Potis
recht präzise zwischen 00000000 und IIIIIIII durchfahren.

Wenn ich jedoch statt dem Poti ein LDR + Reihenwiderstand wähle, kommt
nur geflackere heraus.
Das LDR hat einen Widerstand zwischen 0,6 und 22 KOhm.
Damit daraus ein Spannungsteiler wird, habe ich eine 2,2 KOhm
widerstand in Reihe geschaltet.
Laut meinem analogen (und damit trägen) Messgerät liegen je nach
Beleuchtung Werte von 1 bis 4,5 Volt
am LDR an.
Wenn ich an dieser Stelle Pin 2 Port C anschliesse, erhalte ich aber
keinen vernünftigen Anzeigewert, sondern nur ein wildes Geflackere.
Man kann allerdings tendenziell erkennen, dass die höheren Bits
vorzugsweise bei höheren Leuchtstärken flackern.
Wenn ich den LDR mit einem Schraubenzieher überbrücke, sind alle LEDs
aus. Wenn ich den Widerstand überbrücke, sind alle LEDs an.(oder
umgekehrt ?)
So ganz verkehrt scheint die Schaltung also nicht zu sein.

Kann es sein, dass ich den LDR irgendwie entstören muss?
Irgendwie ein Filter mit Spule in  Reihe und Widerstand parallel
reinbasteln? Wie dimmensionieren? Von mir aus können alle Störungen
über 1 Herz herausgefiltert werden.

Vielen Dank an alle und Frohe Weihnachten
Karl

von Mr_Boertsch (Gast)


Lesenswert?

Hallo

Filtern von solchen Signalen ist immer gut. Dein Messgerät macht das
warscheinlich auch und desshalb kommt da ein sinnvoller Wert raus.

Ausserdem hat warscheinlich deine Beleuchtung auch 50Hz Brumm. Somit
wird der LDR sowiso nie eine Gleichspannung bringen.

mfg
mr_b

von Mr_Boertsch (Gast)


Lesenswert?

Jenachdem wie genau die messung sein soll, könntest du einen passiven
oder aber einen aktiven Filter voranhängen.

Die einfachere Variante wäre aber den Filter Digital im Prozessor zu
integrieren.

von Karl (Gast)


Lesenswert?

@Mr_Boertsch:

Ups, so naheliegend, aber darauf kam ich absolut nicht. Ich hatte
zuerst gedacht, dass mit der Schaltung oder am Programmm irgendwas faul
wäre. Aber dass unsere Energiesparbirnen flackern kam mir absolut nicht
in den Sinn :-(

Und als ich das Ding heute Mittag bei Tageslicht meinem Bruder
vorführte, und auf vorrübergehend alles prime funzte, hatte ich das
schon auf den Mega8 geschoben, der am Vorabend einige Stunden ständig
neu geflashed wurde. Aber das sollte ja eigentlich nichts ausmachen.


An einen digitalen Filter habe ich auch schon gedacht.
Aber wie programmiert man den?

Ich hatte mir folgendes überlegt:

AlterMesswert = 0

loop:
   Messen,
   Ergebnis + Multiplikator * alten Messwert
   das Ergebnis davon durch (Multiplikator+1) teilen
rjmp loop

So müsste doch dann ein Durchschnittswert ermittelt werden.
Was ja auch irgendwie ein Filter wäre.

Hast du zufällig einen Codefetzen für einen Filter rumliegen?

von ...HanneS... (Gast)


Lesenswert?

Häng doch erstmal einen RC-Tiefpass vor den Eingang...

von Andi (Gast)


Lesenswert?

Hier ne kleine ADC-Routine zum lesen und berechnen des Mittelwertes vom
letzten und neuen ADC-Wert als ADC-IRQ:

.def ADCValue=r3

ADCComplete:
  push  r16    ;r16 auf den Stack sichern.
  in  r16,sreg  ;Statusregister in r16
  push  r16    ;und auf den Stack.
  in  r16,adch  ;Obere 8 Bit des ADC-Wertes einlesen.
  add  ADCValue,r16  ;Neuen ADC-Wert zum alten addieren.
  clr  r16    ;r16 ist jetzt das high-Byte.
  adc  r16,r16    ;Carry-Bit zu r16 addieren.
  lsr  r16    ;Neuer ADC-Wert / 2.
  ror  ADCValue
  adc  ADCValue,null  ;Carry-Bit zu r16 addieren.
  pop  r16    ;Statusregister vom Stack holen
  out  sreg,r16  ;und in SREG zurückschreiben.
  pop  r16    ;r16 vom Stack holen.
  reti

Möglich, das man besser den Mittelwert aus einem Ring-Buffer berechnet,
um es ruhiger zu bekommen aber für größeres hatte ich im Tiny26 keinen
Platz mehr und war froh, das alles funtzte.
Ach ja: Der ADC ist auf "Left Adjusted" geschaltet und nur die oberen
8 Bit werden verarbeitet. Sollte für Licht-Messung eigentlich reichen
wenn ab einer bestimmten Licht-Grenze irgend was passieren soll.
Die Routine addiert 2 8Bit-Werte, ADCValue und ADCH, zu einem
16Bit-Wert und halbiert es danach so das es dann in das 8Bit-Register
ADCValue paßt.

Gruß und frohe Weihnachten
Andi

von Andi (Gast)


Lesenswert?

ADCComplete:
  push  r16    ;r16 auf den Stack sichern.
  in  r16,sreg  ;Statusregister in r16
  push  r16    ;und auf den Stack.
  push  r17
  push  zl
  push  zh

  ldi   zl,low(ADCBuffer)
  ldi   zh,high(ADCBuffer)
  lds   r16,ADCPointer
  add   zl,r16
  adc   zh,null
  in    r17,adch
  st    z,r17
  inc   r16
  cpi   r16,4
  brne  ADCCmpl1
  clr   r16
ADCCmpl1:
  sts   ADCPointer,r16
  ldi   zl,low(ADCBuffer)
  ldi   zh,high(ADCBuffer)
  clr   r16
  ld    ADCValue,z+
  ld    r17,z+
  add   r17,ADCValue
  adc   r16,null
  ld    r17,z+
  add   r17,ADCValue
  adc   r16,null
  ld    r17,z+
  add   r17,ADCValue
  adc   r16,null
  lsr   r16
  ror   ADCValue
  lsr   r16
  ror   ADCValue
  adc   ADCValue,null

  pop  zh
  pop  zl
  pop  r17
  pop  r16    ;Statusregister vom Stack holen
  out  sreg,r16  ;und in SREG zurückschreiben.
  pop  r16    ;r16 vom Stack holen.
  reti

.deg
ADCPointer: .byte 1
ADCBuffer: .byte 4

von ...HanneS... (Gast)


Lesenswert?

@Andi:

Bei nur 2 8-Bit-Werten:
 in neuwert,adch      ;Wert einlesen
 add altwert,neuwert  ;Übertrag steht im Carry
 ror altwert          ;durch 2 (incl. Carry)
fertig... (mit zwei unteren (billigen) Registern)

Frohes Fest...

...HanneS...

von Andi (Gast)


Lesenswert?

Stimmt, hast recht.
Ist einfach und macht das selbe.
Wieder was dazu gelernt.

Demnach hier ein neuer Code für den Mittelwert aus alt und neu:

.def null=r2
.def ADCValue=r3

;Irgend wo im Init/Main:
  clr   null
;und nie wieder ändern

ADCComplete:
  push  r16            ;r16 auf den Stack sichern.
  in    r16,sreg       ;Statusregister in r16.
  push  r16            ;und auf den Stack.
  in    r16,adch       ;Obere 8 Bit des ADC-Wertes einlesen.
  add   ADCValue,r16   ;Neuen ADC-Wert zum alten addieren.
  ror   ADCValue       ;Neuer ADC-Wert mit Carry-Bit / 2.
  adc   ADCValue,null  ;Carry-Bit zu ADCValue addieren (aufrunden).
  pop   r16            ;Statusregister vom Stack holen.
  out   sreg,r16       ;und in SREG zurückschreiben.
  pop   r16            ;r16 vom Stack holen.
  reti

Gruß
Andi

PS: Der Code mit dem ADC-Buffer war eigentlich nur ein Gedanke und
hatte den versehentlich, durch gewohnten Klick auf Submit, hier rein
gestellt und ist ungetestet und ungeprüft.
Sehe schon wieder Verbesserungsmöglichtkeiten, aber erst mal egal.

von ...HanneS... (Gast)


Lesenswert?

Moin... - Frohen Rest vom Fest...

@Andi:
Auch ich lerne täglich etwas hier im Forum. Und oft kann man garnicht
so einfach denken wie es eigentlich ist. So ist das nunmal, wenn ein
Mensch versucht, wie eine Maschine zu denken.

Deine ADC-Interrupt-Geschichte hat für Karl aber einen gewaltigen
Wermutstropfen:
Er ist noch nicht beim Int angekommen. Er hat sich gewehrt, seine
Lauflichter mit einem Timer-Int zu realisieren, was das ganze Programm
drastisch vereinfacht hätte. Er geht eben Schritt für Schritt vorwärts,
macht nur das, was er auch nachvollziehen kann. Und das ist sogar gut
so...

@Karl:
Such dir eine Stelle im Programm, die periodisch, aber nicht zu oft
aufgerufen wird, und platziere dort die (angepassten) mittleren Zeilen
aus Andis Vorschlag. Den ADC stellst du dann auf freilaufend ein, so
kann er jederzeit abgefragt werden.

Frohen Rest noch vom Fest...
...HanneS...

von leo9 (Gast)


Lesenswert?

@Karl,
du solltest dir einmal Gedanken über dein zu messendes Signal machen.
Beim Poti wars eindeutig: eine konstante Gleichspannung je nach
Poti-Stellung.
Der LDR reagiert (mehr oder weniger) auf jede Lichtquelle. Bei reinem
Sonnenlicht müßte deine Schaltung recht brauchbar funktionieren. Wenn
du das ganze unter einer Glühbirne testet, ändert sich die Helligkeit
mit der Netzfrequenz.
Ich würde über mehreren Meßwerte das Maximum bilden und eventuell noch
einen Mittelwert.

grüße leo9

von Andi (Gast)


Lesenswert?

Hatte mich früher auch gewundert, warum bei den ersten Tests mit LDR am
ADC die unteren 2 bis 3 Bits bzw. Anzeige-LEDs so hecktisch an und aus
gingen bei den Deckenlampen.
Jetzt ist mir das natürlich klarer.
Zum Glück ist die Anwendung für draußen gewesen.
Mit einer sehr ruhig haltenden Taschenlampe (Gleichstrom) und ohne
Zimmerlicht müßte die Messung dann ruhiger sein.
Dann wäre es doch am besten einen schnellen Scan, z. B. 3KHz, zu machen
und sich dann alle 1/50 Sekunde den höchsten oder kleinsten Wert (je
nach Beschaltung) rauszupicken oder eine Dämpfung mittels RC-Glied.

Gruß
Andi

von AxelR. (Gast)


Lesenswert?

Schalt' doch einefach einen 1uF-Elko parallel zum LDR
Frohe Feiertage
AxelR.

von Andi (Gast)


Lesenswert?

Hier nun mein Versuch die AD-Messung auf ca. 1/50 Sekunde zu glätten:

.def null=r2
.def ADCValue=r3

;Irgend wo im Init/Main:
  clr   null
;und nie wieder ändern

ADCComplete:
  push  r16            ;r16 auf den Stack sichern.
  in    r16,sreg       ;Statusregister in r16
  push  r16            ;und auf den Stack.
  push  r17

  lds   r16,ADBuffer   ;AD-Buffer nach r16.
  in    r17,ADCH       ;Aktuellen AD-Wert einlesen.
  cp    r16,r17        ;Ist der aktuelle AD-Wert größer?
  brcc  ADCCmpl1       ;Wenn nicht, dann beibehalten.
  mov   r16,r17        ;Wenn ja, dann aktuellen AD-Wert
                       ;in den AD-Buffer.
ADCCmpl1:
  lds   r17,ADCounter  ;Zähler für 50 Hz nach r17.
  subi  r17,1          ;Zähler minus 1.
  brcc  ADCCmpl2       ;Ist 1/50 Sekunde vergangen dann
                       ;Mittelwert zwischen alt und neu berechnen.
  add   ADCValue,r16   ;Neuen ADC-Wert zum alten addieren.
  ror   ADCValue       ;Neuer ADC-Wert mit Carry-Bit / 2.
  adc   ADCValue,null  ;Carry-Bit zu ADCValue addieren (aufrunden).

  clr   r16            ;AD-Buffer zurück setzen.
  ldi   r17,47         ;AD-Counter zurück setzen.

ADCCmpl2:
  sts   ADBuffer,r16   ;AD-Buffer speichern.
  sts   ADCounter,r17  ;AD-Counter speichern.

  pop   r17
  pop   r16            ;Statusregister vom Stack holen
  out   sreg,r16       ;und in SREG zurückschreiben.
  pop   r16            ;r16 vom Stack holen.
  reti

.dseg
ADCounter:  .byte 1
ADBuffer:  .byte 1

Dabei wird an jedem Start einer 1/50 Sekunde der AD-Buffer auf 0
gesetzt.
Ist innerhalb der 1/50 Sekunde ein höherer AD-Wert vorhanden kommt der
aktuell gemessene AD-Wert in den AD-Buffer.
Ist 1/50 Sekunde vergangen, wird der Mittelwert aus dem vorigen und dem
neuen AD-Wert aus dem AD-Buffer berechnet und das Spielchen beginnt von
vorne.
Die Berechnung des Mittelwertes kann man auch weglassen und macht dann
nur mov ADCValue,r16 statt add..., ror... und adc...
Der Wert für ADCounter resultiert aus folgendem:
Takt eines Tiny26 = 2MHz.
2MHz / 64 ADC-Prescaler = 31250Hz (Arbeitstakt des ADC).
31250Hz / 13 ADC-Clocks für eine Konvertierung = 2404 Samples/Sekunde.
2404 SPs / 50Hz = 48.
Da auf Unterlauf (brcc) geprüft wird 48 - 1 = 47 als AD-Counter für
50Hz.

ADCComplete ist hier als ISR und sollte auch in einer Endlosschleife
funxionieren wobei man dann die ganzen push und pop und reti wegmacht.
Kann dann passieren, das es keine 50Hz mehr sind. Dazu den ADCouter
entsprechend anpassen (womöglich erhöhen).

Ich muß das Prog erst noch im laufenden Objekt (draußen) uploaden und
habs noch nicht getestet, sollte aber funzen.
Wenn euch Fehler oder Vereinfachungen auffallen, nur her damit.

Gruß
Andi

von Andi (Gast)


Lesenswert?

Wo wir gerade beim Thema sind:
Hat jemand ne Idee, wie man in Hardware am einfachsten eine
Temperaturkompensation machen kann?
Klar, in Software könnte man noch einen NTC auslesen und dazu berechnen
aber vielleicht kennt ja jemand einen passenden NTC den man zum LDR
einfach in Reiche schaltet welcher auf GND geschaltet ist.
Der Widerstand eines LDR wird ja bei Kälte weniger und läßt dann mehr
Strom auf GND durch wobei weniger Spannung am ADC-Pin zurück bleibt.
Dachte, man könnte das mit dem passenden NTC ausgleichen welcher im
umgekehrten Sinn mehr öffnet oder schließt.
Nur darf der nicht zu viel schließen was man durch einen parallel
geschalteten Widerstand wohl ausgleichen könnte.

Gruß
Andi

von Andi (Gast)


Lesenswert?

Noch mal wegen der Temperaturkompensation.
Im Prinzip ist der LDR im PKW so verschaltet:

         VCC
          +
          |
         .-.
         | |10K
         | |
         '-'
   AD-Pin |
      ----o
          |
         .-.
         | |LDR 75 bis 30MOhm
         | |
         '-'
          |
         ===
         GND
created by Andy´s ASCII-Circuit v1.24.140803 Beta www.tech-chat.de

Jetzt dachte ich, man könnte das zum Kompensieren vielleicht so
machen:

         VCC
          +
          |
         .-.
         | |10K
         | |
         '-'   NTC XXX
   AD-Pin |  _
      ----o-|___|-o
                  |
         .       .-.
                 | |LDR 75 bis 30MOhm
                 | |
                 '-'
                  |
                 ===
                 GND
created by Andy´s ASCII-Circuit v1.24.140803 Beta www.tech-chat.de

Gegebenenfalls noch ein Widerstand parallel zum NTC als ausgleich.
Theoretisch könnte das funzen aber welcher NTC und evtl. welcher
Widerstand?

Hat damit jemand Erfahrung?

Gruß
Andi

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.