Forum: Mikrocontroller und Digitale Elektronik lwIP, STM32H7, DMA, Cache und DHCP mag nicht


von J. S. (jojos)


Lesenswert?

Ich quäle mich mit den genannten Komponenten und versuche zu ergründen 
warum meine DHCP Discover Pakete nicht gesendet werden.
Es gibt einige Einstellungen für die MPU um die Cache Bereiche richtig 
zu konfigurieren. Sehr hilfreich ist ein Dokument in der ST Community, 
das hatte ich schonmal im Forum verlinkt, aber die ST Community ist 
wegen Umbau gerade down. Da habe ich auch schon einiges von Piranha 
gelesen, der den ST Code auch mächtig anprangert, sowohl Cube 
generierten als auch andere Beispiele.

Ich habe nun selber einiges probiert und ein eigenes low_level_output 
mit Cache Pflege implementiert:
1
static err_t custom_low_level_output(struct netif *netif, struct pbuf *p)
2
{
3
  uint32_t i = 0U;
4
  struct pbuf *q = NULL;
5
  err_t errval = ERR_OK;
6
  ETH_BufferTypeDef Txbuffer[ETH_TX_DESC_CNT] = {0};
7
8
  memset(Txbuffer, 0 , ETH_TX_DESC_CNT*sizeof(ETH_BufferTypeDef));
9
10
  for(q = p; q != NULL; q = q->next)
11
  {
12
    if(i >= ETH_TX_DESC_CNT)
13
      return ERR_IF;
14
15
    Txbuffer[i].buffer = q->payload;
16
    Txbuffer[i].len = q->len;
17
18
    SCB_CleanDCache_by_Addr(q->payload, q->len);   // diese Zeile dem org Code hinzugefügt
19
20
    if(i>0)
21
    {
22
      Txbuffer[i-1].next = &Txbuffer[i];
23
    }
24
25
    if(q->next == NULL)
26
    {
27
      Txbuffer[i].next = NULL;
28
    }
29
    i++;
30
  }
31
32
  TxConfig.Length = p->tot_len;
33
  TxConfig.TxBuffer = Txbuffer;
34
  TxConfig.pData = p;
35
36
  HAL_ETH_Transmit(&heth, &TxConfig, ETH_DMA_TRANSMIT_TIMEOUT);
37
38
  return errval;
39
}

tut es aber nicht weil q->payload nicht 32 Byte aligned ist, es wird 
etwas gesendet, aber kein gültiges dhcp.
Aber hurra, es gibt in lwipopts.h die Einstellung für das Alignment in 
den pbuf und das wirkt auch wenn ich die auf 32 setze.
Jetzt wird die payload für dhcp zusammengebaut, der p->payload ist 32 
Byte aligned. Dann wird UDP header hinzugefügt und der p->payload um die 
headersize von 8 Byte zurückgesetzt. Und puff ist mein 32 Byte alignment 
kaputt. Das ist doch Mist, warum ist das so? Den lwip Stack möchte ich 
nicht verändern.

Als Workaround kann ich den lwip heap non-cacheable machen, aber das ist 
ja eher Holzhammermethode.
Wobei ich auch noch nicht weiß ob mein Cache cleaning so gut ist wenn 
die Daten aus einem non-cacheable RAM oder ROM kommen.

Hartes Thema für so einen sonnigen Freitag Nachmittag, aber vielleicht 
kennt sich ja jemand mit lwIP besser aus.

: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Du kannst einfach einen größeren Cache-Bereich (also aufgerundet auf 
32Bytes = Cache-LineSize) "cleanen", das schadet nicht, ist nur 
(minimal) ineffizient.
1
const size_t cacheLineSize = 32;
2
// Anfangsadresse auf cacheLineSize aligned
3
uintptr_t beginAddrRounded = ((uintptr_t) q->payload) & ~((uintptr_t) (cacheLineSize - 1));
4
// Originale Endadresse
5
uintptr_t endAddr = ((uintptr_t) q->payload) + q->len;
6
7
// Endadresse aufgerundet
8
uintptr_t endAddrRounded = ((endAddr % cacheLineSize) == 0) ? endAddr : endAddr + (cacheLineSize - (endAddr % cacheLineSize ));
9
10
SCB_CleanDCache_by_Addr((void*) beginAddr, (size_t) (endAddrRounded-beginAddrRounded));

J. S. schrieb:
> Als Workaround kann ich den lwip heap non-cacheable machen, aber das ist
> ja eher Holzhammermethode.

Ja und super langsam.

J. S. schrieb:
> Wobei ich auch noch nicht weiß ob mein Cache cleaning so gut ist wenn
> die Daten aus einem non-cacheable RAM oder ROM kommen.

Das macht nichts, ist dann ein NOP. Aber wie gesagt, langsam.

Beim Empfang das Cache "Invalidate" nicht vergessen :) Da ist das 
Alignment aber schon eher ein Problem: Wenn man damit einen benachbarten 
Speicherbereich invalidated der zuvor lokal beschrieben wurde, hat man 
diese Änderungen gelöscht...

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


Lesenswert?

danke für den Input, versuche ich zu verarbeiten.
Sitze aber schon wieder zulange daran und habe mir Phantom Fehler 
gebaut...
Irgendwann das ethernetif von einer anderen HW rüberkopiert und die 
hatte eine geringfügig aber wichtige andere Belegung des Phy, arghh, und 
das obwohl ich schon Cube und VSC für git parallel auf zwei großen 
Monitoren habe.
Damit waren viele Versuche durch diesen Fehler fehlgeschlagen. Der 
Low_level_output reicht den Fehler von HAL_ETH_Transmit nicht weiter und 
wenn, würde er nicht angezeigt.

Schreibe ich mit dem cache clean nicht auch etwas kaputt? Da muss doch 
der cache Inhalt ins RAM bevor gesendet wird?
Jetzt bin ich quasi zurück auf Los.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

J. S. schrieb:
> Schreibe ich mit dem cache clean nicht auch etwas kaputt? Da muss doch
> der cache Inhalt ins RAM bevor gesendet wird?

Nur wenn eine andere CPU oder ein DMA zuvor etwas in benachbarte 
Bereiche geschrieben hat, was man noch lesen möchte. Das ist wohl etwas 
weniger wahrscheinlich. Aber ja, besser wäre es wohl den Bereich "exakt" 
auf 32 Bytes aligned zu bekommen.

von J. S. (jojos)


Lesenswert?

hmm, warum nicht die gleiche CPU? Wenn der Bereich cachable ist, dann 
wird doch solange der cache auch benutzt?

In einer Richtung musste doch sogar vor und nach Senden oder Empfangen 
invalidate/clear gemacht werden, so hatte ich es in einem Beitrag von 
Piranha gelsesen.

Niklas G. schrieb:
> Aber ja, besser wäre es wohl den Bereich "exakt"
> auf 32 Bytes aligned zu bekommen.

das MEM_ALIGNMENT in den lwipopts wird auch berücksichtigt, nur wie 
geschrieben durch das Header vor dem payload anfügen ist es an der 
Stelle wieder krumm. Aber lwip müsste für diesen Bereich ja auch 32 Byte 
Platz lassen. Gucke ich mir an, dann könnte man um diesen Bereich auch 
den Cachepointer rückwärts korrigieren, so wie du es sowieso 
vorgeschlagen hast.

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


Angehängte Dateien:

Lesenswert?

ok, das funktioniert, habe die Codezeilen von Niklas übernommen. Der 
grobe Fehler war allerdings die falsche übernommene Hardwarekonfig.

Jetzt wundern mich noch die Ping Antwortzeiten: im Bild ist oben die .70 
die immer über 1 ms braucht. Das ist ein Nucleo mit Testsoftware. 
MX_LWIP_Process() wird in while(1) aufgerufen, ohne Wartezeit oder 
andere Aktionen. Der Unterschied zum anderen Projekt mit der .65 ist das 
dort noch der ETH_IRQ aktiviert ist. Aber auch da wird MX_LWIP_Process() 
zyklisch aufgerufen wo doch die Bearbeitung stattfindet? Den ETH IRQ 
Handler habe ich noch nicht gefunden, macht der die Ping Antwort evtl. 
schneller?

Edit:
ok, der Release Build macht es auch schneller, Debug Build ist hier 
deutlich langsamer. Trotzdem interessant ob der ETH_IRQ ausgewertet 
wird, vermutlich nicht.

Noch ein Edit:
der generierte ethernetif.c Code ruft HAL_ETH_Start() auf, für IRQ 
Betrieb müsste es HAL_ETH_Start_IT() sein. Aber dann laufen die 
Callbacks im Interruptkontext was schwieriger zu behandeln ist.

Und im Debug Build gebe ich lwip debug messages über einen UART aus, das 
kostet natürlich auch Zeit. Das Puzzle wird vollständiger.

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


Angehängte Dateien:

Lesenswert?

jetzt bin ich wieder am ursprünglichen Problem: wenn zwei Controller 
gleichzeitig gestartet werden, dann wird in etwa 1 von 20 Fällen kein 
dhcp Discover gesendet.
Ich habe den Fehlerfall und dann den Debugger angehangen. Ich sehe das 
alles normal läuft, dhcp_start wird zyklisch wiederholt solange keine IP 
Addresse empfangen wurde. Es läuft bis zum HAL_ETH_Transmit. Das meldet 
HAL_OK nach Ausführung, es wird aber kein Paket gesendet. Im DMACSR 
steht 0x404, da ist das TBU Bit gesetzt:
1
Bit 2 TBU: Transmit Buffer Unavailable
2
This bit indicates that the application owns the next descriptor in the Transmit list, and the
3
DMA cannot acquire it. Transmission is suspended. The TPS0 field of the
4
DMA_Debug_Status0 register explains the Transmit Process state transitions.
5
To resume processing the Transmit descriptors, the application should do the following:
6
1. Change the ownership of the descriptor by setting Bit 31 of TDES3.
7
2. Issue a Transmit Poll Demand command.
8
For ring mode, the application should advance the Transmit Descriptor Tail Pointer register of
9
a channel.

Da ist also die TxDescriptor Liste voll und ETH DMA scheint nicht 
gestartet zu werden. Was kann da faul sein?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

J. S. schrieb:
> jetzt bin ich wieder am ursprünglichen Problem: wenn zwei Controller
> gleichzeitig gestartet werden, dann wird in etwa 1 von 20 Fällen kein
> dhcp Discover gesendet.

Klingt als würden die Pakete kollidieren, CSMA greifen und dann 
eventuell ein Timeout auftreten?

von J. S. (jojos)


Lesenswert?

Kollisionen sind es sicher nicht. DHCP hat ein retry mit dynamischem 
timeout,  es wird erneut ein Discover gesendet nach 2, 4, 8, 16, 32 s 
und danach alle 60 s. Da müsste irgendwann ein Versuch erfolgreich sein.

Das TBU Bit war auch nicht das Problem. Ich hatte noch einen Zähler 
eingebaut, und im erfolgreichen Fall ist das nach dem Senden auch 
gesetzt. D.h. das Ethernet Paket wurde schon per DMA gesendet und der 
DMA hat das schon wieder freigegeben.

Meine Vermutung ist jetzt das es noch an der cache maintenance liegt: 
beim Senden muss nur vorher ein clean ausgeführt werden, beim Empfangen 
nach dem Empfang ein invalidate, aber auch da ist vorher ein clean 
nötig. Das habe ich noch nicht drin und werde es nachher mal testen. Die 
Sendedaten könnten so falsch sein das der Switch die verwirft oder ich 
die nicht hinter dem Switch sehe (im Fehlerfall, im ok Fall gehen die 
Daten an den Rechner auf dem der DHCP Server und Wireshark laufen).

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


Lesenswert?

ich bin etwas weiter, der Cache war nicht mehr das Problem.

Wenn ich das Nucleo-H743ZI2 mit externen 5 V versorge, dann bekomme ich 
beim Einschalten auch oft den Fehler das Ethernet nicht reagiert. In dem 
Fehlerfall meldet LAN8742_init() einen Fehler -2, Reset Timeout. Dieser 
wird aber in der Cube generierten SW nicht abgefragt:
https://github.com/JojoS62/Test-H743ZI2-lwip/blob/79bd3eb6f7d82179384d7a70a9411745cac3ca20/LWIP/Target/ethernetif.c#L252
Das führt dazu das ETH nicht richtig konfiguriert wird, das Auslesen des 
Linkstatus liefert -1 und dann läuft Ethernet mit 10 Mbit / half duplex.
Der lwip Stack läuft wie er soll, aber Switch und Phy verstehen sich 
nicht.

Im Phy Init wird erst die Phy Adresse ermittelt, das scheint ok zu sein, 
ein Fehler beim Registerzugriff würde gemeldet. Wodurch kann der der 
Softreset dann fehlerhaft sein? Das Timeout ist 500 ms, ist das lang 
genug? Ich hangele mich gerade durch das datasheet vom LAN8742.
Da steht auch das beim Softreset die strapping bits nicht berücksichtigt 
werden, d.h. der Link Mode muss vorher selber konfiguriert werden? 
Autoneg oder ein fixer Mode werden im init code auch nicht gesetzt.

von J. S. (jojos)


Lesenswert?

Ich denke ich habe es gefunden. Kann es sein das der Code von ST nicht 
immer perfekt ist oder sehe ich nur den Wald vor lauten Bäumen nicht 
mehr?
Das PHY (LAN8742) init geht schief, es wird eine falsche PHY Adresse 
ermittelt. Der Code dazu sieht mir sehr strange aus: im SMR steht in 
Bits 0:4 die Adresse drin, per Hardware kann nur 0 oder 1 eingestellt 
werden. Für andere Adressen muss man mehr Aufwand treiben, macht man 
afaik aber nur in Routerandwendungen. Und dann muss der Address Pin von 
einem gpio gesteuert werden.

Hier
https://github.com/JojoS62/Test-H743ZI2-lwip/blob/79bd3eb6f7d82179384d7a70a9411745cac3ca20/Drivers/BSP/Components/lan8742/lan8742.c#L101-L126
wird in einer for Schleife eine variable addr gegen die aus dem SMR 
verglichen und wenn es nicht passt wird addr inkrementiert.
Kann sowas Sinn machen?

Dann habe ich für den Address strapping pin einen R von 10 k nach GND. 
Scheint mir zu hochohmig zu sein und ich vermute daher die Zufälligkeit 
meines Fehlers.

Edit:
verstehe den Code jetzt doch. Wenn ein Device eine andere Adresse hat, 
dann reagiert es ja nicht auf das Lesen der Register, und so muss 
durchprobiert werden.
Im Fehlerfall hat er bei mir Adresse 31 gemeldet, trotzdem komisch.

: Bearbeitet durch User
von Wastl (hartundweichware)


Lesenswert?

J. S. schrieb:
> Dann habe ich für den Address strapping pin einen R von 10 k nach GND.
> Scheint mir zu hochohmig zu sein und ich vermute daher die Zufälligkeit
> meines Fehlers.

Redest du von deiner eigenen Schaltungs-Kreation oder von einem
eval-Board? Bei deiner eigenen Kreation würde ich ggf. mal
kritisch auf die Verdrahtung schauen. Mit 25 oder 50 MHz einen
Chip zu steuern kann schon Fallstricke beinhalten. Na gut, kann
bei einem "schlechten" eval-Board auch passieren.

von J. S. (jojos)


Angehängte Dateien:

Lesenswert?

Wastl schrieb:
> Redest du von deiner eigenen Schaltungs-Kreation oder von einem
> eval-Board?

beides, bzw habe ich die HW bei beiden nicht gemacht: einmal das 
Nucleo-H743ZI2 und einmal eine HW von einem ext. Ing. Büro. Den Fehler 
konnte ich beim Nucleo auch provozieren bei ext. Versorgung oder Jumper 
STLink raus/rein, was auch die MCU neu startet ohne die Resetleitung zu 
ziehen.

Die HW sieht auf jeden Fall deutlich besser als die SW. Wenn es läuft 
dann läuft das Ding, es sind jetzt 'nur' die Startprobleme.

Mit dem Workaround den Treiber mit Phy Addresse 0 zu betreiben scheint 
es jetzt behoben, aber das ist mir noch zu unsicher.
Eine falsch eingelesenes Addressbit müsste dann ja mit Adresse 1 laufen, 
und nicht bis 31 hochzählen. Registerlesen sollte auch ok sein, der 
return code wird ja geprüft.

von Wastl (hartundweichware)


Lesenswert?

J. S. schrieb:
> beides, bzw habe ich die HW bei beiden nicht gemacht: einmal das
> Nucleo-H743ZI2 und einmal eine HW von einem ext. Ing. Büro.

Ok. Beim geposteten Schaltplan vermisse ich Abblock-Kondensatoren
an VDDIO. Bei solchen Chips mit hochfrequenten externen Taktungen
würde ich eher zwei Kondensatoren spendieren. Da bin ich ein
wenig pingelig ..... immerhin kommen ja aus den Chips dann
100 MHz (?) oder mehr auf das Kabel.

von J. S. (jojos)


Angehängte Dateien:

Lesenswert?

Danke, ja das dürften die Kollegen richtig gemacht haben.

Aber ich denke das faule Ei liegt wieder auch hier bei ST im Nest. Ich 
bin noch weiter in die Tiefe und sehe
1
int32_t ETH_PHY_IO_Init(void)
2
{
3
  /* We assume that MDIO GPIO configuration is already done
4
     in the ETH_MspInit() else it should be done here
5
  */
6
7
  /* Configure the MDIO Clock */
8
  HAL_ETH_SetMDIOClockRange(&heth);
9
10
  return 0;
11
}

das HAL_ETH_SetMDIOClockRange() ist nicht nötig weil es im ETH_init 
schon aufgerufen wird.
Aber ST weiß gar nicht wie schnell seine Controller sind:
1
void HAL_ETH_SetMDIOClockRange(ETH_HandleTypeDef *heth)
2
{
3
  uint32_t hclk;
4
  uint32_t tmpreg;
5
6
  /* Get the ETHERNET MACMDIOAR value */
7
  tmpreg = (heth->Instance)->MACMDIOAR;
8
9
  /* Clear CSR Clock Range bits */
10
  tmpreg &= ~ETH_MACMDIOAR_CR;
11
12
  /* Get hclk frequency value */
13
  hclk = HAL_RCC_GetHCLKFreq();
14
15
  /* Set CR bits depending on hclk value */
16
  if ((hclk >= 20000000U) && (hclk < 35000000U))
17
  {
18
19
...
20
21
  else /* (hclk >= 150000000)&&(hclk <= 200000000) */
22
  {
23
    /* CSR Clock Range between 150-200 MHz */
24
    tmpreg |= (uint32_t)ETH_MACMDIOAR_CR_DIV102;
25
  }

Da ist beim MDIO clock bei 200 MHz Schluss, der H743 rennt aber mit 400 
MHz bzw. könnte noch 480 MHz.
Die Kommentare im Header zu den Konstanten sehen anders aus
1
#define ETH_MACMDIOAR_CR_DIV26_Pos                    (8U)
2
#define ETH_MACMDIOAR_CR_DIV26_Msk                    (0x3UL << ETH_MACMDIOAR_CR_DIV26_Pos) /*!< 0x00000300 */
3
#define ETH_MACMDIOAR_CR_DIV26                        ETH_MACMDIOAR_CR_DIV26_Msk /* CSR clock/26 */
4
#define ETH_MACMDIOAR_CR_DIV102_Pos                   (10U)
5
#define ETH_MACMDIOAR_CR_DIV102_Msk                   (0x1UL << ETH_MACMDIOAR_CR_DIV102_Pos) /*!< 0x00000400 */
6
#define ETH_MACMDIOAR_CR_DIV102                       ETH_MACMDIOAR_CR_DIV102_Msk /* CSR clock/102 */
7
#define ETH_MACMDIOAR_CR_DIV124_Pos                   (8U)
8
#define ETH_MACMDIOAR_CR_DIV124_Msk                   (0x5UL << ETH_MACMDIOAR_CR_DIV124_Pos) /*!< 0x00000500 */
9
#define ETH_MACMDIOAR_CR_DIV124                       ETH_MACMDIOAR_CR_DIV124_Msk /* CSR clock/124 */
10
#define ETH_MACMDIOAR_CR_DIV4AR_Pos                   (11U)
11
#define ETH_MACMDIOAR_CR_DIV4AR_Msk                   (0x1UL << ETH_MACMDIOAR_CR_DIV4AR_Pos) /*!< 0x00000800 */
12
#define ETH_MACMDIOAR_CR_DIV4AR                       ETH_MACMDIOAR_CR_DIV4AR_Msk /* CSR clock/4: MDC clock above range specified in IEEE */
13
#define ETH_MACMDIOAR_CR_DIV6AR_Pos                   (8U)

Mal sehen was das Datenblatt sagt.

von Wastl (hartundweichware)


Lesenswert?

J. S. schrieb:
> Danke, ja das dürften die Kollegen richtig gemacht haben.

Das war im original Schaltplan nicht ersichtlich. Hoffentlich
stimmt auch der Ort wo die Kondensatoren platziert wurden.
Man hat ja schon Pferde vor der Apotheke kotzen sehen.

von J. S. (jojos)


Lesenswert?

nah am Phy Chip.

Der Teiler für den Takt ist doch richtig, der Takt der da ankommt ist 
Sysclock/2. Aber für den Fall mit 480 MHz fehlt dann trotzdem die 
nächste Teilerstufe. Bzw. einen Fehler dürfte das mit LAN8742 auch nicht 
machen, der kann bis 2,5 MHz MDC und 240 / 102= 2,35 MHz.

Nächster Test ist nochmal das unnötige HAL_ETH_SetMDIOClockRange() 
wegzulassen weil es vorher schon gemacht wird. Es steht zwar nicht im 
Manual das danach gewartet werden muss, aber in dem Register sind viele 
Steuerbits die vielleicht eine Pause brauchen.

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


Lesenswert?

Also der H7 ist einfach zu schnell. Die Phy Initialisierung kommt 3 ms 
nachdem die 3,3V vorhanden sind. Der Phy braucht aber wohl 23 ms bis er 
seine Strapping Bits verarbeitet hat und ist in dieser Zeit nicht 
ansprechbar. Das Problem liess sich jetzt einfach durch ein delay vor 
dem MX_LWIP_init beheben.

Aber das sauber zu lösen mit CubeMX generierten Code ist schon übel. Das 
MX_LWIP_init kann man aus Codegenerierung herausnehmen und selber später 
aufrufen, soweit ok.
Der generierte Code ist aber nicht ok bezüglich Cachepflege. Hier muss 
Code ausserhalb der User Code Tags geändert werden. Code ändern und lwip 
in Cube abschalten ist nicht gut, dann entfernt der Codegenerator lwip 
komplett.
Dann gibt es die Möglichkeit eigene Code Templates einzugügen. Das ist 
aber spärlich dokumentiert, ein erster Versuch das ethernet_h7.ftl in 
die zusätzlichen Templates zu kopieren hat nicht funktioniert. Es wird 
eine C Datei erzeugt, aber mit einem Java Callstack einer exception.
Kann man damit die vorhandene ethernet_if.c ersetzen? Und der Murks ist 
das die Codegenerierung dann ja von der IDE Installation abhängig ist, 
nicht im Projekt gesichert.
Wie macht ihr das (ausser über Cube/HAL zu schimpfen)?

von Harry L. (mysth)


Lesenswert?

J. S. schrieb:
> Wie macht ihr das (ausser über Cube/HAL zu schimpfen)?

Ich ändere das im Library und füge ein #define dort ein, das ich im 
User-Code auswerte.
Das funktioniert dann zwar nur mit der aktuellen Library-Version, aber 
vermurkst mir nicht gleich den gesamten Code.

Sobald eine neue Library-Version kommt, muß ich diesen Vorgang natürlich 
wiederholen, so lange ST das eigentliche Problem nicht behoben hat, aber 
das erkenne ich dank des #define dann zumindest sofort.

von J. S. (jojos)


Lesenswert?

Das wird dann aber nach Codegenerierung überschrieben. Das wieder 
manuell zu ändern ist sehr unprofessionell. Ziel soll auch sein das auf 
einem Buildserver zu bauen.
Und neue Versionen oder Änderungen erkennt man besser durch git…

: Bearbeitet durch User
von Harry L. (mysth)


Lesenswert?

J. S. schrieb:
> Das wird dann aber nach Codegenerierung überschrieben.

Nein, eben nicht!
Es gibt bei dir ein Verzeichnis in dem sich die heruntergeladenen 
Librarys befinden.
Diese Files werden von CubeMX in dein Projekt kopiert, und wenn du diese 
Änderungen dort vornimmst, sind die auch nach erneuter Codegenerierung 
weiter vorhanden.

So mach ich das z.B. bei meinem UART-Treiber, wo ich die Handle-Struct 
um einen User-Pointer erweitert habe.

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