Forum: Mikrocontroller und Digitale Elektronik Interrupt Arduino, mehr als 2 Eingänge nutzen


von Nanota N. (nanota)


Lesenswert?

Jemand eine Idee wie ich 4 analoge Eingänge des Arduino Nano mit dem 
InterruptPin0 (D2) verbinde, so dass ich bei jeder Änderung des 
Zustandes (HIGH >> LOW >> HIGH >> ...) ein Interrupt ausgelöst wird und 
ich gleichzeitig die Eingänge abfragen kann. (Arduino Nano hat nur 2! 
Interrupt-Pins)

Schwellwerte sind meines Wissens:

LOW: 0.2*VCC
HIGH: 0.6*VCC

Folgendes Skript wollte ich verwenden. Ist es zeitlich zu kritisch? Wie 
lange dauern die jeweiligen Funktionen? Was meint ihr?

Anstatt den analogen Pins könnte ich auch die Digitalen verwenden. Als 
externes Signal steht 0V und 5V pro Pin zur Verfügung. Lieber wären mir 
die analogen Pins, da die digitalen für ein Display verwendet werden 
sollen.

1
int LED = 13;
2
unsigned long interruptCounter = 0;
3
unsigned long now = 0;
4
unsigned long lastInterrupt = 0;
5
int debounceTime = 20; // Zeit in millis
6
unsigned long lastPrint = 0;
7
int intervalPrint = 5000; // Zeit in millis
8
9
int PinA0 = 0; //analog Pin A0
10
int PinA1 = 1; //analog Pin A1
11
int PinA2 = 2; //analog Pin A2
12
int PinA3 = 3; //analog Pin A3
13
14
int val0 = 0;
15
int val1 = 0;
16
int val2 = 0;
17
int val3 = 0;
18
19
int val0StateNew = 0;
20
int val1StateNew = 0;
21
int val2StateNew = 0;
22
int val3StateNew = 0;
23
24
int val0State = 0;
25
int val1State = 0;
26
int val2State = 0;
27
int val3State = 0;
28
29
int val0Counter = 0;
30
int val1Counter = 0;
31
int val2Counter = 0;
32
int val3Counter = 0;
33
34
void setup() {
35
  Serial.begin(9600);
36
  pinMode(LED, OUTPUT);    // LED Pin
37
  pinMode(2, INPUT);       // Pin 2 ist INT0
38
  digitalWrite(2, HIGH);   // interner Pull up Widerstand auf 5V
39
  attachInterrupt(0, interruptRoutine, LOW); // Pin 2 (INT 0) geht auf 0V (LOW) dann interruptRoutine aufrufen
40
  Serial.println(">>>--- new setup done --- --- --- --- ---");
41
}
42
43
void interruptRoutine() {
44
  if((millis() - lastInterrupt) > debounceTime) { 
45
    lastInterrupt = millis(); // letzter Interrupt merken
46
    // innerhalb der entprellZeit nichts machen
47
48
    val0 = analogRead(PinA0);    // read the input pin (takes about 100 microseconds)
49
    val1 = analogRead(PinA1);    // read the input pin (takes about 100 microseconds)
50
    val2 = analogRead(PinA2);    // read the input pin (takes about 100 microseconds)
51
    val3 = analogRead(PinA3);    // read the input pin (takes about 100 microseconds)
52
    interruptCounter++;
53
    val0Function();
54
    val1Function();
55
    val2Function();
56
    val3Function();
57
    digitalWrite(LED, !digitalRead(LED)); // LED umschalten, optische Anzeige
58
  }
59
}
60
void val0Function(){
61
    if((val0 >= 823) && (val0 <= 1023)) { 
62
      val0StateNew = 1;
63
    }
64
    if ((val0 >= 0) && (val0 <= 300)) {
65
      val0StateNew = 0; //pull-down transistor to GND needed
66
    }
67
    if (val0StateNew != val0State) {
68
      val0State = val0StateNew;
69
      val0Counter++;
70
      Serial.print("Counter 0: "), Serial.println(val0Counter);
71
    }
72
}
73
74
void val1Function(){
75
    if((val1 >= 823) && (val1 <= 1023)) { 
76
      val1StateNew = 1;
77
    }
78
    if ((val1 >= 0) && (val1 <= 300)) {
79
      val1StateNew = 0; //pull-down transistor to GND needed
80
    }
81
    if (val1StateNew != val1State) {
82
      val1State = val1StateNew;
83
      val1Counter++;
84
      Serial.print("Counter 1: "), Serial.println(val1Counter);
85
    }
86
}
87
88
void val2Function(){
89
    if((val2 >= 823) && (val2 <= 1023)) { 
90
      val2StateNew = 1;
91
    }
92
    if ((val2 >= 0) && (val2 <= 300)) {
93
      val2StateNew = 0; //pull-down transistor to GND needed
94
    }
95
    if (val2StateNew != val2State) {
96
      val2State = val2StateNew;
97
      val2Counter++;
98
      Serial.print("Counter 2: "), Serial.println(val2Counter);
99
    }
100
}
101
102
void val3Function(){
103
    if((val3 >= 823) && (val3 <= 1023)) { 
104
      val3StateNew = 1;
105
    }
106
    if ((val3 >= 0) && (val3 <= 300)) {
107
      val3StateNew = 0; //pull-down transistor to GND needed
108
    }
109
    if (val3StateNew != val3State) {
110
      val3State = val3StateNew;
111
      val3Counter++;
112
      Serial.print("Counter 3: "), Serial.println(val3Counter);
113
    }
114
}
115
116
void loop() {
117
  if((millis() - lastPrint) >= intervalPrint) {
118
    now = millis();
119
    Serial.println(">>--- new if (in void loop) --- --- --- --- --- ---");
120
    Serial.print("runtime (in millis): "), Serial.print(now), Serial.print(" >> (in min): "), Serial.println(now/1000/60);
121
122
    Serial.print("Counter 0: "), Serial.println(val0Counter);
123
    Serial.print("Counter 1: "), Serial.println(val1Counter);
124
    Serial.print("Counter 2: "), Serial.println(val2Counter);
125
    Serial.print("Counter 3: "), Serial.println(val3Counter);
126
127
    lastPrint = (lastPrint + intervalPrint);
128
  }
129
}

: Bearbeitet durch User
von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Nanota N. schrieb:
> Folgendes Skript
Das ist kein Skript...

Nanota N. schrieb:
> Ist es zeitlich zu kritisch?
Das haengt davon ab, was du fuer anforderungen hast...

Nanota N. schrieb:
> Wie lange dauern die jeweiligen Funktionen?
Schau ins listing und zaehl die Takte...

https://www.arduino.cc/en/Reference/AnalogRead
1
analogRead()
2
3
...It takes about 100 microseconds (0.0001 s) to read an analog input...
Damit braucht deine interruptRoutine() schon mal mindestens 400us fuer 
4x analogRead(). Dazu kommen 4 Aufrufe von valueFunction(), 2 Aufrufe 
von millis() und 1 Aufruf von digitalWrite() plus Interrupt ein- und 
aussprung.
valueFunction() wird ne halbe ewigkeit brauchen, wegen dem 
Serial.print().


Aufgeraeumter Code:
1
#include<stdint.h>
2
3
4
static const int16_t debounceTime = 20; // Zeit in millis
5
static const int16_t intervalPrint = 5000; // Zeit in millis
6
static const int16_t LED = 13;
7
8
static int16_t PinA[4] = {0, 1, 2, 3};
9
static int16_t values[4] = {0};
10
static int16_t valueNewState[4] = {0};
11
static int16_t valueState[4] = {0};
12
static int16_t valueCounter[4] = {0};
13
14
static uint32_t interruptCounter = 0;
15
static uint32_t now = 0;
16
static uint32_t lastInterrupt = 0;
17
static uint32_t lastPrint = 0;
18
19
20
static void valueFunction(uint8_t idx);
21
static void interruptRoutine();
22
23
24
void setup()
25
{
26
    Serial.begin(9600);
27
    pinMode(LED, OUTPUT);    // LED Pin
28
    pinMode(2, INPUT);       // Pin 2 ist INT0
29
    digitalWrite(2, HIGH);   // interner Pull up Widerstand auf 5V
30
    attachInterrupt(0, interruptRoutine, LOW);  // Pin 2 (INT 0) geht auf 0V
31
    // (LOW) dann interruptRoutine
32
    // aufrufen
33
    Serial.println(">>>--- new setup done --- --- --- --- ---");
34
}
35
36
37
void loop()
38
{
39
    if((millis() - lastPrint) >= intervalPrint) {
40
        now = millis();
41
        Serial.println(">>--- new if (in void loop) --- --- --- --- --- ---");
42
        Serial.print("runtime (in millis): ");
43
        Serial.print(now);
44
        Serial.print(" >> (in min): ");
45
        Serial.println(now/1000/60);
46
47
        for (uint8_t i = 0; i < 4; i++) {
48
            Serial.print("Counter ");
49
            Serial.print(i, DEC);
50
            Serial.print(": ");
51
            Serial.println(valueCounter[i], DEC);
52
        }
53
54
        lastPrint = (lastPrint + intervalPrint);
55
    }
56
}
57
58
59
void interruptRoutine()
60
{
61
    if((millis() - lastInterrupt) <= debounceTime) {
62
        return;
63
    }
64
65
    lastInterrupt = millis();   // letzter Interrupt merken
66
                                // innerhalb der entprellZeit nichts machen
67
68
    for (uint8_t i = 0; i < 4; i++) {
69
        values[i] = analogRead(PinA[i]);
70
    }
71
72
    interruptCounter++;
73
74
    for (uint8_t i = 0; i < 4; i++) {
75
        valueFunction(i);
76
    }
77
78
    digitalWrite(LED, !digitalRead(LED)); // LED umschalten, optische Anzeige
79
}
80
81
82
void valueFunction(uint8_t idx)
83
{
84
    if ((values[idx] >= 0) && (values[idx] <= 300)) {
85
        valueNewState[idx] = 0;
86
    } else if((values[idx] >= 823) && (values[idx] <= 1023)) {
87
        valueNewState[idx] = 1;
88
    }
89
90
    if (valueNewState[idx] != valueState[idx]) {
91
        valueState[idx] = valueNewState[idx];
92
        valueCounter[idx]++;
93
94
        Serial.print("Counter ");
95
        Serial.print(idx, DEC);
96
        Serial.print(": ");
97
        Serial.println(valueCounter[idx], DEC);
98
    }
99
}

von Nico W. (nico_w)


Lesenswert?

Nanota N. schrieb:
> Lieber wären mir die analogen Pins, da die digitalen für ein Display
> verwendet werden sollen.

Auch die analogen Pins können digital genutzt werden. Vielleicht 
solltest du mal ein grundlegendes Tutorial dazu suchen.

von Peter D. (peda)


Lesenswert?

Nanota N. schrieb:
> Anstatt den analogen Pins könnte ich auch die Digitalen verwenden.

Die analogen Pins sind auch digital verwendendbar, nur ADC6, ADC7 nicht. 
Beim ATmega328PB sind alle Pins digital verwendbar.

Alle IO-Pins können einen Pin-Change-Interrupt auslösen (ATmega328: 
PCINT0..PCINT23, ATmega328PB: PCINT0..PCINT27), siehe Datenblatt.

von Nanota N. (nanota)


Angehängte Dateien:

Lesenswert?

@Kaj G. - Vielen Dank für das Aufräumen des Codes. Der Serial.print in 
der valueFunction diente für mich nur zum Verständnis.

Was ich vor habe: Ich möchte 8 Wasseruhren (4x kalt, 4x warm) mithilfe 
eines CNY70 auslesen. Die Werte dann in eine Datenbank schreiben. Für 
-Zwei- Wasseruhren klappt das bereits (Pin D2, D3). Ab und an kommt es 
zu einem "verlorenen" Impuls, weil die eine ISR die andere ISR 
"behindert". Hochgerechnet auf das Jahr sind es ca. 40 Liter. Das ist 
für mich i.O..

Das Ganze lässt sich natürlich auch auf Gaszähler, Stromzähler bzw. 
jeglichen Impulsgeber ausweiten. Daher mein "Versuch" eine ISR für jeden 
Pin zu ermöglichen.

Schaltplan des CNY70 Moduls siehe Anhang. Quelle ist mir unbekannt.

@peda: Vielen Dank für deinen Hinweis, dass jeder Pin einen 
PIN-Change-Interrupt auslösen können soll.
Ich habe das versucht zu verstehen. Ist es korrekt, dass es drei Gruppen 
(beim Nano: PB, PC, PD) gibt und egal welcher Pin innerhalb einer Gruppe 
den Interrupt auslöst, es nicht klar ist, welcher Pin es war? Dann wäre 
ich leider nicht viel weiter wie jetzt.
1
| PCINT |  Uno/Nano/Mini  |   Mega/2560    | Leonardo/Micro | HL2 (8/16/32u2) |
2
| ----- | --------------- | -------------- | -------------- | --------------- |
3
|     0 |  8       (PB0)  | 53 SS   (PB0)  |    SS   (PB0)* |  0 SS   (PB0)*  |
4
|     1 |  9       (PB1)  | 52 SCK  (PB1)  |    SCK  (PB1)  |  1 SCK  (PB1)   |
5
|     2 | 10 SS    (PB2)  | 51 MOSI (PB2)  |    MOSI (PB2)  |  2 MOSI (PB2)   |
6
|     3 | 11 MISO  (PB3)  | 50 MISO (PB3)  |    MISO (PB3)  |  3 MISO (PB3)   |
7
|     4 | 12 MOSI  (PB4)  | 10      (PB4)  |  8/A8   (PB4)  |  4      (PB4)   |
8
|     5 | 13 SCK   (PB5)  | 11      (PB5)  |  9/A9   (PB5)  |  5      (PB5)   |
9
|     6 |    XTAL1 (PB6)* | 12      (PB6)  | 10/A10  (PB6)  |  6      (PB6)   |
10
|     7 |    XTAL2 (PB7)* | 13      (PB7)  | 11      (PB7)  |  7      (PB7)   |
11
| ----- | --------------- | -------------- | -------------- | --------------- |
12
|     8 | A0       (PC0)  |  0 RX   (PE0)* |                |         (PC6)*  |
13
|     9 | A1       (PC1)  | 15 RX3  (PJ0)* |                |         (PC5)*  |
14
|    10 | A2       (PC2)  | 14 TX3  (PJ1)* |                |         (PC4)*  |
15
|    11 | A3       (PC3)  |    NC   (PJ2)* |                |         (PC2)*  |
16
|    12 | A4 SDA   (PC4)  |    NC   (PJ3)* |                |         (PD5)*  |
17
|    13 | A5 SDC   (PC5)  |    NC   (PJ4)* |                |                 |
18
|    14 |    RST   (PC6)* |    NC   (PJ5)* |                |                 |
19
|    15 |                 |    NC   (PJ6)* |                |                 |
20
| ----- | --------------- | -------------- | -------------- | --------------- |
21
|    16 |  0 RX    (PD0)  | A8      (PK0)  |                |                 |
22
|    17 |  1 TX    (PD1)  | A9      (PK1)  |                |                 |
23
|    18 |  2 INT0  (PD2)  | A10     (PK2)  |                |                 |
24
|    19 |  3 INT1  (PD3)  | A11     (PK3)  |                |                 |
25
|    20 |  4       (PD4)  | A12     (PK4)  |                |                 |
26
|    21 |  5       (PD5)  | A13     (PK5)  |                |                 |
27
|    22 |  6       (PD6)  | A14     (PK6)  |                |                 |
28
|    23 |  7       (PD7)  | A15     (PK7)  |                |                 |
29
| ----- | --------------- | -------------- | -------------- | --------------- |


Bin beim Verstehen zufällig auf diese "lib" gestoßen: 
https://github.com/NicoHood/PinChangeInterrupt
...scheint sich bereits jemand die Mühe gemacht zu haben, mein Problem 
zu "lösen".

Habt ihr Erfahrungen mit der lib? Gibt es Alternativen oder einen ganz 
anderen Ansatz für meine Anforderung?

von chris (Gast)


Lesenswert?

Nanota N. schrieb:
> void val0Function(){
>     if((val0 >= 823) && (val0 <= 1023)) {
>       val0StateNew = 1;
>     }
>     if ((val0 >= 0) && (val0 <= 300)) {
>       val0StateNew = 0; //pull-down transistor to GND needed
>     }
>     if (val0StateNew != val0State) {
>       val0State = val0StateNew;
>       val0Counter++;
>       Serial.print("Counter 0: "), Serial.println(val0Counter);
>     }
> }

Innerhalb eines Interrupts Serial.print zu benutzen ist auch eine ganz 
schlechte Idee.
Da die UART-Schnittstelle ebenfalls interrupt-basiert arbeitet, wird es 
ab einer gewissen Anzahl von gesendeten Zeichen zu einem Deadlock 
kommen, wenn der Software-FIFO-Puffer der UART voll ist.

von Nanota N. (nanota)


Lesenswert?

Nanota N. schrieb:
> Der Serial.print in
> der valueFunction diente für mich nur zum Verständnis.

@chris: Danke für den Hinweis.

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.