Forum: Mikrocontroller und Digitale Elektronik LPC Registerzugriff Fehler in LPC176x.h


von Karl K. (leluno)


Lesenswert?

Als LPC-Nutzer ist man auf den Import von Dateien aus anderen Projekten 
angewiesen. Dies führt in der Regel zu Fehlermeldungen, weil der Zugriff 
auf Register unterschiedlich gehandhabt wird.

Beispiel CodeRed:
Zugriff über LPC17xx.h/CMSIS-Library:
1
LPC_PINCON_TypeDef LPC_PINCON;
2
  // Set P1_24, P1_25, P1_28, P1_29 to 00 - GPIO
3
  LPC_PINCON->PINSEL3  &= (~0x0F0F0000);  
4
  // Set GPIO - P1_24, P1_25, P1_28, P1_29 - to be outputs
5
  LPC_GPIO1->FIODIR |= ((1 << 24) | (1 << 25) | (1 << 28)| (1 << 29))
6
}


Beispiel Keil:
Zugriff erfolgt über Strukturen, die in diversen Dateinen abgelegt sind, 
z.B. lpc17xx_pinsel.h,lpc17xx_gpio
1
   PINSEL_CFG_Type PinCfg;
2
3
  PinCfg.Funcnum = 0;
4
  PinCfg.OpenDrain = 0;
5
  PinCfg.Pinmode = 0;
6
  PinCfg.Portnum = 3;
7
  PinCfg.Pinnum = 25;
8
  PINSEL_ConfigPin(&PinCfg);
9
  PinCfg.Pinnum = 26;
10
  PINSEL_ConfigPin(&PinCfg);
11
    GPIO_SetDir(3, (1<<25) | (1<<26), 1);

Bei Fehlermeldungen wegen Registerzugriffs müssen die Entsprechenden 
Bibliotheken eingebunden oder der Code geändert werden. Ich bevorzuge 
den Registerzugriff über LPC176x.h, weil die dort definierten 
Registernamen den Bezeichnungen im Handbuch entsprechen. Ein Blick ins 
Manual und man weiß, welche Funktion Registernamen verkörpern:
1
  // Set P1_24, P1_25, P1_28, P1_29 to 00 - GPIO
2
  PINSEL3  &= (~0x0F0F0000);  
3
  // Set GPIO - P1_24, P1_25, P1_28, P1_29 - to be outputs
4
  FIO1DIR |= ((1 << 24) | (1 << 25) | (1 << 28)| (1 << 29))
Dass Pinsel3 für Port1 zuständig ist, liegt daran dass jeder Pin über 
4Bit beschrieben wird und demzufolge für 32Pins insgesamt 4 
32Bit-Register erforderlich sind. Übersichtlicher wird es mit Macros:
1
#define pinSEL(p,b,v)    PINSEL[(p) * 2 + (b) / 16] = (PINSEL[(p) * 2 + (b) / 16] & ~(3 << ((b) * 2 % 32))) | (v << ((b) * 2 % 32))
Damit werden die Register über 
pinSEL(PortNr,PinNr,Funktionsnummer)aufgerufen, ohne dass man 
rückrechnen muss, welches Pinsel-Register dem Pin zugerdnet ist.


LPC176x.h enthält Fehler. Das Register CCR ist doppelt definiert und 
kann daher bei der RTC nicht funktionieren. RxFliterCtrl müsste wohl 
richtig RxFilterCtrl heißen. Das führt bei Anpassung von
importiertem Code zu Fehlermeldungen.

von DN (Gast)


Lesenswert?

In der Version 2.00 CMSIS in LPCXpresso 6.1.4
ist alles korrekt definiert.

Wahrscheinlich eine zu alte Version.

von Karl K. (leluno)


Lesenswert?

Auf der CodeRed-Seite wird 5.2.6 angeboten. Woher hast du 6.1.4?
Meine LPC176x.h ist vom Februar 2013.
Was ich noch störend finde, ist das #define MIN /rtc,  das gibt Probleme 
wenn irgenwo MIN/MAX definiert ist.

von Martin M. (capiman)


Lesenswert?


von Karl K. (leluno)


Lesenswert?

Danke, ich möchte mein CodeRed noch nicht updaten, da meine jetzige 
Version gut läuft. Nach UM10360/20.12.2013 gibt es immer noch zwei 
Register, die gleich benannt sind:

 Configuration and Control Register CCR 0xE000ED14
und
 Clock Control Register CCR 0x40024008

Wie haben die denn dieses Problem in der neuen LPC176x.h gelößt?

von Martin M. (capiman)


Lesenswert?

Wäre es dir möglich, deine Frage(n) auch auf lpcware.com
unter der Sektion Forum lpc175x_6x bzw.
direkter Link

http://www.lpcware.com/forums/microprocessor-forums/lpc17xx

zu posten? Ich denke, die Frage wäre dort gut aufgehoben,
damit NXP sieht, dass es hier Probleme gibt...

von DN (Gast)


Lesenswert?

Die Installation von LPCXpresso geht paralell zu CodeRed, welches im 
übrigen
nicht mehr weiterentwickelt wird.

Der Name CCR existiert mehrfach. Die Nutzung des Register wird über die
jeweiligen Subsysteme und deren Strukturdefinitionen erledigt.

Bsp :

static LPC_TIM_Typedef (*const TIM) = LPC_TIM;
static LPC_RTC_Typedef (*const RTC) = LPC_RTC;

LPC_TIM->CCR ...
LPC_RTC->CCR ...

Das CCR ( Configuration und Control Rgister ) ist in core_cm3h.h
definiert und wird mit der struct SCB ( System Control Block )
angesprochen. Diese Register ist nur im priveligierten Modus nutzbar.

von W.S. (Gast)


Lesenswert?

karl k. schrieb:
> Als LPC-Nutzer ist man auf den Import von Dateien aus anderen Projekten
> angewiesen.

SO?
Mir ist sowas noch nie vorgekommen. Ich benutze entweder die vom 
Chip-Hersteller veröffentlichten Headerdateien oder (wenn mir die nicht 
gefallen) schreibe ich mir sowas selbst - und basta. Da weiß man 
wenigstens, was man tut und worauf man sich verlassen kann.

Die oftmals geübte Methode, Ports in Structs zu fassen, finde ich 
grottenschlecht. Mal abgesehen davon, daß man sich damit tonnenweise 
Dummy-Deklarationen (a la char undef_192[17] und so) einhandelt, ist 
zumindest der Compiler beim Keil so schlau, sich auch bei völlig separat 
deklarierten Registern die optimale Zugriffsvariante auszusuchen.

Ob man nun UART0->TRH = MyByte; oder nach anderer Deklaration UART0THR = 
MyByte; schreibt, ist damit egal und das Verwenden von Struct's führt zu 
keinerlei Vorteil. Da ist mir das separate Deklarieren mit exakt den 
gleichen Namen wie im RefMan bedeutend lieber.

Ebenso ist der Eiertanz mit dem oftmals geübten Bit- und 
Bitgruppen-Deklarieren ein Graus. Sowas liest sich miserabel und führt 
auch nicht wirklich zu besserem Quellcode (vom MC ganz zu schweigen..).

Ich kann dir also nur raten, dich nicht mit allzeit divergierenden 
Geschmacksrichtungen von Header-Dateien herumzuplagen, sondern dir sowas 
im Zweifelsfall selber zu schreiben. Oftmals ist es sogar besser, auf 
allgemeine Header zu verzichten und die HW-Register direkt und selbst in 
der Quelle ihres zuständigen Treibers zu deklarieren. Sie werden ja auch 
nur DORT und sonst nirgendwo gebraucht - wenn man's richtig macht und 
den LowLevel-Treiber so schreibt, daß er die jeweilige HW vernünftig 
kapselt. Und wenn man die HW wechselt, ist ohnehin ein anderer 
LowLevel-Treiber erforderlich.

W.S.

von Andi (Gast)


Lesenswert?

@W.S.
Zum Vergleich UART0->TRH = MyByte bzw. UART0THR = MyByte

Es ist schon ganz praktisch, wenn man eine Funktion hat,
die man für jede Instanz eines UARTs benutzen kann,
etwa in der Art:

SendUartChar(UARTBASE uartbase, uint8_t MyByte)
{
  uartbase->TRH = MyByte;
}

Bei einem Register ist es egal, da könnte man auch UART0THR
als Parameter übergeben, aber spätestens, wenn man mehrere Register
braucht, geht dies damit viel einfacher.

Man kann natürlich auch die Uart-Nr übergeben, dann muss man
aber mittels if oder switch immer von Uart-Nr auf UARTxTHR umrechnen.
(oder halt ein Array pro Register).

Zum selber Schreiben von Register-Dateien:
Ich hatte das früher mal gemacht, weil es für einen Controller
noch keine Datei gab. Es ist eine Riesenarbeit und fehleranfällig.
Ich bin froh, wenn ich das nicht mehr machen brauche.
Da ist mir die Zeit zu schade...

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.