Forum: Mikrocontroller und Digitale Elektronik Interupt schaltet nicht alles ab?


von Sven W. (sickone4)


Lesenswert?

Ich hätte da mal ein Problem :)

ich habe mir relativ simpel mit 4 tastern folgendes szenario 
programmiert:

taster 1-3 sind 3 verschiedene programme
taster 0 ist der interupt "stop" taster


klappt auch alles. die interuptroutine wird auch ausgeführt.

in den 3 programmen habe ich einfach eine LED bissi blinken gelassen mit 
nem delay

und eben genau das schaltet die interuptroutine nicht aus?

werden unterprogramme von der routine gänzlich ausgenommen?

die unterprogramme sind kopiert und mit absicht "nicht" ordentlich 
programmiert, da das nur dummies sind.
ich möchte, bevor ich anfange die unterprogramme zu schreiben die 
aufrufroutinen der programme fertig und ordentlich haben. der stop 
taster soll immer zu jeder zeit ein programm beenden können, analog 
einer NOTAUS-Software (NOTAUS muss ja hardwaretechnisch sein nicht 
softwaretechnisch)

hier mein code:
1
#define  F_CPU 3686400UL                // CPU Takt, externer Quarz
2
#define  BAUD 9600                      // Baudrate = 9600
3
#include <avr/io.h>                     // Einbinden der Bib In/Out
4
#include <util/delay.h>                 // Einbinden der Bib Delay
5
#include <stdint.h>                     // Einbinden der Bib StandardInteger
6
#include <avr/interrupt.h>              // Einbinden der Bib Interrupt
7
8
// Variablendeklaration "Datentypen" -------------------------------------------------------------------------------
9
                         
10
11
12
// Variablendeklaration "Definitionen" -----------------------------------------------------------------------------
13
14
// Bedienpanel
15
16
#define bedienpanel PIND                      // Tasterport
17
#define S0 bit_is_clear(bedienpanel,2)        // S0 = SCHALTER Stop << Interupt
18
#define S1 bit_is_clear(bedienpanel,3)        // S1 = Taster-Start
19
#define S2 bit_is_clear(bedienpanel,4)        // S2 = Taster Referenzpunktfahrt
20
#define S3 bit_is_clear(bedienpanel,5)        // S3 = Taster RESERVE 
21
22
                
23
// LED´s
24
25
#define LED PORTB                             // LED Port
26
#define Stop (1<<0)
27
#define Prog (1<<1)
28
#define RefPunkt (1<<2)
29
#define RESERVE (1<<3)
30
31
32
// Unterprogramm "Interrupt-STOP" ----------------------------------------------------------------------------------
33
// -----------------------------------------------------------------------------------------------------------------
34
35
ISR(INT0_vect)                                // Interrupt 0 Sub-Routine (ISR)
36
{
37
38
  while ( S0 )                                // solange Stop gedrückt ist:
39
  {
40
    PORTB = 0x00;                             // lösche PORTB und
41
    LED |= Stop;                              // setze STOP LED
42
  }
43
  
44
  LED &= ~Stop;                               // lösche Stop LED
45
  
46
}    
47
48
// Unterprogramm "DELAY" -------------------------------------------------------------------------------------------
49
// -----------------------------------------------------------------------------------------------------------------
50
51
void warten_ms(unsigned int ms)
52
{
53
  while(ms--)
54
  _delay_ms(1);
55
}
56
  
57
// Unterprogramm "Progstart" ---------------------------------------------------------------------------------------
58
// -----------------------------------------------------------------------------------------------------------------
59
60
int ProgStart()
61
{
62
  int i = 0;
63
  
64
  for (i = 0; i < 5;)
65
  {      
66
    LED |=  (1<<4);
67
    warten_ms(100);
68
    LED &= ~(1<<4);
69
    if (i <4 )
70
    { warten_ms(100);}
71
    
72
    i++;    
73
  }
74
  
75
  LED &= ~Prog;                               // Ausschalten der "Programm" LED
76
}
77
78
// Unterprogramm "RefPunktfahrt" -----------------------------------------------------------------------------------
79
// -----------------------------------------------------------------------------------------------------------------
80
81
int RefPunktfahrt()
82
{
83
  int i = 0;
84
  
85
  for (i = 0; i < 10;)
86
  {
87
    LED |=  (1<<4);
88
    warten_ms(50);
89
    LED &= ~(1<<4);
90
    if (i <9 )
91
  { warten_ms(50);}
92
    
93
    i++;
94
  }
95
  
96
  LED &= ~RefPunkt;                           // Ausschalten der "RefPunkt" LED
97
}
98
99
// Unterprogramm "RESERVE" -----------------------------------------------------------------------------------------
100
// -----------------------------------------------------------------------------------------------------------------
101
102
int RESERVEa()
103
{
104
  int i = 0;
105
  
106
  for (i = 0; i < 10;)
107
  {
108
    LED |=  (1<<4);
109
    warten_ms(200);
110
    LED &= ~(1<<4);
111
    if (i <9 )
112
  { warten_ms(200);}
113
    
114
    i++;
115
  }
116
  
117
  LED &= ~RESERVE;                            // Ausschalten der "RESERVE" LED
118
}
119
120
121
// Hauptprogramm "MAIN" --------------------------------------------------------------------------------------------
122
// -----------------------------------------------------------------------------------------------------------------
123
124
int main(void)
125
126
{
127
128
// Portdeklaration -------------------------------------------------------------------------------------------------
129
  
130
  DDRB   = 0b11111111;    // Alle auf Ausgang
131
                
132
  DDRC   = 0b00000000;    // Alle auf Eingang  
133
  PORTC  = 0b11111111;    // Alle Pullups gesetzt    
134
    
135
  DDRD   = 0b00000000;    // Alle auf Eingang
136
  PORTD  = 0b11111111;    // Alle Pullups gesetzt, D2 für Interupt0  
137
  
138
  LED &= ~Stop;           // Stop_LED ausschalten
139
  LED &= ~Prog;           // Programm LED ausschalten
140
  LED &= ~RefPunkt;       // RefPunkt LED ausschalten
141
  LED &= ~RESERVE;        // RESERVE  LED ausschalten
142
      
143
  GICR  = 0b01000000;     // Interrupt 0 (INT0) aktivieren
144
  MCUCR = 0b00000010;     // fallende Flanke für Interrupt 0
145
      
146
  sei();                  // alle Interruptquellen freigeben      
147
      
148
149
  while(1)
150
  {               
151
    
152
// Tasterabfrage ---------------------------------------------------------------------------------------------------
153
154
/* 
155
    Der Stop-Taster an Port D2 wird im ganzen Programm niemals abgefragt. Das Konstrukt if ( bit_is_clear ( PIND,2 )) existiert 
156
    hier also nicht. Der Stop-Taster wird „hardwaremäßig“ abgefragt.
157
*/
158
                           
159
    if ((S1) && !( LED & ( Prog | RefPunkt | RESERVE )))      // Taster Start Taster Verriegelung
160
      { 
161
        LED |= Prog;                                          // LED Programm "läuft" 
162
        ProgStart();                                          // Programmaufruf ProgStart
163
      }  
164
        
165
    if ((S2) && !( LED & ( Prog | RefPunkt | RESERVE )))      // Taster Referenzpunkt-Fahrt Taster Verriegelung
166
      { 
167
        LED |= RefPunkt;                                      // LED Referenzpunktfahrt "läuft"
168
        RefPunktfahrt();                                      // Programmaufruf Refpunktfahrt
169
      }      
170
171
    if ((S3) && !( LED & ( Prog | RefPunkt | RESERVE )))      // Taster RESERVE Taster Verriegelung
172
      {                        
173
        LED |= RESERVE;                                       // LED Reserve
174
        RESERVEa();                                           // Programmaufruf RESERVEa
175
      }  
176
            
177
    if ( LED & ( Prog | RefPunkt | RESERVE ))                 // Testroutine "wenn irgendein Programm läuft tue irgendwas"
178
      { ; }
179
                                
180
  }
181
        
182
}        
183
184
// -----------------------------------------------------------------------------------------------------------------

von Karl H. (kbuchegg)


Lesenswert?

Sven Weinmann schrieb:

> und eben genau das schaltet die interuptroutine nicht aus?

Was meinst du mit 'ausschalten'.

Wenn der Interrupt kommt, unterbricht er den normalen Programmfluss. In 
deiner ISR wird eine 'Unterbrechung' gemacht, solange wie der Taster 
gedrückt ist. Ist diese Bedingung nicht mehr gegeben, dann geht es genau 
an der Stelle weiter, an der der Haupt-Programmfluss unterbrochen wurde.

> werden unterprogramme von der routine gänzlich ausgenommen?

Nö.
Interrupt ist Interrupt. Egal was der µC in der Hauptroutine macht, 
sobald der Interrupt kommt, unterbricht die ISR diesen Ablauf.


Es wäre vernünftig, wenn du sagen würdest, was du siehst und wie sich 
das von dem unterscheidet, was du erwartet hättest.

von holger (Gast)


Lesenswert?

>Es wäre vernünftig, wenn du sagen würdest, was du siehst und wie sich
>das von dem unterscheidet, was du erwartet hättest.

Möglicherweise hat er gerade entdeckt das Taster prellen
und eine Abfrage per INT0 einfach nur Gurkenkram ist;)

von Karl H. (kbuchegg)


Lesenswert?

holger schrieb:
>>Es wäre vernünftig, wenn du sagen würdest, was du siehst und wie sich
>>das von dem unterscheidet, was du erwartet hättest.
>
> Möglicherweise hat er gerade entdeckt das Taster prellen
> und eine Abfrage per INT0 einfach nur Gurkenkram ist;)

Das kann durchaus sein, denn

> Ich hätte da mal ein Problem :)

das Problem ist nicht der Taster (ok, er ist auch ein Problem) sondern 
der ganze Programmaufbau. Solange er sich nicht von _delay_ms löst, ist 
das relativ sinnlos. So wird das nichts mit Blinksequenzen, die 
unterbrechbar und/oder zu jedem Zeitpunkt wechselbar sind.

von oldmax (Gast)


Lesenswert?

Hi Swen
>die unterprogramme sind kopiert und mit absicht "nicht" ordentlich
>programmiert, da das nur dummies sind
Tja, so liebt man diese Fragestellung.... ich sag es mal mit anderen 
Worten.... Ich hab da was hingeschludert und es funktioniert nicht so 
wie gedacht...
Fang mal da an, wo (fast) alle Programmierer mit beginnen:
"Ein Projekt".
Das wird beschrieben, mit den Funktionen die es erledigen soll. Das ist 
erst einmal Skizzen auf Papier malen. Wenn du dir dann im klaren bist, 
was dein Programm erfüllen soll, dann kommt Abschnittweise Programm 
zusammen. Möglichst kleine prüfbare Schritte und erst weiter, wenn 
Aufgabe erfüllt. So dahingeschluderte Funktionen machen nicht wirklich 
Sinn. Und der Lerneffekt, na ja, mal ehrlich, außer  "das geht nicht" 
ist nur geringe Erkenntnis gegeben. SChau dir mal deine Doku an. Da 
steht u.a. folgendes:

>Der Stop-Taster an Port D2 wird im ganzen Programm niemals abgefragt. Das 
>Konstrukt if ( bit_is_clear ( PIND,2 )) existiert
>    hier also nicht. Der Stop-Taster wird „hardwaremäßig“ abgefragt.
 Nun jeder Taster ist Hardware und wenn er an einem µC hängt hängt, dann 
wird er, wenn er irgendwo gebraucht wird auch softwaremäßig erfasst. Ob 
in einer ISR oder im Polling, das ist erst mal völlig irrelevant. Ein 
Not-Aus ist Hardware und schaltet z. B. die Leistung weg. So wie dein 
Lichtschalter das Licht. Das ist Hardware.
Solche Kommentare sind irreführend und du selbst gehst dem auf dden 
Leim. Versuche Interrupts zu verstehen und dann erkennst duu auch, warum 
hier alle das als Blödsinn abtun. ( na ja, es gibt auch hin und wieder 
mal einen Versuch, diese Vorgehensweise zu rechtfertigen... ) 
Normalerweise läßt du ein Programm immer durch die Hauptschleife rennen. 
Unterwegs schaust du, ob es irgendwelche Ereignisse gibt, die erledigt 
werden müssen. Nur so kannst du sicherstellen, das jeder Block auch so 
funktioniert, wie er soll. Nimmst du ein "Delay" in dein Programm und 
bremst den Ablauf, ist ein oft "unerklärliches Verhalten" die Folge.
Auch wenn ich nichts mit C am Hut hab, ist nach meiner Meinung die 
Umsetzung einer Aufgabe in C genauso aufzubauen, wie sie sinnvollerweise 
in anderen Sprachen gemacht wird.
Daher mein Tip:
Du willst mit einfachen Übungen Erkenntnisse erlangen. Gut, aber dann 
lerne aus den Ergebnissen. Und wenn du das nicht verstehst, dann denk 
mal nach.
>die unterprogramme sind kopiert und mit absicht "nicht" ordentlich
>programmiert, da das nur dummies sind
Etwas kopiertes ist nicht unbedingt erfolgreiches Lernen.
Einen "schnellen Erfolg" wollen zwar viele, aber glaub mir, es dauert 
eben eine Zeit, bis man versteht, wie es funktioniert.
Gruß oldmax

von Sven W. (sickone4)


Lesenswert?

hallo leute,

hm ich bin ein wenig sprachlos :)

da lösche ich den halben quellcode hier raus, um es übersichtlicher zu 
machen und dann kommen solche antworten :)

also zunächst habe ich genau so begonnen, kleine programmabschnitte zu 
programmieren und zu testen. diese sind oben nicht aufgelistet, weil die 
ADC oder die motorsteuerung oder oder oder da nichts zu suchen haben.

meine frage ging dahin, ob ein interupt ALLES unterbricht oder nur die 
main routine.

ich entnehme euren antworten, dass es alles unterbricht. aber ich habe 
in einem beitrag auch gelesen:

Karl Heinz Buchegger schrieb:
> Solange er sich nicht von _delay_ms löst, ist
> das relativ sinnlos. So wird das nichts mit Blinksequenzen, die
> unterbrechbar und/oder zu jedem Zeitpunkt wechselbar sind.

was bedeutet das?
baut man blinksequenzen nicht mit delay? sondern mit for-schleifen?
und kann man delay nicht unterbrechen? was wäre denn (weiß ja nicht ob 
ich delay im späteren verlauf irgendwo benutze), eine alternative.
das delay sollte mir an den drei stellen nur simuliern, dass das 
unterprogramm läuft. es benötigt ja jedes eine zeit x, die ich mir 
irgendwie darstellen wollte, um die interupt routine zu testen.

daher finde ich die aussage:

oldmax schrieb:
> Etwas kopiertes ist nicht unbedingt erfolgreiches Lernen.
> Einen "schnellen Erfolg" wollen zwar viele, aber glaub mir, es dauert
> eben eine Zeit, bis man versteht, wie es funktioniert.

bisschen doof, da ich oben extra schrieb, dass es absichtlich dummie 
codes sind und kein quellcode. hätte ich das anständig gemacht hätte ich 
mir für´s blinken ein unterprogramm geschrieben und hätte mir beim 
aufruf, die anzahl und die zeit mit übergeben, statt es dreimal gleich 
hinzuschreiben. das meinte ich eigentlich damit :)

nun hoffe ich aber, dass wir auf einer welle sind^^ und ich das mit dem 
delay irgendwie verstehen kann.

sollte/gibt es eine möglichkeit wenn ein interupt ausgeführt wird ALLES 
zu beenden, egal was. unt mit beenden meine ich nicht unterbrechen?

gruß sven

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Merke: Delay verschwendet Rechenzeit.
Zeitliche Abläufe werden mit Timern und Interrupts gemacht.

von spontan (Gast)


Lesenswert?

Etwa chaotisch Deine Beschreibungen.

Ich interpretiere das mal so:

Wenn der Interrupt ausgelöst wurde, sollen die grade laufenden 
Unterprogramme beendet werden.


Lösung:

Im Interruptprogramm vermerkst Du daß es aufgerufen wurde in einer 
Variablen.

- In den Unterprogrammen mußt Du diese Variable abfragen, um zu 
erkennen, daß der Interrupt ausgelöst wurde.
- Dann beendest Du das Unterprogramm durch einen Rücksprung und sonstige 
Maßnahmen, die noch zu tun sind.


Jetzt wirds vielleicht sinnig, daß eine Delay bei dieser 
Aufgabenstellung horrender Blödsinn ist. Im Delay merkst Du nix vom 
Interrupt, auch wenn er kommt.

von spontan (Gast)


Lesenswert?

Interrupt mit Tasten ist immer eine blöde Idee. Begründung kommt gern, 
ist mir grad zu viel zu schreiben.

Viel schlauer ist es die Stop-Taste nicht an den Interrupt zu koppeln.


- Bau keine Delays ein, so das die Main flott durchlaufen wird.
- Frag die Stop-Taste im Main ab.
- Reagier auf die Stop-Taste in dem die Unterprogramme nicht mehr 
ausgeführt werden, die Motoren gestoppt werden, und was sonst noch alles 
zu tun ist.

von Steel (Gast)


Lesenswert?

Schmeiß das Programm weg, kauf dir ein Buch über uC Programmierung oder 
arbeite einige Tutorials durch und fang dann von vorne an.

von Ulli (Gast)


Lesenswert?

Hallo Sven,

es gibt keine Möglichkeit das Progrramm zu beenden im Sinne von "alles 
steht".
Aber wie "spontan" geschrieben hat: Setze in der Inerruptroutine ein 
Flag, mach in der Main abhängig von diesem Flag das was gemacht werden 
muss (zB. Ausgänge a schal
ten usw.)
Und dann rufe eine Endlosschleife auf. Diese läuft dann so lange im 
Kreis bis ein Reset kommt.

mfG Ulli

von Karl H. (kbuchegg)


Lesenswert?

Sven Weinmann schrieb:
> hallo leute,
>
> hm ich bin ein wenig sprachlos :)
>
> da lösche ich den halben quellcode hier raus, um es übersichtlicher zu
> machen und dann kommen solche antworten :)


Sven.
Der springende Punkt ist, dass der komplette Programmaufbau nicht 
stimmt. Wenn du dafür schon rausgeworfen hast, dann sind die Dinge noch 
schlimmer als ursprünglich gedacht.

>
> also zunächst habe ich genau so begonnen, kleine programmabschnitte zu
> programmieren und zu testen.

Ist alles ok.
Trotzdem stimmt dein Aufbau nicht.
Das eine ist die Technik des Programmierens, das Handwerk.-
Das andere ist die Strategie, das Konzept, die Idee, die man mit diesem 
Handwerk umsetzt.
Man muss beides beherrschen! Du kannst ein noch so guter 
Programmier-Handwerker sein, wenn du nicht ein paar 
Programmierstrategien beherrscht, Wissen darüber, wie man bestimmte 
Problemstellungen vernünftig in ein Konzept umwandelt, wird das nichts 
(und natürlich unzählige Standard-Algorithmen). >Ein Programm entwickeln 
ist nun mal nicht so leicht, wie es aussieht. Nicht umsonst spricht man 
von mindestens ein paar Monaten, ehe jemand vom Anfänger zum 
fortgeschrittenen Anfänger aufsteigt. Und bis man in die Riege der 
Experten aufsteigt, dauert es Jahre. Meinen Azubis hab ich ca. 1 Jahr 
gegeben (5 Tage die Woche, 8 Stunden am Tag), ehe ich sie als 'fürs 
Produktionsprogrammieren geeignet' eingestuft habe. Dieses Jahr brauchen 
sie! In diesem Jahr lernen sie!

In dem Moment, in dem du 'Warten' mit 'delay_ms' assozierst, in dem 
Moment bist du schon auf dem Holzweg.


Der Schlüssel zu einem guten µC-Programm besteht darin, seine Denkweise 
umzustellen. Die ganze Denkweise
    erst schalte ich was ein
    dann warte ich eine Zeit lang
    dann schalte ich es aus
diese ganze Denkweise ist schon der 'Weg zur Hölle'.

Du musst anfangen zu arbeiten, wie eine Hausfrau.
Die stellt nicht das Gulasch auf den Herd, bleibt daneben stehen bis es 
kocht, schaltet den Herd aus und geht zur Waschmaschine, räumt die 
Wäsche ein, bleibt daneben stehen bis die Maschine fertig ist und räumt 
dann aus, Sondern sie macht das alles 'gleichzeitig'!
Genau gleichzeitig geht natürlich nicht, aber man kann im schnellen 
Wechsel immer ein bischen was an einer Aufgabe machen
    Herd einschalten
    Wäsche einräumen
    Am Herd nachsehen ob Gulasch kocht
    Waschmaschine einschalten
    Am Herd nachsehen ob Gulasch kocht
    An der Waschmaschine nachsehen ob fertig
    Am Briefkasten nachsehen ob Post da ist
    erste Rechnung der Hausaufgaben der Kinder kontrollieren
    Am Herd nachsehen ob Gulasch fertig
    An der Waschmaschine nachsehen ob fertig
    Fische im Auquarium füttern
    Am Herd nachsehen ob Gulasch kocht. Ja -> Gulasch ausschalten
    Zweite Rechnung der Kinder kontrollieren
    Besteck auf Esstich legen
    Waschmaschine nachsehen ob fertig
    Teller auf Esstisch legen
    ....



Alle Aufgaben werden in kleine Happen zerlegt, von denen keiner lange 
dauert. Und reihum wird bei jeder 'Arbeitsstelle' nachgesehen, ob es 
etwas zu tun gibt. Wenn ja, dann wird das gemacht und eventuell der 
nächste 'Happen' aktiviert.

Und auf die Art schafft man es, dass ein µC scheinbar mnühelos mehrere 
Dinge gleichzeitig machen kann. Unter anderem auch die Beobachtung von 
Tasten, von denen einer dazu dient das ganze Werkl wieder auf 0 zu 
stellen.

In deiner Problemstellung kommen mehrere Konzepte vor. Da gibt es
* Tastenabfrage. Dazu hat sich die PeDa Entprellung bewährt, die
  auf einem Timerinterrupt und Polling basiert.
  Entprellung
* Wenn wir schon bei Timer sind. Timer sind DAS Arbeitspferd in der
  µC Programmierung. Abgesehen von ganz einfachen Lernbeispielen
  kommt so gut wie kein Programm ohne Timer aus. Wann immer es um
  irgendwelche Zeitsteuerungen geht, läuft der Lösungsansatz immer
  über Timer. Nur am ANfang nimmt man _delay_ms, denn niemand kann
  alles auf einmal lernen und irgendwo muss man ja auch mal anfangen
  und erste Programme schreiben. Sobald aber eine kritische Wissens-
  masse erreicht ist, führt an Timern kein Weg mehr vorbei
  FAQ: Timer
* Ablaufsteuerungen
  Wo immer möglich versucht man Abläufe auf Kennzahlen zu reduzieren
  und legt diese Kennzahlen in Arrays
* Programmlogik
  Das Konzept einer Zustandsmaschine hat sich extrem bewährt um
  Abläufe in den Griff zu kriegen. Das ist das Werkzeug um
  komplexe Maschinenlogiken in den Griff zu bekommen, in denen
  Vorgänge von anderen Vorgängen abhängen und deren Beendigung
  wieder neue Vorgänge anstossen.
  Statemachine

Und wahrscheinlich habe ich noch 10 andere durchaus wichtige Konzepte 
jetzt vergessen, ohne die du nicht weiterkommst auf deinem Weg zu einem 
vernünftigen Programm.

von oldmax (Gast)


Lesenswert?

Hi Sven
Du machst nichts besser, wenn du Text ein bisschen doof findest....
>daher finde ich die aussage:

>oldmax schrieb:
>> Etwas kopiertes ist nicht unbedingt erfolgreiches Lernen.
>> Einen "schnellen Erfolg" wollen zwar viele, aber glaub mir, es dauert
>> eben eine Zeit, bis man versteht, wie es funktioniert.

>bisschen doof, da ich oben extra schrieb, dass es absichtlich dummie
>codes sind und kein quellcode. hätte ich das anständig gemacht hätte ich
>mir für´s blinken ein unterprogramm geschrieben und hätte mir beim
>aufruf, die anzahl und die zeit mit übergeben, statt es dreimal gleich
>hinzuschreiben. das meinte ich eigentlich damit :)

Doof finde ich u. a. , wenn man absolut nix mit deutscher 
Rechtschreibung am Hut hat und die Groß- und Kleinschreibung ignoriert. 
Aber das hat nix mit dem Thema zu tun und nur mal am Rande.
Du hast nicht verstanden, was ein Programm ist. Ein Interrupt ist etwas, 
was einen Programmablauf unterbricht und eine andere Arbeit dazwischen 
schiebt. Danach wird an der unterbrochenen Stelle die Arbeit 
fortgesetzt. Willst du ein Unterprogramm mit einem Interrupt 
"abbrechen", so ist das zwar möglich, aber nicht so, wie du denkst.
Ich schreib mal, wie ich das sehe:
Du hast einen Antrieb, der mit dem UP "Drehe_Rechts" bzw. "Drehe_Links" 
eine Bewegung bis in eine Endlage ausführt. Signalisiert ein Sensor, 
Endlage erreicht, dann verläßt du das UP und der Antrieb bleibt stehen.
Diese Denkweise ist falsch !
Der Controller hat ja noch anderes zu tun, daher ist ein Programm anders 
anzugehen:
Hier mal ein Schema nach dem uralten "EVA" - Prinzip:
Loop:
  (E)ingänge lesen
  (V)erarbeite-> soll Antrieb rechts laufen-> ja, dann setze Merker 
Rechts
            -> Sensor rechts erreicht- ja, dann lösche Merker Rechts
            -> soll Antrieb links laufen -> ja, dann setze Merker links
            -> Sensor links erreicht-> ja, dann lösche Merker links
            -> Taster "Halt" gedrückt->ja, lösche Merker rechts und 
links
            -> mach andere Aufgaben
  (A)usgabe Merker
Weiter bei Loop

Interrupt:
    Taster "Not-Halt"
    ja, dannn lösche Merker rechts und links
Ende Interrupt

Der Trick ist, das du mit Tastern die entsprechenden Merkerbits setzt 
und das Programm nicht dauernd fragt, "hälst du den Taster noch ?"
Solange gesetzt, ist auch der Ausgang gesetzt. Löscht dir die ISR den 
Merker, dann wird beim nächsten Erreichen der Ausgabe auch der Ausgang 
weggeschaltet. Willst du noch schneller reagieren, dann kommt auch das 
Schreiben der Ausgänge in der ISR in Betracht. Aber so eine 
Programmschleife braucht vielleicht  10  .. 30 msek. Baust du aber 
"delays" ein, dann verlängert sich so ein Programmzyklus und das ist 
das, was dir die Kollegen hier sagen wollen. Zeiten werden mit dem Timer 
generiert. Dieser wird ebenfalls einen Interrupt auslösen, vielleicht 
jede msek. In der ISR setzt du dir dann entsprechende "Time_Flags", die 
du im Programm für zeitrelevante SChritte auswertest.
Etwa so:
Timer_ISR:
   Inc Zeitzähler
   Zeitzähler = 10  Setze Flag 10 msek
   Zeitzähler = 100 setze Flag 1/10 Sek
   Zeitzähler =1000 setze Zeitzähler zurück und setze Flag 1 Sek.
Ende Interrupt

Was macht nun das Hauptprogramm ?

Hier noch einmal das erweiterte Schema
Loop:
  (E)ingänge lesen
  (V)erarbeite-> soll Antrieb rechts laufen-> ja, dann setze Merker 
Rechts
            -> Sensor rechts erreicht- ja, dann lösche Merker Rechts
            -> soll Antrieb links laufen -> ja, dann setze Merker links
            -> Sensor links erreicht-> ja, dann lösche Merker links
            -> Taster "Halt" gedrückt->ja, lösche Merker rechts und 
links
            -> mach andere Aufgaben... z. B.

                    -> Zeitflag 10 msek -> bearbeite und lösche Flag
                    -> Zeitflag1/10 Sek -> bearbeite und lösche Flag
                    -> Zeitflag 1 Sek   -> bearbeite und lösche Flag
  (A)usgabe Merker
Weiter bei Loop

Du siehst, nirgends wird etwas abgebrochen. Es wird lediglich auf 
Ereignisse reagiert. Und das kann ein µC sehr gut. Damit hast du auch 
überschaubare Zykluszeiten von deinem programm und nicht einen Zustand, 
wo dein Controller tatsächlich nur auf Interrupts reagiert.
Gruß oldmax

von Sven (Gast)


Lesenswert?

oldmax schrieb:
> Loop:
>   (E)ingänge lesen
>   (V)erarbeite-> soll Antrieb rechts laufen-> ja, dann setze Merker
> Rechts
>             -> Sensor rechts erreicht- ja, dann lösche Merker Rechts
>             -> soll Antrieb links laufen -> ja, dann setze Merker links
>             -> Sensor links erreicht-> ja, dann lösche Merker links
>             -> Taster "Halt" gedrückt->ja, lösche Merker rechts und
> links
>             -> mach andere Aufgaben
>   (A)usgabe Merker
> Weiter bei Loop
>
> Interrupt:
>     Taster "Not-Halt"
>     ja, dannn lösche Merker rechts und links
> Ende Interrupt

super erklärt das EVA prinzip, kenne ich ja von der SPS (die ich 
übrigends von grund auf gelernt habe im ggsatz zu C! )

ich habe zwar schon mit timern und so gearbeitet, allerdings bin ich 
damit nicht 100% vertraut, daher habe ich delay genutzt.

ob ich eine zeiterfassung habe weiß ich noch garnicht, ist aber gut 
möglich!

das projekt (portalmanipulator, mit ifrarotsensor) soll 3 programme 
beherrschen.

1. komplettfahrt
2. referenzpunktfahrt
3. (steht noch aus, ggf motor einzelansteuerung)

der motor in X-Richtung ist ein elektromotor, welchen ich über eine 
L298-Schaltung wenden kann.
der motor in Y-Richtung ist ein Schrittmotor, welcher über eine 
L297-L298 Schaltung angesteuert wird, allerdings habe ich da noch keinen 
Erfolg, da die Schaltung nicht fuktioniert....

X Ansteuerung funktioniert, da ich automatisch umschalte bei endschalter 
"erreichen" dort habe ich einen 10 Stufen Poti dran, den ich via ADC 
abfrage.

bei jedem erreichen eines endschalters soll der schrittmotor einen 
schritt fahren.

und der sharp sensor soll alle x-schritte, welche ich aus der 
angesprochenen ADC entnehme eine messung vornehmen. (hier kann ggf die 
zeitrechnung von nutzen sein, da der sensor in den ersten 37ms nicht 
korrekt misst)

letztlich sieht das aus wie eine schnecke/schlang die sich in x und y 
richtung bewegt.

referenzpunktfahrt soll einfach beide motoren in grundstellung bringen, 
oder ggf als zusatz -> je ihren endpunkt anfahren und via ausgabe von 
schritten oder adc wert vergleichen ob und wie die werte übereinstimmen, 
um eine korrekte auflösung der sensorwerte zu gewährleisten. aber das 
nur ggf später, erstmal muss einiges vorher funktionieren.

letztlich soll laut aufgabenstellung das vermessene objekt via visual 
studio 2010 optisch dargestellt werden.

es ist also sozusagen eine art sonar mit infrarot sensor um 
oberflächenreliefs visualisiert darzustellen.

von oldmax (Gast)


Lesenswert?

Hi
>ich habe zwar schon mit timern und so gearbeitet, allerdings bin ich
>damit nicht 100% vertraut, daher habe ich delay genutzt.

Da sehe ich einen kleinen Widerspruch.. wenn du damit schon gearbeitet 
hast, dann ist es doch kein Problem. Du mußt dich nur von dem Gedanken 
trennen, wenn du eine Zeitverzögerung brauchst, dein Programm für diese 
Zeit tatenlos werden zu lassen.
Angenommen, du setzt in deiner Timer_Isr alle 100 msek ein Flag, also 
ein Bit in einer Variablen. In Assembler ist das wie folgt:
1
   lds   r16, time_Flag
2
   ori   r16, 0b00000100
3
   sts time_Flag, r16
 hab nur mal den relevanten Teil aufgeführt. Nun brauchst du eine 
Zeitverzögerung, sagen wir 5 Sek.
immer noch Schleife Hauptprogramm:
      ->Bedingung für Zeitverzögerung erfüllt,
             ->setze Zeitwert 50
             ->reset Bedingung für Zeitverzögerung
      ->TimeFlag = 1
          -> Zeitwert>0 dann
               ->Zeitwert - 1
                   -> Zeitwert = 0 dann Zeit_Ende_Flag
          -> Time_Flag =0
      ->Zeit_Ende_Flag = 1 ?
           -> Ereignisbearbeitung nach Zeit
           -> Zeit_Ende_Flag = 0
      -> weiter mit anderen Dingen....

Wie gesagt, ich spreche Assembler daher...
1
   LDS    r16, Prg_Ctrl      ; Programmsteuerkontrolle
2
   ANDI   r16, 0b00010000    ; Bit Verzögerung für Abschnit "x" starten ?
3
   BREQ   Weiter_1
4
   LDS    R16,50             ; Verzögerungszeit setzen
5
   STS    Zeit_Zaehler, R16   ; und im Zähler ablegen
6
   LDS    r16, Prg_Ctrl      ; Programmsteuerkontrolle
7
   ANDI   r16,0b11101111     ; Verzögerung gestartet, Bedingung löschen
8
Weiter_1:
9
   LDS    r16, Time_Flag     ; Zeitflags laden
10
   ANDI   r16, 0b00000100    ; 100 mSek-Flag gesetzt
11
   BREQ Weiter 2
12
   RCALL  Do_100mSek_Takt
13
Weiter_2:
14
   LDS    r16, Event_Flag
15
   ANDI   r16, 0b00001000    ; Ereignisbit prüfen
16
   BREQ   weiter_3
17
   RCALL  Event_Handle       ; Ereignis bearbeiten
18
Weiter_3:
19
   ....
20
21
22
Do_100msek_Takt:
23
   LDS  r16, Zeit_Zaehler   ; Zählerwert laden
24
   CPI  r16, 0              ; wenn schon 0 dann keine Aktion
25
   BREQ ende_Takt
26
   Dec  r16                 ; runterzählen 
27
   CPI  r16, 0              ; prüfen ob 0 (Zeroflag setzen)
28
   STS  Zeit_Zaehler, r16   ; rückspeichern
29
   BRNE  ende_Takt          ; wenn Zeroflag nicht gesetzt ist ->Ende
30
   LDS  r16, Event_Flag     
31
   ORI  r16, 00001000       ; definiertes Eventflag setzen
32
   STS Event_Flag, r16      ; und  ablegen
33
ende_Takt:  
34
   LDS  r16, Time_Flag
35
   ANDI r16, 0b111110111    ; Zeitflag zurücksetzen 
36
   STS Zeit_Flag, r16       ; und ablegen
37
ret
Ich weiß, das man das auch anders lösen und auch Programmtechnisch 
anders darstellen kann. So wird aber klar, wie mit den Bits in den 
Variablen umgegangen wird und das das Programm nur auf diese Steuerbits 
reagiert. Ob das nun ein Programmkontrollbit oder Zeit oder Ereignisbit 
ist. Nur wenn so ein Bit gesetzt ist, wird etwas getan, ansonsten nur 
weiter im Kreis drehen. Ist ein Ereignis bearbeitet, wird das Bit 
gelöscht. Der Vorteil: Dein Programm kann eine einfache Schleife sein. 
Willst du etwas neues hinzufügen, kommt halt ein Unterprogrammaufruf in 
die Schleife. So kann es auch mal ganz einfach auskommentiert werden.
Wenn du so perfekt auf SPS getrimmt wurdest, dürfte dir die 
Assemblerdarstellung bekannt vorkommen. Sie ist ähnlich der AWL.
Gruß oldmax

von Jobst M. (jobstens-de)


Lesenswert?

Variante 1:
Nimm den Interupt mit der höchsten Priorität im System: Den Reset.

Variante 2: (Ich warte schon auf das Geschrei :)
Wenn Du den Prozessor nicht zurücksetzen möchtest, kannst Du im Interupt 
den Stackpointer manipulieren, so, daß die Programmausführung nicht mehr 
zu der Stelle zurückkehrt, wo sie her kam, sondern dort hin 
'zurückkehrt' wo Du weiter machen möchtest.
Ist zwar nicht die feine englische Art, aber was soll's?
Vor allem, wenn Du schon fertige Programmmodule hast, ist dies ein recht 
einfacher Weg.

Und es bricht auch Dein delay ab.


Gruß

Jobst

von Karl H. (kbuchegg)


Lesenswert?

Jobst M. schrieb:

> Variante 2: (Ich warte schon auf das Geschrei :)
:-)

> Wenn Du den Prozessor nicht zurücksetzen möchtest, kannst Du im Interupt
> den Stackpointer manipulieren, so, daß die Programmausführung nicht mehr
> zu der Stelle zurückkehrt, wo sie her kam, sondern dort hin
> 'zurückkehrt' wo Du weiter machen möchtest.
> Ist zwar nicht die feine englische Art, aber was soll's?
> Vor allem, wenn Du schon fertige Programmmodule hast, ist dies ein recht
> einfacher Weg.

Und du denkst das kriegt er hin?
Er kriegst ja noch nicht mal auf die saubere englische Art hin. Und die 
ist nun  wirklich nicht schwer.

von oldmax (Gast)


Lesenswert?

Hi
Na ja, der Weg, die Rücksprungadresse im Stack zu ändern, macht riesig 
Spaß..... Vor allem, wenn man als Anfänger damit beginnt, wo das Wort 
"Stack" noch ziemlich fremdartig klingt. In Assembler geht das in etwa 
so....
1
  ....
2
  ....
3
  pop   ....                        ; Register zurückholen
4
  ;RETI                             ; Return from Interrupt ausblenden
5
  LDI  XL, Low(Ruecksprung)       ; Adresszeiger setzen
6
  LDI  XH, High(Ruecksprung)
7
  POP    r2                         ; Dummiregister für alten Rücksprung
8
  POP    r2
9
  Push   XL
10
  Push   XH
11
Reti

Hab ich so noch nicht getestet, aber sollte gehen. Eventuell ist XH und 
XL in der Reihenfolge zu tauschen.....
Gruß oldmax

von Peter D. (peda)


Lesenswert?

oldmax schrieb:
> Na ja, der Weg, die Rücksprungadresse im Stack zu ändern, macht riesig
> Spaß.

Und woher weiß der Interrupt, was alles auf dem Stack liegt ?????
Er weiß es nämlich nicht!

Glaub mir, der Spaß ist ganz schnell vorbei, wenn Du Dich dumm und 
dämlich suchst, warum der Stack unter- oder überläuft.


In C kann man das machen mit setjmp, longjmp. Damit wird der CPU-Status 
in einer Struct gesichert und kann daraus wieder restauriert werden.
Du wirst aber sehen, daß diese Funktion erfahrene Programmierer nur sehr 
sehr selten benutzen.


Peter

von Thomas E. (thomase)


Lesenswert?

Peter Dannegger schrieb:
> oldmax schrieb:
>> Na ja, der Weg, die Rücksprungadresse im Stack zu ändern, macht riesig
>> Spaß.
> Und woher weiß der Interrupt, was alles auf dem Stack liegt ?????
> Er weiß es nämlich nicht!

Das ist in diesem Fall aber völlig egal. Wenn schon schweinisch, dann 
aber richtig: Einfach das komplette RAM, Weicheier ab SP aufwärts, 
durchnullen. Dann wird er beim return irgendwas für Adresse 0 halten. 
Egal was. 100 Pro, weil ja alles 0 ist.

mfg.

von oldmax (Gast)


Lesenswert?

Hi Peter
Nun da ich in Assembler programmiere, denke ich schon, das ich weiß, was 
auf dem Stack liegt, dafür stand die "POP ... -Zeile". Aber du hast 
recht, sowas ist kaum pflegbar und auch unsinnig für einen Anfänger.
Gruß oldmax

von Peter D. (peda)


Lesenswert?

oldmax schrieb:
> Nun da ich in Assembler programmiere, denke ich schon, das ich weiß, was
> auf dem Stack liegt

Das weißt Du in Assembler auch nicht. Du weißt ja nicht, ob Du im Main 
oder in einer Unterfunktion unterbrochen wurdest.
Die Calling-Tiefe kann der Interrupt nicht feststellen. Und auch nicht, 
ob noch lokale Variablen auf dem Stack liegen.

Es geht "zuverlässig" nur, wenn man den Stackpointer für den Rücksprung 
irgendwo sichert.

"zuverlässig" deshalb, weil es das in Real nicht ist.
Z.B. Du drückst die Taste während ein 4Bit-Nibble zum LCD übertragen 
wird, springst irgendwo hin und ab da zeigt das LCD nur noch Unsinn an, 
weil die Nibble vertauscht sind.


Peter

von oldmax (Gast)


Lesenswert?

Hi Peter
OK, grad wollt ich nochmal dagegen setzen, aber dabei ist mir mein 
Denkfehler klar geworden...
( Ich werd halt langsam alt....)
Gruß Martin

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.