Forum: Compiler & IDEs ATMega_32-Interrupt-Stack löschen?


von Lukas Wiese (Gast)


Lesenswert?

Hallo!

Ich bin gerade dabei ein LCD-Menü mithilfe eines ATMega32 zu realisieren 
und stoße da so langsam an meine Grenzen.
Ziel ist es, gewisse Unterprogramme über ein Potentiometer 
anzuwählen,welche dann über einen Taster gestartet werden und genauso 
mit dem gleichen Taster auch wieder beendet werden sollen.

[spoiler]
1
#include <avr/io.h>
2
#include <stdlib.h>
3
#include <util/delay.h>
4
#include "lcd-routines.h"
5
#include <avr/interrupt.h>
6
7
#define L1 2
8
#define L2 3
9
#define L3 6
10
#define T1 2
11
12
#define D4 1
13
#define D5 2
14
#define D6 3
15
#define D7 4
16
#define RS 6
17
#define E 5
18
19
#define F_CPU 1000000UL
20
21
22
int uart_putc(unsigned char c) //Eine Konstante über UART ausgeben
23
{
24
  while (!(UCSRA & (1<<UDRE)))  //warten bis Senden moeglich
25
  {
26
  }
27
  
28
  UDR = c;                      //sende Zeichen
29
  return 0;
30
}
31
void uart_puts (char *s) //Einen String über UART ausgeben
32
{
33
  while (*s)
34
  {
35
    uart_putc(*s);
36
    s++;
37
  }
38
}
39
int ADC_init(uint8_t channel)
40
{
41
  ADMUX = (1<<REFS0)|channel; //AVCC als Referenzspannung nutzen & channel entspricht dem Kanal (0-7)
42
  ADCSRA = (1<<ADPS1) | (1<<ADPS0); //Frequenzvorteiler=8 -> 1mHz/8=125 kHz
43
  ADCSRA |= (1<<ADEN); //ADC verfügbar machen
44
}
45
int ADC_m(uint8_t channel)
46
{
47
  ADCSRA |= (1<<ADSC);            // Wandlung starten
48
  while (ADCSRA & (1<<ADSC) )    // auf Abschluss der Wandlung warten
49
  {}
50
  return ADCW;
51
}
52
53
volatile uint8_t state=0;
54
55
ISR(INT2_vect)
56
{
57
  _delay_ms(40);
58
  if(!(PINB & (1<<T1)))
59
  {
60
    state=1;
61
  }
62
}
63
64
int main(void)
65
{
66
    GICR |= (1<<INT2);
67
  MCUCSR &= ~(1<<ISC2);
68
  cli();
69
  
70
  uint8_t menus=2;
71
    
72
    //LED + Taster initialisieren
73
    DDRD |= (1<<L1)|(1<<L2)|(1<<L3);
74
    DDRB &= ~(1<<T1);
75
    PORTB |= (1<<T1);
76
  
77
78
    lcd_init();
79
  ADC_init(7);
80
  
81
  while(1)
82
    {
83
    if(ADC_m(7)>1024/menus)
84
    {
85
      lcd_setcursor( 0, 1 );
86
      lcd_string("LED-Test");
87
      if(!(PINB & (1<<T1)))
88
      {
89
        _delay_ms(40);
90
        while(!(PINB & (1<<T1))){}
91
        _delay_ms(40);
92
        sei();
93
        while(state==0)
94
        {
95
          LedTest();
96
        }
97
        state=0;
98
        cli();
99
      }
100
    }
101
    else
102
    {
103
      lcd_setcursor( 0, 1 );
104
      lcd_string("Menu 2  ");
105
    }
106
    
107
    }
108
}
109
110
void LedTest()
111
{
112
  PORTD |= (1<<L1);
113
  _delay_ms(100);
114
  PORTD &= ~(1<<L1);
115
  PORTD |= (1<<L2);
116
  _delay_ms(100);
117
  PORTD &= ~(1<<L2);
118
  PORTD |= (1<<L3);
119
  _delay_ms(100);
120
  PORTD &= ~(1<<L3);
121
  PORTD |= (1<<L2);
122
  _delay_ms(100);
123
  PORTD &= ~(1<<L2);
124
}
[/spoiler]

Genau beim beenden des Programms liegt nun mein Problem:

über einen Interrupt kann ja der Tastendruck auch mitten im 
Programmdurchlauf erkannt werden. In meinem Fall wird dann eine Variable 
verändert, welche am Ende des Programms abgefragt wird.
Wie kann ich aber nun direkt beim Ausführen der ISR das laufende 
Programm beenden, und sofort ins "Hauptmenü" zurückspringen?
Theoretisch müsste dafür ja der Stack Pointer um eins zurückgesetzt 
werden. oder ist das ein völlig falscher Ansatz?

ich würde mich freuen, wenn mir hier jemand helfen kann!

MfG Lukas

von Karl H. (kbuchegg)


Lesenswert?

Lukas Wiese schrieb:

> Theoretisch müsste dafür ja der Stack Pointer um eins zurückgesetzt
> werden. oder ist das ein völlig falscher Ansatz?

völlig falscher Ansatz.

von Karl H. (kbuchegg)


Lesenswert?

Ich nehme an, mit 'Programm' meinst du die Abfolge der Lichter, die du 
hier

> [c]
> void LedTest()
> {
>   PORTD |= (1<<L1);
>   _delay_ms(100);
>   PORTD &= ~(1<<L1);
>   PORTD |= (1<<L2);
>   _delay_ms(100);
>   PORTD &= ~(1<<L2);
>   PORTD |= (1<<L3);
>   _delay_ms(100);
>   PORTD &= ~(1<<L3);
>   PORTD |= (1<<L2);
>   _delay_ms(100);
>   PORTD &= ~(1<<L2);
> }

ausprogrammiert hast.

Merke:
_delay_ms ist selten die Lösung, oft aber das Problem.

Dein eigentliches Problem liegt hier in der Form vor, dass diese 
'Programmsteuerung' viel zu unflexibel ist.

Im Idealfall liegt dein 'Programm' als Array von auszugebenden Daten 
vor, die schrittweise ausgegeben werden. Die Funktion, die dies 
bewerkstelligt, prüft dann nach jedem Schritt, ob ein Flag zum Abbruch 
des Programms gesetzt ist, welches ihm von der ISR beim Tastendruck 
gesetzt wurde.

UNd die Zeitsteuerung macht man nicht mit _delay_ms. Denn wie gesagt: 
_delay_ms ist selten die Lösung, oft aber das Problem. Zeitsteuerungen 
bedingen in realen Programmen praktisch immer den Einsatz eines Timers.
FAQ: Timer
Weg vom Denkansatz: jetzt mache ich das, dann warte ich, dann mache ich 
jenes, dann warte ich wieder, ....
Hin zu: in diesem AUgenblick ist die Zeitspanne x vergangen. Was gibt es 
jetzt, genau in diesem Augenblick, zu tun. Das mache ich und damit ist 
die Abarbeitung eines Zeitschritts erledigt.

In Kurze: weg von Warteverfahren; hin zu ereigtnisgesteuerertem 
Programmieren, wobei ein Taktgeber für regelmässige Ereignisse sorgt.

: Bearbeitet durch User
von Lukas Wiese (Gast)


Lesenswert?

Erstmal vielen dank für die Antwort!

>"Im Idealfall liegt dein 'Programm' als Array von auszugebenden Daten
>vor, die schrittweise ausgegeben werden"

also meinst du damit, dass das LED-Test Unterprogramm in einer Schleife 
hätte programmiert werden müssen?


also läuft es schlussendlich ja wieder auf Polling hinaus, indem ich die 
Variable, welche in der ISR gesetzt wird genügend oft zwischen den 
Ausführungen abfrage?
ich dachte das ist irgendwie zu umgehen...

von Karl H. (kbuchegg)


Lesenswert?

Lukas Wiese schrieb:

> also läuft es schlussendlich ja wieder auf Polling hinaus, indem ich die
> Variable, welche in der ISR gesetzt wird genügend oft zwischen den
> Ausführungen abfrage?
> ich dachte das ist irgendwie zu umgehen...


Du kannst es im allgemeinen Fall nicht umgehen.
Denn letzten Endes kann die ISR deiner Funktion nur signalisieren:
"Jetzt ist aber gut, aufhören".
Die Funktion muss aber trotzdem Gelegenheit kriegen einen geordneten 
Abbruch durchzuführen. Wenn der Benutzer deiner Wassersteuerung 
signalisiert: Benutzer will abbrechen, dann muss die Funktion die Chance 
haben, die Wasserpumpe auch wieder abzudrehen, die sie sich selbst 
aufgedreht hat.

Spiele mit dem System, nicht gegen es. Je mehr du versuchst durch 
Tricksereien irgendwelche Dinge 'gefinkelt' zu umgehen, desto öfter 
fällst du auf die Schnauze.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Und wenn du auf ereignisgesteuertes Programmieren umstellst, dann löst 
sich dieses Problem sowieso in Luft auf.
Denn dann verbringt dein Programm sowieso den Löwenanteil seiner Zeit 
damit, die Elemente der Benutzersteuerung zu überwachen, während die 
Lichtsteuerungen immer nur kurz in einem Timerinterrupt bearbeitet 
werden.

von Lukas Wiese (Gast)


Lesenswert?

Ich wollte nochmal danke sagen für den Denkanstoß und auch nochmal 
meinen vorläufigen Code posten, vlt liest das ja nochmal jemand, dem es 
weiterhilft.
Bis auf die Tastenentprellung, mit welcher ich mich noch nicht 
beschäftigt habe, funktioniert das nun sehr gut (;
1
#include <avr/io.h>
2
#include <stdlib.h>
3
#include <util/delay.h>
4
#include "lcd-routines.h"
5
#include <avr/interrupt.h>
6
7
#define L1 2
8
#define L2 3
9
#define L3 6
10
#define T1 2
11
12
#define D4 1
13
#define D5 2
14
#define D6 3
15
#define D7 4
16
#define RS 6
17
#define E 5
18
19
#define F_CPU 1000000UL
20
21
22
23
int ADC_init(uint8_t channel)
24
{
25
  ADMUX = (1<<REFS0)|channel; //AVCC als Referenzspannung nutzen & channel entspricht dem Kanal (0-7)
26
  ADCSRA = (1<<ADPS1) | (1<<ADPS0); //Frequenzvorteiler=8 -> 1mHz/8=125 kHz
27
  ADCSRA |= (1<<ADEN); //ADC verfügbar machen
28
}
29
int ADC_m(uint8_t channel)
30
{
31
  ADCSRA |= (1<<ADSC);            // Wandlung starten
32
  while (ADCSRA & (1<<ADSC) )    // auf Abschluss der Wandlung warten
33
  {}
34
  return ADCW;
35
}
36
37
volatile uint16_t state=0;
38
volatile uint16_t intervall=0.1*500;
39
40
ISR(TIMER0_COMP_vect)
41
{
42
  state++;
43
  if(state==intervall)
44
  {
45
    PORTD &= ~(1<<L2);
46
    PORTD |= (1<<L1);
47
  }
48
  if(state==intervall*2)
49
  {
50
    PORTD &= ~(1<<L1);
51
    PORTD |= (1<<L2);
52
  }
53
  if(state==intervall*3)
54
  {
55
    PORTD &= ~(1<<L2);
56
    PORTD |= (1<<L3);
57
  }
58
  if(state==intervall*4)
59
  {
60
    PORTD &= ~(1<<L3);
61
    PORTD |= (1<<L2);
62
    state=0;
63
  }
64
}
65
66
int main(void)
67
{
68
    uint8_t menus=2;
69
    
70
    //Taster initialisieren
71
    DDRB &= ~(1<<T1);
72
    PORTB |= (1<<T1);
73
  
74
75
    lcd_init();
76
  ADC_init(7);
77
  
78
  while(1)
79
    {
80
    //choice = ADC_m(7) / (1024/menus);
81
    if(ADC_m(7)>1024/menus)
82
    {
83
      lcd_setcursor( 0, 1 );
84
      lcd_string("LED-Test");
85
      if(!(PINB & (1<<T1)))
86
      {
87
        while(!(PINB & (1<<T1)));
88
        _delay_ms(5);
89
        LedTest();
90
      }
91
    }
92
    else
93
    {
94
      lcd_setcursor( 0, 1 );
95
      lcd_string("Menu 2  ");
96
    }
97
    
98
    }
99
}
100
101
void LedTest()
102
{
103
  DDRD |= (1<<L1)|(1<<L2)|(1<<L3);
104
  
105
  TIMSK = (1<<OCIE0);               
106
  OCR0  = 249;                    
107
  TCCR0 = (1<<WGM01) | (1<<CS01); //Vorteiler=8 ->125kHz frequenz -> (/250 Zählungen) macht 500 Aufrufe/s
108
  
109
  sei();
110
  while(PINB & (1<<T1))
111
  {}
112
  while(!(PINB & (1<<T1)))
113
  {}
114
  cli();
115
  _delay_ms(5);
116
  PORTD &= ~((1<<L1)|(1<<L2)|(1<<L3));
117
}

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.