Forum: Mikrocontroller und Digitale Elektronik Einsteigerfrage zu Cortex M0 mit LPCXpresso


von H. G. (ledi)


Angehängte Dateien:

Lesenswert?

Hallo,

ich bin gerade dabei, auf ARM Cortex M0 Controller (als Ersatz für die 
8-Bitter) umzusteigen und habe eine Frage zu CMSIS und Cortex M0.
Ich verwende das Entwicklerboard LPC1114 mit LPCXpresso und stelle mir 
die Frage:

Woher weiß ich, welche Headerdateien ich z.B. für die I/O-Ports 
einbinden muss usw. (Ich konnte hier keine "Anleitung" bei NXP oder ARM 
finden)

Ich habe in mein Projekt von NXP die CMSIS-Dateien importiert (siehe 
Bild) und mein mainfile wie folgt erstellt:
1
#ifdef __USE_CMSIS
2
#include "LPC11xx.h"
3
#endif
4
5
#include <cr_section_macros.h>
6
#include <NXP/crp.h>
7
8
__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;  //Enable Code Read Protect"
9
10
// TODO: insert other include files here
11
12
// TODO: insert other definitions and declarations here
13
14
int main(void)
15
{
16
  // Die Std. Peripherie nun Clocks, PLLs usw. einrichten
17
  SystemInit();
18
  //GPIOInit();
19
20
  while(1)
21
  {
22
23
  }
24
  return 0 ;
25
}

Jetzt möchte ich irgendwie die Ports ansprechen um z.B. eine LED blinken 
zu lassen. Welches Headerfile muss ich einbinden?

von Gerhard G. (g_g)


Lesenswert?

Hallo,

schau dir mal die Seite an, hier findest du einen Einstieg:

LPC1100 Series

http://ics.nxp.com/support/lpcxpresso/




Gruß G.G.

von H. G. (ledi)


Lesenswert?

G. G. schrieb:
> Hallo,
>
> schau dir mal die Seite an, hier findest du einen Einstieg:
>
> LPC1100 Series
>
> http://ics.nxp.com/support/lpcxpresso/
>
Da war ich schon. Konnte aber nichts finden, dass mir hier weiterhilft.
Wie ich ein Projekt erstelle oder einbinde, das weiß ich. Wie im obigen 
Beispiel möchte ich auf die I/O-Ports zugreifen.

Wenn ich z.B. die Funktion

GPIOInit();

aufrufe, erhalte ich beim build die Fehlermeldung

undefined reference to `GPIOInit'

Welche Headerdatei muss ich hier einbinden?

von Gerhard G. (g_g)


Angehängte Dateien:

Lesenswert?

H. G. schrieb:

> Wenn ich z.B. die Funktion
> GPIOInit();

> Welche Headerdatei muss ich hier einbinden?

#include "LPC11xx.h"
#include "LPC11xx_GPIO.h"

Wenn alles ordentlich installiert wurde, findet deine Software die 
"LPC11xx.h" usw.. Hier wiederum stehen alle alle weiteren Informationen, 
wo z.B. GPIOInit() definiert ist (Include.. und Haeder... )

Einfacher wäre es sicher, wenn deine Software selbstständig ein Projekt 
eröffnen könnte. Da sind dann die Objekt-Path vorgegeben. (CooCox CoIDE)
Siehe Anlage

CooCox CoIDE is a free software product.

Visit: http://www.coocox.org/CooCox_CoIDE.htm

MfG. G.G.

von Roland H. (batchman)


Lesenswert?

Man muss nicht unbedingt die Funktionen verwenden, man kann auch 
klassisch "Register direkt" verwenden. Dafür sollte #include "LPC11xx.h" 
ausreichen.

Im Falle von GPIO kann "Register direkt" sogar obligatorisch sein, wenn 
die entsprechenden GPIO-Funktionen nicht als "static inline" ausgelegt 
sind - sie sind dann je nach Anwendungsfall u. U. schlicht zu langsam.

Beispiel:
1
void GPIO_SetValue(uint8_t portNum, uint32_t bitValue)
2
3
{
4
5
  LPC_GPIO_TypeDef *pGPIO = GPIO_GetPointer(portNum);
6
7
8
9
  if (pGPIO != NULL)
10
11
  {
12
13
    pGPIO->SET = bitValue;
14
15
  }
16
17
}

Der Funktionsaufruf frisst Zeit, "Pointer holen" kommt dazu, dann kommt 
die Indirektion.

D. h. folgendes hat das gleiche Resultat:
1
GPIO_SetValue(0, 1);
2
3
LPC_GPIOA->SET = 1;

aber die zweite Variante ist der Turbo.

Die Entscheidung "Peripherals library" oder "Register direkt" würde ich 
mir drei Mal überlegen.

Bei den stm32 setze ich die "standard peripherals library" ein, und es 
gibt m. E. drei große Probleme: Die semantische Lücke zwischen 
Datenblatt und der Library, die fehlende Abstraktion (die Funktionen 
sind oft nicht genug "high level") und schließlich ist das Zeugs nicht 
portabel zwischen stm32f1 und stm32f4. Muss nicht zwangsläufig für LPC 
gelten.

Es ist natürlich verlockend, z. B. im Falle von CAN/UART auf bestehende 
Funktionen aufsetzen zu können, die die Baud rate settings korrekt 
machen.

Die Kehrseite ist dann, dass z. B. im Falle des LPC177x_8xCMSIS_110506 
z. B. uint64_t Operationen für UART-Einstellungen eingebunden werden 
(muss für den CM0 nicht gelten). Generellt gilt, dass Code-Größe und 
RAM-Bedarf ansteigen.

von W.S. (Gast)


Lesenswert?

Roland H. schrieb:
> Die Entscheidung "Peripherals library" oder "Register direkt" würde ich
> mir drei Mal überlegen.

JA, das sollte man dreimal ganz dick unterstreichen!
Und nochwas, so ein Grundgerüst gefällt mir überhaupt nicht:

int main(void)
{ // Die Std. Peripherie nun Clocks, PLLs usw. einrichten
  SystemInit();
  //GPIOInit();
  while(1)
  {
  }
  return 0 ;
}

weil man eben nicht genau weiß, was da alles in SystemInit angelassen 
wird.
Ich rate eher dazu, all diese tollen für ein bestimmtes Board 
geschriebenen XYZ_Init Routinen wegzulassen und sich sein System selbst 
aufzusetzen. Dazu kann auch gehören, daß man sich seine eigene 
CPU-Headerdatei zusammenstellt, um unnötigen Ballast erst gar nicht in 
das Projekt kommen zu lassen.

W.S.

von H. G. (ledi)


Lesenswert?

Ok, das leuchtet ein.

Aber wo finde ich die Funktionen und deren Parameter?

Nach

GPIO_SetValue();

habe ich z.B. in der Headerdatei, im Datenblatt und im user manual 
vergebens gesucht.

Also ich bräuchte eine Art Start-up-Lektüre (am Besten mit Beispielen) 
damit ich einmal die Funktionen kennenlerne und weiß, was ich wo 
einbinden muss.
Hast du da für mich eine Empfehlung?

von Jürgen (jliegner)


Lesenswert?

Genau das ist der Punkt bei diesen Libs. Es scheint nirgends einen Bezug 
zum Usermanual des mc's zu geben. Eine vernünftige Doku zu den Libs gibt 
es aber nicht. Und wenn man die Funktionen der Lib aus den Headern und 
dem Code rausklaubt, hat man den mc immer noch nicht verstanden. Also 
lies das Manual des mc und setze die Register. Sowas wie:
1
LPC_GPIO0->DIR  |=  (1<<PinNummer);
2
LPC_GPIO0->DATA |=  (1<<PinNummer);
3
LPC_GPIO0->DATA &= ~(1<<PinNummer);

ist auch nicht schwieriger als ein GPIOSetDir und GPIOSetValue.

Besonders schön ist für den LPC11xx auch die Funktion:
1
void GPIOSetInterrupt( uint32_t portNum, uint32_t bitPosi, uint32_t sense,
2
      uint32_t single, uint32_t event )
3
{
4
  switch ( portNum )
5
  {
6
  case PORT0:
7
    if ( sense == 0 )
8
    {
9
    LPC_GPIO0->IS &= ~(0x1<<bitPosi);
10
    /* single or double only applies when sense is 0(edge trigger). */
11
    if ( single == 0 )
12
      LPC_GPIO0->IBE &= ~(0x1<<bitPosi);
13
    else
14
      LPC_GPIO0->IBE |= (0x1<<bitPosi);
15
    }
16
    else
17
      LPC_GPIO0->IS |= (0x1<<bitPosi);
18
    if ( event == 0 )
19
    LPC_GPIO0->IEV &= ~(0x1<<bitPosi);
20
    else
21
    LPC_GPIO0->IEV |= (0x1<<bitPosi);
22
  break;
23
   case PORT1:
24
    if ( sense == 0 )
25
    {
26
    LPC_GPIO1->IS &= ~(0x1<<bitPosi);
27
    /* single or double only applies when sense is 0(edge trigger). */
28
    if ( single == 0 )
29
      LPC_GPIO1->IBE &= ~(0x1<<bitPosi);
30
    else
31
      LPC_GPIO1->IBE |= (0x1<<bitPosi);
32
    }
33
    else
34
      LPC_GPIO1->IS |= (0x1<<bitPosi);
35
    if ( event == 0 )
36
    LPC_GPIO1->IEV &= ~(0x1<<bitPosi);
37
    else
38
    LPC_GPIO1->IEV |= (0x1<<bitPosi);  
39
  break;
40
  case PORT2:
41
    if ( sense == 0 )
42
    {
43
    LPC_GPIO2->IS &= ~(0x1<<bitPosi);
44
    /* single or double only applies when sense is 0(edge trigger). */
45
    if ( single == 0 )
46
      LPC_GPIO2->IBE &= ~(0x1<<bitPosi);
47
    else
48
      LPC_GPIO2->IBE |= (0x1<<bitPosi);
49
    }
50
    else
51
      LPC_GPIO2->IS |= (0x1<<bitPosi);
52
    if ( event == 0 )
53
    LPC_GPIO2->IEV &= ~(0x1<<bitPosi);
54
    else
55
    LPC_GPIO2->IEV |= (0x1<<bitPosi);  
56
  break;
57
  case PORT3:
58
    if ( sense == 0 )
59
    {
60
    LPC_GPIO3->IS &= ~(0x1<<bitPosi);
61
    /* single or double only applies when sense is 0(edge trigger). */
62
    if ( single == 0 )
63
      LPC_GPIO3->IBE &= ~(0x1<<bitPosi);
64
    else
65
      LPC_GPIO3->IBE |= (0x1<<bitPosi);
66
    }
67
    else
68
      LPC_GPIO3->IS |= (0x1<<bitPosi);
69
    if ( event == 0 )
70
    LPC_GPIO3->IEV &= ~(0x1<<bitPosi);
71
    else
72
    LPC_GPIO3->IEV |= (0x1<<bitPosi);    
73
  break;
74
  default:
75
    break;
76
  }
77
  return;
78
}

Der ganze Code muss dazugelinkt werden nur um eine einzelne Zeile 
Register setzen durch eine Funktion zu ersetzen. Was sense, single und 
event bedeuten, kann man auch nicht erahnen ohne das Usermanual gelesen 
zu haben.
Soviel kann man gar nicht brechen.

Mir kommt aber ein ganz anderer Verdacht auf, eventuell sind auch die 
Hersteller der tollen IDEs nicht unbeteiligt. So kriegt man schnell den 
Flash voll und grenzt die (codegrößenbeschränkten) Freeware-Versionen 
aus.
Das trifft hier zwar für CodeRed und die LPC11xx nicht zu da die nur 32k 
haben, aber große Programme brauchen auch länger um in den Flash geladen 
zu werden. Und Warten nervt.

Einen Vorteil haben die Libs aber. Es gibt sie. Und damit genügend 
Beispielcode wenn man mal Verständnisprobleme mit dem Usermanual hat.


@ Roland H.

Das gibt es bei LPC11xx nicht:
LPC_GPIOA->SET = 1

Die LPC11xx haben die Besonderheit mit dem MASKED_ACCESS 
Speicherbereich, den ich sonst noch nicht gesehen haben.

Setzen eines Pins z.B.
1
//  Pin als Ausgang konfigurieren
2
LPC_GPIO0->DIR  |=  (1<<PinNummer);
3
4
// Pin auf 1 setzen 
5
LPC_GPIO0->MASKED_ACCESS[1<<PinNummer]=1;
6
7
// Pin auf 0 setzen 
8
LPC_GPIO0->MASKED_ACCESS[1<<PinNummer]=0;

von H. G. (ledi)


Lesenswert?

Ok, hab das mal mit einem Beispiel versucht und mittels Debugger 
durchgesteppt.

Die LED (am Port0 Pin7) blinkt aber nicht! Was habe ich vergessen oder 
übersehen?
1
#ifdef __USE_CMSIS
2
#include "LPC11xx.h"
3
#endif
4
5
#include <cr_section_macros.h>
6
#include <NXP/crp.h>
7
8
__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;
9
10
#define PinNummer 7
11
12
int main(void)
13
{
14
  SystemInit();
15
16
  //  Pin als Ausgang konfigurieren
17
  LPC_GPIO0->DIR  |=  (1<<PinNummer);
18
19
  while(1)
20
  {
21
    // Pin auf 1 setzen
22
    LPC_GPIO0->MASKED_ACCESS[1<<PinNummer]=1;
23
24
    // Pin auf 0 setzen
25
    LPC_GPIO0->MASKED_ACCESS[1<<PinNummer]=0;
26
  }
27
  return 0 ;
28
}

von Children (Gast)


Lesenswert?

So schnell ist dein Auge nicht, das du etwas blinken sehen würdest.

Mach mal soetwas wie delay zwischen die Toggelei.

von H. G. (ledi)


Lesenswert?

Children schrieb:
> So schnell ist dein Auge nicht, das du etwas blinken sehen würdest.
>
> Mach mal soetwas wie delay zwischen die Toggelei.

Nein, ich habe den Debugger händisch bedient!
Also Zeile für Zeile (mit F5) ausführen lassen.

von Ralf (Gast)


Lesenswert?

> Die LED (am Port0 Pin7)...
Wie zählst du den Pin? Heisst der tatsächlich Pin7?
Wie ist die LED angeschlossen -> High- oder Low-aktiv?

Ralf

von H. G. (ledi)


Lesenswert?

@ Ralf

Pin von 0,1,2,3,4,5,6,7 = PIO0_7
LED ist active high

von Albert .. (albert-k)


Lesenswert?

Du hast vergessen den Clock für die GPIO's zu aktivieren. Weiss das 
genaue Register dazu nicht aus dem kopf, schau am besten im Manual nach.

von Jürgen (jliegner)


Lesenswert?

das entsprechende Bit steht nach dem Reset immer auf 1 laut Manual.
Trotzdem zur Sicherheit:
1
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6);

hier war noch ein Fehler:
1
// Set
2
LPC_GPIO0->MASKED_ACCESS[(1<<PinNummer)] = (1<<PinNummer);
3
4
5
/ Clr
6
LPC_GPIO0->MASKED_ACCESS[(1<<PinNummer)] = (0<<PinNummer);

War und ist auch ungetestet aus dem Kopf....

von H. G. (ledi)


Lesenswert?

Albert ... schrieb:
> Du hast vergessen den Clock für die GPIO's zu aktivieren. Weiss das
> genaue Register dazu nicht aus dem kopf, schau am besten im Manual nach.

Hab ich nun versucht mit:
1
LPC_SYSCON.SYSAHBCLKCTRL |= 0x8000;

Da erhalte ich aber die Fehlermeldung:
../src/main.c:26:12: error: request for member 'SYSAHBCLKCTRL' in 
something not a structure or union

Hat jemand ein konkretes Beispiel, wie man den Clock für die GPIO´s 
setzt?

von H. G. (ledi)


Lesenswert?

Ah, ja Pointer auf Struktur!
Danke!

von H. G. (ledi)


Lesenswert?

Es funktioniert!!!

Hier der Code:
1
#ifdef __USE_CMSIS
2
#include "LPC11xx.h"
3
#endif
4
5
#include <cr_section_macros.h>
6
#include <NXP/crp.h>
7
8
__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;
9
10
#define PinNummer 7
11
12
int main(void)
13
{
14
  SystemInit();
15
16
  LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6);
17
18
  //  Pin als Ausgang konfigurieren
19
  LPC_GPIO0->DIR  |=  (1<<PinNummer);
20
21
  int i,j;
22
  while(1)
23
  {
24
    // Pin auf 1 setzen
25
    LPC_GPIO0->MASKED_ACCESS[1<<PinNummer]= (1 << 7);
26
    for (i=0; i<=1000; i++)
27
    {
28
      for (j=0; j<=1000; j++)
29
      {
30
      }
31
    }
32
    // Pin auf 0 setzen
33
    LPC_GPIO0->MASKED_ACCESS[1<<PinNummer]= (0 << 7);
34
    for (i=0; i<=1000; i++)
35
    {
36
      for (j=0; j<=1000; j++)
37
      {
38
      }
39
    }
40
  }
41
  return 0 ;
42
}

von H. G. (ledi)


Lesenswert?

Jürgen Liegner schrieb:
> das entsprechende Bit steht nach dem Reset immer auf 1 laut Manual.
> Trotzdem zur Sicherheit:
>
>
1
> LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6);
2
>

Warum muss ich eigentlich das Register SYSAHBCLKCTRL über eine Struktur 
ansprechen?

Kann ich die Bits nicht irgendwie auch direkt setzen

z.B. SYSAHBCLKCTRL |= (1<<6);

von Lutz (Gast)


Lesenswert?

H. G. schrieb:
> Warum muss ich eigentlich das Register SYSAHBCLKCTRL über eine Struktur
> ansprechen?

Weil's im Header in Zeile 147 so definiert ist?

Wenn du dem compiler bzw. linker die Adresse des Registers anders 
"beibringst", kannst du das natürlich auch auf deine gewünschte Art 
schreiben. Ist in der LPC11xx.h aber nun mal so definiert.

Warum hast du eigentlich so eine verschachtelte Schleife für das delay 
gewählt? Der LPC11xx hat 32 bit; die kannst du ruhig voll ausnutzen. Ist 
ja einer der Vorteile.

von Jürgen (jliegner)


Lesenswert?

falls du man sowas wie das bekannt Delayus suchst:
1
// Microsecond delay loop-
2
void DelayuS(uint32_t uS)
3
{
4
  uint32_t CyclestoLoops;
5
6
  CyclestoLoops = SystemCoreClock;
7
  if (CyclestoLoops >= 2000000) 
8
    {
9
    CyclestoLoops /= 1000000;
10
    CyclestoLoops *= uS;
11
    } 
12
  else
13
    {
14
    CyclestoLoops *= uS;
15
    CyclestoLoops /= 1000000;
16
    }
17
18
  if  (CyclestoLoops <= 100)
19
    return;
20
  
21
  CyclestoLoops -= 100; // cycle count for entry/exit 100? should be measured
22
  CyclestoLoops /= 4; // cycle count per iteration- should be 4 on Cortex M0/M3
23
24
  if (!CyclestoLoops)
25
    return;
26
27
  // Delay loop for Cortex M3 thumb2
28
  asm volatile 
29
    (
30
    // Load loop count to register
31
    " mov r3, %[loops]\n"
32
    // loop start- subtract 1 from r3
33
    "loop: sub r3, #1\n"
34
   // " nop \n"
35
    // test for zero, loop if not
36
    " bne loop\n\n"
37
38
    : // No output registers
39
    : [loops] "r" (CyclestoLoops) // Input registers
40
    : "r3" // clobbered registers
41
    );
42
}

Das geht bei meinem lpc11c24 ziemlich genau. Natürlich nur wenn keine 
langen Interrupts dazwischen kommen. Und die SystemCoreClock Variable 
muss auch richtig stehen. Die wird aber von der cr_startup_lpc11.c vor 
der main() schon richtig gesetzt und steht bei mir auf 48000000. Durch 
die assembler-Befehle kann auch die Compileroptimierung nicht dazwischen 
funken. Ich möchte noch betonen, dass diese Funktion nicht von mir 
stammt, sondern mal aus einem der vielen Codeschnipsel im Web. Leider 
weiss ich aber nicht mehr woher das kam.
In realen Anwendeungen sollte sowas sowieso nur sehr begrenzt vorkommen, 
da wartet man besser mit dem SysTick oder einen anderen Timer anstelle 
die Rechenzeit zu verbraten.

von Bär_Tram (Gast)


Lesenswert?

Jürgen Liegner schrieb:
> Sowas wie:
> LPC_GPIO0->DIR  |=  (1<<PinNummer);
> LPC_GPIO0->DATA |=  (1<<PinNummer);
> LPC_GPIO0->DATA &= ~(1<<PinNummer);
>
> ist auch nicht schwieriger als ein GPIOSetDir und GPIOSetValue.

Roland H. schrieb:
> Beispiel:
> void GPIO_SetValue(uint8_t portNum, uint32_t bitValue)
>
> {
>
>   LPC_GPIO_TypeDef *pGPIO = GPIO_GetPointer(portNum);
>
>
>
>   if (pGPIO != NULL)
>
>   {
>
>     pGPIO->SET = bitValue;
>
>   }
>
> }
>
> Der Funktionsaufruf frisst Zeit, "Pointer holen" kommt dazu, dann kommt
> die Indirektion.
>
> D. h. folgendes hat das gleiche Resultat:
> GPIO_SetValue(0, 1);
>
> LPC_GPIOA->SET = 1;
>
> aber die zweite Variante ist der Turbo.
>
> Die Entscheidung "Peripherals library" oder "Register direkt" würde ich
> mir drei Mal überlegen.

Man muß dabei aber auch erwähnen, daß es mit den Libs viel 
universeller und auch gerade für Einsteiger einfacher ist. Mit der Lib 
hat man immer nur eine Funktion, in der man die Parameter setzen muß.
Wie würdest du es denn machen, wenn du z.B. einen DS18B20 angeschlossen 
hast? Zum Ansprechen von DQ müßtest du an jeder Stelle des 
Sensor-Codes sowas wie
LPC_GPIOx->FIODIR |= (1<<y);
schreiben, wobei x und y in jedem Projekt anders sind. Das gleiche für 
das Setzen und Löschen. Man könnte auch defines in der zugehörigen 
headerdatei machen wie
#define DQ_SET_DIR LPC_GPIO2->FIODIR|=(1<<7)
und diese im c.file einfach einsetzen.Es ist halt ein Kompromiss. 
Ansonsten stimmt alles genannte. Aber man kann auch bedenken, daß bei 
den 32-bittern üblicherweise Speicher- und Laufzeitreserven da sind, die 
das dann egalisieren.

von Roland H. (batchman)


Lesenswert?

Bär_Tram schrieb:
> Man muß dabei aber auch erwähnen, daß es mit den Libs viel
> universeller und auch gerade für Einsteiger einfacher ist.

In welchem Sinne "universell"? Portabel auf einen anderen ARM des 
gleichen Herstellers? Mit viel Glück passt die Library noch für die 
Nachfolgegeneration (die nächste CPU-Linie).

Bei STM32 geht das übrigens nicht - die Library zwischen stm32f1 und 
stm32f4 ist anders.

Ich bezweifle, dass es einfacher ist. Da gehen die Meinungen allerdings 
auseinander. Deshalb hatte ich geschrieben, dass man es sich überlegen 
sollte.

Bär_Tram schrieb:
> Mit der Lib
> hat man immer nur eine Funktion, in der man die Parameter setzen muß.

Mag sein, dass ich STM32 library geschädigt bin. In jedem Fall bekommst 
Du dort mit einer Library-Funktion z. B. keine PWM hin. Da hilft die 
Library gar nicht. Auf allen anderen Plattformen habe ich es dann von 
Anfang an gar nicht mehr gemacht.

> Wie würdest du es denn machen, wenn du z.B. einen DS18B20 angeschlossen
> hast? Zum Ansprechen von DQ müßtest du an jeder Stelle des
> Sensor-Codes sowas wie
> LPC_GPIOx->FIODIR |= (1<<y);
> schreiben, wobei x und y in jedem Projekt anders sind. Das gleiche für
> das Setzen und Löschen. Man könnte auch defines in der zugehörigen
> headerdatei machen wie
> #define DQ_SET_DIR LPC_GPIO2->FIODIR|=(1<<7)
> und diese im c.file einfach einsetzen.

Ja, so ähnlich. Um den Begriff "universell" aufzugreifen ;-)

Zunächst definiere ich den GPIO-Pin in einer config.hpp mittels #defines 
(port + pin - bei Dir das x und das y). Dann habe ich GPIO-Makros, 
welche in der Logik-Schicht verwendet werden, und je für AVR/diverse 
ARMs/MSP430/Renesas RX/PIC32 den optimalen GPIO-Aufruf einsetzen. Das 
ist portabel, schont den Flash/Stack und ist am schnellsten. 
Diesbezüglich keine Kompromisse - der liegt darin, diesen Umweg 
vorgesehen zu haben. Ausserdem giesst der Präprozessor die Konfiguration 
so fest in den Code, dass der Pin zur Laufzeit nicht geändert werden 
kann.

Z. B. beim lpc1769 ungefähr so (alles etwas vereinfacht - hier wird das 
bit-banding ignoriert):
1
#define GPIO_MODE_OUT(port, pin) \
2
  CONCAT2(LPC_GPIO, port)->FIODIR |= (1 << pin)

und beispielsweise beim atxmega256a3
1
#define GPIO_MODE_OUT(port, pin) \
2
  CONCAT2(PORT, port.DIRSET) = (1 << pin)

In diesem Fall also ohne "read-modify-write".

Immer wenn eine neue CPU auf dem Tisch landet, dann wird die GPIO-API 
einmalig und zentral ergänzt. Die eigentlichen Programme bleiben 
unberührt.

Selbst wenn es die erste und auf längere Sicht die einzige Plattform 
ist, würde ich immer einen Mini-HAL-Layer einbauen, und sei es nur um 
dort die Möglichkeit zu haben, Ergänzungen/Verbesserungen/zusätzliche 
Einstellungen zentral vornehmen zu können.

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.