Moin,
ich habe hier zusammen mit dem Vermieter im Haus ein Projekt laufen,
welches auf Tastendruck festgelegte Texte, die auf Conrad Soundmodulen
gespeichert sind abspielt. Dieses soll vorne an der Tür Gäste empfangen.
Nach einigem hin und her habe ich nun auch endlich eine Möglichkeit
gefunden, wie es zuverlässig läuft. Verwendet wurde dazu das myAVR MK1
LPT Board mit Atmega8, sowie das 4 Kanal Relais Modul "myDigitalOut".
Letzteres ist notwendig, da die Soundmodule eine sehr hohe
Eingangsempfindlichkeit haben und somit jetzt über die Relais nur dann
Strom erhalten, wenn sie auch wirklich an der Reihe sind.
Quelltext:
1
#define F_CPU 3686400
2
3
#include<avr/io.h>
4
#include<util/delay.h>
5
6
unsignedchartasterFrei;
7
8
// Funktionen
9
10
voidinitPorts(void)
11
{
12
DDRB=0xFF;// PORTB = Ausgang
13
PORTB=0x00;// Ausgänge B auf low
14
DDRC=0xFF;// PORTC = Ausgang
15
PORTC=0x00;// Ausgänge C low
16
DDRD=0x00;// PORTD = Eingang
17
PORTD=0x04;// PORTD = PULL-UP
18
}
19
20
voiddelayLong(unsignedchari)
21
{
22
unsignedcharj;
23
for(j=0;j<i;j++)
24
{
25
_delay_ms(1000);
26
}
27
}
28
charalleAnsagen(chari)
29
{
30
if(i==1)
31
{
32
tasterFrei=0;
33
PORTC|=0x01;
34
_delay_ms(10);
35
PORTB=0x01;
36
_delay_ms(100);
37
PORTB=0x00;
38
delayLong(10);//dauer ansage nr.1 (in sekunden)
39
PORTC&=~0x01;
40
tasterFrei=1;
41
}
42
else
43
{
44
if(i==2)
45
{
46
tasterFrei=0;
47
PORTC|=0x02;
48
_delay_ms(10);
49
PORTB=0x02;
50
_delay_ms(100);
51
PORTB=0x00;
52
delayLong(9);//dauer ansage nr.2
53
PORTC&=~0x02;
54
tasterFrei=1;
55
}
56
else
57
{
58
if(i==3)
59
{
60
tasterFrei=0;
61
PORTC|=0x04;
62
_delay_ms(10);
63
PORTB=0x04;
64
_delay_ms(100);
65
PORTB=0x00;
66
delayLong(18);//dauer ansage nr.3
67
PORTC&=~0x04;
68
delayLong(80);//letzte verzoegerung extra lang (in sek)
69
tasterFrei=1;
70
i=1;
71
}
72
else
73
{
74
tasterFrei=1;
75
i=1;
76
}
77
}
78
return(i);
79
}
80
81
}
82
83
// Haupt
84
85
main(void)
86
{
87
initPorts();
88
unsignedchari;
89
i=1;
90
unsignedcharfertig;
91
fertig=0;
92
unsignedchartasterFrei;
93
tasterFrei=1;
94
do
95
{
96
if((!(PIND&0x04))&&(tasterFrei==1))
97
{
98
alleAnsagen(i);
99
i++;
100
}
101
102
}
103
while(fertig==0);
104
}
Nun zu meinem Problem: Das System besteht aus 3 dieser Soundmodule (also
3 Texte), die je nach Tastendruck in festgelegter Reihenfolge abgespielt
werden. Nach Durchlauf ist das System für eine gewisse Zeit gesperrt und
dann wieder einsatzbereit. Drückt jemand jetzt den Taster jedoch nur 1x
oder 2x und geht danach weg, so soll sich das Programm nach einer
gewissen Zeit zurück auf Anfang zurücksetzen. Leider sind meine
Programmierkenntnisse nicht so ausgereift. Meine Vermutung ist es einen
Timer zu setzen, der global über der gesamten Schleife läuft und nach
einer festgelegten Zeit das System unabhängig von anderen Parametern
zurücksetzt. Allerdings habe ich keine Ahnung wie ich es hinbekomme,
dass der Timer sozusagen im Hintergrund mitläuft.
Ich hoffe, dass jemand mir hier helfen kann.
Gruß,
Klas Meyer
Hi,
mach dich mal im AVR-Tutorial mit Interrupts vertraut. Das ist dort sehr
gut beschrieben, ich denke eine Erklärung hier ist dann gar nicht mehr
nötig.
Hi,
vielen Dank für den Tipp, habe mich dort mal eingelesen, habe es auch
grundsätzlich verstanden, nur scheints in der Anwendung bei mir nicht zu
klappen. Habe aber auch lediglich Beispiele gefunden, die mir erlauben
vor der Main Routine zu warten, nicht, wie ich möchte, die Hauptroutine
zurücksetzen.
Ich vermute mal dass einfach meine Programmierkenntnisse zu gering sind
für das Projekt, werde mir wohl einen versierteren C-Programmierer
suchen müssen.
Gruß,
Klas
Klas Meyer schrieb:> Hi,>> vielen Dank für den Tipp, habe mich dort mal eingelesen, habe es auch> grundsätzlich verstanden, nur scheints in der Anwendung bei mir nicht zu> klappen. Habe aber auch lediglich Beispiele gefunden, die mir erlauben> vor der Main Routine zu warten, nicht, wie ich möchte, die Hauptroutine> zurücksetzen.>> Ich vermute mal dass einfach meine Programmierkenntnisse zu gering sind> für das Projekt, werde mir wohl einen versierteren C-Programmierer> suchen müssen.
So schwer ist das nicht.
Du brauchst:
einen Timer, der in regelmässigen Zeitabständen einen Interrupt auslöst.
Diese Zeitabstände kennst du, sagen wir mal alle 100 Millisekunden.
Dann brauchst du weiters eine globale Variable. Diese wird deine Uhr.
In der Interruptroutine des Timers zählst du diese Variable um 1 hoch.
Soweit so gut.
Wenn du nun in der Hauptschleife diese Variable immer wieder abfrägst
(die Variable volatile machen nicht vergessen) und diese Variable hat
den Wert 10 erreicht, dann bedeutet das, das 1 Sekunde vergangen ist,
denn 10 mal 100 Millisekunden sind 1 Sekunde. Und ganz wichtig: 1
Sekunde, seit dem du das letzte mal diese Varíable auf 0 gesetzt hast.
Wenn du daher bei einem erkannten Tastendruck die Variable auf 0 setzt,
dann kannst du in der weiteren Behandlung der Hauptschleife die Variable
zb auf größer 100 abfragen und wenn der Fall eintritt, dann weißt du,
dass seit mindestens 10 Sekunden kein weiterer Tastendruck erfolgt ist.
1
volatileunsignedcharTicks;// hier zählt die ISR hoch
2
3
ISR(....)
4
{
5
Ticks++;
6
}
7
8
main(void)
9
{
10
initPorts();
11
unsignedchari;
12
unsignedcharfertig;
13
unsignedchartasterFrei;
14
15
i=1;
16
fertig=0;
17
tasterFrei=1;
18
19
// Timer initialisieren
20
.....
21
22
///
23
24
do
25
{
26
if((!(PIND&0x04))&&(tasterFrei==1))
27
{
28
alleAnsagen(i);
29
i++;
30
Ticks=0;
31
}
32
33
if(Ticks>100){
34
i=1;// Grundzustand
35
Ticks=0;
36
}
37
38
}
39
while(fertig==0);
40
}
Du musst nur anfangen, deine geradlinige einfache Denkweise
"zuerst mach dieses, dann mach jenes, dann warte ein Weilchen und mach
schlussendlich noch dieses" aufzugeben und in Ereignissen denken. In der
Hauptschleife werden Ereignisse abgefragt
ist eine Taste gedrückt
ist eine Zeit abgelaufen
ist ....
und dann musst du natürlich noch dafür sorgen, dass das Ereignis auch
entsprechend eintreten kann.
Moin nochmal,
vielen Dank, der Quelltext hat mir schon sehr weiter geholfen,
allerdings scheint es immer noch nicht korrekt zu sein, es lässt sich
zwar ohne Fehler brennen, jedoch scheint er den Timer einfach zu
ignorieren, liegt das im Ausführen innerhalb von "while(1)" ? Wenn ich
dort allerdings nichts hineinschreibe würde er ja einfach warten bis der
Timer am festgelegten Wert ankommt.
Mein derzeitiges Programm sieht jetzt so aus:
1
#define F_CPU 3686400
2
3
#include<avr/io.h>
4
#include<util/delay.h>
5
#include<avr/interrupt.h>
6
7
unsignedchartasterFrei;
8
9
// Funktionen
10
11
voidinitPorts(void)
12
{
13
DDRB=0xFF;// PORTB = Ausgang
14
PORTB=0x00;// Ausgänge B auf low
15
DDRC=0xFF;// PORTC = Ausgang
16
PORTC=0x00;// Ausgänge C auf low
17
DDRD=0x00;// PORTD = Eingang
18
PORTD=0x04;// PORTD = PULL-UP
19
}
20
21
22
voiddelayLong(unsignedchari)
23
{
24
unsignedcharj;
25
for(j=0;j<i;j++)
26
{
27
_delay_ms(1000);
28
}
29
}
30
charalleAnsagen(chari)
31
{
32
if(i==1)
33
{
34
tasterFrei=0;
35
PORTC|=0x01;
36
_delay_ms(10);
37
PORTB=0x01;
38
_delay_ms(100);
39
PORTB=0x00;
40
delayLong(10);//dauer ansage nr.1 (in sekunden)
41
PORTC&=~0x01;
42
tasterFrei=1;
43
}
44
else
45
{
46
if(i==2)
47
{
48
tasterFrei=0;
49
PORTC|=0x02;
50
_delay_ms(10);
51
PORTB=0x02;
52
_delay_ms(100);
53
PORTB=0x00;
54
delayLong(9);//dauer ansage nr.2
55
PORTC&=~0x02;
56
tasterFrei=1;
57
}
58
else
59
{
60
if(i==3)
61
{
62
tasterFrei=0;
63
PORTC|=0x04;
64
_delay_ms(10);
65
PORTB=0x04;
66
_delay_ms(100);
67
PORTB=0x00;
68
delayLong(18);//dauer ansage nr.3
69
PORTC&=~0x04;
70
delayLong(80);//letzte verzoegerung extra lang (in sek)
71
tasterFrei=1;
72
i=1;
73
}
74
else
75
{
76
tasterFrei=1;
77
i=1;
78
}
79
}
80
return(i);
81
}
82
83
}
84
85
86
volatileunsignedcharTicks;//ISR zählt Variable "Ticks" in 1er Schritten hoch
87
88
ISR(TIMER0_OVF_vect)
89
{
90
Ticks++;
91
}
92
93
// Haupt
94
95
main(void)
96
{
97
initPorts();
98
unsignedchari;
99
unsignedcharfertig;
100
unsignedchartasterFrei;
101
102
i=1;
103
fertig=0;
104
tasterFrei=1;
105
106
TCCR0|=(1<<CS02)|(1<<CS00);// ((3686400/1024)/256)*1000ms=71,11ms für 1 Overflow
107
TIMSK|=(1<<TOIE0);
108
sei();
109
110
while(1)
111
{
112
113
do
114
{
115
if((!(PIND&0x04))&&(tasterFrei==1))
116
{
117
alleAnsagen(i);
118
i++;
119
}
120
121
if(Ticks>500){//(71,11ms*500)/1000 = 35,55s für Reset durch Timer
122
i=1;//Grundzustand
123
Ticks=0;//Timer reset
124
}
125
}
126
127
while(fertig==0);
128
}
Ich bedanke mich schonmal im Voraus für die Antworten, ist wirklich sehr
hilfreich hier.
Gruß,
Klas
Klas Meyer schrieb:> Ich bedanke mich schonmal im Voraus für die Antworten,
Jetzt fängst du als allererstes einmal an, eine äussere Form in deinen
Source Code hineinzubringen.
Nach einer { wird alles darunteretehende um 2 Zeichen eingerückt. Bis
zur zugehörigen }. Die kommt wieder 2 Leerzeichen nach links und dann
geht es direkt unter dieser } in derselben Spalte weiter.
Deine Kraut und Rüben Einrückerei durchblickt doch kein Mensch mehr.
Und dann siehst du dir an, ob alle Anweisungen tatsächlich in der
Einrückstufe sind und du sie auch tatsächlich von den entsprechenden
Blockanweisungen (if, while, do) abhängen von denen du sie abhängig
haben willst.
Und nein: Äussere Form wird nicht nachträglich in ein Programm
eingebaut, sondern schon während man entwickelt. Gerade konsequent
durchgezogenes Einrückschema macht sehr oft den Unterschied zwischen
"Ich finde einen Fehler leicht" und "Ich finde den Fehler einfach nicht"
Was mir spontan auffällt: in main() existiert eine lokale Variable
tasterFrei, die natürlich die gleichnamige globale verdeckt. D.h. in
main() wird die lokale mal abgefragt und mal auf 1 gesetzt (und nie
wieder auf 0), in alleAnsagen() dagegen die globale auf 0 gesetzt. Ich
bin mir zu 99,9% sicher, dass das nicht das ist, was du willst.
Kleiner Schönheitsfehler außer der Formatierung: alleAnsagen() macht
nicht alle Ansagen, sondern bei jedem Aufruf nur eine abhängig vom
Übergabeparameter. Das mag gewollt sein, aber dann benenn die doch bitte
anständig.
Oh, 500 mit 8 bit ist selbstverständlich Mist :-), die Anmerkung mit der
Variable tasterFrei ist auch durchaus berechtigt, finde es hingegen
interessant das es überhaupt so funktioniert wie es momentan ist.
Vermutlich lässt sich das lokale Setzen der Variable auf den Wert 1 auch
einfach streichen, da sie ja sowieso in "alleAnsagen" was nun einfach
"Ansage" heißt gesetzt wird auf den gewollten Wert.
Habe nun auch versucht die Anforderungen an die äußere Form zu erfüllen,
ich denke es liegt einfach an meiner mangelnden Erfahrung und dem
ständigen Verändern des Codes dass er immer unübersichtlicher wurde.
Ich vermute den Fehler darin, dass die letzte If-Bedingung mit in der
while(1) steht, allerdings diese einfach rauszunehmen führt auch zu
keinem Effekt, bzw. eher zu Fehlern des Compilers.
Aktueller Code:
1
#define F_CPU 3686400
2
3
#include<avr/io.h>
4
#include<util/delay.h>
5
#include<avr/interrupt.h>
6
7
unsignedchartasterFrei;
8
9
// Funktionen
10
11
voidinitPorts(void)
12
{
13
DDRB=0xFF;// PORTB = Ausgang
14
PORTB=0x00;// Ausgänge B auf low
15
DDRC=0xFF;// PORTC = Ausgang
16
PORTC=0x00;// Ausgänge C auf low
17
DDRD=0x00;// PORTD = Eingang
18
PORTD=0x04;// PORTD = PULL-UP
19
}
20
21
voiddelayLong(unsignedchari)
22
{
23
unsignedcharj;
24
for(j=0;j<i;j++)
25
{
26
_delay_ms(1000);
27
}
28
}
29
30
charAnsage(chari)
31
{
32
if(i==1)
33
{
34
tasterFrei=0;
35
PORTC|=0x01;
36
_delay_ms(10);
37
PORTB=0x01;
38
_delay_ms(100);
39
PORTB=0x00;
40
delayLong(10);//dauer ansage nr.1 (in sekunden)
41
PORTC&=~0x01;
42
tasterFrei=1;
43
}
44
else
45
{
46
if(i==2)
47
{
48
tasterFrei=0;
49
PORTC|=0x02;
50
_delay_ms(10);
51
PORTB=0x02;
52
_delay_ms(100);
53
PORTB=0x00;
54
delayLong(9);//dauer ansage nr.2
55
PORTC&=~0x02;
56
tasterFrei=1;
57
}
58
else
59
{
60
if(i==3)
61
{
62
tasterFrei=0;
63
PORTC|=0x04;
64
_delay_ms(10);
65
PORTB=0x04;
66
_delay_ms(100);
67
PORTB=0x00;
68
delayLong(18);//dauer ansage nr.3
69
PORTC&=~0x04;
70
delayLong(80);//letzte verzoegerung extra lang (in sek)
71
tasterFrei=1;
72
i=1;
73
}
74
else
75
{
76
tasterFrei=1;
77
i=1;
78
}
79
}
80
return(i);
81
}
82
}
83
84
85
volatileunsignedcharTicks;//ISR zählt Variable "Ticks" in 1er Schritten hoch
86
87
ISR(TIMER0_OVF_vect)
88
{
89
Ticks++;
90
}
91
92
93
main(void)
94
{
95
initPorts();
96
unsignedchari;
97
unsignedcharfertig;
98
unsignedchartasterFrei;
99
100
i=1;
101
fertig=0;
102
tasterFrei=1;
103
104
TCCR0|=(1<<CS02)|(1<<CS00);// ((3686400/1024)/256)*1000ms=71,11ms für 1 Overflow
105
TIMSK|=(1<<TOIE0);
106
sei();
107
108
while(1)
109
{
110
do
111
{
112
if((!(PIND&0x04))&&(tasterFrei==1))
113
{
114
Ansage(i);
115
i++;
116
Ticks=0;
117
}
118
119
if(Ticks>100)//(71,11ms*500)/1000 = 35,55s für Reset durch Timer
Klas Meyer schrieb:> Habe nun auch versucht die Anforderungen an die äußere Form zu erfüllen,
In deiner main steht (Übrigens: der return type von main ist int. Du
solltest Compiler Warnings ernster nehmen)
1
intmain()
2
{
3
....
4
5
while(1)
6
{
7
do
8
{
9
....
10
}
11
}
12
while(fertig==0);
13
}
schau doch einfach mal in welcher Einrückstufe das untere while steht.
und dann gehst du von der } aus solange die Spalte hoch, bis du die
öffnende { findest. Sieh dir mal an, wie das alles ineinander
verschachtelt ist. Du trickst dich hier auf Dauer selber aus. Wozu die
ganzen verschachtelten Schleifen? Du brauchst genau 1 Endlosschleife und
nicht mehr!
1
intmain()
2
{
3
....
4
5
while(1)
6
{
7
...
8
Programmlogik
9
...
10
}
11
}
PS:
So etwas
1
if(Bedingung1)
2
{
3
}
4
else
5
{
6
if(Bedingung2)
7
{
8
}
9
else
10
{
11
if(Bedingung3)
12
{
13
}
14
}
15
}
kannst du guten Gewissens so schreiben
1
if(Bedingung1)
2
{
3
}
4
5
elseif(Bedingung2)
6
{
7
}
8
9
elseif(Bedingung3)
10
{
11
}
letzteres ist durch die kleineren Einrückungen übersichtlicher.
Und dann siehst du plötzlich auch, dass in deiner Funktion Ansage der
1
returni;
nur dann gemacht wird, wenn die erste if Bedingung zutrifft (auch
erkennbar daran, dass nach dem return noch eine } kommt, die nicht ganz
links am linken Rand steht.
PS2: return ist kein Funktionsaufruf! Kein Grund da Klammern rund um den
Ausdruck zu machen. Du schreibst ja auch nicht
1
i=(5);
laut Simulator tickt dein Timer. Die ISR wird auch aufgerufen.
Klas Meyer schrieb:> zwar ohne Fehler brennen, jedoch scheint er den Timer einfach zu> ignorieren
woher weißt du das eigentlich?
Mach dir doch mal eine Ausgabe rein, die dir den Fall Timer abgelaufen
anzeigen kann (LED brennen lassen oder sowas)
Mit 'scheint' kann man nicht arbeiten.
Also musst du dir was einfallen lassen, wie du deinem Programm auf die
Finger sehen kannst während es arbeitet. Das kann sein 1 LED die an
bestimmten Code-Stellen eingeschaltet/ausgeschaltet wird oder blinkt.
Das kann eine Ausgabe auf ein LCD sein. Das könnte bei dir zb das
Abspielen eines kleinen 'Pieep' Sounds sein, etc.
Denk dir was aus. Hauptsache du hast eine Rückmeldung vom Programm "Bin
hier angekommen" oder "Variable hat jetzt den und den Wert" oder ....
Was immer du eben an Informationen benötigst um aus dem Zustand "es
scheint so zu sein, dass ..." rauszukommen und in den Zustand "Ich weiß,
dass .." überzugehen.
Karl heinz Buchegger schrieb:> Und nein: Äussere Form wird nicht nachträglich in ein Programm> eingebaut, sondern schon während man entwickelt. Gerade konsequent> durchgezogenes Einrückschema macht sehr oft den Unterschied zwischen> "Ich finde einen Fehler leicht" und "Ich finde den Fehler einfach nicht"
Genau DAS ist ja gerade die Stärke von Python. Dort werden die
Block-/Schachtelungsebenen ausschließlich durch Einrückungen
dargestellt.
In der meisten anderen Sprachen, die spezielle Blockbegrenzungen
besitzen, überliest man diese nämlich auch sehr schnell, wenn der
Quelltext ansonsten ordentlich formatiert ist. Eine schöne äußere Form
verleitet beim Lesen und Fehlersuchen zu ziemlicher Nachlässigkeit.
Gerade in C passiert ja auch leicht der folgende Fehler:
1
if (a > 100)
2
a = 100;
wird "einmal schnell" ergänzt um eine Debug-Ausgabe:
1
if (a > 100)
2
printf("Bereichsbegrenzung fuer a!\n");
3
a = 100;
Oh, b muss ja auch noch angepasst werden:
1
if (a > 100)
2
printf("Bereichsbegrenzung fuer a!\n");
3
a = 100;
4
b = 0;
Und plötzlich stellt man fest, dass a und b immer verändert werden,
wohingegen die Debug-Ausgabe nur zu den richtigen Zeitpunkten erscheint.
vielen Dank für die Ratschläge und kaum zu glauben, es scheint
tatsächlich nun zu funktionieren :-).
Jetzt nur noch die Zeiten anpassen und dann kann es in einen ersten
Praxistest gehen, bin gespannt.