Forum: Mikrocontroller und Digitale Elektronik C-Code STM32 Fragen


von w.e. (Gast)


Lesenswert?

Hallo, in einem STM32 Tutorial bereitet mir die C-Programmiersprache an 
einigen Stellen Kopfzerbrechen:

1
void (* const table_interrupt_vector[])(void) =
2
{
3
  (void *)0x20000800,   // 0 - stack
4
  reset_handler,      // 1 - reset handler
5
  default_handler,   // 2 - NMI handler
6
  default_handler,   // 3 - Hardfault handler
7
};
Was ist das für eine Parameterliste:
1
(* const table_interrupt_vector[])
 und warum steht direkt dahinter nochmal void in Klammer:
1
(void)

Was bedeutet das hier:
1
(void *)0x20000800,   // 0 - stack

Was bedeutet das Konstrukt *(int*) in der Zeile:
1
*(int *)0x40021014 |=  (0x01 <<17U);



Brauche ich unbedingt ein C-Tutotial oder ist der Code nur etwas 
speziell?

von Quelle (Gast)


Lesenswert?

w.e. schrieb:

> Brauche ich unbedingt ein C-Tutotial ...

Ja.

von jo mei (Gast)


Lesenswert?

w.e. schrieb:
> Was ist das für eine Parameterliste:(* const table_interrupt_vector[])
> und warum steht direkt dahinter nochmal void in Klammer:(void)

Das ist keine Parameterliste sondern eine Deklaration eines
Arrays von Pointern, hier etwas spezieller eine Liste von
Adressen von auführbaren Funktionen. () wäre die Liste von
Übergabewerten für die Funktion(en) die bei dieser
Deklaration leer ist.

von jo mei (Gast)


Lesenswert?

w.e. schrieb:
> Brauche ich unbedingt ein C-Tutotial

Quelle schrieb:
> Ja.

Bin ich auch der Meinung, jedoch wird man bei einem Lehrbuch
nicht zwingend und unmittelbar auf dieses seltener vorkommende
Konstrukt hingewiesen.

von w.e. (Gast)


Lesenswert?

Könnt ihr etwas taugliches empfehlen? Ich fange ja nicht bei NULL in C 
an, Grundlegende Dinge behersche ich, aber ab Zeiger hört es bei mir 
auf. Es darf gerne ein Schnelltutorial sein.

von Stefan F. (Gast)


Lesenswert?

jo mei schrieb:
> Bin ich auch der Meinung, jedoch wird man bei einem Lehrbuch
> nicht zwingend und unmittelbar auf dieses seltener vorkommende
> Konstrukt hingewiesen.

Doch ganz sicher. Weil Zeiger und Arrays zu den Grundlagen der 
Programmiersprache gehören.

Vielleicht kommst du mit einer Praxisorientierten Anleitung besser klar: 
http://stefanfrings.de/mikrocontroller_buch/Einstieg%20in%20die%20Elektronik%20mit%20Mikrocontrollern%20-%20Band%201.pdf 
Kapitel 4, und die Fortsetzung in band 2 Kapitel 11

Die bezieht sich allerdings auf AVR Mikrocontroller, anstatt auf STM32. 
Wobei die Programmiersprache dort ja die gleiche ist.

von Leo (Gast)


Lesenswert?

Hallo w.e.
du bist hier ja schon an der richtigen Stelle (Forum). Schau dir doch 
mal die Artikel hier über STM32. Davon hat es hier genügend und dazu 
noch reichlich Links.

https://www.mikrocontroller.net/articles/STM32
https://www.mikrocontroller.net/articles/STM32_f%C3%BCr_Einsteiger
https://diller-technologies.de/stm32_wide.html
http://stefanfrings.de/stm32/index.html
http://www.pomad.fr/node/2

Bei konkreten Fragen, einfach hier im Forum suchen oder posten.

Guten Gelingen
Leo

von jo mei (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Doch ganz sicher.

Ein Array von Funktionspointern sicher nicht. Auf genau das
hat sich meine Aussage bezogen.

von foobar (Gast)


Lesenswert?

> Grundlegende Dinge behersche ich, aber ab Zeiger hört es bei mir auf.

Dummerweise gehören Zeiger bei C zu den absoluten Grundlagen - bereits 
im Hello-World sind sie zu finden.

von Stefan F. (Gast)


Lesenswert?

Die Programmiersprache C (und noch mehr C++) enthält eine ganze Menge 
Feinheiten, die man als Anfänger nicht wissen muss. Das Lesen von 
fremden Quelltexten erfordert aber, dass du alles kennst, was dessen 
Autor kennt und ist daher eher eine Aufgabe für erfahrene Entwickler.

> Ein Array von Funktionspointern sicher nicht.

Ja, über die Zeile
1
void (* const table_interrupt_vector[])(void) = {...}

Musste ich gerade auch eine ganz Weile grübeln. So etwas sieht man nicht 
jeden Tag.

Die Programmiersprachen C und C++ kommen von Professoren, die an 
Universitäten lehren. Solche Professoren stehen auch auf 
Selbstgeisselung mit Matheaufgaben. Aber da müssen wir durch.

von Johannes S. (Gast)


Lesenswert?

Hier ist ein Artikel mit Hilfe zum lesen solcher Deklarationen:
https://www.mikrocontroller.net/articles/Deklarationen_in_C

Oder
https://www.ostc.de/c-declaration.pdf

Das gezeigte Stück Code ist etwas spezielles weil sehr hardwarenah. Da 
muss der Compiler schon genau das produzieren was der Prozessor 
erwartet. Wenn die Ausdrücke zu komplex werden kann man die aber auch 
mit typedefs entschärfen. Hat eigentlich nix mit Geisselung zu tun.

von Rolf M. (rmagnus)


Lesenswert?

Zum GCC gehört auch das Tool, das solche Sachen in Klartext umwandelt.
Scheint bei mir allerdings einen Bug zu haben. Es gibt aus:
1
declare ��F!V as array of const pointer to function that expects (void) returning void;
Der Name ist irgendwie kaputt gegangen.

von leo (Gast)


Lesenswert?

Rolf M. schrieb:
> Zum GCC gehört auch das Tool, das solche Sachen in Klartext
> umwandelt.
> Scheint bei mir allerdings einen Bug zu haben. Es gibt aus:declare ��F!V
> as array of const pointer to function that expects (void) returning
> void;
> Der Name ist irgendwie kaputt gegangen.

Wollte ich auch grad posten. Bei mir geht's:
1
$ cdecl explain "void (* const table_interrupt_vector[])(void)"
2
declare table_interrupt_vector as array of const pointer to function (void) returning void

leo

von A. S. (Gast)


Lesenswert?

Üblicherweise wird das mit einem typedef gemacht, dann ist es viel 
einfacher zu verstehen:

Typedef für den Funktionspointer, und dann halt ein Array von diesem 
Typ.

Das ist so selten, dass die meisten nicht auf Anhieb wissen, wo welche 
klammern hingehören.

von W.S. (Gast)


Lesenswert?

w.e. schrieb:
> Was ist das für eine Parameterliste:

Ganz einfach: Die µC der Cortex-Liga erwarten ab Reset, daß sie ab 
Adresse 0 eine Tafel vorfinden. Die 2 allerersten Einträge sind der 
Startwert für den Stackpointer und die Adresse des ersten Befehls, den 
die CPU ausführen soll, also der Kaltstart.

Danach kommen die Adressen der diversen Interrupt- und 
Exception-Handler.

Normalerweise schreibt man sowas in einer Assembler-Quelle und markiert 
das betreffende Segment als absolut auf Adresse 0 zu linken.

Aber da die werten C-Programmierer ja eine abgrundtiefe Abscheu gegen 
Assembler in jeglicher Form haben, sind die Hersteller darangegangen, 
mit passenden Verrenkungen eben diese Adresstafel nebst Kaltstartcode in 
C zu formulieren.

Und das Ergebnis siehst du gerade vor dir.
also zuerst in C:
1
  (void *)0x20000800,   // 0 - stack
2
  reset_handler,      // 1 - reset handler
3
  default_handler,   // 2 - NMI handler
4
  default_handler,   // 3 - Hardfault handler

dann in Assembler:
1
    AREA    RESET, CODE, READONLY
2
    PRESERVE8
3
    THUMB
4
    EXPORT  __Vectors
5
;                                    Addr Vect Int Typ
6
__Vectors   DCD   __initial_sp       ; 0      0   -  Top of Stack
7
            DCD   Kaltstart          ; 4      1   -  Reset Handler
8
            DCD   NMI_Handler        ; 8      2   -  Non Maskable Interrupt
9
            DCD   HardFault_Handler  ; C      3   -  Cortex-M4 SV Hard Fault Interrupt

Und nun ist der Startwert für den Stackpointer (hier0x20000800) eben 
keine Funktionsangabe, sondern ein Wert, der so wie er ist in den SP 
gehört. Das ist ein wenig anders als bei der klassischen Adressierung 
bei ARM: dort werden ARM- und THUMB- Code auch im Aufruf unterschieden, 
was man am gesetzten LSB erkennt. Daher die für dich verwirrende 
Darstellung per Cast (void*)

Tja, das ist eigentlich alles zum Thema. Wahrscheinlich fragst du dich 
irgendwann mal, was [WEAK] zu bedeuten hat, aber das ist nur ein 
Nachbarthema zu diesem.

W.S.

von Carl D. (jcw2)


Lesenswert?

> Aber da die werten C-Programmierer ja eine abgrundtiefe Abscheu
> gegen Assembler in jeglicher Form haben, sind die Hersteller
> darangegangen, mit passenden Verrenkungen eben diese Adresstafel
> nebst Kaltstartcode in C zu formulieren.

Falsch! Weil der HW-Designer die Exception/Interrupt-Behandlung so 
gebaut hat, daß die C/C++-Aufraukonventionen eingehalten werden, kann 
man auf ASM-Verrenkungen verzichten.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

W.S. schrieb:
> Aber da die werten C-Programmierer ja eine abgrundtiefe Abscheu gegen
> Assembler in jeglicher Form haben, sind die Hersteller darangegangen,
> mit passenden Verrenkungen eben diese Adresstafel nebst Kaltstartcode in
> C zu formulieren.

Mal wieder keine Ahnung von nichts, aber ne große Klappe und daher die 
falschen Schlüsse gezogen.
Frag dich mal wieso der NVIC genau die Register sichert, die er sichert.

von Rolf M. (rmagnus)


Lesenswert?

Im übrigen ist der Code in C eigentlich so gar nicht erlaubt, auch wenn 
er von so ziemlich jedem Compiler trotzdem akzeptiert wird.

von max123 (Gast)


Lesenswert?

Hallo,

>> (void *)0x20000800,

Der Stern macht die Zahl zu einem Zeiger;

>> void
void bewirkt dass der Zeiger vom Type void ist.

Anmerkung:
Bei einem 8 Bit Mikrocontroller bewirkt ein "inc Zeiger" um 1 Byte.
Bei einem 32 Bit Mikro greift man mit "inc Zeiger" um 4 Positionen
weiter zu.

von Nick M. (Gast)


Lesenswert?

w.e. schrieb:
> Ich fange ja nicht bei NULL in C
> an, Grundlegende Dinge behersche ich, aber ab Zeiger hört es bei mir
> auf.

Entschuldigung, aber das mit dem NULL und "keine Ahnung von pointern" 
war leider ein ziemlicher Witz (den C-Programmierer verstehen).

von Dirk B. (dirkb2)


Lesenswert?

max123 schrieb:
>>> void
> void bewirkt dass der Zeiger vom Type void ist.

Nein. Das ist dann der Typ „Zeiger auf void“

> Anmerkung:
> Bei einem 8 Bit Mikrocontroller bewirkt ein "inc Zeiger" um 1 Byte.
> Bei einem 32 Bit Mikro greift man mit "inc Zeiger" um 4 Positionen
> weiter zu.

Nein, das ist großer Müll was du da schreibst.
Das Inkrement hängt vom Typ ab, auf den der Zeiger zeigt, nicht von 
der Architektur.

Das Inkrement ist sizeof(*Zeiger)

von Orakel (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Die Programmiersprachen C und C++ kommen von Professoren, die an
> Universitäten lehren. Solche Professoren stehen auch auf
> Selbstgeisselung mit Matheaufgaben. Aber da müssen wir durch.

Müssen wir dieses Gesülze von dir jetzt eigentlich jedes Mal lesen? 
Dennis Ritchie und Ken Thompson mögen zwar beide Akademiker sein, aber 
es gibt einen guten Grund, weshalb sich C schnell zum 
Industriestandard gemausert hat. Es wurde direkt zur Entwicklung von 
Unix geschaffen. Es war kein typisches akademisches Hobbyprojekt.

Besorg dir endlich mal ordentliche Lektüre zu C und zu C++ und lies es! 
Die meisten Konzepte in Programmiersprachen sind weder kompliziert, noch 
haben sie den Zweck den Anwender zu ärgern. Aber gut, man muss es halt 
auch zulassen. Eigentlich erschreckend, dass du trotzdem Tutorials für 
Anfänger schreibst (auch wenn ich so etwas allgemein für sehr ehrbar 
finde).

von Dirk B. (dirkb2)


Lesenswert?

Stefan ⛄ F. schrieb:
> Die Programmiersprachen C und C++ kommen von Professoren, die an
> Universitäten lehren.

Zumindest auf C trifft dies nicht zu.
K&R haben C erstmal für sich selber geschrieben. Als Werkzeug für 
Profis.
Und das ist es immer noch.

von Carl D. (jcw2)


Lesenswert?

Dirk B. schrieb:
> Stefan ⛄ F. schrieb:
>> Die Programmiersprachen C und C++ kommen von Professoren, die an
>> Universitäten lehren.
>
> Zumindest auf C trifft dies nicht zu.
> K&R haben C erstmal für sich selber geschrieben. Als Werkzeug für
> Profis.
> Und das ist es immer noch.

Und Bjarne saß (später) auf einem Flur mit den/einem der beiden. Beide 
Sprachen wurden von Programmierern gemacht.
Professorensprache ist z.B. Pascal.

von MaWin (Gast)


Lesenswert?

Dirk B. schrieb:
> Als Werkzeug für
> Profis.
> Und das ist es immer noch.

LOL!!!

Du beschreibst das Problem perfekt: C Programmierer glauben etwas 
besseres zu sein; typische Selbstüberschätzung. ???

von P. S. (namnyef)


Lesenswert?

MaWin schrieb:
> LOL!!!
>
> Du beschreibst das Problem perfekt: C Programmierer glauben etwas
> besseres zu sein; typische Selbstüberschätzung. ???

Ne. Das bedeutet nur, dass man mit C viel Unsinn machen kann :)

von Hermann K. (r2d2)


Lesenswert?

Rolf M. schrieb:
> Im übrigen ist der Code in C eigentlich so gar nicht erlaubt, auch wenn
> er von so ziemlich jedem Compiler trotzdem akzeptiert wird.

Was genau ist da nicht erlaubt? Die C++-Kommentare? Sind seit C99 im 
Standard. Ansonsten seh ich nix.

von Dirk B. (dirkb2)


Lesenswert?

MaWin schrieb:
> Du beschreibst das Problem perfekt: C Programmierer glauben etwas
> besseres zu sein;

Nein. C ist in der Anwendung nicht so einfach, wie es der Sprachumfang 
und die Verbreitung erscheinen läßt.

Man ist als Programmierer völlig ungeschützt unterwegs und kann so 
ziemlich jeden Unsinn beim Compiler durchbekommen.

Und meine Aussage bedeutet auch nicht, dass jeder der in C programmiert 
ein Profi ist.

von Rolf M. (rmagnus)


Lesenswert?

Hermann K. schrieb:
> Rolf M. schrieb:
>> Im übrigen ist der Code in C eigentlich so gar nicht erlaubt, auch wenn
>> er von so ziemlich jedem Compiler trotzdem akzeptiert wird.
>
> Was genau ist da nicht erlaubt? Die C++-Kommentare? Sind seit C99 im
> Standard. Ansonsten seh ich nix.

Die Konvertierung eines void* in einen Funktionszeiger ist eigentlich 
nicht vorgesehen, allerdings immerhin unter "portability issues" - 
"common extensions" gelistet. Da aber auch nur, um das, worauf er zeigt, 
aufrufen zu können, wie es z.B. oft beim dynamischen Nachladen von 
Bibliotheken gemacht wird, oder um den Inhalt des Funktionscodes 
betrachten zu können.

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


Lesenswert?

w.e. schrieb:
> void (* const table_interrupt_vector[])(void) =
> {
>   (void *)0x20000800,   // 0 - stack
>   reset_handler,      // 1 - reset handler
>   default_handler,   // 2 - NMI handler
>   default_handler,   // 3 - Hardfault handler
> };

hier eine typedef-variante (ungetested)
1
/* typedef eines Funktionspointers. Hier ist der Typname "innen" */
2
typedef void (* ivector_type)(void);
3
4
/* jetzt das Array dieser Funktionspointer. Hier ist alles wie immer */
5
const ivector_type table_interrupt_vector[]=
6
{
7
  (ivector_type) 0x20000800, // 0 - stack
8
  reset_handler,             // 1 - reset handler
9
  default_handler,           // 2 - NMI handler
10
  default_handler,           // 3 - Hardfault handler
11
};

von Axel S. (a-za-z0-9)


Lesenswert?

Rolf M. schrieb:
> Hermann K. schrieb:
>>
>> Was genau ist da nicht erlaubt? Die C++-Kommentare? Sind seit C99 im
>> Standard. Ansonsten seh ich nix.
>
> Die Konvertierung eines void* in einen Funktionszeiger ist eigentlich
> nicht vorgesehen, allerdings immerhin unter "portability issues" -
> "common extensions" gelistet. Da aber auch nur, um das, worauf er zeigt,
> aufrufen zu können

Das passiert hier aber gar nicht. Der erste Tabelleneintrag ist ja 
gerade kein Funktionspointer, sondern eine "nackte" Speicheradresse - 
top of stack. Und dafür ist void* genau der richtige Typ.

Wenn man die komplette Tabelle als ein Array anlegen will, muß man 
entweder den ersten nicht-Funktionszeiger casten oder die vielen 
anderen. Zweckmäßigerweise hat man sich für den ersten Weg entschieden.

Die ganz saubere Variante wäre gewesen, den ersten Tabelleneintrag 
abzutrennen und zwei Arrays direkt hintereinander zu plazieren. Aber man 
muß ja nicht päpstlicher sein, als der Papst.

---schnipp---

Ganz allgemein zu dem Thread:

1. Ja, Funktionszeiger sind ein elementares Konzept in C. Deswegen 
gehören die in ein Tutorial. Nicht in den ersten Teil, aber ein 
Tutorial, das Funktionszeiger komplett unter den Tisch fallen läßt, ist 
ein Fall für die Tonne.

2. Dieser Code (Vektortabelle und Startup) ist typischer write-once 
Code. Den schreibt man^W irgend jemand genau einmal. Und dann nutzt man 
den nur noch. Typischerweise ist der in einer Support-Library verborgen 
(z.B. libopencm3). Oder die IDE generiert ihn, wenn man ein neues 
Projekt anlegt. Es ist aber durchaus sinnvoll, den wenigstens einmal zu 
sehen und zu verstehen.

3. Der Code wird in der Tat oft in Assembler geschrieben. Wenn man 
Assembler lesen kann, ist das dann auch leichter lesbar als der 
äquivalente C Code. Einfach deshalb, weil Tabellen dieser Art ein 
typisches Ding von Assembler sind. Andererseits kann man ihn auch in C 
schreiben. Die Sprache ist ja extra hardwarenah designed worden, um das 
zu erlauben. Und wenn der Rest des Programms C ist, warum sollte man 
dann für den Startup-Code zu Assembler wechseln?

von Al3ko -. (al3ko)


Lesenswert?

Stefan ⛄ F. schrieb:
> Vielleicht kommst du mit einer Praxisorientierten Anleitung besser klar:
> 
http://stefanfrings.de/mikrocontroller_buch/Einstieg%20in%20die%20Elektronik%20mit%20Mikrocontrollern%20-%20Band%201.pdf
> Kapitel 4, und die Fortsetzung in band 2 Kapitel 11

Hi Stefan

Deine Internetseite ist zur Zeit nicht verfügbar? Oder anders 
ausgedrückt:
Wo kann ich Band 2 finden? ?

Gruß,

von Rolf M. (rmagnus)


Lesenswert?

Axel S. schrieb:
> Das passiert hier aber gar nicht. Der erste Tabelleneintrag ist ja
> gerade kein Funktionspointer, sondern eine "nackte" Speicheradresse -
> top of stack. Und dafür ist void* genau der richtige Typ.

Die Tabelle ist ein Array aus Funktionszeigern. Damit ist auch das erste 
Element ein Funktionszeiger.

> Die ganz saubere Variante wäre gewesen, den ersten Tabelleneintrag
> abzutrennen und zwei Arrays direkt hintereinander zu plazieren. Aber man
> muß ja nicht päpstlicher sein, als der Papst.

Für einen einzelnen Eintrag braucht man kein Array. Ich hätte eine 
struct aus einem void* und einem Array aus Funktionspointern gemacht. 
Gar nicht mal, um "päpstlich" zu sein, sondern weil ich es lesbarer 
finde. Mich hat der Code, so wie er oben steht, erstmal verwirrt.

> Ganz allgemein zu dem Thread:
>
> 1. Ja, Funktionszeiger sind ein elementares Konzept in C. Deswegen
> gehören die in ein Tutorial. Nicht in den ersten Teil, aber ein
> Tutorial, das Funktionszeiger komplett unter den Tisch fallen läßt, ist
> ein Fall für die Tonne.

Die kann man in einem Tutorial auch z.B. über die Funktion qsort() ganz 
gut erklären. Da leuchtet einem da schnell ein.
Hier ist der Fall aber noch komplexer, da es sich um ein Array aus 
Funktionszeigern handelt. Dass dieser Stackzeiger da auch mit drin ist, 
erleichtert das Verständnis auch nicht gerade.

> Und wenn der Rest des Programms C ist, warum sollte man dann für den
> Startup-Code zu Assembler wechseln?

Teile davon werden vermutlich so oder so in Assembler implementiert 
werden müssen, weil man in C über z.B. Register gar nicht die nötige 
Kontrolle hat.

von M. Н. (Gast)


Lesenswert?

Rolf M. schrieb:
>> Und wenn der Rest des Programms C ist, warum sollte man dann für den
>> Startup-Code zu Assembler wechseln?
>
> Teile davon werden vermutlich so oder so in Assembler implementiert
> werden müssen, weil man in C über z.B. Register gar nicht die nötige
> Kontrolle hat.

Gerade die Cortex-M Controller sind von ihrem Architekturaufbau so, dass 
man den Startup-Code auch ausschließlich in C machen kann. Man muss 
bspw. den Stackpointer nicht selbst initialisieren, sondern nur den 
Init-wert an eine bestimmte Stelle der Vektortabelle legen. Der Core 
lädt ihn automatisch. Genauso müssen z.B. für Interrupts keine 
Assembler-Wrapper geschrieben werden, die die Register auf den Stack 
pushen. Das macht der Cortex auch selbst. Somit ist eine komplette 
Implementierung des Startup-Codes in C möglich.

Ich meine NXP lieferte mit seiner IDE für die LPC Controller 
Startupcodes in C mit, wohingegen STM Assembler mitliefert.

Für andere CPU-Architekturen/Mikrocontroller ist es aber teils zwingend 
notwendig ASM-Startupcodes zu schreiben.

von W.S. (Gast)


Lesenswert?

Axel S. schrieb:
> 3. Der Code wird in der Tat oft in Assembler geschrieben. Wenn man
> Assembler lesen kann, ist das dann auch leichter lesbar als der
> äquivalente C Code. Einfach deshalb, weil Tabellen dieser Art ein
> typisches Ding von Assembler sind. Andererseits kann man ihn auch in C
> schreiben. Die Sprache ist ja extra hardwarenah designed worden, um das
> zu erlauben. Und wenn der Rest des Programms C ist, warum sollte man
> dann für den Startup-Code zu Assembler wechseln?

Warum? Oder warum nicht?

Nun, gerade bei dieser Tafel gibt es ja mehrere Besonderheiten, die in C 
eben zusätzliche Umstände machen:

1. Die Tafel darf nicht irgendwo hin gelinkt werden, sondern hat ganz 
präzise auf einer bestimmten Adresse zu stehen. Das ist eigentlich 
ausdrücklich eine Assembler-Angelegenheit, denn alle höheren 
Programmiersprachen sind ja genau dazu angelegt, ihren Benutzer eben 
nicht mit derartigen Basics zu belästigen.

Was das gerade bei µC und deren Hardware-Registern in C für Umstände 
macht, sehen wir ja in den betreffenden Controller- .h Dateien. Ja, es 
geht, aber es ist ein umständliches Konstrukt für C.

2. Die Tafel muß ja auch mit Einträgen gefüllt werden, die man nicht 
generell voraussehen kann. Deshalb muß jeder Eintrag mit einem 
Default-Handler versehen werden, der aber dann, wenn man im Projekt 
einen echten Handler hat, durch selbigen ersetzt werden muß. Daher das 
Konstrukt mit dem "weak"-Attribut, das dann der Linker berücksichtigt. 
Aber auch dieses ist so eigentlich nicht in C vorgesehen.

Und warum sollte man partout mit Krampf einen Startup in C schreiben, wo 
genau dieses eigentlich ein typischer Fall für Assembler ist? Nur dazu, 
daß man damit kaschieren kann, gar keine Ahnung von ARM-Assembler zu 
haben? Dabei braucht man für den Startup gar keine wirklich guten 
Assemblerkenntnisse, ein bissel Basiswissen reicht aus.

W.S.

von Johannes S. (Gast)


Lesenswert?

W.S. schrieb:
> 1. Die Tafel darf nicht irgendwo hin gelinkt werden, sondern hat ganz
> präzise auf einer bestimmten Adresse zu stehen. Das ist eigentlich
> ausdrücklich eine Assembler-Angelegenheit

Und wo steht die ganz bestimmte Adresse im C-Quelltext vom TO???

Wenn HW+SW Design zusammenpassen braucht man keine Assembler 
Verrenkungen. ARM7TDMI war mal modern, aber das ist lange her und es 
gibt mittlerweile besseres. Nur weil man das mal für die Lernbetty 
brauchte muss die Entwicklung da nicht stehenbleiben. NXP hat die gcc 
Vorlage nicht genutzt und den Startupcode in C geschrieben und das 
funktioniert auch.

von Axel S. (a-za-z0-9)


Lesenswert?

Johannes S. schrieb:
> W.S. schrieb:
>> 1. Die Tafel darf nicht irgendwo hin gelinkt werden, sondern hat ganz
>> präzise auf einer bestimmten Adresse zu stehen. Das ist eigentlich
>> ausdrücklich eine Assembler-Angelegenheit
>
> Und wo steht die ganz bestimmte Adresse im C-Quelltext vom TO???

Die steht normalerweise im Linkerskript. Und die Vektortabelle wird per 
Attribut in eine spezielle Sektion geschoben.

von Rolf M. (rmagnus)


Lesenswert?

W.S. schrieb:
> 1. Die Tafel darf nicht irgendwo hin gelinkt werden, sondern hat ganz
> präzise auf einer bestimmten Adresse zu stehen. Das ist eigentlich
> ausdrücklich eine Assembler-Angelegenheit, denn alle höheren
> Programmiersprachen sind ja genau dazu angelegt, ihren Benutzer eben
> nicht mit derartigen Basics zu belästigen.

Eigentlich ist es die Angelegenheit des Linkers. Im Linkerskript muss 
die Adresse angegeben werden.

> 2. Die Tafel muß ja auch mit Einträgen gefüllt werden, die man nicht
> generell voraussehen kann. Deshalb muß jeder Eintrag mit einem
> Default-Handler versehen werden, der aber dann, wenn man im Projekt
> einen echten Handler hat, durch selbigen ersetzt werden muß. Daher das
> Konstrukt mit dem "weak"-Attribut, das dann der Linker berücksichtigt.
> Aber auch dieses ist so eigentlich nicht in C vorgesehen.

Und in Assembler ist das vorgesehen?

von Johannes S. (Gast)


Lesenswert?

Axel S. schrieb:
> Die steht normalerweise im Linkerskript.

Deshalb die Frage an W.S. wo er das im C Quelltext sieht.

von Orakel (Gast)


Lesenswert?

W.S. schrieb:
> Und warum sollte man partout mit Krampf einen Startup in C schreiben, wo
> genau dieses eigentlich ein typischer Fall für Assembler ist? Nur dazu,
> daß man damit kaschieren kann, gar keine Ahnung von ARM-Assembler zu
> haben? Dabei braucht man für den Startup gar keine wirklich guten
> Assemblerkenntnisse, ein bissel Basiswissen reicht aus.

Oha da spricht jemand mit Erfahrung... nicht.

Hier ein Beispiel:
1
extern unsigned int _DATA_ROM_START;
2
extern unsigned int _DATA_RAM_START;
3
extern unsigned int _DATA_RAM_END;
4
extern unsigned int _BSS_START;
5
extern unsigned int _BSS_END;
6
7
#define STACK_TOP 0x20005000
8
void startup();
9
10
unsigned int * myvectors[2] 
11
__attribute__ ((section("vectors")))= {
12
    (unsigned int *)    STACK_TOP,  // stack pointer
13
    (unsigned int *)    startup     // code entry point
14
};
15
16
void main();
17
18
void startup()
19
{
20
    /* Copy data belonging to the `.data` section from its
21
     * load time position on flash (ROM) to its run time position
22
     * in SRAM.
23
     */
24
    unsigned int * data_rom_start_p = &_DATA_ROM_START;
25
    unsigned int * data_ram_start_p = &_DATA_RAM_START;
26
    unsigned int * data_ram_end_p = &_DATA_RAM_END;
27
28
    while(data_ram_start_p != data_ram_end_p)
29
    {
30
        *data_ram_start_p = *data_rom_start_p;
31
        data_ram_start_p++;
32
        data_rom_start_p++;
33
    }
34
35
    /* Initialize data in the `.bss` section to zeros.
36
     */
37
    unsigned int * bss_start_p = &_BSS_START; 
38
    unsigned int * bss_end_p = &_BSS_END;
39
40
    while(bss_start_p != bss_end_p)
41
    {
42
        *bss_start_p = 0;
43
        bss_start_p++;
44
    }
45
46
47
    /* Call the `main()` function defined in `test_program.c`.
48
     */
49
    main();
50
}
Quelle und vollständige Beschreibung: 
https://jacobmossberg.se/posts/2018/08/11/run-c-program-bare-metal-on-arm-cortex-m3.html#arm-cortex-m3-boot-sequence

Was war daran jetzt so schwer?

von Stefan F. (Gast)


Lesenswert?

Orakel schrieb:
> Müssen wir dieses Gesülze von dir jetzt eigentlich jedes Mal lesen?

Das habe ich in meinem ganzen Leben zum ersten mal geschrieben.

> Besorg dir endlich mal ordentliche Lektüre zu C und zu C++ und lies es!

Du kennst mich nicht. Ich habe zwei Bücher von Bjarne gelesen, eins von 
Kernighan und Ritchie, eins von Ulrich Breymann und eins an dessen Autor 
Titel ich mich nicht mehr erinnere.

Also mach mal halblang!

von Stefan F. (Gast)


Lesenswert?

Al3ko -. schrieb:
> Deine Internetseite ist zur Zeit nicht verfügbar?

Die Seite ist zur Zeit verfügbar.

> Wo kann ich Band 2 finden?

Die Datei befindet sich auf der gleichen Seite. Ihr Dateiname endet mit 
2.pdf (anstelle von 1.pdf).

http://stefanfrings.de/mikrocontroller_buch/index.html

Beitrag #6190941 wurde von einem Moderator gelöscht.
von W.S. (Gast)


Lesenswert?

Rolf M. schrieb:
> Und in Assembler ist das vorgesehen?

Ja.
1
Default_Handler    PROC
2
      EXPORT    WDT_IRQHandler         [WEAK]
3
      EXPORT    TIMER0_IRQHandler      [WEAK]
4
      EXPORT    TIMER1_IRQHandler      [WEAK]
5
      EXPORT    TIMER2_IRQHandler      [WEAK]
6
      EXPORT    TIMER3_IRQHandler      [WEAK]
7
      EXPORT    UART0_IRQHandler       [WEAK]
8
      EXPORT    UART1_IRQHandler       [WEAK]
9
...usw.

Dinge wie dieses "weak" sind eben plattformspezifisch und Assembler ist 
ebenso plattformspezifisch. Paßt. Sowas in C unterzubringen, ist 
eigentlich entgegen dem Sinn von C als einer plattformübergreifenden 
höheren Programmiersprache.

W.S.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

W.S. schrieb:
> Dinge wie dieses "weak" sind eben plattformspezifisch

Du hast schonwieder keine Ahnung!
Jetzt mal ne ganz ehrliche Frage: Was kannst du eigentlich?

von W.S. (Gast)


Lesenswert?

Rolf M. schrieb:
> Eigentlich ist es die Angelegenheit des Linkers. Im Linkerskript muss
> die Adresse angegeben werden.

Ähem, nochwas:
1
                PRESERVE8
2
                THUMB
3
4
5
; Vector Table Mapped to Address 0 at Reset
6
7
                AREA    RESET, CODE, READONLY
8
                EXPORT  __Vectors
9
10
__Vectors       DCD             __initial_sp              ; Top of Stack
11
                DCD             Hard_Reset                ; Reset Handler
12
...etc.

Damit ist das im Quellfile angegeben.
Es gibt bei einigen Chips noch ganz andere Dinge, die da ebenfalls auf 
bestimmte Adressen festgenagelt werden müssen:
z.B. beim Kinetis MK22FN...
1
;
2
                AREA    |.ARM.__at_0x400|, CODE, READONLY
3
                EXPORT  FSEC
4
        EXPORT  FOPT
5
BackDoorK0      DCB     0xFF
6
BackDoorK1      DCB     0xFF
7
BackDoorK2      DCB     0xFF
8
BackDoorK3      DCB     0xFF
9
BackDoorK4      DCB     0xFF
10
BackDoorK5      DCB     0xFF
11
BackDoorK6      DCB     0xFF
12
BackDoorK7      DCB     0xFF
13
14
FPROT0          DCB     0xFF
15
FPROT1          DCB     0xFF
16
FPROT2          DCB     0xFF
17
FPROT3          DCB     0xFF
18
                DCB     0xFF
19
                DCB     0xFF
20
FSEC            DCB     0xFE
21
FOPT            DCB     0xFF
22
23
                ALIGN
24
                END
Das ist die Protect-Sache. Wenn an dieser Stelle bunte Knete steht, dann 
hat das ggf. putzige Nachwirkungen.

Oder was Ähnliches beim LPC11E12/14:
1
;
2
                AREA    |.ARM.__at_0x2FC|, CODE, READONLY
3
                EXPORT  CRP_Key
4
5
CRP_Key         DCD     0xFFFFFFFF
6
7
                ALIGN
8
                END

Wie du siehst, gibt es in der Welt so einiges mehr als nur den 
Startupcode.

W.S.

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.