Forum: Mikrocontroller und Digitale Elektronik PIC: Laufzeit eines Programmabschnittes bestimmen?


von Severin E. (severin_e)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

Ich habe Laufzeitprobleme. Dass heisst, der Code-Block im Anhang braucht 
zu lange.

Ist es möglich, dass ich die Laufzeit eines Programmabschnittes (in C) 
berechnen (oder messen?) kann? Der 8-bit PIC16F15325 läuft mit 4MHz.
Ich weiss, dass eine Instruktion bei meinem PIC 4 Takte braucht, aber 
wie viele Instruktionen braucht dann ein if, ein floor(), ein kleiner 
als, ...?

Der Code im Anhang berechnet die Ziffern einer 4-stelligen 
7-Segment-Anzeige die aber nur in 25-er Schritten anzeigen soll 
(0,25,50,75,100,125,150,...2750).

Danke & Grüsse
Severin

von Michael (Gast)


Lesenswert?

Hi,

Kein Wunder, die Gleitkommaarithmetik (float) ist ursächlich dafür.

Wirf die raus, arbeite mit Ganzzahlen (entsprechend skaliert)!


Viele Grüße,

Michael

von K. S. (the_yrr)


Lesenswert?

Severin E. schrieb:
> Ist es möglich, dass ich die Laufzeit eines Programmabschnittes (in C)
> berechnen (oder messen?) kann?

direkt nicht. Entweder am Anfang einen Pin high und am Ende wieder low 
schalten (und Oszi dran), oder in der Assemblerdatei Instructions/Zyklen 
zählen, oder aber step für step Simulieren und Steps zählen.

Rest wurde schon gesagt:
Michael schrieb:
> Kein Wunder, die Gleitkommaarithmetik (float) ist ursächlich dafür.

: Bearbeitet durch User
von Thomas S. (selli69)


Lesenswert?

Michael schrieb:
> Wirf die raus, arbeite mit Ganzzahlen (entsprechend skaliert)!

Am Besten so skaliert, dass Divisionen durch z.B. 1024 anstatt 1000 
durchgeführt werden, denn der Compiler macht dann keine "echte" Division 
mehr daraus sondern shiftet den Wert dann einfach. Dann wirds richtig 
schnell.

: Bearbeitet durch User
von Severin E. (severin_e)


Lesenswert?

Danke für die Infos. Hab den Block mit Pin hoch/tief und Oszi gemessen. 
Braucht etwa 10ms. Seeehr lang. Dass heisst ja bei 4MHz, dass da 10'000 
Instruktionen ausgeführt werden. Ich werde mal versuchen, die floats 
wegzubringen ...

Ausserdem könnte ich den Chiptakt bis auf 32MHz erhöhen.

von neuer PIC Freund (Gast)


Angehängte Dateien:

Lesenswert?

Jede Menge zu tun für diesen Kern bei dieser Berechnung.

von Severin E. (severin_e)


Lesenswert?

Der Screenshot von MPLAB ist spannend. Dass heisst, es gibt irgendwo 
beim Debuggen ein "Stopwatch" zum messen von Laufzeiten. Muss mal suchen 
gehen...

von Thomas S. (selli69)


Lesenswert?

Severin E. schrieb:
> Ich werde mal versuchen, die floats
> wegzubringen ...

Nicht versuchen, machen! Das Schlimmste was dabei passieren kann, ist 
dass Du ne Menge dabei lernst und wenn gar nichts mehr geht, dass Du 
hier noch mal fragen musst. Bei solchen Problemen ist hier jeder gern 
bereit zu helfen.

> Ausserdem könnte ich den Chiptakt bis auf 32MHz erhöhen.

Das ist cheaten..

von Severin E. (severin_e)


Lesenswert?

Thomas S. schrieb:
> Severin E. schrieb:
>> Ich werde mal versuchen, die floats
>> wegzubringen ...
>
> Nicht versuchen, machen! Das Schlimmste was dabei passieren kann, ist
> dass Du ne Menge dabei lernst und wenn gar nichts mehr geht, dass Du
> hier noch mal fragen musst. Bei solchen Problemen ist hier jeder gern
> bereit zu helfen.
>
>> Ausserdem könnte ich den Chiptakt bis auf 32MHz erhöhen.
>
> Das ist cheaten..

GENAU! Takt bleibt erst mal auf 4MHz. Die Berechnung muss sich ändern. 
Aber vielleicht nicht mehr heute Abend.

Danke euch allen!

von leo (Gast)


Lesenswert?

Severin E. schrieb:
> Der Code im Anhang berechnet die Ziffern einer 4-stelligen
> 7-Segment-Anzeige

Interessant. Wer liest die Anzeige so schnell ab, dass das ein Problem 
ist?

leo

von Severin E. (severin_e)


Lesenswert?

leo schrieb:
> Severin E. schrieb:
>> Der Code im Anhang berechnet die Ziffern einer 4-stelligen
>> 7-Segment-Anzeige
>
> Interessant. Wer liest die Anzeige so schnell ab, dass das ein Problem
> ist?
>
> leo

Die 4 Ziffern sollen nicht flackern. Als etwa 100Hz-Betrieb damit die 
Augen das nicht mitbekommen. 100Hz heisst bei 4 Ziffern, dass jede 
Ziffer 2,5ms bekommt. Da sollte man nicht noch andauernd 
10ms-Berechnungen dazwischenschieben.

von Kaj (Gast)


Lesenswert?

Okay, was mir auffaellt:
1
uint16_t RPMdec;
2
uint16_t PWM10bit = 323;
3
4
RPMdec = (float)PWM10bit*2750/1024;
Wozu der Cast auf float? Die Nachkommastellen werden ja eh 
abgeschnitten. Dann tut es auch ein Cast nach uint32_t statt float.
1
DIG1 = (RPMdev/1000) % 10;
Mathematisch macht es keinen Unterschied ob du durch 1000 teilst oder 
nicht und dann den Rest nimmst.
(RPMdev/1000) % 10 = RPMdev % 10.
Ob es im erzeugten Code einen Unterschied macht musst du gucken.

Selbiges fuer DIG2.
1
float temp1;
2
temp1 = RPMdec - 100 * (floor(RPMdec/100));
Den Aufruf von floor() koenntest du auch sparen. Es ist eine 
Integer-Division. Das Abrunden passiert ja von ganz alleine.
Kurz:
100 * (floor(RPMdec/100)) = 100 * (RPMdec/100)

Und damit ist auch der float weg.

Gruesse

von Michael (Gast)


Lesenswert?

Kaj schrieb:
> Kurz:
> 100 * (floor(RPMdec/100)) = 100 * (RPMdec/100)
>
> Und damit ist auch der float weg.

Falsch!

in "floor()" steckt der wieder drin...

Dies muss auch noch weg. Integer-Division ist automatisch ohne 
Nachkommastellen.

von Forist (Gast)


Lesenswert?

Severin E. schrieb:
> Die 4 Ziffern sollen nicht flackern. Als etwa 100Hz-Betrieb damit die
> Augen das nicht mitbekommen. 100Hz heisst bei 4 Ziffern, dass jede
> Ziffer 2,5ms bekommt. Da sollte man nicht noch andauernd
> 10ms-Berechnungen dazwischenschieben.

Du darfst du nur keine festen 10ms Blöcke dazwischen schieben. Die 
Rechnerei kann ganz in Ruhe ablaufen und die Anzeigesteuerung muss dann 
alle 2.5ms per Interrupt die Rechnerei irgendwo kurz unterbrechen und 
ein paar Takte für die Aktualisierung einfordern.

von Volker S. (vloki)


Lesenswert?

Severin E. schrieb:
> Der Screenshot von MPLAB ist spannend. Dass heisst, es gibt irgendwo
> beim Debuggen ein "Stopwatch" zum messen von Laufzeiten. Muss mal suchen
> gehen...

Geht im Simulator. https://www.youtube.com/watch?v=bUookr-2O-s
(Stopwatch kommt so bei 5min.)

: Bearbeitet durch User
von soso... (Gast)


Lesenswert?

Volker S. schrieb:
> Severin E. schrieb:
>> Der Screenshot von MPLAB ist spannend. Dass heisst, es gibt irgendwo
>> beim Debuggen ein "Stopwatch" zum messen von Laufzeiten. Muss mal suchen
>> gehen...
>
> Geht im Simulator. https://www.youtube.com/watch?v=bUookr-2O-s
> (Stopwatch kommt so bei 5min.)

Nicht nur. Das geht so auch zur Laufzeit auf dem Device. Man setzt zwei 
Breakpoints, und kann die Anzahl der Takte zwischen diesen messen.

Das ist dann praktisch, wenn die Laufzeit von externen Faktoren abhängt.
Außerdem kann man sich den Simulator so sparen.

von Volker S. (vloki)


Lesenswert?

soso... schrieb:
> Nicht nur. Das geht so auch zur Laufzeit auf dem Device. Man setzt zwei
> Breakpoints, und kann die Anzahl der Takte zwischen diesen messen.

Aber leider nicht mit jedem Debugger-Tool und nicht bei jedem PIC.

von soso... (Gast)


Lesenswert?

Volker S. schrieb:
> soso... schrieb:
>> Nicht nur. Das geht so auch zur Laufzeit auf dem Device. Man setzt zwei
>> Breakpoints, und kann die Anzahl der Takte zwischen diesen messen.
>
> Aber leider nicht mit jedem Debugger-Tool und nicht bei jedem PIC.

Hmm. Also ich sehe aus dem Datenblatt des erwähnten PIC keine Grund, 
warum das nicht ginge. Man sollte den ja debuggen können, und ein 
PICkit3 kann Stopwatch. Eigentlich sollte das auch mit den billigen 
Klones gehen.

Woran sieht man, ob das geht oder nicht?

Bei mir gings bisher immer (hauptsächlich PIC24 allerdings).

von neuer PIC Freund (Gast)


Lesenswert?

Es wird zum Testen des Codeabschnittes keinerlei wechselwirksamer 
Peripherie beansprucht. Also Simulator. Da gibt es jede menge 
Breakpoints, Stopwatch, Disassembly, ...

Und man sieht gleich, wie langsam der Code des freien XC8 ist. Gut 
optimierend käme wohl sowas wie folgend raus.

1
uint8_t segment_code(uint8_t x)
2
{
3
    asm ("andlw 0x0f");
4
    asm ("brw");
5
    asm ("retlw 0xfc");
6
    asm ("retlw 0x60");
7
    asm ("retlw 0xda");
8
    asm ("retlw 0xf2");
9
    asm ("retlw 0x66");
10
    asm ("retlw 0xb6");
11
    asm ("retlw 0xbe");
12
    asm ("retlw 0xe0");
13
    asm ("retlw 0xfe");
14
    asm ("retlw 0xf6");
15
    asm ("retlw 0x00");
16
    asm ("retlw 0x02");
17
    asm ("retlw 0x01");
18
    asm ("retlw 0xff");
19
    asm ("retlw 0xff");
20
    asm ("retlw 0xff");
21
    
22
    return 0xff;        // suppress warning
23
}

von Volker S. (vloki)


Lesenswert?

soso... schrieb:
> ein PICkit3 kann Stopwatch...
>
> Bei mir gings bisher immer (hauptsächlich PIC24 allerdings).

Ups, da muss ich mal schauen, ob das bei PIC16/18 auch geht.
Sollte laut dieser Tabelle mit PICkit3 eigentlich gar nicht gehen:
http://microchipdeveloper.com/tls0201:debug-tool-capabilities

Vielleicht veraltet?
(ging evtl. früher nicht und ich habe es noch gar nicht bemerkt,
dass das jetzt geht, weil ich den Simulator praktisch nicht benutze)

von TM F. (p_richner)


Lesenswert?

Kaj schrieb:
> Mathematisch macht es keinen Unterschied ob du durch 1000 teilst oder
> nicht und dann den Rest nimmst.
> (RPMdev/1000) % 10 = RPMdev % 10.

Das stimmt nicht ganz.

Bsp:
(5000 / 1000) % 10 = 5
5000 % 10 = 0

MfG

von Volker S. (vloki)


Lesenswert?

Volker S. schrieb:
> Ups, da muss ich mal schauen, ob das bei PIC16/18 auch geht.

Nope, geht nicht bei MPLABX v5.10  PICkit3  PIC18F25K22.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Severin E. schrieb:
> Danke für die Infos. Hab den Block mit Pin hoch/tief und Oszi gemessen.
> Braucht etwa 10ms.

Dann ist ja alles im grünen Bereich. Der Mensch kann nicht mehr als 2..5 
Werte/s ablesen, d.h. Du hast 200..500ms Zeit für die Berechnung.

von soso... (Gast)


Lesenswert?

Volker S. schrieb:
> soso... schrieb:
>> ein PICkit3 kann Stopwatch...
>>
>> Bei mir gings bisher immer (hauptsächlich PIC24 allerdings).
>
> Ups, da muss ich mal schauen, ob das bei PIC16/18 auch geht.
> Sollte laut dieser Tabelle mit PICkit3 eigentlich gar nicht gehen:
> http://microchipdeveloper.com/tls0201:debug-tool-capabilities
>
> Vielleicht veraltet?
> (ging evtl. früher nicht und ich habe es noch gar nicht bemerkt,
> dass das jetzt geht, weil ich den Simulator praktisch nicht benutze)

Interessant, laut deinem Link dürfte es mit dem PICkit3 nicht gehen.

Ich bin mir aber zu sicher, dass ich mit MPLABX, dem PICkit3 und der 
Stopwatch auf und einige Projekte damit optimiert zu haben (zu 100% 
sicher bin ich mir bei den Devices PIC24FJ128GB204  und PIC32MX370). Das 
war mit einem MPLABX 3.xx.

Naja, falls alle Stricke reißen, gibts ja noch Timer. Man kann sich 
einen aufsetzen, der im µs-Takt zählt, und damit die Zeit messen. Man 
kann sogar OC-Timer nehmen, falls vorhanden.

Das Scope wurde schon genannt.

von Severin E. (severin_e)


Angehängte Dateien:

Lesenswert?

Eine Rückmeldung meinerseits:

Ich habe nun den Code umgeschrieben(float weg, floor weg und leicht 
geändert) und nun braucht der Block im Anhang nur noch 2,5ms und nicht 
mehr 10ms wie der Code im ersten Post. Also etwa 4x schneller!

Danke allen & Grüsse

von Patrick (Gast)


Lesenswert?

Was ich gesehen habe, nutzt du das ganze für Anzeigezwecke im 
BCD-Format. Da könntest du die Berechnung doch auch mehr oder weniger 
gleich in BCD machen. Den Quelltext habe ich mal angehängt.
Die 1000er Stelle kann ja nur 0, 1 oder 2 werden --> deswegen if - 
Abfrage "zu Fuß".
Die noch verbliebenen Hunderter können Wertemäßig noch über 255 sein, 
deshalb wird ein konstantes 16 Bit Array zum Vergleich genutzt und die 
Wertigkeit bestimmt.
Bei der Berechnung der 10er - Stelle kann im verbleibenden Wert niemals 
etwas über 255 stehen --> es wird mit 8 Bit gearbeitet, das macht das 
ganze schneller.
Auf die Berechnung der 1er Stelle habe ich verzichtet, das würde ich 
dann aber auch wieder "zu Fuß" machen, wenn benötigt - ungenau ist es 
allemal.
Damit sollte sich noch etwas mehr Rechenzeit sparen lassen, schlecht ist 
nur: Es muss ggf. bei anderer Skalierung der Drehzahl umgeschrieben 
werden.

Ob du dir so eine Code-Krake ins Programm holen willst, ist auch noch 
eine Frage... :)
1
#define MUL 1024*1024/2750
2
const unsigned int div100[9] = { //16 Bit Vergleichsarray für 100er
3
   1*MUL/10, 2*MUL/10, 3*MUL/10, 4*MUL/10, 5*MUL/10, 
4
   6*MUL/10, 7*MUL/10, 8*MUL/10, 9*MUL/10 };
5
6
const unsigned char div10[9] = { //8 Bit Vergleichsarray für 10er
7
   1*MUL/100, 2*MUL/100, 3*MUL/100, 4*MUL/100, 5*MUL/100,
8
   6*MUL/100, 7*MUL/100, 8*MUL/100, 9*MUL/100 };
9
10
unsigned char bcdout[4] = {0,0,0,0}; //BCD-Ausgabe-Array (1000er - 1er)
11
unsigned char j;  //Schleifenzähler
12
unsigned char RPMdecLowerByte;
13
14
RPMdec = PWM10bit;
15
16
if (RPMdec >= 2*MUL) {  //1000er Stelle --> Wertigkeit 2
17
  bcdout[0] = 2;
18
  RPMdec -= 2*MUL;
19
}
20
if (RPMdec >= 1*MUL) {  //1000er Stelle --> Wertigkeit 1
21
  bcdout[0] = 1;
22
  RPMdec -= 1*MUL;
23
}
24
25
26
//Ermittle 100er Stelle mit 16-Bit-Vergleichsarray
27
for (j = 8; j >= 0; j--) {  // fange bei höchster Wertigkeit an
28
  if (RPMdec >= div100[j]) {  //sobald RPMdec größer als vergleichswert
29
    bcdout[1] = j + 1;  //schreibe Ziffer
30
    RPMdec -= div100[j]; //Ziehe Ziffernwertigkeit von RPMdec ab
31
    break;  //und beende for-schleife
32
  }
33
}
34
35
RPMdecLowerByte = RPMdec & 0xFF; //Lower Byte in 8 Bit Variable
36
37
//Ermittle 10er Stelle mit 8-Bit-Vergleichsarray
38
for (j = 8; j >= 0; j--) {  // fange bei höchster Wertigkeit an
39
  if (RPMdecLowerByte >= div10[j]) {  //sobald RPMdec größer als vergleichswert
40
    bcdout[2] = j + 1;  //schreibe Ziffer
41
    RPMdecLowerByte -= div10[j]; //Ziehe Ziffernwertigkeit von RPMdec ab
42
    break;  //und beende for-schleife
43
  }
44
}

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.