Hallo, ich bin gerade dabei, mir die #define für die Register zusammenzuschreiben. Nun stehe ich vor dem Problem, dass ich aus dem Reference Manual nicht erkennen kann, welche (zum Beispiel) 3 von den 5 USARTs in meinem µC (STM32F103C8T6) nun benutzt werden. Dementsprechend müsste ich dann ja auch die Speicheradressen setzen. Sind das einfach die ersten 3 oder gibt es dafür irgendwo eine Übersicht/Dokument? Grüße Leopold
Steht im Datenblatt. Aber warum machst Du das? Das gibt es doch schon fix und fertig von ST.
Leopold N. schrieb: > ich bin gerade dabei, mir die #define für die Register > zusammenzuschreiben. Warum das denn? Lade Dir doch das Cube HAL Firmware Paket für STM32F1 herunter und extrahiere davon das CMSIS Verzeichnis. Da hast du alle Includes drin, in der von ARM spezifizierten Schreibweise. Oder nimm sie aus meinem Beispielprojekt: http://stefanfrings.de/stm32/index.html#cmsis Da hast du dann auch gleich ein passendes Linkerscript und Startup-Code.
Im Datenblatt zum IC steht dann drinne welche Peripherie wirklich vorhanden ist. https://www.st.com/resource/en/datasheet/cd00161566.pdf zB auf Seite 11 in der Übersicht. Aber warum defines? Die Registerdefinitionen packet man eigentlich in structs welche man dann auf die Basiadresse "legt". Für die Bits empfehlen sich enums.
1 | struct spi { |
2 | unsigned int CR1; |
3 | unsigned int CR2; |
4 | unsigned int SR; |
5 | unsigned int DR; |
6 | unsigned int CRCPR; //CRC polynomial register |
7 | unsigned int RXCRCR; //RX CRC register |
8 | unsigned int TXCRCR; //TX CRC register |
9 | unsigned int I2SCFGR; //I2S configuration register |
10 | unsigned int I2SPR; //I2S prescaler register |
11 | };
|
12 | |
13 | //*************************************** Register CR1 **********************************************
|
14 | |
15 | enum reg_cr1 { |
16 | CR1_BIDIMODE = 1 << 15, //Bidirectional data mode enable |
17 | CR1_BIDIOE = 1 << 14, //Output enable in bidirectional mode |
18 | CR1_CRCEN = 1 << 13, //Hardware CRC calculation enable |
19 | CR1_CRCNEXT = 1 << 12, //CRC transfer next |
20 | CR1_DFF = 1 << 11, //Data frame format |
21 | CR1_RXONLY = 1 << 10, //This bit combined with the BIDImode bit selects the direction of transfer in 2-line unidirectional mode. |
22 | CR1_SSM = 1 << 9, //Software slave management |
23 | CR1_SSI = 1 << 8, //Internal slave select |
24 | CR1_LSBFIRST = 1 << 7, //Frame format |
25 | CR1_SPE = 1 << 6, //SPI enable |
26 | CR1_BR_SHIFT = 3, //Baud rate control |
27 | CR1_MSTR = 1 << 2, //Master selection |
28 | CR1_CPOL = 1 << 1, //Clock polarity |
29 | CR1_CPHA = 1 << 0, //Clock phase |
30 | };
|
Ja ich weiß, dass habe ich alles schon durchgelesen. Ich finde es mit den Structs aber nicht sonderlich schön, und HAL hat ja auch noch so einige Bugs hab ich gehört :) Außerdem will ich den Controller verstehen, damit ich nachher weiß wovon ich rede. Und wenn ich sowas wie HAL oder die Standard Lib benutze, verstehe ich das Ganze nicht mal im Ansatz. Grüße
Dann musst du aber für jeden SPI jedes Register 3 mal definieren: SPI1_CR1 SPI2_CR1 SPI3_CR1 Das is doch Murks. Vor allem kannste dann nicht mal eben die Funktionalität von SPI1 nach SPI3 schieben, weils für das Layout besser passt, sondern musste dann alle Namen ändern.
Nachdem du hier bereits kläglich gescheitert bist, Beitrag "ST Link V2 Atollic TrueStudio" glaubst du ernsthaft, das selber besser und -vor allem- fehlerfreier als die ARM-Leute bei ST & Co zu können??? Ich lach mich schlapp!!!
:
Bearbeitet durch User
Leopold N. schrieb: > Und wenn ich sowas wie HAL oder die Standard Lib benutze, > verstehe ich das Ganze nicht mal im Ansatz. Die CMSIS enthält nur wenige Zeilen Code. Sie besteht zu 99% lediglich aus den Defines, die du Dir gerade selbst erstellt. Lass das doch bleiben, das ist Quatsch! Es führt nur zu Problemen, wenn du später Hilfe anforderst und deine Defines und Makros anders heißen, als der ARM Standard festlegt.
stefanus schrieb: > Leopold N. schrieb: >> Und wenn ich sowas wie HAL oder die Standard Lib benutze, >> verstehe ich das Ganze nicht mal im Ansatz. > > Die CMSIS enthält nur wenige Zeilen Code. Sie besteht zu 99% lediglich > aus den Defines, die du Dir gerade selbst erstellt. > > Lass das doch bleiben, das ist Quatsch! Es führt nur zu Problemen, wenn > du später Hilfe anforderst und deine Defines und Makros anders heißen, > als der ARM Standard festlegt. Ich kann später ja immer noch umsteigen, aber für den Anfang geht es mir erstmal darum, mir eine eigene Lib aufzubauen, um das Ganze von Grund auf zu verstehen. Grüße Leopold
Kann ich uint_32 so definieren (ohne irgendeine Standard Lib vorher)? Grüße
:
Bearbeitet durch User
Leopold N. schrieb: > Kann ich uint_32 so definieren (ohne irgendeine Standard Lib vorher)? Warum willst du das? uint8_t und Konsorten sind seit fast 20 Jahren (in Worten: seit _zwanzig Jahren_) im C-Standard enthalten! Das ist wie eine Seuche, dass jeder meint, sich seinen uint8, uint_8, u8, unsigned8, byte selbst definieren zu müssen.
Sven P. schrieb: > Leopold N. schrieb: >> Kann ich uint_32 so definieren (ohne irgendeine Standard Lib vorher)? > > Warum willst du das? > uint8_t und Konsorten sind seit fast 20 Jahren (in Worten: seit _zwanzig > Jahren_) im C-Standard enthalten! > > Das ist wie eine Seuche, dass jeder meint, sich seinen uint8, uint_8, > u8, unsigned8, byte selbst definieren zu müssen. Verstehe ich, aber ich möchte so etwas mal wirklich von Grund auf bauen, und alle Libs selber geschrieben haben (natürlich nicht so was gestörtes wie USB oder so, aber zumindest die kleinen Dinge) Also geht das so oder muss ich das anders machen? Grüße
Leopold N. schrieb: > Ich kann später ja immer noch umsteigen, aber für den Anfang geht es mir > erstmal darum, mir eine eigene Lib aufzubauen, um das Ganze von Grund > auf zu verstehen. Nein eben nicht. Verwende besser die eine Datei: stm32f10x.h Da steht alles drin was der µC so alles hat. Und baue genau auf der einen Datei Deine eigene Lib auf und fange bitte nicht an alles neu zu definieren was der µC kann. Diese eine Datei ist NICHT die HAL von ST, sonder nur die Register-Definition und Deklarationen. Es sind einige tausend Codezeilen und mehrere 100KB, das willst Du nicht nochmal selbst erfinden, glaub mir. Um das ganze von Grund auf zu verstehen zu lernen, befasse dich besser ein paar Tage mit dieser einen Datei - das wird dich weiter bringen.
Leopold N. schrieb: > Ich kann später ja immer noch umsteigen, aber für den Anfang geht es mir > erstmal darum, mir eine eigene Lib aufzubauen, um das Ganze von Grund > auf zu verstehen. Da gibt's doch nichts zu verstehen. Die Defines sind eine laaaange Liste von Register-Adressen. Was lernt man denn dabei, wenn man diese komplett neu schreibt? Hast du deinen Klassenlehrer angezickt und musst das nun als Strafarbeit machen? Verwende deine Zeit doch lieber damit, den Startup-Code aus meinem Beispielprojekt zu analysieren. Der ist bereits auf ein vernünftiges Minimum reduziert.
Könnt ihr mir nicht einfach meine Frage beantworten? Leopold N. schrieb: > Kann ich uint_32 so definieren (ohne irgendeine Standard Lib vorher)?
Leopold N. schrieb: > Könnt ihr mir nicht einfach meine Frage beantworten? Guck doch in die CMSIS rein, oder ins Datenblatt. Wie viele Antworten brauchst du denn noch?
Noch eine Frage: Im Reference Manual steht für CAN zwei CANs drinne, obwohl ich nur einen habe. In dem anderen Dokument steht nur CAN drinne, nicht CAN1 oder CAN2. An welcher Adresse starten denn nun die Register? Grüße
Leopold N. schrieb: > Könnt ihr mir nicht einfach meine Frage beantworten? Guck doch in die CMSIS rein, oder ins Datenblatt. Wie viele Antworten brauchst du denn noch? > Kann ich uint_32 so definieren Kannst du, aber auch das ist Quatsch. Willst du jetzt im Ernst auch noch die stdint.h neu erfinden? Warum schreibst du Dir nicht gleich einen eigenen Compiler? Wenn du unbedingt mal einen µC so von ganz unten programmieren willst, dann mache das in Assembler mit einem 89C2051 - der ist wesentlich simpler. Dein Vorhaben mit dem STM32 ist nur 50x Arbeitsintensiver aber lernen tust du dabei fast nichts zusätzliches - ausser vielleicht Übung im Tippen.
Stefanus F. schrieb: > Guck doch in die CMSIS rein, oder ins Datenblatt. Wie viele Antworten > brauchst du denn noch? Du beantwortest damit immer noch nicht meine Frage. Ich habe es im CMSIS Header File stm32f10x.h versucht und bin auf den oben angehängten Ansatz gekommen. Stimmt der so?
Leopold N. schrieb: > Stefanus F. schrieb: >> Guck doch in die CMSIS rein, oder ins Datenblatt. Wie viele Antworten >> brauchst du denn noch? > > Du beantwortest damit immer noch nicht meine Frage. > Ich habe es im CMSIS Header File stm32f10x.h versucht und bin auf den > oben angehängten Ansatz gekommen. > Stimmt der so? Fehler: Hab das falsche Header File geschrieben. Hab im stdint.h nachgeguckt.
Arduinator schrieb: > Beratungsresistenz ist eine Zier, doch weiter kommt man ohne Ihr. Wenn du meine Frage nicht beantworten möchtest, dann schreib doch bitte einfach gar nichts.
Leopold N. schrieb: > An welcher Adresse starten denn nun die Register? Der STM32 hat 2 Datenblätter: 1) Reference Manual - da steht alles übergriefend für die ganze Produktlinie drin 2) Datasheet - für jeden µC das Datenblatt mit den den Details und Pin-Belegung. Die Adressen stehen in 1) beim jeweiligen Peripheriemodul als Basis-Adresse und die Register als Adressoffset zu der jeweiligen Basisadresse.
In einer öffentlichen Diskussion muss man offene Kritik vertragen können. Ansonsten frage jemanden privat.
Leopold N. schrieb: > Fehler: Hab das falsche Header File geschrieben. > Hab im stdint.h nachgeguckt. Dann guck doch mal im richtigen Header-File: stm32F10x.h und lerne wie es die Profis machen, die viele Jahre Erfahrung haben.
Markus M. schrieb: > Leopold N. schrieb: >> An welcher Adresse starten denn nun die Register? > > Der STM32 hat 2 Datenblätter: > > 1) Reference Manual - da steht alles übergriefend für die ganze > Produktlinie drin > > 2) Datasheet - für jeden µC das Datenblatt mit den den Details und > Pin-Belegung. > > Die Adressen stehen in 1) beim jeweiligen Peripheriemodul als > Basis-Adresse und die Register als Adressoffset zu der jeweiligen > Basisadresse. Ja, das habe ich auch bisher so gemacht, aber beim Peripherimodul steht nur ein Verweis auf die Basisadresse. Der Verweis beim CAN ist ungültig (falsches Kapitel angegeben, hat wohl jemand bei ST vergessen zu ändern) und in der Memory Map steht CAN1 und CAN2 drin. Es gibt aber nur CAN (ohne 1 oder 2). Ist das jetzt CAN1 oder CAN2 oder was ganz anderes (adresstechnisch)?
Markus M. schrieb: > Dann guck doch mal im richtigen Header-File: > > stm32F10x.h > > und lerne wie es die Profis machen, die viele Jahre Erfahrung haben. Da war ich bereits unterwegs, und die definieren 2 CANs (CAN1 und CAN2). Für beide wird ein struct erstellt. Da es aber nur einen CAN gibt weiß ich jetzt nicht welcher das ist.
Beitrag #5724555 wurde vom Autor gelöscht.
Markus M. schrieb im Beitrag #5724555: > Leopold N. schrieb: >> Ist das jetzt CAN1 oder CAN2 oder was ganz anderes >> (adresstechnisch)? > > Markus M. schrieb: >> Dann guck doch mal im richtigen Header-File: >> >> stm32F10x.h Da du mir wohl nicht glaubst, hier der entsprechende Ausschnitt (Anhang).
als Vorlage zum Abschauen, lernen, lade dich einfach mal dem Mikroe Compiler runter. Dort ist es, wenn ich richtig verstanden habe, genau so gemacht, wie Du es vorhast. Die Datei wäre also perfekt zum abschauen
Beitrag #5724566 wurde von einem Moderator gelöscht.
Kim P. schrieb: > als Vorlage zum Abschauen, lernen, lade dich einfach mal dem > Mikroe > Compiler runter. > Dort ist es, wenn ich richtig verstanden habe, genau so gemacht, wie Du > es vorhast. > Die Datei wäre also perfekt zum abschauen Grad gegoogelt, der kostet 300$ richtig?
Im Datenblatt vom STM32F103 steht ja auch wie viele CAN's der hat. Dann wird das wohl oder übel ein einzelner der erste sein.
Markus M. schrieb: > Im Datenblatt vom STM32F103 steht ja auch wie viele CAN's der hat. > Dann > wird das wohl oder übel ein einzelner der erste sein. Aber du schätzst es auch nur oder steht das irgendwo? Weil reintheoretisch könnte man ja - sofern man die Lib benutzt - CAN2 benutzen und somit irgendwo im RAM rumschreiben oder? Grüße
Leopold N. schrieb: > Sind das einfach die ersten 3 oder gibt es dafür irgendwo eine > Übersicht/Dokument? Steht im Datenblatt. Ansonsten: spar dir das Abtippen, ich hatte das schon vor langer Zeit erledigt: https://www.mikrocontroller.net/attachment/316790/STM32F103C8T6.ZIP Und von all dem Cube, ST-lib, ST-HAL und dessen Brüdern halte ich relativ wenig. Man wird nur dazu ermuntert, den inneren Schweinehund walten zu lassen und nicht zu lernen, was man da für ein Pferd zu reiten beabsichtigt. W.S.
Leopold N. schrieb: > Markus M. schrieb: >> Im Datenblatt vom STM32F103 steht ja auch wie viele CAN's der hat. >> Dann >> wird das wohl oder übel ein einzelner der erste sein. > > Aber du schätzst es auch nur oder steht das irgendwo? > Weil reintheoretisch könnte man ja - sofern man die Lib benutzt - CAN2 > benutzen und somit irgendwo im RAM rumschreiben oder? > > Grüße Nix RAM, bestenfalls ins Leere des IO-Bereichs, eventuell aber BusFault/HardFault.
W.S. schrieb: > Leopold N. schrieb: >> Sind das einfach die ersten 3 oder gibt es dafür irgendwo eine >> Übersicht/Dokument? > > Steht im Datenblatt. Ansonsten: spar dir das Abtippen, ich hatte das > schon vor langer Zeit erledigt: > > https://www.mikrocontroller.net/attachment/316790/STM32F103C8T6.ZIP > > Und von all dem Cube, ST-lib, ST-HAL und dessen Brüdern halte ich > relativ wenig. Man wird nur dazu ermuntert, den inneren Schweinehund > walten zu lassen und nicht zu lernen, was man da für ein Pferd zu reiten > beabsichtigt. > > W.S. Endlich mal jemand, der ähnlich denkt. Das Abtippen würde ich jetzt trotzdem gerne nochmal machen, einfach um auch einen Überblick zu bekommen, was der alles für Register und Module hat, die man dann auch verwalten muss. Mich interessiert einfach, wie das alles funktioniert. Danke für deine Datei, an der werde ich mich orientieren. Du hast beim CAN aber auch 2 CANs reingenommen, obwohl der nur einen hat. Grüße
Carl D. schrieb: > Nix RAM, bestenfalls ins Leere des IO-Bereichs, eventuell aber > BusFault/HardFault. Mit der genauen Lokalisation der Register habe ich mich noch nicht beschäftigt. Also stehen die ganzen Peripherie Register in einfachem Speicher getrennt vom SRAM?
Da ich gerne wie in Atmel Studio einfach durch (zum Beispiel) PORTA = 0x13; oder sbi(PORTA, 3); auf Register zugreifen möchte, habe ich überlegt, dass das doch so gehen müsste (Beispiel): #define CAN_TSR ((uint_32*) (BASE_ADDRESS_CAN + 0x08)) Damit habe ich doch einen Pointer auf die Adresse xxx. Ein Zugriff ala CAN_TSR = 0x00 würde doch eine 0 an die Adresse xxx schreiben, oder? Oder muss noch ein Sternchen vor uint_32? Grüße
:
Bearbeitet durch User
So geht das bei STM32 mit den Definitionen der CMSIS:
1 | GPIOA->ODR = 0x13; // Auf Port A den Wert 0x13 ausgeben |
oder
1 | GPIOA->BSRR=GPIO_BSRR_BS3; // Pin PA3 auf High setzen |
2 | GPIOA->BSRR=GPIO_BSRR_BR3; // Pin PA3 auf Low setzen |
Wie gesagt braucht man dafür die CMSIS, keine hand-gedengelten Register-Definitionen. Beim AVR schreibst du dir doch auch nicht die ganzen Register-Definitionen und die libc selbst neu.
Stefanus F. schrieb: > Wie gesagt braucht man dafür die CMSIS, keine hand-gedengelten > Register-Definitionen. Nich ganz, es kommt drauf an wie man Selbstdengelt. Bei meinen Definitionen geht das ja auch in dieser Schreibweise. Aber mal ne kurze Frage zu deinem F103 Tutorial: Wieso hast du da Funktionen gebaut zum Register lesen/schreiben?
Leopold N. schrieb: > Carl D. schrieb: >> Nix RAM, bestenfalls ins Leere des IO-Bereichs, eventuell aber >> BusFault/HardFault. > > Mit der genauen Lokalisation der Register habe ich mich noch nicht > beschäftigt. > Also stehen die ganzen Peripherie Register in einfachem Speicher > getrennt RAM: 0x20000000...$+20k IO: 0x40000000...0x5fffffff (mit großen Lücken) Es gibt das "Referenz Manual", das die ganze STM32F1-Serie im Detail beschreibt und das "Datasheet", das Speicherlayout, Pinout und konkrete Peripherie-Ausstattung einzelner Chips beschreibt. In ersterem steht dann z.B. wie ein Advanced Timer zu bedienen ist und in letzterem wieviele davon ein konkreter Chip besitzt, wo die "Device-Base"-Adressen liegen und an welchen Pins man die einzelnen Signale abgreifen kann/einspeisen muß. Allzu unbedarft sollte man für Baremetall-Programmierung auf STM32 aber nicht sein und mir hat ein ST/Link-Debugger(-Clone) geholfen, z.B Timer und DMA-Zusammenspiel live zu testen. Wenn man eine IDE hat, die da mitspielt.
Mw E. schrieb: > Wieso hast du da Funktionen gebaut zum Register lesen/schreiben? Hab ich nicht. Das sind Markos aus der CMSIS und ich habe sie dort hingeschrieben, damit man sie kennen lernt. Die avr libc stellt ähnliche Makros bereit, zum Beispiel das von Dir genannte sbi(). Dieses ist übrigens als "obsolete/deprecated" gekennzeichnet - nur so nebenbei bemerkt.
Leopold N. schrieb: > Da ich gerne wie in Atmel Studio einfach durch (zum Beispiel) > > PORTA = 0x13; > > oder > > sbi(PORTA, 3); > > auf Register zugreifen möchte, habe ich überlegt, dass das doch so gehen > müsste (Beispiel): > > #define CAN_TSR ((uint_32*) (BASE_ADDRESS_CAN + 0x08)) > > Damit habe ich doch einen Pointer auf die Adresse xxx. > Ein Zugriff ala > > CAN_TSR = 0x00 Im Prinzip schon, aber wenn dem Compiler bekannt gemacht wird, das die Register in einer Struktur organisiert sind (bekannte Offsets zu einer Basisadresse), dann erzeugt er deutlich kompakteren Code. Jedem Register eine eigene Adresse zu geben verschleiert die "Nachbarschaft" der Register. Außerdem: es existieren meist mehrere Devices mit gleicher Registerstruktur, die sich nur in der Basisadresse unterscheiden. Also nur einmal hinschreiben.
:
Bearbeitet durch User
Ich muss mal für stefanus aka Stefan Frings eine Lanze brechen: Er hat wirklich eine gute Anleitung geschrieben, wie man den STM32F103 ohne das ganze ST HAL Zeugs programmiert, genau so wie Leopold das eigentlich will. Unter http://stefanfrings.de/stm32/index.html ist dies alles zu finden. Am Ende findet man dann auch W.S. wirklich brauchbaren USB Code. Stefan hat sogar noch Zeit gefunden, dass ganze in Buchform zu schreiben: http://stefanfrings.de/mikrocontroller_buch2/Einblick%20in%20die%20moderne%20Elektronik.pdf Die verwendeten "Funktionen" WRITE_REG usw. sind eigentlich Makros und stammen aus der stm32f10x.h Header Datei
1 | #define SET_BIT(REG, BIT) ((REG) |= (BIT))
|
2 | #define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT))
|
3 | #define READ_BIT(REG, BIT) ((REG) & (BIT))
|
4 | #define CLEAR_REG(REG) ((REG) = (0x0))
|
5 | #define WRITE_REG(REG, VAL) ((REG) = (VAL))
|
6 | #define READ_REG(REG) ((REG))
|
7 | #define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))
|
d.h. statt
1 | // 64 MHz using the 8 MHz/2 HSI oscillator with 16x PLL, lowspeed I/O runs at 32 MHz
|
2 | WRITE_REG(RCC->CFGR, RCC_CFGR_PLLMULL16 + RCC_CFGR_PPRE1_DIV2); |
3 | |
4 | // Enable PLL
|
5 | SET_BIT(RCC->CR, RCC_CR_PLLON); |
kann man auch ganz einfach (ähnlich beim AVR) schreiben:
1 | // 64 MHz using the 8 MHz/2 HSI oscillator with 16x PLL, lowspeed I/O runs at 32 MHz
|
2 | RCC->CFGR = RCC_CFGR_PLLMULL16 + RCC_CFGR_PPRE1_DIV2; |
3 | |
4 | // Enable PLL
|
5 | RCC->CR |= RCC_CR_PLLON; |
Stefanus F. schrieb: > So geht das bei STM32 mit den Definitionen der CMSIS: > GPIOA->ODR = 0x13; // Auf Port A den Wert 0x13 ausgeben > > oder > GPIOA->BSRR=GPIO_BSRR_BS3; // Pin PA3 auf High setzen > GPIOA->BSRR=GPIO_BSRR_BR3; // Pin PA3 auf Low setzen > > Wie gesagt braucht man dafür die CMSIS, keine hand-gedengelten > Register-Definitionen. Beim AVR schreibst du dir doch auch nicht die > ganzen Register-Definitionen und die libc selbst neu. Wie das mit CMSIS geht habe ich schon begriffen, aber ich wollte es ja selber mal gemacht haben. Drum werd ich solche Beiträge in Zukunft einfach ignorieren. Stefanus F. schrieb: > Die avr libc stellt ähnliche Makros bereit, zum Beispiel das von Dir > genannte sbi(). Dieses ist übrigens als "obsolete/deprecated" > gekennzeichnet - nur so nebenbei bemerkt. Das weiß ich, aber es sind nützliche Makros, an die ich mich gewöhnt habe. Deshalb möchte ich sie auch weiterhin nutzen. Carl D. schrieb: > Außerdem: es existieren meist mehrere Devices mit gleicher > Registerstruktur, die sich nur in der Basisadresse unterscheiden. Also > nur einmal hinschreiben. Das mit den Basisadressen behalte ich auch bei. Nur die structs möchte ich loswerden.
Leopold N. schrieb: > Danke für deine Datei, an der werde ich mich orientieren. Mach es nicht, so programmiert heute niemand ernsthaft mehr C. Das ist Masochismus, was W.S. da treibt. Ich krieg schon Zahnfleischbluten, wenn ich den ganzen Mist mit wilder Bitfrickelei da sehe. Und die Konsequenz ist, dass alles dreifach kopiert wird, weil es z.B. drei UARTs gibt. Mit CMSIS wäre das nicht passiert. Das hat schon einen Sinn, warum die CMSIS Bitnamen definiert und mit Peripherie-Offsets arbeitet. Leopold N. schrieb: > Das weiß ich, aber es sind nützliche Makros, an die ich mich gewöhnt > habe. > Deshalb möchte ich sie auch weiterhin nutzen. Und warum? Sie helfen dir exakt garnicht. Viel schlimmer noch, sie verhindern, dass du mal verstehst, wie Zugriffe über den AHB laufen. Viel Spaß beim Fehlersuchen. Achso, CAN1 und CAN2 sind Instanzen von CAN, falls das noch hilft.
Sven P. schrieb: > Sie helfen dir exakt garnicht. > Viel schlimmer noch, sie verhindern, dass du mal verstehst, wie Zugriffe > über den AHB laufen. Viel Spaß beim Fehlersuchen. > > Achso, CAN1 und CAN2 sind Instanzen von CAN, falls das noch hilft. Das weiß ich schon alles, schließlich habe ich mir das CMSIS File durchgelesen. Aber dennoch hat jeder CAN Controller eine eigene Hardware mit einer eigenen Adresse, die hier nicht eindeutig ist, da es nur einen CAN Controller gibt aber zwei Adressen.
Leopold N. schrieb: > Aber dennoch hat jeder CAN Controller eine eigene Hardware mit einer > eigenen Adresse, die hier nicht eindeutig ist, da es nur einen CAN > Controller gibt aber zwei Adressen. Dieser Satz widerspricht sich selbst zweifach doppelt gemoppelt. > Das weiß ich schon alles, schließlich habe ich mir das > CMSIS File durchgelesen. Ach so, und warum fragst du dann nach den Adressen?
den mikroe Comipiler kannst du KOSTENLOSS runterladen!!! Er hat halt nru eine KB Grenze beim erstellen von Progrmmane..aber Du solslt ja auch keien programme damit erstellen sondenr nur die Definitionsdaei füde n Controler rauskopieren aus dem Ordner...
Leopold N. schrieb: > Markus M. schrieb: >> Dann guck doch mal im richtigen Header-File: >> >> stm32F10x.h >> >> und lerne wie es die Profis machen, die viele Jahre Erfahrung haben. > > Da war ich bereits unterwegs, und die definieren 2 CANs (CAN1 und CAN2). > Für beide wird ein struct erstellt. Da es aber nur einen CAN gibt weiß > ich jetzt nicht welcher das ist. Siehe dieser Post, solltest vllcht mal den ganzen Thread lesen...
Kim P. schrieb: > den mikroe Comipiler kannst du KOSTENLOSS runterladen!!! > Er hat halt nru eine KB Grenze beim erstellen von Progrmmane..aber Du > solslt ja auch keien programme damit erstellen sondenr nur die > Definitionsdaei füde n Controler rauskopieren aus dem Ordner... Ah ok, ja werde ich mal probieren. Danke
Leopold N. schrieb: > Die Frage mit dem typedef vom uint_32 ist noch offen... ja, sollte mit dem GCC so gehen, da beim GCC "__UINT32_TYPE__" ein vordefiniertes Macro ist. Ich empfehle aber trotzdem die Vorschläge meiner Vorredner zu beherzigen und dafür stdint.h zu nutzen, weil das eben einfach standard ist. Was die Register angeht schließe ich mich auch meinen Vorrednern an und empfehle dir die vordefinierten #defines und structs zu nutzen. Die Bezeichner decken sich 1:1 mit den Bezeichnungen im Reference Manual und man kann wunderbar damit arbeiten. Da nochmal für nix alles neu zu schreiben halte ich für groben Unfug, es sei denn du möchtest einen obfuscated code contest gewinnen und/oder du möchtest dadurch deinen Arbeitsplatz langfristig sichern, weil wirklich außer dir niemand mehr durchblickt. Bei letzterem besteht das Risiko, dass du aus genau diesem Grund deinen Job verlierst aber ymmv. Wenn du tatsächlich was über den Controller lernen willst, schnapp dir den "Definitive Guide to Arm Cortex" von Joseph Yiu, das Reference Manual und die CMSIS-Header und leg los. Abgesehen von der Peripherie gibt es noch andere Dinge, wie z.B. das Interrupt-System oder etwa Startup-Code und Linker-Scripte die verstanden werden wollen, vor allem wenn du z.B. gedenkst mal einen eigenen Bootloader zu schreiben.
Leopold N. schrieb: > Carl D. schrieb: >> Außerdem: es existieren meist mehrere Devices mit gleicher >> Registerstruktur, die sich nur in der Basisadresse unterscheiden. Also >> nur einmal hinschreiben. > > Das mit den Basisadressen behalte ich auch bei. > Nur die structs möchte ich loswerden. Also kurz: hoffnungslos.
Carl D. schrieb: > Also kurz: hoffnungslos. ... ja und umfänglich ahnungslos + grob beratungsresistenz, sprich - halt das übliche und weitverbreitete mittelmaß - bäh! mt
Mw E. schrieb: > Für die Bits empfehlen sich enums. Für Bitmasken nimmt man keine enums, sondern defines. Wie sinnfrei enums an der Stelle sind, merkt man schon daran, daß Du die enums nicht benennst und die Register auch nicht von dem Typ des jeweiligen enums nutzt.
Leopold N. schrieb: > Die Frage mit dem typedef vom uint_32 ist noch offen... Warum soll die denn noch offen sein? Hab nicht mitgezählt, aber es waren mehr wie 3 Antworten. So wie du es in deinem Code-Ausschnitt gemacht hast...kann man machen, ist halt kacke! 1) Hat deine *.h keinen Include-Guard 2) Kein Schutz vor Mehrfach-Define deiner uint32_t Nimm die stdint.h und falls du was wissen willst, lies es dort nach! ... Wenn ich Backen lernen möchte, dann fang ich doch auch nicht an und lerne wie man Mehl herstellt :-/
Bei den ADCs habe ich das Problem, das die Register alle ADC_xxx heißen, es aber 2 ADCs gibt. Teilen sich beide ADCs diese Register oder hat jeder ADC einen eigenen Satz dieser Register. Bei den GPIOx_ mit x=A...G war das eindeutig, ist das bei den ADCs genauso? Betrifft auch die SPIs und die USARTs.
#define BASE_PERIPHERAL ((uint32_t) 0x40000000) #define OFFSET_APB2 ((uint32_t) (BASE_PERIPHERAL + 0x00010000)) #define BASE_ADDRESS_GPIO_A ((uint32_t) (OFFSET_APB2 + 0x00000800)) #define GPIO_A_ODR *((uint32_t*) (BASE_ADDRESS_GPIO_A + 0x0C)) Wenn ich ein Register so definiere, dann kann ich einen uint32_t x folgendermaßen in das Register schreiben oder? GPIO_A_ODR = x; Lesen natürlich analog dazu: uint32_t x = GPIO_A_ODR; Oder mache ich da jetzt einen Fehler?
Leopold N. schrieb: > Bei den ADCs habe ich das Problem, das die Register alle ADC_xxx heißen, > es aber 2 ADCs gibt. > > Teilen sich beide ADCs diese Register oder hat jeder ADC einen eigenen > Satz dieser Register. > Bei den GPIOx_ mit x=A...G war das eindeutig, ist das bei den ADCs > genauso? > > Betrifft auch die SPIs und die USARTs. Ich würde mir doch mal überlegen, ob die Leute mit der Struktur/Basisadresse wirklich so falsch liegen. Die kennen nämlich des Rätsels Lösung.
Carl D. schrieb: > Ich würde mir doch mal überlegen, ob die Leute mit der > Struktur/Basisadresse wirklich so falsch liegen. Die kennen nämlich des > Rätsels Lösung. Nun, ich hätte mir überlegt, dass aufgrund der Tatsache, dass für ADC1 und ADC2 jeweils eine eigene Basisadresse definiert ist im RM, dass jeder ADC einen eigenen Registersatz hat. Explizit steht das aber nicht im RM wie bei den GPIOx_ oder den TIMERx_ Registern. Wäre nett, wenn "diese Leute" mir mal des Rätsels Lösung mitteilen könnten, dann müsste ich denen nämlich nicht alles aus der Nase ziehen. Außerdem habe ich ja die Struktur mit den Basisadressen beibehalten, nur halt ohne die Structs. Und wenn du ein Problem damit hast, dass ich Bock hab meinen eigenen Code zu schreiben, dann antworte doch einfach nicht mehr.
Leopold N. schrieb: > Wenn ich ein Register so definiere, dann kann ich einen uint32_t x > folgendermaßen in das Register schreiben oder? > > GPIO_A_ODR = x; ja, sollte gehen, die Offsets habe ich aber nicht überprüft. Mit dem Standard-Header: GPIOA->ODR = x; oder analog für GPIOB: GPIOB->ODR = x; Leopold N. schrieb: > Lesen natürlich analog dazu: > > uint32_t x = GPIO_A_ODR; Bei Open-Drain Ausgängen kann man eine 1 "schreiben" obwohl das Signal in Wirklichkeit low ist aber ansonsten ginge das auch so, ja. analog x = GPIOA->ODR; Leopold N. schrieb: > Teilen sich beide ADCs diese Register oder hat jeder ADC einen eigenen > Satz dieser Register. Jeder ADC hat seine eigenen Register (abgesehen vom Multimode, wo der ADC2 ein paar Register vom ADC1 nutzt aber das ist eine Besonderheit bei den ADC und gibt es so nicht bei UART, SPI oder Timern). ADC1->CR1 = y; // schreibt ins CR1 von ADC1 ADC2->CR1 = z; // schreibt ins CR1 von ADC2 Leopold N. schrieb: > Explizit steht das aber nicht im RM wie bei den GPIOx_ oder den TIMERx_ > Registern. Ich bin mir ziemlich sicher das es irgendwo steht.
Danke für die Antwort. Das hat mir schonmal sehr geholfen. Ich habe den entsprechenden Abschnitt im RM wohl noch nicht gefunden (kein Wunder, ist ja nur 1132 Seiten lang). Nächste Frage: Ich habe meinen Code minimiert, unter anderem alle Std_Peripheral Libs rausgeschmissen und nur das CMSIS Zeug dringelassen (auch die stdint.h, damit dürfen ein paar Leute hoffentlich beruhigt sein). Wenn ich diesen Minimalcode (Anhang) nun compiliere, kommt eine Warnung (Anhang). Hätte hier jemand eine Liste für mich, was (neben dem Reset Handler) noch alles mindestens gemacht werden muss? PS: In General Defines stehen nur ein paar sbi und cbi Makros.
:
Bearbeitet durch User
Leopold N. schrieb: > Hätte hier jemand eine Liste für mich, was (neben dem Reset Handler) > noch alles mindestens gemacht werden muss? Der restliche Kram der im Startup-Code gemacht wird, d.h. RAM nullen, .data aus dem Flash ins RAM kopieren und die Interrupt-Vektoren anlegen, sofern man denn Interrupts nutzen möchte. Ich würde aber erstmal den vorhandenen Startup-Code nehmen und versuchen den zu verstehen. Irgendwelche Fehlermeldungen bitte immer als Text und nicht als Bild und erst recht nicht mit schwarzer Schrift auf grauem Grund!
Christopher J. schrieb: > Leopold N. schrieb: >> Hätte hier jemand eine Liste für mich, was (neben dem Reset Handler) >> noch alles mindestens gemacht werden muss? > > Der restliche Kram der im Startup-Code gemacht wird, d.h. RAM nullen, > .data aus dem Flash ins RAM kopieren und die Interrupt-Vektoren anlegen, > sofern man denn Interrupts nutzen möchte. > > Ich würde aber erstmal den vorhandenen Startup-Code nehmen und versuchen > den zu verstehen. > > Irgendwelche Fehlermeldungen bitte immer als Text und nicht als Bild und > erst recht nicht mit schwarzer Schrift auf grauem Grund! Ja, ich wollte mich an dem Startup-Code von ST orientieren. Sry wegen der Farben...habe ich nicht dran gedacht. Werde mich morgen weiter damit beschäftigen, für heute reichts. Gute Nacht
du kannst den Startup-Code auch in C schreiben, auch wenn das ein paar Fallstricke bietet. Ich hatte vor ein paar Jahren mal was von Bernd K. aus dem Forum auf den F103C8 angepasst, inklusive PLL-Setup: https://github.com/ChristianRinn/bare_metal_stm32f103c8/blob/master/src/STM32F103C8/gcc_startup_system.c In dem Repo sind auch noch ein paar Minimalbeispiele für Timer, GPIO und ADC drin. PS: Wenn du wissen willst was die Welt im innersten zusammenhält dann hol dir den "Definitive Guide to ARM Cortex-M3 and M4" von Joseph Yiu. Damit hast du die Chance zu verstehen was der Controller ab dem ersten Takt macht.
:
Bearbeitet durch User
Leopold N. schrieb: > Wäre nett, wenn "diese Leute" mir mal des Rätsels Lösung mitteilen > könnten Wie gesagt kannst du das dem Referenzhandbuch und den CMSIS Dateien entnehmen. Die Struktur der CMSIS Dateien ist von ARM Spezifiziert und kann daher als Teil der offiziellen Dokumentation betrachtet werden. Du brauchst dort auch keine Bugs zu befürchten. Die Bugs die hier diskutiert werden, beziehen sich auf Codeschnipsel der HAL und was der CubeMx Generator erzeugt. Die CMSIS ist hingegen ziemlich statisch. Übrigens bauen auch HAL und die ältere SPL auf CMSIS auf. Warum auch nicht? Wirklich alle ARM Controller von allen Herstellern werden mit einer CMSIS ausgeliefert, darauf kannst du dich verlassen. Ein ältere Ausgabe von dem hier so oft genannten YUI Buch findest du hier kostenlos: https://www.eecs.umich.edu/courses/eecs373/labs/refs/M3%20Guide.pdf Was den STM32F103 angeht ist dieses noch aktuell. Die aktuelle Fassung hat zusätzliche Infos für den Cortex M4, der STM32F103 ist aber ein Cortex M3.
Leopold N. schrieb: > (natürlich nicht so was gestörtes > wie USB oder so, aber zumindest die kleinen Dinge) Geht aber sogar: USB-Tutorial mit STM32 Hat hier eigentlich noch keiner die SVD-Files erwähnt? Das ist eine Sammlung maschinenlesbarer XML-Dateien mit allen Registerdefinitionen. Daraus kann man sich dann seine eigenen Header bauen. Die können dann genau so wie die stm32f103xx.h aussehen ohne die Tastatur komplett abzunutzen. Die sind m.W. in den Device Packs enthalten: https://www.keil.com/dd2/pack/ https://www.keil.com/cmsis/svd Wenn man es schon selbst macht wäre es etwas innovativer die Adressen per Linkerscript anzugeben:
1 | extern uint32_t GPIOA_BSRR; |
2 | |
3 | int main () { |
4 | GPIOA_BSRR = 0x1; |
5 | }
|
und im LD-Script:
1 | GPIOA_BSRR = 0x40010810; |
Dadurch hätte man sauberen standardkonformen C-Code ohne die hässlichen Casts und Makros. Besser natürlich per struct statt einzelnen Registern. Leopold N. schrieb: > Nächste Frage: Du hast Leerzeichen in den Dateinamen der Code-Files? Mutig...
:
Bearbeitet durch User
Niklas G. schrieb: > Wenn man es schon selbst macht wäre es etwas innovativer die Adressen > per Linkerscript anzugeben: > > extern uint32_t GPIOA_BSRR; > > int main () { > GPIOA_BSRR = 0x1; > } > > und im LD-Script: > GPIOA_BSRR = 0x40010810; Dann musst Du aber trotzdem noch einen riesen Header pflegen und um die ganzen externs zu beinhalten, am Ende pflegst Du die Registerdefinitionen dann in 2 gleich großen Dateien, pro Definition in jedem der beiden je eine Zeile, unterm Strich also nichts gewonnen, nur maximal exzentrisch gelöst und ein herzhaftes "WTF??" bei jedem Leser wäre vorprogrammiert. Im andern Fall (wie man es normalerweise macht) kann alles in einer einzigen Headerdatei gepflegt werden ohne alle Namen doppelt hinschreiben zu müssen. > standardkonformen C-Code ohne die hässlichen Casts und Makros. Casts und Makros sind standardkonform. Und hässlich müssen die auch nicht sein, die kann man sauber und geradeaus hinschreiben (so wie in den CMSIS-Headern bereits geschehen) ohne irgendwelche obskuren Verrenkungen zu machen. Die CMSIS-Header sind absolut sauber und gut lesbar, die haben absolut nichts hässliches an sich. Über die verkrampfte STM32 HAL und deren (Un)Nützlichkeit und (Un)Eleganz kann man trefflich streiten aber an den CMSIS Headern ist eingentlich absolut nichts auszusetzen, die verwende ich gerne.
:
Bearbeitet durch User
Bernd K. schrieb: > Dann musst Du aber trotzdem noch einen riesen Header pflegen und um die > ganzen externs zu beinhalten, Ja, das muss man dann automatisieren. Bernd K. schrieb: > unterm Strich also nichts gewonnen "Möglichst kompakt" war sowieso nie das Ziel... Bernd K. schrieb: > Casts und Makros sind standardkonform. Das Casten von beliebigen Zahlen auf Zeiger aber nicht. Durch die Auslagerung ins Linkerscript wird dies vor C versteckt. Dadurch kann man die Register-Zeiger auch an C++-templates übergeben, was bei Makros mit Casts nicht geht! Bernd K. schrieb: > Die CMSIS-Header sind absolut sauber und gut > lesbar, die haben absolut nichts hässliches an sich. Ich finde mehrfach verschachtelte Textersetzungen grundsätzlich hässlich. Bernd K. schrieb: > maximal exzentrisch gelöst Wenn man das Rad schon zum 100. Mal neu erfindet kann man wenigstens mal was neues probieren...
Niklas G. schrieb: > Das Casten von beliebigen Zahlen auf Zeiger aber nicht. Das ist wahrscheinlich Implementation-defined. Und die Untermenge der Implementierungen auf denen Adressen als Zahlen ausgedrückt werden können umfasst alle Implementierungen auf denen die betreffenden Register die hier definiert werden überhaupt existieren. Es handelt sich hier also um ein rein akademisches Nicht-Problem das sich bei genauerem Hinsehen in Luft auflöst.
Bernd K. schrieb: > Es handelt sich > hier also um ein rein akademisches Nicht-Problem das sich bei genauerem > Hinsehen in Luft auflöst. Kannst du dieses Problem dann bitte mal durch Hinsehen auflösen:
1 | #include <cstdint> |
2 | |
3 | #define GPIOA_BSRR (*((volatile uint32_t*) 0x40010810))
|
4 | |
5 | template <volatile uint32_t& Reg> |
6 | void writeReg (uint32_t val); |
7 | |
8 | int main () { |
9 | writeReg<GPIOA_BSRR> (42); |
10 | }
|
1 | test.cc: In function 'int main()': |
2 | test.cc:3:21: error: expression '*(volatile uint32_t*)1073809424' has side-effects |
Florain schrieb: > Steht im Datenblatt. > Aber warum machst Du das? Das gibt es doch schon fix und fertig von ST. Es ist im professionellen Umfeld durchaus üblich das Zeug selbst zu schreiben, weil ST einem nicht garantiert, dass die Werte stimmen. Die sollten zwar generiert sein , aber ich habe schon einige male (nicht bei ST) erlebt, dass Bits verrutscht waren. Stefanus F. schrieb: > Lade Dir doch das Cube HAL Firmware Paket für STM32F1 > herunter Jeder, der auf Cube verweist hat wohl selbst noch nie in den Code von Cube geschaut. Ein gravierender Fehler zum Beispiel: Es wird RAM benutzt, den es laut Datenblatt nicht gibt. Das funktioniert nur, weil STM den nicht hinaus-fused und jeder Controller immer den maximal verfügbaren seiner Familie enthält. STM sagt aber, dass sie die, nicht im Datenblatt angegebenen, Speicherbereiche nicht testen. Somit läuft fast jeder Code auf STM Controllern mit nicht getestetem RAM. Link zum Thema: Beitrag "SRAM Lücke STM32Fxxx, Linkerskripte falsch" Meines Wissens ist das bis heute nicht repariert.
M. H. schrieb: > aber ich habe schon einige male (nicht bei > ST) erlebt, dass Bits verrutscht waren. Beim STM32F373 war (ist?) das Conversion Done Bit des SD-ADC falsch definiert (als Overrun Bit) sodass man das erste Ergebnis verpasst und ggf. einen Zeitversatz erhält... Ob das allerdings auch in der SVD falsch ist weiß ich nicht.
M. H. schrieb: > Es ist im professionellen Umfeld durchaus üblich das Zeug selbst zu > schreiben Das übersteigt meine Vorstellungkraft.
> Stefanus F. schrieb: > Lade Dir doch das Cube HAL Firmware Paket für STM32F1 herunter M. H. schrieb: > Jeder, der auf Cube verweist hat wohl selbst noch nie in den Code von > Cube geschaut. Ich habe nicht auf Cube HAL verwiesen, sondern auf die CMSIS. Die kann man bei ST nicht mehr einzeln downloaden.
Stefanus F. schrieb: > M. H. schrieb: >> Es ist im professionellen Umfeld durchaus üblich das Zeug selbst zu >> schreiben > > Das übersteigt meine Vorstellungkraft. gerade, wenn deine software TÜV oder ander Sachen bekommen soll, musst du nachweisen, dass alles "prozesskonform" gemacht wurde. Das geht mit vordefinierten Files nicht. Deshalb müssen in so einem Fall die Entwickler selber ran.
Niklas G. schrieb: > Kannst du dieses Problem dann bitte mal durch Hinsehen auflösen: > #include <cstdint> > > #define GPIOA_BSRR (*((volatile uint32_t*) 0x40010810)) So (also mit eingebautem Dereferenzier-Operator) definiert man das üblicherweise auch nicht sondern man macht das sauber so wie das in den CMSIS Headern gelöst ist, dann bekommst Du einen sauberen Pointer und keinen Fetzen C mit einem Pointer zwischendrin.
:
Bearbeitet durch User
M. H. schrieb: > gerade, wenn deine software TÜV oder ander Sachen bekommen soll, musst > du nachweisen, dass alles "prozesskonform" gemacht wurde. Das geht mit > vordefinierten Files nicht. Deshalb müssen in so einem Fall die > Entwickler selber ran. Schreiben die sich auch ihren einene Compiler? Wie soll man sonst nachweisen, dass alles "konform" ist?
Stefanus F. schrieb: > Schreiben die sich auch ihren einene Compiler? Wie soll man sonst > nachweisen, dass alles "konform" ist? Das ist ein Knackpunkt an der Sache. Für STM verwendet man zum Beispiel nen Keil Compiler, der zugelassen ist. Gibt auch Hersteller, die gewisse geprüfte GCCs (natürlich für viel Geld) verkaufen. Generell gilt: Der Compiler ist aber auch nicht komplett zugelassen. Ein Klassiker ist, dass der Code -O0 kompiliert werden muss.
M. H. schrieb: > Das ist ein Knackpunkt an der Sache. Für STM verwendet man zum Beispiel > nen Keil Compiler, der zugelassen ist. Ja schön, sowit ich weiss liefert Keil aber auch einen Satz CMSIS Files dazu. Wenn man dem Compiler vertraut, dann kann man sicher auch den CMSIS Headern der selben Firma vertrauen, oder nicht? Siehe http://www2.keil.com/mdk5/cmsis/driver
Stefanus F. schrieb: > M. H. schrieb: >> Das ist ein Knackpunkt an der Sache. Für STM verwendet man zum Beispiel >> nen Keil Compiler, der zugelassen ist. > > Ja schön, sowit ich weiss liefert Kein aber auch einen Satz CMSIS Files > dazu. Wenn man dem Compiler vertraut, dann kann man sicher auch den > CMSIS Headern der selben Firma vertrauen, oder nicht? Kommt drauf an, ob die da eben die selben Zulassungen erfüllen. Wie gesagt: Es kann schon soweit gehen, dass nur ein einziger Aufruf eine Compilers zugelassen ist. Die einzige Variable ist dann das C-File.
M. H. schrieb: > Stefanus F. schrieb: >> M. H. schrieb: >>> Das ist ein Knackpunkt an der Sache. Für STM verwendet man zum Beispiel >>> nen Keil Compiler, der zugelassen ist. >> >> Ja schön, sowit ich weiss liefert Kein aber auch einen Satz CMSIS Files >> dazu. Wenn man dem Compiler vertraut, dann kann man sicher auch den >> CMSIS Headern der selben Firma vertrauen, oder nicht? > > Kommt drauf an, ob die da eben die selben Zulassungen erfüllen. Wie > gesagt: Es kann schon soweit gehen, dass nur ein einziger Aufruf eine > Compilers zugelassen ist. Die einzige Variable ist dann das C-File. Das sind ja echt schränge Anforderungen. Gut dass ich mich mit so etwas nicht herumschlagen muss.
Stefanus F. schrieb: > Das sind ja echt schränge Anforderungen. Gut dass ich mich mit so etwas > nicht herumschlagen muss. Ja. Aber in manchen Bereichen (Luftfahrt, Automobil, Bahn etc...) ist sowas üblich. Stell dir vor, du willst eine sichere Software (Brandmeldeanlage, Schinenleitsysteme, o.ä.) entwickeln und nutzt CubeMX. Das tut zwar irgendwie. Wenn man aber genau hinschaut, verwendet deine SW dann RAM, der nicht von ST getestet wurde, und den es laut Datenblatt nicht gibt. Das ist schon schwierig zu begründen, warum man das so macht. Und wenn du Pech hast, kommt das Ganze durch den TÜV/andere Kontrollinstanz und wird freigegeben und DANN merkst du, dass es falsch ist. Dann geht der Spaß erst richtig los. Dann darfst du das ändern und musst Auswirkungsanaysen etc machen. Das können dann mehrere Monate werden, bis da wieder alles im Lot ist. Und du hast natürlich das Problem, dass dein Prozess dann offenbar Murks ist. Ansich ist es ja nicht wichtig, dass du nachweist, dass es richtig gemacht wurde, sondern, dass es nach dem Prozess gemacht wurde. Bisschen schräg ist das schon manchmal.
M. H. schrieb: > Stefanus F. schrieb: >> M. H. schrieb: >>> Das ist ein Knackpunkt an der Sache. Für STM verwendet man zum Beispiel >>> nen Keil Compiler, der zugelassen ist. >> >> Ja schön, sowit ich weiss liefert Kein aber auch einen Satz CMSIS Files >> dazu. Wenn man dem Compiler vertraut, dann kann man sicher auch den >> CMSIS Headern der selben Firma vertrauen, oder nicht? > > Kommt drauf an, ob die da eben die selben Zulassungen erfüllen. Wie > gesagt: Es kann schon soweit gehen, dass nur ein einziger Aufruf eine > Compilers zugelassen ist. Die einzige Variable ist dann das C-File. Das ist ja lächerlich. So tief bücke ich mich nicht. Das sollen meinetwegen andere machen denen das eigene Ich nichts mehr wert ist, die haben doch nicht mehr alle Tassen im Schrank, wie kann man so tief sinken sowas freiwillig mitzumachen? Vollkommen lächerlich!
Bernd K. schrieb: > So (also mit eingebautem Dereferenzier-Operator) definiert man das > üblicherweise auch nicht sondern man macht das sauber so wie das in den > CMSIS Headern gelöst ist Stimmt; wenn man einen Pointer statt Referenz verwendet
1 | #include <cstdint> |
2 | |
3 | #define GPIOA_BSRR ((volatile uint32_t*) 0x40010810)
|
4 | |
5 | template <volatile uint32_t* Reg> |
6 | void writeReg (uint32_t val); |
7 | |
8 | int main () { |
9 | writeReg<GPIOA_BSRR> (42); |
10 | }
|
ist die Fehlermeldung eine Andere:
1 | test.cc: In function 'int main()': |
2 | test.cc:3:21: error: reinterpret_cast from integer to pointer |
3 | #define GPIOA_BSRR ((volatile uint32_t*) 0x40010810) |
4 | ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
5 | test.cc:10:11: note: in expansion of macro 'GPIOA_BSRR' |
6 | writeReg<GPIOA_BSRR> (42); |
7 | ^~~~~~~~~~ |
8 | test.cc:10:26: error: no matching function for call to 'writeReg<1073809424>(int)' |
9 | writeReg<GPIOA_BSRR> (42); |
10 | ^ |
11 | test.cc:7:6: note: candidate: template<volatile uint32_t* Reg> void writeReg(uint32_t) |
12 | void writeReg (uint32_t val); |
13 | ^~~~~~~~ |
14 | test.cc:7:6: note: template argument deduction/substitution failed: |
15 | test.cc:10:26: error: '(volatile uint32_t*)1073809424' is not a valid template argument for 'volatile uint32_t* {aka volatile long unsigned int*}' because it is not the address of a variable |
16 | writeReg<GPIOA_BSRR> (42); |
Bernd K. schrieb: > wie kann man so tief > sinken sowas freiwillig mitzumachen? Vollkommen lächerlich! Gibt wahrscheinlich gutes (Schmerzens-)Geld. Und wenn man nie vom süßen Nektar der modernen Programmierung gekostet hat, weiß man auch nicht was man verpasst...
Niklas G. schrieb: > Und wenn man nie vom süßen > Nektar der modernen Programmierung gekostet hat, weiß man auch nicht was > man verpasst... Was ist das? Etwa so: Hurra, mein Matlab Script funktioniert, wie bekomm ich die 2MB generierten Code jetzt auf den kleinen Cortex M0 auf der Schaltung die mitsamt Stecker in eine 30mm lange M8 Hülse passen muss und nicht mehr als 5mA ziehen darf? Geht nicht? OK - Ablage Rund. Undurchführbar. Nächtes Projekt! So modern ungefähr?
:
Bearbeitet durch User
Bernd K. schrieb: > Hurra, mein Matlab Script funktioniert, wie bekomm ich die 2MB > generierten Code Der Witz an C++ ist ja, dass es mindestens genauso effizient wie C ist (in Sachen Größe und Geschwindigkeit) und man dennoch sauberer abstrahieren und strukturieren kann.
Hallo, zum Thema sicherheitsrelevante Software: Entweder man verwendet eine zertifizierte Toolkette (und dann gibt es tatsächlich Einschränkungen, die aber inzwischen bei weitem nicht mehr so drastisch sind), oder man gestaltet die Toolkette so, dass z.B. Compiler und Linker nicht im kritischen Pfad sind (was mehr Teste des Compilats bedeutet). Selber Headerfiles schreiben kommt aber zumindest in der Automobilindustrie nicht (mehr) vor. Aerospace mag anders sein, hier wird dann aber der wirklich sicherheitsrelevante Code von verschiedenen Teams auf verschiedenen Architekturen mehrfach redundant implementiert und eine einzelne Zeile kann auch mal in die 5-Stelligen Euro-Regionen kommen (also z.B. Lageregler mit 100 Zeilen Code = 1 Mio Euro). Schöne Grüße, Martin
Bernd K. schrieb: > Das ist ja lächerlich. So tief bücke ich mich nicht. Das sollen > meinetwegen andere machen denen das eigene Ich nichts mehr wert ist, die > haben doch nicht mehr alle Tassen im Schrank, wie kann man so tief > sinken sowas freiwillig mitzumachen? Vollkommen lächerlich! Beispiel: du baust für einen Zug ein Schienenleitsystem. Du debugst das. Alles tut. Jetzt schaltest du den Compiler auf release. Alles tut weiterhin. Drei Jahre später verunglückt ein Zug mit mehreren Toten. Dann kommt raus, dass die SW aufgrund eines Compilerfehlers nicht richtig funktioniert hat. Da will ich als Entwickler lieber nicht Schuld sein. Deswegen der Aufwand. Ich sitze selbst häufiger in Verkehrsmittlen, in denen Code läuft, mit dem ich zumindest zu tun hatte. Teilweise ist das sehr beunruhigend. Wobei man sagen muss, dass es in der Luftfahrt eigentlich geht. Da läuft halt alles weiter, auch wenn's nen Fehler gab, weil ausgehen ist meist keine gute Option.
Bernd K. schrieb: > Niklas G. schrieb: >> Kannst du dieses Problem dann bitte mal durch Hinsehen auflösen: >> #include <cstdint> >> >> #define GPIOA_BSRR (*((volatile uint32_t*) 0x40010810)) > > So (also mit eingebautem Dereferenzier-Operator) definiert man das > üblicherweise auch nicht sondern man macht das sauber so wie das in den > CMSIS Headern gelöst ist, dann bekommst Du einen sauberen Pointer und > keinen Fetzen C mit einem Pointer zwischendrin. Aber genau so macht es doch das CMSIS, außer das anstatt dem uint32_t z.b. eine Struktur verwendet wird: [c] typedef struct { ... } NVIC_Type; #define SCS_BASE (0xE000E000UL) #define NVIC_BASE (SCS_BASE + 0x0100UL) #define NVIC ((NVIC_Type *) NVIC_BASE) [c] oder wie meinst du wäre es richtig?
M. H. schrieb: > Da will ich als Entwickler lieber nicht Schuld sein. So eine lustige Zertifizierung macht die Toten nicht lebendiger. Wie kommt man überhaupt auf die Idee, einen sicherheitsrelevanten Build nicht zu testen? Unterschiede zwischen Optimierungsstufen sind nun wirklich überhaupt keine Neuheit.
Adam P. schrieb: > Aber genau so macht es doch das CMSIS, außer das anstatt dem uint32_t > z.b. eine Struktur verwendet wird: Das CMSIS-makro ergibt einen Zeiger. Der andere Stil der mir nicht gefällt ergibt einen Zeiger an dem links gleich noch das Dereferenzier-Sternchen dran klebt. Ist Geschmackssache. Den CMSIS-Stil find ich weitaus eleganter.
Jemand schrieb: > Unterschiede zwischen Optimierungsstufen sind nun > wirklich überhaupt keine Neuheit. Unterschiede zwischen Optimierungsstufen werden von falschem Code ausgelöst. Anstatt aber die Ursache genau dort zu suchen und zu verhindern schreibt man lieber dem Compiler die Schuld zu oder tut so als ob der Code in Ordnung wäre nur weil er mit diesem einen zertifizierten Compiler mit genau diesen zertifizierten Einstellungen rein zufällig das gewünschte Ergebnis liefert. Dieses Verhaltensmuster ist tatsächlich keine Neuheit. Solche Trial-and-error Programmierer kennen wir alle zur Genüge. Und es ist daher ein trauriges Armutszeugnis für alle Zertifizierhanseln und für alle die davon überzeugt sind daß diese bizarre Ausgeburt der Lächerlichkeit der richtige Weg ist und dieses hilflose Herumgestochere im Nebel und Festklammern an bedeutungslosen Strohhalmen angesichts des offensichtlich bereits erfolgten Totalverlusts der Kontrolle auch noch irgendwie mit "professionell" assoziieren.
:
Bearbeitet durch User
Der richtige Weg beginnt mit einem vollständigen Konzept, dass nicht nur die geradeaus-Fälle beschreibt, sondern auch alle möglichen Fehler und Schwachpunkte. Es darf im Quelltext kein einziger "if" oder "case" ohne spezifiziertem Verhalten existieren. Man muss die Spezifikation also während der Entwicklung ergänzen. Denn nur dann kann man vollständig testen. Spezifizierte Arbeitsmittel können vollständige Tests nicht ersetzen.
Bernd K. schrieb: > Unterschiede zwischen Optimierungsstufen werden von falschem Code > ausgelöst. Nicht unbedingt! Rechnungen mit Gleitkommazahlen sind geradezu berüchtigt dafür, und das ist im Allgemeinen kein Fehler. Falsche Annahmen über die Rechnungen dagegen schon.
Bernd K. schrieb: > Unterschiede zwischen Optimierungsstufen werden von falschem Code > ausgelöst. Bei normalen Optimierungsstufen (-O2, -O3, usw.) stimmt das durchaus, allerdings gibt es einen interessanten Bug im arm-none-eabi-gcc, wodurch -flto redefinitionen von weak symbols ignoriert, wenn das weak symbol zuerst gelinkt wird (also die .o Datei in der das weak symbol definiert ist in der Kommandozeile vor der .o Datei steht, in der das symbol strong neu deklariert wird)
Alex D. schrieb: > Bei normalen Optimierungsstufen (-O2, -O3, usw.) stimmt das durchaus, > allerdings gibt es einen interessanten Bug im arm-none-eabi-gcc, wodurch > -flto redefinitionen von weak symbols ignoriert, wenn das weak symbol > zuerst gelinkt wird Ja, ich weiß, den hab ich damals gefunden und den zugehörigen Workaround veröffentlicht und die drei Bugreports bei Arm und Gcc und Launchpad miteinander verlinkt. Den meisten Nutzern von Standard-IDEs fiel das scheinbar nie auf, nur Leuten wie mir die sich für bestimmte Zwecke eigene Makefiles gestrickt haben. Und das ist ein offensichtlicher Bug und die knabbern anscheinend immer noch dran rum. Aber wenn er zuschlägt dann gleich so daß man es gar nicht übersehen kann. Und das geschieht übrigens nur wenn die .o-Datei mit den weak-Symbolen aus einer Assemblerdatei generiert wurde (im dem Fall typischerweise der Startup-Code). Weak-Symbole aus C-Kompilat kannst Du linken wie Du lustig bist.
:
Bearbeitet durch User
Bernd K. schrieb: > Den meisten Nutzern von Standard-IDEs fiel das > scheinbar nie auf, nur Leuten wie mir die sich für bestimmte Zwecke > eigene Makefiles gestrickt haben Mir fiel es in SystemWorkbench4STM32 auf, weil ich mir dachte, dass ich mal probiere, wie viel kleiner Code wird wenn man -flto noch einschaltet. Der ist dann auch deutlich kleiner geworden, hauptsächlich aber weil die Interrupt Handler nicht mehr im Programm waren. Bernd K. schrieb: > Und das ist ein offensichtlicher Bug > und die knabbern anscheinend immer noch dran rum Ja den Bug gibts ja schon ne ganze Zeit, da bin ich auch drauf gekommen. Bernd K. schrieb: > Und das geschieht > übrigens nur wenn die .o-Datei mit den weak-Symbolen aus einer > Assemblerdatei generiert wurde (im dem Fall typischerweise der > Startup-Code). Weak-Symbole aus C-Kompilat kannst Du linken wie Du > lustig bist. Das habe ich gar nicht bemerkt, habe weak symbols eigentlich nur wirklich für die Interrupt Handler im assembler startup file benutzt.
Niklas G. schrieb: > Hat hier eigentlich noch keiner die SVD-Files erwähnt? Das ist eine > Sammlung maschinenlesbarer XML-Dateien mit allen Registerdefinitionen. > Daraus kann man sich dann seine eigenen Header bauen. Die können dann > genau so wie die stm32f103xx.h aussehen ohne die Tastatur komplett > abzunutzen. Das ist definitiv ein vernünftiger Vorschlag und manche Frameworks arbeiten ja auch genau mit diesen SVDs, z.B. mit SVD2Rust. Ich hatte mir selber mal ein kleines Programm geschrieben was die Register als reine #defines erzeugt, damit man auch in Assembler damit Spaß haben kann ohne sich die Finger wund zu tippen. Komme leider heute Abend nicht mehr an den Rechner aber vielleicht lade ich es morgen mal bei Github hoch und verlinke das hier. Für C würde ich aber definitiv immer die CMSIS-Header nutzen. Die Problematik mit den Casts bei C++ Templates kannte ich so noch nicht. Fände ich mal interessant wie hier eine saubere und typsichere Lösung aussieht. Wären hier für die Bitdefinitionen nicht C++-Enums prädestiniert?
Christopher J. schrieb: > Fände ich mal interessant wie hier eine saubere und typsichere > Lösung aussieht. Hab ich doch gezeigt - mit "extern" und Adressdefinition im Linkerscript. Christopher J. schrieb: > Wären hier für die Bitdefinitionen nicht C++-Enums > prädestiniert? Enum class vielleicht, zwecks Typsicherheit. Oder eigene Klassentemplates, die die entsprechenden Operatoren typsicher überladen.
Der CubeMX Code ist so geil, dass er beim USB Host im IRQ an der Statemachine des RTOS Task was ändert und DANN ein Signal an den RTOS Task schickt. Das Signal dient nur zum task aufwachen, nicht zum message übertragen. Wenn der dann aber wegen was anderem schlief und dann mit einer vermurksten Statemachine aufwacht tut dieser interessante Dinge. zB wird dann beim Abziehen eines USB Sticks gleich wieder ein anstecken erkannt trotz leerer Buchse. Wenn man dann einen Stick wieder ansteckt hängt der USB Host. Ich nutz den ST HAL bzz deren Middlewars sehr ungerne genau wegen sowas. Aber einen USB Host wollt ich jetzt nicht selber schreiben und genau da ist dann son Kracher drinne ...
Niklas G. schrieb: > Enum class vielleicht, zwecks Typsicherheit. Oder eigene > Klassentemplates, die die entsprechenden Operatoren typsicher überladen. Ja genau das meinte ich. Es sollte durch den Compiler möglichst sichergestellt sein, dass ich keinen Müll in die Register schreibe, z.B. eine Bitdefinition des CR ins SR, etwa TIM1->SR = TIM_CR_CEN; sollte einen Fehler geben, funktioniert aber in C wunderbar. Das gleiche gilt für Libraries wie STs HAL: HAL_GPIO_TogglePin(GPIOA, 4); funktioniert wunderbar, toggelt aber nicht PA4, sondern PA3.
Christopher J. schrieb: > Ja genau das meinte ich. Da gibt's alle möglichen Varianten und auch vorhandene Ansätze, z.B.: https://github.com/kvasir-io/Kvasir
Alex D. schrieb: > Das habe ich gar nicht bemerkt, habe weak symbols eigentlich nur > wirklich für die Interrupt Handler im assembler startup file benutzt. Ich benutze das gerne für optionale Hook-Funktionen von Treibern. Zum Beispiel kann ich nem USB-Treiber beibringen bei USB-Traffic Blinkenlights blinken zu lassen indem ich ne Hook-Funktion implementiere die vom Treiber aufgerufen wird wenn geblinkt werden soll ohne daß der Treiber wissen muss wo und wie die LEDs angeschlossen sind oder wie man die ansteuert. Wenn ich sie nicht implementiere wird stattdessen eine leere weak Funktion aufgerufen die nichts tut und komplett samt Aufruf wegoptimiert wird und dann blinkt halt eben nichts. Den Mechanismus nutze ich für vieles. Manche hooks sind mandatory, dann gibts keinen weak default, manche sind optional, da existiert dann je eine leere weak default Implementierung. Die STM32-HAL nutzt solche weak hooks übrigens auch an vielen Stellen. Wenn man das so macht und somit die Hooks schon zur Compilezeit verdrahtet werden anstatt zur Laufzeit einen Funktionszeiger zu übergeben schrumpft das auch modulübergreifend so schön zusammen daß manchmal nur noch ein kleiner Fetzen Inline-Code im Aufrufer übrigbleibt. Die Vorwärtsdeklarationen für meine ganzen Hooks schreib ich alle in den jeweiligen Header des Moduls das sie aufruft in nem separaten Abschnitt, alle mit "modulname_hook_" im Namen und ein paar Worten Dokumentation dazu daß man sofort sieht was Sache ist.
:
Bearbeitet durch User
Bernd K. schrieb: > Ich benutze das gerne für optionale Hook-Funktionen von Treibern. Zum > Beispiel kann ich nem USB-Treiber beibringen bei USB-Traffic > Blinkenlights blinken zu lassen indem ich ne Hook-Funktion > implementiere die vom Treiber aufgerufen wird wenn geblinkt werden soll > ohne daß der Treiber wissen muss wo und wie die LEDs angeschlossen sind > oder wie man die ansteuert Das ist definitiv eine echt gute Anwendung Bernd K. schrieb: > Die STM32-HAL nutzt solche weak hooks übrigens auch an vielen Stellen. Die HAL benutze ich nicht, habe es einmal probiert und hab es gleich gelassen. Da bin ich bei den meisten Sachen wirklich schneller, wenn ich das auf Register Level schreibe, vor allem ist es dann weniger Doku zum Lesen, das Reference Manual ist ja schon genug. Die Idee den Umstieg zwischen unterschiedlichen Serien zu vereinfachen ist ja gut, nur leider ist es die Umsetzung mit STM32-Cube HAL meiner Meinung nach leider nicht.
Niklas G. schrieb: > Christopher J. schrieb: >> Ja genau das meinte ich. > > Da gibt's alle möglichen Varianten und auch vorhandene Ansätze, z.B.: > https://github.com/kvasir-io/Kvasir Genau diese Variante kenne ich schon, bin aber leider kein C++-Guru und blicke bei der ganzen Template-Magie nicht so wirklich durch. Außerdem finde ich die Syntax, naja, etwas aufgebläht. Elegant ist es jedenfalls in meinen Augen nicht und damit meine ich nicht den (ebenfalls per SVD) generierten Code für die Library an sich (der darf meinetwegen auch Ecken und Kanten haben), sondern den Code um diese Library dann schlussendlich zu nutzen. Mein Wunsch wäre etwas kompakterer und dafür verständlicherer Code, der trotzdem eine gewisse Typsicherheit bietet, also frei nach Pareto 80% des Nutzens mit 20% Aufwand ;)
Christopher J. schrieb: > Der restliche Kram der im Startup-Code gemacht wird, d.h. RAM nullen, > .data aus dem Flash ins RAM kopieren und die Interrupt-Vektoren anlegen, > sofern man denn Interrupts nutzen möchte. Wie geht das genau? Da brauche ich ja Assembler dafür, also muss ich eine .s Datei einbinden. Mach ich das mit #include oder brauch der Atollic Linker das irgendwie irgendwo anders? Grüße
Leopold N. schrieb: > Wie geht das genau? Schau Dir einfach mal einen Startup Code an. Ich habe Dir weiter oben bereits ein Beispielprojekt (http://stefanfrings.de/stm32/index.html#cmsis) empfohlen, wo du das schön sehen kannst. Dass du deine eigene Basis aufbauen willst habe ich inzwischen akzeptiert (meine meinung dazu kennst du). Aber deine Vorgehensweise halte ich für falsch. Du versuchst hier als Anfänger eine Basis aufzubauen, noch bevor das nötige Know-How vorhanden ist. Meine Empfehlung: Arbeite erstmal mit meinem eispielprojekt und schau Dir an, wie dieses funktioniert. Und dann nutze das für einige reale Aufbauten, bevor du das Rad neu erfindest. Das Projekt ist übrigens nichtmal mein eigenes Werk, sondern es wird genau so von der System Workbench generiert. Ich habe lediglich die CMSIS (aus einem HAL Firmware-Paket für die STM32F1 Serie extrahiert und) dazu kopiert, sowie die Namen in der Interrupt-Vektor vervollständigt. Also alles kein Hexenwerk.
Leopold N. schrieb: > Christopher J. schrieb: >> Der restliche Kram der im Startup-Code gemacht wird, d.h. RAM nullen, >> .data aus dem Flash ins RAM kopieren und die Interrupt-Vektoren anlegen, >> sofern man denn Interrupts nutzen möchte. > > Wie geht das genau? > Da brauche ich ja Assembler dafür, also muss ich eine .s Datei > einbinden. > Mach ich das mit #include oder brauch der Atollic Linker das irgendwie > irgendwo anders? Das geht in Assembler oder auch in C. Ein Beispiel wie das in C geht hatte ich bereits verlinkt, sogar für deinen Controller. Ein Beispiel wie es in Assembler geht bekommst du vom Hersteller (siehe Beschreibung von Stefan). Linkerscript und Startup-Code müssen zusammenpassen, da im Linker Symbole definiert werden (Start und Ende des RAM) auf die der Startup-Code Zugriff haben muss. Machst du das in C, wird die Datei kompiliert, machst du es in Assembler wird sie assembliert. In jedem Fall hast du am Ende eine .o-Datei, die der Linker dann in das fertige Binary packen kann.
Christopher J. schrieb: > du kannst den Startup-Code auch in C schreiben, auch wenn das ein paar > Fallstricke bietet. Ich hatte vor ein paar Jahren mal was von Bernd K. > aus dem Forum auf den F103C8 angepasst, inklusive PLL-Setup: > https://github.com/ChristianRinn/bare_metal_stm32f103c8/blob/master/src/STM32F103C8/gcc_startup_system.c Habe ich mir mal angesehen. Frage: Das erste was ausgeführt wird bei einem Reset ist doch der ResetHandler. Du nullst dann den RAM, kopierst die .data ins RAM und dann erst wartest du, dass HSI sich stabilisert hat. Aber der Prozessor braucht doch eine stabile Clock, um überhaupt erst die ersten beiden Aktionen durchführen zu können.
Leopold N. schrieb: > Aber der Prozessor braucht doch eine stabile Clock, um überhaupt erst > die ersten beiden Aktionen durchführen zu können. Darum muss sich wohl die Hardware selbst kümmern. Wie sonst soll er den Reset-Vektor ordentlich ausführen? Was du übersehen hast: Er aktiviert die Vervielfachung des Taktes mittels PLL, und das ist der Part, auf den er warten muss. Die PLL braucht eine Weile, um sich einzuschwingen, nicht der HSI Oszillator.
WEAK void SystemInit(void) { /* Enable Power Control clock -> see section 7.3.8 in the manual */ RCC->APB1ENR |= RCC_APB1ENR_PWREN; /* Wait for HSI to become ready */ while ((RCC->CR & RCC_CR_HSIRDY) == 0); /* Disable main PLL */ RCC->CR &= ~(RCC_CR_PLLON); /* Enable HSE */ RCC->CR |= RCC_CR_HSEON; /* Wait until PLL unlocked (disabled) */ while ((RCC->CR & RCC_CR_PLLRDY) != 0); /* Wait for HSE to become ready */ while ((RCC->CR & RCC_CR_HSERDY) == 0); Also bei mir wartet er darauf, dass HSI stabil wird. Wenn sich die HW drum kümmern würde, müsste er doch nicht warten oder?
Leopold N. schrieb: > Aber der Prozessor braucht doch eine stabile Clock, um überhaupt erst > die ersten beiden Aktionen durchführen zu können. Nein, prinzipiell kann man die Clock-Konfiguration komplett weglassen, auch dann funktioniert der Controller korrekt. Der HSI ist von Anfang an ohne PLL aktiv. Durch Takt-Konfiguration kann man den Takt erhöhen (PLL) und genauer machen (HSE mit Quarz o.ä.). Das ist für manche Peripherien erforderlich welche einen hohen/stabilen Takt benötigen. Der RAM und Prozessor funktionieren aber auch ohne. Rein technisch ist es unnötig auf "HSIRDY" zu warten - nach dem Reset ist das immer an. Das wird nur gebraucht wenn man den HSI aus- und wieder einschaltet; schaden tut es aber nicht. Man könnte auch die PLL-Konfiguration ganz zu Anfang vom ResetHandler anstoßen und parallel dazu den RAM initialisieren; kurz vor Aufruf der main() wartet man noch aufs Einschwingen. Dadurch würde man etwas Zeit sparen.
Leopold N. schrieb: > Also bei mir wartet er darauf, dass HSI stabil wird. Der HSI ist nach dem Reset immer ready, weil das ein simpler R/C Oszillator ist. Auf ihn per Software zu warten wäre auch sinnlos, weil er ja gerade diese Software taktet.
Niklas G. schrieb: > Rein technisch ist es unnötig > auf "HSIRDY" zu warten - nach dem Reset ist das immer an. Das wird nur > gebraucht wenn man den HSI aus- und wieder einschaltet; schaden tut es > aber nicht. Ja, das stimmt. Ich war vielleicht ein bisschen übervorsichtig und habe deshalb die Lösung mit Gürtel und Hosenträger gewählt ;) Leopold N. schrieb: > /* Wait for HSI to become ready */ > while ((RCC->CR & RCC_CR_HSIRDY) == 0); Den Part kann man bedenkenlos rauslöschen.
Nächste Frage: Du deklarierst ein paar extern Variablen, die im Linker definiert sind. Sind diese Variablen im Linker Pointer oder nur Integer? Das hier generiert bei mir nämlich einen Fehler: for (uint32_t *i = _bss_start_; i < _bss_end_; i++) { ... } Fehler: Warning: Comparison between pointer and integer. Außerdem: warum inkrementierst du (bei dir "dest") i vor dem Durchlauf der for-Schleife?
Würde dieser Code zum RAM nullen und Variablen kopieren passen? PS: Erstes Bild ignorieren, da hatte Atollic TrueStudio die eine Variable so übel markiert...
:
Bearbeitet durch User
Leopold N. schrieb: > Würde dieser Code zum RAM nullen und Variablen kopieren passen? Der Code knallt - aber heftig. Du greifst mit Deiner Variablen i, die Du in einen uint32_t-Pointer castest, auf ungerade Adressen zu. Hättest Du den Code nicht als Bild, sondern als ASCII-Code gepostet, hätte ich diejenigen Stellen, die Du falsch gemacht hast, korrigieren können. Aber soll ich Dein Bild erst abtippen? Nee.
Leopold N. schrieb: > Würde dieser Code zum RAM nullen und Variablen kopieren passen? > > PS: Erstes Bild ignorieren, da hatte Atollic TrueStudio die eine > Variable so übel markiert... Wie wäre es denn einfach mal mit Text statt Bildern? Die Forensoftware wirkt zwar von Anno Tobak aber die kann sogar Syntaxhighlighting (mit den richtigen Tags)!
Frank M. schrieb: > Der Code knallt - aber heftig. Du greifst mit Deiner Variablen i, die Du > in einen uint32_t-Pointer castest, auf ungerade Adressen zu. Hm hast Recht, das könnte passieren. Hab den Code aber zum größten Teil übernommen... https://github.com/ChristianRinn/bare_metal_stm32f103c8/blob/master/src/STM32F103C8/gcc_startup_system.c Anbei nochmal als ASCII.
So besser mit dem Zugriff? Habs einmal vergessen zu ersetzen... schon gemerkt.
:
Bearbeitet durch User
Leopold N. schrieb: > So besser mit dem Zugriff? Das ist C Quelltext, also nenne ihn auch so. Dann klappt das auch mit der Darstellung.
Leopold N. schrieb: > Außerdem: warum inkrementierst du (bei dir "dest") i vor dem Durchlauf > der for-Schleife? Das wird nicht vor der Schleife inkrementiert. In diesem Fall ist ++dest gleich mit dest++ ! Im übrigen bin ich mir ganz sicher, dass der Code macht was er soll. Leopold N. schrieb: > Fehler: Warning: Comparison between pointer and integer. Dann caste doch den Pointer zu einem Integer wenn du die vergleichst.
Leopold N. schrieb: > Also, hier nochmal als .c Hallelujah... Mach mal aus uint32_t i = _etext; besser uint32_t *i = _etext; und dann entsprechend *(uint32_t*) dest = *(uint32_t*) i; zu *(uint32_t*) dest = *i; Dann sollte es passen. Zur Erklärung: Wenn man ein uint32_t inkrementiert steigt der Wert der Variablen um eins. Wenn man einen Pointer auf uint32_t inkrementiert, erhöht sich die Adresse auf die der Pointer zeigt um vier. Damit zeigt der Pointer dann auf den nächsten uint32_t und eben nicht auf das nächste Byte!
:
Bearbeitet durch User
Christopher J. schrieb: > Mach mal aus > > uint32_t i = _etext; > > besser > > uint32_t *i = _etext; Hatte ich vorher...gibtn error wegen conversion without a cast.
Ich habs mittlerweile so. Aber jetzt krieg ich diesen Fehler: Multiple Definition of "Reset_Handler"
Leopold N. schrieb: > Aber jetzt krieg ich diesen Fehler: > Multiple Definition of "Reset_Handler" An der einen Datei alleine kann es nicht liegen.
Leopold N. schrieb: > Hatte ich vorher...gibtn error wegen conversion without a cast. Stimmt, den hatte ich vergessen. Leopold N. schrieb: > Ich habs mittlerweile so. > Aber jetzt krieg ich diesen Fehler: Da du ja TrueStudio zu nutzen scheinst, tippe ich mal darauf, dass irgendwo noch der originale Startup-Code von ST rumgeistert und der hat natürlich ebenfalls einen Reset_Handler
Junge Junge Junge, genau deswegen hasse ich IDEs. Anbei mal mein Project Explorer. Wo kann ich da denn die alte Startup Datei rausnehmen? Die startup.c ist meine.
:
Bearbeitet durch User
Schon wieder so ein doofer Screenshot! Es ist doch offensichtlich, dass die Antwort nicht dort zu sehen ist. Also lade doch einfach dein ganzes Projekt als *.zip hier hoch und dann können wir es uns anschauen anstatt um den heissen Brei herum zu raten.
Hier als ZIP. Ist aber noch lange nicht vollständig, also bitte keine Klagen darüber...
:
Bearbeitet durch User
In deiner Projektdatei .cproject steht drin:
1 | <option id="com.atollic.truestudio.ld.general.nostartfiles.531802630" name="Do not use standard start files" superClass="com.atollic.truestudio.ld.general.nostartfiles" useByScannerDiscovery="false" value="false" valueType="boolean"/> |
Deswegen bindet die IDE automatisch eigenen Startup-Code ein. Diese Einstellung ist sicher irgendwo in den Projekteinstellungen grafisch aufbereitet wieder zu finden. Oder du musst es beim Anlegen eines neuen Projektes einstellen. Eventuell einen anderen Projekt-Typ verwenden. Bei meinem bereits 2x empfohlenen System Workbench Beispielprojekt liegt der Startup Code als Assembler Quelltext (startup/startup_stm32.s) vor, wo du ihn leicht editieren kannst. Vielleicht bis du mit der System Workbench besser bedient, als mit TrueStudio.
Leopold N. schrieb: > Junge Junge Junge, genau deswegen hasse ich IDEs. Es zwingt dich niemand dazu TrueStudio zu nutzen. Da du ja sowieso alles zu Fuß machen willst, kannst du dir auch genauso gut ein Makefile schnappen und mit dem Editor deiner Wahl kombinieren. Ich habe ganz gute Erfahrungen mit QT-Creator gemacht (eigentlich ja auch eine IDE). Jedenfalls kann man damit auch debuggen und den restlichen Kram wie Syntaxhighlighting, Autovervollständigung, linting, usw. hat der auch und meiner Meinung nach alles ein bisschen besser als bei Eclipse-basierten IDEs, nur halt ohne klickibunti Projektassistent für STM32 aber das brauchst du ja sowieso nicht.
Hat jemand eine Ahnung, wo dieses Startup Script ist, denn ich kenne mich mit der IDE noch nicht so wahnsinnig aus.
Leopold N. schrieb: > Hat jemand eine Ahnung, wo dieses Startup Script ist Es gibt kein Startup-Script. Es gibt Startup-Code und ein Linkerscript.
Durchsuche doch mal die Projekteinstellungen nach "Do not use standard start files". Das ist natürlich schon ein fieses Ding, wenn da MaschinenCode aus Quelltexten erzeugt wird, die nicht Bestandteil deines Projektes sind. Wie will man damit reproduzierbare Ergebnisse erreichen? So muss man nach jedem Upgrade fürchten, dass irgend etwas in den eigenen Projekten kaputt geht. Finde ich nicht gut, wie TrueStudio das handhabt.
Kann dir nicht sagen wo sie liegt aber vermutlich heißt sie "startup_stm32f103xb.s". Die Dateiendung ist jedenfalls ziemlich sicher .s oder .S. Warum folgst du nicht dem Vorschlag von Stefan und schmeißt sie aus dem Projekt raus? Dann ist es doch völlig Wurst wo die Datei liegt. Wenn nicht, dann nutze sie doch einfach wie sie ist.
Stefanus F. schrieb: > Das ist natürlich schon ein fieses Ding, wenn da MaschinenCode aus > Quelltexten erzeugt wird, die nicht Bestandteil deines Projektes sind. > Wie will man damit reproduzierbare Ergebnisse erreichen? So muss man > nach jedem Upgrade fürchten, dass irgend etwas in den eigenen Projekten > kaputt geht. Im Prinzip ist das aber bei der avr-gcc toolchain ganz genauso. Natürlich brauchen auch die AVR einen Startup-Code und ein Linker-Script haben sie auch aber es ist halt alles Bestandteil des avr-gcc und der sucht sich dann die passenden Dateien anhand des angegebenen Controllers raus. Nichtsdestotrotz gebe ich dir aber recht. Es vernebelt die Sache unnötig.
Christopher J. schrieb: > Leopold N. schrieb: >> Junge Junge Junge, genau deswegen hasse ich IDEs. > > Es zwingt dich niemand dazu TrueStudio zu nutzen. Da du ja sowieso alles > zu Fuß machen willst, kannst du dir auch genauso gut ein Makefile > schnappen und mit dem Editor deiner Wahl kombinieren. Ich habe ganz gute > Erfahrungen mit QT-Creator gemacht (eigentlich ja auch eine IDE). > Jedenfalls kann man damit auch debuggen und den restlichen Kram wie > Syntaxhighlighting, Autovervollständigung, linting, usw. hat der auch > und meiner Meinung nach alles ein bisschen besser als bei > Eclipse-basierten IDEs, nur halt ohne klickibunti Projektassistent für > STM32 aber das brauchst du ja sowieso nicht. Klingt gut. Christopher J. schrieb: > Kann dir nicht sagen wo sie liegt aber vermutlich heißt sie > "startup_stm32f103xb.s". Die Dateiendung ist jedenfalls ziemlich sicher > .s oder .S. Warum folgst du nicht dem Vorschlag von Stefan und schmeißt > sie aus dem Projekt raus? Dann ist es doch völlig Wurst wo die Datei > liegt. Wenn nicht, dann nutze sie doch einfach wie sie ist. Habe ich bereits. Aber scheinbar ist sie irgendwie immer noch drin. Ich werde jetzt mal QT ausprobieren denke ich. Atollic ist einfach zu viel undurchsichtiger Schnickschnack meiner Ansicht nach.
Christopher J. schrieb: > Im Prinzip ist das aber bei der avr-gcc toolchain ganz genauso. Ganz Recht, und deswegen habe ich in meine Kommerziellen Projekt-ZIP Files immer gleiche die ganze Toolchain mit eingepackt. > Ich werde jetzt mal QT ausprobieren denke ich. Ich empfehle Dir erneut die System Workbench, die erzeugt ein sichtbares Startup File, dass du ändern und löschen kannst.
Stefanus F. schrieb: > Ich empfehle Dir erneut die System Workbench, die erzeugt ein sichtbares > Startup File, dass du ändern und löschen kannst. Das wäre auf jeden Fall die einfachere Variante ;)
Stefanus F. schrieb: > Ich empfehle Dir erneut die System Workbench, die erzeugt ein sichtbares > Startup File, dass du ändern und löschen kannst. Da du mir ja wohl nie Ruhe lassen wirst mit deiner Empfehlung, installiere ich mir nebenbei noch SW4STM32 und vergleiche
:
Bearbeitet durch User
Lösch das Projekt aus Eclipse und füge es als makefile projekt wieder hinzu. Dann wird da kein Hokuspokus mehr automatisch heimlich im Hintergrund hinzugelinkt sondern es ruft einfach nur make auf und Du hast volle Kontrolle.
Alex D. schrieb: > Bernd K. schrieb: >> Unterschiede zwischen Optimierungsstufen werden von falschem Code >> ausgelöst. > > Bei normalen Optimierungsstufen (-O2, -O3, usw.) stimmt das durchaus, > allerdings gibt es einen interessanten Bug im arm-none-eabi-gcc, wodurch > -flto redefinitionen von weak symbols ignoriert, wenn das weak symbol > zuerst gelinkt wird (also die .o Datei in der das weak symbol definiert > ist in der Kommandozeile vor der .o Datei steht, in der das symbol > strong neu deklariert wird) Offenbar bin ich Jahre später noch in dasselbe Problem gerannt. Wo sind die URLs dazu, gibt's Lösungen dazu? Ich verwende CubeMX auf stm32f7 und wollte die UART ISR mit dem LL API überschreiben ... startup_stm32f722xxs:
1 | .weak USART3_IRQHandler |
2 | .thumb_set USART3_IRQHandler,Default_Handler |
stm32f7xx it.c:
1 | void USART3_IRQHandler(void) |
2 | {
|
3 | /* USER CODE BEGIN USART3_IRQn 0 */
|
4 | ...
|
5 | }
|
serial.cpp
1 | extern "C" void USART3_IRQHandler(void) |
2 | {
|
3 | USART3_RxISR(); |
4 | USART3_TxISR(); |
5 | }
|
1 | [build] C:/Program Files/STMicroelectronics/STM32CubeCLT/GNU-tools-for-STM32/bin/../lib/gcc/arm-none-eabi/12.3.1/../../../../arm-none-eabi/bin/ld.exe: CMakeFiles/project.dir/Core/Src/stm32f7xx_it.c.obj: in function `USART3_IRQHandler': |
2 | ...
|
3 | multiple definition of `USART3_IRQHandler' |
:
Bearbeitet durch User
Du definierst USART3_IRQHandler() zwei mal. Ein mal in stm32f7xx it.c und ein mal in der serial.cpp. Da ist es kein Wunder, wenn der Linker sich beschwert. Wenn du die IRQ Funktion tatsächlich selber schreiben willst, dann musst du die automatische Generierung in der _it abschalten und die Funktion dort löschen (die ist nämlich nicht weak).
Mike R. schrieb: > Du definierst USART3_IRQHandler() zwei mal. > > Ein mal in stm32f7xx it.c und ein mal in der serial.cpp. Da ist es kein > Wunder, wenn der Linker sich beschwert. Unter normalen (nicht weak) Umständen ist das richtig. > Wenn du die IRQ Funktion tatsächlich selber schreiben willst, dann musst > du die automatische Generierung in der _it abschalten und die Funktion > dort löschen (die ist nämlich nicht weak). Hier werde ich unsicher, das asm Startup File definiert das ja als weak Symbol; unabhängig davon, ob das Symbol in stm32f7xx it.c als 'waek' (__weak oder GCC attribute) oder nicht deklariert wird. Soweit mein Verständnis - so tief bin ich aber auch nicht in Mixing Asm/C vorgedrungen.
Olaf .. schrieb: > Hier werde ich unsicher, das asm Startup File definiert das ja als weak > Symbol; unabhängig davon, ob das Symbol in stm32f7xx it.c als 'waek' > (__weak oder GCC attribute) oder nicht deklariert wird. Das heißt aber nur, dass die Definition aus dem startup File "weak" ist. Die beiden Definitionen in stm32f7xx it.c und serial.cpp sind "strong". Wenn du nur eine strong Definition hättest, würde diese die "weak" Definition überschreiben. Aber du hast 2. Was ist überhaupt das Ziel? Warum hast du 2 Definitionen der Funktion? Welche soll davon ausgeführt werden? Olaf .. schrieb: > so tief bin ich aber auch nicht in Mixing Asm/C > vorgedrungen. Damit hat das übrigens nichts zu tun, die Logik mit weak/strong ist Sache des Linkers, das funktioniert zwischen C, C++ und ASM äquivalent.
:
Bearbeitet durch User
Niklas G. schrieb: > Olaf .. schrieb: >> Hier werde ich unsicher, das asm Startup File definiert das ja als weak >> Symbol; unabhängig davon, ob das Symbol in stm32f7xx it.c als 'waek' >> (__weak oder GCC attribute) oder nicht deklariert wird. > > Das heißt aber nur, dass die Definition aus dem startup File "weak" ist. > Die beiden Definitionen in stm32f7xx it.c und serial.cpp sind "strong". > Wenn du nur eine strong Definition hättest, würde diese die "weak" > Definition überschreiben. Aber du hast 2 das sehe ich ein. > Was ist überhaupt das Ziel? Warum hast du 2 Definitionen der Funktion? > Welche soll davon ausgeführt werden? Ziel ist es möglichst wenig vom STM's CubeMX generierten Source anzufassen und auf meine eigene Quellen zu verwenden. Mit den LowLevel (LL) API von CubeMX werden einige Stubs generiert, manche sind sinnvoll, andere nicht für mich hier. > Olaf .. schrieb: >> so tief bin ich aber auch nicht in Mixing Asm/C >> vorgedrungen. > > Damit hat das übrigens nichts zu tun, die Logik mit weak/strong ist > Sache des Linkers, das funktioniert zwischen C, C++ und ASM äquivalent. Genau, daher dachte ich ursprünglich, ich habe den Bug getroffen :)
Olaf .. schrieb: > Ziel ist es möglichst wenig vom STM's CubeMX generierten Source > anzufassen und auf meine eigene Quellen zu verwenden. Dann verwende den Code-Generator doch einfach überhaupt nicht und schreibe alles selber?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.