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
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
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.
@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?
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
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
@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...
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.
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...
@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
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
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
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.