Registerzugriff C Code frage

Gast #5447698
Lesenswert?

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
1
__enable_peripheral(SPI1EN); // wobei SPI1EN = 44

im stm32f100.h steht dann
1
#define __enable_peripheral(p)  (&RCC_AHBENR)[p/32] |= 1 << (p % 32)
1
(&RCC_AHBENR)[p/32] |
Ich weiß es handelt sich um einen Pointer aber wozu [p/32]? und was 
macht "|"?

Grüße und
Danke im Voraus

Link zum Manual Kapitel 6.3.6 
http://www.st.com/content/ccc/resource/technical/document/reference_manual/a2/2d/02/4b/78/57/41/a3/CD00246267.pdf/files/CD00246267.pdf/jcr:content/translations/en.CD00246267.pdf
Gast #5447892
Lesenswert?

Epimeth schrieb:
> Jedoch scheitere ich gerade beim Verständnis, wie ein bestimmtes
> Register beschrieben wird.

Na ist doch ganz einfach.
Ein kleines Beispiel:
1
#define RCC     0x40021000        //- 0x400213FF 1 K RCC
2
...
3
#define RCC_AHBENR   (*((volatile dword *) (RCC + 0x14)))
4
...
5

6
RCC_AHBENR  = (1<<17) |   // Port A enable
7
              (1<<18) |   // Port B enable
8
              (1<<19) |   // Port C enable
9
              (1<<20) |   // Port D enable
10
              (1<<22);    // Port F enable
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.
#5449563
Lesenswert?

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.
#5449575
Lesenswert?

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.
Gast #5449597
Lesenswert?

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
int main () {
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
int main () {
4
  int M_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.
#5450589
Lesenswert?

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.
Gast #5451651
Lesenswert?

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.
Gast #5452838
Lesenswert?

Hallo,

erstmal Danke für die vielen guten Antworten. :-)
1
#define __enable_peripheral(p)  (&RCC_AHBENR)[p/32] |= 1 << (p % 32)
mit p = 44 wird dann
1
 // [p/32]--> 44/32= 1 --> [1] und p%32 --> 12
2
&RCC_AHBENR)[1] |= 1 << 12  // &RCC_AHB2ENR = 1 << 12

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?
#5453244
Lesenswert?

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.)
#5453377
Lesenswert?

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
Beitrag #5454387 wurde von einem Moderator gelöscht.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren