Ich bräuchte mal etwas Hilfe beim Thema Timer im Umfeld der
STM32CubeIDE. Habe nun schon mehrere Lehrvideos und Tutorials durch,
aber irgendwie bekomme ich nicht die gewünschte Funktion.
Eigentlich klingt es ganz einfach:
- "TIM2" "Clock Source" auf "Internal Clock" stellen
- In den NVIC-Settings "TIM2 global interrupt" auf "Enabled" stellen
- In den "Parameter Settings" einen Prescaler und Counter Period
einstellen (ich habe 32000 und 1000 gewhält um ein hoffentlich 1
Sekunden Intervall zu erhalten)
- Code-Generator anwerfen, das erzeugt in "main.c" dann
vor der while(1) Schleife hinzugefügt um den Timer zu starten.
Dennoch wird mein ISR nie aufgerufen :-(
Ausgeführt wird der Code auf einem Blue Pill board.
Olli Z. schrieb:> Eigentlich klingt es ganz einfach:
Eigentlich ist es ganz einfach:
- Ganze minimalistische Soure posten die das Problem aufzeigt.
- *.ioc posten
- warten was da kommt.
Johannes S. schrieb:> So auf den ersten Blick fehlt da noch ein HAL_TIM_Base_Start_IT().
AAAAARGH!!! Das wars, ich hatte nur ein "HAL_TIM_Base_Start();" drin,
das "_IT" dahinter fehlte. Und schwubs - schon läufts! :-)
DANKE!
Olli Z. schrieb:> In den "Parameter Settings" einen Prescaler und Counter Period> einstellen (ich habe 32000 und 1000 gewhält um ein hoffentlich 1> Sekunden Intervall zu erhalten)
Noch so als Hinweis:
Bei Prescaler 0 wird nicht durch Null "geteilt" und der "Counter" fängt
bei 0 an zu zählen und nicht bei 1.
Bei Werten von 32000 und 1000 fällt das vielleicht nicht so auf, bei
Werten von 12 und 3 aber schon ganz erheblich.
Olli Z. schrieb:> Einen ISR hinzugefügt:/* USER CODE BEGIN 4 */> void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)> {> HAL_GPIO_TogglePin(BP_LED_GPIO_Port, BP_LED_Pin);> }> /* USER CODE END 4 */
Da es nur einen Callback für alle Timer gibt, bei den Callbacks IMMER
testen, ob auch tatsächlich dein Timer gemeint ist.
das gilt auch für UART, SPI, I2C usw.
Also so:
Olli Z. schrieb:> - Einen ISR hinzugefügt:
Nein, das ist keine ISR (Interrupt Service Routine), sondern eine
Callback-Funktion, die von irgendwo anders aufgerufen wird, aber es ist
nicht die eigentliche ISR.
Ich würde sowas direkt machen und mich dabei nur nach dem RefManual
richten. Das ist über alles gesehen sowohl übersichtlicher als auch
einfacher.
Bedenke mal, was da bei dir überhaupt so abgeht: Anstatt daß du die ISR
selber schreibst, mußt du zuerst dein Ansinnen in irgend eine
Callback-Liste oder so eintragen lassen und später, wenn tatsächlich ein
Interrupt kommt, muß die eigentliche ISR nachschauen, ob sie deine
Callback-Funktion findet, ob selbige auch gültig ist, und und und - und
dann wird erst deine Funktion aufgerufen. Mir wäre dieser Haufen an
Indirektionen und Verkomplizierungen überhaupt nicht recht.
W.S.
W.S. schrieb:> Anstatt daß du die ISR> selber schreibst, mußt du zuerst dein Ansinnen in irgend eine> Callback-Liste oder so eintragen lassen
Du hast offenbar absolut keine Ahnung, wie das bei HAL läuft.
NEIN!
Er muß keine Callback-Funktionen registrieren.
Die sind als __WEAK bereits vorhanden, und der Callback des TO
"überschreibt" diese Funktion. (macht der Linker)
Und nochmal NEIN!
Er muß das NICHT selber neu schreiben.
Das funktioniert ganz hervorragend, wenn man es richtig macht.
W.S. schrieb:> mußt du zuerst dein Ansinnen in irgend eine Callback-Liste oder so> eintragen lassen und später, wenn tatsächlich ein Interrupt kommt, muß> die eigentliche ISR nachschauen, ob sie deine Callback-Funktion findet,> ob selbige auch gültig ist, und und und
Nein, hier ist die Funktion weak deklariert und wird direkt in der ISR
aufgerufen. Der Linker kennt die Adresse und da muss nix zur Laufzeit
überprüft werden.
Die STM sind hier nur komplex mit ihren umfangreichen Sammelinterrupts,
da muss erstmal der Auslöser gefunden werden.
Und eine generische Lösung über den Haufen zu werfen, nur um einige zig
ns pro Sekunde zu sparen, ist ja wohl eine sehr unsinnige Optimierung.
Und magic Numbers machen das auch nicht schneller.
Harry L. schrieb:> Da es nur einen Callback für alle Timer gibt, bei den Callbacks IMMER> testen, ob auch tatsächlich dein Timer gemeint ist.
Richtig, da hast Du natürlich völlig recht.
Christopher J. schrieb:> Bei Prescaler 0 wird nicht durch Null "geteilt" und der "Counter" fängt> bei 0 an zu zählen und nicht bei 1.
Hä? Mein Prescaler ist 8000.
Olli Z. schrieb:> Hä? Mein Prescaler ist 8000.
Der wahre Prescaler ist immer Registerwert +1.
In deinem Falle teilst du den Takt daher durch 8001.
Seidenn der HAL hat intern ein -1, da muss man in den Code gucken.
W.S. schrieb:> Nein, das ist keine ISR (Interrupt Service Routine), sondern eine> Callback-Funktion, die von irgendwo anders aufgerufen wird, aber es ist> nicht die eigentliche ISR.
Hach W.S. die wandelnde VOllpanne in diesem Forum.
Du kommst mir vor wie ein alter verbitterter Kerl mit nem Sprung in der
Platte.
Schreibst immer das gleiche falsche Zeuchs und lässt Argumente von
anderen nicht gelten.
Johannes S. schrieb:> Und magic Numbers machen das auch nicht schneller.
Das hat W.S. genausowenig verstanden wie DMA.
Wer so exorbitant Magic Numbers nutzt sollte sich eh zurück halten wenn
es um Code Arcitektur geht, weil er damit schon mitgeteilt hat, dass er
nicht ordentlich programmieren kann.
"The true prescaler is always register value +1. In your case you divide
the clock by 8001. Because the HAL has a -1 internally, you have to look
into the code."
Nope! Because values starts from 0 you have to write "value_x - 1" to
obtain the right prescaler value.
On the other hand, the user WS is right, the callback function is call
from TIM_IRQ_Handler (located in file "stm32f1xx.it" so...
Mw E. schrieb:> Der wahre Prescaler ist immer Registerwert +1.> In deinem Falle teilst du den Takt daher durch 8001.> Seidenn der HAL hat intern ein -1, da muss man in den Code gucken.
Nene, so obfuscated ist STs HAL dann doch wieder nicht ;)
Olli Z. schrieb:> Christopher J. schrieb:>>> Bei Prescaler 0 wird nicht durch Null "geteilt" und der "Counter" fängt>> bei 0 an zu zählen und nicht bei 1.>> Hä? Mein Prescaler ist 8000.
Das mit Prescaler 0 war eine Denksportaufgabe um dich darauf zu bringen,
dass du als Prescaler-Wert 7999 einstellen muss, damit er durch 8000
teilt. Beim Counter ists ähnlich, weil der bei 0 anfängt zu Zählen musst
du N-1 ins ARR-Register schreiben, damit er N Timertakte pro Zyklus
durchläuft.
First of all, thank you for your -1 vote, truth hurts!
To be clear: The prescaler is off by 1 because it’s 0-based: a PSC value
of “0” means to use a prescaler (clock divider) of 1.
On the other hand, you will find "void
HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)" in file "stm32f1xx_tim.c"
and is called from "stm32f1xx_it.c" by "void TIM_IRQHandler(void)"
For OP: if you want to obtain 1 second pulse you may use this, for
SYSCLK = 72MHz:
1
htim1.Init.Prescaler=7199;
2
htim1.Init.Period=9999;
Also you have to activate NVIC interrupt for e.g. TIM1, I mean "TIM1
update interrupt" and start the timer with
Harry L. schrieb:> Du hast offenbar absolut keine Ahnung, wie das bei HAL läuft.>> NEIN!>> Er muß keine Callback-Funktionen registrieren.> Die sind als __WEAK bereits vorhanden, und der Callback des TO> "überschreibt" diese Funktion. (macht der Linker)>> Und nochmal NEIN!
Ach wo, du irrst hier gewaltig.
Du willst mir jetzt nicht wirklich einreden, daß das da:
eine ISR sei, gelle? Oder gibt es bei den µC von ST mittlerweile auch
ISR, die einen Parameter abkriegen?
Nein, natürlich nicht.
Keine ISR dieser Welt hat irgendwelche Parameter, die sind alle
void isrname(void);
und nicht anders.
So, ihr aufgeregten Pappnasen. Nebenbei gesagt, steht die Wahrheit auch
direkt im Namen dieser Funktion, die der TO da als ISR ansieht. Es ist
eben eine Callback-Funktion und da diese eben von woanders zurückgerufen
werden soll (mit Parameter), muß besagte andere Stelle sie in
irgendeiner Liste führen. Eben ganz GENAU DAS, was ich weiter oben
bereits geschrieben habe.
W.S.
Johannes S. schrieb:> W.S. schrieb:>> mußt du zuerst dein Ansinnen in irgend eine Callback-Liste oder so>> eintragen lassen und später, wenn tatsächlich ein Interrupt kommt, muß>> die eigentliche ISR nachschauen, ob sie deine Callback-Funktion findet,>> ob selbige auch gültig ist, und und und>> Nein, hier ist die Funktion weak deklariert und wird direkt in der ISR> aufgerufen. Der Linker kennt die Adresse und da muss nix zur Laufzeit> überprüft werden.
HÄ?
Du schreibst wirr. Also nochmal zum nachlesen: Es gibt eine ISR, die
wird niemals aufgerufen, sondern von der HW gestartet, wobei sie den
gewöhnlichen Programmablauf unterbricht. Das mit dem WEAK ist was
anderes, nämlich der Linker-Mechanismus, um bei im Programm unbenutzten
Interrupts die als WEAK gekennzeichnete Default-ISR in die Vektortabelle
einzutragen.
Was also soll dein Satz "Nein, hier ist die Funktion weak deklariert und
wird direkt in der ISR aufgerufen." eigentlich sagen? Er ist in allen
Teilen wirr.
W.S.
das du keine Ahnung hast, das hast du schon oft genug bewiesen. Das es
so schlimm ist hätte ich nicht gedacht. Schau doch einfach mal in den
Quellcode. Es ist genauso wie Harry und ich geschrieben haben.
Und es können auch andere Funktionen als ISR mit weak gekennzeichnet
werden.
W.S. schrieb:> HÄ?> Du schreibst wirr.
Nein, dir fehlen nur sämtliche Grundlagen zum Thema ST-HAL, und da du
das auch nicht wissen willst, macht es auch wenig Sinn, dir das in
epischer Breite zu erklären.
Die, die damit arbeiten, haben das verstanden.
Ok, ein le. Versuch:
Callback-Funktionen sind in der HAL so definiert:
schreibe, wird ab sofort meine Funktion direkt aus der Interrupt-Routine
aufgerufen.
Daß du "__weak" nicht kennst, wundert mich allerdings nicht wirklich....
W.S. schrieb:> Was also soll dein Satz "Nein, hier ist
Mit 'hier' war der Code vom TO gemeint. Man kann in der HAL per define
noch eine andere Variante einstellen, in der wird eine Sprungtabelle
benutzt. Damit ist es einmal indirekt, aber auch das ist keine Liste in
der ISR wo erst ein Funktionszeiger gesucht und validiert werden muss.
Der Vorteil ist dabei, das die (Callback)Funktion zur Laufzeit gesetzt
werden kann ohne gleiche eine neue ISR bauen zu müssen. Die HAL
Programmierer sind lange nicht so dumm wie du denkst.
Harry L. schrieb:> Indem ich eine Funktion> void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)> .............> schreibe, wird ab sofort meine Funktion direkt aus der Interrupt-Routine> aufgerufen.
Zwischenfrage: und wer generiert das Argument für den Callback?
dunno schrieb:> Zwischenfrage: und wer generiert das Argument für den Callback?
Die Interruptroutine übergibt das DeviceHandle an die Callbacks.
Das wird gebraucht, um die Interrupt-Quelle eindeutig zu identifizieren.
Ansonsten wüsste ich bei meinem Beispiel zwar, daß der Callback vom SPI
ausgelöst wurde, aber nicht von Welchem (wenn es mehr als einen gibt)
Harry L. schrieb:> Die Interruptroutine übergibt das DeviceHandle an die Callbacks.
Und ich dachte die ISR ist ein "fleischloses" Wesen das nur
markiert dass ein spezifischer Interrupt aufgetreten ist aber
nichts über Pointer auf die entsprechende Hardware weiss. (?)