Forum: Mikrocontroller und Digitale Elektronik generierten Code in STM32CubeIDE modularisieren


von BennyV (Gast)


Lesenswert?

Embedded-C-Anfänger-Problem: Habe mein erstes lauffähiges Programm für 
den STM32, ein PWM mit DMA, womit ich einen WS2812 Led-Stripe ansteuere. 
Jetzt würde ich gerne ein wenig Refactoring machen und die Logik für die 
Led-Ansteuerung in ein eigenes Modul (leds.c) auslagern.

Mein Problem ist nun, dass das Timerdefinition struct von der IDE in das 
main-File "hineingeneriert" wurde und ich in meiner leds.c keinen 
direkten Zugriff darauf habe. Da, wo ich meine Funktion aus main.c 
heraus aufrufe, könnte ich das als pointer hineinreichen.

Aber irgendwie hab ich dann noch das Problem, dass ich eine 
HAL_TIM_PWM_PulseFinishedCallback handler definiere, der den DMA stoppt 
(HAL_TIM_PWM_Stop_DMA). Dieser muss den Timer kennen und den channel. 
Ich bin mir auch gar nicht sicher, ob ich so einen Callback überhaupt 
außerhalb meiner main.c definieren sollte. Kann ich mehr als eine 
einzige Implementierung davon in meinem Programm haben? Was, wenn ich 
den für einen anderen Timer auch verwenden will, der gar nichts mit 
meiner Led-Funktion zu tun hat?

Oder sollte ich vielleicht einfach das hadrwarenahe Timerzeug in meiner 
main.c lassen und nur die Funktionen auslagern, die Daten aufbereiten, 
die letztlich an die Led-Controller geschickt werden? Wäre immerhin eine 
Art von Separation-of-Concern.

Mir fällt es leider schwer, meine Frage(n) sauber auf den Punkt zu 
bringen.
Also irgendwie: Wie geht man vor mit generiertem HAL code und Callbacks, 
Interrupts etc. und Zustand, wenn ich Code modular halten will und 
globalen State vermeiden möchte? Habt ihr ein Paar best-practice-Tipps? 
Danke.

von Ein Tipp (Gast)


Lesenswert?

Nach meinen Erfahrungen gibt es 2 Arten von Programmieren.

Die einen kriegen es raus, nachdem ihnen 2 mal das System im Chaos 
versunken ist. Die anderen bekommen niemals den Bogen raus.

So Richtlinien bringen wenig - das eigentliche Problem sind 
Erweiterungen für unvorhersehbare Anforderungen.

Du weißt halt nicht, ob du später mal diesen Timer vür mehrere Aufgaben 
einsetzen musst. Ganz egal, wie du es anlegst, später kommen 
Anforderungen, die nicht zu deiner Struktur passen. Du musst ein Gefühl 
dafür entwickeln, wann wegverfen und neu strukturieren einfacher wird 
als dran frickeln.

von BennyV (Gast)


Lesenswert?

Ein Tipp schrieb:

> So Richtlinien bringen wenig - das eigentliche Problem sind
> Erweiterungen für unvorhersehbare Anforderungen.
>

Von Richtlinien würde ich auch gar nicht reden, eher von Design 
Patterns. In objektorientierten oder in funktionalen Sprachen habe ich 
die Erfahrung gemacht, dass bestimmte Pattern, richtig angewendet eben 
genau die Resilienz für unverhersehbare Anforderungen erhöhen. Aber im 
Embedded-Bereich und in C allgemein habe ich noch kein "Gefühl" dafür 
entwickelt und tue mich schwer damit, den hardwarenahen Teil vom "Rest" 
zu separieren. Und die Tatsache, dass meine IDE Code generiert, bringt 
noch mal eine spezielle Schwierigkeit mit, auf die ich noch keine gute 
Antwort habe.

von Jens R. (tmaniac)


Lesenswert?

Mach dir Gedanken wie du deine gesamte Architektur aufbauen willst.

Du hast auch die Möglichkeit die Generierung der Interruptfunktionen zu 
unterdrücken (Haken löschen in der IDE).

Deinen eigenen Code solltest du eh in eigene Dateien schreiben und dann 
in der Main oder in den Tasks aufrufen.

Ich habe mir komplettes ExpansionPack geschrieben, in dem ich die CAN 
Kommunikation inkl ein paar Transportprotokolle realisiert habe. Dieses 
ExpansionPack generiert auch meine Interruptroutinen.

Also möglich ist alles 😀

von Gerald M. (gerald_m17)


Lesenswert?

schreibe oben dein c-file einmal
1
 #include "main.h"
 rein

von J. S. (jojos)


Lesenswert?

Alles in main ist aber eher das Gegenteil von Modularisierung.
In den erweiterten Projekteinstellungen im Cube gibt es noch Optionen um 
Komponenten aufzuteilen, vielleicht hilft das.

von Harry L. (mysth)


Lesenswert?

J. S. schrieb:
> Alles in main ist aber eher das Gegenteil von Modularisierung.
Aber, du kommst so an Alles heran.
mit
1
htimX->Instance.<REGNAME>
 kann man dann auch wieder auf die Registerstruktur zugreifen.

J. S. schrieb:
> In den erweiterten Projekteinstellungen im Cube gibt es noch Optionen um
> Komponenten aufzuteilen, vielleicht hilft das.

Das mach ich allerdings auch so - immer.

: Bearbeitet durch User
von Gerald M. (gerald_m17)


Lesenswert?

J. S. schrieb:
> Alles in main ist aber eher das Gegenteil von Modularisierung.

Das hat nichts mit dem modularisieren zu tun. Darin stehen die ganzen 
Definitionen welche CubeMX erzeugt. Ohne kannst niemals auf einen Timer 
zugreifen.

Die eigene Modularisierung macht man, indem man die Funktionen für 
Callback usw. in eine Funktion packt und eben in der main.c im Callback 
diese aufrufst. Das kannst auch eigentlich nicht anders machen, da sich 
manche Interrupts die Funktion teilen, entsprechend muss es irgendwo 
eine Stelle geben die über alle Callbacks Bescheid weiß, und das ist in 
der main.c.
In der eigenen Datei definiere ich einfach immer etwas wie
1
#define mytim &htim6
 und nutze immer den. Das muss später natürlich entsprechend ausgefüllt 
werden. Aber die main.h muss wegen der extern deklarierten Komponenten 
eingebunden werden.

von BennyV (Gast)


Angehängte Dateien:

Lesenswert?

Hab was gefunden, was mich vielleicht weiterbringt. Im Projectmanager 
der ioc-Perspektive gibt es eine Option um die Peripherieinitialisierung 
in einzelne h/c files aufzuteilen.

von BennyV (Gast)


Lesenswert?

J. S. schrieb:
> Alles in main ist aber eher das Gegenteil von Modularisierung.
> In den erweiterten Projekteinstellungen im Cube gibt es noch Optionen um
> Komponenten aufzuteilen, vielleicht hilft das.

Genau. Auch gerade selbst drauf gestoßen. Hätte mal refreshen sollen 
bevor ich es poste :)

von BennyV (Gast)


Lesenswert?

Gerald M. schrieb:

> Die eigene Modularisierung macht man, indem man die Funktionen für
> Callback usw. in eine Funktion packt und eben in der main.c im Callback
> diese aufrufst. Das kannst auch eigentlich nicht anders machen, da sich
> manche Interrupts die Funktion teilen, entsprechend muss es irgendwo
> eine Stelle geben die über alle Callbacks Bescheid weiß, und das ist in
> der main.c.

Also die main.c stellt so eine Art Eventbus dar, der der Reihe nach alle 
meine Module aufruft, die an einem bestimmten Hardware-Callback 
interessiert sind? Macht irgendwie Sinn, auch wenn ich mich noch nicht 
so ganz mit der Idee angefreundet habe.

von BennyV (Gast)


Lesenswert?

um meinen Fall nochmal konkreter zu machen. Meine Funktion Display_send 
schickt die Daten für die WS2812 auf PWM-Reise und blockiert bis sie ein 
Flag über den Callback bekommt, wobei auch das PWM wieder gestoppt wird.
1
volatile uint8_t datasent = 0;
2
3
void Display_Send(void) {
4
  ...
5
6
  HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_4, (uint32_t*) pwmData, indx);
7
  while (!datasent) {
8
  };
9
  datasent = 0;
10
}
11
12
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) {
13
  HAL_TIM_PWM_Stop_DMA(htim, TIM_CHANNEL_4);
14
  datasent = 1;
15
}

Wenn ich den callback in meiner main.c unterbringe und main.c dann 
meinem Modul sagt, dass das Paket versendet wurde, reiße ich diesen 
Zustandsautomaten irgendwie auseinander. Die Logik ist dann auf zwei 
source files verteilt. Das behagt mir nicht so richtig.

von Jens R. (tmaniac)


Lesenswert?

Du kannst die Callback Funktion doch in deinem Source Code umsetzen.
1
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) { 13 HAL_TIM_PWM_Stop_DMA(htim, TIM_CHANNEL_4); 14 datasent = 1; 15}

Diese Functionen sind in der HAL als weak deklariert. Wenn du die 
Generierung unterbindest, kannst du die "überall" hin schreiben und dann 
wird die ursprüngliche überschrieben

von J. S. (jojos)


Lesenswert?

Eine Komponente ist aber nur eine wenn sie frei ist von globalen 
Ressourcen oder (angenommenen) Singletons. Beim init muss eben alles 
mitgegeben werden damit diese unabhängig genutzt werden kann. Gilt auch 
für Interrupts und DMA, aber gerade beim beim DMA ist es bei STM32 oder 
auch Cortex—M mit den vielen Bussen schwierig.
Bei den ISR hilft die HAL ja schon mit der neuen Struktur in der die 
callbacks einer Ressource zugeordnet werden und der weak Murks nicht 
mehr nötig ist.

von BennyV (Gast)


Lesenswert?

Jens R. schrieb:
> Du kannst die Callback Funktion doch in deinem Source Code umsetzen.
>
1
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) { 13 
2
> HAL_TIM_PWM_Stop_DMA(htim, TIM_CHANNEL_4); 14 datasent = 1; 15}
>
> Diese Functionen sind in der HAL als weak deklariert. Wenn du die
> Generierung unterbindest, kannst du die "überall" hin schreiben und dann
> wird die ursprüngliche überschrieben

hab mich oben vielleicht missverständlich ausgedrückt bzw. mehrere 
Probleme vermischt. Die Callback-Funktion wird nicht generiert, die 
schreib ich selbst. Aber mein Verständnis war, dass ich 
HAL_TIM_PWM_PulseFinishedCallback nur einmal in meinem ganzen Programm 
nutzen kann. Aber ich kann mir durchaus vorstellen, dass ich noch 
weitere Funktionen habe, die den Callback brauchen, die mit der 
LED-Steuerung nichts zu tun haben (bei mir nicht der Fall gerade).

von Jens R. (tmaniac)


Lesenswert?

Na klar kann es die HAL_TIM_PWM_PulseFinishedCallback auch nur einmal 
geben.

Und sie sollte eben in einem Modul stehen welche sich mit dem TIM 
beschäftigt.
Ergo baut man sich ein "TIM-Steuer-Modul" welches dann auch Funktionen 
hat wo d deine PWM mit einfachen Parametern einschalten kannst.

Dann hat das Module entweder eine Task-Funktion die aktiv zurück meldet 
wenn etwas fertig ist, oder alternativ hat das Modul eine Funktion 
welche du ohne zu blockieren abfragen kannst wann die Arbeit erledigt 
ist.

von BennyV (Gast)


Lesenswert?

Jens R. schrieb:
> Na klar kann es die HAL_TIM_PWM_PulseFinishedCallback auch nur einmal
> geben.
>
> Und sie sollte eben in einem Modul stehen welche sich mit dem TIM
> beschäftigt.
> Ergo baut man sich ein "TIM-Steuer-Modul" welches dann auch Funktionen
> hat wo d deine PWM mit einfachen Parametern einschalten kannst.
>
> Dann hat das Module entweder eine Task-Funktion die aktiv zurück meldet
> wenn etwas fertig ist, oder alternativ hat das Modul eine Funktion
> welche du ohne zu blockieren abfragen kannst wann die Arbeit erledigt
> ist.

jetzt formt sich langsam eine Idee, wie ich's mache. Danke für den 
Schubser!

von Jens R. (tmaniac)


Lesenswert?

Du schreibst ja leider als Gast.
Wenn du mal anschreibst, kann ich dir mal mein Projekt als 
Gedankenanstoß zu kommen lassen. Das werde ich zwar auch mal öffentlich 
stellen, doch bisher ist es noch zu unordentlich :-D

von J. S. (jojos)


Lesenswert?


: Bearbeitet durch User
von Jens R. (tmaniac)


Lesenswert?

J. S. schrieb:
> Nein, den Murks braucht man nicht mehr.

Na das ist doch mal ein sehr hilfreicher Beitrag.
Dir ist aber schon klar, wenn man die Interruptroutinen selber einhängen 
möchte, dass man dann die gesamte HAL raus schmeißen muss 🙄
Und wenn jemand zu beginn mit der HAL und all den Vorzügen einer 
automisch generierten Basissoftware arbeiten möchte, dann kommt man 
nunmal bei ST (wie bei vielen anderen auch) nicht um die HAL des 
Herstellers herum.

> 
https://github.com/STMicroelectronics/STM32CubeF4/blob/52757b5e33259a088509a777a9e3a5b971194c7d/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c#L3829
>
> 
https://github.com/STMicroelectronics/STM32CubeF4/blob/52757b5e33259a088509a777a9e3a5b971194c7d/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c#L3840

Und warum verlinkst du zwei mal die gleiche Datei? Die 10 Zeilen könnte 
man auch scrollen 🤔

: Bearbeitet durch User
von J. S. (jojos)


Lesenswert?

Jens R. schrieb:
> Dir ist aber schon klar, wenn man die Interruptroutinen selber einhängen
> möchte, dass man dann die gesamte HAL raus schmeißen muss 🙄

vielleicht erstmal nen Kaffee trinken und dann genauer hingucken?
Man braucht den Murks mit den **weak** Funktionen nicht mehr. Und ISR 
ist nicht Callback. Die ISR lässt man wie sie ist, und verwendet die 
neueren Callbacks die mit einer Funktion registriert werden und nicht 
mehr die veralteten weak Funktionen. In anderen Codeteilen der HAL sind 
diese Funktionen schon als 'legacy' kommentiert.

Jens R. schrieb:
> Und warum verlinkst du zwei mal die gleiche Datei?

um zu zeigen das es die cb für die verwendeten Funktionen gibt. Tja, 
zweimal gezeigt und immer noch nicht verstanden worum es geht.

von Jens R. (tmaniac)


Lesenswert?

Und warum schreibt man dann nicht

Kein Zitat:
> verwende lieber die Funktionspointeraufrufe anstatt der weak Funktionen 🙄

Wobei es ja an der Grundaufgabe hier nichts ändert.
Der Fragensteller möchte die Callback Funktion (welche ja über die ISR 
aufgerufen wird) an eine Stelle schreiben welche in seinem Kopf einen 
Sinn ergibt. Und dabei ist es unerheblich wie diese aufgerufen (Pointer 
vs weak) wird.

von J. S. (jojos)


Lesenswert?

Jens R. schrieb:
> Und warum schreibt man dann nicht

warum liest man das nicht?

J. S. schrieb:
> Bei den ISR hilft die HAL ja schon mit der neuen Struktur in der die
> callbacks einer Ressource zugeordnet werden und der weak Murks nicht
> mehr nötig ist.

Jens R. schrieb:
> Und dabei ist es unerheblich wie diese aufgerufen (Pointer
> vs weak) wird.

es ist für die Frage der Modularisierung ein riesiger Unterschied. Hat 
STM erkannt und nachgebessert.
Die weak Funktion ist eine globale für alle Timer und z.B. genauso ist 
es bei uart und anderen Stellen wo die ISR Callbacks nutzen. Da muss 
also im low level Callback der Applikationscode aufgerufen werden, und 
damit steht die Pyramide auf dem Kopf. Ich möchte für Modularisierung 
einen low level Treiber haben den ich in die App einbinde und nicht 
umgekehrt.

von Jens R. (tmaniac)


Lesenswert?

J. S. schrieb:
> Jens R. schrieb:
>> Und warum schreibt man dann nicht
>
> warum liest man das nicht?

Wenn man nicht schreibt um was es geht, bleibt nicht viel zum lesen 🙄

> Nein, den Murks braucht man nicht mehr.

Klingt nunmal wie die ganze Hetze gegen die HAL und nicht wie ein Aufruf 
die veralteten weak gegen moderne Funktionspointer einzutauschen.

> J. S. schrieb:
>> Bei den ISR hilft die HAL ja schon mit der neuen Struktur in der die
>> callbacks einer Ressource zugeordnet werden und der weak Murks nicht
>> mehr nötig ist.
>
> Jens R. schrieb:
>> Und dabei ist es unerheblich wie diese aufgerufen (Pointer
>> vs weak) wird.
>
> es ist für die Frage der Modularisierung ein riesiger Unterschied. Hat
> STM erkannt und nachgebessert.
> Die weak Funktion ist eine globale für alle Timer und z.B. genauso ist
> es bei uart und anderen Stellen wo die ISR Callbacks nutzen. Da muss
> also im low level Callback der Applikationscode aufgerufen werden, und
> damit steht die Pyramide auf dem Kopf. Ich möchte für Modularisierung
> einen low level Treiber haben den ich in die App einbinde und nicht
> umgekehrt.

Verzeih mir, aber vom Aufruf ist hier doch null Unterschied 🤔
1
{
2
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1) 
3
  htim->OC_DelayElapsedCallback(htim); 
4
  htim->PWM_PulseFinishedCallback(htim);
5
#else 
6
  HAL_TIM_OC_DelayElapsedCallback(htim); 
7
  HAL_TIM_PWM_PulseFinishedCallback(htim);
8
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */ }

Es wird doch immer eine Funktion aufgerufen welche im ISR Kontext läuft.
Dabei ist doch Schnuppe ob diese weak (und damit per Linker) oder per 
Pointer eingehängt wird.

Also wo ist hier der Vorteil im Programmablauf 😕

von J. S. (jojos)


Lesenswert?

Jens R. schrieb:
> Also wo ist hier der Vorteil im Programmablauf

der Vorteil ist in der Organisation.
Bei der weak Variante wird ein und dieselbe Funktion für alle Timer 
aufgerufen, obwohl die Timer ja für unterschiedliche Zwecke eingesetzt 
werden. In die Funktion müssen jetzt also Abfragen rein für welchen 
Timer der CB gerufen wurde und ob ich jetzt in die Komponente Dingdong 
oder Blinkblink springen muss. Und in diesen Code muss ich jetzt 
includes für Dingdong und Blinkblink einbauen. -> Treiber braucht 
Appcode per include

Bei der anderen Variante habe ich meine Komponenten und sage denen 
welchen Timer sie benutzen sollen. Dann kann die Komponente den Callback 
zum Timer registrieren. -> App benutzt Treiber als include

Ich bin kein HAL Verweigerer, nur weak sollte man in shit umbenennen um 
es deutlicher zu machen.

Noch variabler wird es wenn man das Feature der Cortex-M nutzt die 
Vektortabelle ins RAM zu verlagern. Dann können die Komponenten die eine 
ISR brauchen diese auch zur Laufzeit in der Initialiserung setzen. Aber 
das unterstützt HAL nicht, ist interessanter wenn man Komponenten als 
Objekt Instanzen zusammensetzt.

: Bearbeitet durch User
von Jens R. (tmaniac)


Lesenswert?

J. S. schrieb:
> Jens R. schrieb:
>> Also wo ist hier der Vorteil im Programmablauf
>
> Bei der anderen Variante habe ich meine Komponenten und sage denen
> welchen Timer sie benutzen sollen. Dann kann die Komponente den Callback
> zum Timer registrieren. -> App benutzt Treiber als include

Ah jetzt hat es bei mir klick gemacht 🙈
Da ich bisher im CAN Bereich unterwegs war, ist mir noch garnicht 
aufgefallen dass verschiedene Callbacks in der gleichen Funktion geendet 
haben 😯
Wobei beider USART ist das glaube auch so 🤔 Naja da war ich nur mal kurz 
zu Besuch 🤐

> Ich bin kein HAL Verweigerer, nur weak sollte man in shit umbenennen um
> es deutlicher zu machen.

Wie gesagt, dann wäre es weniger missverständlich, wenn man dann nicht 
gleich mit "Motzsprüchen" daher kommt 🤷‍♂️

> Noch variabler wird es wenn man das Feature der Cortex-M nutzt die
> Vektortabelle ins RAM zu verlagern. Dann können die Komponenten die eine
> ISR brauchen diese auch zur Laufzeit in der Initialiserung setzen. Aber
> das unterstützt HAL nicht, ist interessanter wenn man Komponenten als
> Objekt Instanzen zusammensetzt

Ja dass STM die ISR immer durch eine Interpreterfunktion durch drückt, 
macht die ganze ISR Vektortabelle zu nichte 😕
Wie man das trotz HAL schön einbinden kann, ist aber etwas das man 
später lösen kann 🙂

PS: ich habe mir erlaubt dir mal ne PN zu schicken 🙂

von W.S. (Gast)


Lesenswert?

BennyV schrieb:
> Embedded-C-Anfänger-Problem: Habe mein erstes lauffähiges Programm für
> den STM32, ein PWM mit DMA,...

Ach nö, das ist nur ein Problem derjenigen Leute, die als Allererstes 
sich solche Programme wie Cube usw. installieren und dann meinen, sie 
hätten damit das Programmieren gelernt.

Mach es anders. Ganz anders. Konzipiere deine Firmware zuerst, und zwar 
so, daß du möglichst sauber zwischen den niederen Dingen wie 
Schnittstellenansteuerungen und den höheren Dingen wie Algorithmen 
trennst und dir sinnvolle Schnittstellen dazwischen ausdenkst. Und in 
main gehört zwar ein Aufruf einer Initialisier-Funktion für so ein 
peripheres Ding hinein, aber das ist eher sowas:
void MeinKarlheinz_Init(void);
und es gehören in main keine Typdefinitionen hinein, die evtl. woanders 
auch gebraucht werden. Wenn du also so einen Flimmerstreifen mit WS2812 
ansteuern willst, dann kommt der komplette Treiber dafür in eine 
separate Quelle und von der werden nur die Funktionen exportiert, die 
zum Einstellen aufgerufen werden sollen. Also sowas wie 'an', 'aus', 
Farbe wählen usw.

Anfänger machen fast immer den Fehler, drauflos zu tippen und sich dabei 
dabei die tollsten Dinge zu denken, wobei zumeist die sinnvolle Struktur 
vergessen wird und sich ärgerliche Fehlfunktionen einstellen. Dann wird 
nach dem Debugger gerufen, weil man meint, daß so ein Programm die 
verkorkste Struktur der Firmware richten kann. Bei Leuten, die sich am 
Anfang mit den STM32 befassen, kommt hinzu, daß die Hilfsprogramme von 
ST darauf ausgelegt sind, die Benutzer zum einen von der Hardware 
fernzuhalten und zum anderen an ST zu binden. Man kann sich drauf 
einlassen, aber dann muß man auch die Konsequenzen tragen.

W.S.

von W.S. (Gast)


Lesenswert?

J. S. schrieb:
> Ich bin kein HAL Verweigerer, nur weak sollte man in shit umbenennen um
> es deutlicher zu machen.

Ich denke, das Einführen von 'weak' ist ein typisches Verhalten von 
C-Programmierern. Eigentlich wurde es nur zum Organisieren der 
Vektortabelle bei den Cortexen erfunden und auch nur innerhalb der 
Assemblerquelle für den Startup-Code. Man hätte es dabei bewenden lassen 
können und 'weak' hätte nie in C Eingang gefunden. Aber man ist als 
C-Programmierer ja so grandios, selbst den Startup-Code in C formulieren 
zu wollen, auch wenn das nur unter allergrößten Verrenkungen geht. Und 
anschließend wird gegrübelt, wie man so ein schönes neues Wort auch 
anderweitig anbringen kann, um damit die Programmiersprache noch mehr zu 
verhunzen/verkomplizieren.

W.S.

von BennyV (Gast)


Lesenswert?

W.S. schrieb:
> BennyV schrieb:
>> Embedded-C-Anfänger-Problem: Habe mein erstes lauffähiges Programm für
>> den STM32, ein PWM mit DMA,...
>
> Ach nö, das ist nur ein Problem derjenigen Leute, die als Allererstes
> sich solche Programme wie Cube usw. installieren und dann meinen, sie
> hätten damit das Programmieren gelernt.

schade. Dachte, dass ich das von mir meine, wollte ich gar nicht zum 
Ausdruck bringen.

> Mach es anders. Ganz anders.

Das klingt schon mal überzeugend.

> Anfänger machen fast immer den Fehler, drauflos zu tippen und sich dabei
> dabei die tollsten Dinge zu denken, wobei zumeist die sinnvolle Struktur
> vergessen wird und sich ärgerliche Fehlfunktionen einstellen.

aber genau darüber ist sich der Anfänger doch im Klaren und die 
sinnvolle Struktur zu finden ist doch des Anfängers Frage hier!

Wie bist du eigentlich als Anfänger vorgegangen? Die Antwort erspart mir 
bestimmt viel Ärger.

von Brränko (Gast)


Lesenswert?

Hi.

Die gleiche Frage habe ich mir auch schon öfter gestellt. Das beste was 
ich bis jetzt finden konnte ist die Handler, z.B. htim in die module zu 
ubergen - läuft aber dann nur auf STMs.

Gruß

von W.S. (Gast)


Lesenswert?

BennyV schrieb:
> Wie bist du eigentlich als Anfänger vorgegangen?
> Die Antwort erspart mir bestimmt viel Ärger.

Wohl eher nicht. Ich hatte mit mittlerer 
Datentechnik/Prozeßrechentechnik angefangen. Also mit RTOS und in 
Assembler und nicht mit Mikrocontrollern.

Nun, ich hab hier ja seit langem den Mikrocontroller-Anfängern 
gepredigt, daß man in seiner Firmware die Ebenen trennen soll, also 
Low-Level-Angelegenheiten wie z.B. Treiber für die Peripherie auf dem 
Chip oder Ansteuerungen für Hardware außerhalb des Controllers wie z.B. 
Steuersignale trennen von den Algorithmen und anderen High-Level-Dingen.

Aber die Leute wie du sind ja so unendlich schlau, daß sie Ratschläge 
dieser Art nicht benötigen und stattdessen Programme wie eben Cube und 
Konsorten lieben, wo man sich sämtliche Lowlevel-Angelegenheiten in 
main() hereinholt und dann wird gefragt, wie man sowas hinterher 
modularisieren kann. Da kann ich nur noch antworten: indem man anders 
anfängt und auch anders weitermacht. Also wie geschrieben, ganz anders.

So, hilft dir das? Oder ärgerst du dich jetzt darüber, daß dir jemand 
sagt, daß du es anders machen mußt, wenn du es anders haben willst?

W.S.

von J. S. (jojos)


Lesenswert?

W.S. schrieb:
> Ich hatte mit mittlerer
> Datentechnik/Prozeßrechentechnik angefangen.

Und warum hast du aufgegeben dazuzulernen?

von Jens R. (tmaniac)


Lesenswert?

W.S. schrieb:
> Aber die Leute wie du sind ja so unendlich schlau, daß sie Ratschläge
> dieser Art nicht benötigen und stattdessen Programme wie eben Cube und
> Konsorten lieben, wo man sich sämtliche Lowlevel-Angelegenheiten in
> main() hereinholt und dann wird gefragt, wie man sowas hinterher
> modularisieren kann. Da kann ich nur noch antworten: indem man anders
> anfängt und auch anders weitermacht. Also wie geschrieben, ganz anders.

Naja, die main ist nunmal jene Funktion die bei den meißten 
Implementierungen als erste Funktion jenseits des Assembler angesprungen 
wird.
Also sollte die Initialisierung (so fern diese nicht in Assembler 
geschrieben ist) auch von der main aus ausgerufen werden.
Und genau das macht der von ST erzeugte Code. Dieser trennt sauber 
zwischen Hardware naher Programmierung und jenem Bereich den der 
Entwickler betreten darf.
Will man auf Hardware nahe Sachen zu greifen, ruft man die HAL 
Funktionen auf.
Das man eine Struktur und/oder einen Handle braucht um die jeweilige 
Hardware Komponente zu adressieren lässt sich in C nur bedingt 
vermeiden.
Es steht einem aber offen oberhalb der HAL (die ja Hardware Abstraction 
Layer heißt) noch eine System-unabhängige Interface Ebene zu 
Implementieren.

Also sorry, das Schimpfen gegen Cube und Co zeigt nur, dass du dich 
nicht in die Gedankenwelt eines anderen hein versetzen kannst und nur 
deine eigene Welt siehst.

Achja, wenn ein Anfänger nach Ratschlägen fragt, dann muss man ihn nicht 
anpöpeln, das er keine Ratschläge annehmen würde 😅

von Jefe (Gast)


Lesenswert?

Ein Tipp schrieb:
> vür

Knaller!

von W.S. (Gast)


Lesenswert?

Jens R. schrieb:
> Naja, die main ist nunmal jene Funktion die bei den meißten
> Implementierungen als erste Funktion jenseits des Assembler angesprungen
> wird.

Das löst nicht das Problem, daß man dort nicht alles hineinwerfen 
sollte. Auch wenn einem solche Programme per Kommentar
/* ab hier dein Zeugs reinschreiben */
nahelegen, wo man denn mit dem Schreiben anfangen sollte. Tja und wenn 
jemand erstmal angefangen hat, das als gegeben zu sehen um sich dann von 
einer Biblitheksfunktion zu anderen zu hangeln - in main versteht sich - 
dann ist der Wunsch nach anschließender funktionaler Aufteilung so 
ziemlich vergeblich, weil er das so ziemliche Abreißen alles bisher 
Aufgebautem bedeutet. Nichts ist separierbar und woanders 
wiederverwendbar. Von daher mein Rat, es komplett anders zu machen. 
Und ja, ich weiß, das so etwas erstmal mühsam ist, genau so wie der 
Umstieg von der Büchse mit Fertigessen aus dem Laden auf Selberkochen. 
Nun ja, die Produzenten von Büchsenessen wollen auch leben, genauso wie 
ST eben auch.

W.S.

von Jens R. (tmaniac)


Lesenswert?

W.S. schrieb:
> Jens R. schrieb:
>> Naja, die main ist nunmal jene Funktion die bei den meißten
>> Implementierungen als erste Funktion jenseits des Assembler angesprungen
>> wird.
>
> Das löst nicht das Problem, daß man dort nicht alles hineinwerfen
> sollte.
Ist aber im generierten Code das gleiche wie im selbst geschrieben und 
hat null Komma nix mit ST oder dem Cube-System zu tun.
Nur warum wird es dann von dir verteufelt?

> Auch wenn einem solche Programme per Kommentar
> /* ab hier dein Zeugs reinschreiben */
> nahelegen, wo man denn mit dem Schreiben anfangen sollte.

Ok. Wenn
1
 
2
/* User section begin */
3
4
/* User section end */
für dich bedeuten dass da alles rein muss und nicht evtl ein Aufruf oder 
Sprung in den eigenen Code, dann ist das doch dein Problem und nicht der 
von der Umgebung 🤔
Auch in deiner Architektur wirst du in der main irgendwo anders 
hinspringen. Mikrocontroller Code läuft nunmal immer in einer 
Endlosschleife. Es steht einen frei ob man in der vorgefertigten while 
Schleife einen eigenen Scheduler implementiert oder ob man sich eins von 
den konfigurierbaren OS auswählt. Und genau hier hat die Cube-Umgebung 
einen riesen Vorteil, nämlich in dem man ein OS nutzen kann, welches gut 
getestet ist (ja ich weiß man muss auch noch lernen es richtig zu 
konfigueren).

> Tja und wenn
> jemand erstmal angefangen hat, das als gegeben zu sehen um sich dann von
> einer Biblitheksfunktion zu anderen zu hangeln - in main versteht sich -
> dann ist der Wunsch nach anschließender funktionaler Aufteilung so
> ziemlich vergeblich, weil er das so ziemliche Abreißen alles bisher
> Aufgebautem bedeutet. Nichts ist separierbar und woanders
> wiederverwendbar. Von daher mein Rat, es komplett anders zu machen.
> Und ja, ich weiß, das so etwas erstmal mühsam ist, genau so wie der
> Umstieg von der Büchse mit Fertigessen aus dem Laden auf Selberkochen.
> Nun ja, die Produzenten von Büchsenessen wollen auch leben, genauso wie
> ST eben auch.

Jeder Programmierer hat mit einem "Hello World" Programm angefangen. Und 
hat dort erst einmal auch die main aufgebläht.
Erst wenn man ein Grundgefühl für Abläufe entwickelt hat, macht es auch 
Sinn sich mal eine kleine Architektur auszudenken. Und genau das vom TO 
beschriebene Problem ist meiner Meinung nach prima geeignet sich an 
einem Treiber für die NeoPixel-Streifen zu versuchen. Aber der TO wird 
in seinem ersten Anlauf wohl noch nicht dran denken, die 
Registerschreiberei (bzw die HAL Kapselung) entsprechend zu separieren. 
Das gehört zum Lernprozess und ist dank der Zwischenablage auch 
nachträglich machbar.

: Bearbeitet durch User
von W.S. (Gast)


Lesenswert?

Jens R. schrieb:
> Ist aber im generierten Code das gleiche wie im selbst geschrieben und
> hat null Komma nix mit ST oder dem Cube-System zu tun.
> Nur warum wird es dann von dir verteufelt?

Ich verteufele nichts. Wer nur Blinkprogramme schreiben und nur bei 
STM32 bleiben will und das nur zum Hobbygebrauch, der mag das nach 
seinem Gusto tun. Aber dann jammern, wenn es ihm selber nicht mehr 
gefällt oder Probleme macht, gilt nicht. Der einzige Rat, der dann 
helfen würde, nämlich alles einzureißen und anders zu machen, wird dann 
als Affront empfunden, siehe dieser Thread hier.

Und deine Bemerkung, daß es "das gleiche wie im selbst geschrieben" sei, 
stimmt nur dann, wenn man es genauso tut wie Cube. Sonst nicht. Du hast 
da ganz offensichtlich einen recht speziellen Blickwinkel, der dich auch 
das Folgende schreiben und denken läßt:

Jens R. schrieb:
> Jeder Programmierer hat mit einem "Hello World" Programm angefangen.

Wirklich jeder? Nö.

W.S.

von J. S. (jojos)


Lesenswert?

W.S. schrieb:
> Aber die Leute wie du sind ja so unendlich schlau, daß sie Ratschläge
> dieser Art nicht benötigen und stattdessen Programme wie eben Cube und
> Konsorten lieben, wo man sich sämtliche Lowlevel-Angelegenheiten in
> main() hereinholt und dann wird gefragt, wie man sowas hinterher
> modularisieren kann. Da kann ich nur noch antworten: indem man anders
> anfängt und auch anders weitermacht. Also wie geschrieben, ganz anders.

aha. Im Cube Code stehen zu Anfang von main() Aufrufe von Funktionen die 
die verwendete Hardware initialisieren, was ist schlecht daran? Die 
meisten Funktionen sind hinten im main.c, was ist schlecht daran?
Und wenn man es nicht so mag, dann setzt man das Häkchen bei 'Generate 
perihperial initialization as pair of .c/.h files per peripheral'. Und 
schon sind die Funktionen aus main.c verschwunden und in eigenen 
Dateien. Sogar mit konsistenter Namensgebung und man muss nicht 
V24_init() in serial.h suchen. Auch kein Denglisch, oder soll das auch 
besser sein?
Im main.c direkt auf HW Register zugreifen mit Magic Numbers, ist das 
die vielzitierte Abstraktion von HW?
if (PWMMCR != 3)...
while (T0TC < 3000);...


W.S. schrieb:
> Ich verteufele nichts. Wer nur Blinkprogramme schreiben und nur bei
> STM32 bleiben will und das nur zum Hobbygebrauch, der mag das nach
> seinem Gusto tun.

CubeMX läuft nicht nur im Hobbybereich. Ich habe eine Kiste mit 17x H7 
vor mir und die Entwickler haben das nicht aus Spaß an der Freud gebaut.

: Bearbeitet durch User
von Jens R. (tmaniac)


Lesenswert?

W.S. schrieb:
> Jens R. schrieb:
>> Ist aber im generierten Code das gleiche wie im selbst geschrieben und
>> hat null Komma nix mit ST oder dem Cube-System zu tun.
>> Nur warum wird es dann von dir verteufelt?
>
> Ich verteufele nichts. Wer nur Blinkprogramme schreiben und nur bei
> STM32 bleiben will und das nur zum Hobbygebrauch, der mag das nach
> seinem Gusto tun.

Und warum muss ein NeoPixel-Treiber, den man in der Cube Umgebung 
geschrieben hat unbedingt nur für STM32 sein? Der lässt sich auch in der 
Umgebung ausreichen kapseln.

> Aber dann jammern, wenn es ihm selber nicht mehr
> gefällt oder Probleme macht, gilt nicht. Der einzige Rat, der dann
> helfen würde, nämlich alles einzureißen und anders zu machen, wird dann
> als Affront empfunden, siehe dieser Thread hier.

Der einziger der hier jammert bist du. Der TO fragt nach Auswegen. Und 
die gibt es. Dazu muss er aber lernen wie er die Hardware kapselt. Und 
das muss er lernen egal ob es in der Cube Umgebung oder ob es in seinem 
selbst gestrickten Flickenteppich ist.

> Und deine Bemerkung, daß es "das gleiche wie im selbst geschrieben" sei,
> stimmt nur dann, wenn man es genauso tut wie Cube. Sonst nicht.

Ach wie kommst du dann von der main in deinene abstrahierten Module? 🤔

> Du hast da ganz offensichtlich einen recht speziellen Blickwinkel, der dich auch
> das Folgende schreiben und denken läßt:
>
> Jens R. schrieb:
>> Jeder Programmierer hat mit einem "Hello World" Programm angefangen.

Och ja kann man tatsächlich als speziellen Blickwinkel nennen 🙂.
In dem Fall halt, das der Blickwinkel nicht in enge Mauern gepresst ist.
So gibt es eben verschiedene "Hello World" Programme. Auf einem PC fängt 
man mit einfachen Textausgaben an. Auf einem kleinen uC fängt man mit 
Pinwackeln an. Ich muss gestehen nicht mehr zu wissen, was wir auf dem 
KC als ersten Versuch gemacht haben 🙈 aber es war eben auch etwas in der 
Richtung "hallo hier bin ich"

> Wirklich jeder? Nö.

Also, doch, jeder. Oder war dein erstes Programm gleich mehrere Kilobyte 
groß?

von Harry L. (mysth)


Lesenswert?

Fiktive Auftragsverlauf:

Kunde:
Hallo Herr W.S., wir brauchen eine Firmware für eine Display-Einheit, 
die man per USB an unsere Wärnmepumpen anstöpseln kann. Die Hardware ist 
bereits fertig, und wir suchen jemand, der die erforderliche Firmware 
entwickelt.

Wäre das was für Sie?

W.S.:
Ja sicher, kein Problem!
Nur Anzeigen?
Und um wie viele Werte handelt es sich?

Kunde:
Ja, nur anzeigen, und es sind 6 Parameter, die angezeigt werden sollen.

W.S.
Klingt nach einem lösbaren Problem.
Wie schnell brauchen Sie das?

Kunde:
Wir hatten uns 6 Wochen als Zeitrahmen vorgestellt.

Nennen Sie bitte mal eine Größenordnung um den Kosten-Rahmen 
abzustecken!

W.S.:
<kurze Pause> ...also so um 10.000€ sollte das machbar sein.

***
Die Beiden werden sich handelseinig, und die Uhr startet bei t-6 
Wochen....
***

Woche 5

Kunde:
Hallo Herr S., wie schauts denn aus?

W.S.:
Alles gut, nur die USB-Hardware von dem Chip, den Sie nutzen macht 
Ärger, aber, ich bin dem Problem auf der Spur.

Nach Ablauf der 6 Woche:

Kunde:
Sind Sie fertig?

W.S.:
Leider nicht - der USB-Port ist wirklicher Mist - die Ingenieure von XXX 
sollte man teeren und federn - aber ich bekomm das in den Griff!
Geben Sie mir noch 1 Woche!

Nach 7 Wochen

Kunde:
Und? Lauft das Ding?

W.S.:
<freudig> Ja!
Ich hab das USB-Problem gelöst, aber jetzt zickt das Display.
Warum zum Teufel braucht man zur Anzeige von 6 Werten ein Grafik-Display 
mit 480x320 Pixel?
Ein 2x20 Text-Display wäre viel passender!

...to be continued.

Fazit: Mit ordentlicher Hersteller-Unterstützung (aka HAL) wär das nicht 
passiert :-D

: Bearbeitet durch User
von BennyV (Gast)


Lesenswert?

W.S. schrieb:

> So, hilft dir das? Oder ärgerst du dich jetzt darüber, daß dir jemand
> sagt, daß du es anders machen mußt, wenn du es anders haben willst?
>
> W.S.

Habe mich keinseswegs darüber geärgert, etwas neues aufgezeigt zu 
bekommen oder über mögliche grundsätzliche Fehler meines ersten Ansatzes 
aufgeklärt zu werden. Aber dein herablassender Ton kombiniert mit einer 
fragwürdigen Eindimensionalität der Antwort (so und nicht anders) muss 
man nicht unbedingt freundlich empfangen. Etwas im Bereich 
Wissenskommunikation hinzuzulernen würde deinem professionellen 
Erscheinungsbild nicht schaden. Du würdest dir auch selbst einen 
Gefallen tun, da Anfänger bei einer freundlicheren Ansprache weitaus 
aufnahmebereiter sein werden und du weniger Ärger über all diese 
"unbelehrbaren Idioten" in dich hineinfressen müsstest.

von Hört sich gut an (Gast)


Lesenswert?

J. S. schrieb:
> Bei der anderen Variante habe ich meine Komponenten und sage denen
> welchen Timer sie benutzen sollen. Dann kann die Komponente den Callback
> zum Timer registrieren. -> App benutzt Treiber als include

Gibt es da mal irgendein triviales Beispiel (z.B. Blinky mit Timer 
Output Compare Interrupt)? Trotz 20 Minuten Suche konnte ich nichts 
finden, wie man das dann praktisch umsetzt.

von Hört sich gut an (Gast)


Lesenswert?

Habe jetzt das hier gefunden, aber mit DMA: 
https://blog.softwareentwicklung-als-prozess.de/cpu-zeit-sparen-mit-direct-memory-access
Das macht es schon etwas verständlicher:
- Beim ersten Erscheinen von UART_DMA_TransferComplete handelt es sich 
um einen Prototyp?
- Beim zweiten Erscheinen wird der Callback registriert?
- Beim dritten Erscheinen wird der Callback (die Funktion) definiert?

von J. S. (jojos)


Lesenswert?

3x ja.
zu 1) siehe auch 'forward declaration'

von W.S. (Gast)


Lesenswert?

BennyV schrieb:
> Aber dein herablassender Ton kombiniert mit einer
> fragwürdigen Eindimensionalität der Antwort (so und nicht anders) muss
> man nicht unbedingt freundlich empfangen.

Du verwechselst gerade etwas: Ich bin nicht derjenige, welcher grad nach 
Hilfe gerufen hat.

Und wenn man etwas anders haben will, dann muß man es eben anders 
machen. Also nicht "so und nicht anders" sondern "wenn anders dann 
anders und nicht so wie gehabt".

Ich habe meinerseits den Eindruck, daß viele Anfragende meinen, dieses 
Forum sei sowas wie ein Cola-Automat: Oben ne Münze rein und es hat 
daraufhin eine Flasche Cola (bzw. eine dem Fragesteller gefallende 
Antwort) unten herauszukommen. So etwas empfinde ich als ausgesprochen 
anmaßend und dreist.

Also nochmal: Wenn man sowas wie Cube (oder dessen Gegenstücke bei 
anderen Herstellern) benutzen will, dann soll man auch gefälligst die 
Konsequenzen daraus akzeptieren - oder es eben anders machen, wie ich es 
vorgeschlagen habe.

W.S.

von J. S. (jojos)


Lesenswert?

W.S. schrieb:

> Du verwechselst gerade etwas: Ich bin nicht derjenige, welcher grad nach
> Hilfe gerufen hat.

Es hat auch keiner nach deinen verschobenen Ansichten gesucht.
Es lässt sich alles im Rahmen der Frage lösen. Wenn man es denn 
versteht.

von STK500-Besitzer (Gast)


Lesenswert?

J. S. schrieb:
> Es lässt sich alles im Rahmen der Frage lösen. Wenn man es denn
> versteht.

Das passt aber nicht in das Weltbild des W.S..

Übrigens sollte man bei "Cube" unterscheiden:
STM32CubeMX ist ein Wizard, um die Hardware eines STM32 (abstrakt) zu 
initialisieren.
STM32CubeIDE ist eine auf Eclipse basierende Entwicklungsumgebung, die 
STM von Attollic übernommen und auf STM-Controller beschnitten hat.
Leute wie W.S. verwenden vermutlich noch "vi" und Magic Numbers - selbst 
ein Makro wäre ja schon eine Abstrahierung, die nicht in deren Weltbild 
passt.

von STK500-Besitzer (Gast)


Lesenswert?

STK500-Besitzer schrieb:
> Attollic

Atollic TrueStudio

von Hört sich gut an (Gast)


Lesenswert?

Hört sich gut an schrieb:
> Habe jetzt das hier gefunden, aber mit DMA:
> 
https://blog.softwareentwicklung-als-prozess.de/cpu-zeit-sparen-mit-direct-memory-access
> Das macht es schon etwas verständlicher:
> - Beim ersten Erscheinen von UART_DMA_TransferComplete handelt es sich
> um einen Prototyp?
> - Beim zweiten Erscheinen wird der Callback registriert?
> - Beim dritten Erscheinen wird der Callback (die Funktion) definiert?

Hm, bin gerade wieder zu dem Problem zurückgekehrt und verstehe den 
Vorteil mit RegisterCallback schon. Aber hilft mir konkret irgendwie 
nicht weiter:
1
void TIM2_IRQHandler(void)
2
{
3
  /* USER CODE BEGIN TIM2_IRQn 0 */
4
5
  /* USER CODE END TIM2_IRQn 0 */
6
  HAL_TIM_IRQHandler(&htim2);
7
  /* USER CODE BEGIN TIM2_IRQn 1 */
8
9
  /* USER CODE END TIM2_IRQn 1 */
10
}
Das wird ja immer aufgerufen und in
HAL_TIM_IRQHandler(&htim2);
werden die Callbacks ausgewertet. Nur leider werden dort gleich zu 
Beginn auch die Interruptflags gelöscht. Wenn ich nun z.B. einen Output 
Compare Channel auswerten will, weiß ich in meinem Callback ja schon gar 
nicht mehr, welches Flag/Channel ausgelöst hat. Ich müßte also davor in 
der User Section die Interruptflags auslesen, um sie im Callback 
verarbeiten zu können. Ist nach meinem Verständnis irgendwie nicht 
modular und ich könnte gleich meine komplett eigene ISR in die erste 
User Section schreiben und noch vor HAL_TIM_IRQHandler(&htim2); mit 
return wieder raus, um diesen dann unnötigen Aufruf zu vermeiden.
Da mache ich doch garantiert wieder einen riesigen Gedankenfehler?

von J. S. (jojos)


Lesenswert?

Der auslösende Channel wird in htim->Channel gespeichert. Der Callback 
wird mit htim als Argument aufgerufen, also alles nötige vorhanden.
Das ist der Vorteil das alles in Strukturen gespeichert ist und damit 
z.B. auch die ganzen Init Werte noch bekannt sind.

Hört sich gut an schrieb:
> Ist nach meinem Verständnis irgendwie nicht
> modular und ich könnte gleich meine komplett eigene ISR in die erste
> User Section schreiben

da die ISR weak deklariert ist, kann man die auch komplett ersetzen. 
Oder zur Laufzeit durch Verschieben der Vectortable ins RAM setzen. Sehr 
praktisch wenn man mit (C++)Komponenten arbeitet.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Jens R. schrieb:
> Nur warum wird es dann von dir verteufelt?

W.S. verteufelt jeden Programmierstil, der von seinem eigenen abweicht.

von Hört sich gut an (Gast)


Lesenswert?

J. S. schrieb:
> Der auslösende Channel wird in htim->Channel gespeichert. Der Callback
> wird mit htim als Argument aufgerufen, also alles nötige vorhanden.
> Das ist der Vorteil das alles in Strukturen gespeichert ist und damit
> z.B. auch die ganzen Init Werte noch bekannt sind.
Argh!!! Ich Blindfisch! Das habe ich glatt überlesen. Jetzt hab ich's. 
Danke!

> Oder zur Laufzeit durch Verschieben der Vectortable ins RAM setzen. Sehr
> praktisch wenn man mit (C++)Komponenten arbeitet.
Da bräuchte ich noch einen kleinen Link, um auch das zu raffen. VTOR 
gibt es aber, glaube ich, erst ab M3. Aber das ist vermutlich nur 
Nebensache.

Stefan F. schrieb:
> W.S. verteufelt jeden Programmierstil, der von seinem eigenen abweicht.
Nun laßt doch bitte dieses Rumgetrete. Es bringt überhaupt nix und die 
Ansichten des einen oder anderen Users sind ja nun wirklich hinlänglich 
bekannt.

von J. S. (jojos)


Lesenswert?

Die Vectoren zu setzen ist Aufgabe der CMSIS, da gibt es 
NVIC_SetVector():
https://www.keil.com/pack/doc/CMSIS/Core/html/group__NVIC__gr.html#gab43c1c59d5c081f1bc725237f4b1f916

Und da gibt es auch einen weiterführenden Link wie das Kopieren ins RAM 
aussieht.

von Hört sich gut an (Gast)


Lesenswert?

Besten Dank, der Tag hat sich für mich wieder gelohnt :-)
Und auch noch rausgefunden, daß mein Liebling LPC845 als M0+ auch VTOR 
implementiert hat.

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.