Forum: Mikrocontroller und Digitale Elektronik Fehler im Programm, verstehe es nicht.


von Dirk F. (dirkf)


Lesenswert?

Hallo,
ich möchte eine neues Projekt starten.
hierzu erzeugt ein Timer jese millisekunde ein Interrupt.
In diesem Interrupt wird eine volatile varriable um 1 hochgezählt.
Dass der Interrupt jede ms ausgeführt wird, habe ich überprüft, indem 
ich eine LED immer toggle und das Signal mit einem Oszzi überprüft habe. 
Passt, jede ms ein Flankenwechsel high>low oder low>high.

Dann erfolgt im zyklischen Programmteil eine Logic, die nach 1000 ms 
eine andere LED toggelt.
Was nicht stimmt, dass die Zeit zum Toggeln 2 Sekunden dauert, es sollte 
aber 1 Sekunde sein.
Jetzt könnte man denken, dass irgendwo anders im Zyklus eine Sekunde 
gewartet wird.  Ist es aber nicht, weil wenn ich den Zähler (COUNT) von 
1000 auf 500 reduziere, dass beträgt die Toggelzeit exact 1 Sekunde. 
Also Fehler Faktor 2.
Bin  für jeden Hinweis dankbar......

Hier ein Teil des Codes:
1
extern volatile int zeit;
2
extern int zeit_backup;
3
4
//********************************************************************
5
// 1ms Timer Interrupt
6
//********************************************************************
7
void TMR_CallbackFn (uint32_t status,uintptr_t context );
8
void TMR_CallbackFn (uint32_t status,uintptr_t context )
9
{
10
     zeit ++ ;
11
     // LED1_Toggle();  // 1 ms gemessen
12
}
13
14
//********************************************************************
15
// main loop
16
//********************************************************************
17
case CLKS_STATE_EVENT:          // Loop forever
18
            
19
        if  (zeit >=1)   
20
            {
21
            TMR2_InterruptDisable();;   
22
            // To avoid interrupts during "zeit" transfer
23
            zeit_backup = zeit;         
24
            // abgelaufene Zeit in ms übertragen
25
            zeit = 0;                   // und zurücksetzen
26
            TMR2_InterruptEnable();
27
            
28
            #define COUNT 500   // ????? Warum 500. Müsste 1000 sein  ????
29
            appData.msec_cnt += zeit_backup ; 
30
            if (appData.msec_cnt >=COUNT)
31
                {
32
                //---------jede Sekunde------------------------------------
33
                appData.msec_cnt =- COUNT;
34
                appData.sec_cnt++;
35
                LED1_Toggle();
36
                }
37
            }

von Falk B. (falk)


Lesenswert?

Dirk F. schrieb:
> Also Fehler Faktor 2.
> Bin  für jeden Hinweis dankbar......

Du macht es auch maximal kompliziert und auch resourcenhungrig. Du 
zählst DOPPELT! In der ISR UND im Hauptprogramm! Setz doch einfach nach 
500ms ein Flag, da im Hauptprogramm erkannt wird und die LED schaltet. 
Das ist DEUTLICH einfacher und braucht weniger CPU-Last.

von Sebastian W. (wangnick)


Lesenswert?

Dein Zugriff auf zeit bei "if (zeit>=1)" ist nicht atomar und könnte, 
weil int zeit aus mehreren Bytes besteht, inmitten des Vergleichs 
unterbrochen werden.

LG, Sebastian

von Dirk F. (dirkf)


Lesenswert?

Hallo,
ich möchte die Zykluszeit in ms messen.
Daher brauche ich diesen ms Tick.

von MaWin O. (mawin_original)


Lesenswert?

Dirk F. schrieb:
> erzeugt ein Timer jese millisekunde ein Interrupt.

anscheinend ja nicht.

von Falk B. (falk)


Lesenswert?

Sebastian W. schrieb:
> Dein Zugriff auf zeit bei "if (zeit>=1)" ist nicht atomar und könnte,

Kann sein, muss nicht. Wir kennen die CPU nicht. NAch den ISR-Namen zu 
Urteilen ein ARM oder MSP430. Die können 16 Bit Vergleiche atomar.

von Dirk F. (dirkf)


Lesenswert?

Sebastian W. schrieb:
> Dein Zugriff auf zeit bei "if (zeit>=1)" ist nicht atomar und
> könnte,
> weil int zeit aus mehreren Bytes besteht, inmitten des Vergleichs
> unterbrochen werden.
>
> LG, Sebastian

Hallo, es ist ein 32 Bit MCU.

Habe den teil deaktiviert:
//        if  (zeit >=1)
//            {

Trotzdem ist die Zeit doppelt so lange wie sie sein sollte....

von Dirk F. (dirkf)


Lesenswert?

MaWin O. schrieb:
> Dirk F. schrieb:
>> erzeugt ein Timer jese millisekunde ein Interrupt.
>
> anscheinend ja nicht.

Hab ich doch gemessen.....mit Osszi

von Dirk F. (dirkf)


Lesenswert?

Falk B. schrieb:
> Wir kennen die CPU nicht

PIC32MZ

von Stefan F. (Gast)


Lesenswert?

Beim Lesen deines Codes bekomme ich einen Knoten im Hirn. Aber er ist 
sicher nicht fehlerhaft.

Die "zeit" wird jede ms um 1 erhöht. Du toggelst die LED nach 500ms. Sie 
ist also 500ms AN und dann 500ms AUS. Macht zusammen 1s pro Zyklus, oder 
1 Hz.

von Falk B. (falk)


Lesenswert?

Dirk F. schrieb:
> PIC32MZ

OK, ändert aber nix an meinem Kommentar. Dein Ansatz ist maximal 
aufwändig und kompliziert. Warum der jetzt doppelt so lange dauert? 
Hmmm?

Deine Klammernsetzung ist auch, "besonders". Würde ich nicht empfehlen.

von Dirk F. (dirkf)


Lesenswert?

Stefan F. schrieb:
> Die "zeit" wird jede ms um 1 erhöht. Du toggelst die LED nach 500ms. Sie
> ist also 500ms AN und dann 500ms AUS. Macht zusammen 1s pro Zyklus, oder
> 1 Hz.

Istzustand:
Bei COUNT 1000  :  LED 2s an  2s aus
Bei COUNT 100  :  LED 200 ms an  200 ms aus

von Falk B. (falk)


Lesenswert?

Stefan F. schrieb:
> Macht zusammen 1s pro Zyklus, oder
> 1 Hz.

Tja, hier liegt vielleicht der Fehler. Die LED "toggelt" nach 500ms, 
sprich wechselt den Pegel. Damit ist die Pulsbreite für LOW und HIGH 
500ms. Die Periodendauer aus LOW+HIGH ist aber 1000ms, logisch. 
VErmutlich hat das der OP verwechselt.

von Falk B. (falk)


Lesenswert?

Dirk F. schrieb:
> Istzustand:
> Bei COUNT 1000  :  LED 2s an  2s aus
> Bei COUNT 100  :  LED 200 ms an  200 ms aus

Hmm, das glauben wir einfach mal. Dann läuft irgendwas nur halb so 
schnell wie es soll. Oder es wird immer ein Takt "verdoppelt" ? Komisch. 
Möglichwerweise kommt der Timer mit dem dauernden Reset auf 0 aus dem 
Takt, das kann mit dem Vorteiler zusammen hängen. Wenn der auch einen 
Reset bekommt, kann sowas passieren. So oder so ist dein Ansatz 
unsinnig. Pack die Uhr in den Timer-Interrupt und generiere dort Flags 
für das Hauptprogramm. Dabei läuft der Zähler durch und alles ist gut.

von Dirk F. (dirkf)


Lesenswert?

Falk B. schrieb:
> VErmutlich hat das der OP verwechselt.

Nein.   Wenn ich im 1ms Interrupt die LED Toggele, dann ist klar, dass 
die Periodendauer 2ms ist, aber darum gehts doch nicht.

Gleiches gilt für den main loop Zyklus.
Toggeln nur zur Zeitmessung mit dem Osszi.....

von Dirk F. (dirkf)


Lesenswert?

Falk B. schrieb:
> Pack die Uhr in den Timer-Interrupt

Hmm, die Zeit im Interrupt sollte doch immer so kurz wie möglich 
gehalten werden......

von Stefan F. (Gast)


Lesenswert?

Schreibe mal ein neues Programm, wo nur die für diesen Effekt relevanten 
Teile drin sind. Tritt der Fehler dann noch auf, dann zeige und den 
kompletten Quelltext dieses Testprogramms. Tritt er nicht mehr auf, 
finde den Unterschied.

Und zeige uns dein Oszilloskop-Bild von den 1ms. Vielleicht sind es doch 
2ms und du hast dich nur verguckt.

von Falk B. (falk)


Lesenswert?

1
extern volatile int zeit;
2
extern int zeit_backup;
3
extern volatile int flag_led;
4
5
//********************************************************************
6
// 1ms Timer Interrupt
7
//********************************************************************
8
9
void TMR_CallbackFn (uint32_t status,uintptr_t context );
10
void TMR_CallbackFn (uint32_t status,uintptr_t context )
11
{
12
    zeit ++ ;
13
    // LED1_Toggle();  // 1 ms gemessen
14
15
#define COUNT 1000
16
    appData.msec_cnt++; 
17
    if (appData.msec_cnt >=COUNT)
18
    {
19
        appData.msec_cnt =- COUNT;
20
        appData.sec_cnt++;
21
        flag_led = 1;
22
    }
23
}
24
25
//********************************************************************
26
27
// main loop
28
29
//********************************************************************
30
31
case CLKS_STATE_EVENT:          // Loop forever
32
33
    if (flag_led)
34
    {
35
        flag_led = 0;
36
        LED1_Toggle();
37
    }

von Falk B. (falk)


Lesenswert?

Dirk F. schrieb:
> Hmm, die Zeit im Interrupt sollte doch immer so kurz wie möglich
> gehalten werden......

Ja mein Gott, was glaubst du wie lange dein PIC32 mit 30MHz++ für so ein 
paar Zeilen braucht?

von Dirk F. (dirkf)


Lesenswert?

Stefan F. schrieb:
> Und zeige uns dein Oszilloskop-Bild von den 1ms. Vielleicht sind es doch
> 2ms und du hast dich nur verguckt.

s. Anlage

von Falk B. (falk)


Lesenswert?

Dirk F. schrieb:
> 1ms.png
>
>             9,3 MB

Welche Drogen nimmst du? Lies mal was über Bildformate!!!

von Dirk F. (dirkf)


Lesenswert?

@Falk:
Und wie soll ich dann die Zykluszeit in ms ermitteln ???

von Dirk F. (dirkf)


Lesenswert?

Falk B. schrieb:
> Welche Drogen nimmst du? Lies mal was über Bildformate!!!

Sorry, habs zu spät gemerkt.....

von Dirk F. (dirkf)


Lesenswert?

Stefan F. schrieb:
> Schreibe mal ein neues Programm, wo nur die für diesen Effekt relevanten
> Teile drin sind.

Gute Idee Stefanus.  Mache ich in den nächsten Tagen....

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Hier als JPG, wenn gleich das auch nur ein Workaround ist. Wie schafft 
man es, ein 9,3MB PNG zu erzeugen? OK, bei 4000x2300 Pixel geht das 
anscheinend! Und das bei einem Digitaloszilloskop, das saubere 
Screenshots erzeugen kann. FAIL!

von Falk B. (falk)


Lesenswert?

Dirk F. schrieb:
> @Falk:
> Und wie soll ich dann die Zykluszeit in ms ermitteln ???

Welche Zykluszeit? In deinem Beispiel zählst du nur eine Uhr hoch. Und 
wenn man die Zeitdifferenz zweier Ereignisse messen will, macht man das 
wie der Rest der Welt.

Differenz = Endzeit - Startzeit.

Und wenn man periodisch ein Ereignis auslösen will, kann man das auch 
über den Interrupt machen. Anstatt des #define für die 1000ms nutzt man 
eine Variable. Damit kann man variable Blinkzeiten oder was auch immer 
generieren.

von Dirk F. (dirkf)


Lesenswert?

Falk B. schrieb:
> Welche Zykluszeit?

Die Zykluszeit vom main loop.

von Dirk F. (dirkf)


Lesenswert?

Falk B. schrieb:
> Differenz = Endzeit - Startzeit.

Und wenn ein Übrlauf stattfindet ?

von Sebastian W. (wangnick)


Lesenswert?

Dirk F. schrieb:
> Und wenn ein Übrlauf stattfindet ?

Ja, was passiert dann wohl?

LG, Sebastian

von Falk B. (falk)


Lesenswert?

Dirk F. schrieb:
>> Differenz = Endzeit - Startzeit.
>
> Und wenn ein Übrlauf stattfindet ?

Passiert die Magie der Integerarithmetik. Solange die Differenz kleiner 
als der Zählumfang des Timers ist, ist die Differenz IMMER korrekt!

von Thomas F. (igel)


Lesenswert?

Dirk F. schrieb:
> Die Zykluszeit vom main loop.

Bei jedem Durchlauf zählt man eine Variable um 1 hoch. Ist eine Sekunde 
voll gibt man den Wert aus und hat die Anzahl der Durchläufe.

von Sebastian W. (wangnick)


Lesenswert?

Dirk F. schrieb:
> Habe den teil deaktiviert:
> //        if  (zeit >=1)
> //            {
>
> Trotzdem ist die Zeit doppelt so lange wie sie sein sollte....

Mmh, seltsam. Was hat es denn mit CLKS_STATE_EVENT auf sich?

LG, Sebastian

von Andras H. (kyrk)


Lesenswert?

Man muss den Interrupt gar nicht sperren wenn man das so macht:

volatile int zeit1;
volatile int zeit2;
int failsafe_exit = 0;

while (1) {
  zeit1 = zeit;
  zeit2 = zeit;
  if (zeit1 == zeit2) {
    break;
  }
  failsafe_exit++;
  if (failsafe_exit > 10) {
    break;
  }
}

failsafe_exit braucht man nicht unbedingt. Ist aber besser. Wobei wenn 
der greift, dann gibt es keine konsistente Zeit. Da muss man dann 
richtung Fehlerbehandlung gehen.

Die While schleife sollte max 2 mal laufen, wenn der Interrupt ca 1ms 
kommt.

von Rainer W. (rawi)


Lesenswert?

Dirk F. schrieb:
> 9,3 MB

Herzlichen Glückwunsch. Guck mal rechts unterhalb des Bildschirmes. Da 
gibt es eine Schnittstelle für vernünftige Screenshots.

von Ozvald K. (Firma: Privat) (ozvaldk)


Lesenswert?

Dirk F. schrieb:
> appData.msec_cnt =- COUNT

von -500 bis + 500

von Peter D. (peda)


Lesenswert?

Andras H. schrieb:
> Man muss den Interrupt gar nicht sperren wenn man das so macht:

Man muß aber auch keine Angst davor haben. Ein atomarer Zugriff sollte 
unter 10 CPU-Zyklen kosten, das dürfte vernachlässigbar sein.

Das von hinten durch die Brust ins Auge zu vermeiden, ist den Aufwand 
nicht wert. Und lesbarer wird es auch nicht.

von Dirk F. (dirkf)


Lesenswert?

Thomas F. schrieb:
> Bei jedem Durchlauf zählt man eine Variable um 1 hoch. Ist eine Sekunde
> voll gibt man den Wert aus und hat die Anzahl der Durchläufe.

Aber nicht die aktuelle, minimale, maxixmale und durchschnittliche 
Zykluszeit, so wie es eine SPS (z.B. Siemens)  auch misst.

von Dirk F. (dirkf)


Lesenswert?

Sebastian W. schrieb:
> Mmh, seltsam. Was hat es denn mit CLKS_STATE_EVENT auf sich?

Der wechselt einmalig am Anfang (Statusmaschine) vom State Init in 
Event.

von Sebastian W. (wangnick)


Lesenswert?

Dirk F. schrieb:
> Der wechselt einmalig am Anfang (Statusmaschine) vom State Init in
> Event.

Wenn der Interrupt jede 1ms triggert, wie auf dem Oszi zu sehen, und 
also jede 1ms "zeit" um 1 erhöht, und du häufig genug in der 
Hauptschleife die in "zeit" gezählten 1ms in appData überträgst und auf 
das Erreichen von 1000 überprüfst, dann sollte dieses Erreichen von 1000 
jede Sekunde passieren. Wenn es nur alle zwei Sekunden passiert, bzw. 
nur jede Sekunde passiert indem du statt auf das Erreichen von 1000 auf 
das Erreichen von 500 prüfst, dann ist irgendwo tatsächlich der Wurm 
drin.

Hast du eine Möglichkeit, Testausgaben zu generieren? Dann gib an der 
Stelle "//---------jede Sekunde------------------------------------" mal 
appData.msec_cnt aus.

LG, Sebastian

von Dirk F. (dirkf)


Lesenswert?

Sebastian W. schrieb:
> Hast du eine Möglichkeit, Testausgaben zu generieren? Dann gib an der
> Stelle "//---------jede Sekunde------------------------------------" mal
> appData.msec_cnt aus.

Ja, habe ich. Über CDC (USB-Serial) COM Port kann ich Meldungen 
ausgeben.

Werde dann das Programm anpassen und diese Werte jede Sekunde ausgeben:
appData.msec_cnt
appData.sec_cnt

Dauert aber noch etwas.....

> dann ist irgendwo tatsächlich der Wurm drin.
Sach ich doch......

von Falk B. (falk)


Lesenswert?

Dirk F. schrieb:
>> dann ist irgendwo tatsächlich der Wurm drin.
> Sach ich doch......

Na dann probier mal meinen Vorschlag aus, das dauert wenige Minuten!

Beitrag "Re: Fehler im Programm, verstehe es nicht."

von Peter D. (peda)


Lesenswert?

Das Programm ist mir viel zu sehr von hinten durch die Brust ins Auge, 
da sieht doch keiner durch.
Wenn Du einen Oszi hast, dann setze das Toggle der LED einfach direkt in 
den Interrupthandler. Die 500Hz sollten gut zu sehen sein.

von Dirk F. (dirkf)


Lesenswert?

Peter D. schrieb:
> Wenn Du einen Oszi hast, dann setze das Toggle der LED einfach direkt in
> den Interrupthandler. Die 500Hz sollten gut zu sehen sein

Hab ich doch gemacht.... s.oben

von Dirk F. (dirkf)


Lesenswert?

Falk B. schrieb:
> Na dann probier mal meinen Vorschlag aus, das dauert wenige Minuten!

Ja mach ich über die Feiertage.  Habe im Moment keinen Zugriff....

von Dirk F. (dirkf)


Lesenswert?

Peter D. schrieb:
> Das Programm ist mir viel zu sehr von hinten durch die Brust ins Auge,
> da sieht doch keiner durch.

Dann will ich die Programm Logic mal so erklären:
1. Im Interrupt wird eine Varriable (32 Bit) jede ms um 1 erhöht.
2. Der normalen Zyklus (Superloop) kann  einige us  bis einige ms 
dauern.
3. Im normalen Zyklus wird die bisher vergangene Zeit vom letzten Aufruf 
in ms übertragen (= aktuelle Zykluszeit in ms).
Wenn diese 0 ist, dann ist noch keine ganze ms vergangen  >>> mach nix
Wenn diese 1 oder mehr ist, dann addiere die vergangene Zeit in ms auf 
den ms-Zähler.

Wenn der ms Zähler dann z.B 1003 ist  (letzter Zyklus dauerte z.B. 3 
ms), dann ziehe 1000 ab, bleibt Rest 3 für nächsten Durchlauf, damit 
keine 3ms verloren gehen.
4.Erhöhe den Sekundenzähler.

von Peter D. (peda)


Lesenswert?

Dirk F. schrieb:
> Hab ich doch gemacht.... s.oben

Ja dann liegt es an Deinen undurchschaubaren und unvollständigen 
Programmschnipseln.
Bei Schnipseln fehlt immer das Entscheidende.

von Ozvald K. (Firma: Privat) (ozvaldk)


Lesenswert?

Ozvald K. schrieb:
> Dirk F. schrieb:
>> appData.msec_cnt =- COUNT
>
> von -500 bis + 500

War meine Posting zu subtil? Versuche -= statt =-, oder einfach =0  in 
der Zeile. Wenn appData.msec_cnt = -500, dann bis 500 braucht die 
doppelte Zeit als von 0, oder lege ich falsch?

von Andreas M. (amesser)


Lesenswert?

Sollte
1
appData.msec_cnt =- COUNT;
nicht eher
1
appData.msec_cnt -= COUNT;
sein?

Edit: Ozvald war schneller :-)

: Bearbeitet durch User
von Sebastian W. (wangnick)


Lesenswert?

Ozvald K. schrieb:
> Versuche -= statt =-

Oh, Mann. Tausend Blinde außer Ozvald ...

LG, Sebastian

von Dirk F. (dirkf)


Lesenswert?

Ozvald K. schrieb:
> Versuche -= statt =-

Wo ist der Unterschied ?  Ist =- nach C-Standard überhaupt erlaubt ?

von Falk B. (falk)


Lesenswert?

Dirk F. schrieb:
>> Versuche -= statt =-
>
> Wo ist der Unterschied ?  Ist =- nach C-Standard überhaupt erlaubt ?

Ahhhh.
1
appData.msec_cnt =- COUNT;  // appData.msec_cnt = -COUNT;  feste Zuweisung
2
appData.msec_cnt -= COUNT;  // appData.msec_cnt = appData.msec_cnt - COUNT; Subtraktion

Das erklärt das Problem! Nur merkwürdig, daß das der Compiler mit den 
Leerzeichen so frißt.

: Bearbeitet durch User
von Dirk F. (dirkf)


Lesenswert?

Vielen Dank an alle. Ich habe es kapiert.
Bin halt nur Gelegenheitsprogrammierer....

von Andreas M. (amesser)


Lesenswert?

Trotzdem ist Dein Code maximal ineffizient. Warum nicht alles in der 
ISR?
1
struct zeit { unsigned int ms, s;};
2
3
volatile struct zeit my_zeit = {};
4
5
void TMR_CallbackFn (uint32_t status,uintptr_t context )
6
{
7
  unsigned int ms = my_zeit.ms; /* avoid double volatile var access */
8
9
  if (ms < 999)
10
  {
11
    my_zeit.ms = ms + 1;
12
  }
13
  else
14
  {
15
    my_zeit.ms = 0;
16
    my_zeit.s++;
17
  }
18
}
19
20
struct zeit get_zeit()
21
{
22
  struct zeit z;
23
24
  do {
25
    z.ms = my_zeit.ms;
26
    z.s  = my_zeit.s;
27
28
    /* if ms field changed, isr interrupted us& readout is invalid -> just repeat */
29
  } while(z.ms != my_zeit.ms); 
30
31
  return z;
32
}

Edit: zu schnell getippt, mus != Vergleich sein.

: Bearbeitet durch User
von Dirk F. (dirkf)


Lesenswert?

Andreas M. schrieb:
> Warum nicht alles in der
> ISR?

Weil ich dann keine Zykluszeiten messen kann.

von Michi S. (mista_s)


Lesenswert?

Ozvald K. schrieb:
> Dirk F. schrieb:
>> appData.msec_cnt =- COUNT
>
> von -500 bis + 500

Wenn Du das wirklich so: =- in Deinem Programm stehen hast, dann kann 
Ozvald nur recht geben; das erste Toggeln kommt zwar eventuell noch zum 
richtigen Zeitpunkt, aber das übersieht man natürlich leicht, alle 
folgenden Intervalle sind immer das doppelte von COUNT.

Sollte das nur ein Tippfehler hier im Post sein, dann weißt Du jetzt, 
warum alle nach einem compilierenden Minimalbeispiel verlangen.

von Falk B. (falk)


Lesenswert?

Michi S. schrieb:
> Sollte das nur ein Tippfehler hier im Post sein,

Müßte man ihm zusätzlich eine Watschen verpassen! Quelltext NIEMALS 
abschreiben, immer 100% KOPIEREN! Oder gleich die ganze Datei als Anhang 
senden.

von Dirk F. (dirkf)


Lesenswert?

Michi S. schrieb:
> Wenn Du das wirklich so: =- in Deinem Programm stehen hast

Ja

von Andreas M. (amesser)


Lesenswert?

Dirk F. schrieb:
> Weil ich dann keine Zykluszeiten messen kann.

Verstehe ich nicht. Klar geht das. Wenn Du die Differenz zwischen zwei 
Zeitpunkten messen willst wird es sogar noch einfacher. Du brauchst nur 
eine einzige "ms" Variable (unsigned) im Interrupt hochzuzählen, und 
zwar ohne auf 0 zurückzusetzen. Du merkst Dir den Stand am Anfang und 
den Stand am Ende deines Zyklus. Deine Zyklusdauer ist "Ende - Anfang" 
Und zwar immer, Overflows usw. werden wegen der Subtraktion implizit 
berücksichtigt.

von Stefan F. (Gast)


Angehängte Dateien:

Lesenswert?

Ozvald K. schrieb:
> Versuche -= statt =-

Whaaaaa, das ist ja fies!

Aber schau mal, meine lieblings IDE (Qt Creator) weist auf diesen Fehler 
hin.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Dirk F. schrieb:
> Daher brauche ich diesen ms Tick.
Ein Tickzähler läuft immer nur hoch. Er wird nie zurückgesetzt. Das 
Zurücksetzen passiert implizit nur dann, wenn der Zähler überläuft.

Mein Tipp: sieh dir die Implementierung von millis() beim Arduino an. 
Damit können sogar blutige Laien mit Zeitdifferenzen ohne 
Überlaufprobleme arbeiten.

von Dirk F. (dirkf)


Lesenswert?

Also nochmals Danke an alle.
Der Programmteil läuft jetzt fehlerfrei.
War wirklich ein blöder Fehler.


Falk B. schrieb:
> Passiert die Magie der Integerarithmetik. Solange die Differenz kleiner
> als der Zählumfang des Timers ist, ist die Differenz IMMER korrekt!

Also wenn ich einen 16 Bit Timer, der jede µs um 1 erhöht wird,  frei 
laufen lasse, und ihn an zwei unterschiedlichen Zeitpunkten auslese, 
dann ist das Ergebnis auch noch richtig, auch wenn zwischendurch ein 
Überlauf von 0xFFFF auf 0x0000  stattgefunden hat ?

Mal nachrechnen:
1. Zeitpunkt Zähler = 0xfffe  (Dezimal  65534)
2. Zeitpunkt Zähler = 0x0005  (Dezimal  5)

Zeitdifferenz Dezimal (65536-65534) + 5 = 7 µs

Zeitdifferenz HEX 0xffe - 0x005 = 0xF007  (Dezimal 61447)

von Sebastian W. (wangnick)


Lesenswert?

Dirk F. schrieb:
> Zeitdifferenz HEX 0xffe - 0x005 = 0xF007  (Dezimal 61447)

Nein, Zeitdifferenz 0x0005-0xFFFE!

LG, Sebastian

von Dirk F. (dirkf)


Angehängte Dateien:

Lesenswert?

Sebastian W. schrieb:
> Nein, Zeitdifferenz 0x0005-0xFFFE!

Danke Sebastioan.
Stimmt, habs mit dem Debugger als 32 Bit Varriable laufen lassen.

von Stefan F. (Gast)


Lesenswert?

Wichtig ist die Subtraktion. Mit einer Addition geht es nicht:
1
warteBis = millis() + 1000;
2
while (millis < warteBis) ...

von Norbert (der_norbert)


Lesenswert?

Dirk F. schrieb:
> Stimmt, habs mit dem Debugger als 32 Bit Varriable laufen lassen.

Bitte unbedingt mit unsigned integer machen, sonst ist's UB.

von Dirk F. (dirkf)


Lesenswert?

Also Jungs,  das war der best Hinweis überhaupt.
Durch das neue System wird der Prozessor gar nicht mehr mit Interrupts 
belastet. Und die Messung der aktuellen Zykluszeit hat eine Auflösung 
von 1 µs.

Hier ein Teil des Codes:
1
void CLKS_Initialize ( void )
2
    {
3
    // Timer 2/3 (32 Bit) läuft endlos mit 25 MHz
4
    // Konfiguriert mit Harmony für PIC32MZ
5
    TMR2_PeriodSet(0xffffffff);
6
    TMR2_Start();           // Start the timer            
7
    LogMsg("The 1 microsecond timer has started");
8
    } 
9
10
11
void CLKS_SM ( void )
12
    {
13
    static uint32_t zeit_alt ;
14
    static uint32_t zeit_neu ;
15
    zeit_neu = TMR2_CounterGet(); // Aktuelle Zeit holen
16
    
17
    // Vergangene Zeit in us * 25, weil Timer2/3 mit 25 MHz läuft
18
    zykuszeit_act = (uint32_t) ( (int32_t) zeit_neu - zeit_alt) / 25;    
19
    zeit_alt = zeit_neu;    // Neue Zeit übertragen
20
21
   
22
    appData.usec_cnt += zykuszeit_act ; 
23
    appData.msec_cnt = appData.usec_cnt/1000; 
24
    
25
    if (appData.usec_cnt >= 1000000)
26
        {
27
        //---------jede Sekunde------------------------------------
28
        appData.usec_cnt -= 1000000;
29
        appData.sec_cnt++;
30
        appData.unixsecs++;
31
        clk1000();
32
        }

von Falk B. (falk)


Lesenswert?

Dirk F. schrieb:
> Also Jungs,  das war der best Hinweis überhaupt.
> Durch das neue System wird der Prozessor gar nicht mehr mit Interrupts
> belastet. Und die Messung der aktuellen Zykluszeit hat eine Auflösung
> von 1 µs.

Schöner Unfug. Vor allem, da dein Timer mit 25 MHz läuft. Der hat auch 
einen Vorteiler, den kann man einstellen, dann läuft der direkt mit 
1MHz, was immer noch Käse ist, aber etwas weniger Käse.

>     static uint32_t zeit_alt ;
>     static uint32_t zeit_neu ;
>     zeit_neu = TMR2_CounterGet(); // Aktuelle Zeit holen
>
>     // Vergangene Zeit in us * 25, weil Timer2/3 mit 25 MHz läuft
>     zykuszeit_act = (uint32_t) ( (int32_t) zeit_neu - zeit_alt) / 25;

Was soll der cast-Unsinn? Deine Variablen sind 32 Bit, da braucht man 
nix weiter!

>     zeit_alt = zeit_neu;    // Neue Zeit übertragen
>
>     appData.usec_cnt += zykuszeit_act ;
>     appData.msec_cnt = appData.usec_cnt/1000;
>
>     if (appData.usec_cnt >= 1000000)
>         {
>         //---------jede Sekunde------------------------------------
>         appData.usec_cnt -= 1000000;
>         appData.sec_cnt++;
>         appData.unixsecs++;
>         clk1000();
>         }

Jaja, warum nicht gleich Nanosekundenauflösung?

von Dirk F. (dirkf)


Lesenswert?

Falk B. schrieb:
> einen Vorteiler, den kann man einstellen, dann läuft der direkt mit
> 1MHz, was immer noch Käse ist, aber etwas weniger Käse.

Nee, keinen Vorteiler 100,  nur 128

von Falk B. (falk)


Lesenswert?

Dirk F. schrieb:
>> einen Vorteiler, den kann man einstellen, dann läuft der direkt mit
>> 1MHz, was immer noch Käse ist, aber etwas weniger Käse.
>
> Nee, keinen Vorteiler 100,  nur 128

Ja und? Dann hättest du 25MHz/128=195,3125kHz (5,12us) Auflösung. Immer 
noch mehr als sinnvoll, vor allem für solche Messungen.

von Andreas M. (amesser)


Lesenswert?

Falk B. schrieb:
> Ja und? Dann hättest du 25MHz/128=195,3125kHz (5,12us) Auflösung. Immer
> noch mehr als sinnvoll, vor allem für solche Messungen.

Woher willst Du das wissen? Wir machen hier z.B. synchrone 
Automatisierungstechnik, da messe ich die Laufzeit meiner Software in 
"ns". 2us Jitter liegen bei uns schon außerhalb der Spezifikation. 5us 
sind ne halbe Ewigkeit.

von Falk B. (falk)


Lesenswert?

Andreas M. schrieb:
> Woher willst Du das wissen?

Daher!

"Vielen Dank an alle. Ich habe es kapiert.
Bin halt nur Gelegenheitsprogrammierer...."

Das ist nur Hobbyspielerei.

> Wir machen hier z.B. synchrone
> Automatisierungstechnik, da messe ich die Laufzeit meiner Software in
> "ns". 2us Jitter liegen bei uns schon außerhalb der Spezifikation. 5us
> sind ne halbe Ewigkeit.

Von dir und deiner professionellen Anwendung war gar nicht die Rede.

von Dirk F. (dirkf)


Lesenswert?

Falk B. schrieb:
> Das ist nur Hobbyspielerei.

Nee, industrielle Anwendung mit Profinet Anbindung.

von Falk B. (falk)


Lesenswert?

Dirk F. schrieb:
>> Das ist nur Hobbyspielerei.
>
> Nee, industrielle Anwendung mit Profinet Anbindung.

Und darauf lassen die einen Gelegenheitsprogrammierer los. Sehr 
professionell!

von Dirk F. (dirkf)


Lesenswert?

Falk B. schrieb:
> Und darauf lassen die einen Gelegenheitsprogrammierer los. Sehr
> professionell!

Falk, ich glaube Du bist etwas frustriert.
Es gibt auch Leute, de neben Programmieren auch noch etwas anders 
beruflich machen.
Fachidiot:  Kann eine Sache zu 100 %

Guter Mitarbeiter:  Kann mehrere Sachen, nicht zu 100 % aber ist bereit 
sich neuen Aufgaben zu stellen und sich einzuarbeiten....

von Foobar (asdfasd)


Lesenswert?

dirkf schrieb:
> Fachidiot:  Kann eine Sache zu 100 %

Auf den kann man sich also verlassen - liefert stets korrekte 
Ergebnisse.

> Guter Mitarbeiter:  Kann mehrere Sachen, nicht zu 100 % aber ist bereit
> sich neuen Aufgaben zu stellen und sich einzuarbeiten....

Kann also nichts richtig - liefert stets zusammengeschusterten 
Laienpfusch und wird in Kürze von ChatGPT/Copilot ersetzt.  Diese 
Einstellung, dass sowas ein "Guter Mitarbeiter" ist, scheint bei den 
Personalern leider weit verbreitet zu sein (er ist halt billiger).  Es 
wird übersehen, dass das "sich einarbeiten" in Fachgebiete gerne mal 
Jahre dauert.  Da dafür die Zeit nicht reicht, gibt man sich mit Pfusch 
zufrieden - entsprechend ist der aktuelle Stand der Technik.

Ich wüsste auf jeden Fall, wen ich also Mitarbeiter haben wollte ...

: Bearbeitet durch User
von Andreas M. (amesser)


Lesenswert?

Foobar schrieb:
> Auf den kann man sich also verlassen - liefert stets korrekte
> Ergebnisse.

Kein Mensch liefert stets korrekte Ergebnisse.

Foobar schrieb:
>> Guter Mitarbeiter:  Kann mehrere Sachen, nicht zu 100 % aber ist bereit
>> sich neuen Aufgaben zu stellen und sich einzuarbeiten....
>
> Kann also nichts richtig - liefert stets zusammengeschusterten
> Laienpfusch
> [Noch mehr Unsinn]

Nö, genau die Leute die breit Aufgestellt sind werden die einzigen sein, 
die zukünftig noch Arbeit haben werden. Denn Gerade die "Fachidioten" 
sind die, die man mit KI ersetzen wird. Bisher hat man solche Arbeiten 
gerne nach Fernost abgeschoben, aber die Leute dort haben  auch keinen 
Bock mehr da drauf.

Die Ergebnisse von Entwicklern, die nur in Ihrer eigenen kleinen Welt 
leben, darf ich jeden Tag erleben.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Dirk F. schrieb:
> Guter Mitarbeiter:  Kann mehrere Sachen, nicht zu 100 % aber ist bereit
> sich neuen Aufgaben zu stellen und sich einzuarbeiten....
... und findet nötigenfalls schneller eine Ausrede als eine Maus ein 
Loch!

Sehr guter Mitarbeiter: kann mehrere Sachen zu 100 %, kann über den 
Tellerrand hinausblicken und Zusammenhänge herleiten.

Nachdem das geklärt ist gehen wir mal zur Sache...

Falk B. schrieb:
> Schöner Unfug. Vor allem, da dein Timer mit 25 MHz läuft.
Hab ich mir sofort auch gedacht, als ich /25 und /1000 gesehen habe. Arg 
viel ineffizienter kann man nicht prorammieren, denn auch heute noch 
sind Prozessoren im tiefsten Inneren binär. Eine dezimale Rechnung (oder 
allgemeiner einer Rechnung jenseits von Zweierpotenzen) macht ihnen 
Aufwand.

Dirk F. schrieb:
> Nee, keinen Vorteiler 100,  nur 128
Sag ichs noch: ein µC ist binär.
Für eine effiziente Programmierung musst du dich dem Controller 
anpassen, denn der Witz ist: du schreibst das Programm nur ein einziges 
Mal, der Controller muss es aber zigtausendmal pro Sekunde ausführen.

Ich mache es so: ein Zeitzähler (Timertic) läuft immer hoch. Er wird 
niemals von Hand zurückgesetzt. Nur am "Ende" der läuft er implizit 
über. Und basierend auf diesem Zähler dann werden alle Zeiten per 
Differenz verwaltet. Ein Tipp dazu: sieh dir die Arduino millis() 
Funktion und die Arbeit mit Zeitdifferenz mal genauer an.

Andreas M. schrieb:
> 2us Jitter liegen bei uns schon außerhalb der Spezifikation. 5us sind ne
> halbe Ewigkeit.
Ja, mag sein. Allerdings bin ich mir absolut sicher, dass daran dann 
aber auch keine "fortgeschrittenen Laien" arbeiten.

Im Fall hier würde ich mir überhaupt erst mal klar machen, welche 
Zeitauflösung denn nötig und welcher Jitter erlaubt ist. Und dann 
eine geeignete Zeitbasis aufbauen, die dem Controller "gut liegt" und 
mit der er einfach arbeiten kann.

Falk B. schrieb:
> Dann hättest du 25MHz/128=195,3125kHz (5,12us) Auflösung
Also könnte ich da einfach einen "hundertstel µs"-Zähler bei jedem 
Interrupt um 512 hochzählen. Und wenn der dann über 100000 hinausläuft, 
dann würde ich den ms-Zähler um 1 hochzählen und den "hundertstel 
µs"-Zähler atomar(!) um 100000 verringern.

Also etwa so:
1
long millis, lasttoggle; 
2
:
3
TimerInterrupt () { // 5.12µs
4
   static long us100cnt;
5
   us100cnt += 512;
6
   if (us100cnt>100000) {
7
      millis++;
8
      us100cnt-=100000;
9
   }
10
}
11
:
12
// Arbeiten mit Zeitdifferenzen
13
if (millis-lasttoggle > 1000) {
14
    toggleLED();
15
    lasttoggle=millis; // Umschaltzeitpunkt merken
16
}
17
:
18
// oder besser so, dann summieren sich Verzögerungen nicht auf, 
19
// sondern "verpasste" Toggles werden "nachgeholt" 
20
if (millis-lasttoggle > 1000) {
21
    toggleLED();
22
    lasttoggle+=1000; // Umschaltzeitpunkte jeweils alle 1000 ms
23
}
24
:

: Bearbeitet durch Moderator
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.