Hallo allerseits,
ich wollte meinen Code modularisieren, um später einfach neue Projekte
zusammen zubasteln.
Beschreibung des Problems am Beispiel: Es gibt 2 Module (Modul_A und
Modul_B), die jeweils einen Timer benötigen. Die Signaturen deren
Konfigurationsmethoden sehen folgendermaßen aus:
1
voidModul_A_config(TIM_TypeDef*TIMx,...);
2
voidModul_B_config(TIM_TypeDef*TIMx,...);
In der Main-Methode kann ich für jeden Modul einen Timer festlegen:
1
Modul_A_config(TIM2,...);
2
Modul_B_config(TIM3,...);
So weit, so gut. Die Schwierigkeiten entstehen bei Interrupt-Methoden.
Ich komme nicht dahin, wie man den Methodenrumpf TIM2_IRQHandler durch
TIM3_IRQHandler dynamisch ersetzt. Kann man mittels von Methodenzeiger
die Interruptmethode auf eine andere Methode (z.B. DoCycleWork_A)
"umleiten"? Gibt es überhaupt eine Lösung für dieses Problem?
Minti
Minti schrieb:> Die Schwierigkeiten entstehen bei Interrupt-Methoden.> Ich komme nicht dahin, wie man den Methodenrumpf TIM2_IRQHandler durch> TIM3_IRQHandler dynamisch ersetzt. Kann man mittels von Methodenzeiger> die Interruptmethode auf eine andere Methode (z.B. DoCycleWork_A)> "umleiten"? Gibt es überhaupt eine Lösung für dieses Problem?
Ich habe es noch nie versucht, aber die Adressen der ISRs können vom
Flash ins RAM verlagert werden. So sollte es möglich sein, zur Laufzeit
dort die gewünschten Adressen einzutragen.
Ginge auch über die von Dir beschriebene Methoden mit Funktionszeigern
in der ISR: In der ISR wird einfach ein Funktion über einen Zeiger
aufgerufen.
Die statische Variante verwendet #define/#if:
1
#define MODUL_A_TIMER 1
2
3
#if MODUL_A_TIMER == 1
4
5
void void TIM1_IRQHandler() {
6
TIM_ClearITPendingBit(TIM1, TIM_FLAG_Update);
7
modulAIsr();
8
}
9
10
#elif MODUL_A_TIMER == 2
11
12
void void TIM2_IRQHandler() {
13
TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update);
14
modulAIsr();
15
}
16
17
...
Es stellt sich die Frage, welchen Vorteil der dynamische Ansatz im
Vergleich zur statischen #define/#if-Variante bietet, sofern man nicht
tatsächlich während der Laufzeit mehrfach zwischen verschiedenen ISRs
wechseln will.
Das Problem ist die Vielfalt der Timer beim stm32, und beim stm32 kommt
hinzu, dass teilweise die gleiche ISR von mehreren Timer-Interrupts
angesprungen werden. Positiv beim stm32 ist, dass er so viele Timer hat,
dass man sich den Luxus erlauben kann, grundsätzlich einem Modul einen
Timer fix zuzuweisen - eben den, dessen Fähigkeiten am besten passen.
Da die Anzahl der Timer bzw. der Peripherie limitiert ist, sind das
irgendwie alle "singletons", und das gilt insbesondere bei der ISR.
Hi Roland,
Danke für deine Antwort!
Bei der statischer Variante müsste man für jedes Modul fast das dasselbe
Schreiben. Ich verfolgen den Lösungsweg mit Funktionszeigern. Klappt
jetzt noch nicht (Vertue mich irgendwo mit Zeigern). Allerdings wäre die
Lösung mit "Ersetzen" z.B. TIM2_IRQHandler zu TIM3_IRQHandler besser.
So sieht der aktuelle Stand:
1
//Util.h
2
3
int(*pTIM2_IRQ)(void);
4
int(*pTIM3_IRQ)(void);
5
...//und so weiter
6
7
8
//Util.c
9
10
voidTIM2_IRQHandler(){
11
pTIM2_IRQ();
12
}
13
14
voidTIM3_IRQHandler(){
15
pTIM3_IRQ();
16
}
17
...//und so weiter
Die Signaturen der Konfigurationsmethoden werden folgendermaßen
erweitert:
Ich habe das auch mal versucht...nicht bei Timern aber bei den UARTS.
Habs dann aber wieder sein gelassen. Man muss ja teilweise auch noch
spezielle Pins zuweisen jenachdem welchen UART man z.B. benutzt.
Das Sinnvoll variabel zu gestalten hätte bei mir länger gedauert, als es
von Hand einmal einzutragen.
Das sind ja normalerweise auch Konfigurationen, die sich während des
Programmablaufs nicht ändern
Aber wenn du es hinbekommen hast, gerne posten :)
Der Fehler lag bei der Funktionssignatur der Konfigurationsmethoden von
Modulen. Statt normalen Zeiger muss man den Zeiger vom Funktionszeiger
übergeben. Nach Korrektur:
Ich habe es noch nicht auf µC getestet, da ich ihn nicht zur Hand habe.
Aber in einem mini C-Programm habe ich es nach programmiert.
@A. B. (funky)
In meinem Fall arbeite ich auf einer Platine, also die Pins sind immer
gleich. Über eine mir nicht bekannte Zeitspanne entstanden viele
Projekte für diese Platine. Daher wollte ich es modularisieren, um
leichter diese zu einem Projekt zu verbinden, und um gleichzeitig
spätere Projekte einfacher zu integrieren.
Ich wüßte auch keine schnelle Möglichkeit noch die Pins dynamisch zu
zuweisen.
- Minti
> Bei der statischer Variante müsste man für jedes Modul fast das dasselbe> Schreiben.
Klar. Es hält sich in Grenzen. Jede der beschriebenen Methoden hat ihre
Vor- und Nachteile.
> void Modul_A_config(TIM_TypeDef* TIMx, int (**fkt)(void), ...){> *fkt = DoCycleWork_A;> ... // rest> }
Ich sehe hier zwei Probleme. Zum ersten ist der "logische" Parameter
TIMx in zwei tatsächliche aufgeteilt. D. h. es besteht die Gefahr, dass
z. B. TIM1 und der Zeiger der ISR für TIM2 übergeben wird. Ich würde
hier die beiden in einer Klasse zusammenfassen.
Zum anderen: Wo findet die Initialisierung des Timers statt? Der
aufrufende Teil sollte dies nicht tun. Dann aber findet die
Initialisierung in jedem Modul statt, und dann hast Du das gleiche
Problem, nämlich "für jedes Modul fast das dasselbe" schreiben.
Bei mir ist die Timer-Konfiguration in einer separaten C++-Klasse. D. h.
die Module rufen Methoden dieser Klasse auf.
> Allerdings wäre die> Lösung mit "Ersetzen" z.B. TIM2_IRQHandler zu TIM3_IRQHandler besser.
Es gäbe noch eine weitere Methode, welche mittels "token pasting" den
Namen der ISR mittels Präprozessor zusammenbaut. Das würde die
Tipparbeit im statischen Fall reduzieren:
> Ich wüßte auch keine schnelle Möglichkeit noch die Pins dynamisch zu> zuweisen.
Funktioniert analog. Geht über Zeiger oder aber ein Objekt wird an das
Modul übergeben, und das Modul ruft uart.init() auf. Oder mit "token
pasting" (im statischen Fall).
Ich würde für jeden Timer getrennte Vektoren und getrennte Handler
benutzen.
Das mag dann zwar ähnlicher Code sein, aber es müssen dann nicht ständig
Pointer berechnet und aufgelöst werden. D.h. das Programm ist in jedem
Falle schneller und der Code auch nicht nennenswert größer.
Peter
Roland H. schrieb:> Ich sehe hier zwei Probleme. Zum ersten ist der "logische" Parameter> TIMx in zwei tatsächliche aufgeteilt. D. h. es besteht die Gefahr, dass> z. B. TIM1 und der Zeiger der ISR für TIM2 übergeben wird. Ich würde> hier die beiden in einer Klasse zusammenfassen.
ja, danke für den Hinweis. Ich werde dann zu einem Struct
zusammenführen, da ich in C programmiere.
Roland H. schrieb:> Zum anderen: Wo findet die Initialisierung des Timers statt? Der> aufrufende Teil sollte dies nicht tun. Dann aber findet die> Initialisierung in jedem Modul statt, und dann hast Du das gleiche> Problem, nämlich "für jedes Modul fast das dasselbe" schreiben.>> Bei mir ist die Timer-Konfiguration in einer separaten C++-Klasse. D. h.> die Module rufen Methoden dieser Klasse auf.
ja, stimmt. Ich werde dann eine Funktion dafür schreiben.
Roland H. schrieb:> Es gäbe noch eine weitere Methode, welche mittels "token pasting" den> Namen der ISR mittels Präprozessor zusammenbaut.
Danke, wieder was Neues gelernt.
Peter Dannegger schrieb:> Ich würde für jeden Timer getrennte Vektoren und getrennte Handler> benutzen.
Leider habe ich dich nicht verstanden. Was meinst du mit Vektoren?
> ja, danke für den Hinweis. Ich werde dann zu einem Struct> zusammenführen, da ich in C programmiere.
[...]
> ja, stimmt. Ich werde dann eine Funktion dafür schreiben.
struct + function = class ;-)