Ich schreibe Registerkarten in C für ein TI ARM-basiertes Mikrocontroller-Board. Hier der Link zum Datenblatt .
Ich verwende die folgenden Richtlinien, wie Register Maps in C geschrieben werden sollten: Register Maps . Diese Richtlinien ähneln ARMs CMSIS (Cortex Microcontroller Software Interface Standard) zum Schreiben von C-Code.
Ich habe ein Problem mit dem Schreiben von System Control Register Maps (siehe Abschnitt 5.5 Seite 237 ff. im Datenblatt) mit C struct. Alle Register haben eine Größe von 32 Bit
Wenn ich jetzt so eine Struktur schreibe:
typedef struct
{
uint32_t DID0; // offset 0x000. distance: 0
uint32_t DID1; // offset 0x004. distance: 4
uint32_t PBORCTL; // **HOW to place this at offset 0x030 ?**
uint32_t RIS; // **HOW to place this at offset 0x050 ?**
// ...and so on
}PER_REG_MAP_T;
#define PERIPH_BASE ((uint32_t)0x400FE000)
#define MY_PERIPH_A ((PER_REG_MAP_T*)PERIPH_BASE)
void Reset(PER_REG_MAP_T* PERIPH_A)
{
PERIPH_A->DID0 = 0;
PERIPH_A->DID1= 0;
PERIPH_A->PBORCTL= 1;
PERIPH_A->RIS= 0;
// .. and so on
}
Das Hauptproblem, mit dem ich konfrontiert bin, ist, wie PBORCTL und RIS in der Struktur platziert werden, da sie sich in Bezug auf die Basisadresse der Struktur bei Offset 0x030 und 0x050 befinden. Ich habe noch nicht zu viel Bit-Level-C-Programmierung gemacht, daher könnte diese Frage zu einfach sein, aber ich weiß nicht, wie ich das machen soll.
Strukturen sind zum Schreiben von Registerzuordnungen ungeeignet, da in einer Struktur zu Ausrichtungszwecken an beliebiger Stelle Füllbytes hinzugefügt werden können. Dies hängt vom Compiler ab - Sie müssen sicherstellen, dass kein Padding verwendet wird (z. B. durch ein statisches Assert).
Außerdem kann man beim Schreiben großer Strukturen, die eine Registerkarte widerspiegeln sollen, leicht Fehler machen, man muss jedes einzelne Byte richtig machen, sonst geht alles kaputt. Dies macht die Struktur während der Wartung angreifbar.
Ich würde Ihnen dringend empfehlen, Registerkarten über Makros zu schreiben, wie zum Beispiel:
#define PERIPH_A_DID0 (*(volatile uint32_t*)0x400FE000u))
#define PERIPH_A_DID1 (*(volatile uint32_t*)0x400FE004u))
Dies hat auch den Vorteil, dass es zu 100 % auf jeden C-Compiler portierbar ist.
Alternativ könnten Sie etwas Komplizierteres tun, wie zum Beispiel:
typedef volatile uint8_t* SCI_port;
#ifndef SCI0
#define SCI0 (&SCI0BDH)
#define SCI1 (&SCI1BDH)
#endif
#define SCIBDH(x) (*((SCI_port)x + 0x00)) /* SCI Baud Rate Register High */
#define SCIBDL(x) (*((SCI_port)x + 0x01)) /* SCI Baud Rate Register Low */
#define SCICR1(x) (*((SCI_port)x + 0x02)) /* SCI Control Register1 */
#define SCICR2(x) (*((SCI_port)x + 0x03)) /* SCI Control Register 2 */
#define SCISR1(x) (*((SCI_port)x + 0x04)) /* SCI Status Register 1 */
#define SCISR2(x) (*((SCI_port)x + 0x05)) /* SCI Status Register 2 */
#define SCIDRH(x) (*((SCI_port)x + 0x06)) /* SCI Data Register High */
#define SCIDRL(x) (*((SCI_port)x + 0x07)) /* SCI Data Register Low */
Wenn Sie dann Ihren Treiber schreiben, können Sie folgendermaßen vorgehen:
void sci_init (SCI_port port, ...)
{
SCICR1(port) = THIS | THAT;
SCICR2(port) = SOMETHING_ELSE;
...
}
Dies ist sehr nützlich, wenn Sie viele identische Peripheriegeräte auf der MCU mit denselben Registern haben, aber nur einen Code zur Steuerung wünschen.
__attribute__((packed))
static_assert(sizeof(mystruct) == sizeof(mystruct.obj1) + sizeofmystruct.obj2) + ..., "Struct padding detected");
Erstens empfiehlt ARM die Verwendung __packed
, um die minimale Verpackung zu erhalten. Zweitens, wenn Sie wirklich große Lücken in Ihrer Struktur brauchen (und das können aus irgendeinem Grund nicht zwei Strukturen sein), können Sie einfach so etwas wie ein char[30] reserved1
Array dazwischen setzen und so weiter für die anderen großen Lücken. Stellen Sie sicher, dass Sie die richtigen Größen für diese verwenden. Sie könnten mit einem Assert noch einmal überprüfen, ob der Offset des nächsten Felds wirklich dort ist, wo Sie denken; offsetof() gibt Ihnen den Offset eines Feldes.
Ich habe das etwas in Eile geschrieben, wenn Sie weitere Details benötigen, fragen Sie unten und ich werde es erweitern (wenn auch nicht für einige Stunden).
Übrigens sieht es so aus, als hätte TI die betreffende Header-Datei bereits für Sie geschrieben . Sie scheinen es zu mögen, #define
für alles zu verwenden.
Majenko
mixed_signal_old
nurabha