Hallo,
ich versuche gerae mit elm chan Code Beispiel einen stm32 mit fatfs zum
laufen zu bekommen.
Jedoch scheitere ich gerade beim Verständnis, wie ein bestimmtes
Register beschrieben wird.
Es handelt sich um das
AHB peripheral clock enable register (RCC_AHBENR)
Address offset: 0x14
Reset value: 0x0000 0014
der Zugriff erfolgt über den Befehl
Epimeth schrieb:> [p/32]
Jedes Register hat 32 Bits; so wird der Index des Eintrags auf die
Nummer des Registers umgerechnet. Der Pointer &RCC_AHBENR wird hier als
Array betrachtet, und das in [] ist der Index.
Epimeth schrieb:> und was> macht "|"?
Das steht in jedem C(++)-Buch im Kapitel über Operatoren.
stefan us schrieb:> Heisst das, der Parameter p könnte auch mehr als 32bit adressieren, also> auf das Register hinter RCC_AHBENR "zeigen"?
Ja, macht er ja bei diesem Beispiel, er zeigt auf RCC_APB2ENR.
Epimeth schrieb:> Jedoch scheitere ich gerade beim Verständnis, wie ein bestimmtes> Register beschrieben wird.
Na ist doch ganz einfach.
Ein kleines Beispiel:
So, die passenden #define's findest du in der zugehörigen .h Datei für
deinen Chip und die Bedeutung der Bits im Register findest du im
Referenzmanual zum Chip.
Mehr Brimborium braucht es nicht.
W.S.
W.S. schrieb:> RCC_AHBENR = (1<<17) | // Port A enable> (1<<18) | // Port B enable> (1<<19) | // Port C enable> (1<<20) | // Port D enable> (1<<22); // Port F enable
Vieeel zuviele "magische Zahlen" im Code. Der Hersteller liefert Dir ja
eine Header Datei mit den ganzen Bitdefinitionen - die sollte man dann
auch verwenden.
Da ist ein
1
__enable_peripheral(SPI1EN);
deutlich einfacher lesbar, auch wenn $HERSTELLER die Verwendung der
reservierten "__" im Support um die Ohren gehaut bekommen sollte.
Jim M. schrieb:> deutlich einfacher lesbar, auch wenn $HERSTELLER die Verwendung der> reservierten "__" im Support um die Ohren gehaut bekommen sollte.
ich (die) denke(n), das dürfen die.
__ -> reserved for Implementation. Damit ist üblicherweise die
Kombination aus Compiler + Std Library gemeint. Dein $HERSTELLER
betrachtet seine Header eben als Teil der Implementierung. Kann man so
sehen, finde ich.
Markus F. schrieb:> Dein $HERSTELLER> betrachtet seine Header eben als Teil der Implementierung. Kann man so> sehen, finde ich.
Wenn schon müssten aber alle internen Bezeichner mit __ anfangen, und
API's eben nicht. __enable_peripheral ist ein API, und viele interne
Bezeichner nutzen eben kein __ . Daher ist das also inkonsistent.
Der C Standard definiert nichts mit Peripherie-Takten, daher ist eine
Funktion dafür eher nicht in der Standard Bibliothek zu suchen. Die
Funktionen der ST Library braucht man nicht für Standard-Konforme
Programme, man kann ganz ohne sie alle Funktionalität des Standards
nutzen. Die ST Library ist nur eine ganz normale externe Bibliothek.
Das __ ist nur für interne Namen von Standard-Headern, und
Compiler-Builtins erforderlich, damit 100% Standard-Konformer Code,
welcher ausschließlich Standard-Header inkludiert, keine Kollisionen
produzieren kann. D.h. wenn folgender Code einen Fehler produzieren
würde, wäre das __ erforderlich:
1
#include<stdio.h>
2
3
intmain(){
4
puts("Hello, World!");
5
}
Dem ist aber nicht so; ein korrekterweise "enable_peripheral" genanntes
Makro sorgt nur für Kollisionen wenn man den ST-Header inkludiert, und
dann ist's eh nicht mehr Standard.
Tatsächlich ist die grassierende Nutzung von "__" oder _ +
Großbuchstabe eher auf Unwissen zurückzuführen, auch gerne bei
Include-Guards.
Ein klassisches Gegenbeispiel ist das M_PI bei der GNU Toolchain. Laut
C-Standard ist dieses Programm absolut korrekt:
1
#include<math.h>
2
3
intmain(){
4
intM_PI=2;
5
}
Aber wenn man es direkt mit "gcc" kompiliert gibt's Fehler, weil der
C-Header math.h fälschlicherweise ein M_PI definiert, was im Standard
nicht vorgesehen ist. Daher müsste das Makro eigentlich "__M_PI" o.ä.
heißen. Allerdings kompiliert der GCC default-mäßig nicht in einem
standard-konformen Modus; wenn man "-std=c99" o.ä. übergibt ist's
korrekt.
Wäre das __enable_peripheral in einem Standard-Header wie "stdlib.h"
o.ä. enthalten, dann wäre das __ erforderlich und korrekt.
Dr. Sommer schrieb:> Aber wenn man es direkt mit "gcc" kompiliert gibt's Fehler, weil der> C-Header math.h fälschlicherweise ein M_PI definiert, was im Standard> nicht vorgesehen ist. Daher müsste das Makro eigentlich "__M_PI" o.ä.> heißen. Allerdings kompiliert der GCC default-mäßig nicht in einem> standard-konformen Modus; wenn man "-std=c99" o.ä. übergibt ist's> korrekt.
Wenn du im nicht-ISO-C-Modus compilierst (und der ist eben bei gcc der
Default), kann man kaum davon sprechen, dass die Definition falsch ist,
weil ISO C sie nicht erlaubt.
Jim M. schrieb:> Vieeel zuviele "magische Zahlen" im Code.> ...> Da ist ein __enable_peripheral(SPI1EN);>> deutlich einfacher lesbar,
Hä?
Und dann schreibst du für jeden Peripherie-Core einen separaten
"__enable_peripheral(...)" hin, ja?
Oder du hangelst dich durch ein Dickicht von zusätzlich deklarierten
Verschiebe- und Mask-Hilfsdeklarationen durch, bloß weil du partout
NICHT ins zuständige Manual schauen willst?
Mannomann, warum suchen Leute wie du immer nur nach der uneffektivsten,
fehlerträchtigsten und unleserlichsten Variante, irgend was zu
programmieren?
kopfschüttel...
W.S.
Ich dachte vorher nur man müsste wenn man einen Index verwendet, dies
vorher definieren.
Eine Frage nur noch zum Verständnis eine Speicheradresse hält immer ein
Byte bzw 8 Bit und dadurch kommt der Offset von 0x004 von einer
Registeradresse zur nächsten Registeradresse zustande, wenn die
Registerlänge 32 Bit ist?
Epimeth schrieb:> Ich dachte vorher nur man müsste wenn man einen Index verwendet, dies> vorher definieren.
Der Präprozessor macht Textersetzung, der Compiler sieht dort
tatsächlich nur die nackten Zahlen.
Epimeth schrieb:> Eine Frage nur noch zum Verständnis eine Speicheradresse hält immer ein> Byte bzw 8 Bit und dadurch kommt der Offset von 0x004 von einer> Registeradresse zur nächsten Registeradresse zustande, wenn die> Registerlänge 32 Bit ist?
Ja, per Konvention sind alle Adressen Byte-Adressen.
Das ist aber nicht überall so. Ein Gegenbeispiel sind Flash-Adressen auf
dem AVR, die zeigen immer auf einen 16 Bit-Wert und werden vom
AVR-Assembler auch so behandelt. Einzelne Bytes lassen sich dort nicht
adressieren. (Die GNU-Toolchain für AVR, also auch der GNU-Assembler,
nutzen aber auf jeder Architektur Byte-Adressen.)
S. R. schrieb:> Ein Gegenbeispiel sind Flash-Adressen auf> dem AVR, die zeigen immer auf einen 16 Bit-Wert und werden vom> AVR-Assembler auch so behandelt. Einzelne Bytes lassen sich dort nicht> adressieren.
Das stimmt so nicht. Der Speicher ist 16 Bit breit, und alle
Instruktionen sind entweder 16 oder 32 Bit breit, daher macht natürlich
eine Byte-Adresse als Sprungziel keinen Sinn.
Aber man kann auf Assembler-Ebene die Daten 8-Bittig adressieren. Muss
man ja können, da ein Register nicht mehr als 8 Bit aufnehmen kann.
Siehe
https://www.microchip.com/webdoc/avrassembler/avrassembler.wb_LPM.html