Forum: Mikrocontroller und Digitale Elektronik Atmega 1284 Pin-Change Interrupt, funktioniert nicht


von Matthias (Gast)


Lesenswert?

Hallo zusammen,

Ich bin grade ahnungslos über die Art des Problems.
Ich möchte 7 Taster über die PinChange Interrupts auswerten.

Tatsächlich hat es in meinem Komplett-Programm mit den Tastern niemals 
so funktioniert dass der gewünschte Wert herausgekommen wäre, zudem 
hatte ich noch diverse sehr seltsame nebeneffekte mit den Tastern.

Daher habe ich den Code immer weiter reduziert bis nur noch dies hier 
bleibt:

Die Taster funktionieren leider überhaupt nicht.
Auf die LEDs kann ich jedoch die Wunschwerte ausgeben.

Könnt ihr mir vielleicht sagen, warum das mit den Tastern so absolut 
überhaupt nicht funktionieren kann?
Ich habe schon Stunden investiert ohne nun überhaupt noch eine Idee zu 
haben...

GetSwitch.c
1
/*PChange 
2
PC2 = 18 / SW5 
3
PC3 = 19 / SW4 
4
PC4 = 20 / SW3 
5
PC5 = 21 / SW2 
6
PC6 = 22 / SW1 
7
PC7 = 23 / SW0 --> PC = 0xfc
8
PD6 = 30 / SW6 --> PD = 0x40
9
*/
10
11
void SW_Init()
12
{
13
  PCICR  = 0x0c; //--> PC+PD 
14
  PCMSK0 = 0x00;
15
  PCMSK1 = 0x00;
16
  PCMSK2 = 0xfc;
17
  PCMSK3 = 0x40;
18
}


Main.c
1
#ifndef F_CPU
2
#define F_CPU 20000000UL
3
#endif
4
5
#include <avr/interrupt.h>
6
#include <avr/io.h>
7
#include <util/delay.h>
8
#include <string.h>
9
#include <stdlib.h>
10
#include "GetSwitch.h"
11
12
uint8_t switches =0x00;
13
14
void InituC()
15
{
16
  DDRA |= 0xff;   //D4/D5/D6/D7/RS/E/BEL/LED4 --> Alle Ausgang
17
  DDRB |= 0x0f;   //LED3/LED2/LED1/LED0       --> Als Ausgang, SPI unverändert
18
  DDRC |= 0x03;   //Switches 5/4/3/2/1/0 als eingang, I2C als Ausgang
19
  DDRD |= 0xb4;   //LED5/LED6/SPK/RGB-LED    --> Ausgang
20
  
21
  PORTA |= 0x00;  //All Outputs
22
  PORTB |= 0x00;  //All outputs/undefined
23
  PORTC |= 0xff;  //SW 0..5 with Pull-Up
24
  PORTD |= 0x40;  //SW 6 with Pull-Up
25
  SW_Init();    //Initialize Taster
26
  sei();          //Global IRQ-Enable
27
  //SW_Init();    //Initialize Taster
28
29
}
30
  
31
int main(void)
32
{
33
    InituC();
34
    while(1)
35
    {
36
  LEDs(switches);
37
    }
38
}
39
40
ISR (PCINT2_vect)
41
{
42
  if (!(PIND&0x40)) // Detect rising Edge
43
  {
44
    switches |= 0x40; // Reset by function-call
45
  }
46
  else
47
  {
48
    switches &= 0xbf; // Reset by function-call
49
  }  
50
}
51
ISR (PCINT3_vect)
52
{
53
  if (!(PINC&0x80))
54
  {
55
    switches |= 0x01;
56
  }
57
  else
58
  {
59
    switches &= 0xfe; // Reset by function-call
60
  }
61
  if (!(PINC&0x40))
62
  {
63
    switches |= 0x02;
64
  }
65
  else
66
  {
67
    switches &= 0xfd; // Reset by function-call
68
  }
69
  if (!(PINC&0x20))
70
  {
71
    switches |= 0x04;
72
  }
73
  else
74
  {
75
    switches &= 0xfb; // Reset by function-call
76
  }
77
  if (!(PINC&0x10))
78
  {
79
    switches |= 0x08;
80
  }
81
  else
82
  {
83
    switches &= 0xf7; // Reset by function-call
84
  }
85
  if (!(PINC&0x08))
86
  {
87
    switches |= 0x10;
88
  }
89
  else
90
  {
91
    switches &= 0xef; // Reset by function-call
92
  }
93
  if (!(PINC&0x04))
94
  {
95
    switches |= 0x20;
96
  }
97
  else
98
  {
99
    switches &= 0xdf; // Reset by function-call
100
  }
101
}

Viele Grüße,
Matthias

von Matthias (Gast)


Lesenswert?

Habe nun 2Taster die wie erwartet funktionieren und der Code sieht so 
aus:

Es sind PD6 und PC2. Die anderen haben keine Funktion.
Verständniss für das Problem habe ich dennoch keines??
1
#ifndef F_CPU
2
#define F_CPU 20000000UL
3
#endif
4
5
#include <avr/interrupt.h>
6
#include <avr/io.h>
7
#include <util/delay.h>
8
#include <string.h>
9
#include <stdlib.h>
10
11
uint8_t switches =0xff;
12
13
void InituC()
14
{
15
  DDRA |= 0xff;   //D4/D5/D6/D7/RS/E/BEL/LED4 --> Alle Ausgang
16
  DDRB |= 0x0f;   //LED3/LED2/LED1/LED0       --> Als Ausgang, SPI unverändert
17
  DDRC |= 0x03;   //Switches 5/4/3/2/1/0 als eingang, I2C als Ausgang
18
  DDRD |= 0xb4;   //LED5/LED6/SPK/RGB-LED    --> Ausgang
19
  
20
  PORTA |= 0x00;  //All Outputs
21
  PORTB |= 0x00;  //All outputs/undefined
22
  PORTC |= 0xff;  //SW 0..5 with Pull-Up // c-->f
23
  PORTD |= 0x40;  //SW 6 with Pull-Up
24
  SW_Init();    //Initialize Taster
25
  sei();          //Global IRQ-Enable
26
}
27
28
void SW_Init()
29
{
30
  PCICR  = 0x0c; //--> PC+PD 
31
  PCMSK0 = 0x00;
32
  PCMSK1 = 0x00;
33
  PCMSK2 = 0xfc;
34
  PCMSK3 = 0x40;
35
}
36
  
37
int main(void)
38
{
39
  InituC();
40
    while(1)
41
    {
42
    _delay_ms(300);
43
    LEDs(switches);
44
    }
45
}
46
47
ISR (PCINT2_vect)
48
{
49
  switches = 0x0f;
50
}
51
ISR (PCINT3_vect)
52
{
53
  switches = 0x55;
54
}

von Ralf G. (ralg)


Lesenswert?

Matthias schrieb:
> DDRC |= 0x03;   //Switches 5/4/3/2/1/0 als eingang, I2C als Ausgang
Matthias schrieb:
> PORTC |= 0xff;  //SW 0..5 with Pull-Up // c-->f

Die Zuweisungen mach nicht das, was im Kommentar steht.

Edit(h) meint noch:
Für bessere Übersicht würde ich die Schreibweise mit '0bnnnnnnnn' 
verwenden.

: Bearbeitet durch User
von HildeK (Gast)


Lesenswert?

Ralf G. schrieb:
> Matthias schrieb:
>> DDRC |= 0x03;   //Switches 5/4/3/2/1/0 als eingang, I2C als Ausgang
>
> Die Zuweisungen mach nicht das, was im Kommentar steht.

Doch, schon. SW 5..0 hängen an PC7...PC2.

> Matthias schrieb:
>> PORTC |= 0xff;  //SW 0..5 with Pull-Up // c-->f
Da hast du recht, aber es schadet auch nicht, dass die beiden freien 
Pins PC1 und PC0 auf HIGH gesetzt sind.
Eher unschön ist, dass es in PORTB und PORTD einige Pins gibt als 
Eingang ohne Pullup. Beides hat aber vermutlich nichts mit seinem 
Problem zu tun.

von Ralf G. (ralg)


Lesenswert?

HildeK schrieb:
> Doch, schon. SW 5..0 hängen an PC7...PC2.
Aaah, verstehe... steht weiter oben.

von S. Landolt (Gast)


Lesenswert?

Ohne mir das näher anzuschauen, die Standardfrage in solchen Fällen: 
JTAG? (liegt auf C2..5)

von Stefan F. (Gast)


Lesenswert?

Die Variable "switches" muss volatile sein, weil sie sowohl innerhalb 
von ISR als auch außerhalb von ihnen benutzt wird. Der Compiler neigt 
sonst dazu, sie in CPU Registern zu cachen und verpasst dadurch 
Änderungen durch die ISR.

Die internen Pull-Up Widerstände sind langfristig zu schwach. Die 
allermeisten Taster benötigen mehr Stromfluss, um langfristig gut zu 
kontaktieren. Deswegen empfehle ich im finalen Aufbau Widerstände 
zwischen 1kΩ und 4,7kΩ zu verwenden. Das Fachwort dazu ist "Wetting 
Current". Ist leider in den wenigstens Datenblättern angegeben.

Die Kontakte von Tastern prellen. Wenn der Interrupt ausgelöst wird, 
weil ein Kontakt geschlossen wurde, dann kann die darauf folgende 
Abfrage durchaus "Taste ist nicht gedrückt" liefern.

Ich empfehle dir, Taster in einem Timer-Interrupt abzufragen. Zum 
Beispiel alle 10ms. Dort kannst du auch sie auch bequem per Software 
entprellen.

Dazu hat der Peter einen umfangreichen Artikel samt vorbildlichem 
Beispielcode geschrieben 
https://www.mikrocontroller.net/articles/Entprellung.

von Matthias (Gast)


Lesenswert?

Hallo zusammen,
Vielen Dank für die kommentare :)

S. Landolt schrieb:
> Ohne mir das näher anzuschauen, die Standardfrage in solchen Fällen:
> JTAG? (liegt auf C2..5)

Der JTAG ist ausgefused.

Ich bin nun etwas weiter.

Habe die Funktion aller taster durch
LEDs(PINC>>2);
getestet, die jeweilige LED geht bei Tastensruck aus, ebenso bei Port D.
Somit sollte die HW an sich in orndung sein.

Auch habe ich getestet ob die Pin-Change IRQs überhaupt mal ausgeführt 
werden.
-->Ja sie werden ausgeführt, und dies auch für jeden Taster.

Habe es gefunden, die Interruptvektoren waren vertauscht!!!
Habe schon meiner Frau die Sache erklärt... und war am zweifeln ob ich 
nicht eine UND verknüpfung falsch verstehe...

Unglaublich.

Vielen Dank für eure Kommentare :)

Hier noch die schlussendliche lösung:
1
#ifndef F_CPU
2
#define F_CPU 20000000UL
3
#endif
4
5
#include <avr/interrupt.h>
6
#include <avr/io.h>
7
#include <util/delay.h>
8
#include <string.h>
9
#include <stdlib.h>
10
#include "GetSwitch.h"
11
12
uint8_t switches =0x00;
13
14
uint8_t get_sw(void)
15
{
16
  uint8_t ret = switches;
17
  switches = 0x00;
18
  return ret;
19
}
20
21
void set_sw(uint8_t inp)
22
{
23
  switches = switches | inp;
24
}
25
26
void InituC()
27
{
28
  DDRA |= 0xff;   //D4/D5/D6/D7/RS/E/BEL/LED4 --> Alle Ausgang
29
  DDRB |= 0x0f;   //LED3/LED2/LED1/LED0       --> Als Ausgang, SPI unverändert
30
  DDRC |= 0x03;   //Switches 5/4/3/2/1/0 als eingang, I2C als Ausgang
31
  DDRD |= 0xb4;   //LED5/LED6/SPK/RGB-LED    --> Ausgang
32
  
33
  PORTA |= 0x00;  //All Outputs
34
  PORTB |= 0x00;  //All outputs/undefined
35
  PORTC |= 0xff;  //SW 0..5 with Pull-Up // c-->f
36
  PORTD |= 0x40;  //SW 6 with Pull-Up
37
  SW_Init();    //Initialize Taster
38
  sei();          //Global IRQ-Enable
39
}
40
  
41
int main(void)
42
{
43
  InituC();
44
    while(1)
45
    {
46
    _delay_ms(300);
47
    LEDs(get_sw());
48
    }
49
}
50
51
ISR (PCINT3_vect)
52
{
53
  uint8_t pd = PIND;
54
  if (!(pd&0x40)) // Detect falling Edge
55
  {
56
    set_sw(0x40);
57
  }
58
}
59
60
ISR (PCINT2_vect)
61
{
62
  uint8_t pc = PINC;
63
  if (!(pc&0x80))
64
  {
65
    set_sw(0x01);
66
  }
67
  if (!(pc&0x40))
68
  {
69
    set_sw(0x02);
70
  }
71
  if (!(pc&0x20))
72
  {
73
    set_sw(0x04);
74
  }
75
  if (!(pc&0x10))
76
  {
77
    set_sw(0x08);
78
  }
79
  if (!(pc&0x08))
80
  {
81
    set_sw(0x10);
82
  }
83
  if (!(pc&0x04))
84
  {
85
    set_sw(0x20);
86
  }
87
}

Vielleicht bring das ja jemandem noch was...

von Matthias (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Die Variable "switches" muss volatile sein, weil sie sowohl innerhalb
> von ISR als auch außerhalb von ihnen benutzt wird. Der Compiler neigt
> sonst dazu, sie in CPU Registern zu cachen und verpasst dadurch
> Änderungen durch die ISR.

Vielen Dank für den Hinweis, hast recht baue ich gleich ein...

Die Funktionalität selbst möchte ich erstmal so nutzen...

von Stefan F. (Gast)


Lesenswert?

Matthias schrieb:
> Die Funktionalität selbst möchte ich erstmal so nutzen...

Dann musst du extern mit R/C Gliedern entprellen, falls es zuverlässig 
funktionieren soll.

von Matthias (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Dann musst du extern mit R/C Gliedern entprellen, falls es zuverlässig
> funktionieren soll.

Durch die Funktionenn set_sw und get_sw sehe ich es als entprellt an.
Es wird ja nicht immer abgefragt.

Mit den Pull-Ups hast du recht, ich verwende den I2C da liegen die Pins 
von taster und SDA diekt nebeneinander. Der Switch hat mehrfach ohne 
Betätigung ausgelöst. Nun mit Pull-Up passiert das nicht mehr :)

von Stefan F. (Gast)


Lesenswert?

Matthias schrieb:
> Nun mit Pull-Up passiert das nicht mehr :)

Das wäre ein weiterer Grund für externe stärkere Pull-Ups, hat mit dem 
von mir gemeinten Wetting Current allerdings nichts zu tun.

von HildeK (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Das wäre ein weiterer Grund für externe stärkere Pull-Ups,

IMHO sind die internen gut zum Verhindern, dass ein ungenutzter 
Eingangspin floatet. Für funktional genutzte Pins sollte man immer einen 
externen spendieren.

von Peter D. (peda)


Lesenswert?

Matthias schrieb:
> Durch die Funktionenn set_sw und get_sw sehe ich es als entprellt an.

Entprellen durch die sich rein zufällig ergebenden Programmlaufzeiten 
ist die mit Abstand schlechteste Entprellmethode.

Besser Du entprellst sie gleich richtig. Eine Entprelllib hat den 
Charme, daß sie fertige Ereignissse liefert und man sich fürderhin nie 
mehr drum kümmern muß.

Es ist aber Deine Sache, ob Du die faulen Eier drinlassen willst und 
erst später darüber stolperst. Je älter die faulen Eier, umso besser 
stinken sie, sprich umso mehr Aufwand macht es, sie zu beseitigen.

von c-hater (Gast)


Lesenswert?

Peter D. schrieb:

> Entprellen durch die sich rein zufällig ergebenden Programmlaufzeiten
> ist die mit Abstand schlechteste Entprellmethode.

Jepp. Das ergibt sich ja schon aus dem Problem selber. Wenn man logisch 
denken kann...

> Besser Du entprellst sie gleich richtig. Eine Entprelllib hat den
> Charme, daß sie fertige Ereignissse liefert und man sich fürderhin nie
> mehr drum kümmern muß.

Ja, wenn man faul und doof ist, ist das sicher die optimale Lösung.

Wenn's aber etwas spezieller wird (z.B.: benutze ein- und dieselbe 
Matrix für Taster-Polling und LED-Multiplexing), dann muß man doch 
wieder selber aktiv werden. Natürlich könnte man den vorhandenen (guten) 
Entprellcode dafür benutzen und entsprechend aufhübschen, aber dazu 
müsste man ihn erstmal verstanden haben.

Und das ist ja nun gerade wieder nicht das Ding der Dummen und Faulen...

Die sind halt immer irgendwie gearscht. Ungerechte Welt...

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.