Forum: Mikrocontroller und Digitale Elektronik m328 - isr - zwei Pins auswerten


von Karl K. (leluno)


Lesenswert?

hallo,

ich möchte die Impulse zweier Stromzähler über zwei Pins am gleichen 
Port auswerten:
1
//aus elt.h
2
#define elt_port     C
3
#define elt_pin400    2
4
#define elt_pin1000    3
5
#define elt_int_port   1
6
#define elt_value_register PIN(elt_port)
7
8
#if elt_int_port==0
9
#define elt_vect   PCINT0_vect
10
#endif
11
#if elt_int_port==1
12
#define elt_vect   PCINT1_vect
13
#endif
14
#if elt_int_port==2
15
#define elt_vect   PCINT2_vect
16
#endif
17
#if elt_int_port==3
18
#define elt_vect   PCINT3_vect
19
#endif
20
#define PCMSK(x)    GLUE(PCMSK, x)
21
#define PCIE(x)      GLUE(PCIE, x)
22
23
24
25
//aus elt.c
26
27
elt_init(){
28
DDR(elt_port)&=~(1<< elt_pin400);
29
DDR(elt_port)&=~(1<< elt_pin1000);
30
PORT(elt_port)|=((1<< elt_pin400));
31
PORT(elt_port)|=((1<< elt_pin1000));
32
33
PCICR |= (1 << PCIE(elt_int_port));//PCIntControlRegister
34
PCMSK(elt_int_port) |= (1<< elt_pin400);
35
PCMSK(elt_int_port) |= (1<< elt_pin1000);
36
sei();
37
}
38
39
40
ISR(elt_vect)
41
{
42
43
u8 storagePINC=PINC;
44
45
if(storagePINC&(1<<elt_pin400))
46
  {
47
  elt_zl_imp400++;
48
  }
49
  
50
if(storagePINC&(1<<elt_pin1000))
51
  {
52
  elt_zl_imp1000++;
53
  }
54
55
}

Die beiden Zähler werden alle 20sec ausgewertet und auf 0 gesetzt. 
Leider sind sie nicht voneinander unabhängig. Der zweite Zähler zeigt 
die Hälfte der Impulse des ersten an obwohl er im Leerlauf ist.


Warum?

Danke für Unterstützung.

von Karl K. (leluno)


Lesenswert?

ich glaube das Problem gefunden zu haben:

der pullup geht auf plus. ausgelöst wird die isr bei fallender Flanke. 
Deswegen  muss PINC==0 ausgewertet werden.

von Rainer W. (rawi)


Lesenswert?

Karl K. schrieb:
> ich möchte die Impulse zweier Stromzähler ...

Wie sehen die Impulse an den Portpins aus (Abstand, Dauer, Form)?
Sind die Pulse sauber entprellt?
Entsprechen die Zählerstände (bis auf diesen Faktor 2) deinen 
Erwartungen?

Karl K. schrieb:
> Deswegen  muss PINC==0 ausgewertet werden.

Eigentlich sollten die Pulse doch ein Anfang und ein Ende haben, so dass 
es egal sein sollte, welche Flanken du zählst.

: Bearbeitet durch User
von Karl K. (leluno)


Lesenswert?

Rainer W. schrieb:
> Eigentlich sollten die Pulse doch ein Anfang und ein Ende haben, so dass
> es egal sein sollte, welche Flanken du zählst.

flanke Pin2 runter => isr wird ausgelöst:
Pin2 ==0, Pin3 ==1(pullup)

flanke Pin2 wieder rauf => isr wird ausgelöst:
Pin2 ==1, Pin3 ==1


geht ein pin auf 0 wird der andere doppelt gezählt weil er bei fallender 
und steigender Flanke jeweils ==1 ist.

So funktioniert es korrekt:
1
ISR(elt_vect)
2
{
3
4
u8 storagePINC=PINC;
5
6
if(!(storagePINC&(1<<elt_pin400)))
7
  {
8
  elt_zl_imp400++;
9
  }
10
if(!(storagePINC&(1<<elt_pin1000)))
11
  {
12
  elt_zl_imp1000++;
13
  }
14
15
}

von Steve van de Grens (roehrmond)


Lesenswert?

Karl K. schrieb:
> u8 storagePINC=PINC;

Dass diese Variable den Code kein bisschen besser lesbar macht, muss ich 
nicht erklären, oder? Außerdem ignoriert sie völlig die Konfiguration 
von

> #define elt_port     C

Lass diese Makro-Spielereien lieber weg.

: Bearbeitet durch User
von S. L. (sldt)


Lesenswert?

> So funktioniert es korrekt ...

?
  Und wenn die beiden kurz hintereinander auslösen? Also z.B. pin400, 
gleich darauf pin1000, während pin400 noch auf 0 liegt?

von Karl K. (leluno)


Lesenswert?

Steve van de Grens schrieb:
> Karl K. schrieb:
>> u8 storagePINC=PINC;
>
> Dass diese Variable den Code kein bisschen besser lesbar macht, muss ich
> nicht erklären, oder? Außerdem ignoriert sie völlig die Konfiguration
> von
>
>> #define elt_port     C
>
> Lass diese Makro-Spielereien lieber weg.


Der zweite Zähler ist zusätzlich dazugekommen. Deswegen musste der Code 
angepasst werden. Aber ich glaube du hast recht.

>> u8 storagePINC=PINC; sollte Fehler verhindern, wenn ein zweiter Impuls 
innerhalb der isr ausgelöst wird. Aber was soll's - so ist es besser:
1
ISR(elt_vect)
2
{
3
4
//u8 storagePINC=PINC;
5
6
if(!(PIN(elt_port)&(1<<elt_pin400)))
7
  {
8
  elt_zl_imp400++;
9
  }
10
if(!(PIN(elt_port)&(1<<elt_pin1000)))
11
  {
12
  elt_zl_imp1000++;
13
  }
14
15
}

von Steve van de Grens (roehrmond)


Lesenswert?

Nun gut, die Makros willst du beibehalten.

> u8 storagePINC=PINC sollte Fehler verhindern, wenn ein zweiter Impuls
> innerhalb der isr ausgelöst wird

Dann mache es so:
1
ISR(elt_vect)
2
{
3
    u8 input=PIN(elt_port)
4
5
    if !( input & (1<<elt_pin400) ) {
6
        elt_zl_imp400++;
7
    }
8
9
    if !( input & (1<<elt_pin1000) ) {
10
        elt_zl_imp1000++;
11
    }
12
}

: Bearbeitet durch User
von S. L. (sldt)


Angehängte Dateien:

Lesenswert?

Es fehlt ein Sperrmechanismus, der sicherstellt, dass vor einem erneuten 
Weiterzählen der betreffende Pin auf 1 gegangen ist.

Nachtrag:
  Diagramm

: Bearbeitet durch User
von Ob S. (Firma: 1984now) (observer)


Lesenswert?

Karl K. schrieb:

> ich möchte die Impulse zweier Stromzähler über zwei Pins am gleichen
> Port auswerten:

Es ist ganz sicher eine ganz beschissene Idee, das über 
Pinchange-Interrupts zu machen. Die Hardware der ollen ATmega bietet 
keine Möglichkeit dafür, das zuverlässig umzusetzen. Es gibt kein 
Strobe-Register für Pegelwechsel einzelner Pins und nur ein 
Interruptflag für den ganzen Port.

Es ist also völlig unmöglich, in einer Pinchange-ISR zuverlässig 
festzustellen, welche Pins des Ports gewackelt haben.

von Steve van de Grens (roehrmond)


Lesenswert?

Mit den Eingängen INT0 und INT1 (PD2, PD3) geht es vielleicht besser.

Allerdings scheinen die Signale langsam genug zu sein, dass die ISR auch 
bei obigem Ansatz keine Impulse verpasst. Der Sperrmechanismus wird wohl 
nötig sein, wie S.L. gerade sehr schön dargestellt hat.

: Bearbeitet durch User
von Ob S. (Firma: 1984now) (observer)


Lesenswert?

Steve van de Grens schrieb:

> Mit den Eingängen INT0 und INT1 (PD2, PD3) geht es vielleicht besser.

Das hilft natürlich. Man kann auch einen dieser beiden Pins über INTx 
betreiben und einen zweiten über den PCINT3. Dann geht das über einen 
Port, weil man über das Maskenregister dafür sorgen kann, dass der über 
INTx betriebene Pin den PCINT3 eben nicht auslöst. Und Natürlich sollte 
das dann auch keiner der sonstigen Pins des PortD tun.

Damit hat man dann die eindeutige Erkennbarkeit sichergestellt.

> Allerdings scheinen die Signale langsam genug zu sein, dass die ISR auch
> bei obigem Ansatz keine Impulse verpasst.

Du verstehst nicht, dass diese Signale voneinander unabhängig sind und 
deshalb rein zufällig auch mal gleichzeitig oder (viel gefährlicher) 
nahezu gleichzeitig toggeln können. Und: BUMM.

> Der Sperrmechanismus wird wohl
> nötig sein, wie S.L. gerade sehr schön dargestellt hat.

Das auf jeden Fall. Und zwar ordentlich, d.h.: für jeden Messkanal 
extra.

Bei diesen ganzen Problemen ist aber das Konzept an sich in Frage zu 
stellen!

Polling über eine Timer-IRQ löst praktisch alle Probleme viel 
einfacher...

von Veit D. (devil-elec)


Lesenswert?

Ob S. schrieb:

> Es ist ganz sicher eine ganz beschissene Idee, das über
> Pinchange-Interrupts zu machen. Die Hardware der ollen ATmega bietet
> keine Möglichkeit dafür, das zuverlässig umzusetzen. Es gibt kein
> Strobe-Register für Pegelwechsel einzelner Pins und nur ein
> Interruptflag für den ganzen Port.
>
> Es ist also völlig unmöglich, in einer Pinchange-ISR *zuverlässig*
> festzustellen, welche Pins des Ports gewackelt haben.

ähm hust. Natürlich kann man in der Pin-Change-ISR Flanken auswerten. 
Man muss nur selbst paar Zeilen Code schreiben.

von Ob S. (Firma: 1984now) (observer)


Lesenswert?

Veit D. schrieb:

> ähm hust. Natürlich kann man in der Pin-Change-ISR Flanken auswerten.
> Man muss nur selbst paar Zeilen Code schreiben.

Pinchange reagiert überhaupt nur auf Flanken. Insofern ist dein Einwurf 
schon  mal von Hause aus mal völlig idiotisch.

Das Problem ist halt nur: man kann nicht sicher sein, an welchem Pin 
diese Flanken erfolgt sind, weil es bei den ollen AtMega halt kein 
Register gibt, was man dazu befragen könnte (bei neuzeitlichen hingegen 
schon).

Kapierst du jetzt das Problem?

von Mi N. (msx)


Lesenswert?

Veit D. schrieb:
> Natürlich kann man in der Pin-Change-ISR Flanken auswerten.
> Man muss nur selbst paar Zeilen Code schreiben.

Ein Beispiel für 4 Kanäle habe ich gerade zur Hand:
1
#define MAX_KANAL 4
2
volatile unsigned int kanal_impulse[MAX_KANAL];  // akt. Zaehlerstand
3
4
ISR (PCINT1_vect)                       // fuer PCINT10...13
5
{
6
static char port_alt;
7
char port_neu, aenderung;
8
  port_neu = ~PINC;                     // negative Flanken vonPC.2 - PC.5
9
  port_neu &= (1<<PCINT10 | 1<<PCINT11 | 1<<PCINT12 | 1<<PCINT13);
10
11
  aenderung = (port_alt ^ port_neu) & port_neu; // aktive flanken ermitteln
12
  port_alt = port_neu;                  // neu zu alt
13
  if(aenderung) {
14
    if(aenderung & (1<<PINC2)) {        // PC.2
15
      kanal_impulse[0]++;
16
    }
17
    if(aenderung & (1<<PINC3)) {        // PC.3
18
      kanal_impulse[1]++;
19
    }
20
    if(aenderung & (1<<PINC4)) {        // PC.4
21
      kanal_impulse[2]++;
22
    }
23
    if(aenderung & (1<<PINC5)) {        // PC.5
24
      kanal_impulse[3]++;
25
    }
26
  }
27
}

von Ob S. (Firma: 1984now) (observer)


Lesenswert?

Mi N. schrieb:

> Ein Beispiel für 4 Kanäle habe ich gerade zur Hand:

Typisches Produkt von Leuten, die wirklich Null Ahnung von der Hardware 
haben, mit der sie da arbeiten.

Also: du versuchst da, über den Pegel, die eine ISR irgendwann mal 
liest irgendwelche Rückschlüsse auf Flanken zu ziehen, die in der 
Vergangenheit aufgetreten sind.

Das muss (und wird) oft scheitern. Man muss kein Genie sein, um das 
erkennen zu können. Insbesondere natürlich dann, wenn die Flankenwechsel 
von mehreren Eingänge in zeitlich Größenordnungen liegen, die mit dem 
Interruptsystem auf Grund der Latenz und Laufzeit der ISRs nicht mehr 
auflösbar sind.

von Ob S. (Firma: 1984now) (observer)


Lesenswert?

Ob S. schrieb:

> Das muss (und wird) oft scheitern. Man muss kein Genie sein, um das
> erkennen zu können. Insbesondere natürlich dann, wenn die Flankenwechsel
> von mehreren Eingänge in zeitlich Größenordnungen liegen, die mit dem
> Interruptsystem auf Grund der Latenz und Laufzeit der ISRs nicht mehr
> auflösbar sind.

Das gilt natürlich insbesondere für voneinander unabhängige 
Eingangssignale. Die köennen nämlich zufällig jederzeit auftreten. Also 
auch mal sehr kurz nacheinander.

Du kannst einfach nicht "in Hardware" denken. Sehr schlecht für 
µC-Programmierer...

von Peter D. (peda)


Lesenswert?

Ob S. schrieb:
> Typisches Produkt von Leuten, die wirklich Null Ahnung von der Hardware
> haben, mit der sie da arbeiten.

Bevor Du lästerst, solltest Du den Code vielleicht mal ansehen. Das XOR 
erkennt die Änderung und das AND die Richtung der Flanke. Perfekt, so 
mache ich das auch in meiner Entprellib.

Da die Signale recht langsam sein dürften, kann eine zusätzliche 
Entprellung/Entstörung aber nicht schaden. Das Schalten eines 
Leistungsverbrauchers in der Nähe könnte eine Pseudoflanke generieren. 
So ein AVR ist ja recht fix (50ns Puls bei 20 MHz).

von Veit D. (devil-elec)


Lesenswert?

Ob S. schrieb:

> Du kannst einfach nicht "in Hardware" denken. Sehr schlecht für
> µC-Programmierer...

Mir scheint du hast die Aufgabe überhaupt nicht verstanden. Hast du dich 
überhaupt schon mit dem zeitlichen Verlauf des Signals befasst um das es 
hier geht? Es geht hier jedenfalls nicht um die Erfassung von 
Nadelimpulsen.

von Steve van de Grens (roehrmond)


Lesenswert?

Ob S. schrieb:
> Kapierst du jetzt das Problem?

Ich verstehe, was du meinst. Hier können wir allerdings davon ausgehen, 
dass die Impulse bereits entprellt sind und lange genug anhalten, um per 
Software innerhalb der ISR vom I/O Pin abgefragt zu werden.

Beitrag #7590745 wurde vom Autor gelöscht.
von Karl K. (leluno)


Lesenswert?

Mi N. schrieb:

> Ein Beispiel für 4 Kanäle habe ich gerade zur Hand:
1
> #define MAX_KANAL 4
2
> volatile unsigned int kanal_impulse[MAX_KANAL];  // akt. Zaehlerstand
3
> 
4
> ISR (PCINT1_vect)                       // fuer PCINT10...13
5
> {
6
> static char port_alt;
7
> char port_neu, aenderung;
8
>   port_neu = ~PINC;                     // negative Flanken vonPC.2 - 
9
> PC.5
10
>   port_neu &= (1<<PCINT10 | 1<<PCINT11 | 1<<PCINT12 | 1<<PCINT13);
11
>

Vielen dank für das Beispiel - so ähnlich mache ich es.


was mich verwundert, ist die Änderung der Systematik.

PCINT10 ist das dritte Bit von PCMSK1. Es hat demzufolge den Wert 0 oder 
1 nicht 4. Die 4 ergibt sich erst durch die Stellung im Byte. Der 
Ausdruck (1<<PCINT10 | 1<<PCINT11 | 1<<PCINT12 | 1<<PCINT13) ergibt dann 
immer entweder 2 oder 0.

 Müsste es nicht richtig heißen
   port_neu &= (PCMSK1);

????

von Mi N. (msx)


Lesenswert?

Karl K. schrieb:
> Müsste es nicht richtig heißen
>    port_neu &= (PCMSK1);

J...-Nein.

Hier wird mit einer Konstanten maskiert, die nur die aktiven Kanäle 
zuläßt:
port_neu &= (1<<PCINT10 | 1<<PCINT11 | 1<<PCINT12 | 1<<PCINT13);
oder aufgelöst
port_neu &= 0x3c;

PCMSK1 ist ein Register im Controller, das auch noch weitere Bits 
gesetzt haben könnte, die an anderen Stelle ausgewertet werden könnten.
Alles im weichgespülten Konjunktiv ;-)

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.