Unterschied zwischen __I und __IO und __O im CMSIS-Kern

in der Header-Datei core_cm4.h ist so etwas definiert:

#ifdef __cplusplus
  #define   __I     volatile             /*!< Defines 'read only' permissions                 */
#else
  #define   __I     volatile const       /*!< Defines 'read only' permissions                 */
#endif
#define     __O     volatile             /*!< Defines 'write only' permissions                */
#define     __IO    volatile             /*!< Defines 'read / write' permissions              */

Was ist der Unterschied zwischen __I , __O und __IO, obwohl sie auf die gleiche Weise definiert sind?

  • und wie sie funktionieren, um " nur lesen/schreiben " anzugeben, während sie nur als flüchtige Variable definiert sind?!
Namenskonvention? Ich denke, das Schlüsselwort, nach dem Sie suchen, ist " volatile const ".

Antworten (3)

"I" bedeutet: Eingang

"O" bedeutet: Ausgang

"IO" bedeutet: Eingang und Ausgang

Wie Long Pham feststellt, handelt es sich um eine Namenskonvention, aber es ist auch normal, Bedeutungen für einen Typ zu verwenden. Wie eine Ganzzahl kann ein Zähler, ein Zeitstempel, ein Datum usw.

Es gibt einige Gründe, dies zu verwenden:

  • Es ist gut für die Lesbarkeit
  • Wenn sich in Zukunft der Typ eines I, O oder IO ändern würde, muss (Anwender-)Quellcode, der I, O und IO verwendet, nicht geändert werden, nur die Typdefinitionen / definieren sich.
Erklärt jedoch nicht, warum sie einen anderen C++-Compiler-Schalter verwenden. Ich denke, die Grenzen von C++ sind der Hauptgrund, warum sie diese hausgemachten Qualifizierer überhaupt erst erfunden haben.
@Lundin Das denke ich auch, ich habe mich auf die I/O/IO-Unterschiede in Bezug auf die Benennung konzentriert

Da dies für die Definition einer Hardware-Registerkarte gilt:

Ich denke, der Grund ist, dass C im Gegensatz zu C++ die Deklaration constqualifizierter Variablen zulässt, ohne einen Initialisierer bereitzustellen:

volatile const uint32_t REG; // Ok in C, invalid in C++

In ähnlicher Weise erlaubt C++ auch nicht, dass constMember von struct/classes nicht initialisiert werden. Dies ist unpraktisch, wenn wir schreibgeschützte Hardwareregister haben und eine Registerzuordnung mithilfe von Strukturen (Klassen) erstellen möchten.

Diese Einschränkung der C++-Sprache ist wahrscheinlich der Grund, warum dieser Header einen schmutzigen Hack verwendet, #ifdef __cplusplusum aus einem Register zu entfernen const, das hätte constqualifiziert werden sollen.

Nur eines von vielen kleinen Details, die C++ für hardwarenahe Programmierung weniger geeignet machen als C.

Diese Header-Datei wurde erstellt, um auch C++ zu unterstützen,

Daher wird im Fall von C die konstante Variable als Konstante bezeichnet und kann vom Benutzer nicht geändert werden. Es gibt jedoch keine weitere Optimierung für diese konstante Variable durch den C-Compiler.

Aber im Fall von C++ kann diese konstante Variable durch den konstanten Wert ersetzt werden, der dieser Variablen zugewiesen ist.

Im Fall von c++ sind Strukturmember-Variablen nicht optimiert, daher kann dort const verwendet werden.

Daher stellt diese Header-Datei __I ,__O , __IO für die skalaren Variablen und
__IM ,__OM ,__IOM für die Strukturmitglieder bereit.

Ich verstehe diese Erklärung weder, noch stimme ich ihr zu. Was meinst du mit "keine weitere Optimierung"? Das ist in keinem Standard festgelegt, Compiler können tun, was sie wollen. Und was um alles in der Welt hat das mit Registerkarten zu tun? Ich hoffe sehr, dass irgendein verrückter C++-Compiler meine schreibgeschützte Hardwareregistervariable nicht durch eine ganzzahlige Konstante ersetzt.
@Lundin "keine weiteren Optimierungsmittel" in C wird die Variable 'const' als eine Variable behandelt, deren Wert vom Benutzer nicht im Code geändert werden kann, und im Hintergrund wird sie nicht direkt durch ihren Wert im Code ersetzt. Jede Leseoperation greift auf die Adresse der Variablen für ihren Wert zu. In diesem Fall weist der Compiler Speicher für diese Variable im Abschnitt Text zu.
@Lundin Register-Maps müssen mit 'Volatile' und 'const' entsprechend dem Typ des Registers definiert werden. Nehmen wir zum Beispiel an, wir haben ein ADC-Konvertierungs-Ausgangsregister, das ich einer Variablen zugeordnet habe. Für ein besseres, robusteres und fehlerfreies Programm möchten wir nun, dass die Variable schreibgeschützt ist, damit sie nicht versehentlich geschrieben wird. Daher hilft uns hier const. Und wir möchten auch, dass jeder Lesevorgang tief nach unten geht und den Wert aus dem Speicherort erhält, nicht aus dem Lese-/Schreibpuffer (dies kann bei einer hohen Optimierungseinstellung während der Kompilierung auftreten). Daher hilft in diesem Fall volatile.