Forum: Mikrocontroller und Digitale Elektronik Analog Comparator => Interrupt läst sich nicht einstellen


von Martin (Gast)


Lesenswert?

Hallo,
Ich versuche beim ATmega8 den Analog Comparator in mein Aktuelles 
Projekt einzubauen.
Dazu soll der Comparator Interrupt jedes Mal Auslösen, wenn die Spannung 
am AIN1 geringe wird als die Reverenz.
Dies lässt sich im Register ACSR mit ACIS1 = 1 und ACIS0 = 0 einstellen.
Jedoch löst der Interrupt bei jedem wechsle aus.
Hat einer von euch eine Idee weshalb er immer auslöst?
Hier der Code:
1
DDRB = 0xff;    
2
ACSR = (0<<ACD)|(1<<ACIE)|(1<<ACIS1)|(0<<ACIS0);
3
//ACD Comparator Aktivieren   ACIE Interrupt aktivieren 
4
  sei();
5
    while(1){
6
      //mache solange nix bis der Interrupt auslöst
7
    while (test == 0)
8
      {
9
    _delay_us(1);
10
    }  
11
            
12
      PORTB = 0xff;_delay_ms(1000);
13
      PORTB = 0x00;_delay_ms(100);
14
      test = 0;
15
    }
16
    }
17
  //Comperrator Interrupt
18
     ISR(ANA_COMP_vect){
19
//die Variable sorgt dafür dass das Programm  die while schleife verlässt //und die LEDs einmal Blinken
20
      test = 1;
21
        
22
      }
grüße

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

ist test denn volatile deklariert?

von Huhu (Gast)


Lesenswert?

Martin schrieb:
> ACSR = (0<<ACD)|(1<<ACIE)|(1<<ACIS1)|(0<<ACIS0);

Was soll das, 0<<ACD, denn?

von Martin (Gast)


Lesenswert?

ups
ja test wurde volatile Deklariert.
die LEDs blinken auch, aber halt bei jeder wechselnden Flanke und nicht 
nur bei einer Fallenden.

von Martin (Gast)


Lesenswert?

ist laut Datenblatt der ACD auf 1 ist der Comparator ausgeschaltet.
Deshalb ziehe ich das bit auf 0 um ihn zu aktivieren.

• Bit 7 – ACD: Analog Comparator Disable
When this bit is written logic one, the power to the Analog Comparator 
is switched off. This bit
can be set at any time to turn off the Analog Comparator. This will 
reduce power consumption in
Active and Idle mode. When changing the ACD bit, the Analog Comparator 
Interrupt must be
disabled by clearing the ACIE bit in ACSR. Otherwise an interrupt can 
occur when the bit is
changed.

von c-hater (Gast)


Lesenswert?

Martin schrieb:

> Dies lässt sich im Register ACSR mit ACIS1 = 1 und ACIS0 = 0 einstellen.
> Jedoch löst der Interrupt bei jedem wechsle aus.
> Hat einer von euch eine Idee weshalb er immer auslöst?

Einzig denkbare Erklärung: Es gibt beim Pegelwechsel heftige 
Überschwinger des Eingangssignals. Das ist allerdings ein absolut 
übliches Problem, willkommen also in der Welt der realen Elektronik 
(also dort, wo reine Spice-Jockeys niemals hinkommen)...

Softwaremäßig kannst du dagegen (auf leichte Art) nur dann etwas machen, 
wenn du den AC als Input für den Timer1 benutzt. Dann kannst du nämlich 
den für dessen IPC-Funktionalität bereits hardwaremäßig eingebauten 
Tiefpass mitbenutzen. (Stichwort für die Suche im Datenblatt: "noise 
canceler"). Dein Handler ist dann nicht der AC-Int, sondern der ICP-Int 
des Timers. Das geht natürlich nur dann, wenn du ICP oder das 
ICP-Register noch nicht für andere Zwecke verwendest.

Benutzt du hingegen den AC-Interrupt direkt, dann mußt du dir so einen 
Tiefpass (oder einen besseren) in Software selber bauen. Das ist 
eigentlich kein wirklich großes Problem, denn man kann ein wichtige 
Eigenschaft von ISRs als Zeitkonstante dafür benutzen: Die verdammten 
Dinger verbrauchen nämlich selber eine ziemlich genau quantifizierbare 
Menge an Rechenzeit. Jedenfalls, wenn man die "richtige" (eigentlich: 
die einzig wirklich geeignete) Sprache zu ihrer Programmierung 
benutzt...

Tipp1: Echte Hochsprachen sind dafür völlig ungeeignet.
Tipp2: Blöde Makroassembler mit Hochsprachen-Attitüde wie z.B. C 
ebenfalls.
       Jedenfalls wenn man ein wirklich deterministisches Zeitverhalten
       erreichen möchte...

von der alte Hanns (Gast)


Lesenswert?

> Dazu soll der Comparator Interrupt jedes Mal Auslösen, wenn die Spannung
> am AIN1 geringe wird als die Referenz.

> ACSR = (0<<ACD)|(1<<ACIE)|(1<<ACIS1)|(0<<ACIS0);


Zwei Punkte verstehe ich nicht:

1. fehlt da nicht ein (1<<ACBG)  ?
2. muss das nicht (1<<ACIS1)|(1<<ACIS0)  heißen  ?

von Martin (Gast)


Lesenswert?

Vielen dank für die Antworten, jedoch glaube ich nicht das es nötig ist 
einen Tiefpass-Filter einzubauen, denn meine Schaltung Schwingt nicht 
und das Signal ist "Sauber", bis auf das rauschen meines Oszis :)
ACIS1 und 0 können folgendermaßen Programiert werden :
1 0 = ACIS
0 0 Comparator Interrupt on Output Toggle
1 0 Comparator Interrupt on Falling Output Edge
1 1 Comparator Interrupt on Rising Output Edge

Mein Problem ist, dass der Interrupt auslöst, auch wenn ihm durch
ACIS1 und ACIS0 gesagt wurde das er nur auslösen soll wenn Z.b. die 
Flanke Fällt.
Momentan löst er bei jedem Wechsel aus.
Sorry wenn ich mich schlecht ausgedrückt habe :)

von der alte Hanns (Gast)


Lesenswert?

> Dazu soll der Comparator Interrupt jedes Mal Auslösen, wenn die Spannung
> am AIN1 geringer wird als die Referenz.
> ACSR = (0<<ACD)|(1<<ACIE)|(1<<ACIS1)|(0<<ACIS0);

Sie triggern derzeit auf die fallende Flanke des Ausganges des 
Komparators, also auf die steigende Flanke von AIN1.
Und was haben Sie an AIN0 angeschlossen?

von Martin (Gast)


Angehängte Dateien:

Lesenswert?

An Ain0 habe ich eine feste Referenz von ca. 3V angeschlossen.

von der alte Hanns (Gast)


Lesenswert?

Was passiert, wenn Sie AIN0 offenlassen und stattdessen die interne 
Referenz mit (1<<ACBG) aktivieren?

von Marin (Gast)


Lesenswert?

Nein leider ändert sich dadurch das auslöse verhalten des Interrupts 
nicht

von Martin (Gast)


Lesenswert?

Hallo,
heute Abend werde ich den Code mal mit dem ATmega64 ausprobieren um so 
Fehler ausschließen zu können.
<hat einer von euch evl. noch einen Code, wo der AC verwendet wird? :)
grüße

von der alte Hanns (Gast)


Lesenswert?

Vorschlag: Minimalaufbau zum Lernen:
Controller, 4 LEDs + Rs zur Kontrollanzeige, RC-Glied mit Taster an AIN1
Wie schon mal erwähnt die interne Referenz benutzen. Eine Variable in 
der ISR hochzählen und in der Hauptschleife auf den LEDs ausgeben.

von martin (Gast)


Lesenswert?

Hallo,
ich habe den Code so umgeschrieben, dass ich sehen kann wie oft der 
Interrupt auslöst und kam zu einem erstaunlichen Ergebnis, der löst in 
der Zeit in der die LED einmal kurz blinkt insgesamt 4X mal aus 
(2mal=22X 3mal= 26X 3mal= 39X ...),
Darauf hin habe ich den Code so geändert das der Prozessor im Interrupt 
die Interrupts deaktiviert.
Sie werden auch erst dann wieder aktiviert, wenn die Anzeige wie oft der 
Interrupt ausgelöst hat fertig ist.
Leider löst der Interrupt immer noch viel zu oft aus (1mal = 2X 2mal = 
15X 3mal= 21X 4mal= 24X)

von Hannes L. (hannes)


Lesenswert?

martin schrieb:
> Darauf hin habe ich den Code so geändert das der Prozessor im Interrupt
> die Interrupts deaktiviert.

Das ist wohl keine Lösung, da auch bei abgeschaltetem I-Flag die 
Ereignisse zum Auslösen der Interrupts gescannt werden und auch die 
Interrupt-Pending-Flags gesetzt werden. Es wird lediglich der Aufruf der 
ISR unterdrückt, bis das I-Flag wieder gesetzt ist.

Falls Deine Interrupts durch Prellen des Signals auftreten, könntest Du 
das (zumindest teilweise) unterdrücken, indem Du am Ende der ISR das 
Pending-Flag (ACI in ACSR) löscht, also einen während der Abarbeitung 
der ISR aufgetretenen weiteren Interrupt entwertest. Dazu müsstest Du 
das Bit ACI im Register ACSR per Programm auf 1 setzen. Um das I-Flag 
musst Du Dich während der ISR nicht kümmern, das wird per Hardware beim 
Aufruf der ISR gelöscht und durch RETI (Rücksprung aus der ISR) wieder 
gesetzt.

...

von der alte Hanns (Gast)


Lesenswert?

Bei Assembler hätte ich nun gefragt, ob vielleicht der Stackpointer 
nicht inititalisiert wurde, aber da man ja angeblich in C "einfach 
losrennen" kann, ohne sich um solchen Thünkram zu kümmern, muss es wohl 
an etwas anderem liegen.

von schrieb schrieb schrieb (Gast)


Lesenswert?

Verwendest du einen Taster? Falls ja, kann es sein, dass er prellt. 
Entweder - wie schon von anderen erwähnt - eine RC-Kombination mit 
größerem Tau als die Prellzeit vorschalten oder das Signal sauberer 
erzeugen. Im einfachsten Fall mit einem Frequenzgenerator 1 Hz, 
Rechteck. Oder einen Pin des µCs togglen. Der erzeugt kein Prellen.

von Matin (Gast)


Lesenswert?

ich verwende ein Netzteil um die Spannung zu erzeugen.
Des weiterem habe ich heute den Tiefpass gebaut(Kondensator 100up 
Widerstand 4k7) ob die Werte passen kann ich leider nicht beurteilen, 
aber dadurch ist die Anzahl der Ausgelösten Interrupts nicht 
zurückgegangen.

von Martin (Gast)


Lesenswert?

Also für mich nochmal zu Verständnis:
Sobalt einmal ein Interrupt ausgelöst ist werden in der Zeit(in der der 
Interrupt arbeitet) keine weiten Interrupts ausgeführt, auch wen das 
Signal Schwingt und der AC dadurch nochmal auslösen müsste?
Wieso ist den dann die Variable, die bei jedem Interrupt +1 gezählt wird 
so hoch ?
Und warum wirkt sich das dann auf das auslöse verhalten aus?

von spess53 (Gast)


Lesenswert?

Hi

>Also für mich nochmal zu Verständnis:
>Sobalt einmal ein Interrupt ausgelöst ist werden in der Zeit(in der der
>Interrupt arbeitet) keine weiten Interrupts ausgeführt, auch wen das
>Signal Schwingt und der AC dadurch nochmal auslösen müsste?

Wenn während der Abarbeitung der Interruptroutine (mindestens) ein neues 
Ereignis eintritt, wird das gespeichert und der Interrupt wird nach der 
Interruptroutine erneut ausgelöst.

MfG Spess

von Martin (Gast)


Lesenswert?

OH vielen dank Spess :)
Ich habe jetzt am ende des ISR´s ACi mit einer 1 Beschrieben, und 
zusätzlich ein Delay von 10us eingebaut um so nachträgliches Rauschen zu 
verhindern.

Macht es den eigentlich Sinn den Comperrator so zu Programmieren, dass 
er bei steigender/fallender Flanke auslöst? (da ja jedes Signal Rauscht 
in meinem Beispiel mit ein mikro--Farad im ns Bereich)?
Grüße
Martin

von Hannes L. (hannes)


Lesenswert?

Martin schrieb:
> zusätzlich ein Delay von 10us eingebaut

Wenn Dir der AC-Interrupt zu schnell ist, dann lass den AC-Interrupt 
doch ganz weg und polle im Timer-Interrupt den AC-Ausgang (ACO in ACSR). 
Dort ist auch eine Entprellung möglich, die man mit einer 
Flankenerkennung kombinieren kann. Das verschlingt lange nicht so viel 
Rechenzeit wie ein 10ms-Delay und ist durch Variation des 
Timer-Intervalls recht gut an das Rauschen Deines Signals anpassbar.

...

von der alte Hanns (Gast)


Lesenswert?

> und das Signal ist "Sauber", bis auf das rauschen meines Oszis :)

> Des weiterem habe ich heute den Tiefpass gebaut

Und nun gibt es plötzlich ein Problem mit dem Rauschen.
Jetzt fällt mir nur noch Otto Reutter ein:
"Da fragt man sich ganz fassungslos
wie kommt das bloß, wie kommt das bloß?"

von Martin (Gast)


Lesenswert?

Vielen Danke für die vielen Ideen, die Schlussendlich zur Lösung meines 
Probleme frühten :D
Ich habe das Problem via dem ACO Bits gelöst.
ich Poste den Code für nachfolgende Leute die ähnliche Probleme haben :D

ACSR = (0<<ACD);

      while(1)
      {
        PORTB = (0<<PB2)|(1<<PB3);
      _delay_ms(100);
        while(ACSR &(1<<ACO))
        {
            PORTB = (1<<PB2)|(0<<PB3);
            _delay_ms(100);
        }

        while(ACSR &(0<<ACO))
        {
          PORTB = (0<<PB2)|(1<<PB3);
          _delay_ms(100);
        }

      }


Danke euch nochmal :D

von c-hater (Gast)


Lesenswert?

Martin schrieb:
> Vielen Danke für die vielen Ideen, die Schlussendlich zur Lösung
> meines
> Probleme frühten :D
> Ich habe das Problem via dem ACO Bits gelöst.

Nein, hast du nicht. Du hast es durch einen Tiefpaßfilter gelöst (wie 
von mir vor gefühlten 100 Postings bereits empfohlen). Das ist nämlich, 
was deine dümmlichen Delays im Kern darstellen.

Daß dein Lösung funktioniert, weist übrigens noch auf eine Sache sehr 
deutlich hin: Du kannst nicht mit einem Oszi umgehen. Du warst nicht in 
der Lage, damit die für die Fehlfunktion verantwortlichen 
Signalbestandteile sichtbar zu machen. Nichtmal nach einem 
ausdrücklichen Hinweis, wonach du suchen mußt...

Du hast wohl noch sehr viel zu lernen. Dann würdest du wohl auch 
herausbekommen, daß Zeitkonstanten im Bereich von 100ms bei diesem 
Problem sehr deutlich darauf hinweisen, daß es eine Einflußnahme 
externer Schaltungsbestandteile gibt und diese wahrscheinlich über die 
Stromversorgung einwirken. Wohl die Referenz des AC unzureichend 
entkoppelt...

Ja, man kann solche krassen Schaltungsfehler auch durch Software 
kompensieren. "Schön" geht aber anders...

von Hannes L. (hannes)


Lesenswert?

Martin schrieb:
> Ich habe das Problem via dem ACO Bits gelöst.

Hast Du nicht. Delays im 100ms-Bereich lösen kein Problem, sie sind das 
Problem.

Ich empfahl Dir Pollen im Timer-Interrupt, evtl. kombiniert mit der 
Entprellung (das ist ein Link auf einen Wiki-Artikel!!!) nach Peter 
Dannegger, die auch die Flankenerkennung enthält.

...

von Davis (Gast)


Lesenswert?

Bitte den Beitrag von c-hater löschen. Leute wie der sorgen hier für 
saumäßiges "Betriebsklima". Also weg mit dem Beitrag wegen der 
Pöbeleien.

von Martin (Gast)


Lesenswert?

Die _Delay(100ms) sind nur dazu da, dass man am ende auch noch sehen 
kann, das die LEDs leuchten, und das man soweit möglich auf sie 
verzichten sollte weiß ich bereits :)
Dieser Aufbau soll legendlich ein Versuchsaufbau zeigen in dem der 
Prozessor sich nur um den AC kümmert.

von Hannes L. (hannes)


Lesenswert?

Martin schrieb:
> Dieser Aufbau soll legendlich ein Versuchsaufbau zeigen in dem der
> Prozessor sich nur um den AC kümmert.

Dumm ist nur, dass der "Prozessor" sich aufgrund der blockierenden 
Programmstruktur nur um den AC kümmern kann. Nun kommt es aber in der 
Praxis selten (eigentlich nie) vor, dass der Controller sich nur um 
ein Feature kümmern muss. Der Normalfall ist doch, dass so ein 
Controller auch noch weitere Aufgaben zu erledigen hat. Deshalb schreibt 
man die einzelnen Jobs so, dass sie möglichst wenig Rechenzeit binden 
und dem Controller die Möglichkeit geben, sich zwischendurch andere Jobs 
zu kümmern.

Meine letzte AC-Nutzung hatte ich bei der Auswertung eines 
RC-Empfängers. Dieser hatte kein Summensignal. Ich fasste daher Kanal 1, 
3 und 5 mit Dioden und PullDown-R zusammen (wired OR) und wollte diese 
mit dem ICP des Tiny2313 auswerten. Nur leider waren die Pegel durch den 
Spannungsabfall der Dioden zu niedrig für einen normalen Portpin. Also 
nutzte ich den AC als Eingang, die interne Bandgap-Spannung als Referenz 
und ließ den ICP-Interrupt vom AC auslösen. Geht wunderbar, decodiert 
hochpräzise alle 5 Kanäle und belastet dabei kaum die CPU. Somit kann 
sich der Tiny2313 voll um die anderen Aufgaben kümmern.

...

von Rolf Magnus (Gast)


Lesenswert?

Martin schrieb:
> ist laut Datenblatt der ACD auf 1 ist der Comparator ausgeschaltet.
> Deshalb ziehe ich das bit auf 0 um ihn zu aktivieren.

Ja, aber genau das selbe passiert auch ohne das (0<<ACD). Denn 0<<ACD 
ist 0, und wenn man etwas mit 0 verODERt, ändert sich der Wert dadurch 
nicht.

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.