Forum: Mikrocontroller und Digitale Elektronik AVR, Externer Interrupt 11 beim Atmega168 -> Howto-Problem


von Rolf S. (rollo)


Lesenswert?

Bin jetzt nach 2 Tagen erfolgloser Suche soweit, dass ich dringend auf 
Eure geneigte Expertise angewiesen bin.

Ich betreibe einen Atmega168 bei ext. 20MHz und benötige zur Steuerung 
vier externe Interrupts, die aufgrund der Layoutvorgaben nur an Pins 
PCINT8/ PCINT9/ PCINT10/ PCINT11 angelegt werden können.

Die Signale dort kommen auf TTL-Pegel mit einem Abstand von ca. 1 bis 
100ms.

Ich verwende als Entwicklungsumgebung BASCOM-AVR in der Version 
1.11.8.4. und kann damit nur Routinen bis ENABLE INT7 definieren 
(Handbücher und Datenblätter habe ich schon rauf, runter und durch).

So wie ich es bisher verstanden habe, muss man für die "höheren" 
Interrupts so etwas wie Vektoren und/oder Masken ensetzen - und hier 
liegt mein Problem
   -> Wie definiert man in BASCOM AVR BASIC die Routinen für Interrupts 
oberhalb von PCINT7?

Für einen kleinen Hirnschubser in die richtige Richtung wäre ich schon 
dankbar.

Rolf

von Johannes M. (johnny-m)


Lesenswert?

Erstmal sind die Pin Change Interrupts etwas anderes als die "normalen" 
externen Interrupts, da sie erstens grundsätzlich auf jeden Pegelwechsel 
reagieren (also nicht konfigurierbar sind) und da jeder Pin Change 
Interrupt von einer bestimmten Anzahl von Pins ausgelöst werden kann 
(externe Interrupts nur durch jeweils einen einzigen Pin). Um zu 
definieren, auf welche Pins der Interrupt reagieren soll, muss man die 
Pins maskieren.

INT0 usw. bezieht sich auf die externen Interrupts, und davon hat der 
ATMega168 nur zwei Stück, nämlich INT0 und INT1. INT2...INT7 gibt es da 
gar nicht.

Die Pins PCINT7...0 sind dem Interrupt PCI0 zugeordnet. Steht aber alles 
im Datenblatt...

von Peter D. (peda)


Lesenswert?

Rolf S. wrote:
> Ich betreibe einen Atmega168 bei ext. 20MHz und benötige zur Steuerung
> vier externe Interrupts, die aufgrund der Layoutvorgaben nur an Pins
> PCINT8/ PCINT9/ PCINT10/ PCINT11 angelegt werden können.

Dann mußt Du einen Handler für PCINT1 definieren und natürlich die 
entsprechenden Enablebits in PCMSK1 setzen.

Und in dem Handler mußt Du die 4 Inputs testen und mit dem vorherigen 
Zustand vergleichen, um zu sehen, auf welchem die Flanke war und in 
welche Richtung.

Du hast also nur einen Handler für alle 4 Eingänge und mußt darin 
entscheiden, was bei welchem zu tun ist.

Schau einfach mal ins Datenblatt, da steht alles drin.


Peter

von Rolf S. (rollo)


Lesenswert?

Besten Dank für Eure schnellen Hints -> ich ahne schon etwas Licht am 
Ende des Tunnels....

von Rolf S. (rollo)


Lesenswert?

> Besten Dank für Eure schnellen Hints -> ich ahne schon etwas Licht am
> Ende des Tunnels....

Nachfolgend mein Ansatz zur Lösung -> funktioniert leider nur zu 99%. Es 
ist mir letzendlich nicht gelungen zu ermitteln, welcher der Pins in 
einem Port den PCINT ausgelöst hat...
1
' -----------------------------------------------------------------------
2
' PIN CHANGE INTERRUPTS / ATMEGA168 / BASCOM
3
' siehe auch ATMEL-Datenblatt Seiten 70 und 345
4
' -----------------------------------------------------------------------
5
6
$regfile = "m168def.dat"
7
$crystal = 20000000
8
$hwstack = 80
9
$swstack = 80
10
$framesize = 80
11
' -----------------------
12
Dim Countx As Integer
13
Dim Iportval As Byte
14
Dim Iportvalold As Byte
15
Dim Iportdif As Byte
16
' -----------------------
17
Declare Sub Blinksig
18
' -----------------------
19
Config Lcdpin = Pin , Db4 = Portb.0 , Db5 = Portb.1 , Db6 = Portb.2 , Db7 = Portb.3 , E = Portb.4 , Rs = Portb.5
20
Config Lcd = 16 * 1a                                        ' LCD-Modul 1x16
21
22
23
' ------------------------
24
' Port-Settings (Beispiel)
25
' ------------------------
26
Sensor_1 Alias Portc.3                                      ' PCINT 11
27
Sensor_2 Alias Portc.2                                      ' PCINT 10
28
Sensor_3 Alias Portc.1                                      ' PCINT  9
29
Sensor_4 Alias Portc.0                                      ' PCINT  8
30
Config Sensor_1 = Input
31
Config Sensor_2 = Input
32
Config Sensor_3 = Input
33
Config Sensor_4 = Input
34
Sensor_1 = 1                                                ' Pullups
35
Sensor_2 = 1
36
Sensor_3 = 1
37
Sensor_4 = 1
38
' ------------------------
39
Actor_1 Alias Portc.5
40
Config Actor_1 = Output
41
' ------------------------
42
43
44
45
' ---------------------------
46
' Interrupt settings mit
47
' Besonderheiten in Bascom
48
' ---------------------------
49
' PCINT1 ist NICHT der IC-Pin "PcInt1",
50
' sondern der "reaction Block" für logiclevel-changes
51
' an PortC (aktive Pins siehe PCMSK1)
52
53
On Pcint1 Isr_1
54
Enable Pcint1
55
56
' PCINT8..11 als ext. Interrupt-Pins maskieren.
57
' Wenn an einem dieser Pins ein Logiklevel WECHSELT,
58
' dann wird der Interrupt "Pcint1" ausgelöst
59
60
Pcmsk1 = &B00001111
61
62
63
64
' PCIE1-Block enable
65
' Die Pins PCINT8..14 werden hier als Block freigeschaltet
66
67
Pcicr = &B00000010
68
69
70
71
72
' -----------------------
73
' Initialisieren
74
' -----------------------
75
Countx = 0
76
Iportval = 0
77
Iportvalold = 0
78
' -----------------------
79
80
' +++++++++++++++++++++++++++++
81
' Main loop
82
' +++++++++++++++++++++++++++++
83
Cls
84
Lcd "<Bereit>"
85
Enable Interrupts
86
Do
87
   ' godot...
88
Loop
89
End
90
' +++++++++++++++++++++++++++++
91
92
93
' -----------------------
94
' Subroutine
95
' -----------------------
96
Sub Blinksig
97
   Actor_1 = 0
98
   Waitms 5
99
   Actor_1 = 1
100
End Sub
101
' -----------------------
102
103
104
' ---------------------------*******************************************
105
' Interrupt service routine
106
' ---------------------------*******************************************
107
Isr_1:
108
109
   Disable Interrupts
110
111
   ' Wert vom Port holen und Mögl. betroffene Bits ausfiltern
112
   Iportval = Portc And &B00001111
113
114
   ' Änderungen zum vorherigen Wert ermitteln
115
   Iportdif = Iportvalold Xor Iportval
116
117
   ' Info
118
   Call Blinksig
119
   Countx = Countx + 1
120
   If Countx > 299 Then
121
      Countx = 0
122
   End If
123
   Cls
124
   Lcd ">" ; Bin(iportdif) ; " >" ; Countx
125
126
   ' Aktuellen Portwert für nächsten Interrupt merken
127
   Iportvalold = Iportval
128
129
   Enable Interrupts
130
131
   Return
132
133
' -----------------------

von Peter D. (peda)


Lesenswert?

Rolf S. wrote:
> Nachfolgend mein Ansatz zur Lösung -> funktioniert leider nur zu 99%.

Darunter kann sich niemand etwas vorstellen.

Was wird erwartet, was ist die Abweichung dazu?


Der Code sieht prinzipiell o.k. aus, außer den riesigen Wartezeiten im 
Interrupt drin (lahme LCD-Ausgabe + 5ms Warten).
Interrupts sollten kurz sein, langsame Sachen sollte man nur im Main 
machen.

Wenn Du für über 5ms sämtliche CPU-Aktivität im Interrupt blockierst, 
kanns Du natürlich nie Pulse mit 1ms Abstand erkennen.


Peter

von Rolf S. (rollo)


Lesenswert?

Danke für die Response...

1. Die 100% Lösung wäre, wenn ich erkennen könnte, an welchem Pin der 
Level-Change stattgefunden hat. Dies habe ich bisher noch nicht 
hinbekommen.

2. Die LED war nur zu Testzwecken dabei. Natürlich darf keine Bremse in 
der ISR bleiben.

Rolf

von Bert (Gast)


Lesenswert?

Hallo,
definiere parallel zu den PCINT-Pins die gleichen Pins als normale 
Input-Eingänge und frage Sie in der Interrupt-Routine auf Low ab und 
setze dann entsprechenden Flags, die Du auswerten kannst. So gelangst Du 
zu den auslösenden PCINT.
MfG
Bert

von John Doe (Gast)


Lesenswert?

Bert schrieb:
> Hallo,
> definiere parallel zu den PCINT-Pins die gleichen Pins als normale
> Input-Eingänge und frage Sie in der Interrupt-Routine auf Low ab und
> setze dann entsprechenden Flags, die Du auswerten kannst. So gelangst Du
> zu den auslösenden PCINT.
> MfG
> Bert


Sonst gehts noch???

von Dennis K. (scarfaceno1)


Lesenswert?

Vielleicht nicht elegant aber es funktioniert.
Bei mir sieht das so aus:
1
char test (void){
2
int_mask = PCMSK0;
3
temp = PINB;
4
temp &= int_mask;
5
return temp;
6
}

und im Interrupt:
1
ISR(PCINT0-vect){
2
char x=0,y=0;
3
temp_old = temp;
4
test();
5
x = temp;
6
x &= 4;
7
y = temp_old;
8
y &= 4;
9
10
if (x==y)
11
{
12
    // Code falls es PB3 war...
13
}
14
}


Und nach dem veröffentliche erst gesehen, wie alt der Post ist....

: Bearbeitet durch User
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.