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.
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
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
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.
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.
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.
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
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 | }
|
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 …
Auf der Arduino Seite wurden 48MHz angegeben, bin davon ausgegangen. Wenn ich das ganze mit 1MHz angebe, passt es.
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.
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.