Forum: Mikrocontroller und Digitale Elektronik Blinklicht Quellcode 8051


von Tobias D. (tobid)


Lesenswert?

Hallöchen zusammen ;)
Bin noch Neueinsteiger in der MicroController-Technik. Deshalb auch 
solche Anfängerfragen! Ich hoffe, es nervt hier niemanden ;) Also, ich 
habe hier folgenden Quellcode für ein Blinklicht:
1
#include <reg52.h>
2
3
unsigned char x=0;
4
sbit led=P1^5;
5
sbit schalter=P3^1;
6
7
void init (void)
8
{
9
 TMOD=0x01;
10
 TR0=1;
11
 ET0=1;
12
 EA=1;
13
 P1=0;
14
}
15
16
void main(void)
17
{
18
  init();
19
  while(1);
20
}
21
22
void isr_int_T0(void) interrupt 1
23
{
24
  x++;
25
  if (schalter) x++;
26
  if (x==14) led=1;
27
  if (x==28)
28
  {
29
    led=0;
30
    x=0;
31
  }
32
}

Im Groben und Ganzen ist mir das Programm verständlich und klar. Die 
Funktion der ganzen Schaltung soll sein, dass man mit Betätigung des 
Schalters die Blinkfrequenz erhöhen kann. Jedoch verstehe ich ehrlich 
gesagt nicht ganz wie das funktionieren soll ? Nach jedem Überlauf des 
Timers erhöht sich die Zähl-Variable x ja automatisch und wird dann ab 
einem Wert x = 14 erstmal gesetzt und dann jedes mal in der 
Timer-Schleife zurück gesetzt, soweit auch logisch. Aber ab dem Wert 
x=14 müsste sich die Blinkfrequenz dann ja automatisch erhöhen, bis zu 
dem Wert 28, wo x wieder auf 0 gesetzt wird. Und wenn ich den Schalter 
drücke, erhöht die Blinkfrequenz sich pro Durchgang nur schneller als 
ohne Tastendruck.
Klingt vielleicht komisch, aber so konnte ich mir den Quellcode nur 
erklären! Ich hoffe ihr könnt mir helfen!

MfG

von Karl H. (kbuchegg)


Lesenswert?

Tobias D. schrieb:

> Schalters die Blinkfrequenz erhöhen kann. Jedoch verstehe ich ehrlich
> gesagt nicht ganz wie das funktionieren soll ? Nach jedem Überlauf des
> Timers erhöht sich die Zähl-Variable x ja automatisch

automatisch würde ich das so nicht nennen.
x erhöht sich, weil im Überlauf Handler ein
1
  x++;
steht. Dadurch wird x erhöht.

> und wird dann ab
> einem Wert x = 14 erstmal gesetzt und dann jedes mal in der
> Timer-Schleife zurück gesetzt, soweit auch logisch.

Ähm.
Wovon sprichst du da.
Das was du da (etwas unbeholfen) beschreibst findet sich nicht im 
Programmcode.

> Klingt vielleicht komisch, aber so konnte ich mir den Quellcode nur
> erklären! Ich hoffe ihr könnt mir helfen!

AM besten wäre es, wenn du selber Computer spielst und mit Papier und 
Bleistift den Code durchsimulierst. Deine 'Analyse' ist mehr als konfus 
und wenn mich nicht alles täuscht, bist du ziemlich am Holzweg, wie das 
funktioniert.

Ausgangsszenario:
x habe den Wert 0. Der Schalter ist nicht gedrückt. Der Timer läuft in 
den Overflow und als Folge davon wird die Overflow-ISR aufgerufen.
Was passiert im Detail und warum?


Nimm das nicht auf die leichte Schulter sondern stelle wirklich sicher, 
dass du das verstehst. Das ist nämlich wichtig um zu erkennen, warum da 
im Programm ein(*) schwerer Fehler enthalten ist, der die korrekte 
Funktion verhindert und vor allen Dingen auch, wie man ihn beheben kann.

(*) eigentlich zwei, je nachdem wie man es sehen will.

von Matthias K. (matthiask)


Lesenswert?

Tobias D. schrieb:
> Im Groben und Ganzen ist mir das Programm verständlich und klar. Die
> Funktion der ganzen Schaltung soll sein, dass man mit Betätigung des
> Schalters die Blinkfrequenz erhöhen kann.

So ist es. Ohne Schalter wird je Durchlauf x um eins erhöht. Mit 
Schalter um zwei. Damit verdoppelt sich die Blinkfrequenz.

von Tobias D. (tobid)


Lesenswert?

Der Wert x wird durch "x++" von 0 auf 1 gesetzt. Anschließend springt 
der Prozessor wieder in's MainProgramm, weil keine if-Bedingung erfüllt 
ist, und somit automatisch wieder in den Timer, bis dieser überläuft und 
wieder in den Interrupt springt, den Wert von 1 auf 2 erhöhrt usw...
Das ganze passiert bis die Zählvariable x auf 14 ist. Dann wird die LED 
auf 1 gesetzt. Danach geht's wieder ins Main-Programm und somit in den 
Timer, wo die LED wieder auf 0 gesetzt wird. Nach Überlauf wieder ins 
Interrupt, x erhöhen etc. bis x auf 28 ist. Dann wird x auf 0 gesetzt 
und die led auch.

So, nochmal in Ordentlich. Ich hab's oben vielleicht etwas komisch 
beschrieben... Anders verstehe ich es nicht, selbst wenn ich es Zeile 
für Zeile durchgehe. Jedoch verstehe ich nicht, wie man die Frequenz 
dann erhöhen soll. Das hieße ja, der Schalter müsste die ganze Zeit 
geschlossen sein?


Edit: Okay! Ich hab da nen kleinen Denkfehler drin gehabt. Habe die 
ganze Zeit bei dem Schalter an den Taster gedacht!!! Heut ist wohl nicht 
mein Tag :D


Jedoch habe ich noch eine Frage:
Wozu brauche ich EA = 1 ?  ET0 = 1 sagt ja, dass der Timer nach Überlauf 
in eine Interrupt-Service-Routine springen soll.

von Karl H. (kbuchegg)


Lesenswert?

Tobias D. schrieb:
> Der Wert x wird durch "x++" von 0 auf 1 gesetzt. Anschließend springt
> der Prozessor wieder in's MainProgramm, weil keine if-Bedingung erfüllt
> ist,

ok.

> und somit automatisch wieder in den Timer, bis dieser überläuft und
> wieder in den Interrupt springt, den Wert von 1 auf 2 erhöhrt usw...

ok

> Das ganze passiert bis die Zählvariable x auf 14 ist. Dann wird die LED
> auf 1 gesetzt.

jetzt hast du es richtig ausgedrückt :-)
Im Eröffnungsposting hast du nämlich die Hälfte dessen verschluckt :-)

> Danach geht's wieder ins Main-Programm und somit in den
> Timer, wo die LED wieder auf 0 gesetzt wird.

Äh.
Warum.
Ich seh im Hauptprogramm nichts, was die LED wieder auf 0 setzen würde.

> Nach Überlauf wieder ins
> Interrupt, x erhöhen etc. bis x auf 28 ist. Dann wird x auf 0 gesetzt
> und die led auch.

Genau.
Hier wird die LED wieder ausgeschaltet und nicht im Hauptprogramm

> So, nochmal in Ordentlich. Ich hab's oben vielleicht etwas komisch
> beschrieben... Anders verstehe ich es nicht, selbst wenn ich es Zeile
> für Zeile durchgehe. Jedoch verstehe ich nicht, wie man die Frequenz
> dann erhöhen soll.

> Das hieße ja, der Schalter müsste die ganze Zeit
> geschlossen sein?

Aaaah. Hier liegt dein gedankliches Problem.
Ja, genau. Genau das heisst es. Nur solange der Schalter/Taster 
geschlossen ist, wird mit doppelter Frequenz geblinkt.
Dies deshalb weil x dann nicht
  0, 1, 2, 3, 4, 5 ....
zählt, sondern durch
  if (schalter) x++;

hat x bei den LED Abfragen die Werte
  0, 2, 4, 6, 8, 10, ....

und damit wird 14 bzw 28 dann eben jeweils doppelt so schnell erreicht.

Und da liegt jetzt auch der Fehler.

Angenommen x habe den Wert 0, und der Schalter sei nicht geschlossen. 
Dann wird x nach dem ersten ISR Aufruf den Wert 1 haben. Jetzt schliesst 
du den Schalter. Als Folge davon wird in der ISR x jeweils 2 mal um 1 
erhöht. Ab dann zählt x daher

  0, 1, 3, 5, 7, 9, 11, 13, 15, 17, ....

Huch. die 14 sind nie erreicht worden! Und auch die 28 werden nicht 
erreicht werden! x wird ab jetzt nur noch ungerade Zahlen annehmen. Und 
damit wird weder das hier
1
  if (x==14) led=1;
noch das hier
1
  if (x==28)
2
  {
3
    led=0;
4
    x=0;
5
  }
je true sein können. Als Folge davon hört das Blinken komplett auf.
Erst dann, wenn der Schalter wieder ausgeschaltet wird, kann x wieder in 
Einzelschritten zählen und das Blinken setzt früher oder später wieder 
ein. Je nachdem welchen Wert x genau hat (gerade/ungerade), wenn der 
Schalter eingeschaltet wird, blinkt die LED also doppelt so schnell oder 
gar nicht.

von Tobias D. (tobid)


Lesenswert?

Karl heinz Buchegger schrieb:


>> Danach geht's wieder ins Main-Programm und somit in den
>> Timer, wo die LED wieder auf 0 gesetzt wird.

> Äh.
> Warum.
> Ich seh im Hauptprogramm nichts, was die LED wieder auf 0 setzen würde.

Also nicht direkt im Hauptprogramm, aber im Timer wird die LED auf 0 
gesetzt, wenn ich mich nicht komplett täusche, da:
1
P1 = 0;
und die LED liegt ja auf dem Port P1 !



> Und da liegt jetzt auch der Fehler.
>
> Angenommen x habe den Wert 0, und der Schalter sei nicht geschlossen.
> Dann wird x nach dem ersten ISR Aufruf den Wert 1 haben. Jetzt schliesst
> du den Schalter. Als Folge davon wird in der ISR x jeweils 2 mal um 1
> erhöht. Ab dann zählt x daher
>
>   0, 1, 3, 5, 7, 9, 11, 13, 15, 17, ....

Wow, super! Das muss man erstmal bemerken ! ;)

Danke !

von Karl H. (kbuchegg)


Lesenswert?

Tobias D. schrieb:

>> Äh.
>> Warum.
>> Ich seh im Hauptprogramm nichts, was die LED wieder auf 0 setzen würde.
>
> Also nicht direkt im Hauptprogramm, aber im Timer wird die LED auf 0
> gesetzt, wenn ich mich nicht komplett täusche, da:
>
1
P1 = 0;
> und die LED liegt ja auf dem Port P1 !

Schon richtig.

Aber du hast ja vom Hauptprogramm gesprochen.
Es mag dir jetzt wie Korinthenkacken vorkommen, aber gewöhn dir an, die 
Dinge exakt zu beschreiben.
Bei mir ist das noch harmlos, ich kann auch erraten. Aber ein Compiler, 
eine Programmiersprache, nimmt das sehr genau. Daher ist es besser du 
gewöhnst dir das schludern gleich von vorne herein ab. Egal ob du im 
Forum etwas erzählst, oder ob du programmierst.

von Tobias D. (tobid)


Lesenswert?

Das stimmt natürlich ;)
Das ganze Programm funktioniert ja schon nicht, sobald auch nur ein ";" 
fehlt.

Danke !

MfG

von Tobias D. (tobid)


Lesenswert?

So, hab nochmal über das Problem nachgedacht. Als Lösung des Problemes 
habe ich mir überlegt, dass der Prozessor eine Abfrage des X-Wertes nach 
jeder Erhöhung machen müsste. Und das Ganze habe ich mir so gedacht:

1
void isr_int_T0(void) interrupt 1
2
{
3
  x++;
4
  if (x==14) led=1;
5
  if (x==28)
6
7
  if (schalter) x++;
8
  if (x==14) led=1;
9
  if (x==28)
10
  {
11
    led=0;
12
    x=0;
13
  }
14
}

Es mag zwar vielleicht nicht die eleganteste Variante sein, aber es 
müsste doch rein theoretisch funktionieren , oder habe ich wieder einen 
Denkfehler ?

MfG

von Karl H. (kbuchegg)


Lesenswert?

Tobias D. schrieb:

> jeder Erhöhung machen müsste. Und das Ganze habe ich mir so gedacht:

Die Lösung liegt nicht darin, noch mehr Vergleiche auf Gleichheit 
einzuführen.

Die Lösung liegt darin, sich von der Vorstellung zu lösen, dass es 
bestimmte Werte geben muss oder soll, an denen die LED umgeschaltet 
werden soll, sondern sich zu überlegen, was für die Zahlenwerte gelten 
muss, damit die LED leuchtet.

Du hast ja auch noch andere Möglichkeiten für Vergleiche, nicht nur auf 
Gleichheit.
Zb soll doch die LED leuchten, wenn x kleiner als 15 ist

   if (x < 15)
     led = 1;

Das gilt jetzt immer. Denn egal ob die Zählerei nun

   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, ...

lautet, oder ob es

   0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, ....

oder gar

   1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, ....

lautet, immer gibt es Zahlen die kleiner sind als 15 und es gibt welche 
die nicht kleiner sind als 15. Ist x kleiner als 15 dann soll die LED 
leuchten. Ist x nicht kleiner als 15 soll die LED nicht leuchten


    if (x < 15)
      led = 1;
    else
      led = 0;

Jetzt brauchst du nur noch eine Abfrage dafür, dass bei 28 wieder auf 0 
zurückgeschaltet wird. Auch hier wieder: Ob das jetzt 28 oder 29 sind, 
ist eigentlich nicht so wichtig. Wichtig ist, dass x irgendwann mit 
Sicherheit größer als 27 wird.

> Es mag zwar vielleicht nicht die eleganteste Variante sein, aber es
> müsste doch rein theoretisch funktionieren , oder habe ich wieder einen
> Denkfehler ?

Damit erhöhst du x in einem Interrupt Aufruf um 3. Da aber der Interrupt 
Aufruf im Vergleich zum Rest so gut wie keine Zeit braucht, blinkt deine 
LED damit 3 mal so schnell.

>
> MfG

von Jens G. (jensig)


Lesenswert?

warum nicht einfach auf größer/gleich 14 bzw 28 testen?

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.