m328 - isr - zwei Pins auswerten

OP #7590295
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.

#7590306
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.

OP #7590327
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
}
OP #7590342
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
}
Gast #7590349
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
}
(Firma: 1984now) #7590479
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.

Gast #7590483
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.

(Firma: 1984now) #7590500
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...

#7590567
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.

(Firma: 1984now) #7590580
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?

#7590591
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
}
(Firma: 1984now) #7590597
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.

(Firma: 1984now) #7590603
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...

#7590697
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).

Beitrag #7590745 wurde von einem Moderator gelöscht.
OP #7607399
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);

????

#7607419
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 ;-)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren