Forum: Mikrocontroller und Digitale Elektronik Atmel ARM Tutorial C/C++ gesucht


von Uhl (Gast)


Lesenswert?

Hallo,

ich bin neu in der Welt der ARMs und möchte gerne einen Arduino Zero 
programmieren ohne den ganzen Arduino kram herum.

Gibt es irgendwo eine Erläuterung/Tutorial wie ich da am besten anfangen 
kann? Bei den AVRs habe ich mich immer gerne hier an den Tutorials und 
dem Datenblatt angelehnt.

Den Arduino Zero habe ich im Atmel Studio bereits eingebunden und soll 
über die SWD programmiert werden.

Nur fehlt mir nun der entscheide Anstoß,
wie werden die Ports/Pins initialisiert, und wie werden die angesteuert.

Mir würde für den Anfang reichen, wenn ich die I/Os mit Tastern und 
low-current Leds ansprechen kann. Den Rest werde ich dann später 
nachholen.

Alternativ würde ich mir auch über eine Lektüre/Buchvorschlag freuen, 
der die SAMD Serie bearbeitet.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Hier mal mein Primitiv-Testprogramm, mit dem ich (auf einem selbst
gebauten Testboard) den Zustand eines Jumpers abfrage und entsprechend
eine LED setze.

Das sollte einigermaßen das Handling der Parallelports beschreiben.
1
#include "sam.h"
2
3
void _init(void)
4
{
5
}
6
7
int
8
main(void)
9
{
10
  /* X6:8 PA28: input with pulldown */
11
  PORT->Group[0].PINCFG[28].bit.INEN = 1;
12
  PORT->Group[0].PINCFG[28].bit.PULLEN = 1;
13
  PORT->Group[0].OUTCLR.bit.OUTCLR = (1 << 28);
14
15
  /* X6:7 PB9: input with pullup */
16
  PORT->Group[1].PINCFG[9].bit.INEN = 1;
17
  PORT->Group[1].PINCFG[9].bit.PULLEN = 1;
18
  PORT->Group[1].OUTSET.bit.OUTSET = (1 << 9);
19
20
  /* LED9: PA16, active low */
21
  PORT->Group[0].DIRSET.bit.DIRSET = (1 << 16);
22
  PORT->Group[0].OUTSET.bit.OUTSET = (1 << 16);
23
24
  for (;;)
25
    {
26
      if ((PORT->Group[0].IN.bit.IN & (1 << 28)) != 0)
27
        {
28
          /* PA28 pulled high */
29
          /* turn on LED */
30
          PORT->Group[0].OUTCLR.bit.OUTCLR = (1 << 16);
31
          /* wait for button release */
32
          while ((PORT->Group[0].IN.bit.IN & (1 << 28)) != 0)
33
            {
34
            }
35
        }
36
37
      if ((PORT->Group[1].IN.bit.IN & (1 << 9)) == 0)
38
        {
39
          /* PB9 pulled low */
40
          /* turn off LED */
41
          PORT->Group[0].OUTSET.bit.OUTSET = (1 << 16);
42
          /* wait for button release */
43
          while ((PORT->Group[1].IN.bit.IN & (1 << 9)) == 0)
44
            {
45
            }
46
        }
47
    }
48
49
  return 0;
50
}

: Bearbeitet durch Moderator
von Ben S. (theben)


Lesenswert?

Schau dir mal das Atmel Software Framework an. Dort dibt es massig 
leicht verstänliche manuals wie diese hier: 
http://www.atmel.com/images/Atmel-42118-SAM-D20-D21-Serial-USART-Driver-SERCOM-USART_Application-Note_AT03256.pdf

Weiter am ende des pdf's ist immer ein example. da reicht kopieren 
einfügen und es läuft. der rest geht dann ziemlich schnell

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ben S. schrieb:
> Schau dir mal das Atmel Software Framework an.

Dann kann er auch bei Arduino bleiben.  Ist gleichermaßen aufgebläht,
aber wenigstens in sich schlüssig.  ASF ist einfach nur Overhead,
teilweise eine Funktion, nur um ein Bit in einem Steuerregister zu
setzen.

von Uhl (Gast)


Lesenswert?

Hallo,

die ARM Programmierung scheint sich doch sehr stark von den AVRs 
abzusetzen. Einfache Testprogramme "Blink" sind für mich so noch nicht 
schlüssig, da es keine delay Befehl gibt.
1
#include "sam.h"
2
3
int main(void)
4
{
5
  /* Initialize the SAM system */
6
  SystemInit();
7
  
8
  // PA17 als Ausgang
9
  PORT->Group[0].DIRSET.bit.DIRSET |= (1<<17);
10
  
11
  /* Replace with your application code */
12
  while (1)
13
  {
14
    static uint32_t ticks = 0;
15
    static uint8_t state = 0;
16
    
17
    if(++ticks >= 100000)
18
    {
19
      ticks = 0;
20
      if(state) PORT->Group[0].OUTSET.bit.OUTSET = (1<<17);
21
      else PORT->Group[0].OUTCLR.bit.OUTCLR = (1<<17);
22
23
      state ^= 1;
24
    }
25
  }
26
}

Habe jetzt einfach mal ein einfaches Blink Programm geschrieben anhand 
des Snippets oben. Wobei hier keine Zeit einstellbar ist. Habe einfach 
mit dem Wert von ticks experimentiert, um ein Blinken zu erzeugen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Uhl schrieb:
> da es keine delay Befehl gibt.

Schau mal da:

Beitrag "[ARM / Cortex-M0(+)] delay-Funktionen "avr-libc style""

Nicht nur meine Variante ganz oben, sondern auch A. K.s Version im
unteren Teil des Threads ist recht interessant.

von Uhl (Gast)


Lesenswert?

Hallo Jörg,

danke für deine kleine delay-Lib. Leider geht die so scheinbar nicht.
1
#define F_CPU  32000000UL
2
3
#include "sam.h"
4
#include "delay.h"
5
6
int main(void)
7
{
8
  SystemInit();
9
  
10
  // PA17 (LED) als Ausgang setzen
11
  PORT->Group[0].DIRSET.bit.DIRSET = (1<<17);
12
13
  while (1)
14
  {
15
    PORT->Group[0].OUTSET.bit.OUTSET = (1<<17);
16
    delay_ms(500);
17
    PORT->Group[0].OUTCLR.bit.OUTCLR = (1<<17);
18
    delay_ms(500);
19
  }
20
}
Ich komme an dem delay nicht vorbei. Der Portpin wird einmalig gesetzt. 
Dann ist beim delay schluss. Das viel mir auf, als ich noch ein weiteres 
delay_ms nach dem Eintritt in die while-Schleife gesetzt habe.

: Bearbeitet durch Moderator
von Uhl (Gast)


Lesenswert?

1
#define F_CPU  32000000UL
2
3
#include "sam.h"
4
#include "delay.h"
5
6
int main(void)
7
{
8
  SystemInit();
9
10
  // PA17 (LED) als Ausgang setzen
11
  PORT->Group[0].DIRSET.bit.DIRSET = (1<<17);
12
13
  while (1)
14
  {
15
    PORT->Group[0].OUTSET.bit.OUTSET = (1<<17);
16
    delay_ms(500);
17
    PORT->Group[0].OUTCLR.bit.OUTCLR = (1<<17);
18
    delay_ms(500);
19
  }
20
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Uhl schrieb:
> Ich komme an dem delay nicht vorbei.

Bist du dir sicher, dass es nicht einfach nur sehr lange dauert?

Default clock ist, wenn ich mich recht erinnere, 2 MHz, du hast aber
32 MHz ins F_CPU geschrieben.  Sofern dein SystemInit() nicht gerade
diese 32 MHz wirklich einstellt, werden die Verzögerungen ziemlich
lange brauchen …

von Uhl (Gast)


Lesenswert?

Auf der Arduino Seite wurden 48MHz angegeben, bin davon ausgegangen. 
Wenn ich das ganze mit 1MHz angebe, passt es.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Uhl schrieb:
> Auf der Arduino Seite wurden 48MHz angegeben

Schätzungsweise wird die Arduino-Initialisierung die FLL so starten,
dass sie die 48 MHz generiert.

Ist nur eben halt nicht der Takt, den der Controller nach einem Reset
hat.

von Uhl (Gast)


Lesenswert?

Also so wie ich das sehe, ist der interne Clock nach der Initialisierung 
bei ~ 1MHz. Das war mir schon recht neu. Bei den AVRs habe ich einfach 
einmalig die Fuses gesetzt und dann war das damit erledigt.

Das ist auch das größere Problem. Die ARMs sind doch schon recht 
verschieden gegenüber den AVRs. Hatte mir gedacht, das ganze wäre 
innerhalb von Atmel noch vertretbar.

Bei anderen Herstellern (STM) ist das ganze aber auch nciht wirklich 
angenehmer. Ein weiterer Nachteil ist der große Markt. Bei den 8Bitter 
hat man mehr oder weniger die Entscheidung, AVR oder PIC. Daraus 
resultiert, dass man deutlich besser an die Tutorials kommt.

Ich finde für den ARM (Atmel) überhaupt keine einleuchtenden und gut 
strukturierten Anleitungen.

Ich kenne die Programmierung von ARMs derzeit nur auf dem Raspberry Pi. 
Hier besteht aber ein riesiger Overhead, das ganze ist deutlich 
langsamer. Man lernt dort kaum noch die Hardware. Gearbeitet habe ich 
dort mit PiGPIO und wiringPi.

Hier finde ich das AVR-GCC Tutorial sehr lobenswert. Sicherlich in 
einigen Punkten auch zu ausführlich, aber ansonsten gut durchdacht. Man 
kommt schnell an seine Ziele. Das Augenmerk lege ich bei mir auf die SPI 
Kommunikationen sowie direkte Portsteuerung. Ggf. noch den ein oder 
anderen ADC nutzen können.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Uhl schrieb:
> Das war mir schon recht neu.

Steht aber im Datenblatt. ;-)
1
13.7  Clocks after Reset
2
3
      On any reset the synchronous clocks start to their initial state:
4
        * OSC8M is enabled and divided by 8
5
        * GCLK_MAIN uses OSC8M as source
6
        * CPU and BUS clocks are undivided

> Bei den AVRs habe ich einfach
> einmalig die Fuses gesetzt und dann war das damit erledigt.

Weil sie den Takt nicht zur Laufzeit umschalten konnten (war ein
Problem mit deren Flash-Implementierung).  Bei den Xmegas ging das
auch zur Laufzeit, gar nicht so unähnlich zu den SAMDs.

> Das ist auch das größere Problem. Die ARMs sind doch schon recht
> verschieden gegenüber den AVRs.

An den Xmegas sind sie etwas näher dran als an den alten AVRs, aber
komplexer sind sie natürlich trotzdem.

> Ich finde für den ARM (Atmel) überhaupt keine einleuchtenden und gut
> strukturierten Anleitungen.

Für die älteren findet man wenigstens noch Appnotes (SAM3/4), aber
deren Peripherals wiederum wirken wie aus der Zeit der Dinosaurier:
schwerfällig, mit Features überfrachtet, umständlich.

Dann irgendwann hat sich Atmel drauf versteift, allen ihr ***es ASF
aufzudrängeln, und nun sind die entsprechenden „Appnotes“ oft nur
noch ein Wiederkäuen der ASF-API-Dokumentation. :-((

> Ich kenne die Programmierung von ARMs derzeit nur auf dem Raspberry Pi.

Das ist aber auch 'ne ganz andere Nummer als so ein Cortex-M0+.

Wenn du mit den SAMDs weitermachen willst, solltest du als erstes
versuchen, das ganze Taktsystem zu verstehen.  Zentrales Element
sind dabei die „generic clock generators“ (GCLK), über die läuft das
alles.  Die kann man auf verschiedene Weise an Taktquellen binden,
miteinander verketten, die Ausgänge für Timer oder anderweitig als
Takt benutzen.

Das Verständnis dabei etwas verkomplizierend ist, dass es davon acht
Stück gibt.  Man hat diesen aber nicht acht komplette Registersätze
spendiert, sondern die GENDIV- und GENCTRL-Register enthalten ein
Feld, welches den Generator benennt, für den die getroffenen
Einstellungen gelten sollen.  Dadurch muss man diese beiden Register
immer komplett mit allen Bits auf einmal beschreiben.

Bei mir sieht die Initialisierung auf den extern angeschlossenen
16-MHz-Quarz sowie die Aktivierung des 32-kHz-Quarzes derzeit so aus:
1
#define WAIT_GCLK_SYNC()    while(GCLK->STATUS.bit.SYNCBUSY)
2
#define WAIT_XOSC32K_READY()   while((SYSCTRL->PCLKSR.bit.XOSC32KRDY) == 0 )
3
4
//…
5
  SYSCTRL->XOSC32K.bit.STARTUP = 6; /* ≈ 1 s */
6
  SYSCTRL->XOSC32K.bit.XTALEN = 1;  /* this is a crystal */
7
  /* Auto amplitude control – see errata, does not work */
8
  SYSCTRL->XOSC32K.bit.AAMPEN = false;
9
  /* 32KHz output Enable/Disable */
10
  SYSCTRL->XOSC32K.bit.EN32K = true;
11
  SYSCTRL->XOSC32K.bit.ONDEMAND = false;
12
  SYSCTRL->XOSC32K.bit.RUNSTDBY = true;
13
14
  SYSCTRL->XOSC32K.bit.ENABLE = true;
15
16
  /* Check the XOSC32K Clock source is ready to use */
17
  WAIT_XOSC32K_READY();
18
19
  /* Generic clock generator 1: use XOSC32K without prescaler */
20
  GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) | GCLK_GENDIV_DIV(0);
21
  WAIT_GCLK_SYNC();
22
  GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(1) | GCLK_GENCTRL_SRC_XOSC32K |
23
    GCLK_GENCTRL_GENEN | GCLK_GENCTRL_OE;
24
  WAIT_GCLK_SYNC();
25
26
  /* Enable RTC clock as GCLK[1] */
27
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(1) | GCLK_CLKCTRL_ID_2;
28
  PORT->Group[1].PMUX[23 / 2].bit.PMUXO = PORT_PMUX_PMUXE_H_Val;
29
  PORT->Group[1].PINCFG[23].bit.PMUXEN = 1;

Die Initialisierung der DFLL habe ich im Moment auch noch nicht
probiert.  Mit dieser kann man aus dem 32-kHz-Takt (aber nur aus
diesem!) den zentralen Takt von 48 MHz erzeugen.  Aber Achtung,
der Flash ist nicht so schnell!  Wenn man die CPU mit 48 MHz
werkeln lassen will, muss man dem Flash Waitstates aufbrummen.
Wie viele, hängt noch von der Betriebsspannung ab.  Damit ist ein
CPU-Takt von 48 MHz eigentlich nur dann interessant, wenn man
(zumindest teilweise) Code aus dem RAM ausführen möchte (sowas ging
beim AVR nicht, beim ARM geht das).  Hat man das nicht vor, kann
man die Taktfrequenz lieber so limitieren, dass der Flash ohne
Waitstates betrieben werden kann, dann spart man Energie.

An was man sich beim ARM (nicht nur von Atmel) auch gewöhnen muss
ist, dass die peripheren Takte standardmäßig größtenteils nach dem
Reset abgeschaltet sind (damit die Baugruppen keinen Strom brauchen).
Nur das nötigste ist initial eingeschaltet, damit man die Kiste
erstmal in Schwung bekommen kann.

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.