Dieses Dokument ist eine Zusammenfassung der Beiträge von Doc_Arduino im Thread zum Nano Every, deren Veröffentlichung mir vom Autor genehmigt wurde.

Nano Every - ATmega4809 - am Anfang stand der ... Anfang

Hallo,

hiermit möchte ich euch den aktuellen Top Controller der megaAVR0 Reihe etwas genauer vorstellen der auf dem Arduino Nano Every Board leider ungeachtet dahin schlummert. Es handelt sich um den ATmega4809. Ich möchte die Scheu vorm Neuen abmildern. Zugegeben, er ist intern etwas anders aufgebaut, logisch, sonst wäre es kein neuer Controller, hat damit einen anderen Registeraufbau, der jedoch logischer aufgebaut ist, und bietet viele neue Möglichkeiten. Die meisten neuen Möglichkeiten beziehen sich darauf das die einzelnen Einheiten mehr Funktionalität bieten und mittels Eventsystem ohne CPU agieren können. Jeder Timertyp hat spezielle Fähigkeiten u.v.vm..

Die größeren Verwandten der Controllerserie sind die AVRxxxDA und AVRxxxDB Serien. Wobei die DB Serie konkret mit dem AVR128DB64 aktuell die Spitze dessen darstellt. Das Nebenbei erwähnt. Ein neuer Arduino Mega mit einem AVR128DB64 wäre optimal. Das wäre 'Mega'. :)

Hier gehts aber nun um den Nano Every mit ATmega4809. Dafür habe ich eine Pin- und Timer TCB Lib erstellt, die in den folgenden Beispielen genutzt werden. Ich habe versucht verständliche Texte zu verfassen. Ob es mir gelungen ist wird sich zeigen. Ich habe alles mit besten Wissen und Gewissen erstellt, kann jedoch keine Gewähr übernehmen. Falls etwas unklar ist einfach nachfragen.

Eine Eigenheit hat das Nano Every Board was man vielleicht als technisch Interessierter wissen sollte aber erstmal nicht beunruhigen darf. Die Arduino Pins 18 und 19 sind auf dem Board doppelt belegt. Die Pins PF2/PF3 dienen als Digital/Analog Pins und PA2/PA3 kommen zum Einsatz wenn man I2C benötigt. Das schaltet die Arduino IDE intern um. Meine Pin Lib, die nur digital fähig ist, berücksichtigt das auch in der Form, dass bei Initialisierung von Pin 18 oder 19 die Portpins PA2/PA3 bewusst nochmal "abgeschalten" und auf ungefährliche Eingänge konfiguriert werden. Das heißt falls jemand träumt und I2C aktiviert und danach die gleichen Pins digital initialisiert, wird I2C usw. abgeschalten. Damit sich nicht 2 Ausgänge um die Vorherrschaft streiten. ;)

Was ich auch nicht unerwähnt lassen möchte ist, es gibt von MCUdude ein "MegaCoreX" Package für die IDE. MCUdude MegaCoreX

Alle Dokumente zum ATmega4809 findet man beim Chip Hersteller unter Documents. Die App Notes kann ich auch zum reindenken empfehlen.
Herstellerlink: https://www.microchip.com/wwwproducts/en/ATMEGA4809

Bevor es nun wirklich los geht muss noch eine kleine Änderung an der IDE gemacht werden, weil ich meine Libs nicht bis aufs Letzte für den C++11 Standard umschreibe der in der IDE defaultmäßig eingestellt ist, obwohl der mitgelieferte avr-gcc 7.3.0 locker C++14 und auch C++17 versteht.

Dafür gehen wir im IDE Installationspfad nach ...\packages\arduino\hardware\megaavr\1.8.7\ und fügen eine neue Datei namens platform.local.txt hinzu. Ob nun C++14 oder C++17 ist erstmal egal. Befindet sich mit im .zip File. Wer darüber mehr wissen möchte kann sich hier einlesen. Arduino IDE Feinheiten und Tricks

Im Anhang sind alle benötigten Libs.

Pin Lib:

Wer die Combie Pin Lib schon kennt, sollte sich auch mit meiner schnell zurecht finden. Die megaAVR0 Serie bietet als Feature zusätzlich jedes I/O Signal einzeln zu invertieren. Zusätzlich kann man für alle Pins pro gesamten Port "Slewrate" aktivieren. Das macht die Schaltflanke weniger steil. Desweiteren hat jeder Pin echte Interrupt Fähigkeit mit vielen Einstellmöglichkeiten. Die Interrupt Routine gilt für den gesamten Port. Zur Abfrage ob der konfigurierte Pin einen Interrupt ausgelöst hat, dafür soll die Methode isActive() dienen. Zum löschen des Interrupt Flag dient deleteFlag(). Damit sind wir bei einem kleinen feinen Unterschied zum bisher gewohnten mit den "alten" Controllern. Bisher musste jedes Interrupt Flag nur von Hand gelöscht werden, wenn man ohne Interrupt-Routine programmiert hat. Mit ISR wurde es automatisch gelöscht. Das ist nun nicht mehr der Fall. Jedes Interrupt Flag muss immer manuell gelöscht werden. Es gibt aber Ausnahmen bei den Timern. Pauschal kann man sich manuelles Flag löschen merken und ist prinzipiell erstmal nicht falsch.

Eine Übersicht über alle Methoden für die Objekte findet man im .pdf. Die Beispiele dafür sind unter Beispiele der Lib zu finden. Zusätzlich gibt es ein weiteres Headerfile 'NanoEveryPinSpecial.h'. Das ist eher eine Spielerei, gedacht für binäre Pin Statusausgaben zum Bsp. auf einem Display um eine Übersicht über die untere und obere Pinreihe zubekommen. Zum debuggen oder andere Tests kann man sich noch die verwendete Registeradresse und Registerinhalt anzeigen lassen. Desweiteren gibt es noch ein Headerfile NanoEveryPinNamen.h mit alternativen Pinnamen. Das muss bei Bedarf extra inkludiert werden. Damit ist man der Original IC Pin Bezeichnung näher. Hilfreich wenn man viel mit dem Controller Manual programmiert.

Nano Every -Pin Beschriftung

Hallo,

da auf dem Board leider kein Platz für eine Pinbeschriftung vorhanden ist, habe ich das bei mir durch Ausdruck und aufkleben eines Stück Papiers gelöst. Im .zip ist ein Excelfile zum ausdrucken. Beim Drucken muss ich 95% Größe einstellen, dann passt das 2,54 Raster sehr gut. Innen noch 2 Löcher für die seriellen Leds und Resettaster ausschneiden und auf die größeren Teile mit kleinen Leimklecks kleben.

Nano Every - Timer TCB und auch etwas TCA

Hallo,

diese Beispiele sollen als Einstieg für den Timer TCBn stehen. Timertyp B kann man als Messtimer betrachten. Er stellt verschiedene Möglichkeiten zur Verfügung was man von einem Signal messen kann. Unter anderem auch eine "Timeout" Methode, wenn sich innerhalb einer Zeit das Signal nicht ändert. Vom Typ TCB stehen insgesamt 4 Einheiten zur Verfügung.

Für Timer TCB gibts eine Lib und die Übersicht über alle Methoden findet man im Example "_TCBnMethodenUebersicht".

Vorab.

Falls man einen TCB zugehörigen Ausgangspin (WO - Wave Output) vom Timer schalten lassen möchte, dann stehen auf dem Every Board nur 2 Pins zur Verfügung. Von TCB0 Pin 6 und von TCB1 Pin 3. Die müßte man per PORTMUX Register "aktivieren" bzw. umlegen, weil diese Pins nicht die default Ausgangspins sind. Kurzum, mittels PORTMUX kann man Pins intern umrouten. Dazu später mehr. Benötigen wir hier im Beispiel nicht. Zum benötigten Eventsystem gehe ich in den Beispielen um den ADC näher ein.

1.
In diesem Beispiel wollen wir die bekannte Led D13 in der loop mittels millis takten lassen. Von diesem Takt messen wir die Periodendauer und Pulsweite mit dem Timer TCB0.

Damit wir beim experimentieren nicht alles ständig umkonfigurieren müssen, habe ich den Prescaler von Timer TCB0 auf den Prescaler von Timer TCA0 konfiguriert und mit 64 festgelegt. Dieser ist in den Arduino Einstellungen default auf 64 eingestellt. Damit haben wir ohne größeren Aufwand erstmal mehr zeitlichen Spielraum zum experimentieren. Man muss jetzt nur in dem Beispiel die maximale Messzeit von TCB0 beachten. Die sich einfach aus Eingangstakt-Periodendauer und Wertebereich zusammensetzt. Also 62,5ns * 64 * 65536 = 262,144ms

Damit die Led D13 blinkt, muss man der Funktion heartbeat(n) nur die gewünschte Periodendauer als Parameter mitgeben. Wie gesagt hier in dem Bsp. max. 262ms. Bei größeren Werten blinkt die Led dennoch wie gewünscht, aber die Periodedauer liegt für unseren Timer TCB0 außerhalb seines Wertebereiches. Man bekäme nur Überlaufreste angezeigt. Mit TCB0 eigenen Prescaler 1 und 2 verkleinert sich der heartbeat Parameter entsprechend auf 4 bzw. 8ms.

Am Ende werden wir feststellen, dass die Messwerte etwas schwanken, was immer "schlimmer" wird je höher Led D13 taktet. Das liegt daran das die Led zur Programmlaufzeit getaktet wird. Sprich immer wenn die loop dafür Zeit hat. Für Led Geblinke sicherlich vollkommen egal. Zur Demonstration mit TCB0 aber sehr anschaulich.

2.
Das zweite Bsp. soll für den Umgang mit TCBn und auch TCA0 dienen. Diesmal soll der Led D13 Takt genauer werden. Deswegen konfigurieren wir den Timer TCA0 für unsere Zwecke um und lassen die Led in dessen Interrupt Routine toggeln. Weil durch die TCA0 Umkonfiguration millis() beeinflusst wird, müssen wir das korrigieren. Dafür dient die Funktion correctedMillis().

Timer TCA ist im Normal Mode konfiguriert. In dem Modi ist das PER Register Frequenz bestimmend. Vorweg. Es gibt für alle wichtigen Register immer ein Buffer Register die immer verfügbar sind im Gegensatz zu den bekannten älteren Familien mit Typen wie ATmega328P und ATmega2560 & Co. Man sollte laut meiner Meinung immer das Buffer Register verwenden, weil damit die Timereinstellungen immer zum richtigen Zeitpunkt übernommen werden. Außer es gibt andere Gründe.

Für den Timer TCA0 habe ich keine Lib erstellt, vielleicht auch nicht schlecht, sieht man doch die gesamte Konfiguration. Defaultmäßig wird TCA0 von Arduino für millis und PWM verwendet und arbeitet im 8Bit Modus. Das alles setzen wir anfangs zurück und konfigurieren ihn komplett neu. Wir müssen ihn für das Bsp. nur im Normalmode konfigurieren und den Overflow Interrupt aktivieren. Desweiteren einen Wert ins Perioden Register (PER/PERBUF) schreiben. Sobald der Timerzähler diesen Wert erreicht hat, löst er einen Overflow Interrupt aus, die Programmabarbeitung (loop) springt in dessen Interrupt Routine, toggelt die Led macht in loop weiter.

Am Ende seht ihr hoffentlich wie ich an Hand der Messwerte, dass der Takt schon stabiler ist im Vergleich zu vorher. Das übrig gebliebene zappeln der Messwerte ist bedingt durch die Interruptunterbrechungen der seriellen Ausgabe.

3.
Aufbauend auf dem Beispiel "TCB FRQPW with TCA ISR" und etwas größer ausgelegt. Um den Einfluss von Interrupts rauszunehmen, lassen wir diesmal den Taktpin vom Timer TCA0 selbst schalten. Das sind die sogenannten 'WOn' Pins. Wave Output. Davon hat der Timer TCA0 insgesamt 3 im normalen 16Bit Modus oder 6 im 8Bit Splittmodus. Entweder hat man 0-WO0...0WO2 oder 0-WO0...WO5 zur Verfügung.

Die vorangestellte '0-' steht übrigens für die Null von TCA0. Da der verwendete ATmega4809 vom Timertyp TCA nur einen hat, gibts nur TCA0 und keinen TCA1 oder TCA2.

Ich beschränke mich hier auf den TCA0 Frequency 16Bit Modus. Schaut man sich die Controller Pin-Mux Tabelle an, sieht man das in Default PORTMUX Einstellung zwei Pins zur Verfügung stehen die nach außen geführt sind. 0-WO0 auf Pin 2 und 0-WO1 auf Pin 7. Als Bsp. könnte man alle 0-WOn Pins komplett auf Port E verlegen. Dafür muss man nur das PORTMUX Register ändern. In dem Fall PORTMUX.TCAROUTEA = PORTMUX_TCA0_PORTE_gc. Damit liegt 0-WO0 auf Pin 11 und 0-WO1 auf Pin 12. Nicht vergessen die geänderten Pins auf Ausgang zu konfigurieren.

Wir nutzen wie gesagt in dem Bsp. den Frequency Modus, da im Normal Modus kein 0-WOn Pin zur Verfügung steht. Der Grund ist, dass die WOn Pins nur auf aktivierte CMPnEN und damit nur auf Vergleichswerte in den CMPn/CMPnBUF Registern reagieren. Das Perioden/Frequenz bestimmende Register im Frequency Mode ist also immer CMPn. In unserem Bsp. CMP0. Alle erzeugten Frequenzen haben ein Duty Cycle von 50% und sind auf allen Ausgängen gleich. Zusätzlich lasse ich den TCA0 Timerausgang 0-WO1 takten. Wie schon einmal erwähnt, sollten soweit verfügbar die gepufferten Timerregister verwenden. Das sind in unserem Fall CMP0BUF und CMP1BUF statt CMP0 und CMP1. Mittels Bufferregister ist sichergestellt das Timeränderungen immer zum richtigen Zeitpunkt erfolgen und damit zum Zeitpunkt der Änderung keine unerwünschten Signalverläufe entstehen.

Wir wissen das im Bsp. CMP0 für den gesamten Timer TCA0 das Frequenz bestimmende Register ist. Das bedeutet CMP1 (oder CMP2) darf nie größer sein wie CMP0, ansonsten kann kein Vergleich (Compare) stattfinden. Das bedeutet weiter das ein Signal mit CMP1 im Vergleich zu CMP0 'nur' nach vorn verschoben ist. Ansonsten sind sie immer identisch.

Der Rest ist wie vorher, dass Signal von 0-WO0 wird mittels Timer TCB0 ausgemessen und seriell ausgegeben. Der Messpin 21 wird mit Kanal 2 des Eventsystems verbunden und der wiederum gibt das Signal im Eventsystem an den Timer TBC0 weiter.

Nano Every - CCL – Configurable Custom Logic

Hallo,

hiermit möchte ich euch das Logik Modul vorstellen.

Das Board stellt allerdings nur eine komplette Logikeinheit nach außen zur Verfügung. Konkret LUT2 auf den Arduino Pins 14-17. Damit wollen wir uns etwas näher befassen. Ist am Ende gar nicht so schwer.

Dafür habe ich die Pins wie folgt vorbereitet. An den Logik Eingangspins 15-17 sitzen Taster die nach Masse schalten. An dem Logik Ausgangspin 14 sitzt eine Led.

Damit das alles funktioniert, aktivieren wir für die Eingangspins die Pullups und invertieren das Signal, damit am Ende die CLL Logik korrekt arbeiten kann. Vorweg, ihr müßt dazu nicht zwingend meine NanoEveryPin Lib verwenden. Die konfiguriert hier auch nur die Pins als gewöhnliche Ein- und Ausgänge.

Jetzt gehts los. Jede Logikeinheit hat maximal 3 nutzbare Eingänge und einen Ausgang. Jeder dieser 3 Eingänge kann von irgendwoher sein Signal bekommen. Das muss nicht ein physischer Eingangspin sein wie im folgenden Beispiel. Man könnte auch einen Timerausgang, Usart TX Signal etc. oder den Ausgang einer Logikeinheit auf einen Eingang einer anderen Logikeinheit konfigurieren. Siehe Manual 27.2.2.1. Das alles wird in den LUTn Registern CLTRB und CTRLC konfiguriert. In unserem Fall LUT2. Wir bleiben bei den nach außen verfügbaren Pins. Das heißt für uns wir müssen IN0, IN1 und IN2 von LUT2 konfigurieren.

In den Tabellen 27.5.7 und 27.5.8 haben diese immer den Wert 0x05. Das ist nur der Wert der Bitgruppe INSELx, nicht der benötigte Registerwert. Irgendwelche Bitschubserei können wir uns sparen, weil es vordefnierte Bitnamen für alles gibt. Man muss sie nur finden. :-) Dazu schaut man das Controller Headerfile om4809.h einmal in Ruhe durch.

Wir benötigen die vordefinierten Bitgruppen CCL_INSEL0_IO_gc, CCL_INSEL1_IO_gc und CCL_INSEL2_IO_gc und müssen diese nur noch den richtigen Registern zuweisen.

Eingang LUTn-IN0 und LUTn-IN1 werden immer im LUTn.CLTRB Register konfiguriert.
Eingang LUTn-IN2 wird immer im LUTn.CLTRC Register konfiguriert.
Also verordert man ggf. die Bitnamen und weißt das Ergebnis dem entsprechenden Register zu.
CCL.LUT2CTRLB  = CCL_INSEL0_IO_gc;   // pin LUT2-IN0 input source, PD0
CCL.LUT2CTRLB |= CCL_INSEL1_IO_gc;   // pin LUT2-IN1 input source, PD1
CCL.LUT2CTRLC  = CCL_INSEL2_IO_gc;   // pin LUT2-IN2 input source, PD2
Das der Logikausgang auf einen Pin mündet konfiguriert man im Register CTRLA und schaltet darin auch diese Logikeinheit scharf.
CCL.LUT2CTRLA  = CCL_OUTEN_bm;       // enable Output LUT2, PD3
CCL.LUT2CTRLA |= CCL_ENABLE_bm;      // enable LUT2
Zusätzlich muss man noch den "Hauptschalter" der gesamten CCL Einheit scharf schalten.
CCL.CTRLA = CCL_ENABLE_bm;           // general enable CCL module
Und nun kommen wir zum wichtigsten Teil, der Wahrheitstabelle, also wie der Ausgang auf Eingänge reagieren soll. Das wäre Tabelle 27.3.1.3 Jede Zeile bzw. jede Eingangs Bit Kombination entspricht einem bestimmtes Bit im TRUTHn Register. Diese kann man beliebig kombinieren sprich addieren.
UND Logik:        Vermutlich der einfachste Einstieg in den Aufbau.
Der Ausgang darf nur auf '1' schalten, wenn alle Eingänge '1' haben.
Das entspricht der letzten Zeile in der Tabelle.
Das bedeutet man muss im TRUTHn Register nur das Bit 7 setzen und hat seine UND Funktion. Dezimal 128.

ODER Logik:
Der Ausgang darf auf '1' schalten, wenn mindestens ein Eingang '1' hat, egal welcher.
Hierbei werden sicherlich die meisten Leute, wie ich anfangs auch, alle Zeilen zusammensuchen wo jeder Eingang einzeln logisch '1' ist. Also Bit 1, 2 und 4 im TRUTHn Register setzen und sich wundern das es nicht so richtig funktioniert. Das entspricht nämlich dem XOR. Für ein ODER müssen wir alle Kombinationen addieren außer Bit 0. Dezimal 254. Dann klappt das auch mit dem ODER.

XOR Logik:
Bei nur einer einzigen Änderung eines Eingangs schaltet der Ausgang auf '1'.
Die Antwort habe ich nun schon vorweg genommen. Man kombiniert alle Bits der Zeilen wo nur ein Eingang für sich alleine '1' hat. Das wären hier Bit 1, 2 und 4. Dezimal 22.
Das wars für den Einstieg. Ein hilfreiches Dokument außer dem Manual ist noch die App Note TB3218 oder das Dokument zum ATMEGA4809

PS: Man könnte auch die anderen Logikeinheiten verwenden, da laut meiner Recherche die nicht nach außen geführten Pins auf dem Board nicht beschalten sind. Das heißt für alle internen Eventsignale sind diese theoretisch nutzbar. Ich habs noch nicht getestet.
Besondere Beachtunng sollte allerdings Arduino Pin 18 und 19 geschenkt werden. Die haben Board intern eine Doppelbelegung mit PF2/PF3 und PA2/PA3. Zwischen den Ports wird umgeschalten je nachdem ob die Pins digital, analog oder für I2C verwendet werden. Die ICs Pins sind dabei auf dem Board direkt verbunden. Ich möchte damit nur sagen, falls man selbst an den Registern fummelt, sollte man dafür Sorge tragen das die zusammengelegten Pins nicht zeitgleich Ausgänge sind.

Beispiele

Nano Every - Eventsystem, ADC, RTC

Hallo,

hier soll es um das Eventsystem gehen. Gibts schon länger in den XMEGA Controllern, die ich aber nicht kenne. Das Eventsystem hat nun auch in die neuen Controller der ATmega und ATtiny Serie Einzug gehalten. Der grundlegende Sinn dahinter ist, dass man Aufgaben ohne zutun der eigentlichen MCU bzw. Code erledigen kann. Ein Timer bspw. der seinen eigenen Timerausgang ohne ISR schaltet arbeitet auch selbstständig ohne das die MCU bzw. der Code eingreifen muss. Der arbeitet also autark. Mit dem Eventsystem kann man nun solche Einheiten verbinden ohne das man im Code irgendwelche Signale abfragen und Daten zwischen den Einheiten weitergeben muss. In den gezeigten Bsp. reduziert man damit die CPU bzw. Interruptlast.

Beim Eventsystem gibts es mehrere Channels über die man verschiedene Dinge miteinander verbinden kann. Sprich man legt fest wer auf was reagieren soll. Dabei gibts immer einen Eventgenerator. Dieser sorgt dafür das ein Event überhaupt ausgelöst wird. Bspw. ein Zählerüberlauf nach bestimmter Zeit um irgendwas zyklisch zu machen. Auf der anderen Seite eines Channels gibts immer einen oder mehrer User. Das sind die Empfänger und reagieren auf das Eventereignis. Daraufhin kann irgendeine Aktion gestartet werden. Bspw. sorgt ein Timer Event Generator dafür das zyklisch der ADC ausgelesen wird. Oder ein Signal an einem Pineingang startet einen Timer der das Signal vermisst. Der Phantasie sind keine Grenzen gesetzt.

Das Thema wird im Manual Kapitel 14 beschrieben. Von 14.5.2 ausgehend zeige ich für das Nano Every Board zugeschnitten eine kleine Übersicht welchen Arduino Pin als Event Generator man mit welchen Event Channel verbinden kann. Man kann einen Pin nicht mit jedem x-beliebigen Kanal verbinden. Event User kann man dagegen beliebig anbinden. Anders ausgedrückt ein Event Ausgang (Generator) kann man mit beliebigen Event Eingängen (User) über vorgesehene Kanäle verbinden.
           |    Event       |
  Pin Port | Port | Channel |
   0  PC5  |  0     2 or 3  |
   1  PC4  |  0     2 or 3  |
   2  PA0  |  0     0 or 1  |
   3  PF5  |  1     4 or 5  |
   4  PC6  |  0     2 or 3  |
   5  PB2  |  1     0 or 1  |
   6  PF4  |  1     4 or 5  |
   7  PA1  |  0     0 or 1  |
   8  PE3  |  0     4 or 5  |
   9  PB0  |  1     0 or 1  |
  10  PB1  |  1     0 or 1  |
  11  PE0  |  0     4 or 5  |
  12  PE1  |  0     4 or 5  |
  13  PE2  |  0     4 or 5  |
  14  PD3  |  1     2 or 3  |
  15  PD2  |  1     2 or 3  |
  16  PD1  |  1     2 or 3  |
  17  PD0  |  1     2 or 3  |
  18  PF2  |  1     4 or 5  |
  19  PF3  |  1     4 or 5  |
  20  PD4  |  1     2 or 3  |
  21  PD5  |  1     2 or 3  |
Am Bsp. mit der Timer TCB0 Messung verbinden wir Messpin 21 mit dem Timer Eingang. Der Pin 21 ist damit der Eventgenerator. Er erzeugt mit seinem Signal ein Ereignis worauf reagiert werden soll. Laut Tabelle können wir für Pin 21 den Kanal 2 oder 3 verwenden. Zusätzlich ist Eventport 1 damit festgelegt. Wir haben in dem Fall die Wahl zwischen folgenden Zuweisungen.
EVSYS.CHANNEL2 = EVSYS_GENERATOR_PORT1_PIN5_gc;   // connect Pin 'PD5' to channel 2
oder
EVSYS.CHANNEL3 = EVSYS_GENERATOR_PORT1_PIN5_gc;   // connect Pin 'PD5' to channel 3
'PORT1' im Bitnamen sollte klar sein warum. Die 5 von PIN5 im Namen ist die Bitnummer vom Pin. PD5 ist die orignale Pinbezeichnung des Arduino Pin 21. PD5 sagt aus, dass der Pin am Port.D und Bit.Nummer 5 angebunden ist. Das am Rande.

Wenn der Kanal ausgewählt ist, muss man auf der anderen Seite, bildlich gesprochen, noch den User anbinden. Bspw. den Timer TCB0.
EVSYS.USERTCB0 = EVSYS_CHANNEL_CHANNEL2_gc;
Damit wandert das Signal am Pin 21 mittels Kanal 2 oder 3 zum Timer TCB0. Zusätzlich kann man mehrere User an einen Kanal anbinden, falls mehrere User auf Pin 21 reagieren sollen. Dabei gibts keine Einschränkung. Soviel zur grundlegenden Übersicht.

Kommen wir zu einem Beispiel womit 2 Event Generatoren und 2 Event Usern mit jeweils einem Kanal verbunden werden. Wir wollen das der ADC0 zyklisch aller einer Sekunde eine Messung macht. Das Zeitintervall erzeugen wir diesmal nicht mit millis sondern machen uns vom Timer TCB0 den Timeout Modus zu nutze.

Der TCB Timeout Modus funktioniert wie folgt. Er überwacht eine Signaldauer und wenn diese eine eingestellte Zeit überschreitet, dann reagiert er mit dem Interrupt Capture Flag. Auf welche Flanke er reagieren soll kann man im EVCTRL Register einstellen. Manual 21.5.3. Wir benötigen also nur ein Signal an einem Pin welches wir einschalten aber nicht gleich wieder ausschalten. Damit geht TCB0 in den Timeout und liefert uns eine Reaktion das die Zeit um ist. Die Konfiguration sieht man in der Funktion 'initTCB0()'. Damit wir auf verfolgbare Zeiten kommen im seriellen Monitor lassen wir aller 1s eine Messung machen. Weil wir millis nicht benötigen ändern wir einfach den Prescaler von TCA0 auf 1024 und konfigurieren auch TCB0 darauf. Damit haben wir eine Taktzeit von TCB0 von 64µs. Damit wir auf 1s kommen multiplizieren wir den Wert mit 15625. Das ist unser Vergleichswert damit TCB0 aller 1s auslöst.

Damit das alles klappt wie gewünscht verbinden wir mittels Eventsystem das benötigte 'Timeout' Signal mit dem TCB0 Eventeingang und das TCB0 Capture Signal mit dem ADC0 Eventeingang. Also fangen wir an. Pin 2 (PA0) können wir laut Tabelle (bzw. Manual 14.5.2) nur mittels Kanal 0 oder 1 auf Eventport 0 mit irgendeinem User verbinden. Diesen Event Generator verbinden weiter über Kanal 1 an den Event User Timer TCB0. Damit der ADC0 daraufhin zyklisch seine Messung starten kann, verbinden wir das TCB0 Generator Event über Kanal 0 an den Event User ADC0. Hier hätten wir laut Manual jeden Kanal nehmen können. Außer Kanal 1, der ist von uns schon belegt.

Das Programm folgt nun dem einfachen Prinzip. Setze Pin 13 auf 'High', warte bis Timer TCB0 seinen Timeout auslöst, daraufhin startet ADC0 seine Messung an Pin 21, setzt eine bool Variable, auf die gepollt wird. Wenn diese 'true' ist wird der Messwert seriell ausgegeben. Gleichzeitig setzt der ADC0 nach seiner Messung den Pin13 zurück auf 'Low'. Wenn der Messwert seriell ausgegeben wurde wird Pin 13 erneut auf 'High' gestetz und das Spiel beginnt von vorn. Das 'High' setzen kann man auch am Ende der ISR von ADC0 schreiben, dann wäre die Zykluszeit genauer. Lasse ich außen vor.

Zum Aufbau muss man folgende Pins verbinden. D13 mit D2 und an Pin 21 kommt der Schleiferkontakt eines Potis.

Die Pinkonfiguration für ADC0 ist auch schnell erklärt. Wir schauen im Manual in der Pin Multiplex Übersicht 4.1 welcher Pin davon auf unserem Board zur Verfügung steht. Ich habe mir einfach 'AIN5' rausgesucht der auf Arduino Pin 21 (pinPD5) liegt. Das wird im ADC0.MUXPOS Register konfiguriert. Dann schalten wir noch den Interrupt ein und sagen ihm das er auf das Eventsignal reagieren soll. Zusätzlich aktivieren wir den Sample Akkumulator und wählen den Faktor 32. Der dient zum Oversampling bzw. Rauschunterdrückung und arbeitet ADC intern ohne äußeres zutun. Als ADC Rückgabewert erhalten wir den aufsummierten Akku-Wert. Wir teilen diesen durch 32 und haben den üblichen 10 Bit Messwert als Durchschnitt von 32 Einzelmessungen.

Was hat es mit der inkludierten Lib ForwardCompatibility.h auf sich? Da ich immer auf dem neuesten Stand sein möchte, verwende unter anderem auch immer die aktuelle Version vom Controller Headerfile. Das Headerfile wird fast immer parallel zum Manual verwendet, damit ich möglichst die eh schon vordefinierten Bitnamen verwenden kann. Nun hat sich aber seit Veröffentlichung vom Nano Every Board das Headefile aktualisiert. Sprich ein paar Bitdefinitionen haben sinnvollere Namen bekommen. Damit ich trotz aktueller Namen zur Standard Arduino IDE kompatibel bin, habe ich ein Headerfile erstellt, was niemanden stören wird.

Die anderen beiden ADC Bsp. verwenden die interne RTC statt dem Timer TCB und basieren auf der App Note von Microchip. Einmal mit Interrupt einmal ohne. Der ADC Eingangstaktteiler wird auf Arduino Default Einstellung 128 und Vref auf Vdd belassen. Aktiviert ist er auch.

Beispiele