AVR32 Tutorial: Unterschied zwischen den Versionen

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche
Keine Bearbeitungszusammenfassung
 
(48 dazwischenliegende Versionen von 8 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:




Dieses Tutorial soll eine Einführung in die [[AVR32]](32-bit) Architektur erleichtern. Die [[AVR32]] Architektur ist im Vergleich zu den 8 Bit [[AVR]]s oder PICs schwieriger zu erlernen und deshalb nicht unbedingt für [[Mikrocontroller]]-Anfänger geeignet.Die meisten Änderungen im Vergleich der 32-bit Achitektur mit der 8-bit liegen im Interrupt handeling und beim Register Zugriffe.  
Dieses Tutorial soll eine Einführung in die [[AVR32]](32-bit)-Architektur erleichtern. Die [[AVR32]]-Architektur ist im Vergleich zu den 8 Bit [[AVR]]s oder PICs schwieriger zu erlernen und deshalb nicht unbedingt für [[Mikrocontroller]]-Anfänger geeignet. Die meisten Änderungen der 32-bit- Architektur im Vergleich zur 8-bit-Architektur liegen im Interrupt-Handling und beim Register-Zugriff.  


{{Warnung |
{{Warnung |
Die Besonderheit dieses Tutorials ist es, komplett auf das AVR32 Software Framework von Atmel zu verzichten.
Die Besonderheit dieses Tutorials ist es, komplett auf das AVR32 Software-Framework von Atmel zu verzichten, um so besser verstehen zu können wie der UC funktioniert.
}}
}}
Der vorgestellte Code ist für einen AT32UC3L064 zugeschnitten und dort auch getestet.
Der vorgestellte Code ist für einen AT32UC3L064 zugeschnitten und dort auch getestet.
Zeile 10: Zeile 10:
=== Motivation und Zielgruppe ===
=== Motivation und Zielgruppe ===
Dieses Tutorial wurde geschrieben, da es bisher noch kein Tutorials zu Atmels 32-Bit-Serie gibt.
Dieses Tutorial wurde geschrieben, da es bisher noch kein Tutorials zu Atmels 32-Bit-Serie gibt.
Deshalb Richtet sich das Tutorial in erster Lienie an alle die, die von der 8-bit Serie(ATmega/Attiny/Xmega...)auf die 32-bit-Prozessoren umsteigen möchten.
Deshalb richtet sich das Tutorial in erster Linie an alle, die von der 8-bit Serie (ATmega/Attiny/Xmega...) auf die 32-bit-Prozessoren umsteigen möchten.
Jedoch wurde das Tuorial auch so gehalten, dass es auch von allen anderen, die in der Atmel 32-bit Serie einsteigen wollen, verwendet werden kann.
Das Tutorial wurde so gestaltet, dass es auch von allen anderen, die in die Atmel 32-bit Serie einsteigen wollen, verwendet werden kann.
Sollte es Verbesserungsvorschläge oder Ideen geben, mit denen das Tutorial verbessert werden kann bitte sendet eine PN oder Hinterlässt ein im Diskussions-Bereich einen Kommentar an.
Sollte es Verbesserungsvorschläge oder Ideen geben, mit denen das Tutorial verbessert werden kann: Bitte sendet eine PN oder hinterlasst im Diskussionsbereich einen Kommentar.
--[[Benutzer:Basti195|Basti195]] ([[Benutzer Diskussion:Basti195|Diskussion]]) 11:52, 22. Aug. 2014 (CEST)<br />
--[[Benutzer:Basti195|Basti195]] ([[Benutzer Diskussion:Basti195|Diskussion]]) 11:52, 22. Aug. 2014 (CEST)<br />
<br />
<br />
Zeile 18: Zeile 18:
=== Benötigte Vorkenntnisse ===
=== Benötigte Vorkenntnisse ===


Da der [[GCC]] verwendet wird, ist es unabdinglich gute C Kenntnisse zu besitzen. Ohne das Atmel Software Framework ist es außerdem notwendig, einige grundlegende Kenntnisse in Assembler zu haben, die man sich ggf., wenn man die entsprechenden Stellen des Tutorials erreicht, noch nachträglich aneignen kann. Es werden Makefiles zum Einsatz kommen, weshalb man sich auch damit und den gängigen GNU-Entwicklungstools vertraut machen sollte.<br />
Da der [[GCC]] verwendet wird, ist es notwendig gute C-Kenntnisse zu besitzen. Ohne das Atmel Software Framework ist es außerdem notwendig, einige grundlegende Kenntnisse in Assembler zu haben, die man sich ggf., wenn man die entsprechenden Stellen des Tutorials erreicht, noch nachträglich aneignen kann.<br />


=== Benötigte Software ===
=== Benötigte Software ===


Grundsätzlich kann man mit dem [[AVR32 Studio]] und der Atmel AVR32-Toolchain (mittlerweile auch mit dem [[Atmel Studio]]) alle Aufgaben übernehmen. Es geht aber auch ohne das AVR32 Studio. Man benötigt dann einen Editor, die Atmel AVR32-Toolchain und unter Windows ist cygwin zu empfehlen, bzw. man kann auch einfach unter Linux entwickeln.
Grundsätzlich kann man mit dem [[AVR32 Studio]] und der Atmel AVR32-Toolchain (mittlerweile auch mit dem [[Atmel Studio]]) alle Aufgaben übernehmen. Es geht aber auch ohne das AVR-Studio. Man benötigt dann einen Editor, die Atmel AVR32-Toolchain und unter Windows ist cygwin zu empfehlen, bzw. man kann auch einfach unter Linux entwickeln.


Die Atmel AVR32-Toolchain bekommt man über die Atmel-Homepage (Quellcode oder fertige Installer) oder alternativ hier[http://avr32linux.org/twiki/bin/view/Main/GettingStarted] den Quellcode.
Die Atmel AVR32-Toolchain bekommt man über die Atmel-Homepage (Quellcode oder fertige Installer) oder alternativ hier den Quellcode [http://avr32linux.org/twiki/bin/view/Main/GettingStarted].




Zeile 36: Zeile 36:
<br />
<br />
Benötigt werden  
Benötigt werden  
* Prozessor: z.B. AT32UC3L064 <br />
* Prozessor: z.B. AT32UC3L064 <br />
* Programmer: z.B. JTAG<br/>
* Programmer: z.B. JTAG<br/>
* Board:      z.B. ein Devboard
* Board:      z.B. ein Dev-board
* Oszilloskop -> zum Debuggen<br />
* Oszilloskop -> zum Debuggen<br />
<br />
<br />
Zeile 49: Zeile 49:
Leider funktioniert nur selten alles wie geplant. Deshalb ist es hier umso wichtiger, die Projekte genau zu dokumentieren, da so deutlich schneller Fehler gefunden werden können.<br />
Leider funktioniert nur selten alles wie geplant. Deshalb ist es hier umso wichtiger, die Projekte genau zu dokumentieren, da so deutlich schneller Fehler gefunden werden können.<br />
Schleicht sich doch einmal ein Fehler in den Code und funktioniert es nicht, ist es sehr hilfreich, einen Debugger zu haben. Alternativ kann man auch auf den eingeschränkten Simulator vom [[Atmel Studio]] zurückgreifen.<br />
Schleicht sich doch einmal ein Fehler in den Code und funktioniert es nicht, ist es sehr hilfreich, einen Debugger zu haben. Alternativ kann man auch auf den eingeschränkten Simulator vom [[Atmel Studio]] zurückgreifen.<br />
Wie geht man nun am besten vor: <br />
Am besten geht man dabei folgendermaßen vor: <br />


* Fehler lokalisieren: über ein Oszilloskop kann herausgefunden werden, ob es sich um ein Prozessor-Fehler handelt oder um ein Fehler eines Fremdgerätes.<br /><br />
* Fehler lokalisieren: über ein Oszilloskop kann herausgefunden werden, ob es sich um einen Prozessor-Fehler handelt oder um ein Fehler eines Fremdgerätes.<br /><br />


* Zu nächst einmal sollte der Code nochmals durchgearbeitet werden, um so sicher zu stellen, dass nicht wichtige Teile vergessen wurden.<br /><br />
* Zunächst einmal sollte der Code nochmals durchgearbeitet werden, um so sicher zu stellen, dass nicht wichtige Teile vergessen wurden.<br /><br />


* Nun sollte das Programm mit dem Debugger Schritt für Schritt durchgegangen werden um zu überprüfen, ob wirklich alle Bits in den richtigen Registern gesetzt worden sind. Manchmal löst ein Bit ein Ereignis aus, welches andere Bits löscht!<br /><br />
* Nun sollte das Programm mit dem Debugger Schritt für Schritt durchgegangen werden um zu überprüfen, ob wirklich alle Bits in den richtigen Registern gesetzt worden sind. Manchmal löst ein Bit ein Ereignis aus, welches andere Bits löscht!<br /><br />


* Ebenfalls sollte im Power Management überprüft werden, ob die Funktionen aktiv sind.
* Ebenfalls sollte im Power-Management überprüft werden, ob die Funktionen aktiv sind.
<br />
<br />
<br />
<br />
Zeile 63: Zeile 63:
== Register Zugriff ==
== Register Zugriff ==
Der [[Register]]zugriff bei der [[AT32-Serie]] ist etwas anders als bei der AT8-Serie.
Der [[Register]]zugriff bei der [[AT32-Serie]] ist etwas anders als bei der AT8-Serie.
Um hier ein [[Register]] zu beschreiben muss nicht wie z.B. an einem ATmega8 der Befehl folgend aussehen :  
Um hier ein [[Register]] zu beschreiben muss nicht wie z.B. an einem ATmega8 der Befehl wie folgt aussehen:  
     <syntaxhighlight lang="c">  DDRD = 0xff; </syntaxhighlight>
     <syntaxhighlight lang="c">  DDRD = 0xff; </syntaxhighlight>
Bei der 32-bit Serie wird ein Register folgendermaßen beschrieben:  
Sondern bei der 32-bit Serie wird ein Register stattdessen folgendermaßen beschrieben:  
     <syntaxhighlight lang="c">  AVR32_"Funktion".[Chanel].Register = 0xff;</syntaxhighlight>
     <syntaxhighlight lang="c">  AVR32_"Funktion".[Channel].Register = 0xff;</syntaxhighlight>
Vor jedem [[Register]]befehl steht zunächst das "AVR32_". Hieran wird dann eine Funktion z.B. [[SPI]] angehängt, damit der [[Prozessor]] weiß wem er das Register zuweisen muss.
Vor jedem [[Register]]Befehl steht zunächst das "AVR32_". Hieran wird dann eine Funktion (z.B. [[SPI]]) angehängt, damit der [[Prozessor]] weiß wem er das Register zuweisen muss.
Die Unterfunktion kommt dann zum Einsatz, wenn es mehrere Channels gibt, wie z.B. 3 [[Timer]] oder 2 GPIO-lines. Hier muss dann die Zahl in [] geschrieben werden.
Die Unterfunktion kommt dann zum Einsatz, wenn es mehrere Channels gibt, wie z. B. 3 [[Timer]] oder 2 GPIO-lines. Hier muss dann die Zahl in "[ ]" geschrieben werden.
Zum Schluss muss noch das entsprechende Register angesprochen werden ".register".
Zum Schluss muss noch das entsprechende Register angesprochen werden ".Register".


Der gesamte Befehl sieht dann am Ende so aus:
Der gesamte Befehl sieht dann am Ende so aus:
      
      
     <syntaxhighlight lang="c">  AVR32_TC0.chanel [1].cr = 1<<8;</syntaxhighlight><br />
     <syntaxhighlight lang="c">  AVR32_TC0.channel[1].cr = 1<<8;</syntaxhighlight><br />


== GPIO - General Purpose Input Output ==
== GPIO - General Purpose Input Output ==
Zeile 80: Zeile 80:


=== IO-Zugriff ===
=== IO-Zugriff ===
Bei der 32-Bit Serie ist die Ansteuerung der Pins etwas anders gelöst: hier kann ein Port nicht mehr mit DDRx und PORTx beschrieben werden, sondern dies geschieht über ein spezielles GPIO Register, wobei man sich auch hier entsprechende #DEFINEs machen kann.
Bei der 32-Bit Serie ist die Ansteuerung der Pins etwas anders gelöst: Hier kann ein Port nicht mehr mit DDRx und PORTx beschrieben werden, sondern dies geschieht über ein spezielles GPIO-Register, wobei man sich auch hier entsprechende #DEFINEs machen kann.
   
   
Der GPIO Befehle sind folgendemaßen aufgebaut:  
Die GPIO Befehle sind folgendermaßen aufgebaut:  


   <syntaxhighlight lang="c">    AVR32_GPIO.port[X].register = 0xff; </syntaxhighlight>
   <syntaxhighlight lang="c">    AVR32_GPIO.port[X].register = 0xff; </syntaxhighlight>


Der Teil AVR32_GPIO.port[1/2] bezeichnet die entsprechenden Ports (A oder B). Mit dem ''.registername'' wird dann das entsprechende Register angesprochen. Nun muss nur noch eine 1 auf das entsprechende Bit gesetzt werden. Hier gilt, dass der erste Pin auf der 0ten Position steht und der zweite auf Position 1...
Der Teil AVR32_GPIO.port[1/2] bezeichnet die entsprechenden Ports (A oder B). Mit dem ''.Register-Name'' wird dann das entsprechende Register angesprochen. Nun muss nur noch eine 1 auf das entsprechende Bit gesetzt werden. Hier gilt, dass der erste Pin auf der 0ten Position steht und der zweite auf Position 1...




Um ein Siganal aus dem UCxx zu kriegen, muss zunächst das GPIO Modul aktiviert werden. Dies wird über das GPIO Enable Register(gper) gemacht.
Um ein Signal aus dem UCxx zu bekommen, muss zunächst das GPIO-Modul aktiviert werden. Dies wird über das GPIO Enable Register(gper) gemacht.


     <syntaxhighlight lang="c">  AVR32_GPIO.port[0].gper = 1<<13 // Port A Pin 13 </syntaxhighlight>
     <syntaxhighlight lang="c">  AVR32_GPIO.port[0].gper = 1<<13 // Port A Pin 13 </syntaxhighlight>




Der Pin wird mit dem OVR (Output Value Register) Register auf high gezogen
Der Pin wird mit dem OVR (Output Value Register) auf high gezogen
        
        
     <syntaxhighlight lang="c"> AVR32_GPIO.port[1].ovr = 1<<13 // Port B Pin 13 auf 3,3V''</syntaxhighlight>
     <syntaxhighlight lang="c"> AVR32_GPIO.port[1].ovr = 1<<13 // Port B Pin 13 auf 3,3V''</syntaxhighlight>
Zeile 104: Zeile 104:




Um nun den Wert eines Pins auslesen zu können, muss man sich das ''.pvr''(Port Value Register ) Register anschauen. Ist das entsprechende Bit gesetzt, ist der Port logisch high.<br />
Um nun den Wert eines Pins auslesen zu können, muss man sich das ''.pvr''(Port Value Register ) anschauen. Ist das entsprechende Bit gesetzt, ist der Port logisch high.<br />
 


=== Modul-Konfiguration ===
=== Modul-Konfiguration ===


Die AT32-Prozessoren verfügen über das Feature, dass Hardware-Funktionen nicht auf einen speziellen Pin gebunden sind, sondern sich muliplexen lassen. <br>
Die AT32-Prozessoren verfügen über das Feature, dass Hardware-Funktionen nicht an einen speziellen Pin gebunden sind, sondern sich multiplexen lassen. <br>
Dies hat den Vorteil, dass man nicht mehr an einen bestimmten Pin gebunden ist, sondern dass man den Prozessor an das Layout des Schaltplans anpassen kann. Jedoch macht dies die Konfiguration deutlich komplizierter.  
Dies hat den Vorteil, dass man nicht mehr an einen bestimmten Pin gebunden ist, sondern dass man den Prozessor an das Layout des Schaltplans anpassen kann. Jedoch macht dies die Konfiguration deutlich komplizierter.  




Jeder Port hat die Möglichkeit, bis zu 8 A-H verschiedene Hardware-Funktionen zu übernehmen (Datenblatt: Package and Pinout)   
Jeder Port hat die Möglichkeit, bis zu 8 (A-H) verschiedene Hardware-Funktionen zu übernehmen (Datenblatt: Package and Pinout)   


Nun muss der entsprechende Port im PMRx (GPIO) die entsprechenden Funktion zugewiesen bekommen.
Nun muss der entsprechende Port im PMRx (GPIO) der entsprechenden Funktion zugewiesen bekommen.
<br /><br />
<br /><br />


Zeile 134: Zeile 133:
<br />
<br />
Die prozessorspezifische Tabelle befindet sich unter dem Punkt "Package and Pinout"-"Peripheral Multiplexing on I/O lines" im Datenblatt. <br /><br />
Die prozessorspezifische Tabelle befindet sich unter dem Punkt "Package and Pinout"-"Peripheral Multiplexing on I/O lines" im Datenblatt. <br /><br />
{{Warnung |
'''Wichtig:'''<br />
Damit die Hardware-Funktionen auch verwendet werden können, muss im Register "Gper" der Pin deaktiviert werden, da sonst der Port ganz normale GPIO-Funktionen hat.
}}
<br /><br />
Um nun den Modus eines Pines zu verändern  müssen die PMRx Register wie Folgend  verändert werden.


'''Wichtig:'''<br />
{| class="wikitable"
Damit die Hardware Funktionen auch verwendet werden können, muss wie "Gper" der Pin deaktiviert werden, da sonst der Port ganz normale GPIO-Funktionen hat.
|-
!MODE  !! PMR2!! PMR1!!PMR0
|-
| A|| 0|| 0||0 
|-
| B|| 0|| 0|| 1
|-
| C|| 0|| 1|| 0
|-
| D|| 0|| 1|| 1
|-
| E|| …|| …|| …
|}
<br /><br />
<br /><br />
Beispiel Code um auf A17 Mode B und auf B05 Mode D zu bekommen
 
 
Beispielcode um auf Pin A17 den Mode B und auf Pin B05 den Mode D zu bekommen:
  <syntaxhighlight lang="c">  
  <syntaxhighlight lang="c">  
AVR32_GPIO.port[0].pmr0 = 1<<21; AVR32_GPIO.port[0].pmr1 = 0; AVR32_GPIO.port[0].pmr2 = 0; //mode b
AVR32_GPIO.port[0].pmr0 = 1<<17; AVR32_GPIO.port[0].pmr1 = 0; AVR32_GPIO.port[0].pmr2 = 0; //mode B
AVR32_GPIO.port[1].pmr0 = 1<<5; AVR32_GPIO.port[1].pmr1 = 1<<5; AVR32_GPIO.port[1].pmr2 = 0; //mode d </syntaxhighlight>
AVR32_GPIO.port[1].pmr0 = 1<<5; AVR32_GPIO.port[1].pmr1 = 1<<5; AVR32_GPIO.port[1].pmr2 = 0; //mode D </syntaxhighlight>
 
 
<br /><br />
<br /><br />


== Interrupt==
== Interrupt==
===IRQ(Interrupt Request)===
===IRQ(Interrupt Request)===
Der Interrupt-Controller der AT32 Serie hat die meisten Unterscheide zur AT8-Serie.
Der Interrupt-Controller der AT32-Serie hat zahlreiche Unterscheide zur AT8-Serie.
Hier gibt es verschiedene Interrupt Requests, die jedem Interrupt einzeln zugewiesen werden können. Hierzu ist es wichtig die Gruppe des Interrupts zu wissen. Jede Hardware-Funktion hat seine eigene Gruppe. <br />
Hier gibt es verschiedene Interrupt Requests, die jedem Interrupt einzeln zugewiesen werden können. Hierzu ist es wichtig die Gruppe des Interrupts zu wissen. Jede Hardware-Funktion hat ihre eigene Gruppe. <br />
Jeder Interrupt hat eine eigene Interrupt-Line. So kann man jeden Interrupt direkt zuordnen.
Jeder Interrupt hat eine eigene Interrupt-Line. So kann man jeden Interrupt direkt zuordnen.
Aus den beiden Werten bildet sich die IRQ-Nummer ( interrupt request nummber )<br />
Aus den beiden Werten bildet sich die IRQ-Nummer (interrupt request nummber).<br />


Dies erstellt sich folgendermaßen : <br />
Dies erstellt sich folgendermaßen: <br />


  <syntaxhighlight lang="c"> Interrupt-Gruppe * 32 + Interrupt-Line</syntaxhighlight>
  <syntaxhighlight lang="c"> Interrupt-Gruppe * 32 + Interrupt-Line</syntaxhighlight>


Dem Interrupt-Kontroller muss nun nur die IRQ-Nummer und ein [[pointer]] auf die Interrupt-Routine und die Priorität des Interruptes übergeben werden. Dies geschieht in der main{}.  <br />
Dem Interrupt-Controller müssen nun nur die IRQ-Nummer und ein Pointer auf die Interrupt-Routine und die Priorität des Interrupts übergeben werden. Dies geschieht in der main{}.  <br />
  <syntaxhighlight lang="c">  
  <syntaxhighlight lang="c">  
INTC_register_interrupt( &funktion,IRW,Prio) </syntaxhighlight>
INTC_register_interrupt( &funktion,IRW,Prio) </syntaxhighlight>
<br />
<br />
===Interrupt im AtmelStudio===
===Interrupt im Atmel Studio===
Nun muss nur noch die Funktion erstellt werden, jedoch mit dem Suffix: <br />
Nun muss nur noch die Funktion, die dann wärend des Interruptes durchgeführt wird, erstellt werden, jedoch mit dem Suffix: <br />
<syntaxhighlight lang="c"> __attribute__((__interrupt__))
<syntaxhighlight lang="c"> __attribute__((__interrupt__))
void funktion(void)
void funktion(void)
{
{
//code
//code
}</syntaxhighlight>
}</syntaxhighlight>
<br />
<br />
Hiermit wird dem Compiler mitgeteilt, dass es sich hierbei um eine [[Interrupt]]-Routine handelt und er diese mit "Vorsicht" genießen soll, d.h. aktuelles Programm anhalten/speichern Interrupt ausführen und wieder an die Ausgangssituation gehen.  
So wird dem Compiler mitgeteilt, dass es sich hierbei um eine [[Interrupt]]-Routine handelt und er diese mit "Vorsicht" genießen soll, d.h. aktuelles Programm anhalten, Daten speichern, Interrupt ausführen, und wieder an die letzte Position zurück gehen.  
<br />
<br />
===Beispiel===
===Beispiel===
Zeile 184: Zeile 205:


   Enable_global_interrupt(); //wichtig!!
   Enable_global_interrupt(); //wichtig!!
   INTC_register_interrupt( &timer,800,0); //interupt group int_level
   INTC_register_interrupt( &timer,800,1); //interupt group int_level
   // IRQ%32 = line number   
   // IRQ%32 = line number   
   // IRQ/32 = Gruppe
   // IRQ/32 = Gruppe
Zeile 195: Zeile 216:
<br />
<br />


==Timer/Counter(in Bearbeitung)==
===Sonderfälle===
In einigen Registern ist der AVR32UC3 nicht in der Lage den Interrupt-Aufruf von selbst zu löschen. d.h der Interrupt wird immer wieder nach Beendigung der Interrupt Routine wieder ausgerufen. <br />
um dies zu umgehen muss man laut Datenblatt den Interrupt Disablen, und daraufhin ein Dummy-read auf das Status- bzw. dem Interrupt-Status -Registers ausführen, damit intern der Interrupt Aufruf gelöscht wird.
<br />
möchte man nun die Interrupt Routine wieder starten macht man das über IER
 
Bsp: <syntaxhighlight lang="c">
AVR32_TC0.channel[Timer].idr = 1<< AVR32_TC_IER0_CPCS; //disable  Interuppt bei RC comparae
AVR32_TC0.channel[0].sr;// dummy read
AVR32_TC0.channel[0].ier = 1<< AVR32_TC_IER0_CPCS; //enable interrupt
</syntaxhighlight>
 
Datenblatt: <br />
 
<big><sup>Clearing of the interrupt request is done by writing to registers in the corresponding peripheral
module, which then clears the corresponding NMIREQ/IREQ signal.
The recommended way of clearing an interrupt request is a store operation to the controlling
peripheral register, followed by a dummy load operation from the same register. This causes a
pipeline stall, which prevents the interrupt from accidentally re-triggering in case the handler is
exited and the interrupt mask is cleared before the interrupt request is cleared.
</sup></big>
<br />
<br />
So wie viele andere Prozessoren verfügt auch der AVR32xxx über eine [https://www.mikrocontroller.net/articles/FAQ#Timer] Funktion.<br />
<br />
Der AVR32UCL verfügt über insgesamt 6x 32-Bit-Timer. Diese werden über 2 Register angesprochen:<br />
 
    - AVR32_TC0<br />
==Timer/Counter==
    - AVR32_TC1<br />
<br />
So wie viele andere Prozessoren verfügt auch der AVR32xxx über eine [[FAQ#Timer|Timer]] Funktion.<br />
Der AVR32UCL verfügt über insgesamt 6x 32-Bit-Timer. Diese werden über insgesammt 2 Register angesprochen:<br />
      - AVR32_TC0<br />
      - AVR32_TC1<br />
Jedes dieser Register verfügt über je 3 [https://www.mikrocontroller.net/articles/FAQ#Timer Timer], die sich einzeln konfigurieren lassen. Dies geschieht über die Channel-Funktion. z.B.<br />
Jedes dieser Register verfügt über je 3 [https://www.mikrocontroller.net/articles/FAQ#Timer Timer], die sich einzeln konfigurieren lassen. Dies geschieht über die Channel-Funktion. z.B.<br />
<syntaxhighlight lang="c">AVR32_TC0.channel[0].ccr </syntaxhighlight><br />
<syntaxhighlight lang="c">
===Overflow===
      - AVR32_TC0.channel[0].ccr  
Jedes dieser Timer/counter ist in der Lage bei einem Timer-Overflow ein Interrupt auszulösen. Diese lassen sich über die [https://www.mikrocontroller.net/articles/AVR32-Tutorial#IRQ.28Interrupt_Request.29 IRQ] auseinander halten.
      - AVR32_TC0.channel[1].ccr
      - AVR32_TC0.channel[2].ccr
</syntaxhighlight><br />
Der Timer ist in der Lage in 2 Funktions-Typen zu arbeiten, dem Capture Mode und dem Waveform Mode. Der unterschied liegt  ist, dass im Capture Mode TIOA und TIOB (Timer Input/Output A/B)als Input für den Timer verwendet werde, und im Waveform Mode als Output. <br />
===Capture Mode===
Beispiel Konfiguration, in der der Timer auf einen Bestimmten wert hoch zähl und in der zeit in eine Routine abläuft.
<syntaxhighlight lang="c">
AVR32_TC0.channel[0].ccr = 1<<AVR32_TC_CCR0_CLKEN | 1<<AVR32_TC_CCR0_SWTRG ;//start timer + enable timer
AVR32_TC0.channel[0].cmr = 2<<AVR32_TC_CMR0_WAVSEL |1<<AVR32_TC_CMR0_WAVE | 1<<AVR32_TC_CMR0_TCCLKS;//mode config
 
while(AVR32_TC0.channel[2].cv <= zeit_daten_senden)
{
//mach was
}
 
 
</syntaxhighlight><br />
Über die Register LDRB und LDRA kann der Aktuelle Zählerstand in einen der beiden Register (RA / RB) abgelegt werden.


===Comparare===
===Waveform Mode===
Ebenfalls sind die Timer/Counter in der Lage einen Overflow auszulösen, wenn der Wert des Timer-Counters mit dem der Comperrate-Register übereinstimmt. Nun kann entweder der Counter zurückgesetzt werden, oder der Timer weiterlaufen.<br />
im Waveform Mode kann über ein RA/RB Compare und einem RC Compare ein eine Steigende oder fallende Flanke am TIOA/B erzeugt werden. So ist es einfach ein PWM Signal zu erzeugen, was ein genaues PWM ermöglicht als der 8-Bit PWM der pro Pin verbaut ist.
Hierzu bitte ins Datenblatt schauen.
<br />
<br />
Um den Waveform Mode zu aktivieren muss im CMR Register WAVE auf 1 gesetzt werden.
Insgesamt gibt es 4 verschieden Funktionen, die über WAVESEL ausgewählt werden können: <br />
      -UP mode <br />
      -UPDOWN<br />
      -UP mode mit RC Compare Trigger <br />
      -UPDOWN  mit RC Compare Trigger <br />
Der Counter wird entweder bis 0xFFFF hochgezählt und beginnt wieder bei 0x0000, oder er wird nach 0xffff von oben nach unten zurückgezahlt.<br />
auch ist es möglich bei einem RC Compare einen reset auszulösen. <br />
TIOA und TIOB sind in der Lage ein Signal bei einem Compare mit RA/RB und RC auszulösen. Hierbei ist es möglich
das Signal entweder zu löschen, Toggeln oder zu setzen. <br />
<syntaxhighlight lang="c">


AVR32_TC0.channel[Timer].ier = 1<< AVR32_TC_IER0_CPCS; //enable Interuppt bei RC comparae
AVR32_TC0.channel[Timer].cmr = 1<<AVR32_TC_CMR0_ACPA | 2<<AVR32_TC_CMR0_ACPC // Setzte TIOA bei RC
                                                                          //compere und lösche es bei RA comparae
|  1<<AVR32_TC_CMR0_BCPB | 2<<AVR32_TC_CMR0_BCPC // TIOB Setze bei RC und lösche bei RC
|  1 <<AVR32_TC_CMR0_WAVE // wave enable
|  2<<AVR32_TC_CMR0_WAVSEL //Wavemode 2 UP- RC copare und reset des Counters
|  1<<AVR32_TC_CMR0_TCCLKS //clock select
|  1<<AVR32_TC_CMR0_ENETRG // externe Trigger disable, da ansonsten TIOB nicht als Output ist
|  1<<AVR32_TC_CMR0_EEVT; //keine Externel Trigger EVents behandeln
AVR32_TC0.channel[Timer].ra  = 3686<<AVR32_TC_RA;  //Register a
AVR32_TC0.channel[Timer].rb  = 3686<<AVR32_TC_RB;  //Register b ==>  RA start TIOA    RB lösche
                                                                  // TIOB RC lösche TIOA Start TIOB
AVR32_TC0.channel[Timer].rc  = 0xffff<<AVR32_TC_RC; //Register c
AVR32_TC0.channel[Timer].ccr = 1<<AVR32_TC_CCR0_CLKEN | 1<< AVR32_TC_CCR0_SWTRG; //clock enable und start
</syntaxhighlight><br />
===Interrupt===
Jeder dieser Timer/counter ist in der Lage bei einem Timer-Overflow ein Interrupt auszulösen. Diese lassen sich über die [https://www.mikrocontroller.net/articles/AVR32-Tutorial#IRQ.28Interrupt_Request.29 IRQ] auseinander halten.<br />
Es ist möglich ein Interrupt bei einem RA - RB - RA - Comparare auslösen zu lassen, sowie bei einer Änderung am  Externen Trigger oder einer RB bzw. RA Load-action.


==TWI/I2C==
==TWI/I2C==
===Allgemein===
===Allgemein===
Mit dem TWI-Bus hatte Atmel einen Klon von Philips I2C-Bussens gemacht.<br />
Mit dem TWI-Bus hat Atmel einen Klon von Philips I2C-Bus implementiert.<br />
Der AVR32uc3-L0064 und die meisten anderen AVR32-Prozessoren verfügen über 2x TWI-Master und über 2x TWI-Slaves, die sich einzeln Konfigurieren lassen. <br />
Der AVR32uc3-L0064 und die meisten anderen AVR32-Prozessoren verfügen über 2x TWI-Master und über 2x TWI-Slaves, die sich einzeln Konfigurieren lassen. <br />
Dies wird Über das Register: <br />
Dies wird über folgendes Register durchgeführt: <br />
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
     AVR32_TWIM0  //Master1
     AVR32_TWIM0  //Master1
Zeile 224: Zeile 318:
     AVR32_TWIS1  //Slave2
     AVR32_TWIS1  //Slave2
</syntaxhighlight>
</syntaxhighlight>
Die Register Master10/1 sowie die Register Slave0/1 sind Identisch aufgebaut.<br />
Die Register Master0/1 sowie die Register Slave0/1 sind identisch aufgebaut.<br />
===Konfiguration===
===Konfiguration===
In den AVR32 Bibliothek werden die #Define's für die einzelnen Register nicht nach Master 0 und 1 nicht Unterschieden, da diese Identisch sind, deshalb müssen die Register so angesprochen werden: <br />
In den AVR32-Bibliothek werden die #Define's für die einzelnen Register nicht nach Master 0 und 1 unterschieden, da diese identisch sind. Deshalb müssen die Register wie folgt angesprochen werden: <br />
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
     AVR32_TWIM_NCMDR_VALID
     AVR32_TWIM_NCMDR_VALID
</syntaxhighlight><br />
</syntaxhighlight><br />
===Grundkonfig===
===Grundkonfig===
Um den TWI-bus zum laufen zu bringen muss zunächst einmal das Entsprechende Bit im Powermanagment gesetzt werden(hierzu im Datenblatt nachschauen, welches dies ist)<br />
Um den TWI-Bus zum Laufen zu bringen muss zunächst einmal das Entsprechende Bit im Power-Management gesetzt werden(hierzu im Datenblatt nachschauen, welches dies ist).<br />
Genauso wichtig ist, darauf zu achten, dass die Ports richtig Gemuliplext werden müssen.<br />
Genauso wichtig ist es, darauf zu achten, dass die Ports richtig gemultiplext werden.<br />
Nun kann im "CWGR"-Register(Clock Waveform Generator Register) der Clock Prescaler (EXP) mit den Werden 0-7 bespielt werden.<br />
Nun kann im "CWGR"-Register(Clock Waveform Generator Register) der Clock Prescaler (EXP) mit den Werden 0-7 bespielt werden.<br />
eine Weiter Besonderheit ist, dass sich die TWI High,Low Phasen, sowie wie Daten und Die Start/Stop Zeiten Individuell einstellen lassen. <br />
Eine weitere Besonderheit ist, dass sich die TWI-High- und Low Phasen, sowie wie Daten und die Start/Stopp-Zeiten Individuell einstellen lassen. <br />
Dies hatt den Vorteil, dass hier mit viel Erfahrung noch ein kleines Bisschen Zeit Herausgekitzelt werden kann.
Dies hat den Vorteil, dass hier mit viel Erfahrung noch ein kleines bisschen Zeit herausgekitzelt werden kann.
Ich Empfehle jedoch, die Zeiten auf "1" zu stellen.<br />
Ich empfehle jedoch, die Zeiten auf "1" zu stellen.<br />
===Daten-Übertragung===
===Daten-Übertragung===
Nun sollen die ersten Daten übertragen werden:<br />
Nun sollen die ersten Daten übertragen werden:<br />
Hierzu ist das Register "CMDR" (Command Register) und "NCMDR"(next Command Register)wichtig. Dieses Register sollen nach jeder Übertragung neu beschrieben werden.<br />
Hierzu ist das Register "CMDR" (Command Register) und "NCMDR"(Next Command Register) wichtig. Diese Register sollen nach jeder Übertragung neu beschrieben werden.<br />
Die Wichtigsten Bits sind:<br />
Die wichtigsten Bits sind:<br />
* NBYTES:    hier wird die Anzahl der zu übertragenden Byts eingeben. Eine Stop bedingung wird erst gesendet, wenn all Byts gesendet worden sind.
* NBYTES:    Hier wird die Anzahl der zu übertragenden Bytes eingeben. Eine Stopp-Bedingung wird erst gesendet, wenn all Bytes gesendet worden sind.
* STOP:      Sendet nach der Übertragung ein Stop-Flanke
* STOP:      Sendet nach der Übertragung ein Stopp-Flanke
* Start:    Beginnt die Übertragung mit einer Start-Flanke
* Start:    Beginnt die Übertragung mit einer Start-Flanke
* SADR:      Slaveadresse
* SADR:      Slaveadresse
* Read:      wenn gelesen werde soll muss hier eine 1 stehen, ansonsten weiß der Slave, dass geschrieben wird.
* Read:      Wenn gelesen werden soll muss hier eine 1 stehen, ansonsten weiß der Slave, dass geschrieben wird.
<br />
<br />
Jetzt weiß der TWI-Bus, wie gesendet werden soll. Nun ihm Nur noch gesagt werden was gesendet wird. Die zu Sendende Information wird im Register "THR" (Transmit Holding Register) rein geschrieben.<br />
Jetzt weiß der TWI-Bus, wie gesendet werden soll. Nun muss ihm nur noch gesagt werden was gesendet wird. Die zu sendende Information wird in das Register "THR" (Transmit Holding Register) geschrieben.<br />
Nun muss noch die Übertragung gestartet werden. Hierzu muss im CMDR-Register das Bit VALID gesetzt werden. Hatt man eine Übertragung von mehr als 1 Byte muss nach der Übertragung des Ersten Bytes das THR-Register mit dem nächsten Byte gefüttert werden(jedoch kein VALID!).<br />
Nun muss noch die Übertragung gestartet werden. Hierzu muss im CMDR-Register das Bit VALID gesetzt werden. Hat man eine Übertragung von mehr als 1 Byte, muss nach der Übertragung des ersten Bytes das THR-Register mit dem nächsten Byte gefüttert werden (jedoch ohne VALID!).<br />


===Daten Senden und Empfangen===
===Daten Senden und Empfangen===
Um zb. Sensoren Auszulesen wird normaler weise zunächst die Register-Adresse auf dem Bus geschrieben und dann Sendet der Slave die werde des Registers.<br />
Um z. B. Sensoren auszulesen wird normalerweise zunächst die Register-Adresse auf dem Bus geschrieben, und dann sendet der Slave den oder die Werte des angefragten Registers.<br />
Hierzu muss eine Übertragung mit START = 1,SADR,Stopn = 0, Read = 0 und Nbytets = 1 bekonnen werden. in THR wird das Register geschrieben.<br />
Hierzu muss eine Übertragung mit START = 1,SADR,Stopn = 0, Read = 0 und Nbytets = 1 begonnen werden. In THR wird das Register geschrieben.<br />
im nächsten schritt wird dann(z.B. über das Ncmdr-Register -- wichtg hierbei : das VALID Register muss dann auch im NCDMR-Register gesetzt werden und nicht im CMDR) eine 2 Übertragung gestartet: START = 0,SADR,Stopn = 1, Read = 1 und Nbytets = 1. Nun weiß der Slave, dass der Etwas senden soll und legt die Information auf die Datenleitung. Nach der Übertragung schreibt der Master die Information in das "RHR"-Register (Receive Holding Register).<br />
Im nächsten Schritt wird dann (z. B. über das Ncmdr-Register -- wichtig hierbei: das VALID-Register muss dann auch im NCDMR-Register gesetzt werden und nicht im CMDR!) eine Übertragung gestartet: START = 0, SADR, Stopn = 1, Read = 1 und Nbytets = 1. Nun weiß der Slave, dass er etwas zurücksenden soll, und legt die Information auf die Datenleitung. Beim Zurücksenden wird der Takt (Clock) vom Master angelegt. Nach der Übertragung schreibt der Master die Information in das "RHR"-Register (Receive Holding Register).<br />
Viele Slaves sind in der Lage eines Burst-Read, hier wird einmal das Lesen gestartet und der Slave sendet denn immer weiter(Register abwärts oder aufwärts), bis eine STOP Bedingung gesendet wird. Einfach Nbytes auf die Anzahl der Byts stellen.<br />
Viele Slaves sind in der Lage einen Burst-Read durchzuführen: Hier wird einmal das Lesen gestartet und der Slave sendet denn immer weiter(Register abwärts oder aufwärts), bis eine STOP Bedingung gesendet wird. Einfach Nbytes auf die Anzahl der Bytes stellen.<br />
 
== Takt-Quelle ==
Im AVR32UC können übers Powermanagement verschiedene Einstellungen zur Takt-Source und zur Taktversorgung der Teilkomponenten des Prozessors getroffen werden. <br />
 
===RC-Oszillator===
Der AVRUC3X verfügt über einen interne RC-Oszillator, der als Takt-Source verwendet werden kann. hierzu müssen folgende Register im Powermanagement(PM) gesetzt werden:
<syntaxhighlight lang="c">
//Rc enable
AVR32_SCIF.unlock =  0x0058 << AVR32_SCIF_UNLOCK_ADDR| 0xAA<<AVR32_SCIF_UNLOCK_KEY;
AVR32_SCIF.rc120mcr = 0xff; //enable 120mhz oszilator
AVR32_PM.unlock = 0xAA << AVR32_PM_UNLOCK_KEY  | 0x004 << AVR32_PM_UNLOCK_ADDR;
AVR32_PM.cpusel = 1<< AVR32_PM_CPUSEL_CPUDIV | 2<< AVR32_PM_CPUSEL_CPUSEL; //pba auf ein 6 von mainclock
        // hier ist auch twi drinnen
AVR32_PM.unlock = 0xAA << AVR32_PM_UNLOCK_KEY  | 0x000 << AVR32_PM_UNLOCK_ADDR;
AVR32_PM.mcctrl = 3; //clocksource auf RC-oszilator umstellen
</syntaxhighlight>
 
Wichtig hierbei ist das die entsprechenden Register über ein Log-Register geschützt sind.
Ist das zu beschreibende Register nicht entsperrt, kann es nicht beschrieben werden.<br />
Um das entsprechende Register zu entsperren, muss im Log-Register der "Key" eingetragen werden und die Adresse des zu entsperrenden Registers.<br />
Wichtig: das Register ist nur für einen weiten Befehl entsperrt.   
<br />
<br />
 
=== Peripherie Komponenten ===
Ebenfals ist es möglich die Peripherie Komponenten einen eigen Takt zu geben dies geschieht über das PBAMASK-Register und dem PBASEL-Register im Power-Management. <br />
Hier kann allen Peripherie Komponenten einen Main-Clock unabhängigem Takt gegeben werden. Hierbei wird die Main-Clock durch  den Teiler im PBASEL geteilt. Der Takt ist nun Mainclock/2^(teiler+1)
<br /><br />
 
== Links und Literaturempfehlungen ==
== Links und Literaturempfehlungen ==
Es ist empfehlenswert, im Datenblatt nicht nur den entsprechende Abschnitt durchzulesen, sondern auch nach den Begriffen zu suchen, da viele wichtige Informationen nicht in dem entsprechendem Abschnitt stehen (wie z.B. Modulkonfiguration).
Es ist empfehlenswert, im Datenblatt nicht nur den entsprechende Abschnitt durchzulesen, sondern auch nach den Begriffen zu suchen, da viele wichtige Informationen nicht in dem entsprechenden Abschnitt stehen (wie z.B. Modulkonfiguration).
* [http://www.mikrocontroller.net/articles/AVR32 AVR32]
* http://de.wikipedia.org/wiki/Atmel_AVR32
* http://www.atmel.com/products/microcontrollers/avr/32-bitavruc3.aspx
* [http://www.atmel.com/Images/doc32117.pdf Datenblatt]
 
<br />
<br />


Zeile 264: Zeile 394:
<br />
<br />
<br />
<br />
 
--[[Benutzer:Basti195|Basti195]] ([[Benutzer Diskussion:Basti195|Diskussion]]) 15:39, 6. Sep. 2014 (CEST)
--[[Benutzer:Basti195|Basti195]] ([[Benutzer Diskussion:Basti195|Diskussion]]) 18:16, 22. Jul. 2014 (CEST)

Aktuelle Version vom 16. Februar 2016, 09:12 Uhr


Dieses Tutorial soll eine Einführung in die AVR32(32-bit)-Architektur erleichtern. Die AVR32-Architektur ist im Vergleich zu den 8 Bit AVRs oder PICs schwieriger zu erlernen und deshalb nicht unbedingt für Mikrocontroller-Anfänger geeignet. Die meisten Änderungen der 32-bit- Architektur im Vergleich zur 8-bit-Architektur liegen im Interrupt-Handling und beim Register-Zugriff.

Die Besonderheit dieses Tutorials ist es, komplett auf das AVR32 Software-Framework von Atmel zu verzichten, um so besser verstehen zu können wie der UC funktioniert.

Der vorgestellte Code ist für einen AT32UC3L064 zugeschnitten und dort auch getestet.

Motivation und Zielgruppe

Dieses Tutorial wurde geschrieben, da es bisher noch kein Tutorials zu Atmels 32-Bit-Serie gibt. Deshalb richtet sich das Tutorial in erster Linie an alle, die von der 8-bit Serie (ATmega/Attiny/Xmega...) auf die 32-bit-Prozessoren umsteigen möchten. Das Tutorial wurde so gestaltet, dass es auch von allen anderen, die in die Atmel 32-bit Serie einsteigen wollen, verwendet werden kann. Sollte es Verbesserungsvorschläge oder Ideen geben, mit denen das Tutorial verbessert werden kann: Bitte sendet eine PN oder hinterlasst im Diskussionsbereich einen Kommentar. --Basti195 (Diskussion) 11:52, 22. Aug. 2014 (CEST)

Benötigte Vorkenntnisse

Da der GCC verwendet wird, ist es notwendig gute C-Kenntnisse zu besitzen. Ohne das Atmel Software Framework ist es außerdem notwendig, einige grundlegende Kenntnisse in Assembler zu haben, die man sich ggf., wenn man die entsprechenden Stellen des Tutorials erreicht, noch nachträglich aneignen kann.

Benötigte Software

Grundsätzlich kann man mit dem AVR32 Studio und der Atmel AVR32-Toolchain (mittlerweile auch mit dem Atmel Studio) alle Aufgaben übernehmen. Es geht aber auch ohne das AVR-Studio. Man benötigt dann einen Editor, die Atmel AVR32-Toolchain und unter Windows ist cygwin zu empfehlen, bzw. man kann auch einfach unter Linux entwickeln.

Die Atmel AVR32-Toolchain bekommt man über die Atmel-Homepage (Quellcode oder fertige Installer) oder alternativ hier den Quellcode [1].


Die Toolchain besteht aus mindestens 3 Komponenten:

  • binutils
  • gcc
  • eine c-library

Wenn man diese Software aus dem Quellcode installieren möchte, sind die Patches von Atmel erforderlich. Man kann sich die fertig gepatchten Quellen von avr32linux.org runterladen. Selbst zu patchen kann je nach Linuxdistribution bzw. der Version der Distribution sehr kompliziert werden, da nach dem Patchen die Makefiles neu aufgebaut werden müssen.

Benötigte Hardware


Benötigt werden

  • Prozessor: z.B. AT32UC3L064
  • Programmer: z.B. JTAG
  • Board: z.B. ein Dev-board
  • Oszilloskop -> zum Debuggen


Trouble-Shooting


Hier geht es um Logik-Fehler und nicht um Syntax-Fehler.
Die hier dargestellten Methoden sind allgemein und dienen als Checkliste.

Leider funktioniert nur selten alles wie geplant. Deshalb ist es hier umso wichtiger, die Projekte genau zu dokumentieren, da so deutlich schneller Fehler gefunden werden können.
Schleicht sich doch einmal ein Fehler in den Code und funktioniert es nicht, ist es sehr hilfreich, einen Debugger zu haben. Alternativ kann man auch auf den eingeschränkten Simulator vom Atmel Studio zurückgreifen.
Am besten geht man dabei folgendermaßen vor:

  • Fehler lokalisieren: über ein Oszilloskop kann herausgefunden werden, ob es sich um einen Prozessor-Fehler handelt oder um ein Fehler eines Fremdgerätes.

  • Zunächst einmal sollte der Code nochmals durchgearbeitet werden, um so sicher zu stellen, dass nicht wichtige Teile vergessen wurden.

  • Nun sollte das Programm mit dem Debugger Schritt für Schritt durchgegangen werden um zu überprüfen, ob wirklich alle Bits in den richtigen Registern gesetzt worden sind. Manchmal löst ein Bit ein Ereignis aus, welches andere Bits löscht!

  • Ebenfalls sollte im Power-Management überprüft werden, ob die Funktionen aktiv sind.



Register Zugriff

Der Registerzugriff bei der AT32-Serie ist etwas anders als bei der AT8-Serie. Um hier ein Register zu beschreiben muss nicht wie z.B. an einem ATmega8 der Befehl wie folgt aussehen:

  DDRD = 0xff;

Sondern bei der 32-bit Serie wird ein Register stattdessen folgendermaßen beschrieben:

  AVR32_"Funktion".[Channel].Register = 0xff;

Vor jedem RegisterBefehl steht zunächst das "AVR32_". Hieran wird dann eine Funktion (z.B. SPI) angehängt, damit der Prozessor weiß wem er das Register zuweisen muss. Die Unterfunktion kommt dann zum Einsatz, wenn es mehrere Channels gibt, wie z. B. 3 Timer oder 2 GPIO-lines. Hier muss dann die Zahl in "[ ]" geschrieben werden. Zum Schluss muss noch das entsprechende Register angesprochen werden ".Register".

Der gesamte Befehl sieht dann am Ende so aus:

  AVR32_TC0.channel[1].cr = 1<<8;


GPIO - General Purpose Input Output

Codebeispiel anhand eines AT32UC3L064

IO-Zugriff

Bei der 32-Bit Serie ist die Ansteuerung der Pins etwas anders gelöst: Hier kann ein Port nicht mehr mit DDRx und PORTx beschrieben werden, sondern dies geschieht über ein spezielles GPIO-Register, wobei man sich auch hier entsprechende #DEFINEs machen kann.

Die GPIO Befehle sind folgendermaßen aufgebaut:

    AVR32_GPIO.port[X].register = 0xff;

Der Teil AVR32_GPIO.port[1/2] bezeichnet die entsprechenden Ports (A oder B). Mit dem .Register-Name wird dann das entsprechende Register angesprochen. Nun muss nur noch eine 1 auf das entsprechende Bit gesetzt werden. Hier gilt, dass der erste Pin auf der 0ten Position steht und der zweite auf Position 1...


Um ein Signal aus dem UCxx zu bekommen, muss zunächst das GPIO-Modul aktiviert werden. Dies wird über das GPIO Enable Register(gper) gemacht.

  AVR32_GPIO.port[0].gper = 1<<13 // Port A Pin 13


Der Pin wird mit dem OVR (Output Value Register) auf high gezogen

 AVR32_GPIO.port[1].ovr = 1<<13 // Port B Pin 13 auf 3,3V''


Es gibt zu jedem Register zusätzlich die Endung t/c/s toggl/clear/set

  AVR32_GPIO.port[0].gperc = 1<<13 //löscht das entsprechende Bit da gperC (clear)


Um nun den Wert eines Pins auslesen zu können, muss man sich das .pvr(Port Value Register ) anschauen. Ist das entsprechende Bit gesetzt, ist der Port logisch high.

Modul-Konfiguration

Die AT32-Prozessoren verfügen über das Feature, dass Hardware-Funktionen nicht an einen speziellen Pin gebunden sind, sondern sich multiplexen lassen.
Dies hat den Vorteil, dass man nicht mehr an einen bestimmten Pin gebunden ist, sondern dass man den Prozessor an das Layout des Schaltplans anpassen kann. Jedoch macht dies die Konfiguration deutlich komplizierter.


Jeder Port hat die Möglichkeit, bis zu 8 (A-H) verschiedene Hardware-Funktionen zu übernehmen (Datenblatt: Package and Pinout)

Nun muss der entsprechende Port im PMRx (GPIO) der entsprechenden Funktion zugewiesen bekommen.

Ausschnitt aus der "Peripheral Multiplexing on I/O lines"-Tabelle:

PIN Pin Type A B C ...
PA00 Normal I/O USART0 TXD USART1 RTS SPI NPCS[2]
PA02 Highdrive I/O USART0 RTS ADCIFB TRIGGER USART2 TXD
PA06 Highdrive I/O, 5V tolerant SPI SCK USART2 TXD USART1 CLK



Die prozessorspezifische Tabelle befindet sich unter dem Punkt "Package and Pinout"-"Peripheral Multiplexing on I/O lines" im Datenblatt.

Wichtig:
Damit die Hardware-Funktionen auch verwendet werden können, muss im Register "Gper" der Pin deaktiviert werden, da sonst der Port ganz normale GPIO-Funktionen hat.



Um nun den Modus eines Pines zu verändern müssen die PMRx Register wie Folgend verändert werden.

MODE PMR2 PMR1 PMR0
A 0 0 0
B 0 0 1
C 0 1 0
D 0 1 1
E




Beispielcode um auf Pin A17 den Mode B und auf Pin B05 den Mode D zu bekommen:

 
AVR32_GPIO.port[0].pmr0 = 1<<17;	AVR32_GPIO.port[0].pmr1 = 0;	AVR32_GPIO.port[0].pmr2 = 0; //mode B
AVR32_GPIO.port[1].pmr0 = 1<<5;		AVR32_GPIO.port[1].pmr1 = 1<<5;	AVR32_GPIO.port[1].pmr2 = 0; //mode D




Interrupt

IRQ(Interrupt Request)

Der Interrupt-Controller der AT32-Serie hat zahlreiche Unterscheide zur AT8-Serie. Hier gibt es verschiedene Interrupt Requests, die jedem Interrupt einzeln zugewiesen werden können. Hierzu ist es wichtig die Gruppe des Interrupts zu wissen. Jede Hardware-Funktion hat ihre eigene Gruppe.
Jeder Interrupt hat eine eigene Interrupt-Line. So kann man jeden Interrupt direkt zuordnen. Aus den beiden Werten bildet sich die IRQ-Nummer (interrupt request nummber).

Dies erstellt sich folgendermaßen:

 Interrupt-Gruppe * 32 + Interrupt-Line

Dem Interrupt-Controller müssen nun nur die IRQ-Nummer und ein Pointer auf die Interrupt-Routine und die Priorität des Interrupts übergeben werden. Dies geschieht in der main{}.

 
INTC_register_interrupt( &funktion,IRW,Prio)


Interrupt im Atmel Studio

Nun muss nur noch die Funktion, die dann wärend des Interruptes durchgeführt wird, erstellt werden, jedoch mit dem Suffix:

 __attribute__((__interrupt__))
void funktion(void)
{
//code
}


So wird dem Compiler mitgeteilt, dass es sich hierbei um eine Interrupt-Routine handelt und er diese mit "Vorsicht" genießen soll, d.h. aktuelles Programm anhalten, Daten speichern, Interrupt ausführen, und wieder an die letzte Position zurück gehen.

Beispiel

Ein solches Programm kann dann so ausschauen:


 __attribute__((__interrupt__)) void timer(void) //interrupt handler 
         {  
		//Code	  
	 }	


int main (void)
  {

   Enable_global_interrupt(); //wichtig!!
   INTC_register_interrupt( &timer,800,1); //interupt group int_level 	
   // IRQ%32 = line number  
   // IRQ/32 = Gruppe
   // IRQ = 32 *25 (Gruppe)+0(line)= 800.

   // im Register den Interrupt erlauben
   AVR32_TC0.channel[0].ier = 1<<AVR32_TC_IER0_CPCS; //interrupt enable 
   }



Sonderfälle

In einigen Registern ist der AVR32UC3 nicht in der Lage den Interrupt-Aufruf von selbst zu löschen. d.h der Interrupt wird immer wieder nach Beendigung der Interrupt Routine wieder ausgerufen.
um dies zu umgehen muss man laut Datenblatt den Interrupt Disablen, und daraufhin ein Dummy-read auf das Status- bzw. dem Interrupt-Status -Registers ausführen, damit intern der Interrupt Aufruf gelöscht wird.
möchte man nun die Interrupt Routine wieder starten macht man das über IER

Bsp:

	
		AVR32_TC0.channel[Timer].idr =	1<< AVR32_TC_IER0_CPCS; //disable  Interuppt bei RC comparae
		AVR32_TC0.channel[0].sr;// dummy read
		AVR32_TC0.channel[0].ier = 1<< AVR32_TC_IER0_CPCS; //enable interrupt

Datenblatt:

Clearing of the interrupt request is done by writing to registers in the corresponding peripheral module, which then clears the corresponding NMIREQ/IREQ signal. The recommended way of clearing an interrupt request is a store operation to the controlling peripheral register, followed by a dummy load operation from the same register. This causes a pipeline stall, which prevents the interrupt from accidentally re-triggering in case the handler is exited and the interrupt mask is cleared before the interrupt request is cleared.

Timer/Counter


So wie viele andere Prozessoren verfügt auch der AVR32xxx über eine Timer Funktion.
Der AVR32UCL verfügt über insgesamt 6x 32-Bit-Timer. Diese werden über insgesammt 2 Register angesprochen:

     - AVR32_TC0
- AVR32_TC1

Jedes dieser Register verfügt über je 3 Timer, die sich einzeln konfigurieren lassen. Dies geschieht über die Channel-Funktion. z.B.

      - AVR32_TC0.channel[0].ccr 
      - AVR32_TC0.channel[1].ccr 
      - AVR32_TC0.channel[2].ccr


Der Timer ist in der Lage in 2 Funktions-Typen zu arbeiten, dem Capture Mode und dem Waveform Mode. Der unterschied liegt ist, dass im Capture Mode TIOA und TIOB (Timer Input/Output A/B)als Input für den Timer verwendet werde, und im Waveform Mode als Output.

Capture Mode

Beispiel Konfiguration, in der der Timer auf einen Bestimmten wert hoch zähl und in der zeit in eine Routine abläuft.

AVR32_TC0.channel[0].ccr = 1<<AVR32_TC_CCR0_CLKEN | 1<<AVR32_TC_CCR0_SWTRG ;//start timer + enable timer
AVR32_TC0.channel[0].cmr = 2<<AVR32_TC_CMR0_WAVSEL |1<<AVR32_TC_CMR0_WAVE | 1<<AVR32_TC_CMR0_TCCLKS;//mode config

while(AVR32_TC0.channel[2].cv <= zeit_daten_senden) 
			{	
				//mach was 
			}


Über die Register LDRB und LDRA kann der Aktuelle Zählerstand in einen der beiden Register (RA / RB) abgelegt werden.

Waveform Mode

im Waveform Mode kann über ein RA/RB Compare und einem RC Compare ein eine Steigende oder fallende Flanke am TIOA/B erzeugt werden. So ist es einfach ein PWM Signal zu erzeugen, was ein genaues PWM ermöglicht als der 8-Bit PWM der pro Pin verbaut ist.
Um den Waveform Mode zu aktivieren muss im CMR Register WAVE auf 1 gesetzt werden. Insgesamt gibt es 4 verschieden Funktionen, die über WAVESEL ausgewählt werden können:

     -UP mode 
-UPDOWN
-UP mode mit RC Compare Trigger
-UPDOWN mit RC Compare Trigger

Der Counter wird entweder bis 0xFFFF hochgezählt und beginnt wieder bei 0x0000, oder er wird nach 0xffff von oben nach unten zurückgezahlt.
auch ist es möglich bei einem RC Compare einen reset auszulösen.
TIOA und TIOB sind in der Lage ein Signal bei einem Compare mit RA/RB und RC auszulösen. Hierbei ist es möglich das Signal entweder zu löschen, Toggeln oder zu setzen.

	AVR32_TC0.channel[Timer].ier =		1<< AVR32_TC_IER0_CPCS; //enable Interuppt bei RC comparae
	AVR32_TC0.channel[Timer].cmr =	 1<<AVR32_TC_CMR0_ACPA | 2<<AVR32_TC_CMR0_ACPC // Setzte TIOA bei RC 
                                                                          //compere und lösche es bei RA comparae
		 |   1<<AVR32_TC_CMR0_BCPB | 2<<AVR32_TC_CMR0_BCPC // TIOB Setze bei RC und lösche bei RC 
		 |   1 <<AVR32_TC_CMR0_WAVE // wave enable
		 |   2<<AVR32_TC_CMR0_WAVSEL //Wavemode 2 UP- RC copare und reset des Counters
		 |   1<<AVR32_TC_CMR0_TCCLKS //clock select
		 |   1<<AVR32_TC_CMR0_ENETRG // externe Trigger disable, da ansonsten TIOB nicht als Output ist 
		 |   1<<AVR32_TC_CMR0_EEVT; //keine Externel Trigger EVents behandeln 
		
		AVR32_TC0.channel[Timer].ra  = 3686<<AVR32_TC_RA;  //Register a
		AVR32_TC0.channel[Timer].rb  = 3686<<AVR32_TC_RB;  //Register b	==>  RA start TIOA     RB lösche
                                                                   // TIOB		RC lösche TIOA Start TIOB
		AVR32_TC0.channel[Timer].rc  = 0xffff<<AVR32_TC_RC; //Register c
		AVR32_TC0.channel[Timer].ccr = 1<<AVR32_TC_CCR0_CLKEN | 1<< AVR32_TC_CCR0_SWTRG; //clock enable und start


Interrupt

Jeder dieser Timer/counter ist in der Lage bei einem Timer-Overflow ein Interrupt auszulösen. Diese lassen sich über die IRQ auseinander halten.
Es ist möglich ein Interrupt bei einem RA - RB - RA - Comparare auslösen zu lassen, sowie bei einer Änderung am Externen Trigger oder einer RB bzw. RA Load-action.

TWI/I2C

Allgemein

Mit dem TWI-Bus hat Atmel einen Klon von Philips I2C-Bus implementiert.
Der AVR32uc3-L0064 und die meisten anderen AVR32-Prozessoren verfügen über 2x TWI-Master und über 2x TWI-Slaves, die sich einzeln Konfigurieren lassen.
Dies wird über folgendes Register durchgeführt:

    AVR32_TWIM0  //Master1
    AVR32_TWIM1  //Master2
    AVR32_TWIS0  //Slave1
    AVR32_TWIS1  //Slave2

Die Register Master0/1 sowie die Register Slave0/1 sind identisch aufgebaut.

Konfiguration

In den AVR32-Bibliothek werden die #Define's für die einzelnen Register nicht nach Master 0 und 1 unterschieden, da diese identisch sind. Deshalb müssen die Register wie folgt angesprochen werden:

    AVR32_TWIM_NCMDR_VALID


Grundkonfig

Um den TWI-Bus zum Laufen zu bringen muss zunächst einmal das Entsprechende Bit im Power-Management gesetzt werden(hierzu im Datenblatt nachschauen, welches dies ist).
Genauso wichtig ist es, darauf zu achten, dass die Ports richtig gemultiplext werden.
Nun kann im "CWGR"-Register(Clock Waveform Generator Register) der Clock Prescaler (EXP) mit den Werden 0-7 bespielt werden.
Eine weitere Besonderheit ist, dass sich die TWI-High- und Low Phasen, sowie wie Daten und die Start/Stopp-Zeiten Individuell einstellen lassen.
Dies hat den Vorteil, dass hier mit viel Erfahrung noch ein kleines bisschen Zeit herausgekitzelt werden kann. Ich empfehle jedoch, die Zeiten auf "1" zu stellen.

Daten-Übertragung

Nun sollen die ersten Daten übertragen werden:
Hierzu ist das Register "CMDR" (Command Register) und "NCMDR"(Next Command Register) wichtig. Diese Register sollen nach jeder Übertragung neu beschrieben werden.
Die wichtigsten Bits sind:

  • NBYTES: Hier wird die Anzahl der zu übertragenden Bytes eingeben. Eine Stopp-Bedingung wird erst gesendet, wenn all Bytes gesendet worden sind.
  • STOP: Sendet nach der Übertragung ein Stopp-Flanke
  • Start: Beginnt die Übertragung mit einer Start-Flanke
  • SADR: Slaveadresse
  • Read: Wenn gelesen werden soll muss hier eine 1 stehen, ansonsten weiß der Slave, dass geschrieben wird.


Jetzt weiß der TWI-Bus, wie gesendet werden soll. Nun muss ihm nur noch gesagt werden was gesendet wird. Die zu sendende Information wird in das Register "THR" (Transmit Holding Register) geschrieben.
Nun muss noch die Übertragung gestartet werden. Hierzu muss im CMDR-Register das Bit VALID gesetzt werden. Hat man eine Übertragung von mehr als 1 Byte, muss nach der Übertragung des ersten Bytes das THR-Register mit dem nächsten Byte gefüttert werden (jedoch ohne VALID!).

Daten Senden und Empfangen

Um z. B. Sensoren auszulesen wird normalerweise zunächst die Register-Adresse auf dem Bus geschrieben, und dann sendet der Slave den oder die Werte des angefragten Registers.
Hierzu muss eine Übertragung mit START = 1,SADR,Stopn = 0, Read = 0 und Nbytets = 1 begonnen werden. In THR wird das Register geschrieben.
Im nächsten Schritt wird dann (z. B. über das Ncmdr-Register -- wichtig hierbei: das VALID-Register muss dann auch im NCDMR-Register gesetzt werden und nicht im CMDR!) eine Übertragung gestartet: START = 0, SADR, Stopn = 1, Read = 1 und Nbytets = 1. Nun weiß der Slave, dass er etwas zurücksenden soll, und legt die Information auf die Datenleitung. Beim Zurücksenden wird der Takt (Clock) vom Master angelegt. Nach der Übertragung schreibt der Master die Information in das "RHR"-Register (Receive Holding Register).
Viele Slaves sind in der Lage einen Burst-Read durchzuführen: Hier wird einmal das Lesen gestartet und der Slave sendet denn immer weiter(Register abwärts oder aufwärts), bis eine STOP Bedingung gesendet wird. Einfach Nbytes auf die Anzahl der Bytes stellen.

Takt-Quelle

Im AVR32UC können übers Powermanagement verschiedene Einstellungen zur Takt-Source und zur Taktversorgung der Teilkomponenten des Prozessors getroffen werden.

RC-Oszillator

Der AVRUC3X verfügt über einen interne RC-Oszillator, der als Takt-Source verwendet werden kann. hierzu müssen folgende Register im Powermanagement(PM) gesetzt werden:

//Rc enable
	AVR32_SCIF.unlock =  0x0058 << AVR32_SCIF_UNLOCK_ADDR| 0xAA<<AVR32_SCIF_UNLOCK_KEY;
	AVR32_SCIF.rc120mcr = 0xff; //enable 120mhz oszilator
	
	AVR32_PM.unlock = 0xAA << AVR32_PM_UNLOCK_KEY   | 0x004 << AVR32_PM_UNLOCK_ADDR;
	AVR32_PM.cpusel = 1<< AVR32_PM_CPUSEL_CPUDIV | 2<< AVR32_PM_CPUSEL_CPUSEL; //pba auf ein 6 von mainclock 
        // hier ist auch twi drinnen
	
	AVR32_PM.unlock = 0xAA << AVR32_PM_UNLOCK_KEY  | 0x000 << AVR32_PM_UNLOCK_ADDR;
	AVR32_PM.mcctrl = 3; //clocksource auf RC-oszilator umstellen

Wichtig hierbei ist das die entsprechenden Register über ein Log-Register geschützt sind. Ist das zu beschreibende Register nicht entsperrt, kann es nicht beschrieben werden.
Um das entsprechende Register zu entsperren, muss im Log-Register der "Key" eingetragen werden und die Adresse des zu entsperrenden Registers.
Wichtig: das Register ist nur für einen weiten Befehl entsperrt.

Peripherie Komponenten

Ebenfals ist es möglich die Peripherie Komponenten einen eigen Takt zu geben dies geschieht über das PBAMASK-Register und dem PBASEL-Register im Power-Management.
Hier kann allen Peripherie Komponenten einen Main-Clock unabhängigem Takt gegeben werden. Hierbei wird die Main-Clock durch den Teiler im PBASEL geteilt. Der Takt ist nun Mainclock/2^(teiler+1)

Links und Literaturempfehlungen

Es ist empfehlenswert, im Datenblatt nicht nur den entsprechende Abschnitt durchzulesen, sondern auch nach den Begriffen zu suchen, da viele wichtige Informationen nicht in dem entsprechenden Abschnitt stehen (wie z.B. Modulkonfiguration).


Autoren

Ersteller : unbekannt
Bearbeitung: Sebastian Balz


--Basti195 (Diskussion) 15:39, 6. Sep. 2014 (CEST)