Hallo,
ich möchte eine kleine LED-Lampe bauen, die mehrere "Programme"
ausführen kann (sprich: LEDs leuchten in verschiedenen Reihenfolgen).
Nun habe ich mir das wie folgt vorgestellt:
Sobald die Schaltung Strom bekommt, wird Programm 1 ausgeführt. In der
Schaltung gibt es einen einzigen Taster. Sobald man ihn dann einmal
drückt, leuchtet die zweite LED auf um "Programm 2" anzuzeigen. Der
Taster kann mehrmals betätigt werden um dann die nächsten Programme auch
auswählen zu können. Drückt man den Taster für 5 Sekunden nicht mehr,
soll das eben ausgewählte Programm dargestellt werden.
Wie setze ich das am besten im Code um??
Zum Einsatz soll ein Attiny 45 kommen!
Hier mal meine Idee: (nur grob programmiert)
1
programm(intprogrammnr){
2
//Hier wird der Ablauf jedes Programm hinterlegt
3
}
4
main-loop{
5
while(1){
6
ifselected_programm==1{
7
programm(1);
8
}
9
//if andere programme...
10
}
11
}
Jetzt müsste ich eine Zustandsänderung am Eingang an dem der Taster
hängt per Interrupt gemeldet bekommen und dann die main-loop
unterbrechen und stattdessen das "Menü" mithilfe der LEDs anzeigen...
Hat da jemand schon ein fertiges Grundgerüst? Komme zwar ganz gut mit
der Programmierung klar, aber wäre doch da sehr an einem Muster
interessiert.
Oder ist mein Ansatz sowieso nicht ganz passen?
Danke schonmal:)
Michael N. schrieb:> Jetzt müsste ich eine Zustandsänderung am Eingang an dem der Taster> hängt per Interrupt gemeldet bekommen
Taster wertet man nicht per Interrupt aus.
Im Artikel Entprellung findest du sauber aufgebaute Funktionalität,
mit der man Tasten auswerten kann. Ganz unten.
Und für deine 'Programme' kannst du dann den Timer gleich mitverwenden.
@Johannes G:
In Wirklich würde hier natürlich programm(selected_programm) stehen.
Danke trotzdem für den Hinweis. Wollte damit oben nur deutlich machen,
dass es eben mehrere gibt und die einfach durchnummeriert sind...
@K. H. Buchegger:
Wenn in meiner mainloop aber nun folgendes passiert:
(Beispielcode)
1
LEDan(1)
2
wait (0,5s)
3
LEDaus(1)
4
LEDan(2)
5
wait (0,5s)
6
LEDaus(2)
7
LEDan(3)
8
wait (0,5s)
9
LEDaus(3)
10
LEDan(4)
11
wait (0,5s)
12
//...alle LEDs werden durchgeschaltet
Dann müssten ja immer zwischendrin eine Funktion zur Prüfung des
Eingangs wo der Taster hängt ausführen.
Dann sähe mein Code in etwa so aus:
(nur für erste LED)
Wenn du deine Wait-Funktionen mit einem Timer machst, dann kannst du
auch gleichzeitig den Taster in einem Timerinterrupt abfragen.
Wenn jedes Unter-Programm wie eine Statemachine aufgebaut ist, brauchst
du auch keine langen Durchlaufzeiten, bis ein Programmwechsel erfolgen
kann.
Du müsstest, wenn gerade z.B. Programm 1 läuft und ein Wechsel auf
Programm 2
erfolgen soll, nicht bis zum Ende von Programm 1 warten.
mfg mf
Wenn ich sowas bauen müsste würde ich sofort an einen Zustandsautomaten
denken. Das ist meiner Meinung der sauberste Weg.
In C kannst du hier für die switch case funktion nutzen, vermutlich als
Moore-Automat.
http://www.peacesoftware.de/ckurs7.html
Würde ich täglich Mikrocontroller programmieren, würde ich mir
wahrscheinlich einfacher tun...
Allerdings habe ich eigentlich nichts damit am Hut, aber bisher (bis auf
den Taster) schon alles zum laufen gebracht.
Genau diese Problematik, dass erst ein Unterprogramm fertiglaufen muss,
habe ich mit einer anderen Schaltung auch (dort ist es ein Schalter
statt eines Tasters, wodurch das nicht soooo schlimm ist, aber trotzdem
stört).
Hast du vielleicht einen Link zu einem kleinen Beispielprogramm wo das
so gemacht wird. Mir ist noch nicht ganz klar wie ich das umsetzen
soll...
@bobtheworker:
Geht es hierbei nur um das abfragen des aktuellen Programmes und dann
die Ausführung eben dieses?? Weil das ist ja mit
"programm(selected_programm);" schon getan. selected_programm müsste
halt nur bei jedem Tastendruck hochgezählt werden.
Und am liebsten würde ich nach dem Tastendruck über die LEDs (z.B. nur
Leuchten der dritten LED --> drittes Programm gewählt) das aktuell
ausgewählte Programm kurz anzeigen (bzw. weiterschalten dann mit
erneutem Tastendruck)
Michael N. schrieb:> @Johannes G:> In Wirklich würde hier natürlich programm(selected_programm) stehen.> Danke trotzdem für den Hinweis. Wollte damit oben nur deutlich machen,> dass es eben mehrere gibt und die einfach durchnummeriert sind...>> @K. H. Buchegger:> Wenn in meiner mainloop aber nun folgendes passiert:> (Beispielcode)>
1
> LEDan(1)
2
> wait (0,5s)
3
> LEDaus(1)
4
> LEDan(2)
5
> wait (0,5s)
6
> LEDaus(2)
7
> LEDan(3)
8
> wait (0,5s)
9
> LEDaus(3)
10
> LEDan(4)
11
> wait (0,5s)
12
> //...alle LEDs werden durchgeschaltet
13
>
Dachte mir schon, das sowas kommen wird.
Das, genau das, ist der völlig falsche Weg.
Lern doch bitte erst mal ein paar Dinge aus der Programmiersprache, die
du benutzt. Als erstes könntest du mal mit Arrays anfangen.
Und wenn du dann erst mal soweit bist, wie du diese Codemonster mittels
Arrays auf 3 Zeilen eindampfen kannst, dann gehts weiter, wie man da mit
einem Timer die Warterei erledigt.
Und dann ergibt sich plötzlich auch völlig klar und einfach, wie man da
eine Tastenbedienung einbaut.
...Okay^^ Sowas dachte ich mir schon, dass gleich kommt...
Ich habe schon relative große Projekte in PHP programmiert und auch in
Java und Pyhton einiges zu tun gehabt.
Für das oben hätte ich mal gesagt:
1
for(inti=1;i<=8;i++){
2
LEDan(i);
3
_delay_ms(500);
4
LEDaus(i);
5
}
Besser so um zu zeigen, dass ich das kann?^^
Um die Wartezeit aufzuteilen und immer wieder zu prüfen ob die Taste
gedrückt wurde, hätte ich das halt dann wie folgt gemacht:
1
//funktion zum warten und Tasterabrufen
2
voidwait(inttimer){
3
for(inti=0;i<=timer;i=i+10){
4
//Es sind dann nur immer volle 10ms, aber der Programmablauf muss nicht so genau sein
5
_delay_ms(10);
6
check_taster();
7
}
8
}
9
//main:
10
for(inti=1;i<=8;i++){
11
LEDan(i);
12
wait(500);
13
LEDaus(i);
14
}
Ich weiß zwar noch nicht genau für was ich da jetzt ein Array nutzen
soll, aber je nach dem in welcher Reihenfolge die leuchten sollen,
könnte ich natürlich ein Array z.B. mit (1, 3, 5, 7, 8, 6, 4, 2)
befüllen und die dann in der Reihenfolge aufleuchten lassen...
(foreach bzw. gibts das glaube ich in C ja nicht!? Aber array[i] würde
natürlich den gewünschten Wert tragen)
Eigentlich geht es mir nur um den Teil, wie ich an den Tastendruck komme
und dann halt zB für 3 Sekunden den normalen Programmablauf "stoppe"
(eben zB das hintereinander anschalten der LEDs) und solange das
ausgewählte Programm mit den LEDs anzeige.
Ich denke, das Codemonster ist gebändigt! Und wie hab ich mir das mit
dem Timer nun vorzustellen?
(Habe ich bis jetzt nur ein paarmal genutzt, aber grundlegend ist mir
schon klar wie das Ding arbeitet. Ich weiß nur noch nicht wie ich das
dann passend einbauen soll...)
Michael N. schrieb:> Würde ich täglich Mikrocontroller programmieren, würde ich mir> wahrscheinlich einfacher tun...> Allerdings habe ich eigentlich nichts damit am Hut, aber bisher (bis auf> den Taster) schon alles zum laufen gebracht.>> Genau diese Problematik, dass erst ein Unterprogramm fertiglaufen muss,> habe ich mit einer anderen Schaltung auch (dort ist es ein Schalter> statt eines Tasters, wodurch das nicht soooo schlimm ist, aber trotzdem> stört).>> Hast du vielleicht einen Link zu einem kleinen Beispielprogramm wo das> so gemacht wird. Mir ist noch nicht ganz klar wie ich das umsetzen> soll...>> @bobtheworker:> Geht es hierbei nur um das abfragen des aktuellen Programmes und dann> die Ausführung eben dieses?? Weil das ist ja mit> "programm(selected_programm);" schon getan. selected_programm müsste> halt nur bei jedem Tastendruck hochgezählt werden.> Und am liebsten würde ich nach dem Tastendruck über die LEDs (z.B. nur> Leuchten der dritten LED --> drittes Programm gewählt) das aktuell> ausgewählte Programm kurz anzeigen (bzw. weiterschalten dann mit> erneutem Tastendruck)
im prinzip das innenleben deiner funktion programm() ^^ Wenn du dich gut
anstellst, brauchst du dich bei nem Zustandsautomaten um nichts mehr
kümmern ( nichts hochzählen), Tastendruck bedeutet einfach nur noch
wechsle auf den nächsten State. Setzt halt vorher gute Planung vorraus.
Zusatzt Features kannst du natürlich immer einbinden.
Taster -> wechsle State -> zeige State an -> Starte Programm.
Allerdings wenn du mit der Tasterabfrage schon den Microcontroller voll
auslasten willst, frage ich mich wie du die Programme laufen lassen
willst ^^
Tastenabfrage mit Interrupt du wartest auf ne steigende Flanke und
fragst im Interrupt nach den Eingang "1" ab ja-> state wechsel nein->
interrupt verwerfen. Zwischen Interrupt und Abfrage sollte min 1ms
liegen. Das sollte die Prellungen abfangen und schneller drückt ein
Durchschnittlicher Mensch eh nicht
man sollte sich hierzu mal einfache Multitasking Konzepte ansehen...
Ein einfacher Ansatz:
[c]
initTimer()
{
// starte einen Timer der alle 10 ms einen Interrupt auslöst
}
TimerISR()
{
IntCounter++;
if (IntCounter & 1) // Aktion jeden 2. Takt, 20 ms
{
// Tasterzustand abfragen, wenn geändert ProgrammNr hochzählen
}
if (IntCounter % 5) // Aktion jeden 5. Takt, 500 ms
{
// lade LED Muster aus aktueller Programmtabelle
}
}
main()
{
initTimer();
// Interrupts freigeben
while (1)
{
// wenn ProgrammNr geändert dann aktiviere neues Programm
}
}
{/c]
das ist alles. Die 'Programme' können Tabellen (Arrays) sein mit den
Bitmustern die nacheinander dargestellt werden sollen. Zujm Umschalten
der Programme setzt man einen Zeiger mit Namen 'AktuellesProgramm' auf
eine Startadresse der gespeicherten Programme. Die Tabelle kann noch
zweidmensional gemacht werden um z.B. die Zeit je Muster variabel zu
gestalten.
Die Zeit von 10 ms ist dann die kleinste Zeit im System mit der
Änderungen erfasst (Taster) oder Ausgaben (LED Muster umschalten)
gemacht werden können.
Mist, ein falsches Zeichen in der C-Formatkennung. Und für 500 ms muss
man bei 10 ms Takt natürlich % 50 rechnen, aber es ging ja nur ums
Prinzip. Und wenn die 50 Takte erreicht sind wieder auf Null setzen
sonst passt es beim Überlauf nicht, für verschiedene Zykluszeiten sind
mehrere Zähler anzulegen. Und in der Int Routine nur das nötigste
machen, siehe auch das AVR-GCC Tutorial (wenn das dein Spielsystem ist).
break();//<<Schaffe ich es, alles abzubrechen, sodass sogar in der Mainloop der Unterprogrammaufruf gestoppt wird???
33
}
34
elseif(time_to_next_input<=1){//time_to_next_input noch nicht auf 0
35
time_to_next_input--;
36
}
37
if(!(gedrückt)){
38
last_input=0;
39
}
40
}
41
42
voidwait(inttimer){
43
for(inti=0;i<=timer;i=i+10){
44
//Während Wartezeit wird der Taster alle 10ms
45
_delay_ms(10);
46
check_taster();
47
}
48
}
49
50
voidprogramm(intunterprogramm){
51
switch(x)
52
{
53
case1:
54
//Von links nach rechts
55
for(inti=1;i<=8;i++){
56
LEDan(i);
57
wait(500);
58
LEDaus(i);
59
}
60
61
case2:
62
//Von rechts nach links
63
for(inti=8;i>=1;i--){
64
LEDan(i);
65
wait(500);
66
LEDaus(i);
67
}
68
//restliche Cases, bis 8
69
}
70
}
71
72
intmain(){
73
while(1){
74
if(show_menu==1){
75
//Menü wird angezeigt
76
alle_LEDaus();
77
LEDan(unterprogramm);
78
wait(3000);//3 Sek warten, wobei der Taster wieder abgefragt wird
79
}
80
else{
81
programm(unterprogramm);
82
}
83
84
}
85
}
Voraussetzung wäre allerdings, dass das break() oben, bis in die
Mainschleife zurückspringen kann.
Funktioniert das über mehrere aufgerufene Funktionen hinweg???
Michael N. schrieb:> Ich weiß zwar noch nicht genau für was ich da jetzt ein Array nutzen> soll, aber je nach dem in welcher Reihenfolge die leuchten sollen,> könnte ich natürlich ein Array z.B. mit (1, 3, 5, 7, 8, 6, 4, 2)> befüllen und die dann in der Reihenfolge aufleuchten lassen...> (foreach bzw. gibts das glaube ich in C ja nicht!? Aber array[i] würde> natürlich den gewünschten Wert tragen)
Damit fängt es schon mal an.
Jetzt bist du auf Kurs.
Nur:
Anstelle dieser for-Schleife machst du jetzt etwas ganz anderes:
Du bemühst einen Timer für das Timing. Der Timer sorgt dafür, dass eine
bestimmte Funktion, die ISR (Interrupt Service Routine) in regelmäsigen
zeitlichen Abständen aufgerufen wird.
zb alle 10 Millisekunden
Damit baust du das dann um:
Du hast ein Array, in dem die LED-Nummern stehen der LED in der
Reihenfolge in der sie aufleuchten sollen.
Und bei jedem Zeitschritt lässt du dann die jeweils nächste aufleuchten.
Im Prinzip so für 1 "Programm"
1
uint8_tledNummer[6]={1,2,3,4,3,2};
2
uint8_tactStep;
3
4
ISR(...)
5
{
6
ledmitderNummerledNummer[actStep]ausschalten
7
8
actStep++;
9
if(actStep==6)
10
actStep=0;
11
12
ledmitderNummerledNummer[actStep]einschalten
13
}
14
15
intmain()
16
{
17
...
18
}
dadurch, dass die ISR regelmässig, ohne dein Zutun aufgerufen wird,
arbeitet sie sich selbst völlig selbsttätig durch die
"Programmbeschreibung" durch.
Und da hast du jetzt deine einfache Programmwechselfunktionalität:
alles was du tun musst, ist diesem Mechanismus von aussen je nach
'Programm' ein anderes Array unterzujubeln. Hast du ihm das
untergejubelt, dann arbeitet die ISR ein ganz anderes "Leucht-Programm"
ab.
Zb. könnte man anstelle des 1-D Arrays ein 2D Array nehmen, wenn alle
Leucht-Programme gleich lang sind.
Die ISR ist mithilfe der Hilfsvariablen lastLitLed so geschrieben, dass
man zu jedem Zeitpunkt der Variablen actPgm einen neuen Wert verpassen
kann und dann dieses Programm abgearbeitet wird.
Und genau das tut main(). Wird ein Tastendruck erkannt, dann wird
einfach ein neuer Wert an actPgm zugewiesen.
Und das erkennen des Tastendrucks erfolgt mit den Hilfsfunktionen aus
Entprellung. Die ihrerseits wieder einen Timer benötigen.
Aber: Wundersamerweise gibt es kein einziges _delay_ms im Programm.
Nirgends wird gewartet. Und das ist schon mal ein extrem gutes Zeichen.
Na ich dachte das soll man ohne Interrupts lösen^^ (Stand oben i-wo mal)
Aber eure beiden Lösungen sind wirklich super.
Werde das später gleich mal in passenden Code ausarbeiten.
Durch den Timerinterrupt erfolgt die eigentliche Ausgabe (+
Tasterabfrage)
Und sobald sich da etwas ändert, wird einfach etwas anderes zur Ausgabe
herangezogen (eben immer die passenden Muster für das jeweilige
Unterprogramm)
Michael N. schrieb:> Na ich dachte das soll man ohne Interrupts lösen^^ (Stand oben i-wo mal)
Gemeint war:
Tastenabfrage ohne externen Interrupt.
Interrupt ist nicht gleich Interrupt. In deinem µC gibt es viele
verschiedene Interrupts. Einen Taster einen Interrupt auslösen zu lassen
ist eine schlechte Idee. Einen Timer regelmässig einen Interrupt
auslösen zu lassen, bei dem dann nachgesehen wird, ob eine Taste
gedrückt ist, ist hingegen eine gute Idee. Genau das macht der Code, der
in Entprellung ganz unten angeführt ist.
> Aber eure beiden Lösungen sind wirklich super.
Es ist die Standard-Lösung für µC-Dinge.
Du musst weg von der Denkweise
Zuerst macht mein Programm das
dann macht es das
dann wartet es ein wenig
und dann macht es das
Statt dessen brauchst du
ich habe einen Zeitgeber
der löst alle x Zeiteinheiten ein Ereignis aus
-> was gibt es jetzt, genau zum jetzigen Zeitpunkt zu tun
wenn du deine Problemstellung auf diese Denkweise anpasst, dann gehst du
praktisch nie verkehrt und landest automatisch bei einem Programm, bei
dem du zu jedem Zeitpunkt Benutzereingaben aktiv hast und die, bei
laufendem Betrieb vom restlichen Programm, immer das richtige auslösen.
Man kann es z.B. so machen:
Beitrag "Re: AVR Sleep Mode / Knight Rider"
Es handelt sich um 2 Statemaschinen:
"sample" in "main.c" wählt aus, welches Muster angezeigt werden soll und
wird mit der Taste weiter gezählt.
"step" in "knightrider.c" zählt den Anzeigeschritt im ausgewählten
Muster und wird per Timer weiter gezählt.
Peter