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)
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
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.
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.
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...
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.
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.
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
intADC_init(uint8_tchannel)
24
{
25
ADMUX=(1<<REFS0)|channel;//AVCC als Referenzspannung nutzen & channel entspricht dem Kanal (0-7)