Forum: Mikrocontroller und Digitale Elektronik Timer Interrupt Service Routine und die main()


von Detlef _. (detlef_a)


Lesenswert?

Hallo,

ich sehe nen Effekt, den ich nicht verstehe, was zweifellos an meiner 
mangelnden Einsichtsfähigkeit liegt, aber Ihr könnt das besser:

STM32F405 Olimex board, gcc. etc. pp., hat mit der hardware nix zum tun.

Ich hole mir in der timergesteuerten ISR einen ADC-Wert (160ks/s) und 
werfe den ADC dann gleich wieder an, warte also nicht. Die Werte 
schreibe ich in einen Wechselbuffer: auf einem wird in der main() 
gerechnet, der andere wird in der ISR vollgeschrieben. In der main() 
rechne ich also auf dem fertigen Buffer und warte dann, bis der nächste 
buffer fertig ist. Wenn der fertig ist, fange ich von vorn an.

Die Wartezeit macht eine langsame Sägezahnkurve, steigt linear an bis 
sie schlagartig wieder abfällt.

Das verstehe ich nicht, eigentlich müsste die Wartezeit doch konstant 
sein!? Das Ganze würde ich wie so vieles Andere unverstanden lassen, 
wenn nicht meine berechneten Messerte auch mit derselben Periode von 
einem Signal überlagert wären.

Kann sich jemand nen Reim drauf machen, Programmierfehler, irgendwas 
anderes oder ist der Sägezahn in dem plot schon angelegt?

hm
THX
Cheers
Detlef

von (prx) A. K. (prx)


Lesenswert?

Code sagt mehr als 1000 Worte.

von Falk B. (falk)


Lesenswert?

@  Detlef _a (detlef_a)

>STM32F405 Olimex board, gcc. etc. pp., hat mit der hardware nix zum tun.

Wirklich?

>Ich hole mir in der timergesteuerten ISR einen ADC-Wert (160ks/s) und

160 ks/s sind auch für einen 32 Bitter schon SEHR sportlich, wenn man 
jeden Meßwert einzeln per ISR abholt!

>Die Wartezeit macht eine langsame Sägezahnkurve, steigt linear an bis
>sie schlagartig wieder abfällt.

Klingt danach, als ob deine CPU ISRs verschluckt. Dann kommt eine 
Schwebung raus, eben deine Sägezahnkurve.

von Detlef _. (detlef_a)


Lesenswert?

Yo, das könntes' sein. Probier ich aus, indem ich die Rechenlast im 
Interrupt reduziere, vllt. is dann weg.
Ausserdem mache ich mich kundig, wie man beim Cortex einen ISR Overrun 
entdecken kann :-/

Melde mich.
Danke.
Cheers
Detlef

von Detlef _. (detlef_a)


Lesenswert?

Hallo,

ich habe immer noch das Problem: Die Wartezeit auf die interrupt service 
routine (idle_cnt, anghängte source) beschreibt eine Sägezahnkurve, 
meine Messwerte sind auch überlagert von dieser Kurve.

Ich habe die ISR jetzt bis auf die Knochen abgemagert, da passiert nix 
langwieriges mehr, siehe angehängte source.

Frage: Das 'TIM_ClearITPendingBit(TIM2, TIM_IT_Update);' ist so ok !?

Die ganze Rechnerei passiert jetzt im main(), angehängt. Dort kriege ich 
mit, wenn ich die Rechenlast nicht mehr schaffe, das geht.
Dort rufe ich auch meine Funktion lin_regress() auf, die macht fett 
(hardware, 32Bit) float-Rechnung.

Ich benutze dort die lib-Funktionen acosf, sinf, cosf, atan2f, sqrtf, 
log10f neben float add, mult und div.

Frage: Kann es sein, dass die lib-Routinen die interrupts stilllegen !?

War da nicht irgendwas mit float Rechnung und interrupts !? Die 
float-Register wurden im interrupt nicht gesichert und durften deswegen 
im interrupt nicht benutzt werden (was ich nicht mehr tue), oder 
verwechsel ich da Prozessoren!? Wüßte auch nicht, wo ich in der Doku 
suchen sollte.

Bin STM32 Anfänger, ich weiß.

Danke für Eure Mühe.
Cheers
Detlef





main:

   while(1){
     lin_regress(&hbuf[buf_calc*(HBUFLEN/2)],HBUFLEN/2,0);
     val8= 1&((uint8_t)(buf_calc)^(uint8_t)(hbufp<HBUFLEN/2));
     if(val8==1) statusword |= STATUS_OVERRUN;
     buf_calc = 1&(buf_calc+1);
     idle_cnt=0;
     while( 1&((uint8_t)(buf_calc)^(uint8_t)(hbufp<HBUFLEN/2))) 
idle_cnt++;
  }



/******************************************************************/
void TIM2_IRQHandler()
/******************************************************************/

{  uint8_t ui;
         int16_t i1;
  uint16_t adcval;
  uint32_t ind;
  uint8_t c;

    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);

    isrcnt ++;
    timercnt ++;

    adcval=ADC_GetConversionValue(ADC1);
    ADC_SoftwareStartConv(ADC1);

    hbuf[hbufp]=(adcval);
    hbufp++;

    if(hbufp==HBUFLEN) hbufp=0;



}

von Rolf Magnus (Gast)


Lesenswert?

1/160k sind 6,25 µs Zeit zwischen zwei Interrupts. Das ist schon eine 
sehr kurze Zeit.
Eine andere Sache noch: Was passiert denn, wenn dein ADC beim Einteten 
des Timer-Interrupts noch nicht fertig mit der Konvertierung ist?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Warum machst du das überhaupt so kompliziert? Der STM32F4 hat einen DMA 
mit Double-Buffer-Modus, d.h. alles was deine ISR macht kann der 
komplett in Hardware. In der main() musst du also nur noch die Werte aus 
einem Array holen (welches, sagt dir ein DMA-Register).

von Detlef _. (detlef_a)


Lesenswert?

Hallo,

>>>>>>>>>>>>>>>>
Was passiert denn, wenn dein ADC beim Einteten
des Timer-Interrupts noch nicht fertig mit der Konvertierung ist?

Ist er auf jeden Fall, hatte ich so aus dem Datenblatt gesehen.

>>>Warum machst du das überhaupt so kompliziert?

Ja, ich weiss der kann DMA. Aber erstens finde ich DAS komplizierter 
aufzusetzen als die ISR und ausserdem wollte ich fast alles gleich im 
interrupt rechnen.

Aber wenn das so nix wird werde ich mich wohl durch die DMA beissen.

Was ist denn mit float-Rechnung und interrupt, gibts' da was zu beachten 
was ich nicht weiss?

Cheers
Detlef

von Pandur S. (jetztnicht)


Lesenswert?

Der ADC wird aber nicht in einem Buffer geschrieben um einen Mittelwert 
zu errechnen ? Das waer dann schade und komplett unnuetz.

Und float ... aaahhhhhhhh. Wozu braucht man float ? Um einen ADC Wert 
umzurechnen ? Sowas macht man sonst eigentlich nicht. Der ist als 
Integerwertt genauso nuetzlich.

von Detlef _. (detlef_a)


Lesenswert?

Jetztnicht wieder die Standardeinlassung 'kein float'. Oh Doch, ich 
weiss genau was ich rechne.

Geht denn jetzt interrupt zusammen mit float?

Cheers
Detlef

von aSma>> (Gast)


Lesenswert?

> Ja, ich weiss der kann DMA. Aber erstens finde ich DAS komplizierter
> aufzusetzen als die ISR und ausserdem wollte ich fast alles gleich im
> interrupt rechnen.

Das hört sich nach
> mangelnden Einsichtsfähigkeit
an.

Was ist da kompliziert? Ein paar Zeilen Code mehr. ILOIL.

> Was ist denn mit float-Rechnung und interrupt, gibts' da was zu beachten
> was ich nicht weiss?

Nicht wenn du die FPU angemacht hast.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Detlef _. schrieb:
> Geht denn jetzt interrupt zusammen mit float?
Ja, du musst einschalten dass die Float-Register beim ISR-Betreten 
gesichert werden, sonst werden die float-Werte der main() oder 
unterbrochenen anderen ISR's überschrieben. Das geht zB über das Bit 
ASPEN im Register FPCCR. Dadurch werden die ISR's logischerweise nochmal 
langsamer. Für einen Mittelwert braucht man natürlich kein float, und es 
ist natürlich auch zu beachten dass bei floating-Point immer Präzision 
verloren gehen kann.

Siehe S. 672 im ARMv7M Architecture Reference Manual.

von дампфтроль (Gast)


Lesenswert?

Auch wenn man eine FPU hat rechnet man nie den Mittelwert, sondern den 
Tiefpass. Siehe zB auch
http://www.ibrtses.com/embedded/exponential.html
Den rechnet man auf den integer ADC Werten und kann den getiefpassten 
Wert dann skalieren wenn man dem skalieren verfallen ist.
Der Vorteil vom Tiefpass : nach jedem Sample gibt's einen Wert mit 
minimalem aufwand.

von Wolfgang K. (donkracho)


Lesenswert?

Ohne mich mit dem Chip bzw. SDK schon einmal auseinandergesetzt zu 
haben:

Erstens: Ich sehe keinerlei Synchronisationsmechanismus des Buffers 
zwischen ISR und Main. Wie soll das zuverlässig funktionieren?

Zweitens: Was ist denn die maximale Abtastrate des ADC?

Wenn die Funktion ADC_GetConversionValue(ADC1) blockierend ist bis die 
Wandlung fertig ist, ist das Verhalten doch kein Wunder. Die wartende 
ISR blockiert denn sogar noch den Prozessor. Das kann IMHO so nicht 
gewollt sein.

Ist die Funktion nicht blockierend, werden aber auch merkwürdige Dinge 
passieren, denn dann liest du ungültige Werte, wenn die Wandlung zum 
Abfragezeitpunkt nicht fertig ist.

Kommt es dir auf eine konstante Abtastfrequenz an, oder willst du so 
schnell wie möglich ADC-Werte erhalten? Hat der ADC keinen "Wandlung 
fertig" Interrupt?

Beim Atmel würde man mit dem ADC-Interrupt lesen und will man eine 
bestimmte, konstante Abtastrate erreichen eine neue Wandlung mit einem 
Timer-Interrupt starten. Die Frequenz des Timer-Interrupts muss dabei 
niedriger sein als die maximale Abtastrate des ADC. Dann laufen beide 
Interrupts so kurz wie nötig.

Auch die Main Routine muss mit einer Berechnung schneller fertig werden 
als die Werte angeliefert werden, ansonsten hast du trotzdem diesen 
Effekt weil die Main dann Abtastwerte unberechnet verschluckt.

DMA hin DMA her, deine Beobachtung deutet darauf hin, dass Wandlung 
und/oder Berechnung zu langsam sind.

: Bearbeitet durch User
von Boris O. (bohnsorg) Benutzerseite


Lesenswert?

Wolfgang K. schrieb:
> DMA hin DMA her, deine Beobachtung deutet darauf hin, dass Wandlung
> und/oder Berechnung zu langsam sind.

Was nach Aussage des TO nicht sein kann, weil im Datenblatt steht, sein 
Code sei rechtzeitig fertig. (Ich wundere mich, wie aufwändig die 
Datenblätter heutzutage sind, dass sogar fremder Entwickler Code darin 
bedacht ist.)

Detlef _. schrieb:
>> Was passiert denn, wenn dein ADC beim Einteten
>> des Timer-Interrupts noch nicht fertig mit der Konvertierung ist?
>
> Ist er auf jeden Fall, hatte ich so aus dem Datenblatt gesehen.

Und was (Lib-) Funktionen wie die für Winkel anbelangt wäre ich bzgl. 
der Ausführungszeit vorsichtig. Die Standarddiskussion über float vs. 
int möchtest du ja nicht. Schnell geht jedenfalls anders und darüber 
gibt es dicke Pappen mit bedrucktem Papier dazwischen.

von Little B. (lil-b)


Lesenswert?

Detlef _. schrieb:
> Geht denn jetzt interrupt zusammen mit float?

Natürlich geht Interrupt mit Float. Hierbei gibt es aber verschiedene 
Szenarios:

- Komplette FPU Register beim Interrupt entry sichern und beim return 
wieder herstellen. Kostet extrem viel zeit und ist meist völlig 
überflüssig. Denn:
- Keine FPU benutzen im Interrupt. Dann muss nichts gesichert werden. 
Das ist bei deinem Beispielcode ja der Fall.
- Oder Lazy Stacking verwenden. Hier wird die FPU erst gesichert, wenn 
sie tatsächlich im Interrupt verwendet wird. Damit ist man eigentlich 
auf der sicheren Seite, aber bitte nachschaun, wie und wo die register 
gesichert werden.

Oh D. schrieb:
> Und float ... aaahhhhhhhh. Wozu braucht man float ? Um einen ADC Wert
> umzurechnen ? Sowas macht man sonst eigentlich nicht. Der ist als
> Integerwertt genauso nuetzlich.

Der Cortex M4 ist für Regel- und DSP-Aufgaben gemacht. Natürlich kann 
der Float berechnen, und das sogar sehr gut. Die Zeiten der alten PIC16 
sind vorbei, "float ist böse" gilt also nicht mehr!

Rolf Magnus schrieb:
> 1/160k sind 6,25 µs Zeit zwischen zwei Interrupts. Das ist schon eine
> sehr kurze Zeit.

Ich nehme mal an, dass dein Prozessor mit 168MHz taktet. Dann hast du 
zwischen zwei Interrupts genau 1050 Taktzyklen. Das ist schon arg knapp, 
da hier auch noch der Context Switch mit abgedeckt werden muss.

von bla (Gast)


Lesenswert?

Little B. schrieb:
> Der Cortex M4 ist für Regel- und DSP-Aufgaben gemacht. Natürlich kann
> der Float berechnen, und das sogar sehr gut. Die Zeiten der alten PIC16
> sind vorbei, "float ist böse" gilt also nicht mehr!

Yep. Details hier:

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0439b/BEHJADED.html

von Detlef _. (detlef_a)


Lesenswert?

Hallo,

danke für die vielen Anregungen und Überlegungen.

Zu den Zweifeln am Grundkonzept:
ich setze nen Timerinterupt auf. Wenn der kommt lese ich den ADC aus und 
starte ihn gleich neu. Den gelesenen Wert speichere ich in einem Buffer. 
Der Bufferpointer macht mir Synchronisation im main(), da weiss ich wo 
der Sampler gerade ist. Wenn ich sicher bin, dass der ADC fertig ist 
wenn der nächste Interrupt kommt, funktioniert das so.

>>>>>>>>>>
- Keine FPU benutzen im Interrupt. Dann muss nichts gesichert werden.
Das ist bei deinem Beispielcode ja der Fall.
<<<<<<<<<<

Ok, da bin ich erstmal beruhigt.

>>>>>>>>
Der Cortex M4 ist für Regel- und DSP-Aufgaben gemacht. Natürlich kann
der Float berechnen, und das sogar sehr gut.
<<<<<<<<<

Ja, deswegen hab ich den ja genommen.

>>>>>>>>>>
Und was (Lib-) Funktionen wie die für Winkel anbelangt wäre ich bzgl.
der Ausführungszeit vorsichtig.
<<<<<<<<<

Ja, die sind unübersichtlich und aufwendig. Ich werde mal probieren, was 
ohne diese Funktionen passiert. Den Effekt (Sägezahn bei der Wartezeit) 
sollte ich auch ohne korrekte Rechergebnisse sehen.

Wenn das nix bringt setze ich den DMA auf. Sollte ich trotz meiner 
mangelnden Einsichtsfähigkeit schaffen, wird aber dauern. Und, nein, 
einen Mittelwert möchte ich nicht berechnen.

Danke
Cheers
Detlef

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.