Hi Leute,
ich habe ein Problem mit einer Funktion...
_Funktion in MPLAB_
Im Grunde sieht die Funktion so aus:
1
staticchardata_read_buf[100];//buffer for queueing data to be read
2
staticintread_cnt=0;//counter icnrements through data_red_buf
3
4
voidi2c_drv_read_data_get(int*data){
5
6
*data=data_read_buf[read_cnt++];
7
if(read_cnt==100)read_cnt=0;
8
9
return;
10
}
In das Read-Buffer werden Daten welche via I2C empfangen werden
zwischengespeichert. Das Funktioniert soweit auch (das Array füllt sich
mit Daten ungleich 0). Die oben dargestellte Funktion soll nun diese
Daten element für element in "int* data" schreiben.
Auch das Inkrementieren durch die Elemente funktioniert tadellos.
Jedoch nimmt *data nicht die Werte aus data_read_buf an, sondern
verbleibt bei 0.
Ich verwende hier einen PIC24hj128gp202 mit der MPLAB X IDE v5.50 mit
dem xc16 v1.70 compiler.
Ich weiß jetzt nicht ob es am Debugger liegt. Kann die Werte leider nur
im Debug-Modus auslesen, da ich sonst keine Daten/Info-Ausgänge habe.
_Funktion mit online Compiler klappt_
Ich habe eine ähnliche Funktion in einem online c-compiler testweise
ausgewertet. Diese verhält sich so wie sie soll...
1
#include<stdio.h>
2
3
intdata_array[]={0,100,200,300,400};
4
5
voidfoo(int*data){
6
7
staticinti=0;
8
*data=data_array[i++];
9
if(i>=5)i=0;
10
11
return;
12
}
13
14
intmain()
15
{
16
17
inti=0;
18
for(i=0;i<5;i++){
19
inttmp;
20
foo(&tmp);
21
printf("%i\n",tmp);
22
}
23
24
return0;
25
}
Diese Funktion gibt dann aus:
0
100
200
300
400
_eigentliche Frage_
Habe ich iwo einen Fehler gemacht? Kann mit jemand das Verhalten
erklären?
Aaron K. schrieb:> Habe ich iwo einen Fehler gemacht?
Eine Deklaration
1
intirgendwas;
ist fast immer scheisse wenn man die Registerbreite bzw. die
Architektur des Zielsystems nicht kennt. Kann dein online-
Compiler das riechen was du willst?
An welcher Stelle genau sollte, das ein Problem darstellen?
Ich verwende bei beiden Beispielen nur int-Variablen, es wird nichts
bitwise manipuliert, also ist dier Registerbreite doch völlig egal.
Bei meiner Frage gehts ja auch darum, warum es in der online IDE funzt,
aber in MPLAB nicht. Ist ja an sich der selbe Code in beiden Fällen. Nur
iwie tut die MPLAB-Funktion nicht was sie soll und ich weiß nicht warum
...
Aaron K. schrieb:> Habe ich iwo einen Fehler gemacht? Kann mit jemand das Verhalten> erklären?
Das ist schwer zu beantworten mit den Infos die du uns gegeben hast.
Dein "online c-compiler" Beispiel weist einige Unterschiede auf zu
deinem ursprünglichen Beispiel. Z.B. static char data_read_buf[] vs. int
data_array[].
Aaron K. schrieb:> Bei meiner Frage gehts ja auch darum, warum es in der online IDE funzt,> aber in MPLAB nicht. Ist ja an sich der selbe Code in beiden Fällen. Nur> iwie tut die MPLAB-Funktion nicht was sie soll und ich weiß nicht warum> ...
Hast du denn data_read_buf[] mal mit Pseudo-Werten bei der
Initialisierung gefüllt, und geguckt was dann passiert?
Bzw. was passiert mit data nach dem schreiben? Kann es sein, dass das
Kopieren einfach weg-optimiert wird?
>> Welchen Sinn soll das "return" hier haben? Was würde die Funktion> machen, wenn das da nicht steht?
Ist einfach eine gute Angewohntheit selbst bei einer Void-Funktion ein
return reinzusetzen. Damit jeder weiß hier ist die Funktion zu ende und
es wurde nichts vergessen (ende ist hier auch wirklich vorgesehen)
Gelangweilter schrieb:> Aaron K. schrieb:>> In das Read-Buffer werden Daten welche via I2C empfangen werden>> zwischengespeichert.>> Lass mich raten: Mit einem Interrupt?
Nope habe nicht einen einzigen Interrupt am laufen. Alles via Polling.
Aaron K. schrieb:> Ist einfach eine gute Angewohntheit selbst bei einer Void-Funktion ein> return reinzusetzen.
Nee. Ist keine gute Angewohnheit, sondern überflüssig und irritierend.
So, als ob der Programmierer sich nicht sicher ist, was er da eigentlich
will.
Mit der gleichen Argumentation könntest Du hinter jedes if() auch einen
leeren else-Zweig packen, um klarzumachen, daß der da nicht
versehentlich vergessen wurde:
1
if (a == 4)
2
{
3
printf("A ist vier\n");
4
}
5
else
6
{ };
Oder in einem switch-Statement jeden unbenutzten Wert einzeln aufführen
und/oder einen leeren default-Zweig einfügen.
Ich finde, dass es eine gute Angewohnheit ist (wenn es konsquent überall
gemacht wird). Dann kann ich auf den ersten Blick sehen, wo die Ausgänge
aus der Funktion sind und kann sehr einfach den Datenfluss
zurückverfolgen, ohne jede Funktion von Anfang bis Ende durchgehen zu
müssen.
Aaron K. schrieb:> Nope habe nicht einen einzigen Interrupt am laufen. Alles via Polling.
Na dann, nächste Stufe:
Aaron K. schrieb:> Im Grunde sieht die Funktion so aus:
Zeige die Originalfunktion, und nicht eine frei erfundene Lügenfunktion!
DerEgon schrieb:> Es ist überflüssig, denn auch das Ende einer Funktion ist ein "Ausgang".> Immer.
Es gibt bei allen deinen Funktionen einen logischen und sinnvollen Weg
die Funktion am Ende (letzte Zeile) zu verlassen?
Wilhelm M. schrieb:> Dann zeige mal den Caller, bisher haben wir nur den Callee gesehen.
Sry. Was meinst du mit Caller/Callee?? Kann mit den Begrifflichkeiten
nichts anfangen.
Aaron K. schrieb:> Wilhelm M. schrieb:>> Dann zeige mal den Caller, bisher haben wir nur den Callee gesehen.>> Sry. Was meinst du mit Caller/Callee?? Kann mit den Begrifflichkeiten> nichts anfangen.
Caller = Aufrufer
Gelangweilter schrieb:> Aaron K. schrieb:>> Nope habe nicht einen einzigen Interrupt am laufen. Alles via Polling.>> Na dann, nächste Stufe:>>> Aaron K. schrieb:>> Im Grunde sieht die Funktion so aus:>> Zeige die Originalfunktion, und nicht eine frei erfundene Lügenfunktion!
Das ist meine Originalfunktion.
1
staticchardata_read_buf[100];//buffer for queueing data to be read
2
staticintread_cnt=0;//counter icnrements through data_red_buf
3
4
voidi2c_drv_read_data_get(int*data){
5
6
*data=data_read_buf[read_cnt++];
7
if(read_cnt==100)read_cnt=0;
8
9
return;
10
}
11
12
intmain(void){
13
14
TRISAbits.TRISA0=OUTPUT;
15
TRISAbits.TRISA1=OUTPUT;
16
17
i2c_drv_init();
18
19
inti=0;
20
for(i=0;i<0xffff;i++)continue;
21
for(i=0;i<0xffff;i++)continue;
22
for(i=0;i<0xffff;i++)continue;
23
24
intdata_bridge=0;
25
intdata_temperature=0;
26
27
while(1){
28
29
if(i2c_drv_manager()==1){
30
31
for(i=0;i<0xffff;i++)continue;
32
33
LATAbits.LATA1=1;
34
pressure_sensor_start_measurement();
35
LATAbits.LATA1=0;
36
37
inttmp=0;
38
i2c_drv_read_data_get(&tmp);
39
data_bridge=tmp<<8;
40
i2c_drv_read_data_get(&tmp);
41
data_bridge+=tmp;
42
43
i2c_drv_read_data_get(&tmp);
44
data_temperature=tmp<<3;
45
i2c_drv_read_data_get(&tmp);
46
data_temperature+=tmp>>5;
47
48
tmp=0;
49
}
50
}
51
52
return1;
53
}
Der I2C_manager arbeitet im Grunde die I2C-Tasks ab. Der funktioniert
soweit auch.
pressure_sensor_start_measurement setzt die Tasks die notwendig sind um
Daten aus einem Sensor zu lesen. Auch das funktioniert soweit tadellos.
Das Problem entsteht wie gesagt erst an der Stelle, wo die Daten aus
einem Software-Read-Buffer via Pointer an eine andere Adresse kopiert
werden. (die void i2c_drv_read_data_get(int* data) Funktion). um genau
zu sein macht dort soweit ich das bewerten kann nur die Zeile...
1
*data=data_read_buf[read_cnt++];
...Probleme. Da genau hier der Wert des Pointers 0 bleibt, während im
Array an gegebener Stelle Werte ungleich 0 stehen.
[/c]
Wilhelm M. schrieb:> Aaron K. schrieb:>> Wilhelm M. schrieb:>>> Dann zeige mal den Caller, bisher haben wir nur den Callee gesehen.>>>> Sry. Was meinst du mit Caller/Callee?? Kann mit den Begrifflichkeiten>> nichts anfangen.>> Caller = Aufrufer
Ist das nicht der Fuktionsname?
Aber was ist dann ein CAllee??? Tippfehler?
Aaron K. schrieb:> Wilhelm M. schrieb:>> Aaron K. schrieb:>>> Wilhelm M. schrieb:>>>> Dann zeige mal den Caller, bisher haben wir nur den Callee gesehen.>>>>>> Sry. Was meinst du mit Caller/Callee?? Kann mit den Begrifflichkeiten>>> nichts anfangen.>>>> Caller = Aufrufer>> Ist das nicht der Fuktionsname?>> Aber was ist dann ein CAllee??? Tippfehler?
Nein. Callee ist "der Aufgerufene"
Aaron K. schrieb:> void i2c_drv_read_data_get(int* data) {> *data = data_read_buf[read_cnt++];> if (read_cnt == 100)read_cnt = 0;> return;> }
Wäre diese Funktion nicht so viel einfacher:
1
inti2c_drv_read_data_get(void){
2
intc=read_cnt;
3
read_cnt=read_cnt==99?0:read_cnt+1;
4
returndata_read_buf[c];
5
}
Wozu die Schnippselei mit dem Pointer?
Mombert H. schrieb:> DerEgon schrieb:>> Es ist überflüssig, denn auch das Ende einer Funktion ist ein "Ausgang".>> Immer.>> Es gibt bei allen deinen Funktionen einen logischen und sinnvollen Weg> die Funktion am Ende (letzte Zeile) zu verlassen?
Fast immer. Besser in den seltenen Fällen, wo das nicht so ist, ein "/*
unreachable */" ans Ende schreiben. Besser den häufigen Standardfall
einfach machen als überall etwas redundantes hinzuschreiben...
Aaron K. schrieb:> pressure_sensor_start_measurement setzt die Tasks die notwendig sind um> Daten aus einem Sensor zu lesen. Auch das funktioniert soweit tadellos.
FreeRTos?
Welche Optimierung hast du eingeschaltet?
da
int data_bridge = 0;
int data_temperature = 0;
nicht weiter genutzt werden, könnte das ganze Gespeichere ignoriert
werden.
versuche mal die beiden Variablen global und volatile zu deklarieren.
Wilhelm M. schrieb:> Aaron K. schrieb:>> int tmp = 0;>> i2c_drv_read_data_get(&tmp);>> Und was erwartest Du, was hier passiert?
Ich möchte die Daten in tmp zwischenspeichern und den beiden variablen
data_bridge und data_temperature zuweisen.
Die empfangenen i2c Bytes sind wie folgt aufgabaut:
1
Bit|1|2|3|4|5|6|7|8|
2
------|---|---|---|---|---|---|---|---|
3
Byte1|Flags|HighBytePressure|
4
Byte2|LowBytePressure|
5
Byte3|HighByteTemperature|
6
Byte4|LowByteTe|Dontcare|
Ich muss dei Daten quasi wieder zusammenkleben. Lässt sich sicherlich
ohne tmp lösen, ist so finde ich einfach und tuts auch.
Simon schrieb:> Welche Optimierung hast du eingeschaltet?> da> int data_bridge = 0;> int data_temperature = 0;>> nicht weiter genutzt werden, könnte das ganze Gespeichere ignoriert> werden.>> versuche mal die beiden Variablen global und volatile zu deklarieren.
Optimierungsstufe 0 also quasi keine Optimierung. Aber ich versuche das
mal.
Aaron K. schrieb:> Wilhelm M. schrieb:>> Aaron K. schrieb:>>> int tmp = 0;>>> i2c_drv_read_data_get(&tmp);>>>> Und was erwartest Du, was hier passiert?>> Ich möchte die Daten in tmp zwischenspeichern und den beiden variablen> data_bridge und data_temperature zuweisen.> Ich muss dei Daten quasi wieder zusammenkleben. Lässt sich sicherlich> ohne tmp lösen, ist so finde ich einfach und tuts auch.
Wo wird denn Dein Buffer data_read_buf gefüllt. Das fehlt immer noch ...
Simon schrieb:> versuche mal die beiden Variablen global und volatile zu deklarieren.>>>> if (!have_newpost_anchor && (!topic_last_read_at ||> topic_last_read_at < 1656070342000)) {> document.write('<a name="new" id="new"></a>');> have_newpost_anchor = true;> }
oder gucke dir den generierten Assembler-Code an, da siehst du auch gut
was passiert.
Wilhelm M. schrieb:> Wo wird denn Dein Buffer data_read_buf gefüllt. Das fehlt immer noch ...>
deshalb fragte ich:
Simon schrieb:> Hast du denn data_read_buf[] mal mit Pseudo-Werten bei der> Initialisierung gefüllt, und geguckt was dann passiert?> Bzw. was passiert mit data nach dem schreiben? Kann es sein, dass das> Kopieren einfach weg-optimiert wird?
Wilhelm M. schrieb:> Aaron K. schrieb:>> Wilhelm M. schrieb:>>> Aaron K. schrieb:>>>> int tmp = 0;>>>> i2c_drv_read_data_get(&tmp);>>>>>> Und was erwartest Du, was hier passiert?>>>> Ich möchte die Daten in tmp zwischenspeichern und den beiden variablen>> data_bridge und data_temperature zuweisen.>>> Ich muss dei Daten quasi wieder zusammenkleben. Lässt sich sicherlich>> ohne tmp lösen, ist so finde ich einfach und tuts auch.>> Wo wird denn Dein Buffer data_read_buf gefüllt. Das fehlt immer noch ...
>> Das Buffer wird hier gefüllt. Es geht aber wie gesagt darum, dass die> Daten nicht an den Wert des Pointers kopiert werden. :)
Oh man, und wo / wie wird das aufgerufen?
>>>> Das Buffer wird hier gefüllt. Es geht aber wie gesagt darum, dass die>> Daten nicht an den Wert des Pointers kopiert werden. :)>> Oh man, und wo / wie wird das aufgerufen?
Wie gesagt, dass funktioniert alles soweit... Es geht wirklich nur darum
warum der Wert des Pointers nicht geändert wird. Aber falls es hilft
schicke ich gerne noch den restlichen code :)
1
/*
2
* manages events of i2c_drv. Has to be called periodically (higher frequency than i2c events)
3
*
4
* @since 1.0.0
5
* @category hardware
6
* @param {void}
7
* @returns {void}
8
* @example none
9
*/
10
inti2c_drv_manager(void){
11
12
staticinti=0;
13
14
if(busy_flag==FALSE){
15
return1;
16
}
17
18
switch(cmd_buf[i]){
19
20
//transmit
21
caseI2C_DRV_CMD_TX_START:
22
if(i2c_drv_start_set_check()==TRUE){
23
break;
24
}else{
25
return0;
26
}
27
caseI2C_DRV_CMD_TX_RESTART:
28
if(i2c_drv_restart_set_check()==TRUE){
29
break;
30
}else{
31
return0;
32
}
33
caseI2C_DRV_CMD_TX_STOP:
34
if(i2c_drv_stop_set_check()==TRUE){
35
break;
36
}else{
37
return0;
38
}
39
caseI2C_DRV_CMD_TX_ACK:
40
if(i2c_drv_ack_set_check()==TRUE){
41
break;
42
}else{
43
return0;
44
}
45
caseI2C_DRV_CMD_TX_NACK:
46
if(i2c_drv_nack_set_check()==TRUE){
47
break;
48
}else{
49
return0;
50
}
51
caseI2C_DRV_CMD_TX_DATA:
52
if(i2c_drv_data_set_check()==TRUE){
53
break;
54
}else{
55
return0;
56
}
57
58
//recieve
59
caseI2C_DRV_CMD_RX_ACK:
60
if(i2c_drv_ack_get_check()==TRUE){
61
i2c_drv_ack_get();
62
break;
63
}else{
64
return0;
65
}
66
caseI2C_DRV_CMD_RX_NACK:
67
if(i2c_drv_nack_get_check()==TRUE){
68
i2c_drv_nack_get();
69
break;
70
}else{
71
return0;
72
}
73
caseI2C_DRV_CMD_RX_DATA:
74
if(i2c_drv_data_get_check()==TRUE){
75
i2c_drv_data_get();
76
break;
77
}else{
78
return0;
79
}
80
81
//general
82
caseI2C_DRV_CMD_END:
83
break;
84
}
85
86
//skip first cmt incremention, then increment every time
Ich vermute jetzt mal, dass Du eigentlich ein FIFO erstellen wolltest.
Das gibt Dein Code aber nicht her. Wenn also die Daten vom I2C langsamer
eintreffen als Deine main-loop, dann geht es eben nicht, weil der Leser
aus der FIFO den Schreiber in die FIFO überholt.
Edit: Bzw. Deine main-loop wir ggf. sogar verlassen!
Wilhelm M. schrieb:> Ich vermute jetzt mal, dass Du eigentlich ein FIFO erstellen wolltest.> Das gibt Dein Code aber nicht her. Wenn also die Daten vom I2C langsamer> eintreffen als Deine main-loop, dann geht es eben nicht, weil der Leser> aus der FIFO den Schreiber in die FIFO überholt.
Dei Main kann nicht schneller sein als der manager daten bereitstellt.
Da die Daten erst aus dem Buffer ausgelesen werden, wenn die letzte
Nachricht über den Manager vollständig abgearbeitet wurde.
Und ja es ist Fifo/Ringbuffer. Aber auch das stellt kein Problem dar.
Wie ich bereits erwähnt habe...
Ich möchte Daten aus einem Buffer, welche ungleich null sind an den Wert
eines Pointers kopieren. Das funktioniert jedoch nicht, weil die Daten
die als Wert des Pointers abgespeichert nach wie vor 0 betragen, obwohl
im eigentliche Buffer Daten ungleich 0 sind...
Zugegeben das Fifo ist etwas eigensinnig gecodet. Aber es funktioniert
beim debuggen tadellos :) Es handelt sich hierbei eher um eine
Auswertung eines Sensors. Aus dem Grund sind solche Feinheiten (wie ein
gut lesbares/wartbares FIFO) auf später aufgeschoben.
Wilhelm M. schrieb:> Aaron K. schrieb:>> *>> * @since 1.0.0>> * @category hardware>> * @param {void}>> * @returns {void}>> * @example none>> */>> Schönes Beispiel für falsche (Doku-) Kommentare
Danke wird verbessert. Vier Augen sehen immernoch mehr als 2 ;)
Wilhelm M. schrieb:> Ich vermute jetzt mal, dass Du eigentlich ein FIFO erstellen wolltest.> Das gibt Dein Code aber nicht her. Wenn also die Daten vom I2C langsamer> eintreffen als Deine main-loop, dann geht es eben nicht, weil der Leser> aus der FIFO den Schreiber in die FIFO überholt.>> Edit: Bzw. Deine main-loop wir ggf. sogar verlassen!
1
intmain(void){
2
3
TRISAbits.TRISA0=OUTPUT;
4
TRISAbits.TRISA1=OUTPUT;
5
6
i2c_drv_init();
7
8
inti=0;
9
for(i=0;i<0xffff;i++)continue;
10
for(i=0;i<0xffff;i++)continue;
11
for(i=0;i<0xffff;i++)continue;
12
13
intdata_bridge=0;
14
intdata_temperature=0;
15
16
while(1){
17
18
if(i2c_drv_manager()==1){
19
20
for(i=0;i<0xffff;i++)continue;
21
22
LATAbits.LATA1=1;
23
pressure_sensor_start_measurement();
24
LATAbits.LATA1=0;
25
26
volatileinttmp=0;
27
i2c_drv_read_data_get(&tmp);
28
data_bridge=tmp<<8;
29
i2c_drv_read_data_get(&tmp);
30
data_bridge+=tmp;
31
32
i2c_drv_read_data_get(&tmp);
33
data_temperature=tmp<<3;
34
i2c_drv_read_data_get(&tmp);
35
data_temperature+=tmp>>5;
36
37
tmp=0;
38
}
39
}
40
41
return1;
42
}
Wie kann der Main-Loop hier verlassen werden? Einmal in der While kein
zurück mehr. Gibt kein Goto, Return oder break innerhalb der Schleife.
Übersehe ich da was ???
Aaron K. schrieb:> Wie kann der Main-Loop hier verlassen werden? Einmal in der While kein> zurück mehr. Gibt kein Goto, Return oder break innerhalb der Schleife.> Übersehe ich da was ???
Ja, mein Fehler. Hatte Deinen Code falsch im Kopf.
Aaron K. schrieb:> Zugegeben das Fifo ist etwas eigensinnig gecodet. Aber es funktioniert> beim debuggen tadellos :)
Ich kann kein FiFo erkennen.
Und was funktioniert beim Debuggen?
So habe mein Fehler....
In der Main wird der Manager augerufen bevor eine Nachricht in Auftrag
gegeben wird (pressur_sensor_start_measurement). Dadurch werden zu
Beginn Daten aus dem Buffer ausgelesen die nocht nicht existent sind.
Ich dachte das ist nicht weiterschlimm (wie gesagt Testlauf) und habe es
ignoriert. Was dabei logischerweise jedoch auch passiert ist, dass der
Incrementor für die Buffer-Elemente so immer auf Daten verweist die noch
nicht ausgelesen wurden...
Also um die Überschrift dieses Threads zu beantworten. Ich habe komische
Sachen mit Pointern gemacht.
Danke für jeden Beitrag :)
P.S.: Jetzt klappt alles wie es soll :D
Aaron K. schrieb:> n der Main wird der Manager augerufen bevor eine Nachricht in Auftrag> gegeben wird (pressur_sensor_start_measurement). Dadurch werden zu> Beginn Daten aus dem Buffer ausgelesen die nocht nicht existent sind.
Genau das habe ich gesagt: Du hast keinen FiFo!
Du wirst Dich wundern, was Deine "Verzögerungsschleifen" bewirken,
sobald Du den Optimierer einschaltest. Diese Schleifen werden dann
nämlich gnadenlos wegoptimiert.
Übrigens ist das lediglich ein Feature Deines Compilers, wenn er diese
Schleifen tatsächlich ohne Optmierung stehen lässt. Verlassen kannst Du
Dich darauf nicht. Code, welcher keinen Effekt hat, kann nämlich vom
Compiler immer wegoptimiert werden - egal in welcher
Optimierungsstufe. Von daher sehe ich das mal als nicht-portablen Code
an.
Aaron K. schrieb:> Aber es funktioniert beim debuggen tadellos :)
Klar, weil dann eben Dein Code nicht die reinkommenden I2C-Daten
"überholt", Wilhelm hat das Problem schon vorher einwandfrei erkannt.
Aaron K. schrieb:> Wie gesagt, dass funktioniert alles soweit...
Wie Du nun gesehen hast, funktionierte das Füllen des Arrays doch nicht
"soweit" korrekt, weil Du den Parameter "Zeit" einfach nicht
berücksichtigt hast.
Deshalb ein Tipp für die Zukunft: Versuche zukünftig nicht mehr, den
Leser durch Behauptungen, die sich im nachhinein als falsch
herausstellen, in die Irre zu führen und lediglich den Codeschnipsel, wo
Du den Fehler vermutest, zu posten. Du zwingst sonst den Leser, nur in
Deine gewünschte Richtung zu denken. Das hat aber keinen Mehrwert - im
Gegenteil: Du verzögerst damit die Problemfindung.
Jo stimmt alles was du geschrieben hast. Die For-Schleifen werden später
definitiv durch Timer-Routinen abgelöst. Das war nur der erste Versuch
Daten aus dem Sensor zu bekommen. Und Timer initialisieren dauert zwar
nicht lang aber ein paar For-Schleifen iwo reinzuschreiben tuts auch und
dauert noch weniger lang :D
Zum Thema Thread schreiben. Tipp werde ich beherzigen. :)