<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://www.mikrocontroller.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Nospam2000</id>
	<title>Mikrocontroller.net - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="https://www.mikrocontroller.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Nospam2000"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/Nospam2000"/>
	<updated>2026-04-11T03:54:23Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=106544</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=106544"/>
		<updated>2024-01-05T05:12:16Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: Tippfehler korrigiert: andere =&amp;gt; anderen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es um die Input Capture Funktionalität, welche sehr präzise Zeitmessungen ermöglicht, da Programmlaufzeiten und Verzögerungen für die Genauigkeit keine Rolle spielen. Bei einem Flankenwechsel eines externen Signals an einem ICPn Pin speichert der Timer seinen aktuellen Wert im ICRn Register.&lt;br /&gt;
&lt;br /&gt;
Das hier beschriebene Projekt zeichnet die High- und Low-Zeiten eines externen Binärsignals auf. Nach der Aufzeichnung wird es analysiert und ausgegeben. Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme.&lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed Capture&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Das realisierte Programm speichert bis zu 3600 Flankenwechsel mit einer Auflösung von 62,5 ns. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65535 µs liegen.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, TSOP1738, TSOP1740) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und das Signal zu demodulieren&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png|850px]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von Signalen wird eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern der Abstand zwischen zwei Flanken größer als ca. 2,8 µs ist. Bei der IR-Messung werden die Modulationsfrequenz und das Puls/Pause Verhältniss bestimmt und zusätzlich die Zeitwerte ausgegeben. Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png|850px]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [http://www.mikrocontroller.net/topic/212195 diesen Artikel]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) ist unter [[#Siehe auch|Siehe auch]] zu finden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Grafik, welche den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte werden in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 \&lt;br /&gt;
+9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 \&lt;br /&gt;
+9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 \&lt;br /&gt;
+9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 \&lt;br /&gt;
+9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration [ns]&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die anderen MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Makros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über AVR Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bits schieben, setzen, rücksetzen&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
* Modulo-Arithmetik (für Berechnung der Zeitdifferenzen)&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Vermeidung von Fließkommaarithmetik (für Umrechung von Clocks in Nanosekunden)&lt;br /&gt;
** [[Festkommaarithmetik]]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
** [https://github.com/bengtmartensson/Infrared4Arduino Infrared4Arduino library, welche Teile von diesem Projekt verwendet]&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=105395</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=105395"/>
		<updated>2022-09-07T11:10:23Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: Link hinzugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es um die Input Capture Funktionalität, welche sehr präzise Zeitmessungen ermöglicht, da Programmlaufzeiten und Verzögerungen für die Genauigkeit keine Rolle spielen. Bei einem Flankenwechsel eines externen Signals an einem ICPn Pin speichert der Timer seinen aktuellen Wert im ICRn Register.&lt;br /&gt;
&lt;br /&gt;
Das hier beschriebene Projekt zeichnet die High- und Low-Zeiten eines externen Binärsignals auf. Nach der Aufzeichnung wird es analysiert und ausgegeben. Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme.&lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed Capture&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Das realisierte Programm speichert bis zu 3600 Flankenwechsel mit einer Auflösung von 62,5 ns. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65535 µs liegen.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, TSOP1738, TSOP1740) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und das Signal zu demodulieren&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png|850px]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von Signalen wird eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern der Abstand zwischen zwei Flanken größer als ca. 2,8 µs ist. Bei der IR-Messung werden die Modulationsfrequenz und das Puls/Pause Verhältniss bestimmt und zusätzlich die Zeitwerte ausgegeben. Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png|850px]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [http://www.mikrocontroller.net/topic/212195 diesen Artikel]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) ist unter [[#Siehe auch|Siehe auch]] zu finden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Grafik, welche den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte werden in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 \&lt;br /&gt;
+9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 \&lt;br /&gt;
+9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 \&lt;br /&gt;
+9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 \&lt;br /&gt;
+9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration [ns]&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Makros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über AVR Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bits schieben, setzen, rücksetzen&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
* Modulo-Arithmetik (für Berechnung der Zeitdifferenzen)&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Vermeidung von Fließkommaarithmetik (für Umrechung von Clocks in Nanosekunden)&lt;br /&gt;
** [[Festkommaarithmetik]]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
** [https://github.com/bengtmartensson/Infrared4Arduino Infrared4Arduino library, welche Teile von diesem Projekt verwendet]&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Logic_Analyzer&amp;diff=102429</id>
		<title>Logic Analyzer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Logic_Analyzer&amp;diff=102429"/>
		<updated>2020-10-11T12:08:03Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* BitHound Logic Analyzer */ fixed link&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Diese Seite enthält eine Übersicht über verschiedene Logic Analyzer &#039;&#039;&#039;Selbstbau-Projekte&#039;&#039;&#039;. Zur genellen Funktion dieser Geräte, siehe die Seite [[Logikanalysator]].&lt;br /&gt;
&lt;br /&gt;
== AVR-basiert ==&lt;br /&gt;
&lt;br /&gt;
=== LoLA ===&lt;br /&gt;
&lt;br /&gt;
* 2002 von A. Hinrichs&lt;br /&gt;
* AT90S1200 Controller with a 2k-RAM&lt;br /&gt;
* ASM&lt;br /&gt;
&lt;br /&gt;
LoLA is a 8-Bit Logicanalyzer. Internal sampletimes from 2µs to 16ms. Triggerword with don&#039;t cares. External clock or trigger on rising or falling edge. Timing and state with I2C-analysis. 2k memory, very low cost.&lt;br /&gt;
With Pulsegenerator. Programmable Ti and Tp 1us..8sec, Duty-Cycle, Period/Frequency, Continuous- and Burst-Mode. 16- and 32-bit Software for Windows&lt;br /&gt;
&lt;br /&gt;
[http://avrfreaks.net/index.php?module=FreaksAcademy&amp;amp;func=viewItem&amp;amp;item_type=project&amp;amp;item_id=47 Projektseite bei avrfreaks.net]&lt;br /&gt;
&lt;br /&gt;
===  AVR USB Logikanalysator ===&lt;br /&gt;
&lt;br /&gt;
* 2006 B. Sauter&lt;br /&gt;
* Mega 32 &amp;amp; USBN9604 &lt;br /&gt;
* 8-Bit&lt;br /&gt;
* 250kHz online, 1000 Samples @ 4MHz&lt;br /&gt;
* USB&lt;br /&gt;
&lt;br /&gt;
http://www.mikrocontroller.net/topic/48559&lt;br /&gt;
&lt;br /&gt;
=== Simple Logic Analyzer ===&lt;br /&gt;
&lt;br /&gt;
* 8-channel&lt;br /&gt;
* 32kB SRAM&lt;br /&gt;
* RS232&lt;br /&gt;
* 2 MHz&lt;br /&gt;
* Software: Win GUI&lt;br /&gt;
&lt;br /&gt;
http://antoniak.ep.com.pl/index.php?id=sla&lt;br /&gt;
&lt;br /&gt;
=== SCANALOGIC ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Version 1&#039;&#039;&#039;&lt;br /&gt;
* bis 4 Kanäle&lt;br /&gt;
* bis 4 Million Samples per second&lt;br /&gt;
* RS232 (bzw. USB-RS232)&lt;br /&gt;
* Windows-Software (Visual Basic)&lt;br /&gt;
* Software (AVR und Windows): Closed Source?&lt;br /&gt;
* Basis: Atmega16 und NAND-Gate&lt;br /&gt;
* Eingestellte Produktlinie&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Version 2 Pro&#039;&#039;&#039;&lt;br /&gt;
* bis 4 Kanäle&lt;br /&gt;
* bis 20 Million Samples per second&lt;br /&gt;
* Maximum sampling buffer 256K&lt;br /&gt;
* Generator/Playback Modus&lt;br /&gt;
* USB HID mode&lt;br /&gt;
* Firmware upgrade feature&lt;br /&gt;
* Windows-Software (Visual Basic) und &amp;quot;ScanaStudio&amp;quot;&lt;br /&gt;
* Basis: Atmel AVR&lt;br /&gt;
* Aufgebaut und getestet&lt;br /&gt;
* Wird nicht mehr direkt von Ikalogic hergestellt, aber noch unterstützt, es gibt Bausätze als &amp;quot;Edu-Kit&amp;quot;&lt;br /&gt;
* https://www.ikalogic.com/scanalogic-2-support-page/&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Version ScanaQuad&#039;&#039;&#039;&lt;br /&gt;
* bis 4 Kanäle&lt;br /&gt;
* bis 25/50/100/200 Million Samples per second&lt;br /&gt;
* Maximum sampling buffer 256K/1M/2M/4M&lt;br /&gt;
* Generator/Playback Modus&lt;br /&gt;
* USB HID mode&lt;br /&gt;
* Firmware upgrade feature&lt;br /&gt;
* Windows-Software &amp;quot;ScanaStudio V2.0&amp;quot;&lt;br /&gt;
* https://www.ikalogic.com/scanaquad-logic-analyzer-signal-generators/scanaquad-specifications/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Version ScanaPlus&#039;&#039;&#039;&lt;br /&gt;
* bis 9 Kanäle&lt;br /&gt;
* 100 Million Samples per second&lt;br /&gt;
* Generator/Playback Modus&lt;br /&gt;
* USB HID mode&lt;br /&gt;
* Windows-Software &amp;quot;ScanaStudio V2.0&amp;quot;&lt;br /&gt;
* https://www.ikalogic.com/scanaplus/&lt;br /&gt;
&lt;br /&gt;
=== MiniLog ===&lt;br /&gt;
&lt;br /&gt;
Zur Zeit gibt es zwei Versionen, die sich hauptsächlich in der Samplerate und in der Speichergröße unterscheiden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;50 MSa/s Version&#039;&#039;&#039;&lt;br /&gt;
* 8 Kanäle&lt;br /&gt;
* 50 MSa/s&lt;br /&gt;
* 4k Samplespeicher&lt;br /&gt;
* Trigger auf beliebige Pegelkombinationen &lt;br /&gt;
* Basis ATMega644&lt;br /&gt;
* FT232 USB 115200 Baud&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;80 MSa/s Version&#039;&#039;&#039;&lt;br /&gt;
* 8 Kanäle&lt;br /&gt;
* 50 MSa/s&lt;br /&gt;
* 16k Samplespeicher (32k bei neuer Software)&lt;br /&gt;
* Trigger auf beliebige Pegelkombinationen &lt;br /&gt;
* Basis ATMega88 / ATMega48&lt;br /&gt;
* FT232 USB 500000 Baud&lt;br /&gt;
&lt;br /&gt;
Weitere Infos und Diskussion:&lt;br /&gt;
* http://www.avr.roehres-home.de/logikanalyzer/index.html&lt;br /&gt;
* http://www.mikrocontroller.net/topic/141602&lt;br /&gt;
&lt;br /&gt;
=== Arduino Logic Analyzer ===&lt;br /&gt;
Funktioniert auf einem Arduino mit AVR, STM32 und ESP8266.&lt;br /&gt;
&lt;br /&gt;
Benutzung:&lt;br /&gt;
* .ino auf den Arduino laden&lt;br /&gt;
* processing.pde in Processing laden und seriellen Port auswählen&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://github.com/aster94/logic-analyzer GitHub.com/aster94/logic-analyzer]&lt;br /&gt;
&lt;br /&gt;
== FPGA / CLPD ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Deditec LAs sind keine Selbstbauprojekte, daher hier entfernt. Ggf. neuen Artikel aufmachen oder anderen, passenderen ergänzen. Danke.&lt;br /&gt;
&lt;br /&gt;
=== Deditec Logicanalyzer USB-LOGI-500===&lt;br /&gt;
&lt;br /&gt;
* 36 Kanäle&lt;br /&gt;
* 500 MSamples/sec (bis 100 Samples/Sec)&lt;br /&gt;
* 4096 Samples Speichertiefe/Kanal&lt;br /&gt;
* 2 Trigger Ebene&lt;br /&gt;
* Logi+ Windows Software&lt;br /&gt;
* sehr kostengünstiges Einsteigermodell&lt;br /&gt;
&lt;br /&gt;
=== Deditec Logicanalyzer USB-LOGI-250===&lt;br /&gt;
&lt;br /&gt;
* 36 Kanäle&lt;br /&gt;
* 250 MHz bis 100 Hz Sampling Rate&lt;br /&gt;
* 512 KSamples Speichertiefe/Kanal&lt;br /&gt;
* 1/8 .. 7/8 Pretrigger&lt;br /&gt;
* 2 Trigger Ebenen&lt;br /&gt;
* Windows Software Logi+&lt;br /&gt;
&lt;br /&gt;
[http://www.deditec.de/de/logikanalysatoren/prod.html Herstellerseite]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SUMP Logic Analyzer ===&lt;br /&gt;
&lt;br /&gt;
* 32 Kanäle bis 100 MHz, 16 Kanäle bei 200 MHz&lt;br /&gt;
* 200 MHz - 10 Hz Sampling Rate&lt;br /&gt;
* 256 KSamples Speicher (1 MByte)&lt;br /&gt;
* Ankopplung über RS232&lt;br /&gt;
* Java Software&lt;br /&gt;
&lt;br /&gt;
Der SUMP Logic Analyzer hat wahlweise einen einfachen Trigger (Rising/Falling mit Maskierung) oder einen komplexen 4stufigen Trigger, sowie einen einfachen zuschaltbaren Noise Filter, der Pulse kürzer als 1/100 MHz filtert um Crosstalk-Effekte in den Anschlussleitungen zu kompensieren. Der Speicher wird im Wartezustand als &amp;quot;Ringstack&amp;quot; kontinuierlich beschrieben. Dadurch können auch Daten von vor der Trigger-Auslösung ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Das Gerät nutzt das Xilinx Spartan 3 Starter Kit Board von Digilent (Kosten Stand Mai 2006: 100 - 150 Euro). Es muss lediglich der FPGA beschrieben und die Software eingerichtet werden. Die Software nutzt für den Zugriff auf die RS232 die RXTX Bibliothek, die für alle gängigen Betriebssysteme existiert.&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [http://www.sump.org/projects/analyzer/ Projektseite bei sump.org]&lt;br /&gt;
* [http://logicanalyzer.sourceforge.net/ LogicAnalyzer] is a framework as well as an application to operate a PC based logic analyzer. It&#039;s built using the Eclipse RCP and designed with extensibiliy in mind. Integrating devices, providing tools or creating completely new functionality is easy. The most prominent OpenSource logic analyzer hardware (namely the SUMP Logic Analyzer - and derivates such as the [[Logic_Analyzer#OpenBench_Logic_Sniffer|Open Workbench Logic Sniffer]]) work out of the box. It also serves as an example how easy it is to integrate new devices. (GPL)&lt;br /&gt;
&lt;br /&gt;
=== BitHound Logic Analyzer ===&lt;br /&gt;
Hier handelt es sich um eine Erweiterung des SUMP auf einen Spartan-6. &lt;br /&gt;
Es gibt zwei Versionen; eine für das Xilinx-SP601 Development Board und eine für das Digilent ATLYS-Spartan-6 Board. &lt;br /&gt;
&lt;br /&gt;
Der Analyzer-Core ist (noch) derselbe wie beim SUMP, der Speicher ist nun jedoch 128MB gross und die Datenübertragung erfolgt per Ethernet zum ebenfalls leicht verbesserten JAVA-Client. &lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://bastli.ethz.ch/index.php?page=bithound Projektseite von www.bastli.ethz.ch]&lt;br /&gt;
&lt;br /&gt;
=== eebit Logic Analyzer ===&lt;br /&gt;
&lt;br /&gt;
* 32 Kanäle&lt;br /&gt;
* 100 MHz Sampling Rate&lt;br /&gt;
* 65 KSamples Speicher pro Kanal (2 MByte)&lt;br /&gt;
* Ankopplung über ISA-Bus oder Parallelport&lt;br /&gt;
* 2 stufiger Trigger&lt;br /&gt;
* einfaches Windows-Programm zur Steuerung/Darstellung&lt;br /&gt;
&lt;br /&gt;
Der LA basiert auf einem Altera FPGA und speichert die Daten in einem externen SRAM. Die Hardware existiert in 2 Varianten, einmal als PC/104-Karte und mit einem Parallelinterface.&lt;br /&gt;
&lt;br /&gt;
[http://www.freepcb.com/eebit/ Projektseite]&lt;br /&gt;
&lt;br /&gt;
=== miniLA ===&lt;br /&gt;
&lt;br /&gt;
Der LA basiert auf einem Xilinx CPLD&lt;br /&gt;
&lt;br /&gt;
* 32 Kanäle&lt;br /&gt;
* 100 MHz Sampling Rate&lt;br /&gt;
* 128 kSamples Speicher&lt;br /&gt;
* Ankopplung über USB oder LPT (neuere Version von mockup: nur USB)&lt;br /&gt;
* 5V-tolerante Eingänge&lt;br /&gt;
&lt;br /&gt;
neuere Version von mockup: &lt;br /&gt;
* 512 kSamples bzw. 1MSamples&lt;br /&gt;
* Ankoppelung über USB&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [http://minila.sourceforge.net Projektseite]&lt;br /&gt;
* [[MiniLA]] Artikel auf www.mikrocontroller.net&lt;br /&gt;
&lt;br /&gt;
neuere Version von mockup: &lt;br /&gt;
* http://www.mikrocontroller.net/topic/174860 Forum&lt;br /&gt;
* http://www.mikrocontroller.net/articles/Minila_Version_MockUp Artikel&lt;br /&gt;
&lt;br /&gt;
=== coolLA ===&lt;br /&gt;
&lt;br /&gt;
coolLA ist eine erweiterte Version des miniLA. Die wesentlichen Änderungen sind:&lt;br /&gt;
&lt;br /&gt;
* USB Stromversorgung mit galvanischer Trennung&lt;br /&gt;
* Einsatz einer Coolrunner II CPLD&lt;br /&gt;
* Time- und Stateanalyse umschaltbar zusammen im System mit angepasster Software&lt;br /&gt;
* 512k Samples Speicher 32 Bit&lt;br /&gt;
* integrierte Datenempfänger&lt;br /&gt;
* 40-poliger IDE Stecker Intronix LogicPort kompatibel&lt;br /&gt;
* passt in Elpac Gehäuse&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [http://coolla.kleinefreiheit.org Projektseite mit Bilder und Download  -  Achtung: hat gewechselt ]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/175219#1682430 Forumbeitrag] von Wolfgang R. (portside)&lt;br /&gt;
&lt;br /&gt;
=== lekernel&#039;s USB Logic Analyzer ===&lt;br /&gt;
&lt;br /&gt;
* 24 Kanäle&lt;br /&gt;
&lt;br /&gt;
Der LA basiert auf einem Altera Cyclone II FPGA. Alle Schaltpläne, VHDLs, Designer Dateien stehen zur Verfügung. Leider nur ein Quick+Dirty Linux Treiber erstmal da. &lt;br /&gt;
&lt;br /&gt;
[http://lekernel.net/scrapbook/ula.html Projektseite]&lt;br /&gt;
=== OpenBench Logic Sniffer ===&lt;br /&gt;
FPGA basierter LA, HW &amp;amp; SW ist offen (open source). Specs:&lt;br /&gt;
&lt;br /&gt;
* 70MHz+ sample speeds&lt;br /&gt;
** 200Msps captures up to 100MHz waveforms on 16 channels&lt;br /&gt;
** 100Msps captures up to 50MHz waveforms on 32 channels&lt;br /&gt;
* 32 channels&lt;br /&gt;
** 8 channels with 24K sample depth&lt;br /&gt;
** 16 channels with 12K sample depth&lt;br /&gt;
** 32 channels with 6K sample depth&lt;br /&gt;
* 16 buffered, 5volt tolerant channels&lt;br /&gt;
* USB interface, USB powered&lt;br /&gt;
Die Software hat Interpreter für RS232, SPI, I2C, 1-Wire und einen A/D-Wandler.&lt;br /&gt;
&lt;br /&gt;
http://www.gadgetfactory.net/gf/project/butterflylogic/&lt;br /&gt;
&lt;br /&gt;
Software in action:&lt;br /&gt;
* Overview and using RLE : [http://www.screencast.com/t/yWBdfwCYh6]&lt;br /&gt;
* Capturing &amp;amp; Decoding UART data: [http://www.screencast.com/t/MWM5MDUyNGIt]&lt;br /&gt;
&lt;br /&gt;
Eine fertige Platine gibts von www.Seeedstudio.com für 50$ oder in Deutschland bei http://www.watterott.com/de/Open-Logic-Sniffer für 45,22€ exkl. Versand (2.90 € UPS Standard).&lt;br /&gt;
&lt;br /&gt;
=== EjLA - Embedded jTAG Logic Analyzer ===&lt;br /&gt;
Dies ist ein kleiner Logicanalyzer der dafür gedacht ist, den (kommerziellen) Xilinx Chipscope Analyzer zu ersetzen. Daher verbietet die Lizenz auch&lt;br /&gt;
die kommerzielle Nutzung.&lt;br /&gt;
&lt;br /&gt;
Das EjLA Setup verwendet SUMP als Client-GUI am PC. Die Kommunikation erfolgt über jTAG und verwendet das Xilinx &amp;quot;impact&amp;quot; Programm. Daher sollte jedes Kabel, dass von Xilinx dafür unterstützt wird, auch mit EjLA laufen. Außerdem muss man beim Entwickeln nichts umstecken bzw. Pins &amp;quot;vorhalten&amp;quot; (man kann das ISE und Bitstream-download gleichzeitig mit der modifizierten SUMP Software zur Analyse verwenden). Um das zu realisieren, wird eine kleine&lt;br /&gt;
&amp;quot;server&amp;quot; Software verwendet, die zwischen dem SUMP-GUI und dem EjLA Core über&lt;br /&gt;
&amp;quot;impact&amp;quot; kommuniziert. Das ISE Setup wird dabei auch nicht verändert. Damit ist auch sichergestellt, dass SUMP kein wesentlicher Bestandteil von EjLA ist/wird und man stattdessen in Zukunft ggf. auch andere Clients verwenden kann. &lt;br /&gt;
&lt;br /&gt;
Die kleinste Core Version läßt noch mehr als genug Platz sogar auf einem kleinen xc3s100e FPGA und bietet einen LA mit ~500 bis 8000 Samples x 32 Kanäle Speichertiefe nur mit einem BRAM Block(!) Dies wird erreicht durch die Verwendung von einfacher Lauflängen-Kompression.&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [http://www.pin4.at/pro_fpga.php#ejla EjLA Projektseite]&lt;br /&gt;
&lt;br /&gt;
== PIC-basiert ==&lt;br /&gt;
&lt;br /&gt;
=== PIC Logicx ===&lt;br /&gt;
&lt;br /&gt;
[http://martin.hinner.info/electronics/piclogix/ PIC Logix] (GPL, optional Standalone VGA Ausgabe, PIC24 or dsPIC33)&lt;br /&gt;
&lt;br /&gt;
=== Logic Analyzer III (USB) ===&lt;br /&gt;
&lt;br /&gt;
[http://technology.niagarac.on.ca/people/mcsele/LogicAnalyzer.html Logic Analyzer III (USB)] uses a multithreaded Win-32 front-end and a native USB interface. It features a 20MS/s sample rate and a 4K sample depth. Zentrale Bauteile sind ein PIC18F4550 Mikrocontroller und ein AM7204 FIFO IC z.B. von AMD.&lt;br /&gt;
&lt;br /&gt;
=== pk2-la ===&lt;br /&gt;
&lt;br /&gt;
* Hardware: PICkit 2 Programmer Logic Tool Analyzer&lt;br /&gt;
** 3 Kanal bis 500 kHz&lt;br /&gt;
** http://www.microchip.com/pickit2&lt;br /&gt;
* Software: &lt;br /&gt;
** [http://pk2-la.sourceforge.net/ pk2-la]. The Microchip PICkit 2 has the ability to act as a logic analyzer and I/O probe. This project provides a reverse engineered libusb user space device driver and GUI to provide this functionality for users of unsupported operating systems. (Python, Lizenz: GPL)&lt;br /&gt;
&lt;br /&gt;
== Parallelport LA ==&lt;br /&gt;
&lt;br /&gt;
Bitte beachten: Derartige Parallelport LAs sind z.B. bei der Samplerate durch die PC-Hardware und das PC OS im Vergleich zu höherwertigen LAs eingeschränkt.&lt;br /&gt;
&lt;br /&gt;
* [http://tfla-01.berlios.de/ The Fabulous Logic Analyzer] (GPL, multiplatform, Simple [[I2C]] analyzer)&lt;br /&gt;
* [http://akikorhonen.org/projects.php?action=view&amp;amp;id=59 Parallel port logic analyzer]&lt;br /&gt;
* [http://uvasux.googlepages.com/simplelogicanalyser Simple Logic Analyser]&lt;br /&gt;
* [http://www.xs4all.nl/~jwasys/old/diy2.html A logic analyzer using the PC&#039;s parallel port] (Win32)&lt;br /&gt;
* [http://www.codeproject.com/KB/system/17ChannelLogicAnalyzer.aspx 17 Channel Logic Analyzer] By Elmue auf www.codeproject.com (Windows)&lt;br /&gt;
&lt;br /&gt;
== Open Source Software ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;The [http://sigrok.org/wiki/Main_Page sigrok] project aims at creating a portable, cross-platform, Free/Libre/Open-Source logic analyzer software that supports various (usually USB-based) logic analyzer hardware products. The code is licensed under the terms of the GNU GPL.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
sigrok supported hardware:&lt;br /&gt;
* Saleae Logic (supported)&lt;br /&gt;
* EE Electronics XLA/ESLA100 (supported)&lt;br /&gt;
* ASIX SIGMA (partially supported)&lt;br /&gt;
* Openbench Logic Sniffer (work in progress)&lt;br /&gt;
* ZEROPLUS Logic Cube LAP-C (work in progress)&lt;br /&gt;
* CWAV USBee SX (coming up)&lt;br /&gt;
* Braintechnology USB-LPS (planned)&lt;br /&gt;
* Buspirate (planned)&lt;br /&gt;
* Intronix Logicport (planned)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;[http://logicanalyzer.sourceforge.net/ LogicAnalyzer] is a framework as well as an application to operate a PC based logic analyzer. It&#039;s built using the Eclipse RCP and designed with extensibiliy in mind. Integrating devices, providing tools or creating completely new functionality is easy.&lt;br /&gt;
&lt;br /&gt;
LogicAnalyzer main features:&lt;br /&gt;
* Platform independent ( LogicAnalyzer is built using Eclipse RCP, thus it&#039;s mostly platform independent. It also features a native and clean UI.)&lt;br /&gt;
* Rich set of tools ( Several tools come out of the box, such as frequency measurement, distance measurement and serial byte interpreter. LA also comes with an I2C protocol analyzer (SPI, as well as UART analyzer will follow).)&lt;br /&gt;
* SUMP integration ( The most prominent OpenSource logic analyzer hardware (namely the SUMP Logic Analyzer - and derivates such as the Open Workbench Logic Sniffer) work out of the box. It also serves as an example how easy it is to integrate new devices.)&lt;br /&gt;
&lt;br /&gt;
== Sonstiges ==&lt;br /&gt;
&lt;br /&gt;
=== Vergleichstabelle Logic-Analyzer ===&lt;br /&gt;
&lt;br /&gt;
* [http://sigrok.org/wiki/Logic_analyzer_comparison sigrok: Logic Analyzer Comparison] (TODO: evtl. dort sowie hier Einträge synchronisieren)&lt;br /&gt;
&lt;br /&gt;
=== Isolation, galvanische Trennung vom PC ===&lt;br /&gt;
&lt;br /&gt;
Bei günstigen Geräten am USB-Anschluss ist oft die Masseleitung vom Testgerät über den Logic-Analyzer bis hin zum PC durchgeschleift. Das ist nicht immer wünschenswert und man möchte daher keine elektrische Verbindung.&lt;br /&gt;
Prinzipiell gibt es für die galvanische Trennung vom PC zwei Ansätze. Zum einen kann man die Eingänge des Logic Analyzers durch Übertrager derart gestalten, dass keine elektrische Verbindung zum Gerät besteht. Bei teuren Analyzern ist das meist eingebaut, manchmal sogar kanalweise. &lt;br /&gt;
Man kann auch den Logic-Analyzer direkt über einen USB-Isolator vom PC trennen. Dabei muss aber die maximale Übertragungsrate, der verfügbare Strom des USB-Isolators beachtet werden und die notwendige Mindestgeschwindigkeit zum Betrieb des Logic-Analyzers. Bringt das Gerät beispielsweise keinen Pufferspeicher mit, so wird mindestens die Abtastrate herabgesetzt oder der Betrieb am USB-Isolator sogar unmöglich. Hier kann man dann eine Isolation auf Eingangsseite des Logic-Analyzers nachrüsten.&lt;br /&gt;
&lt;br /&gt;
* [http://www.keinschnickschnack.de/?page_id=647 Projektseite: Isolation von 8 Kanälen durch Optokoppler an der Eingangsseite des Logic-Analyzers] (Bis 10MHz auf 8 Kanälen gleichzeitig)&lt;br /&gt;
* [http://www.elv.de/USB-galvanisch-getrennt-USB-Isolator-UI-100/x.aspx/cid_726/detail_30743 ELV: Isolator bis 12MBit Datendurchsatz am USB] (Full-Speed, kein HI-Speed)&lt;br /&gt;
&lt;br /&gt;
=== In Planung: Mikrocontroller.net LA ===&lt;br /&gt;
&lt;br /&gt;
Hier entsteht langsam seit Mitte 2005 ein komplexer (FPGA, CLPD, AVR) Logic Analyser für recht gehobene Ansprüche, allerdings ist die Beteiligung seitdem etwas eingeschlafen:&lt;br /&gt;
&lt;br /&gt;
* [[Logic Analyzer-Projekt: Ideen zur Hardware|Hardware]]&lt;br /&gt;
* [[Logic Analyzer-Projekt: Ideen zur Software|Software]]&lt;br /&gt;
&lt;br /&gt;
=== In Planung: Logikanalysator mit Palm ===&lt;br /&gt;
&lt;br /&gt;
In Planung, Brainstorming: [[Palm-Logicanalyzer]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Oszilloskope und Analyzer]]&lt;br /&gt;
[[Category:Messtechnik]]&lt;br /&gt;
[[Category:Signalverarbeitung]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Pegelwandler&amp;diff=102015</id>
		<title>Pegelwandler</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Pegelwandler&amp;diff=102015"/>
		<updated>2020-06-07T12:05:57Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Weblinks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Pegelwandeln (engl. level shifting) wird oft notwendig, wenn Systeme mit unterschiedlicher Ausgangs- und Eingangsspannungen (z.&amp;amp;nbsp;B. Versorgungs- oder Logikspannungen) miteinander verbunden werden sollen. Das vielleicht bekannteste Beispiel ist die Umsetzung von 0V/5V [[TTL]] Logikpegeln auf die -13V/13V Pegel einer seriellen [[RS232]] Schnittstelle. Die Probleme beim Pegelwandeln können sein:&lt;br /&gt;
&lt;br /&gt;
# Überlastung einer oder beider Seiten, bis hin zur Zerstörung&lt;br /&gt;
# Inkompatible Logikpegel und daraus resultierendes Nichtfunktionieren der Schaltung, oder noch schlimmer, sporadische Fehlfunktionen&lt;br /&gt;
# Verzögerungen der Signale durch die Pegelwandlung und daraus resultierende maximale Signalfrequenzen&lt;br /&gt;
&lt;br /&gt;
=== Überlastung ===&lt;br /&gt;
&lt;br /&gt;
Das Erzeugen von verschiedenen Versorgungsspannungen ist ziemlich einfach, aber man muss sicher gehen, dass man die Signalpegel der Bauteile auf Toleranz überprüft. Wenn z.B. ein 5V Bauteil ein Signal an ein 3V Bauteil schickt, können beide Bauteile beschädigt werden. Vor allem für neue ICs ist es ein Problem mit &amp;quot;hohen&amp;quot; Spannungen wie 5V zu arbeiten. Auf Grund der immer kleineren Schaltkreisstrukturen (aktuelle Prozessoren werden mit 14nm Technologie hergestellt!) werden auch die Abstände und Schichtdicken immer geringer. Das reduziert natürlich auch die Spannungs- und Stromfestigkeit der Transistoren auf dem IC. Neue ICs vertragen deshalb meist nur noch 3.3V, teilweise sogar weniger! Die Überlastung erfolgt durch zu hohe Spannung und dadurch mehr oder weniger langsame Zerstörung des ICs.&lt;br /&gt;
&lt;br /&gt;
=== Schutzdioden ===&lt;br /&gt;
&lt;br /&gt;
Hauptursache Nummer zwei für Überlastung von ICs mit verschiedenen Betriebsspannungen sind die in nahezu allen ICs integrierten Schutzdioden. Deren Aufgabe ist es in Normalfall, elektrostatische Entladungen auf eine sichere, niedrige Spannung zu begrenzen. Die Entladungen geschehen durch unsachgemässe Handhabung und Transport von ICs, z.&amp;amp;nbsp;B. wenn jemand über einen Kunstfaserteppich läuft, sich dabei elektrostatisch auflädt und einen IC anfasst, oder wenn Bauteile in einem Gerät eingebaut sind und der Anwender berührt offen liegende Kontakte (RS232 Eingang, USB-Stick, PCI-Steckkarten beim Einbau etc.). Auch elektrostatische Entladungen / EMV können Ursache zu hoher Pegel auf den Leitungen sein.&lt;br /&gt;
&lt;br /&gt;
Die Schutzdioden beginnen, Strom zu leiten, wenn die Eingangsspannung ca. 300 mV - 600 mV über U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; ansteigt oder entsprechend unter GND absinkt. Im Normalbetrieb sollten die Schutzdioden keinen Strom leiten. Manchmal kann man sie aber zur Spannungsbegrenzung missbrauchen, siehe [[#STEP-DOWN:_5V_-.3E_3.3V | Spannungsherabsetzung mit Vorwiderstand]].&lt;br /&gt;
&lt;br /&gt;
Besonderes Augenmerk ist hierbei auf die optimale Dimensionierung des R zu legen, um sicherzustellen, dass kein zu hoher Strom über die Schutzdioden abgeführt werden muss. Je nach Chip-Type und Ausgang halten diese zwischen 100 µA und 10 mA aus.&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_schutzdioden.png]]&lt;br /&gt;
&lt;br /&gt;
=== 5-V-tolerante Eingänge ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;5-Volt-tolerant&amp;lt;/i&amp;gt; bedeutet, dass 3-Volt-Bausteine ohne Probleme von einem 5-Volt-Baustein angesteuert werden dürfen.&lt;br /&gt;
&lt;br /&gt;
Viele Bauteile mit einer Betriebsspannung von 3 V verfügen über 5-V-tolerante Eingänge. Man sollte aber grundsätzlich im Datenblatt dies nachschauen, bevor die Schaltung aufgebaut wird. Sind sie es nicht, so ist ein &amp;lt;b&amp;gt;Pegelwandler&amp;lt;/b&amp;gt; auf den Verbindungsleitungen zwischen den Bauteilen notwendig. Ein Pegelwandler kann eine einfache Zener-Diode mit einem Widerstand sein, es kann aber auch ein eigens dafür vorgesehener IC sein. Sind die Signalwege bidirektional, so wird man meist die Lösung mit einem eigenen IC bevorzugen.&lt;br /&gt;
&lt;br /&gt;
Bei geringen Geschwindigkeitsanforderungen und erlaubten flachflankigen Signalen (bei Zähl- und Takteingängen ist dazu Schmitt-Trigger-Verhalten erforderlich) genügt ein Serienwiderstand (Richtwert 10 kΩ) in Verbindung mit der Eingangsschutzbeschaltung. Bei allen derartigen „passiven Pegelkonvertern“ muss die Logikschaltschwelle durchfahren werden. Bei heutzutage üblichen treibenden CMOS-Ausgangsstufen ist das kein Problem.&lt;br /&gt;
&lt;br /&gt;
Ob ein Bauteil 5-V-tolerant ist und unter welchen Betriebsbedingungen das gilt, steht im Datenblatt des betreffenden Bauteils vom betreffenden Hersteller. Wenn es auf diese Eigenschaft ankommt, lieber genau bei Lieferanten nachsehen, von welchem Hersteller die Bauteile kommen.&lt;br /&gt;
&lt;br /&gt;
==== Beispiele ====&lt;br /&gt;
&lt;br /&gt;
[[AVR]]s sind generell &#039;&#039;&#039;nicht&#039;&#039;&#039; 5-V-tolerant, wenn sie mit 3,3 V betrieben werden! Die absolute obere Grenze für Eingangsspannungen liegt bei U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; + 0,5 V. Zu finden in den elektrischen Spezifikationen im Datenblatt.&lt;br /&gt;
&lt;br /&gt;
Die GPIO-Pins des Raspberry Pi sind ebenfalls &#039;&#039;&#039;nicht&#039;&#039;&#039; 5-V-tolerant!&lt;br /&gt;
&lt;br /&gt;
Vorsicht bei:&lt;br /&gt;
&lt;br /&gt;
* 74&#039;&#039;&#039;LVX&#039;&#039;&#039;xxxx und 74&#039;&#039;&#039;LCX&#039;&#039;&#039;xxxx (245, 244, 240 ...) an Vcc = 3,3 V.&amp;lt;br&amp;gt;&amp;lt;font color=FF0000&amp;gt;Achtung&amp;lt;/font&amp;gt;: Nicht alle 74LVX sind für 5 V -&amp;gt; 3,3 V geeignet, da jeder Hersteller die ICs anders baut!&lt;br /&gt;
* SN74LVC07AD&lt;br /&gt;
* SN74LV1T04 (auch geeignet zur umgekehrten Konvertierung (3,3V-&amp;gt;5V))&lt;br /&gt;
&lt;br /&gt;
=== Kompatibilität von Logikpegeln ===&lt;br /&gt;
&lt;br /&gt;
Siehe auch http://www.interfacebus.com/Design_Translation.html&lt;br /&gt;
&lt;br /&gt;
Verschiedene Mikroprozessoren haben eigene elektrische Kenndaten für HIGH- und LOW-Pegel, die abhängig von der Versorgungsspannung sind, z.&amp;amp;nbsp;B. der [[R8C]]:&lt;br /&gt;
&lt;br /&gt;
* HIGH größer 0,8 * U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;&lt;br /&gt;
* LOW kleiner 0,2 * U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man muss die Spannungen der Aus- und Eingänge vergleichen. Wenn es um ein Hobbyprojekt geht, kann man einfach messen. Wenn es um eine kommerzielle Anwendung geht, die man verkaufen will, sollte man besser die Spezifikationen der ICs studieren.&lt;br /&gt;
&lt;br /&gt;
== UNIDIREKTIONAL ==&lt;br /&gt;
&lt;br /&gt;
=== 1,8 V ⇒ 5 V ===&lt;br /&gt;
&lt;br /&gt;
* Die besondere Eigenschaft der alten TTL-Schaltkreise, nämlich dass Strom bei LOW &#039;&#039;&#039;aus&#039;&#039;&#039; dem Eingang in den treibenden Ausgang fließt kann man sich zunutze machen, wie die nachfolgende Schaltung zeigt. In dieser wird der HIGH-Pegel des 1,8-V-Signals durch eine Schottkydiode um ca. 0,3 V auf 2,1 V erhöht. Damit ist man fast offiziell im HIGH-Bereich für TTL (Schaltschwelle 1,4 V, HIGH &amp;gt; 2,0 V). Der LOW-Pegel wird auf ca. 0,3 V erhöht, was voll den TTL-Richtlinien entspricht. Als Schaltkreisfamilie &#039;&#039;&#039;muss&#039;&#039;&#039; ein [[74xx|TTL-Typ]] eingesetzt werden, also LS, F, AS oder ähnlich. CMOS-Typen wie HC, LVC etc. funktionieren &#039;&#039;&#039;nicht&#039;&#039;&#039;!&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_LS.png]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=FF0000&amp;gt;Achtung&amp;lt;/font&amp;gt;: Diese Schaltung entspricht bei HIGH ungefähr einem offenen TTL-Eingang, was zwar meistens funktioniert, aber etwas störempfindlich sein kann. Davon wurde in der TTL-Ära stets abgeraten. Zudem ist der Pegelwechsel LOW nach HIGH durch den niedrigen Strom eher langsam. Man kann das jedoch mit einem Pullup-Widerstand absichern. Dann sind auch Gatter der 74HCT-Reihe einsetzbar.&lt;br /&gt;
&lt;br /&gt;
=== 3,3 V ⇒ 5 V ===&lt;br /&gt;
&lt;br /&gt;
Diese Konversion ist mit Abstand die häufigste. Dabei kann man getrost 3,3 V (früher) und 3 V (moderner) gleich setzen.&lt;br /&gt;
&lt;br /&gt;
* 3,3-V-Pegel werden bei TTL-kompatiblen Eingängen richtig erkannt (Schaltschwelle 1,4 V). Es ist kein Pegelwandler erforderlich. Direkte Verbindung. Einer der großen Vorteile klassischer TTL-Technik!&lt;br /&gt;
&lt;br /&gt;
* 5-V-CMOS Eingänge haben typisch eine minimale Eingangsspannug für HIGH (&amp;lt;math&amp;gt;V_{IH}&amp;lt;/math&amp;gt;) von 0.6 * U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; = 0.6 * 5 V = 3 V. Das kann ein 3,3-V-CMOS-Ausgang direkt treiben, allerdings kann sich das Zeitverhalten dadurch etwas ändern, weil der HIGH Pegel später erkannt wird. Vorsicht! Viele 5-V-CMOS-ICs wollen für HIGH offiziell mindestens 0,7 * U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; = 3,5 V oder manche auch 0,8 * U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; = 4,0V! Das geht dann offiziell nicht mehr mit einem 3,3-V-Ausgang! Für Hobbyzwecke kann man das aber ggf. probieren.&lt;br /&gt;
Zu beachten ist, dass der nicht ganz nach High durchgesteuerte Eingang Querstrom von der Speisespannung ziehen kann. Das kann für batteriebetriebene Geräte oder USB-konformes Standby durchaus ausschlaggebend sein.&lt;br /&gt;
&lt;br /&gt;
* 3,3-V-[[Ausgangsstufen_Logik-ICs | Open Collector]] nach 5 V (TTL oder CMOS): Einfach einen Pull-Up Widerstand hinzufügen und gut. Allerdings verbraucht der Pull-Up-Widerstand bei LOW auf jeden Fall Strom und begrenzt bei HIGH den maximalen Gate-Umladestrom. Die Schaltgeschwindigkeit von LOW nach HIGH wird durch die Größe des Pull-Ups bestimmt. Je nach Geschwindigkeitsanforderungen kann der in Mikrocontrollern meistens zuschaltbare innere Widerstand dazu benutzt werden. Zudem kann dieser, bei bekannt LOW bleibendem Eingangspegel, zur Reduktion der Stromaufnahme abgeschaltet werden. Bipolare TTL-Schaltkreise benötigen in der Bastelschaltung keinen Pull-Up (liefern Strom); bei Schaltungen mit erhöhter Zuverlässigkeit ist dennoch ein externer Pull-Up angeraten (Richtwert 4,7 kΩ).&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_oc_3-5.png]]&lt;br /&gt;
&lt;br /&gt;
* 3,3 V auf echte 5 V (CMOS) geht am einfachsten mit einem Baustein der HCT-Familie (NICHT HC!). Diese haben TTL-kompatible Eingänge und echte CMOS-Ausgänge&lt;br /&gt;
&lt;br /&gt;
* Man kann einen Komparator &amp;lt;small&amp;gt;in nichtinvertierender Schaltung&amp;lt;/small&amp;gt; benutzen (LM339/393). Allerdings ist diese Lösung relativ langsam, abhängig vom verwendeten Komparator. Komparatoren bieten eine freie Wahl des Eingangsspannungsbereichs und sind deshalb eine gute Wahl bei &#039;&#039;variabler&#039;&#039; Speisespannung der Treiberseite.&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_comp_3-5.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/part/74HCT245 74HCT245], [http://www.mikrocontroller.net/part/74HCT244 74HCT244]oder [http://www.mikrocontroller.net/part/74HCT240 74HCT240] (Das T ist wichtig. HCs können funktionieren, ist aber eigtl. ungeeignet, da bei 5V Versorgung und höheren Temperaturen V(input,high)=3,2V)&lt;br /&gt;
* [http://www.mikrocontroller.net/part/74HCT125 74HCT125]: OE Pins auf Masse und dann das Signal einfach anschließen. &lt;br /&gt;
* SN74LVC07AD &lt;br /&gt;
* SN74LV1T04 (auch geeignet zur umgekehrten Konvertierung (5V-&amp;gt;3,3V))&lt;br /&gt;
* 74V1T126 (single Gatter, V(input,high)=2V)&lt;br /&gt;
&lt;br /&gt;
=== 5 V ⇒ 9..15(..30) V ===&lt;br /&gt;
&lt;br /&gt;
* Am einfachsten geht das mit einem (geeigneten!) Open-Collector-Ausgang, einfach einen Pull-Up hinzufügen (an die hohe Spannung) und fertig. Ein 74&#039;&#039;xx&#039;&#039;03 geht hier nicht! Auch kann man nicht einen Push-Pull-Ausgang eines Mikrocontrollers dafür verwenden, indem man den Ausgang bei HIGH zum Eingang macht.&lt;br /&gt;
Hintergrund sind parasitäre Dioden zwischen Ausgang und Speisespannung.&lt;br /&gt;
Alle (geeigneten) Treiberausgänge haben eine maximal erlaubte Kollektorspannung, die zu beachten ist. Mehr Freiheit hat man bei der Verwendung von Einzeltransistoren, wobei eine gewisse Lücke von 30 V bis 200 V von Bipolartransistoren dominiert wird; für kleinere oder größere Spannungen gibt es preiswerte MOSFETs. (Die Lücke entsteht durch den geringen Bedarf des Weltmarktes an diesen Kollektorspannungen.)&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_oc_5-12.png]]&lt;br /&gt;
&lt;br /&gt;
* Man kann einen Komparator benutzen. Allerdings ist diese Lösung relativ langsam, abhängig vom verwendeten Komparator. Wenn nur zwei Signale gewandelt werden müssen bietet sich der LM393 an, ein Doppelkomparator mit Open-Collector-Ausgang, mit dem man auf einen beliebigen Pegel ausgeben kann. Der LM339 (man beachte den unauffälligen Zahlendreher) ist ein Vierfachkomparator mit den gleichen Eigenschaften. Wenn wenig Platz vorhanden ist, dann ist der TL311 im winzigen SOT-23 Gehäuse sehr empfehlenswert. Bei jedem Komparator kann auch einfach eine Invertierung gemacht werden, einfach die Eingänge + und - vertauschen. Diese Komparatoren eignen sich bis ca. 1 MHz.&lt;br /&gt;
&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/praxis/bausatz_pegelwandler-mit-transistoren.htm Pegelwandler mit Transistor, invertierend]&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_trans_inv.png]]&lt;br /&gt;
&lt;br /&gt;
* Pegelwandler mit Transistor, nicht invertierend&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_trans_ni.png]]&lt;br /&gt;
&lt;br /&gt;
Die Idee ist einfach. Wenn der Ausgang des 5-V-Gatters auf HIGH ist dann ist der Transistor ausgeschaltet, der Pull-Up-Widerstand R7 zieht den Ausgang auf + 12 V. Ist der Ausgang des 5-V-Gatters auf LOW ist, dann ist er vollkommen durchgesteuert und der Ausgang nahe 0 V (je nach Typ ca. 300 mV). Der Vorteil ist hier erhöhte Störsicherheit im Gegensatz zur einfachen Ansteuerung der Basis über einen Vorwiderstand. Außerdem wird dadurch nicht die Logik invertiert. Nachteilig ist der geringe Strom, der bei HIGH zur Verfügung steht (typisch 100 µA). Diese Schaltung ist die seltene Anwendung einer Basisschaltung für digitale Signale. Der Vorteil der Basisschaltung ist die höhere Grenzfrequenz durch die herabgesetzte Wirksamkeit der (störenden) Miller-Kapazität.&lt;br /&gt;
&lt;br /&gt;
* Wenn mehr Geschwindigkeit, Ausgangsstrom und weniger Stromverbrauch nötig ist, dann muss ein spezieller Baustein her, wie z.&amp;amp;nbsp;B.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
** [[Mosfet-Übersicht#Mosfet-Treiber|MOSFET-Treiber]] z.B. ICL7667&lt;br /&gt;
** [[H-Brücken Übersicht | Motortreiber]] ICs: (z.&amp;amp;nbsp;B. L293, L298, UCC27325 und deren Verwandte), wenns nicht zu schnell ist (einige Dutzend kHz)&lt;br /&gt;
** CD40109, 4fach Pegelwandler, bei Reichelt verfügbar&lt;br /&gt;
** HEF4104, 4fach Pegelwandler mit normalen und invertierten Ausgängen sowie Tristate. Um ggf. sicherzustellen, dass wie im Datenblatt beschrieben immer U&amp;lt;sub&amp;gt;DDI&amp;lt;/sub&amp;gt; &amp;lt;= U&amp;lt;sub&amp;gt;DDO&amp;lt;/sub&amp;gt; ist, kann man einfach eine Diode von U&amp;lt;sub&amp;gt;DDO&amp;lt;/sub&amp;gt; nach U&amp;lt;sub&amp;gt;DDI&amp;lt;/sub&amp;gt; schalten (z.&amp;amp;nbsp;B. Schottky SB120, aber auch 1N4148 &amp;amp; Co. sollte problemlos funktionieren)&lt;br /&gt;
** CD4504, 6fach Pegelwandler 3-20V, Eingangspegel TTL oder CMOS (umschaltbar) =&amp;gt; CMOS, keine Reihenfolge von U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;/U&amp;lt;sub&amp;gt;EE&amp;lt;/sub&amp;gt; erforderlich (Bezugsquelle: CSD)&lt;br /&gt;
** MAX232, der braucht nur 5 V Versorgungsspannung. Allerdings ist der Ausgangswiderstand relativ hoch (ca. 300 Ω) und man kann nur ca. 5 mA Ausgangstrom liefern. Die Ausgangsspannung beträgt maximal 10 V.&lt;br /&gt;
&lt;br /&gt;
=== 5 V ⇒ 3,3 V ===&lt;br /&gt;
&lt;br /&gt;
Ob 3,3 V (klassisch) oder 3 V (modern) ist bei dieser Betrachtung nahezu egal.&lt;br /&gt;
&lt;br /&gt;
* Zuerst sollte man prüfen, ob die Eingänge 5V-tolerant sind. Dann kann man die ICs direkt verbinden. Sehr schnell und billig!&lt;br /&gt;
&lt;br /&gt;
* Wenn die Eingänge nicht 5-V-tolerant sind und es trotzdem schnell sein soll, muss ein Gatter aus der LVC- oder AHC-Familie dazwischen geschaltet werden, also eines mit 5V-Toleranz. Bei 3 V Betriebsspannung kann man problemlos 5 V an den Eingang anlegen. Der Baustein 74HC4050 erlaubt per Definition eine Pegelwandlung bis etwa 15 V (siehe Datenblatt). Beide Anordungen haben auch eine sehr niedrige Ruhestromaufnahme.&lt;br /&gt;
&lt;br /&gt;
: &#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:* 74LVC245A (&#039;A&#039; ist wichtig, I/Os 5V-tolerant)&lt;br /&gt;
:* 74LVC245DW &lt;br /&gt;
:* 74LVT245 &lt;br /&gt;
:* 74LVXxxx (245, 244, 240 ...) an Vcc=3,3V. Achtung: Nicht alle 74LVX sind für 5V -&amp;gt; 3,3V geeignet, da jeder Hersteller die ICs anders baut!&lt;br /&gt;
:** 74LVX04 &lt;br /&gt;
:** 74LVX244 (Fairchild)&lt;br /&gt;
:** 74LVX245 (nicht von Reichelt, nicht 5V tolerant)&lt;br /&gt;
:** bei TI heissen die 74LVX... nur 74LV...&lt;br /&gt;
&lt;br /&gt;
:* 74HC4050 (bis 15 V Step-Down-Pegelwandlung laut Datenblatt, bei Reichelt in DIP und SO erhältlich)&lt;br /&gt;
:* MAX3373/MAX3375&lt;br /&gt;
:* NC7SZ08 oder andere aus derselben Serie. CMOS-Logik mit 5-V-toleranten Eingängen, recht flott und braucht dank SOT-23 auch wenig Platz auf der Platine&lt;br /&gt;
&lt;br /&gt;
* 5 V Open Collector auf 3,3-V-Eingang. Einfach einen Pull-Up hinzufügen (Pull-Up liegt auf 3,3 V). Nachteilig ist der relativ hohe Stromverbrauch bei LOW, die begrenzte Geschwindigkeit bei hochohmigen Pull-Ups und der relativ geringe Ausgangsstrom bei HIGH (abhängig vom Pull-Up).&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_oc_5-3.png]]&lt;br /&gt;
&lt;br /&gt;
* Spannungsteiler mit 680 Ω und 1 kΩ. Der Nachteil dieser Lösung ist der relativ hohe Stromverbrauch (~3mA), der relativ geringe Ausgangsstrom (mehr als 200..300 µA sollte man da nicht rausziehen) und die relativ geringe Geschwindigkeit (ca. 10 MHz).&lt;br /&gt;
&lt;br /&gt;
[[Datei:SPI level shifter with resistor divider.png|miniatur|rechts|fehlerhafter SPI-Takt nach Pegelwandler mit Widerstandsteiler (1,8/3,3 kΩ)&amp;lt;br /&amp;gt;unten: 5V-Ausgang am Mikrocontroller&amp;lt;br /&amp;gt;&lt;br /&gt;
oben: 3,3V-Eingang an der SD-Karte nach Pegelwandler]]&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_st_5-3.png]]&lt;br /&gt;
&lt;br /&gt;
* 1 kΩ Vorwiderstand. Dadurch wird der Strom vom 5-V-Ausgang in die 3,3-V-Versorgung durch die internen Schutzdioden auf ca. 1 mA begrenzt. Diese Lösung ist auch relativ langsam (ca. 5MHz). Ggf. kann man den Vorwiderstand auf 100 Ω reduzieren, das erhöht dann wieder die Geschwindigkeit. Aufpassen, einige ICs vertragen nur 1 mA oder weniger durch die Schutzdioden! Ausserdem muss man aufpassen, da jetzt von der 5-V-Seite Strom in die 3,3-V-Versorgung eingespeist wird. Besonders in Schaltungen mit sehr niedriger Stromaufnahme kann das zum Problem werden, wenn die Stromaufnahme geringer ist, als über die Vorwiderstände eingespeist wird. Dann nimmt es meist der Spannungsregler für 3,3 V übel wenn jemand „schiebt“, sprich, Strom einspeist. Denn die allermeisten Spannungsregler können nur Strom liefern (source), aber keinen Strom aufnehmen (sink). Es gibt 4-fach-Diodennetzwerke, die die internen Schutzdioden entlasten können (Schottkydioden mit kleinerer Flusspannung von ≈ 0,3 V als die internen Silizizumdioden mit ≈ 0,7 V), außerdem ist teilweise noch eine [[Diode#Z-Diode|Zenerdiode]] enthalten, die ggf. den überschüssig eingespeisten Strom aufnehmen kann.&lt;br /&gt;
&lt;br /&gt;
Alle Lösungen mit Vorwiderständen reduzieren die Flankensteilheit der Signale. Dies kann bei Takt- und Zähleingängen zu unerwünschten Schwingungen und damit Fehlzählungen führen. Derartig benutzte Eingänge sollten Schmitt-Trigger-Verhalten aufweisen.&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_vw_5-3.png]]&lt;br /&gt;
&lt;br /&gt;
Achtung: Mindestens für 74HC(T) Gatter ist dokumentiert (Philips 74HC/T High-Speed CMOS User Guide), dass auch schon geringer Strom durch die internen Schutzdioden zu einer unerwünschten Kopplung von Eingängen führen kann, d.h. der Strom fliesst zu einem anderen Eingang wieder hinaus. Sind also andere Eingänge ebenso hochohmig angeschlossen, kann dieser Querstrom zu Fehlfunktion führen.&lt;br /&gt;
&lt;br /&gt;
== BIDIREKTIONAL ==&lt;br /&gt;
&lt;br /&gt;
Für bidirektionale Busse gibt es spezielle Pegelwandler mit 2 Versorgungsspannungen. Allerdings brauchen die meist ein Signal zur Richtungsumschaltung. Auch muss man die Reihenfolge der Versorgungsspannungen beim Einschalten beachten. Aktive bidirektionale Pegelwandler OHNE Steuereingang zur Richtungsumschaltung sind mit Vorsicht zu genießen, denn die brauchen teilweise kurzzeitig einen relativ hohen Strom, um die Eingänge zu treiben.&lt;br /&gt;
&lt;br /&gt;
=== 5 V ⇔ 3,3 V ===&lt;br /&gt;
&lt;br /&gt;
* Wenn die 5-V-Seite TTL-kompatible Eingänge hat kann wieder der Spannungsteiler oder Vorwiderstand wie bei der unidirektionalen Anpassung verwendet werden (mit all seinen Vor- und Nachteilen).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* SN74CB3T3306&lt;br /&gt;
* SN74CBTD3861 (10 Bit,flow through, Betrieb mit 5 Volt)&lt;br /&gt;
* MAX1741 &lt;br /&gt;
* MAX3378E &lt;br /&gt;
* 74AHC126 s.u.&lt;br /&gt;
* ST2378 (bei CSD erhältlich, 3.5 eur, leider TSSOP)&lt;br /&gt;
* TXS0104E (TI: 4-BIT BIDIRECTIONAL VOLTAGE-LEVEL TRANSLATOR FOR OPEN-DRAIN AND PUSH-PULL APPLICATIONS)&lt;br /&gt;
* SN74LVC07A&lt;br /&gt;
* von Analog Devices die ADUM Serie&lt;br /&gt;
&lt;br /&gt;
=== 1,65 V ... 5,5 V ⇔ 1,65 V ... 5,5 V ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
* SN74LVC1T45&lt;br /&gt;
* SN74LVC2T45&lt;br /&gt;
* SN74LVC(H)8T245&lt;br /&gt;
* SN74LVC(H)16T245&lt;br /&gt;
&lt;br /&gt;
=== 1,2 V ... 3,6 V ⇔ 1,65V ... 5,5V ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
* TXB0101&lt;br /&gt;
* TXB0102&lt;br /&gt;
* TXB0104&lt;br /&gt;
* TXB0106&lt;br /&gt;
* TXB0108&lt;br /&gt;
&lt;br /&gt;
=== 1,2 V ... 3,6V ⇔ 1,2 V ... 3,6 V ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
* SN74AVC(H)1T45&lt;br /&gt;
* SN74AVC(H)2T45&lt;br /&gt;
* SN74AVC(H)4T245&lt;br /&gt;
* SN74AVC(H)8T245&lt;br /&gt;
* SN74AVC(H)16T245&lt;br /&gt;
* SN74AVC(H)20T245&lt;br /&gt;
* SN74AVC(H)24T245&lt;br /&gt;
* SN74AVC(H)32T245&lt;br /&gt;
&lt;br /&gt;
=== 1,5 V ... 3,6 V ⇔ 1,5 V ... 5,5 V ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
* 74LVC4245A&lt;br /&gt;
&lt;br /&gt;
== Mit galvanischer Trennung ==&lt;br /&gt;
&lt;br /&gt;
* [[Optokoppler]] (Langsam! Es gibt verschieden schnelle Koppler, aber über 1 MHz kommen sie kaum hinaus. Grundregel: Solche mit Fototransistoren sind am langsamsten, Richtwert 10 kHz, Fotodioden sind schneller, schnelle Optokoppler haben eine gesondert zu speisende Empfängerschaltung.)&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_opto.png]]&lt;br /&gt;
&lt;br /&gt;
* GMR-Koppler von der Firma NVE &lt;br /&gt;
* iCoupler Technologie von der Firma Analog Devices&lt;br /&gt;
* [[Kapazitiver Koppler]] (schnell, begrenzter Potenzialversatz)&lt;br /&gt;
* Transformatorkopplung (nur für gleichspannungsfreie Wechselsignale geeignet; sehr schnell; Beispiel: Netzkarten)&lt;br /&gt;
&lt;br /&gt;
Lit.: &#039;&#039;Galvanische Trennung: Optokoppler, GMR-Koppler oder iCoupler?&#039;&#039;, Siegfried W. Best, Redaktion elektronik industrie, [http://www.elektronik-industrie.de/ei/11,2003/article/2f0082f824c.html elektronik industrie 11-2003, S. 22ff.]&lt;br /&gt;
&lt;br /&gt;
== Praktische Beispiele ==&lt;br /&gt;
&lt;br /&gt;
=== Einfaches RS232-Interface ===&lt;br /&gt;
&lt;br /&gt;
[http://web.archive.org/web/20050122013618/http://www.henrik-reimers.de/control/rs232interface.gif Erfolgreicher Einsatz bis 19200 Baud und bis zu 10 m Leitungslänge]&lt;br /&gt;
Beschränkungen:&lt;br /&gt;
&lt;br /&gt;
* ggf. Platzbedarf&lt;br /&gt;
* Geschwindigkeit s.o.&lt;br /&gt;
&lt;br /&gt;
Beispiel: http://www.hagtech.com/pdf/translator.pdf&lt;br /&gt;
&lt;br /&gt;
=== [[I2C]]-Bus: gemeinsam 3.3V und 5V ===&lt;br /&gt;
&lt;br /&gt;
* [[MSP430]] an 3,3V/5V: http://www-s.ti.com/sc/psheets/slaa148/slaa148.pdf&lt;br /&gt;
&lt;br /&gt;
* Philips [http://www.nxp.com/documents/data_sheet/PCA9515.pdf PCA9515]: I2C Puffer mit Pegelwandlung. Der PCA9515 ist ein I2C-Bus Repeater, welcher I2C Busse mit verschiedenen Spannungen isoliert. Verfügbar bei Reichelt und DigiKey.&lt;br /&gt;
&lt;br /&gt;
* [http://ics.nxp.com/support/documents/interface/pdf/an97055.pdf Philips AN97055 Bi-directional level shifter for I²C-bus and other systems]&lt;br /&gt;
&lt;br /&gt;
* Bevor man ein Philips I2C Chip auswählt sollte man prüfen ob er verfügbar ist und auch das verfügbare Gehäuse wählen. Man sollte auch überlegen ob ein Puffer wirklich gebraucht wird. Wenn man echte I2C ICs mit 5V betreibt, dann sind die Eingänge vom Typ Schmitt Trigger CMOS (z.&amp;amp;nbsp;B. PCF8574). Dann müssen 3.3V Pegel auf 5V umgesetzt werden. Wenn man jedoch SMBUS Ics verwendet (z.&amp;amp;nbsp;B. ADT7461, Silabs 8051) dann sind die Schwellspannungen TTL kompatibel und es ist keine Anpassung notwendig. Für neue Pegelwandler sollte man hier nachschauen. http://www.bus-buffer.com&lt;br /&gt;
&lt;br /&gt;
* [http://www.edn.com/article/CA193193.html &amp;quot;Two-transistor circuit replaces IC&amp;quot;]. Für diese Anwendung kann ENABLE direkt mit 3.3V verbunden werden. Es ist eigentlich nur dazu da, den ICs &amp;quot;hot-swappable&amp;quot; zu machen (kann unter Spannung gesteckt und getrennt werden). Es geht sogar mit nur einem [[Transistor]] [http://www.mikrocontroller.net/topic/92447 siehe Forum]. Man sollte beachten, daß die Schaltung sowohl für SCL als auch SDA benötigt wird. &lt;br /&gt;
* Noch einfachere Lösungen mit nur einem MOSFET und zwei Pull-Up Widerständen pro Leitung sind in den folgenden Links zu finden. &lt;br /&gt;
** http://www.nxp.com/documents/application_note/AN10441.pdf&lt;br /&gt;
** http://www.semiconductors.philips.com/acrobat_download/literature/9398/39340011.pdf (Kapitel 18), bei der Berechnung der erreichbaren Geschwindigkeit dürfen die parasitären Kapazitäten der FETs nicht ignoriert werden&lt;br /&gt;
&lt;br /&gt;
=== Auswählbare Pegel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frage:&#039;&#039;&#039;&lt;br /&gt;
Ein CMOS Logikpegel zwischen 1,8V, 2,5V und 3,3V (abhängig von der Anwendung) muss auf 5V CMOS Logikpegel gewandelt werden. Es geht nur um diese Richtung mit maximal 8MHz. Es gibt die Stromversorgung für alle Pegel. Ein normaler Komparator wie LM311 ist nicht möglich, da er beim Betrieb mit 5V Versorgunsspannung erst ab 1V zu schalten anfängt. Meine Idee ist die Verwendung eines High Speed OPVs mit R2R Eingang, z.&amp;amp;nbsp;B. LMH6645.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;Antworten:&#039;&#039;&#039;&lt;br /&gt;
* Man könnte einen ultra-low threshold N-Kanal MOSFET nehmen und als Open Drain mit einem Pull-Up nach 5V betreiben, BSH103 könnte passen (Schwellspannung ~0,4V).&lt;br /&gt;
* High-Speed Single Supply Komparator wie z.&amp;amp;nbsp;B. [http://www-s.ti.com/sc/ds/tl712.pdf TL712] .&lt;br /&gt;
* SN74LVC1T45&lt;br /&gt;
* SN74LVC8T245&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frage:&#039;&#039;&#039;&lt;br /&gt;
Ich suchen einen IC, welcher eine Pegelwandlung von 3,3V nach 1,8V, 2,0V oder 5V ermöglicht und während des Betriebs umgeschaltet werden kann.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Antworten:&#039;&#039;&#039;&lt;br /&gt;
* So ein IC ist der Linear [http://www.linear.com/pc/productDetail.jsp?navId=H0,C1,C1007,C1071,P1601 LTC1555L-1.8] .&lt;br /&gt;
* SN74LVC1T45&lt;br /&gt;
* SN74LVC8T245&lt;br /&gt;
&lt;br /&gt;
=== AVR SPI (SDC/MMC)===&lt;br /&gt;
&lt;br /&gt;
Für &#039;&#039;&#039;bidirektionalen Betrieb&#039;&#039;&#039; zwischen 5V-AVR und 3,3V-Geräten und anders herum gibt es den Level-Translator &#039;&#039;&#039;MAX3378E&#039;&#039;&#039; von Maxim.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenrichtung am SPI im Zielsystem festgelegt ist, reichen &#039;&#039;&#039;unidirektionale Bausteine&#039;&#039;&#039;:&lt;br /&gt;
* 3x von 5V nach 3,3V und 1x von 3,3V nach 5V: &#039;&#039;&#039;MAX3392E&#039;&#039;&#039;&lt;br /&gt;
* 1x von 5V nach 3,3V und 3x von 3,3V nach 5V: &#039;&#039;&#039;MAX3390E&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Zum Anschließen einer SDC/MMC an einen 5V-AVR eignen sich somit der MAX3978E und der MAX3392E. Beide sind u.A. im winzigen TSSOP-14-Gehäuse verfügbar, nehmen sehr wenig Energie auf und eignen sich auch für andere Spannungen. Mit 3,3 und 5V beträgt die garantierte Übertragungsrate 8Mbps.&lt;br /&gt;
&lt;br /&gt;
* [http://datasheets.maxim-ic.com/en/ds/MAX3372E-MAX3393E.pdf Datenblatt]&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit zum Übersetzen zwischen 3,3 und 5V liegt in der Verwendung des &#039;&#039;&#039;74LVC245&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bastlerfreundlicher als &#039;&#039;&#039;MAX33XXX&#039;&#039;&#039; (in SO-Gehäuse):&lt;br /&gt;
* 5V &amp;gt; 3,3V (SCK, MOSI, CS): 74LVC-serie (z.B. 74LVC14A)&lt;br /&gt;
* 3,3V &amp;gt; 5V (MISO): 74HCT-Serie (z.B. 74HCT125, 74HCT251)&lt;br /&gt;
&lt;br /&gt;
5V-AVR an eine MMC (ohne Level-Shifter-Baustein):&lt;br /&gt;
* [http://www.microsyl.com/index.php/2010/03/24/led-sign-with-mmc-memory-card/ Projektseite] &lt;br /&gt;
* [http://www.microsyl.com/projects/ledsign/ledsign1.pdf Schaltplan]&lt;br /&gt;
&lt;br /&gt;
=== Mikrocontroller ⇔ Parallelport ([[ISP]]-Dongle, [[JTAG]] Wiggler, ...) ===&lt;br /&gt;
&lt;br /&gt;
Dieser Schaltplan funktioniert auch bei 3,3 V wenn man einen 74&amp;lt;B&amp;gt;HC&amp;lt;/B&amp;gt;244 anstatt eines 74&amp;lt;B&amp;gt;LS&amp;lt;/B&amp;gt;244 verwendet: [http://www.epanorama.net/circuits/parallel_output.html Parallel port interfacing made easy: Simple circuits and programs to show how to use PC parallel port output capabilities].&lt;br /&gt;
&lt;br /&gt;
=== Doppeltes Leitungspaar RX/TX 5V/3,3V ===&lt;br /&gt;
&lt;br /&gt;
Der [http://www.hackaday.com/2008/06/19/sparkfuns-logic-level-converter/ SparkFun&#039;s Logic Level Converter] ist eine Baugruppe mit MOSFETs [http://www.fairchildsemi.com/pf/BS/BSS138.html BSS138] für die Pegelwandlung von 5V auf 3,3V. 5V/2,8V und 5V/1,8V sind ebenfalls machbar.&lt;br /&gt;
&lt;br /&gt;
=== Steuerleitung zwischen Mikrocontroller und FPGA ===&lt;br /&gt;
&lt;br /&gt;
Oftmals werden PLDs oder FPGAs per Microcontroller-Platine angesteuert. Ältere Typen laufen meist als 5V oder sitzen in 5V-kompatiblen Platinen. Sollen moderne FPGAs angesteuert werden, trifft man fast immer auf 3,3-V-Typen, bzw. muss sogar 2,5-V- / 1,8-V-Bänke beschalten, wenn nur noch dort Pins frei sind.&lt;br /&gt;
&lt;br /&gt;
==== Mikrocontroller ⇒ FPGA ====&lt;br /&gt;
&lt;br /&gt;
Die 5 V sind also im Extremfall auf 1,8 V herabzusetzen, was bei einem maximal zulässigen Diodenstrom von 3 mA (Beispiel Xilinx) einen Mindestwiderstand von ca. 1 kΩ erfordert. Die resultierende maximale Schaltfrequenz liegt dann bei einem typischen FPGA-Eingang bei etwa &amp;lt; 500 kHz. Soll der Eingang aus Belastungsgründen nicht mit mehr als 0,3 mA belastet werden, müsste der Widerstand auf 10 kΩ steigen, wodurch die Frequenz auf 1/10 sinkt. Zudem ist der Eingang dann störempfindlicher. Daher ist es besser, man schaltet dem Eingang eine zusätzliche Z-Diode bei und dimensioniert den Vorwiderstand so, dass die Strombelastbarkeit des Mikrocontrollers ausgelastet wird.&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit ist es, die Spannung mit einer Diode in Vorwärtsrichtung herabzusetzen. Dann muss jedoch der Vorwiderstand noch exakter toleriert werden und auch Abweichungen der Spannung (Welligkeit) berücksichtigt werden.&lt;br /&gt;
&lt;br /&gt;
==== FPGA ⇒ Mikrocontroller ====&lt;br /&gt;
&lt;br /&gt;
Umgekehrt ist es oft nötig, dass Bausteine einen fremden Chip treiben müssen, dessen Eingang bereits mit einem Pull-Up versehen ist. Über diesen wird dann stets ein Strom in die Schutzdiode eingeprägt, auch wenn der Ausgang auf HIGH geht. Soll z. B. von einem PLD oder einem FPGA aus eine Mikrocontrollerplatine bedient werden, die über einen Pull-Up von 1 kΩ verfügt, würden immer ca. 1 mA in die Schutzdiode eingeprägt. Hier kann eine Seriendiode helfen, die Spannung genügend herabzusetzen, um den Ausgang zu schützen und dennoch die Funktion zu erhalten. Dann steuert ein LOW-Ausgang den Eingang auf geschätzte 1V, was aber meistens für das Erkennen von LOW noch sicher reicht.&lt;br /&gt;
&lt;br /&gt;
== Bauteile ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;74ALVC164245&#039;&#039;&#039; - &#039;&#039;16bit dual supply translating transceiver&#039;&#039;. Eine Seite von 1.5V bis 3.6V, die andere von 1.5 bis 5.5V.&lt;br /&gt;
* &#039;&#039;&#039;74LVX573&#039;&#039;&#039; (unidirektional, Latch, nicht alle Hersteller bauen diesen 5V tolerant!)&lt;br /&gt;
* &#039;&#039;&#039;74LVX245&#039;&#039;&#039; (bidirektional, nicht alle Hersteller bauen diesen 5V tolerant!)&lt;br /&gt;
* &#039;&#039;&#039;74LVX125&#039;&#039;&#039; - &#039;&#039;Low Voltage Quad Buffer with 3-STATE Outputs&#039;&#039;. http://www.fairchildsemi.com/pf/74/74LVX125.html&lt;br /&gt;
* &#039;&#039;&#039;SN74LVC2T45&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;SN74LVC8T245&#039;&#039;&#039; - &#039;&#039;8-Bit Dual-Supply Bus Transceiver with Configurable Voltage Translation and Three-State Outputs&#039;&#039;. http://focus.ti.com/docs/prod/folders/print/sn74lvc8t245.html&lt;br /&gt;
* &#039;&#039;&#039;74LCX244MSA&#039;&#039;&#039; von Fairchild.&lt;br /&gt;
* &#039;&#039;&#039;MAX3377&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAX3000&#039;&#039;&#039; 8-Kanal bidirektioneler Pegelwandler ohne Richtungsumschaltung&lt;br /&gt;
* &#039;&#039;&#039;ADG3308&#039;&#039;&#039; 8-Kanal bi-dir. Pegelwandler ohne Richtungsumschaltung, 1,15V..5,5V, 50MBps (hohe Umschaltströme beachten)&lt;br /&gt;
&lt;br /&gt;
Vierfachdioden im kleinen 6-poligen SMD-Gehäuse:&lt;br /&gt;
* [http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/CD00130230.pdf DSILC6-4xx.pdf]&lt;br /&gt;
* [http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/CD00065974.pdf DVIULC6-4SC6.pdf]&lt;br /&gt;
* [http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/CD00001734.pdf DALC208.pdf]&lt;br /&gt;
* [http://www.diodes.com/datasheets/ds30195.pdf QSBT40, vierfach Schottky Terminator für Datenleitungen]&lt;br /&gt;
* [http://www.littlefuse.com/data/en/Data_Sheets/SP724Lead_Free.pdf SP724, Siliziumschutzarray]&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/307702#3316500 Forumsbeitrag]: Entkopplung von FT232 und AVR&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* Holmes D., [http://delphys.net/d.holmes/hardware/levelshift.html Bi-directional level-shift with MOSFETs]&lt;br /&gt;
* Gaurang Kavaiya, [http://www.edn.com/design/analog/4318916/Don-t-pay-for-level-translators-in-systems-using-multiple-power-supply-voltages Don’t pay for level translators in systems using multiple power-supply voltages], EDN, MAY 25, 2006, 81-86&lt;br /&gt;
* [http://www.elektronik-kompendium.de/public/schaerer/scf3_lc.htm Einfacher Pegelwandler im ELKO]&lt;br /&gt;
* [http://www.prog-link.com/dcf77/dcf77-17.html Pegelwandler für DFC77 Module]&lt;br /&gt;
* [http://elektronik.kai-uwe-schmidt.de/index.php?page=mp3_blueschaltung Pegelwandler für [[I2C]] Bus in einem MP3 Player]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/256452/levelshifter.pdf Application Note von Philips, I2C Pegelwandler]&lt;br /&gt;
* [http://www.nxp.com/documents/user_manual/UM10204.pdf I2C Spezifikation]  &lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-1-234277.html#new Forumsbeitrag zum Thema 1,8V-5V Pegelwandler] &lt;br /&gt;
* [http://www.st.com/web/en/resource/technical/document/datasheet/CD00001208.pdf 74LCX16245, 16 Bit Pegelwandler]&lt;br /&gt;
* [http://www.standardics.nxp.com/products/lvc/buffers/ LVC Logikfamilie]&lt;br /&gt;
* [http://www.standardics.nxp.com/products/lvc/transceivers/ LVC Tranceiver]&lt;br /&gt;
* [http://www.microchip.com/stellent/groups/techpub_sg/documents/devicedoc/en026368.pdf 3V Tips ‘n Tricks] (PDF) von Microchip&lt;br /&gt;
* [http://www.ti.com/lit/an/slaa148/slaa148.pdf Interfacing the 3-V MSP430 to 5-V Circuits] (PDF) von Texas Instruments&lt;br /&gt;
* [http://www.ti.com/logic-circuit/voltage-level-translation/overview.html Texas Instruments Voltage level translators]: Auswahl passender Bauelemente durch Eingabe der gewünschten Parameter&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Pegelwandler&amp;diff=102014</id>
		<title>Pegelwandler</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Pegelwandler&amp;diff=102014"/>
		<updated>2020-06-06T06:11:36Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Weblinks */ Formatierung Link&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Pegelwandeln (engl. level shifting) wird oft notwendig, wenn Systeme mit unterschiedlicher Ausgangs- und Eingangsspannungen (z.&amp;amp;nbsp;B. Versorgungs- oder Logikspannungen) miteinander verbunden werden sollen. Das vielleicht bekannteste Beispiel ist die Umsetzung von 0V/5V [[TTL]] Logikpegeln auf die -13V/13V Pegel einer seriellen [[RS232]] Schnittstelle. Die Probleme beim Pegelwandeln können sein:&lt;br /&gt;
&lt;br /&gt;
# Überlastung einer oder beider Seiten, bis hin zur Zerstörung&lt;br /&gt;
# Inkompatible Logikpegel und daraus resultierendes Nichtfunktionieren der Schaltung, oder noch schlimmer, sporadische Fehlfunktionen&lt;br /&gt;
# Verzögerungen der Signale durch die Pegelwandlung und daraus resultierende maximale Signalfrequenzen&lt;br /&gt;
&lt;br /&gt;
=== Überlastung ===&lt;br /&gt;
&lt;br /&gt;
Das Erzeugen von verschiedenen Versorgungsspannungen ist ziemlich einfach, aber man muss sicher gehen, dass man die Signalpegel der Bauteile auf Toleranz überprüft. Wenn z.B. ein 5V Bauteil ein Signal an ein 3V Bauteil schickt, können beide Bauteile beschädigt werden. Vor allem für neue ICs ist es ein Problem mit &amp;quot;hohen&amp;quot; Spannungen wie 5V zu arbeiten. Auf Grund der immer kleineren Schaltkreisstrukturen (aktuelle Prozessoren werden mit 14nm Technologie hergestellt!) werden auch die Abstände und Schichtdicken immer geringer. Das reduziert natürlich auch die Spannungs- und Stromfestigkeit der Transistoren auf dem IC. Neue ICs vertragen deshalb meist nur noch 3.3V, teilweise sogar weniger! Die Überlastung erfolgt durch zu hohe Spannung und dadurch mehr oder weniger langsame Zerstörung des ICs.&lt;br /&gt;
&lt;br /&gt;
=== Schutzdioden ===&lt;br /&gt;
&lt;br /&gt;
Hauptursache Nummer zwei für Überlastung von ICs mit verschiedenen Betriebsspannungen sind die in nahezu allen ICs integrierten Schutzdioden. Deren Aufgabe ist es in Normalfall, elektrostatische Entladungen auf eine sichere, niedrige Spannung zu begrenzen. Die Entladungen geschehen durch unsachgemässe Handhabung und Transport von ICs, z.&amp;amp;nbsp;B. wenn jemand über einen Kunstfaserteppich läuft, sich dabei elektrostatisch auflädt und einen IC anfasst, oder wenn Bauteile in einem Gerät eingebaut sind und der Anwender berührt offen liegende Kontakte (RS232 Eingang, USB-Stick, PCI-Steckkarten beim Einbau etc.). Auch elektrostatische Entladungen / EMV können Ursache zu hoher Pegel auf den Leitungen sein.&lt;br /&gt;
&lt;br /&gt;
Die Schutzdioden beginnen, Strom zu leiten, wenn die Eingangsspannung ca. 300 mV - 600 mV über U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; ansteigt oder entsprechend unter GND absinkt. Im Normalbetrieb sollten die Schutzdioden keinen Strom leiten. Manchmal kann man sie aber zur Spannungsbegrenzung missbrauchen, siehe [[#STEP-DOWN:_5V_-.3E_3.3V | Spannungsherabsetzung mit Vorwiderstand]].&lt;br /&gt;
&lt;br /&gt;
Besonderes Augenmerk ist hierbei auf die optimale Dimensionierung des R zu legen, um sicherzustellen, dass kein zu hoher Strom über die Schutzdioden abgeführt werden muss. Je nach Chip-Type und Ausgang halten diese zwischen 100 µA und 10 mA aus.&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_schutzdioden.png]]&lt;br /&gt;
&lt;br /&gt;
=== 5-V-tolerante Eingänge ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;5-Volt-tolerant&amp;lt;/i&amp;gt; bedeutet, dass 3-Volt-Bausteine ohne Probleme von einem 5-Volt-Baustein angesteuert werden dürfen.&lt;br /&gt;
&lt;br /&gt;
Viele Bauteile mit einer Betriebsspannung von 3 V verfügen über 5-V-tolerante Eingänge. Man sollte aber grundsätzlich im Datenblatt dies nachschauen, bevor die Schaltung aufgebaut wird. Sind sie es nicht, so ist ein &amp;lt;b&amp;gt;Pegelwandler&amp;lt;/b&amp;gt; auf den Verbindungsleitungen zwischen den Bauteilen notwendig. Ein Pegelwandler kann eine einfache Zener-Diode mit einem Widerstand sein, es kann aber auch ein eigens dafür vorgesehener IC sein. Sind die Signalwege bidirektional, so wird man meist die Lösung mit einem eigenen IC bevorzugen.&lt;br /&gt;
&lt;br /&gt;
Bei geringen Geschwindigkeitsanforderungen und erlaubten flachflankigen Signalen (bei Zähl- und Takteingängen ist dazu Schmitt-Trigger-Verhalten erforderlich) genügt ein Serienwiderstand (Richtwert 10 kΩ) in Verbindung mit der Eingangsschutzbeschaltung. Bei allen derartigen „passiven Pegelkonvertern“ muss die Logikschaltschwelle durchfahren werden. Bei heutzutage üblichen treibenden CMOS-Ausgangsstufen ist das kein Problem.&lt;br /&gt;
&lt;br /&gt;
Ob ein Bauteil 5-V-tolerant ist und unter welchen Betriebsbedingungen das gilt, steht im Datenblatt des betreffenden Bauteils vom betreffenden Hersteller. Wenn es auf diese Eigenschaft ankommt, lieber genau bei Lieferanten nachsehen, von welchem Hersteller die Bauteile kommen.&lt;br /&gt;
&lt;br /&gt;
==== Beispiele ====&lt;br /&gt;
&lt;br /&gt;
[[AVR]]s sind generell &#039;&#039;&#039;nicht&#039;&#039;&#039; 5-V-tolerant, wenn sie mit 3,3 V betrieben werden! Die absolute obere Grenze für Eingangsspannungen liegt bei U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; + 0,5 V. Zu finden in den elektrischen Spezifikationen im Datenblatt.&lt;br /&gt;
&lt;br /&gt;
Die GPIO-Pins des Raspberry Pi sind ebenfalls &#039;&#039;&#039;nicht&#039;&#039;&#039; 5-V-tolerant!&lt;br /&gt;
&lt;br /&gt;
Vorsicht bei:&lt;br /&gt;
&lt;br /&gt;
* 74&#039;&#039;&#039;LVX&#039;&#039;&#039;xxxx und 74&#039;&#039;&#039;LCX&#039;&#039;&#039;xxxx (245, 244, 240 ...) an Vcc = 3,3 V.&amp;lt;br&amp;gt;&amp;lt;font color=FF0000&amp;gt;Achtung&amp;lt;/font&amp;gt;: Nicht alle 74LVX sind für 5 V -&amp;gt; 3,3 V geeignet, da jeder Hersteller die ICs anders baut!&lt;br /&gt;
* SN74LVC07AD&lt;br /&gt;
* SN74LV1T04 (auch geeignet zur umgekehrten Konvertierung (3,3V-&amp;gt;5V))&lt;br /&gt;
&lt;br /&gt;
=== Kompatibilität von Logikpegeln ===&lt;br /&gt;
&lt;br /&gt;
Siehe auch http://www.interfacebus.com/Design_Translation.html&lt;br /&gt;
&lt;br /&gt;
Verschiedene Mikroprozessoren haben eigene elektrische Kenndaten für HIGH- und LOW-Pegel, die abhängig von der Versorgungsspannung sind, z.&amp;amp;nbsp;B. der [[R8C]]:&lt;br /&gt;
&lt;br /&gt;
* HIGH größer 0,8 * U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;&lt;br /&gt;
* LOW kleiner 0,2 * U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man muss die Spannungen der Aus- und Eingänge vergleichen. Wenn es um ein Hobbyprojekt geht, kann man einfach messen. Wenn es um eine kommerzielle Anwendung geht, die man verkaufen will, sollte man besser die Spezifikationen der ICs studieren.&lt;br /&gt;
&lt;br /&gt;
== UNIDIREKTIONAL ==&lt;br /&gt;
&lt;br /&gt;
=== 1,8 V ⇒ 5 V ===&lt;br /&gt;
&lt;br /&gt;
* Die besondere Eigenschaft der alten TTL-Schaltkreise, nämlich dass Strom bei LOW &#039;&#039;&#039;aus&#039;&#039;&#039; dem Eingang in den treibenden Ausgang fließt kann man sich zunutze machen, wie die nachfolgende Schaltung zeigt. In dieser wird der HIGH-Pegel des 1,8-V-Signals durch eine Schottkydiode um ca. 0,3 V auf 2,1 V erhöht. Damit ist man fast offiziell im HIGH-Bereich für TTL (Schaltschwelle 1,4 V, HIGH &amp;gt; 2,0 V). Der LOW-Pegel wird auf ca. 0,3 V erhöht, was voll den TTL-Richtlinien entspricht. Als Schaltkreisfamilie &#039;&#039;&#039;muss&#039;&#039;&#039; ein [[74xx|TTL-Typ]] eingesetzt werden, also LS, F, AS oder ähnlich. CMOS-Typen wie HC, LVC etc. funktionieren &#039;&#039;&#039;nicht&#039;&#039;&#039;!&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_LS.png]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=FF0000&amp;gt;Achtung&amp;lt;/font&amp;gt;: Diese Schaltung entspricht bei HIGH ungefähr einem offenen TTL-Eingang, was zwar meistens funktioniert, aber etwas störempfindlich sein kann. Davon wurde in der TTL-Ära stets abgeraten. Zudem ist der Pegelwechsel LOW nach HIGH durch den niedrigen Strom eher langsam. Man kann das jedoch mit einem Pullup-Widerstand absichern. Dann sind auch Gatter der 74HCT-Reihe einsetzbar.&lt;br /&gt;
&lt;br /&gt;
=== 3,3 V ⇒ 5 V ===&lt;br /&gt;
&lt;br /&gt;
Diese Konversion ist mit Abstand die häufigste. Dabei kann man getrost 3,3 V (früher) und 3 V (moderner) gleich setzen.&lt;br /&gt;
&lt;br /&gt;
* 3,3-V-Pegel werden bei TTL-kompatiblen Eingängen richtig erkannt (Schaltschwelle 1,4 V). Es ist kein Pegelwandler erforderlich. Direkte Verbindung. Einer der großen Vorteile klassischer TTL-Technik!&lt;br /&gt;
&lt;br /&gt;
* 5-V-CMOS Eingänge haben typisch eine minimale Eingangsspannug für HIGH (&amp;lt;math&amp;gt;V_{IH}&amp;lt;/math&amp;gt;) von 0.6 * U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; = 0.6 * 5 V = 3 V. Das kann ein 3,3-V-CMOS-Ausgang direkt treiben, allerdings kann sich das Zeitverhalten dadurch etwas ändern, weil der HIGH Pegel später erkannt wird. Vorsicht! Viele 5-V-CMOS-ICs wollen für HIGH offiziell mindestens 0,7 * U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; = 3,5 V oder manche auch 0,8 * U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; = 4,0V! Das geht dann offiziell nicht mehr mit einem 3,3-V-Ausgang! Für Hobbyzwecke kann man das aber ggf. probieren.&lt;br /&gt;
Zu beachten ist, dass der nicht ganz nach High durchgesteuerte Eingang Querstrom von der Speisespannung ziehen kann. Das kann für batteriebetriebene Geräte oder USB-konformes Standby durchaus ausschlaggebend sein.&lt;br /&gt;
&lt;br /&gt;
* 3,3-V-[[Ausgangsstufen_Logik-ICs | Open Collector]] nach 5 V (TTL oder CMOS): Einfach einen Pull-Up Widerstand hinzufügen und gut. Allerdings verbraucht der Pull-Up-Widerstand bei LOW auf jeden Fall Strom und begrenzt bei HIGH den maximalen Gate-Umladestrom. Die Schaltgeschwindigkeit von LOW nach HIGH wird durch die Größe des Pull-Ups bestimmt. Je nach Geschwindigkeitsanforderungen kann der in Mikrocontrollern meistens zuschaltbare innere Widerstand dazu benutzt werden. Zudem kann dieser, bei bekannt LOW bleibendem Eingangspegel, zur Reduktion der Stromaufnahme abgeschaltet werden. Bipolare TTL-Schaltkreise benötigen in der Bastelschaltung keinen Pull-Up (liefern Strom); bei Schaltungen mit erhöhter Zuverlässigkeit ist dennoch ein externer Pull-Up angeraten (Richtwert 4,7 kΩ).&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_oc_3-5.png]]&lt;br /&gt;
&lt;br /&gt;
* 3,3 V auf echte 5 V (CMOS) geht am einfachsten mit einem Baustein der HCT-Familie (NICHT HC!). Diese haben TTL-kompatible Eingänge und echte CMOS-Ausgänge&lt;br /&gt;
&lt;br /&gt;
* Man kann einen Komparator &amp;lt;small&amp;gt;in nichtinvertierender Schaltung&amp;lt;/small&amp;gt; benutzen (LM339/393). Allerdings ist diese Lösung relativ langsam, abhängig vom verwendeten Komparator. Komparatoren bieten eine freie Wahl des Eingangsspannungsbereichs und sind deshalb eine gute Wahl bei &#039;&#039;variabler&#039;&#039; Speisespannung der Treiberseite.&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_comp_3-5.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/part/74HCT245 74HCT245], [http://www.mikrocontroller.net/part/74HCT244 74HCT244]oder [http://www.mikrocontroller.net/part/74HCT240 74HCT240] (Das T ist wichtig. HCs können funktionieren, ist aber eigtl. ungeeignet, da bei 5V Versorgung und höheren Temperaturen V(input,high)=3,2V)&lt;br /&gt;
* [http://www.mikrocontroller.net/part/74HCT125 74HCT125]: OE Pins auf Masse und dann das Signal einfach anschließen. &lt;br /&gt;
* SN74LVC07AD &lt;br /&gt;
* SN74LV1T04 (auch geeignet zur umgekehrten Konvertierung (5V-&amp;gt;3,3V))&lt;br /&gt;
* 74V1T126 (single Gatter, V(input,high)=2V)&lt;br /&gt;
&lt;br /&gt;
=== 5 V ⇒ 9..15(..30) V ===&lt;br /&gt;
&lt;br /&gt;
* Am einfachsten geht das mit einem (geeigneten!) Open-Collector-Ausgang, einfach einen Pull-Up hinzufügen (an die hohe Spannung) und fertig. Ein 74&#039;&#039;xx&#039;&#039;03 geht hier nicht! Auch kann man nicht einen Push-Pull-Ausgang eines Mikrocontrollers dafür verwenden, indem man den Ausgang bei HIGH zum Eingang macht.&lt;br /&gt;
Hintergrund sind parasitäre Dioden zwischen Ausgang und Speisespannung.&lt;br /&gt;
Alle (geeigneten) Treiberausgänge haben eine maximal erlaubte Kollektorspannung, die zu beachten ist. Mehr Freiheit hat man bei der Verwendung von Einzeltransistoren, wobei eine gewisse Lücke von 30 V bis 200 V von Bipolartransistoren dominiert wird; für kleinere oder größere Spannungen gibt es preiswerte MOSFETs. (Die Lücke entsteht durch den geringen Bedarf des Weltmarktes an diesen Kollektorspannungen.)&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_oc_5-12.png]]&lt;br /&gt;
&lt;br /&gt;
* Man kann einen Komparator benutzen. Allerdings ist diese Lösung relativ langsam, abhängig vom verwendeten Komparator. Wenn nur zwei Signale gewandelt werden müssen bietet sich der LM393 an, ein Doppelkomparator mit Open-Collector-Ausgang, mit dem man auf einen beliebigen Pegel ausgeben kann. Der LM339 (man beachte den unauffälligen Zahlendreher) ist ein Vierfachkomparator mit den gleichen Eigenschaften. Wenn wenig Platz vorhanden ist, dann ist der TL311 im winzigen SOT-23 Gehäuse sehr empfehlenswert. Bei jedem Komparator kann auch einfach eine Invertierung gemacht werden, einfach die Eingänge + und - vertauschen. Diese Komparatoren eignen sich bis ca. 1 MHz.&lt;br /&gt;
&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/praxis/bausatz_pegelwandler-mit-transistoren.htm Pegelwandler mit Transistor, invertierend]&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_trans_inv.png]]&lt;br /&gt;
&lt;br /&gt;
* Pegelwandler mit Transistor, nicht invertierend&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_trans_ni.png]]&lt;br /&gt;
&lt;br /&gt;
Die Idee ist einfach. Wenn der Ausgang des 5-V-Gatters auf HIGH ist dann ist der Transistor ausgeschaltet, der Pull-Up-Widerstand R7 zieht den Ausgang auf + 12 V. Ist der Ausgang des 5-V-Gatters auf LOW ist, dann ist er vollkommen durchgesteuert und der Ausgang nahe 0 V (je nach Typ ca. 300 mV). Der Vorteil ist hier erhöhte Störsicherheit im Gegensatz zur einfachen Ansteuerung der Basis über einen Vorwiderstand. Außerdem wird dadurch nicht die Logik invertiert. Nachteilig ist der geringe Strom, der bei HIGH zur Verfügung steht (typisch 100 µA). Diese Schaltung ist die seltene Anwendung einer Basisschaltung für digitale Signale. Der Vorteil der Basisschaltung ist die höhere Grenzfrequenz durch die herabgesetzte Wirksamkeit der (störenden) Miller-Kapazität.&lt;br /&gt;
&lt;br /&gt;
* Wenn mehr Geschwindigkeit, Ausgangsstrom und weniger Stromverbrauch nötig ist, dann muss ein spezieller Baustein her, wie z.&amp;amp;nbsp;B.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
** [[Mosfet-Übersicht#Mosfet-Treiber|MOSFET-Treiber]] z.B. ICL7667&lt;br /&gt;
** [[H-Brücken Übersicht | Motortreiber]] ICs: (z.&amp;amp;nbsp;B. L293, L298, UCC27325 und deren Verwandte), wenns nicht zu schnell ist (einige Dutzend kHz)&lt;br /&gt;
** CD40109, 4fach Pegelwandler, bei Reichelt verfügbar&lt;br /&gt;
** HEF4104, 4fach Pegelwandler mit normalen und invertierten Ausgängen sowie Tristate. Um ggf. sicherzustellen, dass wie im Datenblatt beschrieben immer U&amp;lt;sub&amp;gt;DDI&amp;lt;/sub&amp;gt; &amp;lt;= U&amp;lt;sub&amp;gt;DDO&amp;lt;/sub&amp;gt; ist, kann man einfach eine Diode von U&amp;lt;sub&amp;gt;DDO&amp;lt;/sub&amp;gt; nach U&amp;lt;sub&amp;gt;DDI&amp;lt;/sub&amp;gt; schalten (z.&amp;amp;nbsp;B. Schottky SB120, aber auch 1N4148 &amp;amp; Co. sollte problemlos funktionieren)&lt;br /&gt;
** CD4504, 6fach Pegelwandler 3-20V, Eingangspegel TTL oder CMOS (umschaltbar) =&amp;gt; CMOS, keine Reihenfolge von U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;/U&amp;lt;sub&amp;gt;EE&amp;lt;/sub&amp;gt; erforderlich (Bezugsquelle: CSD)&lt;br /&gt;
** MAX232, der braucht nur 5 V Versorgungsspannung. Allerdings ist der Ausgangswiderstand relativ hoch (ca. 300 Ω) und man kann nur ca. 5 mA Ausgangstrom liefern. Die Ausgangsspannung beträgt maximal 10 V.&lt;br /&gt;
&lt;br /&gt;
=== 5 V ⇒ 3,3 V ===&lt;br /&gt;
&lt;br /&gt;
Ob 3,3 V (klassisch) oder 3 V (modern) ist bei dieser Betrachtung nahezu egal.&lt;br /&gt;
&lt;br /&gt;
* Zuerst sollte man prüfen, ob die Eingänge 5V-tolerant sind. Dann kann man die ICs direkt verbinden. Sehr schnell und billig!&lt;br /&gt;
&lt;br /&gt;
* Wenn die Eingänge nicht 5-V-tolerant sind und es trotzdem schnell sein soll, muss ein Gatter aus der LVC- oder AHC-Familie dazwischen geschaltet werden, also eines mit 5V-Toleranz. Bei 3 V Betriebsspannung kann man problemlos 5 V an den Eingang anlegen. Der Baustein 74HC4050 erlaubt per Definition eine Pegelwandlung bis etwa 15 V (siehe Datenblatt). Beide Anordungen haben auch eine sehr niedrige Ruhestromaufnahme.&lt;br /&gt;
&lt;br /&gt;
: &#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:* 74LVC245A (&#039;A&#039; ist wichtig, I/Os 5V-tolerant)&lt;br /&gt;
:* 74LVC245DW &lt;br /&gt;
:* 74LVT245 &lt;br /&gt;
:* 74LVXxxx (245, 244, 240 ...) an Vcc=3,3V. Achtung: Nicht alle 74LVX sind für 5V -&amp;gt; 3,3V geeignet, da jeder Hersteller die ICs anders baut!&lt;br /&gt;
:** 74LVX04 &lt;br /&gt;
:** 74LVX244 (Fairchild)&lt;br /&gt;
:** 74LVX245 (nicht von Reichelt, nicht 5V tolerant)&lt;br /&gt;
:** bei TI heissen die 74LVX... nur 74LV...&lt;br /&gt;
&lt;br /&gt;
:* 74HC4050 (bis 15 V Step-Down-Pegelwandlung laut Datenblatt, bei Reichelt in DIP und SO erhältlich)&lt;br /&gt;
:* MAX3373/MAX3375&lt;br /&gt;
:* NC7SZ08 oder andere aus derselben Serie. CMOS-Logik mit 5-V-toleranten Eingängen, recht flott und braucht dank SOT-23 auch wenig Platz auf der Platine&lt;br /&gt;
&lt;br /&gt;
* 5 V Open Collector auf 3,3-V-Eingang. Einfach einen Pull-Up hinzufügen (Pull-Up liegt auf 3,3 V). Nachteilig ist der relativ hohe Stromverbrauch bei LOW, die begrenzte Geschwindigkeit bei hochohmigen Pull-Ups und der relativ geringe Ausgangsstrom bei HIGH (abhängig vom Pull-Up).&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_oc_5-3.png]]&lt;br /&gt;
&lt;br /&gt;
* Spannungsteiler mit 680 Ω und 1 kΩ. Der Nachteil dieser Lösung ist der relativ hohe Stromverbrauch (~3mA), der relativ geringe Ausgangsstrom (mehr als 200..300 µA sollte man da nicht rausziehen) und die relativ geringe Geschwindigkeit (ca. 10 MHz).&lt;br /&gt;
&lt;br /&gt;
[[Datei:SPI level shifter with resistor divider.png|miniatur|rechts|fehlerhafter SPI-Takt nach Pegelwandler mit Widerstandsteiler (1,8/3,3 kΩ)&amp;lt;br /&amp;gt;unten: 5V-Ausgang am Mikrocontroller&amp;lt;br /&amp;gt;&lt;br /&gt;
oben: 3,3V-Eingang an der SD-Karte nach Pegelwandler]]&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_st_5-3.png]]&lt;br /&gt;
&lt;br /&gt;
* 1 kΩ Vorwiderstand. Dadurch wird der Strom vom 5-V-Ausgang in die 3,3-V-Versorgung durch die internen Schutzdioden auf ca. 1 mA begrenzt. Diese Lösung ist auch relativ langsam (ca. 5MHz). Ggf. kann man den Vorwiderstand auf 100 Ω reduzieren, das erhöht dann wieder die Geschwindigkeit. Aufpassen, einige ICs vertragen nur 1 mA oder weniger durch die Schutzdioden! Ausserdem muss man aufpassen, da jetzt von der 5-V-Seite Strom in die 3,3-V-Versorgung eingespeist wird. Besonders in Schaltungen mit sehr niedriger Stromaufnahme kann das zum Problem werden, wenn die Stromaufnahme geringer ist, als über die Vorwiderstände eingespeist wird. Dann nimmt es meist der Spannungsregler für 3,3 V übel wenn jemand „schiebt“, sprich, Strom einspeist. Denn die allermeisten Spannungsregler können nur Strom liefern (source), aber keinen Strom aufnehmen (sink). Es gibt 4-fach-Diodennetzwerke, die die internen Schutzdioden entlasten können (Schottkydioden mit kleinerer Flusspannung von ≈ 0,3 V als die internen Silizizumdioden mit ≈ 0,7 V), außerdem ist teilweise noch eine [[Diode#Z-Diode|Zenerdiode]] enthalten, die ggf. den überschüssig eingespeisten Strom aufnehmen kann.&lt;br /&gt;
&lt;br /&gt;
Alle Lösungen mit Vorwiderständen reduzieren die Flankensteilheit der Signale. Dies kann bei Takt- und Zähleingängen zu unerwünschten Schwingungen und damit Fehlzählungen führen. Derartig benutzte Eingänge sollten Schmitt-Trigger-Verhalten aufweisen.&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_vw_5-3.png]]&lt;br /&gt;
&lt;br /&gt;
Achtung: Mindestens für 74HC(T) Gatter ist dokumentiert (Philips 74HC/T High-Speed CMOS User Guide), dass auch schon geringer Strom durch die internen Schutzdioden zu einer unerwünschten Kopplung von Eingängen führen kann, d.h. der Strom fliesst zu einem anderen Eingang wieder hinaus. Sind also andere Eingänge ebenso hochohmig angeschlossen, kann dieser Querstrom zu Fehlfunktion führen.&lt;br /&gt;
&lt;br /&gt;
== BIDIREKTIONAL ==&lt;br /&gt;
&lt;br /&gt;
Für bidirektionale Busse gibt es spezielle Pegelwandler mit 2 Versorgungsspannungen. Allerdings brauchen die meist ein Signal zur Richtungsumschaltung. Auch muss man die Reihenfolge der Versorgungsspannungen beim Einschalten beachten. Aktive bidirektionale Pegelwandler OHNE Steuereingang zur Richtungsumschaltung sind mit Vorsicht zu genießen, denn die brauchen teilweise kurzzeitig einen relativ hohen Strom, um die Eingänge zu treiben.&lt;br /&gt;
&lt;br /&gt;
=== 5 V ⇔ 3,3 V ===&lt;br /&gt;
&lt;br /&gt;
* Wenn die 5-V-Seite TTL-kompatible Eingänge hat kann wieder der Spannungsteiler oder Vorwiderstand wie bei der unidirektionalen Anpassung verwendet werden (mit all seinen Vor- und Nachteilen).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* SN74CB3T3306&lt;br /&gt;
* SN74CBTD3861 (10 Bit,flow through, Betrieb mit 5 Volt)&lt;br /&gt;
* MAX1741 &lt;br /&gt;
* MAX3378E &lt;br /&gt;
* 74AHC126 s.u.&lt;br /&gt;
* ST2378 (bei CSD erhältlich, 3.5 eur, leider TSSOP)&lt;br /&gt;
* TXS0104E (TI: 4-BIT BIDIRECTIONAL VOLTAGE-LEVEL TRANSLATOR FOR OPEN-DRAIN AND PUSH-PULL APPLICATIONS)&lt;br /&gt;
* SN74LVC07A&lt;br /&gt;
* von Analog Devices die ADUM Serie&lt;br /&gt;
&lt;br /&gt;
=== 1,65 V ... 5,5 V ⇔ 1,65 V ... 5,5 V ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
* SN74LVC1T45&lt;br /&gt;
* SN74LVC2T45&lt;br /&gt;
* SN74LVC(H)8T245&lt;br /&gt;
* SN74LVC(H)16T245&lt;br /&gt;
&lt;br /&gt;
=== 1,2 V ... 3,6 V ⇔ 1,65V ... 5,5V ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
* TXB0101&lt;br /&gt;
* TXB0102&lt;br /&gt;
* TXB0104&lt;br /&gt;
* TXB0106&lt;br /&gt;
* TXB0108&lt;br /&gt;
&lt;br /&gt;
=== 1,2 V ... 3,6V ⇔ 1,2 V ... 3,6 V ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
* SN74AVC(H)1T45&lt;br /&gt;
* SN74AVC(H)2T45&lt;br /&gt;
* SN74AVC(H)4T245&lt;br /&gt;
* SN74AVC(H)8T245&lt;br /&gt;
* SN74AVC(H)16T245&lt;br /&gt;
* SN74AVC(H)20T245&lt;br /&gt;
* SN74AVC(H)24T245&lt;br /&gt;
* SN74AVC(H)32T245&lt;br /&gt;
&lt;br /&gt;
=== 1,5 V ... 3,6 V ⇔ 1,5 V ... 5,5 V ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
* 74LVC4245A&lt;br /&gt;
&lt;br /&gt;
== Mit galvanischer Trennung ==&lt;br /&gt;
&lt;br /&gt;
* [[Optokoppler]] (Langsam! Es gibt verschieden schnelle Koppler, aber über 1 MHz kommen sie kaum hinaus. Grundregel: Solche mit Fototransistoren sind am langsamsten, Richtwert 10 kHz, Fotodioden sind schneller, schnelle Optokoppler haben eine gesondert zu speisende Empfängerschaltung.)&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_opto.png]]&lt;br /&gt;
&lt;br /&gt;
* GMR-Koppler von der Firma NVE &lt;br /&gt;
* iCoupler Technologie von der Firma Analog Devices&lt;br /&gt;
* [[Kapazitiver Koppler]] (schnell, begrenzter Potenzialversatz)&lt;br /&gt;
* Transformatorkopplung (nur für gleichspannungsfreie Wechselsignale geeignet; sehr schnell; Beispiel: Netzkarten)&lt;br /&gt;
&lt;br /&gt;
Lit.: &#039;&#039;Galvanische Trennung: Optokoppler, GMR-Koppler oder iCoupler?&#039;&#039;, Siegfried W. Best, Redaktion elektronik industrie, [http://www.elektronik-industrie.de/ei/11,2003/article/2f0082f824c.html elektronik industrie 11-2003, S. 22ff.]&lt;br /&gt;
&lt;br /&gt;
== Praktische Beispiele ==&lt;br /&gt;
&lt;br /&gt;
=== Einfaches RS232-Interface ===&lt;br /&gt;
&lt;br /&gt;
[http://web.archive.org/web/20050122013618/http://www.henrik-reimers.de/control/rs232interface.gif Erfolgreicher Einsatz bis 19200 Baud und bis zu 10 m Leitungslänge]&lt;br /&gt;
Beschränkungen:&lt;br /&gt;
&lt;br /&gt;
* ggf. Platzbedarf&lt;br /&gt;
* Geschwindigkeit s.o.&lt;br /&gt;
&lt;br /&gt;
Beispiel: http://www.hagtech.com/pdf/translator.pdf&lt;br /&gt;
&lt;br /&gt;
=== [[I2C]]-Bus: gemeinsam 3.3V und 5V ===&lt;br /&gt;
&lt;br /&gt;
* [[MSP430]] an 3,3V/5V: http://www-s.ti.com/sc/psheets/slaa148/slaa148.pdf&lt;br /&gt;
&lt;br /&gt;
* Philips [http://www.nxp.com/documents/data_sheet/PCA9515.pdf PCA9515]: I2C Puffer mit Pegelwandlung. Der PCA9515 ist ein I2C-Bus Repeater, welcher I2C Busse mit verschiedenen Spannungen isoliert. Verfügbar bei Reichelt und DigiKey.&lt;br /&gt;
&lt;br /&gt;
* [http://ics.nxp.com/support/documents/interface/pdf/an97055.pdf Philips AN97055 Bi-directional level shifter for I²C-bus and other systems]&lt;br /&gt;
&lt;br /&gt;
* Bevor man ein Philips I2C Chip auswählt sollte man prüfen ob er verfügbar ist und auch das verfügbare Gehäuse wählen. Man sollte auch überlegen ob ein Puffer wirklich gebraucht wird. Wenn man echte I2C ICs mit 5V betreibt, dann sind die Eingänge vom Typ Schmitt Trigger CMOS (z.&amp;amp;nbsp;B. PCF8574). Dann müssen 3.3V Pegel auf 5V umgesetzt werden. Wenn man jedoch SMBUS Ics verwendet (z.&amp;amp;nbsp;B. ADT7461, Silabs 8051) dann sind die Schwellspannungen TTL kompatibel und es ist keine Anpassung notwendig. Für neue Pegelwandler sollte man hier nachschauen. http://www.bus-buffer.com&lt;br /&gt;
&lt;br /&gt;
* [http://www.edn.com/article/CA193193.html &amp;quot;Two-transistor circuit replaces IC&amp;quot;]. Für diese Anwendung kann ENABLE direkt mit 3.3V verbunden werden. Es ist eigentlich nur dazu da, den ICs &amp;quot;hot-swappable&amp;quot; zu machen (kann unter Spannung gesteckt und getrennt werden). Es geht sogar mit nur einem [[Transistor]] [http://www.mikrocontroller.net/topic/92447 siehe Forum]. Man sollte beachten, daß die Schaltung sowohl für SCL als auch SDA benötigt wird. &lt;br /&gt;
* Noch einfachere Lösungen mit nur einem MOSFET und zwei Pull-Up Widerständen pro Leitung sind in den folgenden Links zu finden. &lt;br /&gt;
** http://www.nxp.com/documents/application_note/AN10441.pdf&lt;br /&gt;
** http://www.semiconductors.philips.com/acrobat_download/literature/9398/39340011.pdf (Kapitel 18), bei der Berechnung der erreichbaren Geschwindigkeit dürfen die parasitären Kapazitäten der FETs nicht ignoriert werden&lt;br /&gt;
&lt;br /&gt;
=== Auswählbare Pegel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frage:&#039;&#039;&#039;&lt;br /&gt;
Ein CMOS Logikpegel zwischen 1,8V, 2,5V und 3,3V (abhängig von der Anwendung) muss auf 5V CMOS Logikpegel gewandelt werden. Es geht nur um diese Richtung mit maximal 8MHz. Es gibt die Stromversorgung für alle Pegel. Ein normaler Komparator wie LM311 ist nicht möglich, da er beim Betrieb mit 5V Versorgunsspannung erst ab 1V zu schalten anfängt. Meine Idee ist die Verwendung eines High Speed OPVs mit R2R Eingang, z.&amp;amp;nbsp;B. LMH6645.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;Antworten:&#039;&#039;&#039;&lt;br /&gt;
* Man könnte einen ultra-low threshold N-Kanal MOSFET nehmen und als Open Drain mit einem Pull-Up nach 5V betreiben, BSH103 könnte passen (Schwellspannung ~0,4V).&lt;br /&gt;
* High-Speed Single Supply Komparator wie z.&amp;amp;nbsp;B. [http://www-s.ti.com/sc/ds/tl712.pdf TL712] .&lt;br /&gt;
* SN74LVC1T45&lt;br /&gt;
* SN74LVC8T245&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frage:&#039;&#039;&#039;&lt;br /&gt;
Ich suchen einen IC, welcher eine Pegelwandlung von 3,3V nach 1,8V, 2,0V oder 5V ermöglicht und während des Betriebs umgeschaltet werden kann.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Antworten:&#039;&#039;&#039;&lt;br /&gt;
* So ein IC ist der Linear [http://www.linear.com/pc/productDetail.jsp?navId=H0,C1,C1007,C1071,P1601 LTC1555L-1.8] .&lt;br /&gt;
* SN74LVC1T45&lt;br /&gt;
* SN74LVC8T245&lt;br /&gt;
&lt;br /&gt;
=== AVR SPI (SDC/MMC)===&lt;br /&gt;
&lt;br /&gt;
Für &#039;&#039;&#039;bidirektionalen Betrieb&#039;&#039;&#039; zwischen 5V-AVR und 3,3V-Geräten und anders herum gibt es den Level-Translator &#039;&#039;&#039;MAX3378E&#039;&#039;&#039; von Maxim.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenrichtung am SPI im Zielsystem festgelegt ist, reichen &#039;&#039;&#039;unidirektionale Bausteine&#039;&#039;&#039;:&lt;br /&gt;
* 3x von 5V nach 3,3V und 1x von 3,3V nach 5V: &#039;&#039;&#039;MAX3392E&#039;&#039;&#039;&lt;br /&gt;
* 1x von 5V nach 3,3V und 3x von 3,3V nach 5V: &#039;&#039;&#039;MAX3390E&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Zum Anschließen einer SDC/MMC an einen 5V-AVR eignen sich somit der MAX3978E und der MAX3392E. Beide sind u.A. im winzigen TSSOP-14-Gehäuse verfügbar, nehmen sehr wenig Energie auf und eignen sich auch für andere Spannungen. Mit 3,3 und 5V beträgt die garantierte Übertragungsrate 8Mbps.&lt;br /&gt;
&lt;br /&gt;
* [http://datasheets.maxim-ic.com/en/ds/MAX3372E-MAX3393E.pdf Datenblatt]&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit zum Übersetzen zwischen 3,3 und 5V liegt in der Verwendung des &#039;&#039;&#039;74LVC245&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bastlerfreundlicher als &#039;&#039;&#039;MAX33XXX&#039;&#039;&#039; (in SO-Gehäuse):&lt;br /&gt;
* 5V &amp;gt; 3,3V (SCK, MOSI, CS): 74LVC-serie (z.B. 74LVC14A)&lt;br /&gt;
* 3,3V &amp;gt; 5V (MISO): 74HCT-Serie (z.B. 74HCT125, 74HCT251)&lt;br /&gt;
&lt;br /&gt;
5V-AVR an eine MMC (ohne Level-Shifter-Baustein):&lt;br /&gt;
* [http://www.microsyl.com/index.php/2010/03/24/led-sign-with-mmc-memory-card/ Projektseite] &lt;br /&gt;
* [http://www.microsyl.com/projects/ledsign/ledsign1.pdf Schaltplan]&lt;br /&gt;
&lt;br /&gt;
=== Mikrocontroller ⇔ Parallelport ([[ISP]]-Dongle, [[JTAG]] Wiggler, ...) ===&lt;br /&gt;
&lt;br /&gt;
Dieser Schaltplan funktioniert auch bei 3,3 V wenn man einen 74&amp;lt;B&amp;gt;HC&amp;lt;/B&amp;gt;244 anstatt eines 74&amp;lt;B&amp;gt;LS&amp;lt;/B&amp;gt;244 verwendet: [http://www.epanorama.net/circuits/parallel_output.html Parallel port interfacing made easy: Simple circuits and programs to show how to use PC parallel port output capabilities].&lt;br /&gt;
&lt;br /&gt;
=== Doppeltes Leitungspaar RX/TX 5V/3,3V ===&lt;br /&gt;
&lt;br /&gt;
Der [http://www.hackaday.com/2008/06/19/sparkfuns-logic-level-converter/ SparkFun&#039;s Logic Level Converter] ist eine Baugruppe mit MOSFETs [http://www.fairchildsemi.com/pf/BS/BSS138.html BSS138] für die Pegelwandlung von 5V auf 3,3V. 5V/2,8V und 5V/1,8V sind ebenfalls machbar.&lt;br /&gt;
&lt;br /&gt;
=== Steuerleitung zwischen Mikrocontroller und FPGA ===&lt;br /&gt;
&lt;br /&gt;
Oftmals werden PLDs oder FPGAs per Microcontroller-Platine angesteuert. Ältere Typen laufen meist als 5V oder sitzen in 5V-kompatiblen Platinen. Sollen moderne FPGAs angesteuert werden, trifft man fast immer auf 3,3-V-Typen, bzw. muss sogar 2,5-V- / 1,8-V-Bänke beschalten, wenn nur noch dort Pins frei sind.&lt;br /&gt;
&lt;br /&gt;
==== Mikrocontroller ⇒ FPGA ====&lt;br /&gt;
&lt;br /&gt;
Die 5 V sind also im Extremfall auf 1,8 V herabzusetzen, was bei einem maximal zulässigen Diodenstrom von 3 mA (Beispiel Xilinx) einen Mindestwiderstand von ca. 1 kΩ erfordert. Die resultierende maximale Schaltfrequenz liegt dann bei einem typischen FPGA-Eingang bei etwa &amp;lt; 500 kHz. Soll der Eingang aus Belastungsgründen nicht mit mehr als 0,3 mA belastet werden, müsste der Widerstand auf 10 kΩ steigen, wodurch die Frequenz auf 1/10 sinkt. Zudem ist der Eingang dann störempfindlicher. Daher ist es besser, man schaltet dem Eingang eine zusätzliche Z-Diode bei und dimensioniert den Vorwiderstand so, dass die Strombelastbarkeit des Mikrocontrollers ausgelastet wird.&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit ist es, die Spannung mit einer Diode in Vorwärtsrichtung herabzusetzen. Dann muss jedoch der Vorwiderstand noch exakter toleriert werden und auch Abweichungen der Spannung (Welligkeit) berücksichtigt werden.&lt;br /&gt;
&lt;br /&gt;
==== FPGA ⇒ Mikrocontroller ====&lt;br /&gt;
&lt;br /&gt;
Umgekehrt ist es oft nötig, dass Bausteine einen fremden Chip treiben müssen, dessen Eingang bereits mit einem Pull-Up versehen ist. Über diesen wird dann stets ein Strom in die Schutzdiode eingeprägt, auch wenn der Ausgang auf HIGH geht. Soll z. B. von einem PLD oder einem FPGA aus eine Mikrocontrollerplatine bedient werden, die über einen Pull-Up von 1 kΩ verfügt, würden immer ca. 1 mA in die Schutzdiode eingeprägt. Hier kann eine Seriendiode helfen, die Spannung genügend herabzusetzen, um den Ausgang zu schützen und dennoch die Funktion zu erhalten. Dann steuert ein LOW-Ausgang den Eingang auf geschätzte 1V, was aber meistens für das Erkennen von LOW noch sicher reicht.&lt;br /&gt;
&lt;br /&gt;
== Bauteile ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;74ALVC164245&#039;&#039;&#039; - &#039;&#039;16bit dual supply translating transceiver&#039;&#039;. Eine Seite von 1.5V bis 3.6V, die andere von 1.5 bis 5.5V.&lt;br /&gt;
* &#039;&#039;&#039;74LVX573&#039;&#039;&#039; (unidirektional, Latch, nicht alle Hersteller bauen diesen 5V tolerant!)&lt;br /&gt;
* &#039;&#039;&#039;74LVX245&#039;&#039;&#039; (bidirektional, nicht alle Hersteller bauen diesen 5V tolerant!)&lt;br /&gt;
* &#039;&#039;&#039;74LVX125&#039;&#039;&#039; - &#039;&#039;Low Voltage Quad Buffer with 3-STATE Outputs&#039;&#039;. http://www.fairchildsemi.com/pf/74/74LVX125.html&lt;br /&gt;
* &#039;&#039;&#039;SN74LVC2T45&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;SN74LVC8T245&#039;&#039;&#039; - &#039;&#039;8-Bit Dual-Supply Bus Transceiver with Configurable Voltage Translation and Three-State Outputs&#039;&#039;. http://focus.ti.com/docs/prod/folders/print/sn74lvc8t245.html&lt;br /&gt;
* &#039;&#039;&#039;74LCX244MSA&#039;&#039;&#039; von Fairchild.&lt;br /&gt;
* &#039;&#039;&#039;MAX3377&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAX3000&#039;&#039;&#039; 8-Kanal bidirektioneler Pegelwandler ohne Richtungsumschaltung&lt;br /&gt;
* &#039;&#039;&#039;ADG3308&#039;&#039;&#039; 8-Kanal bi-dir. Pegelwandler ohne Richtungsumschaltung, 1,15V..5,5V, 50MBps (hohe Umschaltströme beachten)&lt;br /&gt;
&lt;br /&gt;
Vierfachdioden im kleinen 6-poligen SMD-Gehäuse:&lt;br /&gt;
* [http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/CD00130230.pdf DSILC6-4xx.pdf]&lt;br /&gt;
* [http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/CD00065974.pdf DVIULC6-4SC6.pdf]&lt;br /&gt;
* [http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/CD00001734.pdf DALC208.pdf]&lt;br /&gt;
* [http://www.diodes.com/datasheets/ds30195.pdf QSBT40, vierfach Schottky Terminator für Datenleitungen]&lt;br /&gt;
* [http://www.littlefuse.com/data/en/Data_Sheets/SP724Lead_Free.pdf SP724, Siliziumschutzarray]&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/307702#3316500 Forumsbeitrag]: Entkopplung von FT232 und AVR&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* Holmes D., [http://delphys.net/d.holmes/hardware/levelshift.html Bi-directional level-shift with MOSFETs]&lt;br /&gt;
* Gaurang Kavaiya, [http://www.edn.com/design/analog/4318916/Don-t-pay-for-level-translators-in-systems-using-multiple-power-supply-voltages Don’t pay for level translators in systems using multiple power-supply voltages], EDN, MAY 25, 2006, 81-86&lt;br /&gt;
* [http://www.elektronik-kompendium.de/public/schaerer/scf3_lc.htm Einfacher Pegelwandler im ELKO]&lt;br /&gt;
* [http://www.prog-link.com/dcf77/dcf77-17.html Pegelwandler für DFC77 Module]&lt;br /&gt;
* [http://elektronik.kai-uwe-schmidt.de/index.php?page=mp3_blueschaltung Pegelwandler für [[I2C]] Bus in einem MP3 Player]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/256452/levelshifter.pdf Application Note von Philips, I2C Pegelwandler]&lt;br /&gt;
* [http://www.nxp.com/documents/user_manual/UM10204.pdf I2C Spezifikation]  &lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-1-234277.html#new Forumsbeitrag zum Thema 1,8V-5V Pegelwandler] &lt;br /&gt;
* [http://www.st.com/web/en/resource/technical/document/datasheet/CD00001208.pdf 74LCX16245, 16 Bit Pegelwandler]&lt;br /&gt;
* [http://www.standardics.nxp.com/products/lvc/buffers/ LVC Logikfamilie]&lt;br /&gt;
* [http://www.standardics.nxp.com/products/lvc/transceivers/ LVC Tranceiver]&lt;br /&gt;
* [http://www.microchip.com/stellent/groups/techpub_sg/documents/devicedoc/en026368.pdf 3V Tips ‘n Tricks] (PDF) von Microchip&lt;br /&gt;
* [http://www.ti.com/lit/an/slaa148/slaa148.pdf Interfacing the 3-V MSP430 to 5-V Circuits] (PDF) von Texas Instruments&lt;br /&gt;
* [http://www.ti.com/logic-circuit/voltage-level-translation/overview.html &amp;quot;Texas Instruments Voltage level translators&amp;quot;]: Auswahl passender Bauelemente Anhand von Parametern&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Pegelwandler&amp;diff=102013</id>
		<title>Pegelwandler</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Pegelwandler&amp;diff=102013"/>
		<updated>2020-06-06T06:10:20Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Weblinks */ : Seite Texas Instruments Voltage level translators hinzugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Pegelwandeln (engl. level shifting) wird oft notwendig, wenn Systeme mit unterschiedlicher Ausgangs- und Eingangsspannungen (z.&amp;amp;nbsp;B. Versorgungs- oder Logikspannungen) miteinander verbunden werden sollen. Das vielleicht bekannteste Beispiel ist die Umsetzung von 0V/5V [[TTL]] Logikpegeln auf die -13V/13V Pegel einer seriellen [[RS232]] Schnittstelle. Die Probleme beim Pegelwandeln können sein:&lt;br /&gt;
&lt;br /&gt;
# Überlastung einer oder beider Seiten, bis hin zur Zerstörung&lt;br /&gt;
# Inkompatible Logikpegel und daraus resultierendes Nichtfunktionieren der Schaltung, oder noch schlimmer, sporadische Fehlfunktionen&lt;br /&gt;
# Verzögerungen der Signale durch die Pegelwandlung und daraus resultierende maximale Signalfrequenzen&lt;br /&gt;
&lt;br /&gt;
=== Überlastung ===&lt;br /&gt;
&lt;br /&gt;
Das Erzeugen von verschiedenen Versorgungsspannungen ist ziemlich einfach, aber man muss sicher gehen, dass man die Signalpegel der Bauteile auf Toleranz überprüft. Wenn z.B. ein 5V Bauteil ein Signal an ein 3V Bauteil schickt, können beide Bauteile beschädigt werden. Vor allem für neue ICs ist es ein Problem mit &amp;quot;hohen&amp;quot; Spannungen wie 5V zu arbeiten. Auf Grund der immer kleineren Schaltkreisstrukturen (aktuelle Prozessoren werden mit 14nm Technologie hergestellt!) werden auch die Abstände und Schichtdicken immer geringer. Das reduziert natürlich auch die Spannungs- und Stromfestigkeit der Transistoren auf dem IC. Neue ICs vertragen deshalb meist nur noch 3.3V, teilweise sogar weniger! Die Überlastung erfolgt durch zu hohe Spannung und dadurch mehr oder weniger langsame Zerstörung des ICs.&lt;br /&gt;
&lt;br /&gt;
=== Schutzdioden ===&lt;br /&gt;
&lt;br /&gt;
Hauptursache Nummer zwei für Überlastung von ICs mit verschiedenen Betriebsspannungen sind die in nahezu allen ICs integrierten Schutzdioden. Deren Aufgabe ist es in Normalfall, elektrostatische Entladungen auf eine sichere, niedrige Spannung zu begrenzen. Die Entladungen geschehen durch unsachgemässe Handhabung und Transport von ICs, z.&amp;amp;nbsp;B. wenn jemand über einen Kunstfaserteppich läuft, sich dabei elektrostatisch auflädt und einen IC anfasst, oder wenn Bauteile in einem Gerät eingebaut sind und der Anwender berührt offen liegende Kontakte (RS232 Eingang, USB-Stick, PCI-Steckkarten beim Einbau etc.). Auch elektrostatische Entladungen / EMV können Ursache zu hoher Pegel auf den Leitungen sein.&lt;br /&gt;
&lt;br /&gt;
Die Schutzdioden beginnen, Strom zu leiten, wenn die Eingangsspannung ca. 300 mV - 600 mV über U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; ansteigt oder entsprechend unter GND absinkt. Im Normalbetrieb sollten die Schutzdioden keinen Strom leiten. Manchmal kann man sie aber zur Spannungsbegrenzung missbrauchen, siehe [[#STEP-DOWN:_5V_-.3E_3.3V | Spannungsherabsetzung mit Vorwiderstand]].&lt;br /&gt;
&lt;br /&gt;
Besonderes Augenmerk ist hierbei auf die optimale Dimensionierung des R zu legen, um sicherzustellen, dass kein zu hoher Strom über die Schutzdioden abgeführt werden muss. Je nach Chip-Type und Ausgang halten diese zwischen 100 µA und 10 mA aus.&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_schutzdioden.png]]&lt;br /&gt;
&lt;br /&gt;
=== 5-V-tolerante Eingänge ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;5-Volt-tolerant&amp;lt;/i&amp;gt; bedeutet, dass 3-Volt-Bausteine ohne Probleme von einem 5-Volt-Baustein angesteuert werden dürfen.&lt;br /&gt;
&lt;br /&gt;
Viele Bauteile mit einer Betriebsspannung von 3 V verfügen über 5-V-tolerante Eingänge. Man sollte aber grundsätzlich im Datenblatt dies nachschauen, bevor die Schaltung aufgebaut wird. Sind sie es nicht, so ist ein &amp;lt;b&amp;gt;Pegelwandler&amp;lt;/b&amp;gt; auf den Verbindungsleitungen zwischen den Bauteilen notwendig. Ein Pegelwandler kann eine einfache Zener-Diode mit einem Widerstand sein, es kann aber auch ein eigens dafür vorgesehener IC sein. Sind die Signalwege bidirektional, so wird man meist die Lösung mit einem eigenen IC bevorzugen.&lt;br /&gt;
&lt;br /&gt;
Bei geringen Geschwindigkeitsanforderungen und erlaubten flachflankigen Signalen (bei Zähl- und Takteingängen ist dazu Schmitt-Trigger-Verhalten erforderlich) genügt ein Serienwiderstand (Richtwert 10 kΩ) in Verbindung mit der Eingangsschutzbeschaltung. Bei allen derartigen „passiven Pegelkonvertern“ muss die Logikschaltschwelle durchfahren werden. Bei heutzutage üblichen treibenden CMOS-Ausgangsstufen ist das kein Problem.&lt;br /&gt;
&lt;br /&gt;
Ob ein Bauteil 5-V-tolerant ist und unter welchen Betriebsbedingungen das gilt, steht im Datenblatt des betreffenden Bauteils vom betreffenden Hersteller. Wenn es auf diese Eigenschaft ankommt, lieber genau bei Lieferanten nachsehen, von welchem Hersteller die Bauteile kommen.&lt;br /&gt;
&lt;br /&gt;
==== Beispiele ====&lt;br /&gt;
&lt;br /&gt;
[[AVR]]s sind generell &#039;&#039;&#039;nicht&#039;&#039;&#039; 5-V-tolerant, wenn sie mit 3,3 V betrieben werden! Die absolute obere Grenze für Eingangsspannungen liegt bei U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; + 0,5 V. Zu finden in den elektrischen Spezifikationen im Datenblatt.&lt;br /&gt;
&lt;br /&gt;
Die GPIO-Pins des Raspberry Pi sind ebenfalls &#039;&#039;&#039;nicht&#039;&#039;&#039; 5-V-tolerant!&lt;br /&gt;
&lt;br /&gt;
Vorsicht bei:&lt;br /&gt;
&lt;br /&gt;
* 74&#039;&#039;&#039;LVX&#039;&#039;&#039;xxxx und 74&#039;&#039;&#039;LCX&#039;&#039;&#039;xxxx (245, 244, 240 ...) an Vcc = 3,3 V.&amp;lt;br&amp;gt;&amp;lt;font color=FF0000&amp;gt;Achtung&amp;lt;/font&amp;gt;: Nicht alle 74LVX sind für 5 V -&amp;gt; 3,3 V geeignet, da jeder Hersteller die ICs anders baut!&lt;br /&gt;
* SN74LVC07AD&lt;br /&gt;
* SN74LV1T04 (auch geeignet zur umgekehrten Konvertierung (3,3V-&amp;gt;5V))&lt;br /&gt;
&lt;br /&gt;
=== Kompatibilität von Logikpegeln ===&lt;br /&gt;
&lt;br /&gt;
Siehe auch http://www.interfacebus.com/Design_Translation.html&lt;br /&gt;
&lt;br /&gt;
Verschiedene Mikroprozessoren haben eigene elektrische Kenndaten für HIGH- und LOW-Pegel, die abhängig von der Versorgungsspannung sind, z.&amp;amp;nbsp;B. der [[R8C]]:&lt;br /&gt;
&lt;br /&gt;
* HIGH größer 0,8 * U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;&lt;br /&gt;
* LOW kleiner 0,2 * U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man muss die Spannungen der Aus- und Eingänge vergleichen. Wenn es um ein Hobbyprojekt geht, kann man einfach messen. Wenn es um eine kommerzielle Anwendung geht, die man verkaufen will, sollte man besser die Spezifikationen der ICs studieren.&lt;br /&gt;
&lt;br /&gt;
== UNIDIREKTIONAL ==&lt;br /&gt;
&lt;br /&gt;
=== 1,8 V ⇒ 5 V ===&lt;br /&gt;
&lt;br /&gt;
* Die besondere Eigenschaft der alten TTL-Schaltkreise, nämlich dass Strom bei LOW &#039;&#039;&#039;aus&#039;&#039;&#039; dem Eingang in den treibenden Ausgang fließt kann man sich zunutze machen, wie die nachfolgende Schaltung zeigt. In dieser wird der HIGH-Pegel des 1,8-V-Signals durch eine Schottkydiode um ca. 0,3 V auf 2,1 V erhöht. Damit ist man fast offiziell im HIGH-Bereich für TTL (Schaltschwelle 1,4 V, HIGH &amp;gt; 2,0 V). Der LOW-Pegel wird auf ca. 0,3 V erhöht, was voll den TTL-Richtlinien entspricht. Als Schaltkreisfamilie &#039;&#039;&#039;muss&#039;&#039;&#039; ein [[74xx|TTL-Typ]] eingesetzt werden, also LS, F, AS oder ähnlich. CMOS-Typen wie HC, LVC etc. funktionieren &#039;&#039;&#039;nicht&#039;&#039;&#039;!&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_LS.png]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=FF0000&amp;gt;Achtung&amp;lt;/font&amp;gt;: Diese Schaltung entspricht bei HIGH ungefähr einem offenen TTL-Eingang, was zwar meistens funktioniert, aber etwas störempfindlich sein kann. Davon wurde in der TTL-Ära stets abgeraten. Zudem ist der Pegelwechsel LOW nach HIGH durch den niedrigen Strom eher langsam. Man kann das jedoch mit einem Pullup-Widerstand absichern. Dann sind auch Gatter der 74HCT-Reihe einsetzbar.&lt;br /&gt;
&lt;br /&gt;
=== 3,3 V ⇒ 5 V ===&lt;br /&gt;
&lt;br /&gt;
Diese Konversion ist mit Abstand die häufigste. Dabei kann man getrost 3,3 V (früher) und 3 V (moderner) gleich setzen.&lt;br /&gt;
&lt;br /&gt;
* 3,3-V-Pegel werden bei TTL-kompatiblen Eingängen richtig erkannt (Schaltschwelle 1,4 V). Es ist kein Pegelwandler erforderlich. Direkte Verbindung. Einer der großen Vorteile klassischer TTL-Technik!&lt;br /&gt;
&lt;br /&gt;
* 5-V-CMOS Eingänge haben typisch eine minimale Eingangsspannug für HIGH (&amp;lt;math&amp;gt;V_{IH}&amp;lt;/math&amp;gt;) von 0.6 * U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; = 0.6 * 5 V = 3 V. Das kann ein 3,3-V-CMOS-Ausgang direkt treiben, allerdings kann sich das Zeitverhalten dadurch etwas ändern, weil der HIGH Pegel später erkannt wird. Vorsicht! Viele 5-V-CMOS-ICs wollen für HIGH offiziell mindestens 0,7 * U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; = 3,5 V oder manche auch 0,8 * U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; = 4,0V! Das geht dann offiziell nicht mehr mit einem 3,3-V-Ausgang! Für Hobbyzwecke kann man das aber ggf. probieren.&lt;br /&gt;
Zu beachten ist, dass der nicht ganz nach High durchgesteuerte Eingang Querstrom von der Speisespannung ziehen kann. Das kann für batteriebetriebene Geräte oder USB-konformes Standby durchaus ausschlaggebend sein.&lt;br /&gt;
&lt;br /&gt;
* 3,3-V-[[Ausgangsstufen_Logik-ICs | Open Collector]] nach 5 V (TTL oder CMOS): Einfach einen Pull-Up Widerstand hinzufügen und gut. Allerdings verbraucht der Pull-Up-Widerstand bei LOW auf jeden Fall Strom und begrenzt bei HIGH den maximalen Gate-Umladestrom. Die Schaltgeschwindigkeit von LOW nach HIGH wird durch die Größe des Pull-Ups bestimmt. Je nach Geschwindigkeitsanforderungen kann der in Mikrocontrollern meistens zuschaltbare innere Widerstand dazu benutzt werden. Zudem kann dieser, bei bekannt LOW bleibendem Eingangspegel, zur Reduktion der Stromaufnahme abgeschaltet werden. Bipolare TTL-Schaltkreise benötigen in der Bastelschaltung keinen Pull-Up (liefern Strom); bei Schaltungen mit erhöhter Zuverlässigkeit ist dennoch ein externer Pull-Up angeraten (Richtwert 4,7 kΩ).&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_oc_3-5.png]]&lt;br /&gt;
&lt;br /&gt;
* 3,3 V auf echte 5 V (CMOS) geht am einfachsten mit einem Baustein der HCT-Familie (NICHT HC!). Diese haben TTL-kompatible Eingänge und echte CMOS-Ausgänge&lt;br /&gt;
&lt;br /&gt;
* Man kann einen Komparator &amp;lt;small&amp;gt;in nichtinvertierender Schaltung&amp;lt;/small&amp;gt; benutzen (LM339/393). Allerdings ist diese Lösung relativ langsam, abhängig vom verwendeten Komparator. Komparatoren bieten eine freie Wahl des Eingangsspannungsbereichs und sind deshalb eine gute Wahl bei &#039;&#039;variabler&#039;&#039; Speisespannung der Treiberseite.&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_comp_3-5.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/part/74HCT245 74HCT245], [http://www.mikrocontroller.net/part/74HCT244 74HCT244]oder [http://www.mikrocontroller.net/part/74HCT240 74HCT240] (Das T ist wichtig. HCs können funktionieren, ist aber eigtl. ungeeignet, da bei 5V Versorgung und höheren Temperaturen V(input,high)=3,2V)&lt;br /&gt;
* [http://www.mikrocontroller.net/part/74HCT125 74HCT125]: OE Pins auf Masse und dann das Signal einfach anschließen. &lt;br /&gt;
* SN74LVC07AD &lt;br /&gt;
* SN74LV1T04 (auch geeignet zur umgekehrten Konvertierung (5V-&amp;gt;3,3V))&lt;br /&gt;
* 74V1T126 (single Gatter, V(input,high)=2V)&lt;br /&gt;
&lt;br /&gt;
=== 5 V ⇒ 9..15(..30) V ===&lt;br /&gt;
&lt;br /&gt;
* Am einfachsten geht das mit einem (geeigneten!) Open-Collector-Ausgang, einfach einen Pull-Up hinzufügen (an die hohe Spannung) und fertig. Ein 74&#039;&#039;xx&#039;&#039;03 geht hier nicht! Auch kann man nicht einen Push-Pull-Ausgang eines Mikrocontrollers dafür verwenden, indem man den Ausgang bei HIGH zum Eingang macht.&lt;br /&gt;
Hintergrund sind parasitäre Dioden zwischen Ausgang und Speisespannung.&lt;br /&gt;
Alle (geeigneten) Treiberausgänge haben eine maximal erlaubte Kollektorspannung, die zu beachten ist. Mehr Freiheit hat man bei der Verwendung von Einzeltransistoren, wobei eine gewisse Lücke von 30 V bis 200 V von Bipolartransistoren dominiert wird; für kleinere oder größere Spannungen gibt es preiswerte MOSFETs. (Die Lücke entsteht durch den geringen Bedarf des Weltmarktes an diesen Kollektorspannungen.)&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_oc_5-12.png]]&lt;br /&gt;
&lt;br /&gt;
* Man kann einen Komparator benutzen. Allerdings ist diese Lösung relativ langsam, abhängig vom verwendeten Komparator. Wenn nur zwei Signale gewandelt werden müssen bietet sich der LM393 an, ein Doppelkomparator mit Open-Collector-Ausgang, mit dem man auf einen beliebigen Pegel ausgeben kann. Der LM339 (man beachte den unauffälligen Zahlendreher) ist ein Vierfachkomparator mit den gleichen Eigenschaften. Wenn wenig Platz vorhanden ist, dann ist der TL311 im winzigen SOT-23 Gehäuse sehr empfehlenswert. Bei jedem Komparator kann auch einfach eine Invertierung gemacht werden, einfach die Eingänge + und - vertauschen. Diese Komparatoren eignen sich bis ca. 1 MHz.&lt;br /&gt;
&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/praxis/bausatz_pegelwandler-mit-transistoren.htm Pegelwandler mit Transistor, invertierend]&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_trans_inv.png]]&lt;br /&gt;
&lt;br /&gt;
* Pegelwandler mit Transistor, nicht invertierend&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_trans_ni.png]]&lt;br /&gt;
&lt;br /&gt;
Die Idee ist einfach. Wenn der Ausgang des 5-V-Gatters auf HIGH ist dann ist der Transistor ausgeschaltet, der Pull-Up-Widerstand R7 zieht den Ausgang auf + 12 V. Ist der Ausgang des 5-V-Gatters auf LOW ist, dann ist er vollkommen durchgesteuert und der Ausgang nahe 0 V (je nach Typ ca. 300 mV). Der Vorteil ist hier erhöhte Störsicherheit im Gegensatz zur einfachen Ansteuerung der Basis über einen Vorwiderstand. Außerdem wird dadurch nicht die Logik invertiert. Nachteilig ist der geringe Strom, der bei HIGH zur Verfügung steht (typisch 100 µA). Diese Schaltung ist die seltene Anwendung einer Basisschaltung für digitale Signale. Der Vorteil der Basisschaltung ist die höhere Grenzfrequenz durch die herabgesetzte Wirksamkeit der (störenden) Miller-Kapazität.&lt;br /&gt;
&lt;br /&gt;
* Wenn mehr Geschwindigkeit, Ausgangsstrom und weniger Stromverbrauch nötig ist, dann muss ein spezieller Baustein her, wie z.&amp;amp;nbsp;B.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
** [[Mosfet-Übersicht#Mosfet-Treiber|MOSFET-Treiber]] z.B. ICL7667&lt;br /&gt;
** [[H-Brücken Übersicht | Motortreiber]] ICs: (z.&amp;amp;nbsp;B. L293, L298, UCC27325 und deren Verwandte), wenns nicht zu schnell ist (einige Dutzend kHz)&lt;br /&gt;
** CD40109, 4fach Pegelwandler, bei Reichelt verfügbar&lt;br /&gt;
** HEF4104, 4fach Pegelwandler mit normalen und invertierten Ausgängen sowie Tristate. Um ggf. sicherzustellen, dass wie im Datenblatt beschrieben immer U&amp;lt;sub&amp;gt;DDI&amp;lt;/sub&amp;gt; &amp;lt;= U&amp;lt;sub&amp;gt;DDO&amp;lt;/sub&amp;gt; ist, kann man einfach eine Diode von U&amp;lt;sub&amp;gt;DDO&amp;lt;/sub&amp;gt; nach U&amp;lt;sub&amp;gt;DDI&amp;lt;/sub&amp;gt; schalten (z.&amp;amp;nbsp;B. Schottky SB120, aber auch 1N4148 &amp;amp; Co. sollte problemlos funktionieren)&lt;br /&gt;
** CD4504, 6fach Pegelwandler 3-20V, Eingangspegel TTL oder CMOS (umschaltbar) =&amp;gt; CMOS, keine Reihenfolge von U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;/U&amp;lt;sub&amp;gt;EE&amp;lt;/sub&amp;gt; erforderlich (Bezugsquelle: CSD)&lt;br /&gt;
** MAX232, der braucht nur 5 V Versorgungsspannung. Allerdings ist der Ausgangswiderstand relativ hoch (ca. 300 Ω) und man kann nur ca. 5 mA Ausgangstrom liefern. Die Ausgangsspannung beträgt maximal 10 V.&lt;br /&gt;
&lt;br /&gt;
=== 5 V ⇒ 3,3 V ===&lt;br /&gt;
&lt;br /&gt;
Ob 3,3 V (klassisch) oder 3 V (modern) ist bei dieser Betrachtung nahezu egal.&lt;br /&gt;
&lt;br /&gt;
* Zuerst sollte man prüfen, ob die Eingänge 5V-tolerant sind. Dann kann man die ICs direkt verbinden. Sehr schnell und billig!&lt;br /&gt;
&lt;br /&gt;
* Wenn die Eingänge nicht 5-V-tolerant sind und es trotzdem schnell sein soll, muss ein Gatter aus der LVC- oder AHC-Familie dazwischen geschaltet werden, also eines mit 5V-Toleranz. Bei 3 V Betriebsspannung kann man problemlos 5 V an den Eingang anlegen. Der Baustein 74HC4050 erlaubt per Definition eine Pegelwandlung bis etwa 15 V (siehe Datenblatt). Beide Anordungen haben auch eine sehr niedrige Ruhestromaufnahme.&lt;br /&gt;
&lt;br /&gt;
: &#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:* 74LVC245A (&#039;A&#039; ist wichtig, I/Os 5V-tolerant)&lt;br /&gt;
:* 74LVC245DW &lt;br /&gt;
:* 74LVT245 &lt;br /&gt;
:* 74LVXxxx (245, 244, 240 ...) an Vcc=3,3V. Achtung: Nicht alle 74LVX sind für 5V -&amp;gt; 3,3V geeignet, da jeder Hersteller die ICs anders baut!&lt;br /&gt;
:** 74LVX04 &lt;br /&gt;
:** 74LVX244 (Fairchild)&lt;br /&gt;
:** 74LVX245 (nicht von Reichelt, nicht 5V tolerant)&lt;br /&gt;
:** bei TI heissen die 74LVX... nur 74LV...&lt;br /&gt;
&lt;br /&gt;
:* 74HC4050 (bis 15 V Step-Down-Pegelwandlung laut Datenblatt, bei Reichelt in DIP und SO erhältlich)&lt;br /&gt;
:* MAX3373/MAX3375&lt;br /&gt;
:* NC7SZ08 oder andere aus derselben Serie. CMOS-Logik mit 5-V-toleranten Eingängen, recht flott und braucht dank SOT-23 auch wenig Platz auf der Platine&lt;br /&gt;
&lt;br /&gt;
* 5 V Open Collector auf 3,3-V-Eingang. Einfach einen Pull-Up hinzufügen (Pull-Up liegt auf 3,3 V). Nachteilig ist der relativ hohe Stromverbrauch bei LOW, die begrenzte Geschwindigkeit bei hochohmigen Pull-Ups und der relativ geringe Ausgangsstrom bei HIGH (abhängig vom Pull-Up).&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_oc_5-3.png]]&lt;br /&gt;
&lt;br /&gt;
* Spannungsteiler mit 680 Ω und 1 kΩ. Der Nachteil dieser Lösung ist der relativ hohe Stromverbrauch (~3mA), der relativ geringe Ausgangsstrom (mehr als 200..300 µA sollte man da nicht rausziehen) und die relativ geringe Geschwindigkeit (ca. 10 MHz).&lt;br /&gt;
&lt;br /&gt;
[[Datei:SPI level shifter with resistor divider.png|miniatur|rechts|fehlerhafter SPI-Takt nach Pegelwandler mit Widerstandsteiler (1,8/3,3 kΩ)&amp;lt;br /&amp;gt;unten: 5V-Ausgang am Mikrocontroller&amp;lt;br /&amp;gt;&lt;br /&gt;
oben: 3,3V-Eingang an der SD-Karte nach Pegelwandler]]&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_st_5-3.png]]&lt;br /&gt;
&lt;br /&gt;
* 1 kΩ Vorwiderstand. Dadurch wird der Strom vom 5-V-Ausgang in die 3,3-V-Versorgung durch die internen Schutzdioden auf ca. 1 mA begrenzt. Diese Lösung ist auch relativ langsam (ca. 5MHz). Ggf. kann man den Vorwiderstand auf 100 Ω reduzieren, das erhöht dann wieder die Geschwindigkeit. Aufpassen, einige ICs vertragen nur 1 mA oder weniger durch die Schutzdioden! Ausserdem muss man aufpassen, da jetzt von der 5-V-Seite Strom in die 3,3-V-Versorgung eingespeist wird. Besonders in Schaltungen mit sehr niedriger Stromaufnahme kann das zum Problem werden, wenn die Stromaufnahme geringer ist, als über die Vorwiderstände eingespeist wird. Dann nimmt es meist der Spannungsregler für 3,3 V übel wenn jemand „schiebt“, sprich, Strom einspeist. Denn die allermeisten Spannungsregler können nur Strom liefern (source), aber keinen Strom aufnehmen (sink). Es gibt 4-fach-Diodennetzwerke, die die internen Schutzdioden entlasten können (Schottkydioden mit kleinerer Flusspannung von ≈ 0,3 V als die internen Silizizumdioden mit ≈ 0,7 V), außerdem ist teilweise noch eine [[Diode#Z-Diode|Zenerdiode]] enthalten, die ggf. den überschüssig eingespeisten Strom aufnehmen kann.&lt;br /&gt;
&lt;br /&gt;
Alle Lösungen mit Vorwiderständen reduzieren die Flankensteilheit der Signale. Dies kann bei Takt- und Zähleingängen zu unerwünschten Schwingungen und damit Fehlzählungen führen. Derartig benutzte Eingänge sollten Schmitt-Trigger-Verhalten aufweisen.&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_vw_5-3.png]]&lt;br /&gt;
&lt;br /&gt;
Achtung: Mindestens für 74HC(T) Gatter ist dokumentiert (Philips 74HC/T High-Speed CMOS User Guide), dass auch schon geringer Strom durch die internen Schutzdioden zu einer unerwünschten Kopplung von Eingängen führen kann, d.h. der Strom fliesst zu einem anderen Eingang wieder hinaus. Sind also andere Eingänge ebenso hochohmig angeschlossen, kann dieser Querstrom zu Fehlfunktion führen.&lt;br /&gt;
&lt;br /&gt;
== BIDIREKTIONAL ==&lt;br /&gt;
&lt;br /&gt;
Für bidirektionale Busse gibt es spezielle Pegelwandler mit 2 Versorgungsspannungen. Allerdings brauchen die meist ein Signal zur Richtungsumschaltung. Auch muss man die Reihenfolge der Versorgungsspannungen beim Einschalten beachten. Aktive bidirektionale Pegelwandler OHNE Steuereingang zur Richtungsumschaltung sind mit Vorsicht zu genießen, denn die brauchen teilweise kurzzeitig einen relativ hohen Strom, um die Eingänge zu treiben.&lt;br /&gt;
&lt;br /&gt;
=== 5 V ⇔ 3,3 V ===&lt;br /&gt;
&lt;br /&gt;
* Wenn die 5-V-Seite TTL-kompatible Eingänge hat kann wieder der Spannungsteiler oder Vorwiderstand wie bei der unidirektionalen Anpassung verwendet werden (mit all seinen Vor- und Nachteilen).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* SN74CB3T3306&lt;br /&gt;
* SN74CBTD3861 (10 Bit,flow through, Betrieb mit 5 Volt)&lt;br /&gt;
* MAX1741 &lt;br /&gt;
* MAX3378E &lt;br /&gt;
* 74AHC126 s.u.&lt;br /&gt;
* ST2378 (bei CSD erhältlich, 3.5 eur, leider TSSOP)&lt;br /&gt;
* TXS0104E (TI: 4-BIT BIDIRECTIONAL VOLTAGE-LEVEL TRANSLATOR FOR OPEN-DRAIN AND PUSH-PULL APPLICATIONS)&lt;br /&gt;
* SN74LVC07A&lt;br /&gt;
* von Analog Devices die ADUM Serie&lt;br /&gt;
&lt;br /&gt;
=== 1,65 V ... 5,5 V ⇔ 1,65 V ... 5,5 V ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
* SN74LVC1T45&lt;br /&gt;
* SN74LVC2T45&lt;br /&gt;
* SN74LVC(H)8T245&lt;br /&gt;
* SN74LVC(H)16T245&lt;br /&gt;
&lt;br /&gt;
=== 1,2 V ... 3,6 V ⇔ 1,65V ... 5,5V ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
* TXB0101&lt;br /&gt;
* TXB0102&lt;br /&gt;
* TXB0104&lt;br /&gt;
* TXB0106&lt;br /&gt;
* TXB0108&lt;br /&gt;
&lt;br /&gt;
=== 1,2 V ... 3,6V ⇔ 1,2 V ... 3,6 V ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
* SN74AVC(H)1T45&lt;br /&gt;
* SN74AVC(H)2T45&lt;br /&gt;
* SN74AVC(H)4T245&lt;br /&gt;
* SN74AVC(H)8T245&lt;br /&gt;
* SN74AVC(H)16T245&lt;br /&gt;
* SN74AVC(H)20T245&lt;br /&gt;
* SN74AVC(H)24T245&lt;br /&gt;
* SN74AVC(H)32T245&lt;br /&gt;
&lt;br /&gt;
=== 1,5 V ... 3,6 V ⇔ 1,5 V ... 5,5 V ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
* 74LVC4245A&lt;br /&gt;
&lt;br /&gt;
== Mit galvanischer Trennung ==&lt;br /&gt;
&lt;br /&gt;
* [[Optokoppler]] (Langsam! Es gibt verschieden schnelle Koppler, aber über 1 MHz kommen sie kaum hinaus. Grundregel: Solche mit Fototransistoren sind am langsamsten, Richtwert 10 kHz, Fotodioden sind schneller, schnelle Optokoppler haben eine gesondert zu speisende Empfängerschaltung.)&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_opto.png]]&lt;br /&gt;
&lt;br /&gt;
* GMR-Koppler von der Firma NVE &lt;br /&gt;
* iCoupler Technologie von der Firma Analog Devices&lt;br /&gt;
* [[Kapazitiver Koppler]] (schnell, begrenzter Potenzialversatz)&lt;br /&gt;
* Transformatorkopplung (nur für gleichspannungsfreie Wechselsignale geeignet; sehr schnell; Beispiel: Netzkarten)&lt;br /&gt;
&lt;br /&gt;
Lit.: &#039;&#039;Galvanische Trennung: Optokoppler, GMR-Koppler oder iCoupler?&#039;&#039;, Siegfried W. Best, Redaktion elektronik industrie, [http://www.elektronik-industrie.de/ei/11,2003/article/2f0082f824c.html elektronik industrie 11-2003, S. 22ff.]&lt;br /&gt;
&lt;br /&gt;
== Praktische Beispiele ==&lt;br /&gt;
&lt;br /&gt;
=== Einfaches RS232-Interface ===&lt;br /&gt;
&lt;br /&gt;
[http://web.archive.org/web/20050122013618/http://www.henrik-reimers.de/control/rs232interface.gif Erfolgreicher Einsatz bis 19200 Baud und bis zu 10 m Leitungslänge]&lt;br /&gt;
Beschränkungen:&lt;br /&gt;
&lt;br /&gt;
* ggf. Platzbedarf&lt;br /&gt;
* Geschwindigkeit s.o.&lt;br /&gt;
&lt;br /&gt;
Beispiel: http://www.hagtech.com/pdf/translator.pdf&lt;br /&gt;
&lt;br /&gt;
=== [[I2C]]-Bus: gemeinsam 3.3V und 5V ===&lt;br /&gt;
&lt;br /&gt;
* [[MSP430]] an 3,3V/5V: http://www-s.ti.com/sc/psheets/slaa148/slaa148.pdf&lt;br /&gt;
&lt;br /&gt;
* Philips [http://www.nxp.com/documents/data_sheet/PCA9515.pdf PCA9515]: I2C Puffer mit Pegelwandlung. Der PCA9515 ist ein I2C-Bus Repeater, welcher I2C Busse mit verschiedenen Spannungen isoliert. Verfügbar bei Reichelt und DigiKey.&lt;br /&gt;
&lt;br /&gt;
* [http://ics.nxp.com/support/documents/interface/pdf/an97055.pdf Philips AN97055 Bi-directional level shifter for I²C-bus and other systems]&lt;br /&gt;
&lt;br /&gt;
* Bevor man ein Philips I2C Chip auswählt sollte man prüfen ob er verfügbar ist und auch das verfügbare Gehäuse wählen. Man sollte auch überlegen ob ein Puffer wirklich gebraucht wird. Wenn man echte I2C ICs mit 5V betreibt, dann sind die Eingänge vom Typ Schmitt Trigger CMOS (z.&amp;amp;nbsp;B. PCF8574). Dann müssen 3.3V Pegel auf 5V umgesetzt werden. Wenn man jedoch SMBUS Ics verwendet (z.&amp;amp;nbsp;B. ADT7461, Silabs 8051) dann sind die Schwellspannungen TTL kompatibel und es ist keine Anpassung notwendig. Für neue Pegelwandler sollte man hier nachschauen. http://www.bus-buffer.com&lt;br /&gt;
&lt;br /&gt;
* [http://www.edn.com/article/CA193193.html &amp;quot;Two-transistor circuit replaces IC&amp;quot;]. Für diese Anwendung kann ENABLE direkt mit 3.3V verbunden werden. Es ist eigentlich nur dazu da, den ICs &amp;quot;hot-swappable&amp;quot; zu machen (kann unter Spannung gesteckt und getrennt werden). Es geht sogar mit nur einem [[Transistor]] [http://www.mikrocontroller.net/topic/92447 siehe Forum]. Man sollte beachten, daß die Schaltung sowohl für SCL als auch SDA benötigt wird. &lt;br /&gt;
* Noch einfachere Lösungen mit nur einem MOSFET und zwei Pull-Up Widerständen pro Leitung sind in den folgenden Links zu finden. &lt;br /&gt;
** http://www.nxp.com/documents/application_note/AN10441.pdf&lt;br /&gt;
** http://www.semiconductors.philips.com/acrobat_download/literature/9398/39340011.pdf (Kapitel 18), bei der Berechnung der erreichbaren Geschwindigkeit dürfen die parasitären Kapazitäten der FETs nicht ignoriert werden&lt;br /&gt;
&lt;br /&gt;
=== Auswählbare Pegel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frage:&#039;&#039;&#039;&lt;br /&gt;
Ein CMOS Logikpegel zwischen 1,8V, 2,5V und 3,3V (abhängig von der Anwendung) muss auf 5V CMOS Logikpegel gewandelt werden. Es geht nur um diese Richtung mit maximal 8MHz. Es gibt die Stromversorgung für alle Pegel. Ein normaler Komparator wie LM311 ist nicht möglich, da er beim Betrieb mit 5V Versorgunsspannung erst ab 1V zu schalten anfängt. Meine Idee ist die Verwendung eines High Speed OPVs mit R2R Eingang, z.&amp;amp;nbsp;B. LMH6645.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;Antworten:&#039;&#039;&#039;&lt;br /&gt;
* Man könnte einen ultra-low threshold N-Kanal MOSFET nehmen und als Open Drain mit einem Pull-Up nach 5V betreiben, BSH103 könnte passen (Schwellspannung ~0,4V).&lt;br /&gt;
* High-Speed Single Supply Komparator wie z.&amp;amp;nbsp;B. [http://www-s.ti.com/sc/ds/tl712.pdf TL712] .&lt;br /&gt;
* SN74LVC1T45&lt;br /&gt;
* SN74LVC8T245&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frage:&#039;&#039;&#039;&lt;br /&gt;
Ich suchen einen IC, welcher eine Pegelwandlung von 3,3V nach 1,8V, 2,0V oder 5V ermöglicht und während des Betriebs umgeschaltet werden kann.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Antworten:&#039;&#039;&#039;&lt;br /&gt;
* So ein IC ist der Linear [http://www.linear.com/pc/productDetail.jsp?navId=H0,C1,C1007,C1071,P1601 LTC1555L-1.8] .&lt;br /&gt;
* SN74LVC1T45&lt;br /&gt;
* SN74LVC8T245&lt;br /&gt;
&lt;br /&gt;
=== AVR SPI (SDC/MMC)===&lt;br /&gt;
&lt;br /&gt;
Für &#039;&#039;&#039;bidirektionalen Betrieb&#039;&#039;&#039; zwischen 5V-AVR und 3,3V-Geräten und anders herum gibt es den Level-Translator &#039;&#039;&#039;MAX3378E&#039;&#039;&#039; von Maxim.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenrichtung am SPI im Zielsystem festgelegt ist, reichen &#039;&#039;&#039;unidirektionale Bausteine&#039;&#039;&#039;:&lt;br /&gt;
* 3x von 5V nach 3,3V und 1x von 3,3V nach 5V: &#039;&#039;&#039;MAX3392E&#039;&#039;&#039;&lt;br /&gt;
* 1x von 5V nach 3,3V und 3x von 3,3V nach 5V: &#039;&#039;&#039;MAX3390E&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Zum Anschließen einer SDC/MMC an einen 5V-AVR eignen sich somit der MAX3978E und der MAX3392E. Beide sind u.A. im winzigen TSSOP-14-Gehäuse verfügbar, nehmen sehr wenig Energie auf und eignen sich auch für andere Spannungen. Mit 3,3 und 5V beträgt die garantierte Übertragungsrate 8Mbps.&lt;br /&gt;
&lt;br /&gt;
* [http://datasheets.maxim-ic.com/en/ds/MAX3372E-MAX3393E.pdf Datenblatt]&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit zum Übersetzen zwischen 3,3 und 5V liegt in der Verwendung des &#039;&#039;&#039;74LVC245&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bastlerfreundlicher als &#039;&#039;&#039;MAX33XXX&#039;&#039;&#039; (in SO-Gehäuse):&lt;br /&gt;
* 5V &amp;gt; 3,3V (SCK, MOSI, CS): 74LVC-serie (z.B. 74LVC14A)&lt;br /&gt;
* 3,3V &amp;gt; 5V (MISO): 74HCT-Serie (z.B. 74HCT125, 74HCT251)&lt;br /&gt;
&lt;br /&gt;
5V-AVR an eine MMC (ohne Level-Shifter-Baustein):&lt;br /&gt;
* [http://www.microsyl.com/index.php/2010/03/24/led-sign-with-mmc-memory-card/ Projektseite] &lt;br /&gt;
* [http://www.microsyl.com/projects/ledsign/ledsign1.pdf Schaltplan]&lt;br /&gt;
&lt;br /&gt;
=== Mikrocontroller ⇔ Parallelport ([[ISP]]-Dongle, [[JTAG]] Wiggler, ...) ===&lt;br /&gt;
&lt;br /&gt;
Dieser Schaltplan funktioniert auch bei 3,3 V wenn man einen 74&amp;lt;B&amp;gt;HC&amp;lt;/B&amp;gt;244 anstatt eines 74&amp;lt;B&amp;gt;LS&amp;lt;/B&amp;gt;244 verwendet: [http://www.epanorama.net/circuits/parallel_output.html Parallel port interfacing made easy: Simple circuits and programs to show how to use PC parallel port output capabilities].&lt;br /&gt;
&lt;br /&gt;
=== Doppeltes Leitungspaar RX/TX 5V/3,3V ===&lt;br /&gt;
&lt;br /&gt;
Der [http://www.hackaday.com/2008/06/19/sparkfuns-logic-level-converter/ SparkFun&#039;s Logic Level Converter] ist eine Baugruppe mit MOSFETs [http://www.fairchildsemi.com/pf/BS/BSS138.html BSS138] für die Pegelwandlung von 5V auf 3,3V. 5V/2,8V und 5V/1,8V sind ebenfalls machbar.&lt;br /&gt;
&lt;br /&gt;
=== Steuerleitung zwischen Mikrocontroller und FPGA ===&lt;br /&gt;
&lt;br /&gt;
Oftmals werden PLDs oder FPGAs per Microcontroller-Platine angesteuert. Ältere Typen laufen meist als 5V oder sitzen in 5V-kompatiblen Platinen. Sollen moderne FPGAs angesteuert werden, trifft man fast immer auf 3,3-V-Typen, bzw. muss sogar 2,5-V- / 1,8-V-Bänke beschalten, wenn nur noch dort Pins frei sind.&lt;br /&gt;
&lt;br /&gt;
==== Mikrocontroller ⇒ FPGA ====&lt;br /&gt;
&lt;br /&gt;
Die 5 V sind also im Extremfall auf 1,8 V herabzusetzen, was bei einem maximal zulässigen Diodenstrom von 3 mA (Beispiel Xilinx) einen Mindestwiderstand von ca. 1 kΩ erfordert. Die resultierende maximale Schaltfrequenz liegt dann bei einem typischen FPGA-Eingang bei etwa &amp;lt; 500 kHz. Soll der Eingang aus Belastungsgründen nicht mit mehr als 0,3 mA belastet werden, müsste der Widerstand auf 10 kΩ steigen, wodurch die Frequenz auf 1/10 sinkt. Zudem ist der Eingang dann störempfindlicher. Daher ist es besser, man schaltet dem Eingang eine zusätzliche Z-Diode bei und dimensioniert den Vorwiderstand so, dass die Strombelastbarkeit des Mikrocontrollers ausgelastet wird.&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit ist es, die Spannung mit einer Diode in Vorwärtsrichtung herabzusetzen. Dann muss jedoch der Vorwiderstand noch exakter toleriert werden und auch Abweichungen der Spannung (Welligkeit) berücksichtigt werden.&lt;br /&gt;
&lt;br /&gt;
==== FPGA ⇒ Mikrocontroller ====&lt;br /&gt;
&lt;br /&gt;
Umgekehrt ist es oft nötig, dass Bausteine einen fremden Chip treiben müssen, dessen Eingang bereits mit einem Pull-Up versehen ist. Über diesen wird dann stets ein Strom in die Schutzdiode eingeprägt, auch wenn der Ausgang auf HIGH geht. Soll z. B. von einem PLD oder einem FPGA aus eine Mikrocontrollerplatine bedient werden, die über einen Pull-Up von 1 kΩ verfügt, würden immer ca. 1 mA in die Schutzdiode eingeprägt. Hier kann eine Seriendiode helfen, die Spannung genügend herabzusetzen, um den Ausgang zu schützen und dennoch die Funktion zu erhalten. Dann steuert ein LOW-Ausgang den Eingang auf geschätzte 1V, was aber meistens für das Erkennen von LOW noch sicher reicht.&lt;br /&gt;
&lt;br /&gt;
== Bauteile ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;74ALVC164245&#039;&#039;&#039; - &#039;&#039;16bit dual supply translating transceiver&#039;&#039;. Eine Seite von 1.5V bis 3.6V, die andere von 1.5 bis 5.5V.&lt;br /&gt;
* &#039;&#039;&#039;74LVX573&#039;&#039;&#039; (unidirektional, Latch, nicht alle Hersteller bauen diesen 5V tolerant!)&lt;br /&gt;
* &#039;&#039;&#039;74LVX245&#039;&#039;&#039; (bidirektional, nicht alle Hersteller bauen diesen 5V tolerant!)&lt;br /&gt;
* &#039;&#039;&#039;74LVX125&#039;&#039;&#039; - &#039;&#039;Low Voltage Quad Buffer with 3-STATE Outputs&#039;&#039;. http://www.fairchildsemi.com/pf/74/74LVX125.html&lt;br /&gt;
* &#039;&#039;&#039;SN74LVC2T45&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;SN74LVC8T245&#039;&#039;&#039; - &#039;&#039;8-Bit Dual-Supply Bus Transceiver with Configurable Voltage Translation and Three-State Outputs&#039;&#039;. http://focus.ti.com/docs/prod/folders/print/sn74lvc8t245.html&lt;br /&gt;
* &#039;&#039;&#039;74LCX244MSA&#039;&#039;&#039; von Fairchild.&lt;br /&gt;
* &#039;&#039;&#039;MAX3377&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAX3000&#039;&#039;&#039; 8-Kanal bidirektioneler Pegelwandler ohne Richtungsumschaltung&lt;br /&gt;
* &#039;&#039;&#039;ADG3308&#039;&#039;&#039; 8-Kanal bi-dir. Pegelwandler ohne Richtungsumschaltung, 1,15V..5,5V, 50MBps (hohe Umschaltströme beachten)&lt;br /&gt;
&lt;br /&gt;
Vierfachdioden im kleinen 6-poligen SMD-Gehäuse:&lt;br /&gt;
* [http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/CD00130230.pdf DSILC6-4xx.pdf]&lt;br /&gt;
* [http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/CD00065974.pdf DVIULC6-4SC6.pdf]&lt;br /&gt;
* [http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/CD00001734.pdf DALC208.pdf]&lt;br /&gt;
* [http://www.diodes.com/datasheets/ds30195.pdf QSBT40, vierfach Schottky Terminator für Datenleitungen]&lt;br /&gt;
* [http://www.littlefuse.com/data/en/Data_Sheets/SP724Lead_Free.pdf SP724, Siliziumschutzarray]&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/307702#3316500 Forumsbeitrag]: Entkopplung von FT232 und AVR&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* Holmes D., [http://delphys.net/d.holmes/hardware/levelshift.html Bi-directional level-shift with MOSFETs]&lt;br /&gt;
* Gaurang Kavaiya, [http://www.edn.com/design/analog/4318916/Don-t-pay-for-level-translators-in-systems-using-multiple-power-supply-voltages Don’t pay for level translators in systems using multiple power-supply voltages], EDN, MAY 25, 2006, 81-86&lt;br /&gt;
* [http://www.elektronik-kompendium.de/public/schaerer/scf3_lc.htm Einfacher Pegelwandler im ELKO]&lt;br /&gt;
* [http://www.prog-link.com/dcf77/dcf77-17.html Pegelwandler für DFC77 Module]&lt;br /&gt;
* [http://elektronik.kai-uwe-schmidt.de/index.php?page=mp3_blueschaltung Pegelwandler für [[I2C]] Bus in einem MP3 Player]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/256452/levelshifter.pdf Application Note von Philips, I2C Pegelwandler]&lt;br /&gt;
* [http://www.nxp.com/documents/user_manual/UM10204.pdf I2C Spezifikation]  &lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-1-234277.html#new Forumsbeitrag zum Thema 1,8V-5V Pegelwandler] &lt;br /&gt;
* [http://www.st.com/web/en/resource/technical/document/datasheet/CD00001208.pdf 74LCX16245, 16 Bit Pegelwandler]&lt;br /&gt;
* [http://www.standardics.nxp.com/products/lvc/buffers/ LVC Logikfamilie]&lt;br /&gt;
* [http://www.standardics.nxp.com/products/lvc/transceivers/ LVC Tranceiver]&lt;br /&gt;
* [http://www.microchip.com/stellent/groups/techpub_sg/documents/devicedoc/en026368.pdf 3V Tips ‘n Tricks] (PDF) von Microchip&lt;br /&gt;
* [http://www.ti.com/lit/an/slaa148/slaa148.pdf Interfacing the 3-V MSP430 to 5-V Circuits] (PDF) von Texas Instruments&lt;br /&gt;
* [http://www.ti.com/logic-circuit/voltage-level-translation/overview.html] &amp;quot;Texas Instruments Voltage level translators&amp;quot;: Auswahl passender Bauelemente Anhand von Parametern &lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Energieerzeugung_und_Speicherung&amp;diff=101706</id>
		<title>Energieerzeugung und Speicherung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Energieerzeugung_und_Speicherung&amp;diff=101706"/>
		<updated>2020-03-19T07:23:59Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: aufteilung in Speicher- und Laufkraftwerk&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In vielen Artikeln hier im Forum wird Bezug genommen auf Konzepte zur Erzeugung und Speicherung von elektrischer Energie, hauptsächlich im Zusammenhang mit erneuerbaren Energien und der (Auto-)Mobilität. &lt;br /&gt;
&lt;br /&gt;
Dieser Artikel soll bekannte Konzepte sammeln, um technisch interessierten Lesern einen Überblick gerade auch über neue Ideen zu geben, andererseits sollen ohne Streit - also möglichst wertfrei - jeweils die hauptsächlichen Für- und Wider- Argumente aufgelistet werden und, soweit vorhanden, auf Diskussionen im Forum verwiesen werden.&lt;br /&gt;
&lt;br /&gt;
ACHTUNG Dieser Artikel ist mein erster Versuch; als Entwurf zu verstehen und als Einladung, mitzuwirken. Ich hab mir vorgenommen, immer mal ein bisschen zu ergänzen. &lt;br /&gt;
&lt;br /&gt;
= Energiespeicherung =&lt;br /&gt;
(z.B. für Überkapazitäten in der Stromerzeugung)&lt;br /&gt;
	&lt;br /&gt;
== Ideen für die Zukunft ==&lt;br /&gt;
	&lt;br /&gt;
=== Batteriespeicher (Muster) ===&lt;br /&gt;
&lt;br /&gt;
Viele Akkus an einem Ort konzentriert&lt;br /&gt;
&lt;br /&gt;
Redox-Flow-Batterien&lt;br /&gt;
&lt;br /&gt;
Platzhalter pro - bitte nur kurz und dann auf weitere Infos verlinken&lt;br /&gt;
&lt;br /&gt;
Platzhalter contra - bitte nur kurz und dann auf weitere Infos verlinken&lt;br /&gt;
&lt;br /&gt;
Platzhalter Links zu Artikeln hier im Forum und anderswo&lt;br /&gt;
&lt;br /&gt;
=== wasserstoffbasiertes Speicherkraftwerk ===&lt;br /&gt;
&lt;br /&gt;
https://www.berliner-zeitung.de/zukunft-technologie/wasserstoffbasiertes-speicherkraftwerk-schwarze-pumpe-soll-in-lausitz-entstehen-li.3207&lt;br /&gt;
&lt;br /&gt;
https://www.mikrocontroller.net/topic/486237&lt;br /&gt;
&lt;br /&gt;
=== neuartige &amp;quot;Batterien&amp;quot; (Akkus) ===&lt;br /&gt;
&amp;quot;Quantenbatterie&amp;quot; -&amp;gt; http://www.ps-blnkd.de/Regenerative_Energiegewinnung.pdf, Abschnitt &amp;quot;Speichertechnologie&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Wasserstofftank - Speicherung als Metallhydrat ===&lt;br /&gt;
&lt;br /&gt;
https://www.hzg.de/public_relations_media/news/077973/index.php.de&lt;br /&gt;
&lt;br /&gt;
=== Wasserstoff-Speicherung in Flüssigkeiten ===&lt;br /&gt;
&lt;br /&gt;
https://www.nzz.ch/wissen/wissenschaft/wasserstoffspeicher-fuer-den-hausgebrauch-1.17997973&lt;br /&gt;
&lt;br /&gt;
https://blogs.fz-juelich.de/llec/2019/12/03/neuartiger-lohc-wasserstoffspeicher/&lt;br /&gt;
&lt;br /&gt;
=== Komprimierte / verflüssigte Luft ===&lt;br /&gt;
&lt;br /&gt;
https://www.ingenieur.de/technik/fachbereiche/energie/erste-grossanlage-speichert-windstrom-in-fluessiger-luft/&lt;br /&gt;
&lt;br /&gt;
=== Beton-Stapelturm ===&lt;br /&gt;
&lt;br /&gt;
https://www.srf.ch/kultur/wissen/neue-super-batterie-kann-dieser-turm-unsere-energiezukunft-sichern&lt;br /&gt;
https://energyvault.com/&lt;br /&gt;
&lt;br /&gt;
=== Schwungrad-Speicher ===&lt;br /&gt;
&lt;br /&gt;
https://de.wikipedia.org/wiki/Schwungrad-Speicherkraftwerk&lt;br /&gt;
&lt;br /&gt;
=== Supraleitende Magnetische Energiespeicher (SMES) ===&lt;br /&gt;
&lt;br /&gt;
== aktuell im Betrieb ==&lt;br /&gt;
&lt;br /&gt;
=== Pumpspeicherkraftwerk ===&lt;br /&gt;
&lt;br /&gt;
https://de.wikipedia.org/wiki/Pumpspeicherkraftwerk&lt;br /&gt;
	&lt;br /&gt;
=== Heißluft-Speicher ===&lt;br /&gt;
&lt;br /&gt;
https://bizz-energy.com/stein_stromspeicher_von_siemens_gamesa_soll_2019_ans_netz&lt;br /&gt;
&lt;br /&gt;
https://www.mikrocontroller.net/topic/476188#new&lt;br /&gt;
&lt;br /&gt;
= Energieerzeugung =&lt;br /&gt;
(genauer: Umwandlung anderer Energieformen in Elektroenergie)&lt;br /&gt;
&lt;br /&gt;
Eine Übersicht dazu kann man hier nachlesen: -&amp;gt; http://www.ps-blnkd.de/Regenerative_Energiegewinnung.pdf (Bilder sind aus Gründen des Urheberrechts nicht dabei)&lt;br /&gt;
&lt;br /&gt;
== Ideen für die Zukunft ==&lt;br /&gt;
	&lt;br /&gt;
=== Modell-Flugzeug auf dem Meer und power to gas ===&lt;br /&gt;
&lt;br /&gt;
Ein Flugzeug am Seil treibt auf einem Schiff einen Generator an&lt;br /&gt;
&lt;br /&gt;
https://www.enerkite.de/&lt;br /&gt;
&lt;br /&gt;
https://www.3sat.de/wissen/nano/im-hoehenflug-100.html&lt;br /&gt;
&lt;br /&gt;
=== Kernfusion (ITER usw.) ===&lt;br /&gt;
	&lt;br /&gt;
=== Dual-Fluid-Reaktor (erhoffte Lösung der Endlagerproblematik von Kernbrennstäben) ===&lt;br /&gt;
Pro: Wenn er funktioniert wie er soll, dann kann er über die Zeit abgebrannte Brennstäbe aus alten AKW verwerten und dabei praktisch unbegrenzt Energie gewinnen.&lt;br /&gt;
Die Reststoffe wären nach ca. 300 Jahren zum grössten Teil harmlos, was eine grosse Verbesserung zum heutigen Müll darstellt, der praktisch ewig strahlen wird.&lt;br /&gt;
&lt;br /&gt;
Contra: In Deutschland kann man z Zt. derartige Konzepte nicht testen oder wirtschaftlich betreiben.&lt;br /&gt;
Ungeklärte Materialfragen. Ungeklärte Finanzierung.&lt;br /&gt;
&lt;br /&gt;
Fazit: Wenn man nie anfängt, wird man nie fertig.&lt;br /&gt;
 &lt;br /&gt;
https://dual-fluid-reaktor.de/&lt;br /&gt;
&lt;br /&gt;
== aktuell im Betrieb ==&lt;br /&gt;
	&lt;br /&gt;
=== Kohlekraftwerk ===&lt;br /&gt;
&lt;br /&gt;
=== Gaskraftwerk ===&lt;br /&gt;
&lt;br /&gt;
=== übliche Kernkraftwerke ===&lt;br /&gt;
Pro:&lt;br /&gt;
Liefern grosse Mengen Energie planbar und Grundlastfähig.&lt;br /&gt;
Keine CO2 Emission im Betrieb, geringe Mengen an nuklearem Müll, verglichen mit tausenden von Tonnen Asche aus Kohlekraftwerken.&lt;br /&gt;
Kosten für den Spaltstoff/Betrieb relativ gering. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Contra: &lt;br /&gt;
Die derzeitigen LWR können nur 3% des eingesetzten Materials verbrauchen, brüten dabei unvermeidlich schlecht spaltbare Aktinide wie PU240, PU241,usw., und fliegen von Zeit zu Zeit in die Luft.&lt;br /&gt;
Hohe politische und Sicherheitskosten. &lt;br /&gt;
Ungeklärte Entsorgungsfrage für hochradioaktive abgebrannte Brennstäbe.&lt;br /&gt;
&lt;br /&gt;
Hervorgegangen aus der politischen Lage des &amp;quot;Kalten Krieges&amp;quot; sind sie technisch besser dazu zu gebrauchen, Atomwaffen zu erbrüten, als sicher Energie zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Viele Länder haben ihre Neubaupläne auf Eis gelegt, um die neuen Entwicklungen der Reaktortechnologie abzuwarten.&lt;br /&gt;
&lt;br /&gt;
Fazit: Es ist himmerlschreiender Schwachsinn, gut funktionierende AKW ausgerechnet durch Strom aus Braunkohle zu ersetzen, aber man sollte die auch nicht für die Ewigkeit einplanen.&lt;br /&gt;
&lt;br /&gt;
=== Wasserkraft ===&lt;br /&gt;
&lt;br /&gt;
==== [https://de.wikipedia.org/wiki/Speicherkraftwerk_(Wasser) Speicherkraftwerk (Wasser)] ====&lt;br /&gt;
&lt;br /&gt;
Pro:&lt;br /&gt;
Gut Planbar, schnell verfügbar und bis zu einem gewissen Grad Grundlastfähig.&lt;br /&gt;
Gute Ergänzung zu Windkraft.&lt;br /&gt;
&lt;br /&gt;
Contra: &lt;br /&gt;
Grosser Flächenverbrauch, bei den derzeitigen langen Dürren Versorgungsprobleme, in Deutschland gibt es nur sehr wenige mögliche Standorte.&lt;br /&gt;
&lt;br /&gt;
Fazit: Wenn man sie hat, dann ist die Energiewende kein Problem. &lt;br /&gt;
Deutschland hat zu wenig und kann keine mehr bauen. Tja, schade.&lt;br /&gt;
&lt;br /&gt;
==== [https://de.wikipedia.org/wiki/Laufwasserkraftwerk Laufwasserkraftwerk (Laufkraftwerk, Flusskraftwerk)] ====&lt;br /&gt;
&lt;br /&gt;
Pro:&lt;br /&gt;
Grundlastfähig, CO2 neutral&lt;br /&gt;
&lt;br /&gt;
Contra:&lt;br /&gt;
Für den Schiffsverkehr werden Schleusen benötigt und für Fische Fischtreppen. Natürlicher Flusslauf wird durch Wehr eingeschränkt.&lt;br /&gt;
Die Kapazität ist begrenzt durch die vorhandenen Flüsse.&lt;br /&gt;
&lt;br /&gt;
Fazit:&lt;br /&gt;
Gute, relativ umweltfreundliche Energiequelle&lt;br /&gt;
&lt;br /&gt;
=== Geothermie ===&lt;br /&gt;
	&lt;br /&gt;
=== Photovoltaik ===&lt;br /&gt;
Pro: Liefert Strom am Tag, wenn der Strombedarf hoch ist. Gut Planbar. &lt;br /&gt;
&lt;br /&gt;
Contra: Bei Dunkelheit keine Leistung. Besonders im Winter, wenn der Strombedarf hoch ist, ist die Leistung besonders kurz und niedrig.&lt;br /&gt;
&lt;br /&gt;
Fazit: Speicherung oder zusätzliche Stromquellen als Puffer erforderlich.&lt;br /&gt;
&lt;br /&gt;
=== Windkraft ===&lt;br /&gt;
Pro: Liefert günstig Strom, auch in der Nacht. Ergänzt hervorragend Wasserkraftwerke.&lt;br /&gt;
&lt;br /&gt;
Contra: Liefert nur Strom, wenn der Wind weht. Bei zuviel Wind entsteht Strom-Müll, der teuer entsorgt werden muss, wenn nicht die Windräder abgestellt werden oder der Strom gespeichert wird.&lt;br /&gt;
Erzeugt Infraschall. Tötet Vögel, Insekten und Fledermäuse.&lt;br /&gt;
Stromerzeugung ist nicht planbar und kann nicht als &amp;quot;gesicherte Leistung&amp;quot; gelten.&lt;br /&gt;
Sinnlos ohne Wasserkraftwerke oder Speicher.&lt;br /&gt;
&lt;br /&gt;
Fazit: Speicherung oder zusätzliche Stromquellen als Puffer erforderlich.&lt;br /&gt;
&lt;br /&gt;
=== Sonnenwärmekraftwerk ===&lt;br /&gt;
&lt;br /&gt;
Beim Parabolrinnen-Kraftwerk fließt Öl durch eine Leitung in der Brennlinie von Parabolrinnen-Spiegeln.&lt;br /&gt;
Beim Turm-Kraftwerk erhitzen viele Spiegel einen Receiver mit einem Wärmetauscher.&lt;br /&gt;
&lt;br /&gt;
https://de.wikipedia.org/wiki/Sonnenw%C3%A4rmekraftwerk&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=%C3%9Cbersicht_Funkmodule&amp;diff=100702</id>
		<title>Übersicht Funkmodule</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=%C3%9Cbersicht_Funkmodule&amp;diff=100702"/>
		<updated>2019-06-30T08:55:43Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* LoRa RFM95W, RFM96W, RFM97W, RFM98W */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Diese Seite bietet eine Übersicht über Funkmodule für Elektronikprojekte. Ergänzungen sind willkommen!&lt;br /&gt;
&lt;br /&gt;
== Übersicht ==&lt;br /&gt;
&lt;br /&gt;
=== Module ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! !! [[RFM12|RFM12B]] !! [[RFM69|RFM69(H)(C)W]] !! [[NRF24L01_Tutorial|nRF24L01+]] !! [[ESP8266]] !! EMW3162&lt;br /&gt;
|-&lt;br /&gt;
| aktiv (Stand: Feb 2017)|| ja || ja || ja || ja || ja&lt;br /&gt;
|-&lt;br /&gt;
| Frequenz || 433, 868 Mhz || 315, 433, 868, 915 Mhz || 2,4 Ghz || colspan=&amp;quot;2&amp;quot;  style=&amp;quot;text-align:center&amp;quot; | 2,4 Ghz (WLAN 802.11b/g/n)&lt;br /&gt;
|-&lt;br /&gt;
| Vcc || 2,2 - 3,8 V || 1,8 - 3,6 V || 1,9 - 3,6 V || 3,3 V || 3,3V&lt;br /&gt;
|-&lt;br /&gt;
| Max. Datenrate || 115,2 kbit/s || 300 kbit/s || 2 Mbit/s || 4,5-7 Mbit/s &amp;lt;ref&amp;gt;http://www.mikrocontroller.net/articles/ESP8266#Datendurchsatz.2FPerformanz&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;http://www.mikrocontroller.net/topic/342240?page=single#3857630&amp;lt;/ref&amp;gt; || bis 20 Mbit/s&amp;lt;ref&amp;gt;http://www.seeedstudio.com/depot/EMW3162-WiFi-Module-p-2122.html&amp;lt;/ref&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Min. Datenrate || 1,2 kbit/s || 1,2 kbit/s || 0,25 Mbit/s || colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | ? Mbit/s&lt;br /&gt;
|-&lt;br /&gt;
| Stromaufnahme (TX/RX/Sleep) || 22 mA/11 mA/0,3 µA || 45 mA/16 mA/0,1 µA&amp;lt;br&amp;gt;130 mA/16 mA/0,1 µA (HCW) || 11,3 mA/13,3 mA/0,9 µA&amp;lt;br&amp;gt;115 mA/13,3 mA/0,9 µA (PA) || 215 mA/60 mA/0,9 µA || 24mA (20kbit/s), 320mA (max) / 52-59mA / 7mA (connected), 200 µA (sleep), 2µA (off)&lt;br /&gt;
|-&lt;br /&gt;
| Sendeleistung || +5 dBm || +13 dBm (~20 mW !)&amp;lt;br /&amp;gt;+20 dBm (HCW) || 0 dBm&amp;lt;br /&amp;gt;+20 dBm (mit PA) || +15 dBm (802.11g/n)&amp;lt;br /&amp;gt;+18,5 dBm (802.11b) || +14,5 dBm (802.11n)&amp;lt;br /&amp;gt;+15,5 dBm (802.11g)&amp;lt;br /&amp;gt;+18,5 dBm (802.11b)&lt;br /&gt;
|- &lt;br /&gt;
| Empfindlichkeit || -105 dBm || -120 dBm || -94 dBm || -98 dBm (802.11b)&amp;lt;br /&amp;gt;-93 dBm (802.11g) || -96 dBm (802.11b)&amp;lt;br /&amp;gt;-90 dBm (802.11g)&amp;lt;br /&amp;gt;-89 dBm (802.11n)&lt;br /&gt;
|-&lt;br /&gt;
| Reichweite || + || ++ || - || - || -&lt;br /&gt;
|-&lt;br /&gt;
| Paketmanagement || keines || Prüfsumme, Adresse, Verschlüsslung || Prüfsumme, Adresse, Retransmit || IPv4&amp;lt;br&amp;gt;(TCP und UDP) || IPv4 und IPv6&amp;lt;br&amp;gt;(TCP und UDP)&lt;br /&gt;
|-&lt;br /&gt;
| Verschlüsselung || - || ○ (AES, nur ECB &amp;lt;ref&amp;gt;https://de.wikipedia.org/wiki/Electronic_Code_Book_Mode&amp;lt;/ref&amp;gt; ) || - || colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | + (WLAN Verschlüsslung)&lt;br /&gt;
|-&lt;br /&gt;
| FIFO Size || 16 Bit || 66 Bytes || 3 separate 32 bytes TX and RX FIFOs || ? || ?&lt;br /&gt;
|-&lt;br /&gt;
| Support im Forum || ++ || ○ || ++ || + || ○&lt;br /&gt;
|-&lt;br /&gt;
| einfache Lib (Prüfsumme, Sender, Retransmit) || ? || ja, siehe Links || ja, siehe Links || Firmware buggy (01.2015) || WICED, UART Firmware&lt;br /&gt;
|-&lt;br /&gt;
|Preis (02.2017) || 2-4 € || 2-4 € || 0,7-2 € || 1-2 € || 8-10€&lt;br /&gt;
|-&lt;br /&gt;
|Verfügbarkeit (02.2017) || ++ || + || ++ || ++ || +&lt;br /&gt;
|-&lt;br /&gt;
|Bezugsquellen (02.2017) || [http://www.pollin.de/shop/suchergebnis.html?S_TEXT=RFM12B Pollin], ebay, Ali || [http://www.pollin.de/shop/suchergebnis.html?S_TEXT=RFM69 Pollin], Ali, ebay || ebay, Ali || ebay, Ali, [http://www.amazon.de/s/url=search-alias%3Daps&amp;amp;field-keywords=esp8266ex Amazon] || Ali, [http://www.seeedstudio.com/depot/EMW3162-WiFi-Module-p-2122.html Seedstudio]&lt;br /&gt;
|-&lt;br /&gt;
|Tauglichkeit SmartHome || + || ++ || ○ || ○ || ○&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Frequenzen ===&lt;br /&gt;
&lt;br /&gt;
siehe auch [[Allgemeinzuteilung]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! !! 433 Mhz !! 868 Mhz !! 2400 Mhz (2,4 Ghz)&lt;br /&gt;
|-&lt;br /&gt;
| Sendeleistung || 10 mW ERP || 10-500 mW ERP|| 10 mW ERP&amp;lt;br /&amp;gt;100 mW EIRP&lt;br /&gt;
|-&lt;br /&gt;
| Duty Cycle || keinen || 0,1-10 % || keinen&lt;br /&gt;
|-&lt;br /&gt;
| Reichweite || ++ || + || - &lt;br /&gt;
|-&lt;br /&gt;
| Störung durch andere || + || ○ || ++ &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Details zu den Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== RFM12B ===&lt;br /&gt;
&lt;br /&gt;
Die RFM12(B) sind wohl die Urgesteine der drahtlosen Kommunikation: Tausendfach erprobt!&lt;br /&gt;
&lt;br /&gt;
* [[AVR RFM12]]&lt;br /&gt;
* [[RFM12 Protokoll Stack]]&lt;br /&gt;
* [[RF_SOAP]]&lt;br /&gt;
* [[Pollin_Funk-AVR-Evaluationsboard]]&lt;br /&gt;
* [http://www.mikrocontroller-elektronik.de/funkmodul-programmierung-rfm12b-rn-mikrofunk/ RN-MikroFunk AVR Miniatur Evaluationsboard und Tutorial]&lt;br /&gt;
&lt;br /&gt;
=== RFM69 ===&lt;br /&gt;
&lt;br /&gt;
Der RFM69 ist der Nachfolger des RFM12B. Er untersützt mehr Frequenzen und hat eine leicht höhere Sendeleistung. Die größten Neuerungen sind die integrierte AES-Verschlüsselung sowie das eingebaute Paketmanagement, das sich um Prüfsummen, Adressen kümmert.&lt;br /&gt;
&lt;br /&gt;
Es gibt folgende Module:&lt;br /&gt;
* RFM69HCW, mit zusätzlicher PA, (+20 dBm TX)&lt;br /&gt;
* RFM69HW, mit zusätzlicher PA, (+20 dBm TX)&lt;br /&gt;
* RFM69CW, Pinkompatibel zum RFM12B&lt;br /&gt;
* RFM69W&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://github.com/LowPowerLab/RFM69 RFM69 Lib zur Ansteuerung mit Arduino]&lt;br /&gt;
* [http://www.airspayce.com/mikem/arduino/RadioHead/index.html RadioHead Lib zum Aufbau eines MESH-Netzwerkes]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/332579 Thread im Forum: Wer verwendet RFM69]&lt;br /&gt;
&lt;br /&gt;
=== LoRa RFM95W, RFM96W, RFM97W, RFM98W ===&lt;br /&gt;
&lt;br /&gt;
Diese [https://en.wikipedia.org/wiki/LoRa LoRa (Long Range)] Module von HopeRF unterstützen das LoRa Protokoll. Sie unterscheiden sich hauptsächlich im unterstützen Frequenzbereich:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Typ !! 169 MHz !! 315 MHz !! 433 MHz !! 868 MHz !! 915 MHz&lt;br /&gt;
|-&lt;br /&gt;
| RFM95W-868S2 ||   ||   ||   || x ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM95W-915S2 ||   ||   ||   ||   || x&lt;br /&gt;
|-&lt;br /&gt;
| RFM96W-315S2 ||   || x ||   ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM96W-433S2 ||   ||   || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM97W-868S2 ||   ||   ||   || x || &lt;br /&gt;
|-&lt;br /&gt;
| RFM97W-915S2 ||   ||   ||   ||   || x&lt;br /&gt;
|-&lt;br /&gt;
| RFM98W-315S2 || x || x ||   ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM98W-433S2 || x ||   || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| SX1278       || x || x || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| SX1276       || x || x || x || x || x&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die aufgeführten RFM9x Module enthalten jeweils entweder den SX1278 oder den SX1276 Chip.&lt;br /&gt;
Das &amp;quot;W&amp;quot; am Ende der Bezeichnung sagt nur aus, dass es sich um die International verwendbare Variante handelt und nicht um eine Vorabversion oder nur für den chinesischen Markt gedachte Variante.&lt;br /&gt;
Es gibt noch weitere Module z.B für eine größere Reichweite (+27 anstatt +20 dBm output power).&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/453948 Thread im Forum: Unterschiede RFM96W RFM98W]&lt;br /&gt;
* [https://www.hoperf.com/modules/lora/index.html Übersicht aller LoRa Module bei HopeRF]&lt;br /&gt;
* [https://www.thethingsnetwork.org/#metrics LoRaWAN The Things Network: LoRaWAN Gateways]&lt;br /&gt;
* [https://platformio.org/lib/show/124/RadioHead/examples?file=rf95_server.pde RadioHead Library example]&lt;br /&gt;
&lt;br /&gt;
=== nRF24L01+ ===&lt;br /&gt;
&lt;br /&gt;
Die wohl billigsten Funkmodule. Funken im 2,4 Ghz Band und haben alles in Hardware integriert für eine einfache Kommunikation. Gibt es schon länger am Markt, daher gibt es schon viele erprobte Bibliotheken für die Ansteuerung vom Arduino, RaspberryPi, ...&lt;br /&gt;
&lt;br /&gt;
Achtung! Nicht mit nRF24L01 (ohne Plus) verwechseln. Diese Module werden nicht mehr hergestellt und sind veraltet!&lt;br /&gt;
&lt;br /&gt;
* [http://tmrh20.github.io/RF24/ Bibliothek für die Ansteuerung über Arduino, RaspberryPi, Intel Galileo...]&lt;br /&gt;
* [http://tmrh20.github.io/RF24Network/ Bibliothek für eine ZigBee ähnliche Kommunikation (Mesh Nettwerk)]&lt;br /&gt;
&lt;br /&gt;
=== SE8R01 ===&lt;br /&gt;
&lt;br /&gt;
Wird bei AliExpress und E-Bay auch als simular nRF24L01+ angeboten. Er kostet die Hälfte vom nRF24L01+ und hat eine sehr kleine Bauform. Er ist NICHT kompatibel zum nRF24L01+ weder auf dem Funkkanal noch im Umgang mit den Konfigurationsregistern.&lt;br /&gt;
Die bislang brauchbarsten Informationen zu den Chip gab es unter [https://forum.arduino.cc/index.php?topic=366294.0 Finally Working code for se8r01(similar nrf24l01) 2mps, 1mps and 500kps]&lt;br /&gt;
Es scheint eine Datasheet V2.1 zu geben, ich habe es aber noch nirgends downloaden können.&lt;br /&gt;
&lt;br /&gt;
[[Benutzer:Prediger|Prediger]] ([[Benutzer Diskussion:Prediger|Diskussion]]) &lt;br /&gt;
&lt;br /&gt;
=== nRF52 ===&lt;br /&gt;
Die [https://www.nordicsemi.com/eng/Products/Bluetooth-Smart-Bluetooth-low-energy/nRF52832#Overview nRF52 SoCs] können für Bluetooth und für Übertragungen auf Basis des proprietären 2.4 GHZ-Nordic-Protokolls (kompatibel zu nRF24) genutzt werden. Für Bluetooth 4.1-Übertragungen steht ein Software Stack ([https://www.nordicsemi.com/eng/nordic/download_resource/33863/4/14045603 Nordic Softdevice 130, specification 0.5]) zur Verfügung.&lt;br /&gt;
Das proprietäre 2.4 GHZ-Nordic-Protokoll unterstützt das Übertragen von Packages mit Addressierung, CRC sowie dem ACK und RESEND von Packages.&lt;br /&gt;
Die nRF52-SoCs besitzen einen Cortex M4F, 64kB RAM, 512kB integrierten Flash-Speicher, einen NFC-Tag sowie vielfältige weitere Peripherie. Der Cortex ist zur Nutzung für Anwenderprogramme vorgesehen. SDKs, auch für gcc, werden angeboten. Die Bausteine wurden auf die Verwendung mit 2-Lagen-PCBs hin optimiert und werden auch im QFN48 package verfügbar sein (Stand 09/2015).&lt;br /&gt;
Für das Empfangen beträgt der Strombedarf ca. 7mA ([http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.pdf.ps/nRF52832_OPS_v0.6.pdf Product Specification 0.6, &amp;quot;radio current consumption&amp;quot;, page 224]). Dies ist relativ viel im Vergleich zu  kabelgebundenen Übertragungen, entspricht jedoch lediglich 1/8 typischer WLAN-ICs (ESP8266).&lt;br /&gt;
&lt;br /&gt;
=== ESP8266 ===&lt;br /&gt;
&lt;br /&gt;
Das WLAN-Modul kann sowohl Client als auch AP sein. Damals hat der Release einen starken Hype ausgelöst und dementsprechend ist die Community rasant gewachsen, wodurch sich sogar ein eigenes Forum entwickelt hat ([http://www.esp8266.com www.esp8266.com]). &lt;br /&gt;
&lt;br /&gt;
Das Modul kann man über AT-Kommandos steuern, es gibt ein LUA-Skript sowie MicroPython Interpreter und vieles mehr. Zudem kann den integrieten SoC auch direkt programmieren, sodass man für einfache Aufgaben (Temperatur auslesen etc.) keinen extra Mikrocontroller braucht. Für die Programmierung wird vom Hersteller eine fertig eingerichtete VM zur Verfügung gestellt, es gibt aber auch eine Port vom GCC und selbst in die Arduino Umgebung ist der ESP8266 mittlerweile eingepflegt.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.micropython.org/en/latest/esp8266/esp8266/tutorial/intro.html ESP8266 MicroPython]&lt;br /&gt;
* [http://www.nodemcu.com/index_en.html NodeMCU LUA Interpreter Website]&lt;br /&gt;
** [https://github.com/nodemcu/nodemcu-firmware NodeMCU Firmware]&lt;br /&gt;
* [https://github.com/esp8266/Arduino Arduino ESP8266]&lt;br /&gt;
&lt;br /&gt;
=== EMW3162 ===&lt;br /&gt;
&lt;br /&gt;
[http://hackaday.com/2015/03/24/emw3162-wifi-120mhz-needs-attention/ HackADay Eintrag]&lt;br /&gt;
&lt;br /&gt;
[http://www.joinmx.com/uploadfiles/soft/EMW/DS0006E_EMW3162_V2.2.pdf Datasheet]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/354149 Thread zum Artikel (Fragen, Anregungen etc. hier posten)]&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RF12.pdf Datenblatt des ICs RF12] (PDF)&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM12.pdf Datenblatt des Moduls RFM12] (PDF)&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM69CW-V1.1.pdf Datenblatt RFM69CW]&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM69W-V1.3.pdf Datenblatt RFM69W]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/NRF24L01_Tutorial kleines NRF24L01+ Tutorial in C]&lt;br /&gt;
&lt;br /&gt;
== Fußnoten ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]]&lt;br /&gt;
[[Kategorie:RFM12| ]]&lt;br /&gt;
[[Kategorie:Funk]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;br /&gt;
[[Category:Hausbus]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=%C3%9Cbersicht_Funkmodule&amp;diff=100682</id>
		<title>Übersicht Funkmodule</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=%C3%9Cbersicht_Funkmodule&amp;diff=100682"/>
		<updated>2019-06-15T09:58:55Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* LoRa RFM95W, RFM96W, RFM97W, RFM98W */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier eine kleine Übersicht über die vorhanden Funkmodule. in Arbeit !!!&lt;br /&gt;
&lt;br /&gt;
== Übersicht ==&lt;br /&gt;
&lt;br /&gt;
=== Module ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! !! [[RFM12|RFM12B]] !! [[RFM69|RFM69(H)(C)W]] !! [[NRF24L01_Tutorial|nRF24L01+]] !! [[ESP8266]] !! EMW3162&lt;br /&gt;
|-&lt;br /&gt;
| aktiv (Stand: Feb 2017)|| ja || ja || ja || ja || ja&lt;br /&gt;
|-&lt;br /&gt;
| Frequenz || 433, 868 Mhz || 315, 433, 868, 915 Mhz || 2,4 Ghz || colspan=&amp;quot;2&amp;quot;  style=&amp;quot;text-align:center&amp;quot; | 2,4 Ghz (WLAN 802.11b/g/n)&lt;br /&gt;
|-&lt;br /&gt;
| Vcc || 2,2 - 3,8 V || 1,8 - 3,6 V || 1,9 - 3,6 V || 3,3 V || 3,3V&lt;br /&gt;
|-&lt;br /&gt;
| Max. Datenrate || 115,2 kbit/s || 300 kbit/s || 2 Mbit/s || 4,5-7 Mbit/s &amp;lt;ref&amp;gt;http://www.mikrocontroller.net/articles/ESP8266#Datendurchsatz.2FPerformanz&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;http://www.mikrocontroller.net/topic/342240?page=single#3857630&amp;lt;/ref&amp;gt; || bis 20 Mbit/s&amp;lt;ref&amp;gt;http://www.seeedstudio.com/depot/EMW3162-WiFi-Module-p-2122.html&amp;lt;/ref&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Min. Datenrate || 1,2 kbit/s || 1,2 kbit/s || 0,25 Mbit/s || colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | ? Mbit/s&lt;br /&gt;
|-&lt;br /&gt;
| Stromaufnahme (TX/RX/Sleep) || 22 mA/11 mA/0,3 µA || 45 mA/16 mA/0,1 µA&amp;lt;br&amp;gt;130 mA/16 mA/0,1 µA (HCW) || 11,3 mA/13,3 mA/0,9 µA&amp;lt;br&amp;gt;115 mA/13,3 mA/0,9 µA (PA) || 215 mA/60 mA/0,9 µA || 24mA (20kbit/s), 320mA (max) / 52-59mA / 7mA (connected), 200 µA (sleep), 2µA (off)&lt;br /&gt;
|-&lt;br /&gt;
| Sendeleistung || +5 dBm || +13 dBm (~20 mW !)&amp;lt;br /&amp;gt;+20 dBm (HCW) || 0 dBm&amp;lt;br /&amp;gt;+20 dBm (mit PA) || +15 dBm (802.11g/n)&amp;lt;br /&amp;gt;+18,5 dBm (802.11b) || +14,5 dBm (802.11n)&amp;lt;br /&amp;gt;+15,5 dBm (802.11g)&amp;lt;br /&amp;gt;+18,5 dBm (802.11b)&lt;br /&gt;
|- &lt;br /&gt;
| Empfindlichkeit || -105 dBm || -120 dBm || -94 dBm || -98 dBm (802.11b)&amp;lt;br /&amp;gt;-93 dBm (802.11g) || -96 dBm (802.11b)&amp;lt;br /&amp;gt;-90 dBm (802.11g)&amp;lt;br /&amp;gt;-89 dBm (802.11n)&lt;br /&gt;
|-&lt;br /&gt;
| Reichweite || + || ++ || - || - || -&lt;br /&gt;
|-&lt;br /&gt;
| Paketmanagement || keines || Prüfsumme, Adresse, Verschlüsslung || Prüfsumme, Adresse, Retransmit || IPv4&amp;lt;br&amp;gt;(TCP und UDP) || IPv4 und IPv6&amp;lt;br&amp;gt;(TCP und UDP)&lt;br /&gt;
|-&lt;br /&gt;
| Verschlüsselung || - || ○ (AES, nur ECB &amp;lt;ref&amp;gt;https://de.wikipedia.org/wiki/Electronic_Code_Book_Mode&amp;lt;/ref&amp;gt; ) || - || colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | + (WLAN Verschlüsslung)&lt;br /&gt;
|-&lt;br /&gt;
| FIFO Size || 16 Bit || 66 Bytes || 3 separate 32 bytes TX and RX FIFOs || ? || ?&lt;br /&gt;
|-&lt;br /&gt;
| Support im Forum || ++ || ○ || ++ || + || ○&lt;br /&gt;
|-&lt;br /&gt;
| einfache Lib (Prüfsumme, Sender, Retransmit) || ? || ja, siehe Links || ja, siehe Links || Firmware buggy (01.2015) || WICED, UART Firmware&lt;br /&gt;
|-&lt;br /&gt;
|Preis (02.2017) || 2-4 € || 2-4 € || 0,7-2 € || 1-2 € || 8-10€&lt;br /&gt;
|-&lt;br /&gt;
|Verfügbarkeit (02.2017) || ++ || + || ++ || ++ || +&lt;br /&gt;
|-&lt;br /&gt;
|Bezugsquellen (02.2017) || [http://www.pollin.de/shop/suchergebnis.html?S_TEXT=RFM12B Pollin], ebay, Ali || [http://www.pollin.de/shop/suchergebnis.html?S_TEXT=RFM69 Pollin], Ali, ebay || ebay, Ali || ebay, Ali, [http://www.amazon.de/s/url=search-alias%3Daps&amp;amp;field-keywords=esp8266ex Amazon] || Ali, [http://www.seeedstudio.com/depot/EMW3162-WiFi-Module-p-2122.html Seedstudio]&lt;br /&gt;
|-&lt;br /&gt;
|Tauglichkeit SmartHome || + || ++ || ○ || ○ || ○&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Frequenzen ===&lt;br /&gt;
&lt;br /&gt;
siehe auch [[Allgemeinzuteilung]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! !! 433 Mhz !! 868 Mhz !! 2400 Mhz (2,4 Ghz)&lt;br /&gt;
|-&lt;br /&gt;
| Sendeleistung || 10 mW ERP || 10-500 mW ERP|| 10 mW ERP&amp;lt;br /&amp;gt;100 mW EIRP&lt;br /&gt;
|-&lt;br /&gt;
| Duty Cycle || keinen || 0,1-10 % || keinen&lt;br /&gt;
|-&lt;br /&gt;
| Reichweite || ++ || + || - &lt;br /&gt;
|-&lt;br /&gt;
| Störung durch andere || + || ○ || ++ &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Details zu den Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== RFM12B ===&lt;br /&gt;
&lt;br /&gt;
Die RFM12(B) sind wohl die Urgesteine der drahtlosen Kommunikation: Tausendfach erprobt!&lt;br /&gt;
&lt;br /&gt;
* [[AVR RFM12]]&lt;br /&gt;
* [[RFM12 Protokoll Stack]]&lt;br /&gt;
* [[RF_SOAP]]&lt;br /&gt;
* [[Pollin_Funk-AVR-Evaluationsboard]]&lt;br /&gt;
* [http://www.mikrocontroller-elektronik.de/funkmodul-programmierung-rfm12b-rn-mikrofunk/ RN-MikroFunk AVR Miniatur Evaluationsboard und Tutorial]&lt;br /&gt;
&lt;br /&gt;
=== RFM69 ===&lt;br /&gt;
&lt;br /&gt;
Der RFM69 ist der Nachfolger des RFM12B. Er untersützt mehr Frequenzen und hat eine leicht höhere Sendeleistung. Die größten Neuerungen sind die integrierte AES-Verschlüsselung sowie das eingebaute Paketmanagement, das sich um Prüfsummen, Adressen kümmert.&lt;br /&gt;
&lt;br /&gt;
Es gibt folgende Module:&lt;br /&gt;
* RFM69HCW, mit zusätzlicher PA, (+20 dBm TX)&lt;br /&gt;
* RFM69HW, mit zusätzlicher PA, (+20 dBm TX)&lt;br /&gt;
* RFM69CW, Pinkompatibel zum RFM12B&lt;br /&gt;
* RFM69W&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://github.com/LowPowerLab/RFM69 RFM69 Lib zur Ansteuerung mit Arduino]&lt;br /&gt;
* [http://www.airspayce.com/mikem/arduino/RadioHead/index.html RadioHead Lib zum Aufbau eines MESH-Netzwerkes]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/332579 Thread im Forum: Wer verwendet RFM69]&lt;br /&gt;
&lt;br /&gt;
=== LoRa RFM95W, RFM96W, RFM97W, RFM98W ===&lt;br /&gt;
&lt;br /&gt;
Diese [https://en.wikipedia.org/wiki/LoRa LoRa (Long Range)] Module von HopeRF unterstützen das LoRa Protokoll. Sie unterscheiden sich hauptsächlich im unterstützen Frequenzbereich:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Typ !! 315 MHz !! 433 MHz !! 868 MHz !! 915 MHz&lt;br /&gt;
|-&lt;br /&gt;
| RFM96W-315S2 || x ||   ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM96W-433S2 ||   || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM98W-315S2 || x ||   ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM98W-433S2 ||   || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM95W-868S2 ||   ||   || x ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM95W-915S2 ||   ||   ||   || x&lt;br /&gt;
|-&lt;br /&gt;
| RFM97W-868S2 ||   ||   || x || &lt;br /&gt;
|-&lt;br /&gt;
| RFM97W-915S2 ||   ||   ||   || x&lt;br /&gt;
|-&lt;br /&gt;
| SX1278       || x || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| SX1276       || x || x || x || x&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die aufgeführten RFM9x Module enthalten jeweils entweder den SX1278 oder den SX1276 Chip.&lt;br /&gt;
Das &amp;quot;W&amp;quot; am Ende der Bezeichnung sagt nur aus, dass es sich um die International verwendbare Variante handelt und nicht um eine Vorabversion oder nur für den chinesischen Markt gedachte Variante.&lt;br /&gt;
Es gibt noch weitere Module z.B für eine größere Reichweite (+27 anstatt +20 dBm output power).&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/453948 Thread im Forum: Unterschiede RFM96W RFM98W]&lt;br /&gt;
* [https://www.hoperf.com/modules/lora/index.html Übersicht aller LoRa Module bei HopeRF]&lt;br /&gt;
* [https://www.thethingsnetwork.org/#metrics LoRaWAN The Things Network: LoRaWAN Gateways]&lt;br /&gt;
&lt;br /&gt;
=== nRF24L01+ ===&lt;br /&gt;
&lt;br /&gt;
Die wohl billigsten Funkmodule. Funken im 2,4 Ghz Band und haben alles in Hardware integriert für eine einfache Kommunikation. Gibt es schon länger am Markt, daher gibt es schon viele erprobte Bibliotheken für die Ansteuerung vom Arduino, RaspberryPi, ...&lt;br /&gt;
&lt;br /&gt;
Achtung! Nicht mit nRF24L01 (ohne Plus) verwechseln. Diese Module werden nicht mehr hergestellt und sind veraltet!&lt;br /&gt;
&lt;br /&gt;
* [http://tmrh20.github.io/RF24/ Bibliothek für die Ansteuerung über Arduino, RaspberryPi, Intel Galileo...]&lt;br /&gt;
* [http://tmrh20.github.io/RF24Network/ Bibliothek für eine ZigBee ähnliche Kommunikation (Mesh Nettwerk)]&lt;br /&gt;
&lt;br /&gt;
=== SE8R01 ===&lt;br /&gt;
&lt;br /&gt;
Wird bei AliExpress und E-Bay auch als simular nRF24L01+ angeboten. Er kostet die Hälfte vom nRF24L01+ und hat eine sehr kleine Bauform. Er ist NICHT kompatibel zum nRF24L01+ weder auf dem Funkkanal noch im Umgang mit den Konfigurationsregistern.&lt;br /&gt;
Die bislang brauchbarsten Informationen zu den Chip gab es unter [https://forum.arduino.cc/index.php?topic=366294.0 Finally Working code for se8r01(similar nrf24l01) 2mps, 1mps and 500kps]&lt;br /&gt;
Es scheint eine Datasheet V2.1 zu geben, ich habe es aber noch nirgends downloaden können.&lt;br /&gt;
&lt;br /&gt;
[[Benutzer:Prediger|Prediger]] ([[Benutzer Diskussion:Prediger|Diskussion]]) &lt;br /&gt;
&lt;br /&gt;
=== nRF52 ===&lt;br /&gt;
Die [https://www.nordicsemi.com/eng/Products/Bluetooth-Smart-Bluetooth-low-energy/nRF52832#Overview nRF52 SoCs] können für Bluetooth und für Übertragungen auf Basis des proprietären 2.4 GHZ-Nordic-Protokolls (kompatibel zu nRF24) genutzt werden. Für Bluetooth 4.1-Übertragungen steht ein Software Stack ([https://www.nordicsemi.com/eng/nordic/download_resource/33863/4/14045603 Nordic Softdevice 130, specification 0.5]) zur Verfügung.&lt;br /&gt;
Das proprietäre 2.4 GHZ-Nordic-Protokoll unterstützt das Übertragen von Packages mit Addressierung, CRC sowie dem ACK und RESEND von Packages.&lt;br /&gt;
Die nRF52-SoCs besitzen einen Cortex M4F, 64kB RAM, 512kB integrierten Flash-Speicher, einen NFC-Tag sowie vielfältige weitere Peripherie. Der Cortex ist zur Nutzung für Anwenderprogramme vorgesehen. SDKs, auch für gcc, werden angeboten. Die Bausteine wurden auf die Verwendung mit 2-Lagen-PCBs hin optimiert und werden auch im QFN48 package verfügbar sein (Stand 09/2015).&lt;br /&gt;
Für das Empfangen beträgt der Strombedarf ca. 7mA ([http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.pdf.ps/nRF52832_OPS_v0.6.pdf Product Specification 0.6, &amp;quot;radio current consumption&amp;quot;, page 224]). Dies ist relativ viel im Vergleich zu  kabelgebundenen Übertragungen, entspricht jedoch lediglich 1/8 typischer WLAN-ICs (ESP8266).&lt;br /&gt;
&lt;br /&gt;
=== ESP8266 ===&lt;br /&gt;
&lt;br /&gt;
Das WLAN-Modul kann sowohl Client als auch AP sein. Damals hat der Release einen starken Hype ausgelöst und dementsprechend ist die Community rasant gewachsen, wodurch sich sogar ein eigenes Forum entwickelt hat ([http://www.esp8266.com www.esp8266.com]). &lt;br /&gt;
&lt;br /&gt;
Das Modul kann man über AT-Kommandos steuern, es gibt ein LUA-Skript sowie MicroPython Interpreter und vieles mehr. Zudem kann den integrieten SoC auch direkt programmieren, sodass man für einfache Aufgaben (Temperatur auslesen etc.) keinen extra Mikrocontroller braucht. Für die Programmierung wird vom Hersteller eine fertig eingerichtete VM zur Verfügung gestellt, es gibt aber auch eine Port vom GCC und selbst in die Arduino Umgebung ist der ESP8266 mittlerweile eingepflegt.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.micropython.org/en/latest/esp8266/esp8266/tutorial/intro.html ESP8266 MicroPython]&lt;br /&gt;
* [http://www.nodemcu.com/index_en.html NodeMCU LUA Interpreter Website]&lt;br /&gt;
** [https://github.com/nodemcu/nodemcu-firmware NodeMCU Firmware]&lt;br /&gt;
* [https://github.com/esp8266/Arduino Arduino ESP8266]&lt;br /&gt;
&lt;br /&gt;
=== EMW3162 ===&lt;br /&gt;
&lt;br /&gt;
[http://hackaday.com/2015/03/24/emw3162-wifi-120mhz-needs-attention/ HackADay Eintrag]&lt;br /&gt;
&lt;br /&gt;
[http://www.joinmx.com/uploadfiles/soft/EMW/DS0006E_EMW3162_V2.2.pdf Datasheet]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/354149 Thread zum Artikel (Fragen, Anregungen etc. hier posten)]&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RF12.pdf Datenblatt des ICs RF12] (PDF)&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM12.pdf Datenblatt des Moduls RFM12] (PDF)&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM69CW-V1.1.pdf Datenblatt RFM69CW]&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM69W-V1.3.pdf Datenblatt RFM69W]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/NRF24L01_Tutorial kleines NRF24L01+ Tutorial in C]&lt;br /&gt;
&lt;br /&gt;
== Fußnoten ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]]&lt;br /&gt;
[[Kategorie:RFM12| ]]&lt;br /&gt;
[[Kategorie:Funk]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;br /&gt;
[[Category:Hausbus]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=%C3%9Cbersicht_Funkmodule&amp;diff=100681</id>
		<title>Übersicht Funkmodule</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=%C3%9Cbersicht_Funkmodule&amp;diff=100681"/>
		<updated>2019-06-15T09:57:00Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* LoRa RFM95W, RFM96W, RFM97W, RFM98W */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier eine kleine Übersicht über die vorhanden Funkmodule. in Arbeit !!!&lt;br /&gt;
&lt;br /&gt;
== Übersicht ==&lt;br /&gt;
&lt;br /&gt;
=== Module ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! !! [[RFM12|RFM12B]] !! [[RFM69|RFM69(H)(C)W]] !! [[NRF24L01_Tutorial|nRF24L01+]] !! [[ESP8266]] !! EMW3162&lt;br /&gt;
|-&lt;br /&gt;
| aktiv (Stand: Feb 2017)|| ja || ja || ja || ja || ja&lt;br /&gt;
|-&lt;br /&gt;
| Frequenz || 433, 868 Mhz || 315, 433, 868, 915 Mhz || 2,4 Ghz || colspan=&amp;quot;2&amp;quot;  style=&amp;quot;text-align:center&amp;quot; | 2,4 Ghz (WLAN 802.11b/g/n)&lt;br /&gt;
|-&lt;br /&gt;
| Vcc || 2,2 - 3,8 V || 1,8 - 3,6 V || 1,9 - 3,6 V || 3,3 V || 3,3V&lt;br /&gt;
|-&lt;br /&gt;
| Max. Datenrate || 115,2 kbit/s || 300 kbit/s || 2 Mbit/s || 4,5-7 Mbit/s &amp;lt;ref&amp;gt;http://www.mikrocontroller.net/articles/ESP8266#Datendurchsatz.2FPerformanz&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;http://www.mikrocontroller.net/topic/342240?page=single#3857630&amp;lt;/ref&amp;gt; || bis 20 Mbit/s&amp;lt;ref&amp;gt;http://www.seeedstudio.com/depot/EMW3162-WiFi-Module-p-2122.html&amp;lt;/ref&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Min. Datenrate || 1,2 kbit/s || 1,2 kbit/s || 0,25 Mbit/s || colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | ? Mbit/s&lt;br /&gt;
|-&lt;br /&gt;
| Stromaufnahme (TX/RX/Sleep) || 22 mA/11 mA/0,3 µA || 45 mA/16 mA/0,1 µA&amp;lt;br&amp;gt;130 mA/16 mA/0,1 µA (HCW) || 11,3 mA/13,3 mA/0,9 µA&amp;lt;br&amp;gt;115 mA/13,3 mA/0,9 µA (PA) || 215 mA/60 mA/0,9 µA || 24mA (20kbit/s), 320mA (max) / 52-59mA / 7mA (connected), 200 µA (sleep), 2µA (off)&lt;br /&gt;
|-&lt;br /&gt;
| Sendeleistung || +5 dBm || +13 dBm (~20 mW !)&amp;lt;br /&amp;gt;+20 dBm (HCW) || 0 dBm&amp;lt;br /&amp;gt;+20 dBm (mit PA) || +15 dBm (802.11g/n)&amp;lt;br /&amp;gt;+18,5 dBm (802.11b) || +14,5 dBm (802.11n)&amp;lt;br /&amp;gt;+15,5 dBm (802.11g)&amp;lt;br /&amp;gt;+18,5 dBm (802.11b)&lt;br /&gt;
|- &lt;br /&gt;
| Empfindlichkeit || -105 dBm || -120 dBm || -94 dBm || -98 dBm (802.11b)&amp;lt;br /&amp;gt;-93 dBm (802.11g) || -96 dBm (802.11b)&amp;lt;br /&amp;gt;-90 dBm (802.11g)&amp;lt;br /&amp;gt;-89 dBm (802.11n)&lt;br /&gt;
|-&lt;br /&gt;
| Reichweite || + || ++ || - || - || -&lt;br /&gt;
|-&lt;br /&gt;
| Paketmanagement || keines || Prüfsumme, Adresse, Verschlüsslung || Prüfsumme, Adresse, Retransmit || IPv4&amp;lt;br&amp;gt;(TCP und UDP) || IPv4 und IPv6&amp;lt;br&amp;gt;(TCP und UDP)&lt;br /&gt;
|-&lt;br /&gt;
| Verschlüsselung || - || ○ (AES, nur ECB &amp;lt;ref&amp;gt;https://de.wikipedia.org/wiki/Electronic_Code_Book_Mode&amp;lt;/ref&amp;gt; ) || - || colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | + (WLAN Verschlüsslung)&lt;br /&gt;
|-&lt;br /&gt;
| FIFO Size || 16 Bit || 66 Bytes || 3 separate 32 bytes TX and RX FIFOs || ? || ?&lt;br /&gt;
|-&lt;br /&gt;
| Support im Forum || ++ || ○ || ++ || + || ○&lt;br /&gt;
|-&lt;br /&gt;
| einfache Lib (Prüfsumme, Sender, Retransmit) || ? || ja, siehe Links || ja, siehe Links || Firmware buggy (01.2015) || WICED, UART Firmware&lt;br /&gt;
|-&lt;br /&gt;
|Preis (02.2017) || 2-4 € || 2-4 € || 0,7-2 € || 1-2 € || 8-10€&lt;br /&gt;
|-&lt;br /&gt;
|Verfügbarkeit (02.2017) || ++ || + || ++ || ++ || +&lt;br /&gt;
|-&lt;br /&gt;
|Bezugsquellen (02.2017) || [http://www.pollin.de/shop/suchergebnis.html?S_TEXT=RFM12B Pollin], ebay, Ali || [http://www.pollin.de/shop/suchergebnis.html?S_TEXT=RFM69 Pollin], Ali, ebay || ebay, Ali || ebay, Ali, [http://www.amazon.de/s/url=search-alias%3Daps&amp;amp;field-keywords=esp8266ex Amazon] || Ali, [http://www.seeedstudio.com/depot/EMW3162-WiFi-Module-p-2122.html Seedstudio]&lt;br /&gt;
|-&lt;br /&gt;
|Tauglichkeit SmartHome || + || ++ || ○ || ○ || ○&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Frequenzen ===&lt;br /&gt;
&lt;br /&gt;
siehe auch [[Allgemeinzuteilung]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! !! 433 Mhz !! 868 Mhz !! 2400 Mhz (2,4 Ghz)&lt;br /&gt;
|-&lt;br /&gt;
| Sendeleistung || 10 mW ERP || 10-500 mW ERP|| 10 mW ERP&amp;lt;br /&amp;gt;100 mW EIRP&lt;br /&gt;
|-&lt;br /&gt;
| Duty Cycle || keinen || 0,1-10 % || keinen&lt;br /&gt;
|-&lt;br /&gt;
| Reichweite || ++ || + || - &lt;br /&gt;
|-&lt;br /&gt;
| Störung durch andere || + || ○ || ++ &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Details zu den Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== RFM12B ===&lt;br /&gt;
&lt;br /&gt;
Die RFM12(B) sind wohl die Urgesteine der drahtlosen Kommunikation: Tausendfach erprobt!&lt;br /&gt;
&lt;br /&gt;
* [[AVR RFM12]]&lt;br /&gt;
* [[RFM12 Protokoll Stack]]&lt;br /&gt;
* [[RF_SOAP]]&lt;br /&gt;
* [[Pollin_Funk-AVR-Evaluationsboard]]&lt;br /&gt;
* [http://www.mikrocontroller-elektronik.de/funkmodul-programmierung-rfm12b-rn-mikrofunk/ RN-MikroFunk AVR Miniatur Evaluationsboard und Tutorial]&lt;br /&gt;
&lt;br /&gt;
=== RFM69 ===&lt;br /&gt;
&lt;br /&gt;
Der RFM69 ist der Nachfolger des RFM12B. Er untersützt mehr Frequenzen und hat eine leicht höhere Sendeleistung. Die größten Neuerungen sind die integrierte AES-Verschlüsselung sowie das eingebaute Paketmanagement, das sich um Prüfsummen, Adressen kümmert.&lt;br /&gt;
&lt;br /&gt;
Es gibt folgende Module:&lt;br /&gt;
* RFM69HCW, mit zusätzlicher PA, (+20 dBm TX)&lt;br /&gt;
* RFM69HW, mit zusätzlicher PA, (+20 dBm TX)&lt;br /&gt;
* RFM69CW, Pinkompatibel zum RFM12B&lt;br /&gt;
* RFM69W&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://github.com/LowPowerLab/RFM69 RFM69 Lib zur Ansteuerung mit Arduino]&lt;br /&gt;
* [http://www.airspayce.com/mikem/arduino/RadioHead/index.html RadioHead Lib zum Aufbau eines MESH-Netzwerkes]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/332579 Thread im Forum: Wer verwendet RFM69]&lt;br /&gt;
&lt;br /&gt;
=== LoRa RFM95W, RFM96W, RFM97W, RFM98W ===&lt;br /&gt;
&lt;br /&gt;
Diese [https://en.wikipedia.org/wiki/LoRa LoRa (Long Range)] Module von HopeRF unterstützen das LoRa Protokoll. Sie unterscheiden sich hauptsächlich im unterstützen Frequenzbereich:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Typ !! 315 MHz !! 433 MHz !! 868 MHz !! 915 MHz&lt;br /&gt;
|-&lt;br /&gt;
| RFM96W-315S2 || x ||   ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM96W-433S2 ||   || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM98W-315S2 || x ||   ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM98W-433S2 ||   || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM95W-868S2 ||   ||   || x ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM95W-915S2 ||   ||   ||   || x&lt;br /&gt;
|-&lt;br /&gt;
| RFM97W-868S2 ||   ||   || x || &lt;br /&gt;
|-&lt;br /&gt;
| RFM97W-915S2 ||   ||   ||   || x&lt;br /&gt;
|-&lt;br /&gt;
| SX1278       || x || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| SX1276       || x || x || x || x&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die aufgeführten RFM9x Module enthalten jeweils entweder den SX1278 oder den SX1276 Chip.&lt;br /&gt;
Das &amp;quot;W&amp;quot; am Ende der Bezeichnung sagt nur aus, dass es sich um die International verwendbare Variante handelt und nicht um eine Vorabversion oder nur für den chinesischen Markt gedachte Variante.&lt;br /&gt;
Es gibt noch weitere Module z.B für eine größere Reichweite.&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/453948 Thread im Forum: Unterschiede RFM96W RFM98W]&lt;br /&gt;
* [https://www.hoperf.com/modules/lora/index.html Übersicht aller LoRa Module bei HopeRF]&lt;br /&gt;
* [https://www.thethingsnetwork.org/#metrics LoRaWAN The Things Network: LoRaWAN Gateways]&lt;br /&gt;
&lt;br /&gt;
=== nRF24L01+ ===&lt;br /&gt;
&lt;br /&gt;
Die wohl billigsten Funkmodule. Funken im 2,4 Ghz Band und haben alles in Hardware integriert für eine einfache Kommunikation. Gibt es schon länger am Markt, daher gibt es schon viele erprobte Bibliotheken für die Ansteuerung vom Arduino, RaspberryPi, ...&lt;br /&gt;
&lt;br /&gt;
Achtung! Nicht mit nRF24L01 (ohne Plus) verwechseln. Diese Module werden nicht mehr hergestellt und sind veraltet!&lt;br /&gt;
&lt;br /&gt;
* [http://tmrh20.github.io/RF24/ Bibliothek für die Ansteuerung über Arduino, RaspberryPi, Intel Galileo...]&lt;br /&gt;
* [http://tmrh20.github.io/RF24Network/ Bibliothek für eine ZigBee ähnliche Kommunikation (Mesh Nettwerk)]&lt;br /&gt;
&lt;br /&gt;
=== SE8R01 ===&lt;br /&gt;
&lt;br /&gt;
Wird bei AliExpress und E-Bay auch als simular nRF24L01+ angeboten. Er kostet die Hälfte vom nRF24L01+ und hat eine sehr kleine Bauform. Er ist NICHT kompatibel zum nRF24L01+ weder auf dem Funkkanal noch im Umgang mit den Konfigurationsregistern.&lt;br /&gt;
Die bislang brauchbarsten Informationen zu den Chip gab es unter [https://forum.arduino.cc/index.php?topic=366294.0 Finally Working code for se8r01(similar nrf24l01) 2mps, 1mps and 500kps]&lt;br /&gt;
Es scheint eine Datasheet V2.1 zu geben, ich habe es aber noch nirgends downloaden können.&lt;br /&gt;
&lt;br /&gt;
[[Benutzer:Prediger|Prediger]] ([[Benutzer Diskussion:Prediger|Diskussion]]) &lt;br /&gt;
&lt;br /&gt;
=== nRF52 ===&lt;br /&gt;
Die [https://www.nordicsemi.com/eng/Products/Bluetooth-Smart-Bluetooth-low-energy/nRF52832#Overview nRF52 SoCs] können für Bluetooth und für Übertragungen auf Basis des proprietären 2.4 GHZ-Nordic-Protokolls (kompatibel zu nRF24) genutzt werden. Für Bluetooth 4.1-Übertragungen steht ein Software Stack ([https://www.nordicsemi.com/eng/nordic/download_resource/33863/4/14045603 Nordic Softdevice 130, specification 0.5]) zur Verfügung.&lt;br /&gt;
Das proprietäre 2.4 GHZ-Nordic-Protokoll unterstützt das Übertragen von Packages mit Addressierung, CRC sowie dem ACK und RESEND von Packages.&lt;br /&gt;
Die nRF52-SoCs besitzen einen Cortex M4F, 64kB RAM, 512kB integrierten Flash-Speicher, einen NFC-Tag sowie vielfältige weitere Peripherie. Der Cortex ist zur Nutzung für Anwenderprogramme vorgesehen. SDKs, auch für gcc, werden angeboten. Die Bausteine wurden auf die Verwendung mit 2-Lagen-PCBs hin optimiert und werden auch im QFN48 package verfügbar sein (Stand 09/2015).&lt;br /&gt;
Für das Empfangen beträgt der Strombedarf ca. 7mA ([http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.pdf.ps/nRF52832_OPS_v0.6.pdf Product Specification 0.6, &amp;quot;radio current consumption&amp;quot;, page 224]). Dies ist relativ viel im Vergleich zu  kabelgebundenen Übertragungen, entspricht jedoch lediglich 1/8 typischer WLAN-ICs (ESP8266).&lt;br /&gt;
&lt;br /&gt;
=== ESP8266 ===&lt;br /&gt;
&lt;br /&gt;
Das WLAN-Modul kann sowohl Client als auch AP sein. Damals hat der Release einen starken Hype ausgelöst und dementsprechend ist die Community rasant gewachsen, wodurch sich sogar ein eigenes Forum entwickelt hat ([http://www.esp8266.com www.esp8266.com]). &lt;br /&gt;
&lt;br /&gt;
Das Modul kann man über AT-Kommandos steuern, es gibt ein LUA-Skript sowie MicroPython Interpreter und vieles mehr. Zudem kann den integrieten SoC auch direkt programmieren, sodass man für einfache Aufgaben (Temperatur auslesen etc.) keinen extra Mikrocontroller braucht. Für die Programmierung wird vom Hersteller eine fertig eingerichtete VM zur Verfügung gestellt, es gibt aber auch eine Port vom GCC und selbst in die Arduino Umgebung ist der ESP8266 mittlerweile eingepflegt.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.micropython.org/en/latest/esp8266/esp8266/tutorial/intro.html ESP8266 MicroPython]&lt;br /&gt;
* [http://www.nodemcu.com/index_en.html NodeMCU LUA Interpreter Website]&lt;br /&gt;
** [https://github.com/nodemcu/nodemcu-firmware NodeMCU Firmware]&lt;br /&gt;
* [https://github.com/esp8266/Arduino Arduino ESP8266]&lt;br /&gt;
&lt;br /&gt;
=== EMW3162 ===&lt;br /&gt;
&lt;br /&gt;
[http://hackaday.com/2015/03/24/emw3162-wifi-120mhz-needs-attention/ HackADay Eintrag]&lt;br /&gt;
&lt;br /&gt;
[http://www.joinmx.com/uploadfiles/soft/EMW/DS0006E_EMW3162_V2.2.pdf Datasheet]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/354149 Thread zum Artikel (Fragen, Anregungen etc. hier posten)]&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RF12.pdf Datenblatt des ICs RF12] (PDF)&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM12.pdf Datenblatt des Moduls RFM12] (PDF)&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM69CW-V1.1.pdf Datenblatt RFM69CW]&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM69W-V1.3.pdf Datenblatt RFM69W]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/NRF24L01_Tutorial kleines NRF24L01+ Tutorial in C]&lt;br /&gt;
&lt;br /&gt;
== Fußnoten ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]]&lt;br /&gt;
[[Kategorie:RFM12| ]]&lt;br /&gt;
[[Kategorie:Funk]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;br /&gt;
[[Category:Hausbus]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=%C3%9Cbersicht_Funkmodule&amp;diff=100680</id>
		<title>Übersicht Funkmodule</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=%C3%9Cbersicht_Funkmodule&amp;diff=100680"/>
		<updated>2019-06-15T09:55:54Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* LoRa RFM95W, RFM96W, RFM97W, RFM98W */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier eine kleine Übersicht über die vorhanden Funkmodule. in Arbeit !!!&lt;br /&gt;
&lt;br /&gt;
== Übersicht ==&lt;br /&gt;
&lt;br /&gt;
=== Module ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! !! [[RFM12|RFM12B]] !! [[RFM69|RFM69(H)(C)W]] !! [[NRF24L01_Tutorial|nRF24L01+]] !! [[ESP8266]] !! EMW3162&lt;br /&gt;
|-&lt;br /&gt;
| aktiv (Stand: Feb 2017)|| ja || ja || ja || ja || ja&lt;br /&gt;
|-&lt;br /&gt;
| Frequenz || 433, 868 Mhz || 315, 433, 868, 915 Mhz || 2,4 Ghz || colspan=&amp;quot;2&amp;quot;  style=&amp;quot;text-align:center&amp;quot; | 2,4 Ghz (WLAN 802.11b/g/n)&lt;br /&gt;
|-&lt;br /&gt;
| Vcc || 2,2 - 3,8 V || 1,8 - 3,6 V || 1,9 - 3,6 V || 3,3 V || 3,3V&lt;br /&gt;
|-&lt;br /&gt;
| Max. Datenrate || 115,2 kbit/s || 300 kbit/s || 2 Mbit/s || 4,5-7 Mbit/s &amp;lt;ref&amp;gt;http://www.mikrocontroller.net/articles/ESP8266#Datendurchsatz.2FPerformanz&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;http://www.mikrocontroller.net/topic/342240?page=single#3857630&amp;lt;/ref&amp;gt; || bis 20 Mbit/s&amp;lt;ref&amp;gt;http://www.seeedstudio.com/depot/EMW3162-WiFi-Module-p-2122.html&amp;lt;/ref&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Min. Datenrate || 1,2 kbit/s || 1,2 kbit/s || 0,25 Mbit/s || colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | ? Mbit/s&lt;br /&gt;
|-&lt;br /&gt;
| Stromaufnahme (TX/RX/Sleep) || 22 mA/11 mA/0,3 µA || 45 mA/16 mA/0,1 µA&amp;lt;br&amp;gt;130 mA/16 mA/0,1 µA (HCW) || 11,3 mA/13,3 mA/0,9 µA&amp;lt;br&amp;gt;115 mA/13,3 mA/0,9 µA (PA) || 215 mA/60 mA/0,9 µA || 24mA (20kbit/s), 320mA (max) / 52-59mA / 7mA (connected), 200 µA (sleep), 2µA (off)&lt;br /&gt;
|-&lt;br /&gt;
| Sendeleistung || +5 dBm || +13 dBm (~20 mW !)&amp;lt;br /&amp;gt;+20 dBm (HCW) || 0 dBm&amp;lt;br /&amp;gt;+20 dBm (mit PA) || +15 dBm (802.11g/n)&amp;lt;br /&amp;gt;+18,5 dBm (802.11b) || +14,5 dBm (802.11n)&amp;lt;br /&amp;gt;+15,5 dBm (802.11g)&amp;lt;br /&amp;gt;+18,5 dBm (802.11b)&lt;br /&gt;
|- &lt;br /&gt;
| Empfindlichkeit || -105 dBm || -120 dBm || -94 dBm || -98 dBm (802.11b)&amp;lt;br /&amp;gt;-93 dBm (802.11g) || -96 dBm (802.11b)&amp;lt;br /&amp;gt;-90 dBm (802.11g)&amp;lt;br /&amp;gt;-89 dBm (802.11n)&lt;br /&gt;
|-&lt;br /&gt;
| Reichweite || + || ++ || - || - || -&lt;br /&gt;
|-&lt;br /&gt;
| Paketmanagement || keines || Prüfsumme, Adresse, Verschlüsslung || Prüfsumme, Adresse, Retransmit || IPv4&amp;lt;br&amp;gt;(TCP und UDP) || IPv4 und IPv6&amp;lt;br&amp;gt;(TCP und UDP)&lt;br /&gt;
|-&lt;br /&gt;
| Verschlüsselung || - || ○ (AES, nur ECB &amp;lt;ref&amp;gt;https://de.wikipedia.org/wiki/Electronic_Code_Book_Mode&amp;lt;/ref&amp;gt; ) || - || colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | + (WLAN Verschlüsslung)&lt;br /&gt;
|-&lt;br /&gt;
| FIFO Size || 16 Bit || 66 Bytes || 3 separate 32 bytes TX and RX FIFOs || ? || ?&lt;br /&gt;
|-&lt;br /&gt;
| Support im Forum || ++ || ○ || ++ || + || ○&lt;br /&gt;
|-&lt;br /&gt;
| einfache Lib (Prüfsumme, Sender, Retransmit) || ? || ja, siehe Links || ja, siehe Links || Firmware buggy (01.2015) || WICED, UART Firmware&lt;br /&gt;
|-&lt;br /&gt;
|Preis (02.2017) || 2-4 € || 2-4 € || 0,7-2 € || 1-2 € || 8-10€&lt;br /&gt;
|-&lt;br /&gt;
|Verfügbarkeit (02.2017) || ++ || + || ++ || ++ || +&lt;br /&gt;
|-&lt;br /&gt;
|Bezugsquellen (02.2017) || [http://www.pollin.de/shop/suchergebnis.html?S_TEXT=RFM12B Pollin], ebay, Ali || [http://www.pollin.de/shop/suchergebnis.html?S_TEXT=RFM69 Pollin], Ali, ebay || ebay, Ali || ebay, Ali, [http://www.amazon.de/s/url=search-alias%3Daps&amp;amp;field-keywords=esp8266ex Amazon] || Ali, [http://www.seeedstudio.com/depot/EMW3162-WiFi-Module-p-2122.html Seedstudio]&lt;br /&gt;
|-&lt;br /&gt;
|Tauglichkeit SmartHome || + || ++ || ○ || ○ || ○&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Frequenzen ===&lt;br /&gt;
&lt;br /&gt;
siehe auch [[Allgemeinzuteilung]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! !! 433 Mhz !! 868 Mhz !! 2400 Mhz (2,4 Ghz)&lt;br /&gt;
|-&lt;br /&gt;
| Sendeleistung || 10 mW ERP || 10-500 mW ERP|| 10 mW ERP&amp;lt;br /&amp;gt;100 mW EIRP&lt;br /&gt;
|-&lt;br /&gt;
| Duty Cycle || keinen || 0,1-10 % || keinen&lt;br /&gt;
|-&lt;br /&gt;
| Reichweite || ++ || + || - &lt;br /&gt;
|-&lt;br /&gt;
| Störung durch andere || + || ○ || ++ &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Details zu den Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== RFM12B ===&lt;br /&gt;
&lt;br /&gt;
Die RFM12(B) sind wohl die Urgesteine der drahtlosen Kommunikation: Tausendfach erprobt!&lt;br /&gt;
&lt;br /&gt;
* [[AVR RFM12]]&lt;br /&gt;
* [[RFM12 Protokoll Stack]]&lt;br /&gt;
* [[RF_SOAP]]&lt;br /&gt;
* [[Pollin_Funk-AVR-Evaluationsboard]]&lt;br /&gt;
* [http://www.mikrocontroller-elektronik.de/funkmodul-programmierung-rfm12b-rn-mikrofunk/ RN-MikroFunk AVR Miniatur Evaluationsboard und Tutorial]&lt;br /&gt;
&lt;br /&gt;
=== RFM69 ===&lt;br /&gt;
&lt;br /&gt;
Der RFM69 ist der Nachfolger des RFM12B. Er untersützt mehr Frequenzen und hat eine leicht höhere Sendeleistung. Die größten Neuerungen sind die integrierte AES-Verschlüsselung sowie das eingebaute Paketmanagement, das sich um Prüfsummen, Adressen kümmert.&lt;br /&gt;
&lt;br /&gt;
Es gibt folgende Module:&lt;br /&gt;
* RFM69HCW, mit zusätzlicher PA, (+20 dBm TX)&lt;br /&gt;
* RFM69HW, mit zusätzlicher PA, (+20 dBm TX)&lt;br /&gt;
* RFM69CW, Pinkompatibel zum RFM12B&lt;br /&gt;
* RFM69W&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://github.com/LowPowerLab/RFM69 RFM69 Lib zur Ansteuerung mit Arduino]&lt;br /&gt;
* [http://www.airspayce.com/mikem/arduino/RadioHead/index.html RadioHead Lib zum Aufbau eines MESH-Netzwerkes]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/332579 Thread im Forum: Wer verwendet RFM69]&lt;br /&gt;
&lt;br /&gt;
=== LoRa RFM95W, RFM96W, RFM97W, RFM98W ===&lt;br /&gt;
&lt;br /&gt;
Diese [https://en.wikipedia.org/wiki/LoRa LoRa (Long Range)] Module von HopeRF unterstützen das LoRa Protokoll. Sie unterscheiden sich hauptsächlich im unterstützen Frequenzbereich:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Typ !! 315 MHz !! 433 MHz !! 868 MHz !! 915 MHz&lt;br /&gt;
|-&lt;br /&gt;
| RFM96W-315S2 || x ||   ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM96W-433S2 ||   || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM98W-315S2 || x ||   ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM98W-433S2 ||   || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM95W-868S2 ||   ||   || x ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM95W-915S2 ||   ||   ||   || x&lt;br /&gt;
|-&lt;br /&gt;
| RFM97W-868S2 ||   ||   || x || &lt;br /&gt;
|-&lt;br /&gt;
| RFM97W-915S2 ||   ||   ||   || x&lt;br /&gt;
|-&lt;br /&gt;
| SX1278       || x || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| SX1276       || x || x || x || x&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die aufgeführten RFM9x Module enthalten jeweils entweder den SX1278 oder den SX1276 Chip.&lt;br /&gt;
Das &amp;quot;W&amp;quot; am Ende der Bezeichnung sagt nur aus, dass es sich um die International verwendbare Variante handelt und nicht um eine Vorabversion oder nur für den chinesischen Markt gedachte Variante.&lt;br /&gt;
Es gibt noch weitere Module z.B für eine größere Reichweite.&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/453948 Thread im Forum: Unterschiede RFM96W RFM98W]&lt;br /&gt;
* [https://www.hoperf.com/modules/lora/RFM95.html Infos zu den Modulen bei HopeRF]&lt;br /&gt;
* [https://www.thethingsnetwork.org/#metrics LoRaWAN The Things Network: LoRaWAN Gateways]&lt;br /&gt;
&lt;br /&gt;
=== nRF24L01+ ===&lt;br /&gt;
&lt;br /&gt;
Die wohl billigsten Funkmodule. Funken im 2,4 Ghz Band und haben alles in Hardware integriert für eine einfache Kommunikation. Gibt es schon länger am Markt, daher gibt es schon viele erprobte Bibliotheken für die Ansteuerung vom Arduino, RaspberryPi, ...&lt;br /&gt;
&lt;br /&gt;
Achtung! Nicht mit nRF24L01 (ohne Plus) verwechseln. Diese Module werden nicht mehr hergestellt und sind veraltet!&lt;br /&gt;
&lt;br /&gt;
* [http://tmrh20.github.io/RF24/ Bibliothek für die Ansteuerung über Arduino, RaspberryPi, Intel Galileo...]&lt;br /&gt;
* [http://tmrh20.github.io/RF24Network/ Bibliothek für eine ZigBee ähnliche Kommunikation (Mesh Nettwerk)]&lt;br /&gt;
&lt;br /&gt;
=== SE8R01 ===&lt;br /&gt;
&lt;br /&gt;
Wird bei AliExpress und E-Bay auch als simular nRF24L01+ angeboten. Er kostet die Hälfte vom nRF24L01+ und hat eine sehr kleine Bauform. Er ist NICHT kompatibel zum nRF24L01+ weder auf dem Funkkanal noch im Umgang mit den Konfigurationsregistern.&lt;br /&gt;
Die bislang brauchbarsten Informationen zu den Chip gab es unter [https://forum.arduino.cc/index.php?topic=366294.0 Finally Working code for se8r01(similar nrf24l01) 2mps, 1mps and 500kps]&lt;br /&gt;
Es scheint eine Datasheet V2.1 zu geben, ich habe es aber noch nirgends downloaden können.&lt;br /&gt;
&lt;br /&gt;
[[Benutzer:Prediger|Prediger]] ([[Benutzer Diskussion:Prediger|Diskussion]]) &lt;br /&gt;
&lt;br /&gt;
=== nRF52 ===&lt;br /&gt;
Die [https://www.nordicsemi.com/eng/Products/Bluetooth-Smart-Bluetooth-low-energy/nRF52832#Overview nRF52 SoCs] können für Bluetooth und für Übertragungen auf Basis des proprietären 2.4 GHZ-Nordic-Protokolls (kompatibel zu nRF24) genutzt werden. Für Bluetooth 4.1-Übertragungen steht ein Software Stack ([https://www.nordicsemi.com/eng/nordic/download_resource/33863/4/14045603 Nordic Softdevice 130, specification 0.5]) zur Verfügung.&lt;br /&gt;
Das proprietäre 2.4 GHZ-Nordic-Protokoll unterstützt das Übertragen von Packages mit Addressierung, CRC sowie dem ACK und RESEND von Packages.&lt;br /&gt;
Die nRF52-SoCs besitzen einen Cortex M4F, 64kB RAM, 512kB integrierten Flash-Speicher, einen NFC-Tag sowie vielfältige weitere Peripherie. Der Cortex ist zur Nutzung für Anwenderprogramme vorgesehen. SDKs, auch für gcc, werden angeboten. Die Bausteine wurden auf die Verwendung mit 2-Lagen-PCBs hin optimiert und werden auch im QFN48 package verfügbar sein (Stand 09/2015).&lt;br /&gt;
Für das Empfangen beträgt der Strombedarf ca. 7mA ([http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.pdf.ps/nRF52832_OPS_v0.6.pdf Product Specification 0.6, &amp;quot;radio current consumption&amp;quot;, page 224]). Dies ist relativ viel im Vergleich zu  kabelgebundenen Übertragungen, entspricht jedoch lediglich 1/8 typischer WLAN-ICs (ESP8266).&lt;br /&gt;
&lt;br /&gt;
=== ESP8266 ===&lt;br /&gt;
&lt;br /&gt;
Das WLAN-Modul kann sowohl Client als auch AP sein. Damals hat der Release einen starken Hype ausgelöst und dementsprechend ist die Community rasant gewachsen, wodurch sich sogar ein eigenes Forum entwickelt hat ([http://www.esp8266.com www.esp8266.com]). &lt;br /&gt;
&lt;br /&gt;
Das Modul kann man über AT-Kommandos steuern, es gibt ein LUA-Skript sowie MicroPython Interpreter und vieles mehr. Zudem kann den integrieten SoC auch direkt programmieren, sodass man für einfache Aufgaben (Temperatur auslesen etc.) keinen extra Mikrocontroller braucht. Für die Programmierung wird vom Hersteller eine fertig eingerichtete VM zur Verfügung gestellt, es gibt aber auch eine Port vom GCC und selbst in die Arduino Umgebung ist der ESP8266 mittlerweile eingepflegt.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.micropython.org/en/latest/esp8266/esp8266/tutorial/intro.html ESP8266 MicroPython]&lt;br /&gt;
* [http://www.nodemcu.com/index_en.html NodeMCU LUA Interpreter Website]&lt;br /&gt;
** [https://github.com/nodemcu/nodemcu-firmware NodeMCU Firmware]&lt;br /&gt;
* [https://github.com/esp8266/Arduino Arduino ESP8266]&lt;br /&gt;
&lt;br /&gt;
=== EMW3162 ===&lt;br /&gt;
&lt;br /&gt;
[http://hackaday.com/2015/03/24/emw3162-wifi-120mhz-needs-attention/ HackADay Eintrag]&lt;br /&gt;
&lt;br /&gt;
[http://www.joinmx.com/uploadfiles/soft/EMW/DS0006E_EMW3162_V2.2.pdf Datasheet]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/354149 Thread zum Artikel (Fragen, Anregungen etc. hier posten)]&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RF12.pdf Datenblatt des ICs RF12] (PDF)&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM12.pdf Datenblatt des Moduls RFM12] (PDF)&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM69CW-V1.1.pdf Datenblatt RFM69CW]&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM69W-V1.3.pdf Datenblatt RFM69W]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/NRF24L01_Tutorial kleines NRF24L01+ Tutorial in C]&lt;br /&gt;
&lt;br /&gt;
== Fußnoten ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]]&lt;br /&gt;
[[Kategorie:RFM12| ]]&lt;br /&gt;
[[Kategorie:Funk]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;br /&gt;
[[Category:Hausbus]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=%C3%9Cbersicht_Funkmodule&amp;diff=100679</id>
		<title>Übersicht Funkmodule</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=%C3%9Cbersicht_Funkmodule&amp;diff=100679"/>
		<updated>2019-06-15T09:52:24Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* LoRaWAN RFM95W, RFM96W, RFM97W, RFM98W */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier eine kleine Übersicht über die vorhanden Funkmodule. in Arbeit !!!&lt;br /&gt;
&lt;br /&gt;
== Übersicht ==&lt;br /&gt;
&lt;br /&gt;
=== Module ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! !! [[RFM12|RFM12B]] !! [[RFM69|RFM69(H)(C)W]] !! [[NRF24L01_Tutorial|nRF24L01+]] !! [[ESP8266]] !! EMW3162&lt;br /&gt;
|-&lt;br /&gt;
| aktiv (Stand: Feb 2017)|| ja || ja || ja || ja || ja&lt;br /&gt;
|-&lt;br /&gt;
| Frequenz || 433, 868 Mhz || 315, 433, 868, 915 Mhz || 2,4 Ghz || colspan=&amp;quot;2&amp;quot;  style=&amp;quot;text-align:center&amp;quot; | 2,4 Ghz (WLAN 802.11b/g/n)&lt;br /&gt;
|-&lt;br /&gt;
| Vcc || 2,2 - 3,8 V || 1,8 - 3,6 V || 1,9 - 3,6 V || 3,3 V || 3,3V&lt;br /&gt;
|-&lt;br /&gt;
| Max. Datenrate || 115,2 kbit/s || 300 kbit/s || 2 Mbit/s || 4,5-7 Mbit/s &amp;lt;ref&amp;gt;http://www.mikrocontroller.net/articles/ESP8266#Datendurchsatz.2FPerformanz&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;http://www.mikrocontroller.net/topic/342240?page=single#3857630&amp;lt;/ref&amp;gt; || bis 20 Mbit/s&amp;lt;ref&amp;gt;http://www.seeedstudio.com/depot/EMW3162-WiFi-Module-p-2122.html&amp;lt;/ref&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Min. Datenrate || 1,2 kbit/s || 1,2 kbit/s || 0,25 Mbit/s || colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | ? Mbit/s&lt;br /&gt;
|-&lt;br /&gt;
| Stromaufnahme (TX/RX/Sleep) || 22 mA/11 mA/0,3 µA || 45 mA/16 mA/0,1 µA&amp;lt;br&amp;gt;130 mA/16 mA/0,1 µA (HCW) || 11,3 mA/13,3 mA/0,9 µA&amp;lt;br&amp;gt;115 mA/13,3 mA/0,9 µA (PA) || 215 mA/60 mA/0,9 µA || 24mA (20kbit/s), 320mA (max) / 52-59mA / 7mA (connected), 200 µA (sleep), 2µA (off)&lt;br /&gt;
|-&lt;br /&gt;
| Sendeleistung || +5 dBm || +13 dBm (~20 mW !)&amp;lt;br /&amp;gt;+20 dBm (HCW) || 0 dBm&amp;lt;br /&amp;gt;+20 dBm (mit PA) || +15 dBm (802.11g/n)&amp;lt;br /&amp;gt;+18,5 dBm (802.11b) || +14,5 dBm (802.11n)&amp;lt;br /&amp;gt;+15,5 dBm (802.11g)&amp;lt;br /&amp;gt;+18,5 dBm (802.11b)&lt;br /&gt;
|- &lt;br /&gt;
| Empfindlichkeit || -105 dBm || -120 dBm || -94 dBm || -98 dBm (802.11b)&amp;lt;br /&amp;gt;-93 dBm (802.11g) || -96 dBm (802.11b)&amp;lt;br /&amp;gt;-90 dBm (802.11g)&amp;lt;br /&amp;gt;-89 dBm (802.11n)&lt;br /&gt;
|-&lt;br /&gt;
| Reichweite || + || ++ || - || - || -&lt;br /&gt;
|-&lt;br /&gt;
| Paketmanagement || keines || Prüfsumme, Adresse, Verschlüsslung || Prüfsumme, Adresse, Retransmit || IPv4&amp;lt;br&amp;gt;(TCP und UDP) || IPv4 und IPv6&amp;lt;br&amp;gt;(TCP und UDP)&lt;br /&gt;
|-&lt;br /&gt;
| Verschlüsselung || - || ○ (AES, nur ECB &amp;lt;ref&amp;gt;https://de.wikipedia.org/wiki/Electronic_Code_Book_Mode&amp;lt;/ref&amp;gt; ) || - || colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | + (WLAN Verschlüsslung)&lt;br /&gt;
|-&lt;br /&gt;
| FIFO Size || 16 Bit || 66 Bytes || 3 separate 32 bytes TX and RX FIFOs || ? || ?&lt;br /&gt;
|-&lt;br /&gt;
| Support im Forum || ++ || ○ || ++ || + || ○&lt;br /&gt;
|-&lt;br /&gt;
| einfache Lib (Prüfsumme, Sender, Retransmit) || ? || ja, siehe Links || ja, siehe Links || Firmware buggy (01.2015) || WICED, UART Firmware&lt;br /&gt;
|-&lt;br /&gt;
|Preis (02.2017) || 2-4 € || 2-4 € || 0,7-2 € || 1-2 € || 8-10€&lt;br /&gt;
|-&lt;br /&gt;
|Verfügbarkeit (02.2017) || ++ || + || ++ || ++ || +&lt;br /&gt;
|-&lt;br /&gt;
|Bezugsquellen (02.2017) || [http://www.pollin.de/shop/suchergebnis.html?S_TEXT=RFM12B Pollin], ebay, Ali || [http://www.pollin.de/shop/suchergebnis.html?S_TEXT=RFM69 Pollin], Ali, ebay || ebay, Ali || ebay, Ali, [http://www.amazon.de/s/url=search-alias%3Daps&amp;amp;field-keywords=esp8266ex Amazon] || Ali, [http://www.seeedstudio.com/depot/EMW3162-WiFi-Module-p-2122.html Seedstudio]&lt;br /&gt;
|-&lt;br /&gt;
|Tauglichkeit SmartHome || + || ++ || ○ || ○ || ○&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Frequenzen ===&lt;br /&gt;
&lt;br /&gt;
siehe auch [[Allgemeinzuteilung]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! !! 433 Mhz !! 868 Mhz !! 2400 Mhz (2,4 Ghz)&lt;br /&gt;
|-&lt;br /&gt;
| Sendeleistung || 10 mW ERP || 10-500 mW ERP|| 10 mW ERP&amp;lt;br /&amp;gt;100 mW EIRP&lt;br /&gt;
|-&lt;br /&gt;
| Duty Cycle || keinen || 0,1-10 % || keinen&lt;br /&gt;
|-&lt;br /&gt;
| Reichweite || ++ || + || - &lt;br /&gt;
|-&lt;br /&gt;
| Störung durch andere || + || ○ || ++ &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Details zu den Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== RFM12B ===&lt;br /&gt;
&lt;br /&gt;
Die RFM12(B) sind wohl die Urgesteine der drahtlosen Kommunikation: Tausendfach erprobt!&lt;br /&gt;
&lt;br /&gt;
* [[AVR RFM12]]&lt;br /&gt;
* [[RFM12 Protokoll Stack]]&lt;br /&gt;
* [[RF_SOAP]]&lt;br /&gt;
* [[Pollin_Funk-AVR-Evaluationsboard]]&lt;br /&gt;
* [http://www.mikrocontroller-elektronik.de/funkmodul-programmierung-rfm12b-rn-mikrofunk/ RN-MikroFunk AVR Miniatur Evaluationsboard und Tutorial]&lt;br /&gt;
&lt;br /&gt;
=== RFM69 ===&lt;br /&gt;
&lt;br /&gt;
Der RFM69 ist der Nachfolger des RFM12B. Er untersützt mehr Frequenzen und hat eine leicht höhere Sendeleistung. Die größten Neuerungen sind die integrierte AES-Verschlüsselung sowie das eingebaute Paketmanagement, das sich um Prüfsummen, Adressen kümmert.&lt;br /&gt;
&lt;br /&gt;
Es gibt folgende Module:&lt;br /&gt;
* RFM69HCW, mit zusätzlicher PA, (+20 dBm TX)&lt;br /&gt;
* RFM69HW, mit zusätzlicher PA, (+20 dBm TX)&lt;br /&gt;
* RFM69CW, Pinkompatibel zum RFM12B&lt;br /&gt;
* RFM69W&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://github.com/LowPowerLab/RFM69 RFM69 Lib zur Ansteuerung mit Arduino]&lt;br /&gt;
* [http://www.airspayce.com/mikem/arduino/RadioHead/index.html RadioHead Lib zum Aufbau eines MESH-Netzwerkes]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/332579 Thread im Forum: Wer verwendet RFM69]&lt;br /&gt;
&lt;br /&gt;
=== LoRa RFM95W, RFM96W, RFM97W, RFM98W ===&lt;br /&gt;
&lt;br /&gt;
Diese [https://en.wikipedia.org/wiki/LoRa LoRa (Long Range)] Module von HopeRF unterstützen das LoRa Protokoll. Sie unterscheiden sich hauptsächlich im unterstützen Frequenzbereich:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Typ !! 315 MHz !! 433 MHz !! 868 MHz !! 915 MHz&lt;br /&gt;
|-&lt;br /&gt;
| RFM96W-315S2 || x ||   ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM96W-433S2 ||   || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM98W-315S2 || x ||   ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM98W-433S2 ||   || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM95W-868S2 ||   ||   || x ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM95W-915S2 ||   ||   ||   || x&lt;br /&gt;
|-&lt;br /&gt;
| RFM97W-868S2 ||   ||   || x || &lt;br /&gt;
|-&lt;br /&gt;
| RFM97W-915S2 ||   ||   ||   || x&lt;br /&gt;
|-&lt;br /&gt;
| SX1278       || x || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| SX1276       || x || x || x || x&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die aufgeführten RFM9x Module enthalten jeweils entweder den SX1278 oder den SX1276 Chip.&lt;br /&gt;
Es gibt noch weitere Module z.B für eine größere Reichweite.&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/453948 Thread im Forum: Unterschiede RFM96W RFM98W]&lt;br /&gt;
* [https://www.hoperf.com/modules/lora/RFM95.html Infos zu den Modulen bei HopeRF]&lt;br /&gt;
* [https://www.thethingsnetwork.org/#metrics LoRaWAN The Things Network: LoRaWAN Gateways]&lt;br /&gt;
&lt;br /&gt;
=== nRF24L01+ ===&lt;br /&gt;
&lt;br /&gt;
Die wohl billigsten Funkmodule. Funken im 2,4 Ghz Band und haben alles in Hardware integriert für eine einfache Kommunikation. Gibt es schon länger am Markt, daher gibt es schon viele erprobte Bibliotheken für die Ansteuerung vom Arduino, RaspberryPi, ...&lt;br /&gt;
&lt;br /&gt;
Achtung! Nicht mit nRF24L01 (ohne Plus) verwechseln. Diese Module werden nicht mehr hergestellt und sind veraltet!&lt;br /&gt;
&lt;br /&gt;
* [http://tmrh20.github.io/RF24/ Bibliothek für die Ansteuerung über Arduino, RaspberryPi, Intel Galileo...]&lt;br /&gt;
* [http://tmrh20.github.io/RF24Network/ Bibliothek für eine ZigBee ähnliche Kommunikation (Mesh Nettwerk)]&lt;br /&gt;
&lt;br /&gt;
=== SE8R01 ===&lt;br /&gt;
&lt;br /&gt;
Wird bei AliExpress und E-Bay auch als simular nRF24L01+ angeboten. Er kostet die Hälfte vom nRF24L01+ und hat eine sehr kleine Bauform. Er ist NICHT kompatibel zum nRF24L01+ weder auf dem Funkkanal noch im Umgang mit den Konfigurationsregistern.&lt;br /&gt;
Die bislang brauchbarsten Informationen zu den Chip gab es unter [https://forum.arduino.cc/index.php?topic=366294.0 Finally Working code for se8r01(similar nrf24l01) 2mps, 1mps and 500kps]&lt;br /&gt;
Es scheint eine Datasheet V2.1 zu geben, ich habe es aber noch nirgends downloaden können.&lt;br /&gt;
&lt;br /&gt;
[[Benutzer:Prediger|Prediger]] ([[Benutzer Diskussion:Prediger|Diskussion]]) &lt;br /&gt;
&lt;br /&gt;
=== nRF52 ===&lt;br /&gt;
Die [https://www.nordicsemi.com/eng/Products/Bluetooth-Smart-Bluetooth-low-energy/nRF52832#Overview nRF52 SoCs] können für Bluetooth und für Übertragungen auf Basis des proprietären 2.4 GHZ-Nordic-Protokolls (kompatibel zu nRF24) genutzt werden. Für Bluetooth 4.1-Übertragungen steht ein Software Stack ([https://www.nordicsemi.com/eng/nordic/download_resource/33863/4/14045603 Nordic Softdevice 130, specification 0.5]) zur Verfügung.&lt;br /&gt;
Das proprietäre 2.4 GHZ-Nordic-Protokoll unterstützt das Übertragen von Packages mit Addressierung, CRC sowie dem ACK und RESEND von Packages.&lt;br /&gt;
Die nRF52-SoCs besitzen einen Cortex M4F, 64kB RAM, 512kB integrierten Flash-Speicher, einen NFC-Tag sowie vielfältige weitere Peripherie. Der Cortex ist zur Nutzung für Anwenderprogramme vorgesehen. SDKs, auch für gcc, werden angeboten. Die Bausteine wurden auf die Verwendung mit 2-Lagen-PCBs hin optimiert und werden auch im QFN48 package verfügbar sein (Stand 09/2015).&lt;br /&gt;
Für das Empfangen beträgt der Strombedarf ca. 7mA ([http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.pdf.ps/nRF52832_OPS_v0.6.pdf Product Specification 0.6, &amp;quot;radio current consumption&amp;quot;, page 224]). Dies ist relativ viel im Vergleich zu  kabelgebundenen Übertragungen, entspricht jedoch lediglich 1/8 typischer WLAN-ICs (ESP8266).&lt;br /&gt;
&lt;br /&gt;
=== ESP8266 ===&lt;br /&gt;
&lt;br /&gt;
Das WLAN-Modul kann sowohl Client als auch AP sein. Damals hat der Release einen starken Hype ausgelöst und dementsprechend ist die Community rasant gewachsen, wodurch sich sogar ein eigenes Forum entwickelt hat ([http://www.esp8266.com www.esp8266.com]). &lt;br /&gt;
&lt;br /&gt;
Das Modul kann man über AT-Kommandos steuern, es gibt ein LUA-Skript sowie MicroPython Interpreter und vieles mehr. Zudem kann den integrieten SoC auch direkt programmieren, sodass man für einfache Aufgaben (Temperatur auslesen etc.) keinen extra Mikrocontroller braucht. Für die Programmierung wird vom Hersteller eine fertig eingerichtete VM zur Verfügung gestellt, es gibt aber auch eine Port vom GCC und selbst in die Arduino Umgebung ist der ESP8266 mittlerweile eingepflegt.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.micropython.org/en/latest/esp8266/esp8266/tutorial/intro.html ESP8266 MicroPython]&lt;br /&gt;
* [http://www.nodemcu.com/index_en.html NodeMCU LUA Interpreter Website]&lt;br /&gt;
** [https://github.com/nodemcu/nodemcu-firmware NodeMCU Firmware]&lt;br /&gt;
* [https://github.com/esp8266/Arduino Arduino ESP8266]&lt;br /&gt;
&lt;br /&gt;
=== EMW3162 ===&lt;br /&gt;
&lt;br /&gt;
[http://hackaday.com/2015/03/24/emw3162-wifi-120mhz-needs-attention/ HackADay Eintrag]&lt;br /&gt;
&lt;br /&gt;
[http://www.joinmx.com/uploadfiles/soft/EMW/DS0006E_EMW3162_V2.2.pdf Datasheet]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/354149 Thread zum Artikel (Fragen, Anregungen etc. hier posten)]&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RF12.pdf Datenblatt des ICs RF12] (PDF)&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM12.pdf Datenblatt des Moduls RFM12] (PDF)&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM69CW-V1.1.pdf Datenblatt RFM69CW]&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM69W-V1.3.pdf Datenblatt RFM69W]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/NRF24L01_Tutorial kleines NRF24L01+ Tutorial in C]&lt;br /&gt;
&lt;br /&gt;
== Fußnoten ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]]&lt;br /&gt;
[[Kategorie:RFM12| ]]&lt;br /&gt;
[[Kategorie:Funk]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;br /&gt;
[[Category:Hausbus]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=%C3%9Cbersicht_Funkmodule&amp;diff=100678</id>
		<title>Übersicht Funkmodule</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=%C3%9Cbersicht_Funkmodule&amp;diff=100678"/>
		<updated>2019-06-15T09:50:28Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* LoRaWAN RFM95W, RFM96W, RFM97W, RFM98W */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier eine kleine Übersicht über die vorhanden Funkmodule. in Arbeit !!!&lt;br /&gt;
&lt;br /&gt;
== Übersicht ==&lt;br /&gt;
&lt;br /&gt;
=== Module ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! !! [[RFM12|RFM12B]] !! [[RFM69|RFM69(H)(C)W]] !! [[NRF24L01_Tutorial|nRF24L01+]] !! [[ESP8266]] !! EMW3162&lt;br /&gt;
|-&lt;br /&gt;
| aktiv (Stand: Feb 2017)|| ja || ja || ja || ja || ja&lt;br /&gt;
|-&lt;br /&gt;
| Frequenz || 433, 868 Mhz || 315, 433, 868, 915 Mhz || 2,4 Ghz || colspan=&amp;quot;2&amp;quot;  style=&amp;quot;text-align:center&amp;quot; | 2,4 Ghz (WLAN 802.11b/g/n)&lt;br /&gt;
|-&lt;br /&gt;
| Vcc || 2,2 - 3,8 V || 1,8 - 3,6 V || 1,9 - 3,6 V || 3,3 V || 3,3V&lt;br /&gt;
|-&lt;br /&gt;
| Max. Datenrate || 115,2 kbit/s || 300 kbit/s || 2 Mbit/s || 4,5-7 Mbit/s &amp;lt;ref&amp;gt;http://www.mikrocontroller.net/articles/ESP8266#Datendurchsatz.2FPerformanz&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;http://www.mikrocontroller.net/topic/342240?page=single#3857630&amp;lt;/ref&amp;gt; || bis 20 Mbit/s&amp;lt;ref&amp;gt;http://www.seeedstudio.com/depot/EMW3162-WiFi-Module-p-2122.html&amp;lt;/ref&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Min. Datenrate || 1,2 kbit/s || 1,2 kbit/s || 0,25 Mbit/s || colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | ? Mbit/s&lt;br /&gt;
|-&lt;br /&gt;
| Stromaufnahme (TX/RX/Sleep) || 22 mA/11 mA/0,3 µA || 45 mA/16 mA/0,1 µA&amp;lt;br&amp;gt;130 mA/16 mA/0,1 µA (HCW) || 11,3 mA/13,3 mA/0,9 µA&amp;lt;br&amp;gt;115 mA/13,3 mA/0,9 µA (PA) || 215 mA/60 mA/0,9 µA || 24mA (20kbit/s), 320mA (max) / 52-59mA / 7mA (connected), 200 µA (sleep), 2µA (off)&lt;br /&gt;
|-&lt;br /&gt;
| Sendeleistung || +5 dBm || +13 dBm (~20 mW !)&amp;lt;br /&amp;gt;+20 dBm (HCW) || 0 dBm&amp;lt;br /&amp;gt;+20 dBm (mit PA) || +15 dBm (802.11g/n)&amp;lt;br /&amp;gt;+18,5 dBm (802.11b) || +14,5 dBm (802.11n)&amp;lt;br /&amp;gt;+15,5 dBm (802.11g)&amp;lt;br /&amp;gt;+18,5 dBm (802.11b)&lt;br /&gt;
|- &lt;br /&gt;
| Empfindlichkeit || -105 dBm || -120 dBm || -94 dBm || -98 dBm (802.11b)&amp;lt;br /&amp;gt;-93 dBm (802.11g) || -96 dBm (802.11b)&amp;lt;br /&amp;gt;-90 dBm (802.11g)&amp;lt;br /&amp;gt;-89 dBm (802.11n)&lt;br /&gt;
|-&lt;br /&gt;
| Reichweite || + || ++ || - || - || -&lt;br /&gt;
|-&lt;br /&gt;
| Paketmanagement || keines || Prüfsumme, Adresse, Verschlüsslung || Prüfsumme, Adresse, Retransmit || IPv4&amp;lt;br&amp;gt;(TCP und UDP) || IPv4 und IPv6&amp;lt;br&amp;gt;(TCP und UDP)&lt;br /&gt;
|-&lt;br /&gt;
| Verschlüsselung || - || ○ (AES, nur ECB &amp;lt;ref&amp;gt;https://de.wikipedia.org/wiki/Electronic_Code_Book_Mode&amp;lt;/ref&amp;gt; ) || - || colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | + (WLAN Verschlüsslung)&lt;br /&gt;
|-&lt;br /&gt;
| FIFO Size || 16 Bit || 66 Bytes || 3 separate 32 bytes TX and RX FIFOs || ? || ?&lt;br /&gt;
|-&lt;br /&gt;
| Support im Forum || ++ || ○ || ++ || + || ○&lt;br /&gt;
|-&lt;br /&gt;
| einfache Lib (Prüfsumme, Sender, Retransmit) || ? || ja, siehe Links || ja, siehe Links || Firmware buggy (01.2015) || WICED, UART Firmware&lt;br /&gt;
|-&lt;br /&gt;
|Preis (02.2017) || 2-4 € || 2-4 € || 0,7-2 € || 1-2 € || 8-10€&lt;br /&gt;
|-&lt;br /&gt;
|Verfügbarkeit (02.2017) || ++ || + || ++ || ++ || +&lt;br /&gt;
|-&lt;br /&gt;
|Bezugsquellen (02.2017) || [http://www.pollin.de/shop/suchergebnis.html?S_TEXT=RFM12B Pollin], ebay, Ali || [http://www.pollin.de/shop/suchergebnis.html?S_TEXT=RFM69 Pollin], Ali, ebay || ebay, Ali || ebay, Ali, [http://www.amazon.de/s/url=search-alias%3Daps&amp;amp;field-keywords=esp8266ex Amazon] || Ali, [http://www.seeedstudio.com/depot/EMW3162-WiFi-Module-p-2122.html Seedstudio]&lt;br /&gt;
|-&lt;br /&gt;
|Tauglichkeit SmartHome || + || ++ || ○ || ○ || ○&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Frequenzen ===&lt;br /&gt;
&lt;br /&gt;
siehe auch [[Allgemeinzuteilung]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! !! 433 Mhz !! 868 Mhz !! 2400 Mhz (2,4 Ghz)&lt;br /&gt;
|-&lt;br /&gt;
| Sendeleistung || 10 mW ERP || 10-500 mW ERP|| 10 mW ERP&amp;lt;br /&amp;gt;100 mW EIRP&lt;br /&gt;
|-&lt;br /&gt;
| Duty Cycle || keinen || 0,1-10 % || keinen&lt;br /&gt;
|-&lt;br /&gt;
| Reichweite || ++ || + || - &lt;br /&gt;
|-&lt;br /&gt;
| Störung durch andere || + || ○ || ++ &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Details zu den Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== RFM12B ===&lt;br /&gt;
&lt;br /&gt;
Die RFM12(B) sind wohl die Urgesteine der drahtlosen Kommunikation: Tausendfach erprobt!&lt;br /&gt;
&lt;br /&gt;
* [[AVR RFM12]]&lt;br /&gt;
* [[RFM12 Protokoll Stack]]&lt;br /&gt;
* [[RF_SOAP]]&lt;br /&gt;
* [[Pollin_Funk-AVR-Evaluationsboard]]&lt;br /&gt;
* [http://www.mikrocontroller-elektronik.de/funkmodul-programmierung-rfm12b-rn-mikrofunk/ RN-MikroFunk AVR Miniatur Evaluationsboard und Tutorial]&lt;br /&gt;
&lt;br /&gt;
=== RFM69 ===&lt;br /&gt;
&lt;br /&gt;
Der RFM69 ist der Nachfolger des RFM12B. Er untersützt mehr Frequenzen und hat eine leicht höhere Sendeleistung. Die größten Neuerungen sind die integrierte AES-Verschlüsselung sowie das eingebaute Paketmanagement, das sich um Prüfsummen, Adressen kümmert.&lt;br /&gt;
&lt;br /&gt;
Es gibt folgende Module:&lt;br /&gt;
* RFM69HCW, mit zusätzlicher PA, (+20 dBm TX)&lt;br /&gt;
* RFM69HW, mit zusätzlicher PA, (+20 dBm TX)&lt;br /&gt;
* RFM69CW, Pinkompatibel zum RFM12B&lt;br /&gt;
* RFM69W&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://github.com/LowPowerLab/RFM69 RFM69 Lib zur Ansteuerung mit Arduino]&lt;br /&gt;
* [http://www.airspayce.com/mikem/arduino/RadioHead/index.html RadioHead Lib zum Aufbau eines MESH-Netzwerkes]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/332579 Thread im Forum: Wer verwendet RFM69]&lt;br /&gt;
&lt;br /&gt;
=== LoRaWAN RFM95W, RFM96W, RFM97W, RFM98W ===&lt;br /&gt;
&lt;br /&gt;
Diese Module von HopeRF unterstützen das LoRaWAN Protokoll. Sie unterscheiden sich hauptsächlich im unterstützen Frequenzbereich:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Typ !! 315 MHz !! 433 MHz !! 868 MHz !! 915 MHz&lt;br /&gt;
|-&lt;br /&gt;
| RFM96W-315S2 || x ||   ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM96W-433S2 ||   || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM98W-315S2 || x ||   ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM98W-433S2 ||   || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM95W-868S2 ||   ||   || x ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM95W-915S2 ||   ||   ||   || x&lt;br /&gt;
|-&lt;br /&gt;
| RFM97W-868S2 ||   ||   || x || &lt;br /&gt;
|-&lt;br /&gt;
| RFM97W-915S2 ||   ||   ||   || x&lt;br /&gt;
|-&lt;br /&gt;
| SX1278       || x || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| SX1276       || x || x || x || x&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die aufgeführten RFM9x Module enthalten jeweils entweder den SX1278 oder den SX1276 Chip.&lt;br /&gt;
Es gibt noch weitere Module z.B für eine größere Reichweite.&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/453948 Thread im Forum: Unterschiede RFM96W RFM98W]&lt;br /&gt;
* [https://www.hoperf.com/modules/lora/RFM95.html Infos zu den Modulen bei HopeRF]&lt;br /&gt;
* [https://www.thethingsnetwork.org/#metrics LoRaWAN The Things Network: LoRaWAN Gateways]&lt;br /&gt;
&lt;br /&gt;
=== nRF24L01+ ===&lt;br /&gt;
&lt;br /&gt;
Die wohl billigsten Funkmodule. Funken im 2,4 Ghz Band und haben alles in Hardware integriert für eine einfache Kommunikation. Gibt es schon länger am Markt, daher gibt es schon viele erprobte Bibliotheken für die Ansteuerung vom Arduino, RaspberryPi, ...&lt;br /&gt;
&lt;br /&gt;
Achtung! Nicht mit nRF24L01 (ohne Plus) verwechseln. Diese Module werden nicht mehr hergestellt und sind veraltet!&lt;br /&gt;
&lt;br /&gt;
* [http://tmrh20.github.io/RF24/ Bibliothek für die Ansteuerung über Arduino, RaspberryPi, Intel Galileo...]&lt;br /&gt;
* [http://tmrh20.github.io/RF24Network/ Bibliothek für eine ZigBee ähnliche Kommunikation (Mesh Nettwerk)]&lt;br /&gt;
&lt;br /&gt;
=== SE8R01 ===&lt;br /&gt;
&lt;br /&gt;
Wird bei AliExpress und E-Bay auch als simular nRF24L01+ angeboten. Er kostet die Hälfte vom nRF24L01+ und hat eine sehr kleine Bauform. Er ist NICHT kompatibel zum nRF24L01+ weder auf dem Funkkanal noch im Umgang mit den Konfigurationsregistern.&lt;br /&gt;
Die bislang brauchbarsten Informationen zu den Chip gab es unter [https://forum.arduino.cc/index.php?topic=366294.0 Finally Working code for se8r01(similar nrf24l01) 2mps, 1mps and 500kps]&lt;br /&gt;
Es scheint eine Datasheet V2.1 zu geben, ich habe es aber noch nirgends downloaden können.&lt;br /&gt;
&lt;br /&gt;
[[Benutzer:Prediger|Prediger]] ([[Benutzer Diskussion:Prediger|Diskussion]]) &lt;br /&gt;
&lt;br /&gt;
=== nRF52 ===&lt;br /&gt;
Die [https://www.nordicsemi.com/eng/Products/Bluetooth-Smart-Bluetooth-low-energy/nRF52832#Overview nRF52 SoCs] können für Bluetooth und für Übertragungen auf Basis des proprietären 2.4 GHZ-Nordic-Protokolls (kompatibel zu nRF24) genutzt werden. Für Bluetooth 4.1-Übertragungen steht ein Software Stack ([https://www.nordicsemi.com/eng/nordic/download_resource/33863/4/14045603 Nordic Softdevice 130, specification 0.5]) zur Verfügung.&lt;br /&gt;
Das proprietäre 2.4 GHZ-Nordic-Protokoll unterstützt das Übertragen von Packages mit Addressierung, CRC sowie dem ACK und RESEND von Packages.&lt;br /&gt;
Die nRF52-SoCs besitzen einen Cortex M4F, 64kB RAM, 512kB integrierten Flash-Speicher, einen NFC-Tag sowie vielfältige weitere Peripherie. Der Cortex ist zur Nutzung für Anwenderprogramme vorgesehen. SDKs, auch für gcc, werden angeboten. Die Bausteine wurden auf die Verwendung mit 2-Lagen-PCBs hin optimiert und werden auch im QFN48 package verfügbar sein (Stand 09/2015).&lt;br /&gt;
Für das Empfangen beträgt der Strombedarf ca. 7mA ([http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.pdf.ps/nRF52832_OPS_v0.6.pdf Product Specification 0.6, &amp;quot;radio current consumption&amp;quot;, page 224]). Dies ist relativ viel im Vergleich zu  kabelgebundenen Übertragungen, entspricht jedoch lediglich 1/8 typischer WLAN-ICs (ESP8266).&lt;br /&gt;
&lt;br /&gt;
=== ESP8266 ===&lt;br /&gt;
&lt;br /&gt;
Das WLAN-Modul kann sowohl Client als auch AP sein. Damals hat der Release einen starken Hype ausgelöst und dementsprechend ist die Community rasant gewachsen, wodurch sich sogar ein eigenes Forum entwickelt hat ([http://www.esp8266.com www.esp8266.com]). &lt;br /&gt;
&lt;br /&gt;
Das Modul kann man über AT-Kommandos steuern, es gibt ein LUA-Skript sowie MicroPython Interpreter und vieles mehr. Zudem kann den integrieten SoC auch direkt programmieren, sodass man für einfache Aufgaben (Temperatur auslesen etc.) keinen extra Mikrocontroller braucht. Für die Programmierung wird vom Hersteller eine fertig eingerichtete VM zur Verfügung gestellt, es gibt aber auch eine Port vom GCC und selbst in die Arduino Umgebung ist der ESP8266 mittlerweile eingepflegt.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.micropython.org/en/latest/esp8266/esp8266/tutorial/intro.html ESP8266 MicroPython]&lt;br /&gt;
* [http://www.nodemcu.com/index_en.html NodeMCU LUA Interpreter Website]&lt;br /&gt;
** [https://github.com/nodemcu/nodemcu-firmware NodeMCU Firmware]&lt;br /&gt;
* [https://github.com/esp8266/Arduino Arduino ESP8266]&lt;br /&gt;
&lt;br /&gt;
=== EMW3162 ===&lt;br /&gt;
&lt;br /&gt;
[http://hackaday.com/2015/03/24/emw3162-wifi-120mhz-needs-attention/ HackADay Eintrag]&lt;br /&gt;
&lt;br /&gt;
[http://www.joinmx.com/uploadfiles/soft/EMW/DS0006E_EMW3162_V2.2.pdf Datasheet]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/354149 Thread zum Artikel (Fragen, Anregungen etc. hier posten)]&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RF12.pdf Datenblatt des ICs RF12] (PDF)&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM12.pdf Datenblatt des Moduls RFM12] (PDF)&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM69CW-V1.1.pdf Datenblatt RFM69CW]&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM69W-V1.3.pdf Datenblatt RFM69W]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/NRF24L01_Tutorial kleines NRF24L01+ Tutorial in C]&lt;br /&gt;
&lt;br /&gt;
== Fußnoten ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]]&lt;br /&gt;
[[Kategorie:RFM12| ]]&lt;br /&gt;
[[Kategorie:Funk]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;br /&gt;
[[Category:Hausbus]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=%C3%9Cbersicht_Funkmodule&amp;diff=100677</id>
		<title>Übersicht Funkmodule</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=%C3%9Cbersicht_Funkmodule&amp;diff=100677"/>
		<updated>2019-06-15T09:47:57Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* LoRaWAN RFM95W, RFM96W, RFM97W, RFM98W */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier eine kleine Übersicht über die vorhanden Funkmodule. in Arbeit !!!&lt;br /&gt;
&lt;br /&gt;
== Übersicht ==&lt;br /&gt;
&lt;br /&gt;
=== Module ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! !! [[RFM12|RFM12B]] !! [[RFM69|RFM69(H)(C)W]] !! [[NRF24L01_Tutorial|nRF24L01+]] !! [[ESP8266]] !! EMW3162&lt;br /&gt;
|-&lt;br /&gt;
| aktiv (Stand: Feb 2017)|| ja || ja || ja || ja || ja&lt;br /&gt;
|-&lt;br /&gt;
| Frequenz || 433, 868 Mhz || 315, 433, 868, 915 Mhz || 2,4 Ghz || colspan=&amp;quot;2&amp;quot;  style=&amp;quot;text-align:center&amp;quot; | 2,4 Ghz (WLAN 802.11b/g/n)&lt;br /&gt;
|-&lt;br /&gt;
| Vcc || 2,2 - 3,8 V || 1,8 - 3,6 V || 1,9 - 3,6 V || 3,3 V || 3,3V&lt;br /&gt;
|-&lt;br /&gt;
| Max. Datenrate || 115,2 kbit/s || 300 kbit/s || 2 Mbit/s || 4,5-7 Mbit/s &amp;lt;ref&amp;gt;http://www.mikrocontroller.net/articles/ESP8266#Datendurchsatz.2FPerformanz&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;http://www.mikrocontroller.net/topic/342240?page=single#3857630&amp;lt;/ref&amp;gt; || bis 20 Mbit/s&amp;lt;ref&amp;gt;http://www.seeedstudio.com/depot/EMW3162-WiFi-Module-p-2122.html&amp;lt;/ref&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Min. Datenrate || 1,2 kbit/s || 1,2 kbit/s || 0,25 Mbit/s || colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | ? Mbit/s&lt;br /&gt;
|-&lt;br /&gt;
| Stromaufnahme (TX/RX/Sleep) || 22 mA/11 mA/0,3 µA || 45 mA/16 mA/0,1 µA&amp;lt;br&amp;gt;130 mA/16 mA/0,1 µA (HCW) || 11,3 mA/13,3 mA/0,9 µA&amp;lt;br&amp;gt;115 mA/13,3 mA/0,9 µA (PA) || 215 mA/60 mA/0,9 µA || 24mA (20kbit/s), 320mA (max) / 52-59mA / 7mA (connected), 200 µA (sleep), 2µA (off)&lt;br /&gt;
|-&lt;br /&gt;
| Sendeleistung || +5 dBm || +13 dBm (~20 mW !)&amp;lt;br /&amp;gt;+20 dBm (HCW) || 0 dBm&amp;lt;br /&amp;gt;+20 dBm (mit PA) || +15 dBm (802.11g/n)&amp;lt;br /&amp;gt;+18,5 dBm (802.11b) || +14,5 dBm (802.11n)&amp;lt;br /&amp;gt;+15,5 dBm (802.11g)&amp;lt;br /&amp;gt;+18,5 dBm (802.11b)&lt;br /&gt;
|- &lt;br /&gt;
| Empfindlichkeit || -105 dBm || -120 dBm || -94 dBm || -98 dBm (802.11b)&amp;lt;br /&amp;gt;-93 dBm (802.11g) || -96 dBm (802.11b)&amp;lt;br /&amp;gt;-90 dBm (802.11g)&amp;lt;br /&amp;gt;-89 dBm (802.11n)&lt;br /&gt;
|-&lt;br /&gt;
| Reichweite || + || ++ || - || - || -&lt;br /&gt;
|-&lt;br /&gt;
| Paketmanagement || keines || Prüfsumme, Adresse, Verschlüsslung || Prüfsumme, Adresse, Retransmit || IPv4&amp;lt;br&amp;gt;(TCP und UDP) || IPv4 und IPv6&amp;lt;br&amp;gt;(TCP und UDP)&lt;br /&gt;
|-&lt;br /&gt;
| Verschlüsselung || - || ○ (AES, nur ECB &amp;lt;ref&amp;gt;https://de.wikipedia.org/wiki/Electronic_Code_Book_Mode&amp;lt;/ref&amp;gt; ) || - || colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | + (WLAN Verschlüsslung)&lt;br /&gt;
|-&lt;br /&gt;
| FIFO Size || 16 Bit || 66 Bytes || 3 separate 32 bytes TX and RX FIFOs || ? || ?&lt;br /&gt;
|-&lt;br /&gt;
| Support im Forum || ++ || ○ || ++ || + || ○&lt;br /&gt;
|-&lt;br /&gt;
| einfache Lib (Prüfsumme, Sender, Retransmit) || ? || ja, siehe Links || ja, siehe Links || Firmware buggy (01.2015) || WICED, UART Firmware&lt;br /&gt;
|-&lt;br /&gt;
|Preis (02.2017) || 2-4 € || 2-4 € || 0,7-2 € || 1-2 € || 8-10€&lt;br /&gt;
|-&lt;br /&gt;
|Verfügbarkeit (02.2017) || ++ || + || ++ || ++ || +&lt;br /&gt;
|-&lt;br /&gt;
|Bezugsquellen (02.2017) || [http://www.pollin.de/shop/suchergebnis.html?S_TEXT=RFM12B Pollin], ebay, Ali || [http://www.pollin.de/shop/suchergebnis.html?S_TEXT=RFM69 Pollin], Ali, ebay || ebay, Ali || ebay, Ali, [http://www.amazon.de/s/url=search-alias%3Daps&amp;amp;field-keywords=esp8266ex Amazon] || Ali, [http://www.seeedstudio.com/depot/EMW3162-WiFi-Module-p-2122.html Seedstudio]&lt;br /&gt;
|-&lt;br /&gt;
|Tauglichkeit SmartHome || + || ++ || ○ || ○ || ○&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Frequenzen ===&lt;br /&gt;
&lt;br /&gt;
siehe auch [[Allgemeinzuteilung]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! !! 433 Mhz !! 868 Mhz !! 2400 Mhz (2,4 Ghz)&lt;br /&gt;
|-&lt;br /&gt;
| Sendeleistung || 10 mW ERP || 10-500 mW ERP|| 10 mW ERP&amp;lt;br /&amp;gt;100 mW EIRP&lt;br /&gt;
|-&lt;br /&gt;
| Duty Cycle || keinen || 0,1-10 % || keinen&lt;br /&gt;
|-&lt;br /&gt;
| Reichweite || ++ || + || - &lt;br /&gt;
|-&lt;br /&gt;
| Störung durch andere || + || ○ || ++ &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Details zu den Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== RFM12B ===&lt;br /&gt;
&lt;br /&gt;
Die RFM12(B) sind wohl die Urgesteine der drahtlosen Kommunikation: Tausendfach erprobt!&lt;br /&gt;
&lt;br /&gt;
* [[AVR RFM12]]&lt;br /&gt;
* [[RFM12 Protokoll Stack]]&lt;br /&gt;
* [[RF_SOAP]]&lt;br /&gt;
* [[Pollin_Funk-AVR-Evaluationsboard]]&lt;br /&gt;
* [http://www.mikrocontroller-elektronik.de/funkmodul-programmierung-rfm12b-rn-mikrofunk/ RN-MikroFunk AVR Miniatur Evaluationsboard und Tutorial]&lt;br /&gt;
&lt;br /&gt;
=== RFM69 ===&lt;br /&gt;
&lt;br /&gt;
Der RFM69 ist der Nachfolger des RFM12B. Er untersützt mehr Frequenzen und hat eine leicht höhere Sendeleistung. Die größten Neuerungen sind die integrierte AES-Verschlüsselung sowie das eingebaute Paketmanagement, das sich um Prüfsummen, Adressen kümmert.&lt;br /&gt;
&lt;br /&gt;
Es gibt folgende Module:&lt;br /&gt;
* RFM69HCW, mit zusätzlicher PA, (+20 dBm TX)&lt;br /&gt;
* RFM69HW, mit zusätzlicher PA, (+20 dBm TX)&lt;br /&gt;
* RFM69CW, Pinkompatibel zum RFM12B&lt;br /&gt;
* RFM69W&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://github.com/LowPowerLab/RFM69 RFM69 Lib zur Ansteuerung mit Arduino]&lt;br /&gt;
* [http://www.airspayce.com/mikem/arduino/RadioHead/index.html RadioHead Lib zum Aufbau eines MESH-Netzwerkes]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/332579 Thread im Forum: Wer verwendet RFM69]&lt;br /&gt;
&lt;br /&gt;
=== LoRaWAN RFM95W, RFM96W, RFM97W, RFM98W ===&lt;br /&gt;
&lt;br /&gt;
Diese Module von HopeRF unterstützen das LoRaWAN Protokoll. Sie unterscheiden sich hauptsächlich im unterstützen Frequenzbereich:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Typ !! 315 MHz !! 433 MHz !! 868 MHz !! 915 MHz&lt;br /&gt;
|-&lt;br /&gt;
| RFM96W-315S2 || x ||   ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM96W-433S2 ||   || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM98W-315S2 || x ||   ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM98W-433S2 ||   || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM95W-868S2 ||   ||   || x ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM95W-915S2 ||   ||   ||   || x&lt;br /&gt;
|-&lt;br /&gt;
| RFM97W-868S2 ||   ||   || x || &lt;br /&gt;
|-&lt;br /&gt;
| RFM97W-915S2 ||   ||   ||   || x&lt;br /&gt;
|-&lt;br /&gt;
| SX1278       || x || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| SX1276       || x || x || x || x&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Intern enthalten die aufgeführen RFM9x Chips jeweils entweder den SX1278 oder den SX1276 Kern.&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/453948 Thread im Forum: Unterschiede RFM96W RFM98W]&lt;br /&gt;
* [https://www.hoperf.com/modules/lora/RFM95.html Infos zu den Modulen bei HopeRF]&lt;br /&gt;
* [https://www.thethingsnetwork.org/#metrics LoRaWAN The Things Network: LoRaWAN Gateways]&lt;br /&gt;
&lt;br /&gt;
=== nRF24L01+ ===&lt;br /&gt;
&lt;br /&gt;
Die wohl billigsten Funkmodule. Funken im 2,4 Ghz Band und haben alles in Hardware integriert für eine einfache Kommunikation. Gibt es schon länger am Markt, daher gibt es schon viele erprobte Bibliotheken für die Ansteuerung vom Arduino, RaspberryPi, ...&lt;br /&gt;
&lt;br /&gt;
Achtung! Nicht mit nRF24L01 (ohne Plus) verwechseln. Diese Module werden nicht mehr hergestellt und sind veraltet!&lt;br /&gt;
&lt;br /&gt;
* [http://tmrh20.github.io/RF24/ Bibliothek für die Ansteuerung über Arduino, RaspberryPi, Intel Galileo...]&lt;br /&gt;
* [http://tmrh20.github.io/RF24Network/ Bibliothek für eine ZigBee ähnliche Kommunikation (Mesh Nettwerk)]&lt;br /&gt;
&lt;br /&gt;
=== SE8R01 ===&lt;br /&gt;
&lt;br /&gt;
Wird bei AliExpress und E-Bay auch als simular nRF24L01+ angeboten. Er kostet die Hälfte vom nRF24L01+ und hat eine sehr kleine Bauform. Er ist NICHT kompatibel zum nRF24L01+ weder auf dem Funkkanal noch im Umgang mit den Konfigurationsregistern.&lt;br /&gt;
Die bislang brauchbarsten Informationen zu den Chip gab es unter [https://forum.arduino.cc/index.php?topic=366294.0 Finally Working code for se8r01(similar nrf24l01) 2mps, 1mps and 500kps]&lt;br /&gt;
Es scheint eine Datasheet V2.1 zu geben, ich habe es aber noch nirgends downloaden können.&lt;br /&gt;
&lt;br /&gt;
[[Benutzer:Prediger|Prediger]] ([[Benutzer Diskussion:Prediger|Diskussion]]) &lt;br /&gt;
&lt;br /&gt;
=== nRF52 ===&lt;br /&gt;
Die [https://www.nordicsemi.com/eng/Products/Bluetooth-Smart-Bluetooth-low-energy/nRF52832#Overview nRF52 SoCs] können für Bluetooth und für Übertragungen auf Basis des proprietären 2.4 GHZ-Nordic-Protokolls (kompatibel zu nRF24) genutzt werden. Für Bluetooth 4.1-Übertragungen steht ein Software Stack ([https://www.nordicsemi.com/eng/nordic/download_resource/33863/4/14045603 Nordic Softdevice 130, specification 0.5]) zur Verfügung.&lt;br /&gt;
Das proprietäre 2.4 GHZ-Nordic-Protokoll unterstützt das Übertragen von Packages mit Addressierung, CRC sowie dem ACK und RESEND von Packages.&lt;br /&gt;
Die nRF52-SoCs besitzen einen Cortex M4F, 64kB RAM, 512kB integrierten Flash-Speicher, einen NFC-Tag sowie vielfältige weitere Peripherie. Der Cortex ist zur Nutzung für Anwenderprogramme vorgesehen. SDKs, auch für gcc, werden angeboten. Die Bausteine wurden auf die Verwendung mit 2-Lagen-PCBs hin optimiert und werden auch im QFN48 package verfügbar sein (Stand 09/2015).&lt;br /&gt;
Für das Empfangen beträgt der Strombedarf ca. 7mA ([http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.pdf.ps/nRF52832_OPS_v0.6.pdf Product Specification 0.6, &amp;quot;radio current consumption&amp;quot;, page 224]). Dies ist relativ viel im Vergleich zu  kabelgebundenen Übertragungen, entspricht jedoch lediglich 1/8 typischer WLAN-ICs (ESP8266).&lt;br /&gt;
&lt;br /&gt;
=== ESP8266 ===&lt;br /&gt;
&lt;br /&gt;
Das WLAN-Modul kann sowohl Client als auch AP sein. Damals hat der Release einen starken Hype ausgelöst und dementsprechend ist die Community rasant gewachsen, wodurch sich sogar ein eigenes Forum entwickelt hat ([http://www.esp8266.com www.esp8266.com]). &lt;br /&gt;
&lt;br /&gt;
Das Modul kann man über AT-Kommandos steuern, es gibt ein LUA-Skript sowie MicroPython Interpreter und vieles mehr. Zudem kann den integrieten SoC auch direkt programmieren, sodass man für einfache Aufgaben (Temperatur auslesen etc.) keinen extra Mikrocontroller braucht. Für die Programmierung wird vom Hersteller eine fertig eingerichtete VM zur Verfügung gestellt, es gibt aber auch eine Port vom GCC und selbst in die Arduino Umgebung ist der ESP8266 mittlerweile eingepflegt.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.micropython.org/en/latest/esp8266/esp8266/tutorial/intro.html ESP8266 MicroPython]&lt;br /&gt;
* [http://www.nodemcu.com/index_en.html NodeMCU LUA Interpreter Website]&lt;br /&gt;
** [https://github.com/nodemcu/nodemcu-firmware NodeMCU Firmware]&lt;br /&gt;
* [https://github.com/esp8266/Arduino Arduino ESP8266]&lt;br /&gt;
&lt;br /&gt;
=== EMW3162 ===&lt;br /&gt;
&lt;br /&gt;
[http://hackaday.com/2015/03/24/emw3162-wifi-120mhz-needs-attention/ HackADay Eintrag]&lt;br /&gt;
&lt;br /&gt;
[http://www.joinmx.com/uploadfiles/soft/EMW/DS0006E_EMW3162_V2.2.pdf Datasheet]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/354149 Thread zum Artikel (Fragen, Anregungen etc. hier posten)]&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RF12.pdf Datenblatt des ICs RF12] (PDF)&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM12.pdf Datenblatt des Moduls RFM12] (PDF)&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM69CW-V1.1.pdf Datenblatt RFM69CW]&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM69W-V1.3.pdf Datenblatt RFM69W]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/NRF24L01_Tutorial kleines NRF24L01+ Tutorial in C]&lt;br /&gt;
&lt;br /&gt;
== Fußnoten ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]]&lt;br /&gt;
[[Kategorie:RFM12| ]]&lt;br /&gt;
[[Kategorie:Funk]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;br /&gt;
[[Category:Hausbus]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=%C3%9Cbersicht_Funkmodule&amp;diff=100676</id>
		<title>Übersicht Funkmodule</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=%C3%9Cbersicht_Funkmodule&amp;diff=100676"/>
		<updated>2019-06-15T09:41:53Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* RFM69 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier eine kleine Übersicht über die vorhanden Funkmodule. in Arbeit !!!&lt;br /&gt;
&lt;br /&gt;
== Übersicht ==&lt;br /&gt;
&lt;br /&gt;
=== Module ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! !! [[RFM12|RFM12B]] !! [[RFM69|RFM69(H)(C)W]] !! [[NRF24L01_Tutorial|nRF24L01+]] !! [[ESP8266]] !! EMW3162&lt;br /&gt;
|-&lt;br /&gt;
| aktiv (Stand: Feb 2017)|| ja || ja || ja || ja || ja&lt;br /&gt;
|-&lt;br /&gt;
| Frequenz || 433, 868 Mhz || 315, 433, 868, 915 Mhz || 2,4 Ghz || colspan=&amp;quot;2&amp;quot;  style=&amp;quot;text-align:center&amp;quot; | 2,4 Ghz (WLAN 802.11b/g/n)&lt;br /&gt;
|-&lt;br /&gt;
| Vcc || 2,2 - 3,8 V || 1,8 - 3,6 V || 1,9 - 3,6 V || 3,3 V || 3,3V&lt;br /&gt;
|-&lt;br /&gt;
| Max. Datenrate || 115,2 kbit/s || 300 kbit/s || 2 Mbit/s || 4,5-7 Mbit/s &amp;lt;ref&amp;gt;http://www.mikrocontroller.net/articles/ESP8266#Datendurchsatz.2FPerformanz&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;http://www.mikrocontroller.net/topic/342240?page=single#3857630&amp;lt;/ref&amp;gt; || bis 20 Mbit/s&amp;lt;ref&amp;gt;http://www.seeedstudio.com/depot/EMW3162-WiFi-Module-p-2122.html&amp;lt;/ref&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Min. Datenrate || 1,2 kbit/s || 1,2 kbit/s || 0,25 Mbit/s || colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | ? Mbit/s&lt;br /&gt;
|-&lt;br /&gt;
| Stromaufnahme (TX/RX/Sleep) || 22 mA/11 mA/0,3 µA || 45 mA/16 mA/0,1 µA&amp;lt;br&amp;gt;130 mA/16 mA/0,1 µA (HCW) || 11,3 mA/13,3 mA/0,9 µA&amp;lt;br&amp;gt;115 mA/13,3 mA/0,9 µA (PA) || 215 mA/60 mA/0,9 µA || 24mA (20kbit/s), 320mA (max) / 52-59mA / 7mA (connected), 200 µA (sleep), 2µA (off)&lt;br /&gt;
|-&lt;br /&gt;
| Sendeleistung || +5 dBm || +13 dBm (~20 mW !)&amp;lt;br /&amp;gt;+20 dBm (HCW) || 0 dBm&amp;lt;br /&amp;gt;+20 dBm (mit PA) || +15 dBm (802.11g/n)&amp;lt;br /&amp;gt;+18,5 dBm (802.11b) || +14,5 dBm (802.11n)&amp;lt;br /&amp;gt;+15,5 dBm (802.11g)&amp;lt;br /&amp;gt;+18,5 dBm (802.11b)&lt;br /&gt;
|- &lt;br /&gt;
| Empfindlichkeit || -105 dBm || -120 dBm || -94 dBm || -98 dBm (802.11b)&amp;lt;br /&amp;gt;-93 dBm (802.11g) || -96 dBm (802.11b)&amp;lt;br /&amp;gt;-90 dBm (802.11g)&amp;lt;br /&amp;gt;-89 dBm (802.11n)&lt;br /&gt;
|-&lt;br /&gt;
| Reichweite || + || ++ || - || - || -&lt;br /&gt;
|-&lt;br /&gt;
| Paketmanagement || keines || Prüfsumme, Adresse, Verschlüsslung || Prüfsumme, Adresse, Retransmit || IPv4&amp;lt;br&amp;gt;(TCP und UDP) || IPv4 und IPv6&amp;lt;br&amp;gt;(TCP und UDP)&lt;br /&gt;
|-&lt;br /&gt;
| Verschlüsselung || - || ○ (AES, nur ECB &amp;lt;ref&amp;gt;https://de.wikipedia.org/wiki/Electronic_Code_Book_Mode&amp;lt;/ref&amp;gt; ) || - || colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | + (WLAN Verschlüsslung)&lt;br /&gt;
|-&lt;br /&gt;
| FIFO Size || 16 Bit || 66 Bytes || 3 separate 32 bytes TX and RX FIFOs || ? || ?&lt;br /&gt;
|-&lt;br /&gt;
| Support im Forum || ++ || ○ || ++ || + || ○&lt;br /&gt;
|-&lt;br /&gt;
| einfache Lib (Prüfsumme, Sender, Retransmit) || ? || ja, siehe Links || ja, siehe Links || Firmware buggy (01.2015) || WICED, UART Firmware&lt;br /&gt;
|-&lt;br /&gt;
|Preis (02.2017) || 2-4 € || 2-4 € || 0,7-2 € || 1-2 € || 8-10€&lt;br /&gt;
|-&lt;br /&gt;
|Verfügbarkeit (02.2017) || ++ || + || ++ || ++ || +&lt;br /&gt;
|-&lt;br /&gt;
|Bezugsquellen (02.2017) || [http://www.pollin.de/shop/suchergebnis.html?S_TEXT=RFM12B Pollin], ebay, Ali || [http://www.pollin.de/shop/suchergebnis.html?S_TEXT=RFM69 Pollin], Ali, ebay || ebay, Ali || ebay, Ali, [http://www.amazon.de/s/url=search-alias%3Daps&amp;amp;field-keywords=esp8266ex Amazon] || Ali, [http://www.seeedstudio.com/depot/EMW3162-WiFi-Module-p-2122.html Seedstudio]&lt;br /&gt;
|-&lt;br /&gt;
|Tauglichkeit SmartHome || + || ++ || ○ || ○ || ○&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Frequenzen ===&lt;br /&gt;
&lt;br /&gt;
siehe auch [[Allgemeinzuteilung]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! !! 433 Mhz !! 868 Mhz !! 2400 Mhz (2,4 Ghz)&lt;br /&gt;
|-&lt;br /&gt;
| Sendeleistung || 10 mW ERP || 10-500 mW ERP|| 10 mW ERP&amp;lt;br /&amp;gt;100 mW EIRP&lt;br /&gt;
|-&lt;br /&gt;
| Duty Cycle || keinen || 0,1-10 % || keinen&lt;br /&gt;
|-&lt;br /&gt;
| Reichweite || ++ || + || - &lt;br /&gt;
|-&lt;br /&gt;
| Störung durch andere || + || ○ || ++ &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Details zu den Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== RFM12B ===&lt;br /&gt;
&lt;br /&gt;
Die RFM12(B) sind wohl die Urgesteine der drahtlosen Kommunikation: Tausendfach erprobt!&lt;br /&gt;
&lt;br /&gt;
* [[AVR RFM12]]&lt;br /&gt;
* [[RFM12 Protokoll Stack]]&lt;br /&gt;
* [[RF_SOAP]]&lt;br /&gt;
* [[Pollin_Funk-AVR-Evaluationsboard]]&lt;br /&gt;
* [http://www.mikrocontroller-elektronik.de/funkmodul-programmierung-rfm12b-rn-mikrofunk/ RN-MikroFunk AVR Miniatur Evaluationsboard und Tutorial]&lt;br /&gt;
&lt;br /&gt;
=== RFM69 ===&lt;br /&gt;
&lt;br /&gt;
Der RFM69 ist der Nachfolger des RFM12B. Er untersützt mehr Frequenzen und hat eine leicht höhere Sendeleistung. Die größten Neuerungen sind die integrierte AES-Verschlüsselung sowie das eingebaute Paketmanagement, das sich um Prüfsummen, Adressen kümmert.&lt;br /&gt;
&lt;br /&gt;
Es gibt folgende Module:&lt;br /&gt;
* RFM69HCW, mit zusätzlicher PA, (+20 dBm TX)&lt;br /&gt;
* RFM69HW, mit zusätzlicher PA, (+20 dBm TX)&lt;br /&gt;
* RFM69CW, Pinkompatibel zum RFM12B&lt;br /&gt;
* RFM69W&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [https://github.com/LowPowerLab/RFM69 RFM69 Lib zur Ansteuerung mit Arduino]&lt;br /&gt;
* [http://www.airspayce.com/mikem/arduino/RadioHead/index.html RadioHead Lib zum Aufbau eines MESH-Netzwerkes]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/332579 Thread im Forum: Wer verwendet RFM69]&lt;br /&gt;
&lt;br /&gt;
=== LoRaWAN RFM95W, RFM96W, RFM97W, RFM98W ===&lt;br /&gt;
&lt;br /&gt;
Diese Module von HopeRF unterstützen das LoRaWAN Protokoll. Sie unterscheiden sich hauptsächlich im unterstützen Frequenzbereich:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Typ !! 315 MHz !! 433 MHz !! 868 MHz !! 915 MHz&lt;br /&gt;
|-&lt;br /&gt;
| RFM96W-315S2 || x ||   ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM96W-433S2 ||   || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM98W-315S2 || x ||   ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM98W-433S2 ||   || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM95W-868S2 ||   ||   || x ||&lt;br /&gt;
|-&lt;br /&gt;
| RFM95W-915S2 ||   ||   ||   || x&lt;br /&gt;
|-&lt;br /&gt;
| RFM97W-868S2 ||   ||   || x || &lt;br /&gt;
|-&lt;br /&gt;
| RFM97W-915S2 ||   ||   ||   || x&lt;br /&gt;
|-&lt;br /&gt;
| SX1278       || x || x ||   ||&lt;br /&gt;
|-&lt;br /&gt;
| SX1276       || x || x || x || x&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Intern enthalten die aufgeführen RFM9x Chips jeweils entweder den SX1278 oder den SX1276 Kern.&lt;br /&gt;
&lt;br /&gt;
=== nRF24L01+ ===&lt;br /&gt;
&lt;br /&gt;
Die wohl billigsten Funkmodule. Funken im 2,4 Ghz Band und haben alles in Hardware integriert für eine einfache Kommunikation. Gibt es schon länger am Markt, daher gibt es schon viele erprobte Bibliotheken für die Ansteuerung vom Arduino, RaspberryPi, ...&lt;br /&gt;
&lt;br /&gt;
Achtung! Nicht mit nRF24L01 (ohne Plus) verwechseln. Diese Module werden nicht mehr hergestellt und sind veraltet!&lt;br /&gt;
&lt;br /&gt;
* [http://tmrh20.github.io/RF24/ Bibliothek für die Ansteuerung über Arduino, RaspberryPi, Intel Galileo...]&lt;br /&gt;
* [http://tmrh20.github.io/RF24Network/ Bibliothek für eine ZigBee ähnliche Kommunikation (Mesh Nettwerk)]&lt;br /&gt;
&lt;br /&gt;
=== SE8R01 ===&lt;br /&gt;
&lt;br /&gt;
Wird bei AliExpress und E-Bay auch als simular nRF24L01+ angeboten. Er kostet die Hälfte vom nRF24L01+ und hat eine sehr kleine Bauform. Er ist NICHT kompatibel zum nRF24L01+ weder auf dem Funkkanal noch im Umgang mit den Konfigurationsregistern.&lt;br /&gt;
Die bislang brauchbarsten Informationen zu den Chip gab es unter [https://forum.arduino.cc/index.php?topic=366294.0 Finally Working code for se8r01(similar nrf24l01) 2mps, 1mps and 500kps]&lt;br /&gt;
Es scheint eine Datasheet V2.1 zu geben, ich habe es aber noch nirgends downloaden können.&lt;br /&gt;
&lt;br /&gt;
[[Benutzer:Prediger|Prediger]] ([[Benutzer Diskussion:Prediger|Diskussion]]) &lt;br /&gt;
&lt;br /&gt;
=== nRF52 ===&lt;br /&gt;
Die [https://www.nordicsemi.com/eng/Products/Bluetooth-Smart-Bluetooth-low-energy/nRF52832#Overview nRF52 SoCs] können für Bluetooth und für Übertragungen auf Basis des proprietären 2.4 GHZ-Nordic-Protokolls (kompatibel zu nRF24) genutzt werden. Für Bluetooth 4.1-Übertragungen steht ein Software Stack ([https://www.nordicsemi.com/eng/nordic/download_resource/33863/4/14045603 Nordic Softdevice 130, specification 0.5]) zur Verfügung.&lt;br /&gt;
Das proprietäre 2.4 GHZ-Nordic-Protokoll unterstützt das Übertragen von Packages mit Addressierung, CRC sowie dem ACK und RESEND von Packages.&lt;br /&gt;
Die nRF52-SoCs besitzen einen Cortex M4F, 64kB RAM, 512kB integrierten Flash-Speicher, einen NFC-Tag sowie vielfältige weitere Peripherie. Der Cortex ist zur Nutzung für Anwenderprogramme vorgesehen. SDKs, auch für gcc, werden angeboten. Die Bausteine wurden auf die Verwendung mit 2-Lagen-PCBs hin optimiert und werden auch im QFN48 package verfügbar sein (Stand 09/2015).&lt;br /&gt;
Für das Empfangen beträgt der Strombedarf ca. 7mA ([http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.pdf.ps/nRF52832_OPS_v0.6.pdf Product Specification 0.6, &amp;quot;radio current consumption&amp;quot;, page 224]). Dies ist relativ viel im Vergleich zu  kabelgebundenen Übertragungen, entspricht jedoch lediglich 1/8 typischer WLAN-ICs (ESP8266).&lt;br /&gt;
&lt;br /&gt;
=== ESP8266 ===&lt;br /&gt;
&lt;br /&gt;
Das WLAN-Modul kann sowohl Client als auch AP sein. Damals hat der Release einen starken Hype ausgelöst und dementsprechend ist die Community rasant gewachsen, wodurch sich sogar ein eigenes Forum entwickelt hat ([http://www.esp8266.com www.esp8266.com]). &lt;br /&gt;
&lt;br /&gt;
Das Modul kann man über AT-Kommandos steuern, es gibt ein LUA-Skript sowie MicroPython Interpreter und vieles mehr. Zudem kann den integrieten SoC auch direkt programmieren, sodass man für einfache Aufgaben (Temperatur auslesen etc.) keinen extra Mikrocontroller braucht. Für die Programmierung wird vom Hersteller eine fertig eingerichtete VM zur Verfügung gestellt, es gibt aber auch eine Port vom GCC und selbst in die Arduino Umgebung ist der ESP8266 mittlerweile eingepflegt.&lt;br /&gt;
&lt;br /&gt;
* [https://docs.micropython.org/en/latest/esp8266/esp8266/tutorial/intro.html ESP8266 MicroPython]&lt;br /&gt;
* [http://www.nodemcu.com/index_en.html NodeMCU LUA Interpreter Website]&lt;br /&gt;
** [https://github.com/nodemcu/nodemcu-firmware NodeMCU Firmware]&lt;br /&gt;
* [https://github.com/esp8266/Arduino Arduino ESP8266]&lt;br /&gt;
&lt;br /&gt;
=== EMW3162 ===&lt;br /&gt;
&lt;br /&gt;
[http://hackaday.com/2015/03/24/emw3162-wifi-120mhz-needs-attention/ HackADay Eintrag]&lt;br /&gt;
&lt;br /&gt;
[http://www.joinmx.com/uploadfiles/soft/EMW/DS0006E_EMW3162_V2.2.pdf Datasheet]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/354149 Thread zum Artikel (Fragen, Anregungen etc. hier posten)]&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RF12.pdf Datenblatt des ICs RF12] (PDF)&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM12.pdf Datenblatt des Moduls RFM12] (PDF)&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM69CW-V1.1.pdf Datenblatt RFM69CW]&lt;br /&gt;
* [http://www.hoperf.com/upload/rf/RFM69W-V1.3.pdf Datenblatt RFM69W]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/NRF24L01_Tutorial kleines NRF24L01+ Tutorial in C]&lt;br /&gt;
&lt;br /&gt;
== Fußnoten ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Bauteile]]&lt;br /&gt;
[[Kategorie:RFM12| ]]&lt;br /&gt;
[[Kategorie:Funk]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;br /&gt;
[[Category:Hausbus]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=89138</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=89138"/>
		<updated>2015-06-27T07:15:50Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Unterschied zwischen Messung und Protokollerkennung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es um die Input Capture Funktionalität, welche sehr präzise Zeitmessungen ermöglicht, da Programmlaufzeiten und Verzögerungen für die Genauigkeit keine Rolle spielen. Bei einem Flankenwechsel eines externen Signals an einem ICPn Pin speichert der Timer seinen aktuellen Wert im ICRn Register.&lt;br /&gt;
&lt;br /&gt;
Das hier beschriebene Projekt zeichnet die High- und Low-Zeiten eines externen Binärsignals auf. Nach der Aufzeichnung wird es analysiert und ausgegeben. Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme.&lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed Capture&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Das realisierte Programm speichert bis zu 3600 Flankenwechsel mit einer Auflösung von 62,5 ns. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65535 µs liegen.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, TSOP1738, TSOP1740) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und das Signal zu demodulieren&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png|850px]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von Signalen wird eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern der Abstand zwischen zwei Flanken größer als ca. 2,8 µs ist. Bei der IR-Messung werden die Modulationsfrequenz und das Puls/Pause Verhältniss bestimmt und zusätzlich die Zeitwerte ausgegeben. Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png|850px]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [http://www.mikrocontroller.net/topic/212195 diesen Artikel]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) ist unter [[#Siehe auch|Siehe auch]] zu finden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Grafik, welche den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte werden in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 \&lt;br /&gt;
+9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 \&lt;br /&gt;
+9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 \&lt;br /&gt;
+9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 \&lt;br /&gt;
+9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration [ns]&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Makros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über AVR Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bits schieben, setzen, rücksetzen&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
* Modulo-Arithmetik (für Berechnung der Zeitdifferenzen)&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Vermeidung von Fließkommaarithmetik (für Umrechung von Clocks in Nanosekunden)&lt;br /&gt;
** [[Festkommaarithmetik]]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=89137</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=89137"/>
		<updated>2015-06-27T06:28:08Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Warten auf Flankenwechsel mit Polling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es um die Input Capture Funktionalität, welche sehr präzise Zeitmessungen ermöglicht, da Programmlaufzeiten und Verzögerungen für die Genauigkeit keine Rolle spielen. Bei einem Flankenwechsel eines externen Signals an einem ICPn Pin speichert der Timer seinen aktuellen Wert im ICRn Register.&lt;br /&gt;
&lt;br /&gt;
Das hier beschriebene Projekt zeichnet die High- und Low-Zeiten eines externen Binärsignals auf. Nach der Aufzeichnung wird es analysiert und ausgegeben. Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme.&lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed Capture&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Das realisierte Programm speichert bis zu 3600 Flankenwechsel mit einer Auflösung von 62,5 ns. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65535 µs liegen.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und das Signal zu demodulieren&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png|850px]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von Signalen wird eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern der Abstand zwischen zwei Flanken größer als ca. 2,8 µs ist. Bei der IR-Messung werden die Modulationsfrequenz und das Puls/Pause Verhältniss bestimmt und zusätzlich die Zeitwerte ausgegeben. Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png|850px]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [http://www.mikrocontroller.net/topic/212195 diesen Artikel]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) ist unter [[#Siehe auch|Siehe auch]] zu finden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Grafik, welche den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte werden in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 \&lt;br /&gt;
+9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 \&lt;br /&gt;
+9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 \&lt;br /&gt;
+9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 \&lt;br /&gt;
+9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration [ns]&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Makros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über AVR Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bits schieben, setzen, rücksetzen&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
* Modulo-Arithmetik (für Berechnung der Zeitdifferenzen)&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Vermeidung von Fließkommaarithmetik (für Umrechung von Clocks in Nanosekunden)&lt;br /&gt;
** [[Festkommaarithmetik]]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=89136</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=89136"/>
		<updated>2015-06-27T06:26:36Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Vergleich Logic Analyzer Messung mit ATmega Messung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es um die Input Capture Funktionalität, welche sehr präzise Zeitmessungen ermöglicht, da Programmlaufzeiten und Verzögerungen für die Genauigkeit keine Rolle spielen. Bei einem Flankenwechsel eines externen Signals an einem ICPn Pin speichert der Timer seinen aktuellen Wert im ICRn Register.&lt;br /&gt;
&lt;br /&gt;
Das hier beschriebene Projekt zeichnet die High- und Low-Zeiten eines externen Binärsignals auf. Nach der Aufzeichnung wird es analysiert und ausgegeben. Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme.&lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed Capture&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Das realisierte Programm speichert bis zu 3600 Flankenwechsel mit einer Auflösung von 62,5 ns. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65535 µs liegen.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und das Signal zu demodulieren&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png|850px]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von Signalen wird eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern der Abstand zwischen zwei Flanken größer als ca. 2,8 µs ist. Bei der IR-Messung werden die Modulationsfrequenz und das Puls/Pause Verhältniss bestimmt und zusätzlich die Zeitwerte ausgegeben. Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png|850px]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [http://www.mikrocontroller.net/topic/212195 diesen Artikel]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) ist unter [[#Siehe auch|Siehe auch]] zu finden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Grafik, welche den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte werden in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 \&lt;br /&gt;
+9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 \&lt;br /&gt;
+9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 \&lt;br /&gt;
+9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 \&lt;br /&gt;
+9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration [ns]&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Makros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über AVR Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bits schieben, setzen, rücksetzen&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
* Modulo-Arithmetik (für Berechnung der Zeitdifferenzen)&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Vermeidung von Fließkommaarithmetik (für Umrechung von Clocks in Nanosekunden)&lt;br /&gt;
** [[Festkommaarithmetik]]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=74659</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=74659"/>
		<updated>2013-03-14T22:51:20Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Downloads */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es um die Input Capture Funktionalität, welche sehr präzise Zeitmessungen ermöglicht, da Programmlaufzeiten und Verzögerungen für die Genauigkeit keine Rolle spielen. Bei einem Flankenwechsel eines externen Signals an einem ICPn Pin speichert der Timer seinen aktuellen Wert im ICRn Register.&lt;br /&gt;
&lt;br /&gt;
Das hier beschriebene Projekt zeichnet die High- und Low-Zeiten eines externen Binärsignals auf. Nach der Aufzeichnung wird es analysiert und ausgegeben. Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme.&lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed Capture&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Das realisierte Programm speichert bis zu 3600 Flankenwechsel mit einer Auflösung von 62,5 ns. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65535 µs liegen.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und das Signal zu demodulieren&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von Signalen wird eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern der Abstand zwischen zwei Flanken größer als ca. 2,8 µs ist. Bei der IR-Messung werden die Modulationsfrequenz und das Puls/Pause Verhältniss bestimmt und zusätzlich die Zeitwerte ausgegeben. Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [http://www.mikrocontroller.net/topic/212195 diesen Artikel]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) ist unter [[#Siehe auch|Siehe auch]] zu finden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Grafik, welche den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte werden in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration [ns]&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Makros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über AVR Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bits schieben, setzen, rücksetzen&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
* Modulo-Arithmetik (für Berechnung der Zeitdifferenzen)&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Vermeidung von Fließkommaarithmetik (für Umrechung von Clocks in Nanosekunden)&lt;br /&gt;
** [[Festkommaarithmetik]]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=74563</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=74563"/>
		<updated>2013-03-09T15:15:12Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Ausgabe der Daten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es um die Input Capture Funktionalität, welche sehr präzise Zeitmessungen ermöglicht, da Programmlaufzeiten und Verzögerungen für die Genauigkeit keine Rolle spielen. Bei einem Flankenwechsel eines externen Signals an einem ICPn Pin speichert der Timer seinen aktuellen Wert im ICRn Register.&lt;br /&gt;
&lt;br /&gt;
Das hier beschriebene Projekt zeichnet die High- und Low-Zeiten eines externen Binärsignals auf. Nach der Aufzeichnung wird es analysiert und ausgegeben. Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme.&lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed Capture&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Das realisierte Programm speichert bis zu 3600 Flankenwechsel mit einer Auflösung von 62,5 ns. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65535 µs liegen.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und das Signal zu demodulieren&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von Signalen wird eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern der Abstand zwischen zwei Flanken größer als ca. 2,8 µs ist. Bei der IR-Messung werden die Modulationsfrequenz und das Puls/Pause Verhältniss bestimmt und zusätzlich die Zeitwerte ausgegeben. Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [http://www.mikrocontroller.net/topic/212195 diesen Artikel]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) ist unter [[#Siehe auch|Siehe auch]] zu finden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Grafik, welche den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte werden in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration [ns]&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über AVR Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bits schieben, setzen, rücksetzen&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
* Modulo-Arithmetik (für Berechnung der Zeitdifferenzen)&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Vermeidung von Fließkommaarithmetik (für Umrechung von Clocks in Nanosekunden)&lt;br /&gt;
** [[Festkommaarithmetik]]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=74461</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=74461"/>
		<updated>2013-03-08T20:27:30Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Unterschied zwischen Messung und Protokollerkennung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es um die Input Capture Funktionalität, welche sehr präzise Zeitmessungen ermöglicht, da Programmlaufzeiten und Verzögerungen für die Genauigkeit keine Rolle spielen. Bei einem Flankenwechsel eines externen Signals an einem ICPn Pin speichert der Timer seinen aktuellen Wert im ICRn Register.&lt;br /&gt;
&lt;br /&gt;
Das hier beschriebene Projekt zeichnet die High- und Low-Zeiten eines externen Binärsignals auf. Nach der Aufzeichnung wird es analysiert und ausgegeben. Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme.&lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed Capture&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Das realisierte Programm speichert bis zu 3600 Flankenwechsel mit einer Auflösung von 62,5 ns. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65535 µs liegen.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und das Signal zu demodulieren&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von Signalen wird eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern der Abstand zwischen zwei Flanken größer als ca. 2,8 µs ist. Bei der IR-Messung werden die Modulationsfrequenz und das Puls/Pause Verhältniss bestimmt und zusätzlich die Zeitwerte ausgegeben. Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [http://www.mikrocontroller.net/topic/212195 diesen Artikel]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) ist unter [[#Siehe auch|Siehe auch]] zu finden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Grafik, welche den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration [ns]&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über AVR Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bits schieben, setzen, rücksetzen&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
* Modulo-Arithmetik (für Berechnung der Zeitdifferenzen)&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Vermeidung von Fließkommaarithmetik (für Umrechung von Clocks in Nanosekunden)&lt;br /&gt;
** [[Festkommaarithmetik]]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=74460</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=74460"/>
		<updated>2013-03-08T20:24:20Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es um die Input Capture Funktionalität, welche sehr präzise Zeitmessungen ermöglicht, da Programmlaufzeiten und Verzögerungen für die Genauigkeit keine Rolle spielen. Bei einem Flankenwechsel eines externen Signals an einem ICPn Pin speichert der Timer seinen aktuellen Wert im ICRn Register.&lt;br /&gt;
&lt;br /&gt;
Das hier beschriebene Projekt zeichnet die High- und Low-Zeiten eines externen Binärsignals auf. Nach der Aufzeichnung wird es analysiert und ausgegeben. Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme.&lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed Capture&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Das realisierte Programm speichert bis zu 3600 Flankenwechsel mit einer Auflösung von 62,5 ns. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65535 µs liegen.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von Signalen wird eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern der Abstand zwischen zwei Flanken größer als ca. 2,8 µs ist. Bei der IR-Messung werden die Modulationsfrequenz und das Puls/Pause Verhältniss bestimmt und zusätzlich die Zeitwerte ausgegeben. Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [http://www.mikrocontroller.net/topic/212195 diesen Artikel]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) ist unter [[#Siehe auch|Siehe auch]] zu finden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Grafik, welche den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration [ns]&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über AVR Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bits schieben, setzen, rücksetzen&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
* Modulo-Arithmetik (für Berechnung der Zeitdifferenzen)&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Vermeidung von Fließkommaarithmetik (für Umrechung von Clocks in Nanosekunden)&lt;br /&gt;
** [[Festkommaarithmetik]]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=74458</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=74458"/>
		<updated>2013-03-08T20:18:47Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es um die Input Capture Funktionalität, welche sehr präzise Zeitmessungen ermöglicht, da Programmlaufzeiten und Verzögerungen für die Genauigkeit keine Rolle spielen. Bei einem Flankenwechsel eines externen Signals an einem ICPn Pin speichert der Timer seinen aktuellen Wert im ICRn Register.&lt;br /&gt;
&lt;br /&gt;
Das hier beschriebene Projekt zeichnet die High- und Low-Zeiten eines externen Binärsignals auf. Nach der Aufzeichnung wird es analysiert und ausgegeben. Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme.&lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed Capture&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Mit dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von Signalen wird eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern der Abstand zwischen zwei Flanken größer als ca. 2,8 µs ist. Bei der IR-Messung werden die Modulationsfrequenz und das Puls/Pause Verhältniss bestimmt und zusätzlich die Zeitwerte ausgegeben. Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [http://www.mikrocontroller.net/topic/212195 diesen Artikel]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) ist unter [[#Siehe auch|Siehe auch]] zu finden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Grafik, welche den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration [ns]&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über AVR Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bits schieben, setzen, rücksetzen&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
* Modulo-Arithmetik (für Berechnung der Zeitdifferenzen)&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Vermeidung von Fließkommaarithmetik (für Umrechung von Clocks in Nanosekunden)&lt;br /&gt;
** [[Festkommaarithmetik]]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72367</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72367"/>
		<updated>2013-02-14T21:12:20Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) ist unter [[#Siehe auch|Siehe auch]] zu finden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Grafik, welche den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über AVR Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bits schieben, setzen, rücksetzen&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
* Modulo-Arithmetik (für Berechnung der Zeitdifferenzen)&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Vermeidung von Fließkommaarithmetik (für Umrechung von Clocks in Nanosekunden)&lt;br /&gt;
** [[Festkommaarithmetik]]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72366</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72366"/>
		<updated>2013-02-14T21:12:00Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) ist unter [[#Siehe auch|Siehe auch]] zu finden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Grafik, welche den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über AVR Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bits Schieben, Setzen Rücksetzen&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
* Modulo-Arithmetik (für Berechnung der Zeitdifferenzen)&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Vermeidung von Fließkommaarithmetik (für Umrechung von Clocks in Nanosekunden)&lt;br /&gt;
** [[Festkommaarithmetik]]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72365</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72365"/>
		<updated>2013-02-14T21:10:32Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) ist unter [[#Siehe auch|Siehe auch]] zu finden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Grafik, welche den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über AVR Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bitschiebereien&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
* Modulo-Arithmetik (für Berechnung der Zeitdifferenzen)&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Vermeidung von Fließkommaarithmetik (für Umrechung von Clocks in Nanosekunden)&lt;br /&gt;
** [[Festkommaarithmetik]]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72364</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72364"/>
		<updated>2013-02-14T21:06:53Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) ist unter [[#Siehe auch|Siehe auch]] zu finden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Grafik, welche den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über AVR Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bitschiebereien&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
* Modulo-Arithmetik&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Festkommaarithmetik&lt;br /&gt;
** [[Festkommaarithmetik]]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72363</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72363"/>
		<updated>2013-02-14T21:02:37Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) ist unter [[#Siehe auch|Siehe auch]] zu finden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Grafik, welche den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bitschiebereien und Modulo Arithmetik&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
** [[Festkommaarithmetik]]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72361</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72361"/>
		<updated>2013-02-14T20:42:57Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Keine Angst vor Überläufen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) ist unter [[#Siehe auch|Siehe auch]] zu finden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Grafik, welche den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bitschiebereien und Modulo Arithmetik&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72359</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72359"/>
		<updated>2013-02-14T20:40:47Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Keine Angst vor Überläufen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 den Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bitschiebereien und Modulo Arithmetik&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72358</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72358"/>
		<updated>2013-02-14T20:39:56Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Keine Angst vor Überläufen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: T(7) - T(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: T(7) - T(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Zwischen den Zeitpunkten T(7) und T(6) hat Timer1 der Wert 43600 (Timer-Wert bei T(6)) noch zweimal erreicht, d.h. die Differenz ist 2 mal übergelaufen und der Wert um 2*2^16 größer:&lt;br /&gt;
: T(7) - T(6) = (0x266C – 0xAA50) + 2*2^16 = 0x7C1C + 2 * 0x10000 = 027C1Cx = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-2^x Arithmetik (Restklassenring) finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bitschiebereien und Modulo Arithmetik&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72355</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72355"/>
		<updated>2013-02-14T20:30:49Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Keine Angst vor Überläufen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T(5) || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T(6) || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T(7) || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bitschiebereien und Modulo Arithmetik&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72354</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72354"/>
		<updated>2013-02-14T20:22:55Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bitschiebereien und Modulo Arithmetik&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Restklassenring Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72353</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72353"/>
		<updated>2013-02-14T20:21:12Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bitschiebereien und Modulo Arithmetik&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Zweierkomplement#Interpretation_im_Restklassenring Zweierkomplement: Interpretation im Restklassenring]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72341</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72341"/>
		<updated>2013-02-13T23:16:32Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bitschiebereien und Modulo Arithmetik&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient, hat aber mit der Umsetzung nichts zu tun)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72340</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72340"/>
		<updated>2013-02-13T23:13:07Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bitschiebereien und Modulo Arithmetik&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
** [http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide IR Scope and IR Widget User&#039;s Guide] (hat als Anregung für diesen Artikel gedient)&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72339</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72339"/>
		<updated>2013-02-13T23:08:58Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bitschiebereien und Modulo Arithmetik&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP|IRMP (Infrarot-Multiprotokoll-Decoder)]]&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72338</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72338"/>
		<updated>2013-02-13T23:07:43Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Informationen über Timer&lt;br /&gt;
** [[AVR-Tutorial: Timer]]&lt;br /&gt;
** [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* Bitschiebereien und Modulo Arithmetik&lt;br /&gt;
** [[Bitmanipulation]]&lt;br /&gt;
** [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
** [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
** [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
* Infrarot Projekte&lt;br /&gt;
** [[IRMP]]&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72337</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72337"/>
		<updated>2013-02-13T23:02:50Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Downloads */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:Enthält den SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:Enhält den SourceCode für den ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4 &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AVR-Tutorial: Timer]]&lt;br /&gt;
* [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* [[IRMP]]&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
* [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72336</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72336"/>
		<updated>2013-02-13T22:59:08Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Speicherung der ermittelten Werte */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken direkt darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4: &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AVR-Tutorial: Timer]]&lt;br /&gt;
* [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* [[IRMP]]&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
* [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72335</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72335"/>
		<updated>2013-02-13T22:56:55Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Speicherung der ermittelten Werte */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050 und mit dem ATmega2560 kommt man auf 3600 Werte. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4: &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AVR-Tutorial: Timer]]&lt;br /&gt;
* [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* [[IRMP]]&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
* [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72334</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72334"/>
		<updated>2013-02-13T22:55:10Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Keine Angst vor Überläufen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert des Timers setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht. Nach jeder Erfassung eines Timer-Wertes wird OCF1A auf den erfassten Timer-Wert gesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4: &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AVR-Tutorial: Timer]]&lt;br /&gt;
* [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* [[IRMP]]&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
* [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72333</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72333"/>
		<updated>2013-02-13T22:53:01Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Keine Angst vor Überläufen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches den Verlauf von Timer1, die Erfassungs-Zeitpunkte (Capture) und das Hochzählen von ovlCnt darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4: &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AVR-Tutorial: Timer]]&lt;br /&gt;
* [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* [[IRMP]]&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
* [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72332</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72332"/>
		<updated>2013-02-13T22:50:24Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Keine Angst vor Überläufen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise (damit man die 16-Bit Wortgrenze erkennen kann):&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches die Zeitpunkte und den Verlauf von Timer1 darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4: &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AVR-Tutorial: Timer]]&lt;br /&gt;
* [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* [[IRMP]]&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
* [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72331</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72331"/>
		<updated>2013-02-13T22:49:12Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Keine Angst vor Überläufen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, wenn der Subtrahend größer ist als der Minuend und daher ein negatives Ergebnis erwartet wird. Daher hier eine genauere Ausführung in hexadezimaler Schreibweise:&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t)&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0xFFFF7C1C MOD 2^16 = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo-Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches die Zeitpunkte und den Verlauf von Timer1 darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4: &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AVR-Tutorial: Timer]]&lt;br /&gt;
* [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* [[IRMP]]&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
* [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72330</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72330"/>
		<updated>2013-02-13T22:43:10Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Warten auf Flankenwechsel mit Polling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde. Eine stabile Lösung ist dies aber nicht, daher ist für diese Anwendung der ATmega32U4 nicht zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, daher eine genauere Ausführung in hexadezimaler Schreibweise:&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t) = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches die Zeitpunkte und den Verlauf von Timer1 darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4: &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AVR-Tutorial: Timer]]&lt;br /&gt;
* [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* [[IRMP]]&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
* [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72329</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72329"/>
		<updated>2013-02-13T22:40:41Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Warten auf Flankenwechsel mit Polling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dieser Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, daher eine genauere Ausführung in hexadezimaler Schreibweise:&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t) = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches die Zeitpunkte und den Verlauf von Timer1 darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4: &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AVR-Tutorial: Timer]]&lt;br /&gt;
* [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* [[IRMP]]&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
* [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72328</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72328"/>
		<updated>2013-02-13T22:39:27Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Warten auf Flankenwechsel mit ISR */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]). Hinzu kommen noch 4 Zyklen, bis zur Ausführung der ersten Instruction der ISR, außerdem können andere IRQs (z.B. der Arduino Timer0 IRQ oder USB IRQs vom ATmega32U4) ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Für die Speicherung von Zuständen müssen globale Variablen verwendet werden, welche dann nicht in Registern gehalten werden können und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen nochmal deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, daher eine genauere Ausführung in hexadezimaler Schreibweise:&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t) = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches die Zeitpunkte und den Verlauf von Timer1 darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4: &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AVR-Tutorial: Timer]]&lt;br /&gt;
* [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* [[IRMP]]&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
* [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72327</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72327"/>
		<updated>2013-02-13T22:35:16Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Vergleich Logic Analyzer Messung mit ATmega Messung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Die Zeitskala ist in Sekunden, die Grafik zeigt einen Zeitraum von 40 µs. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]), außerdem können andere IRQs ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Dazu kommt noch, dass für die Speicherung von Zuständen globale Variablen verwendet werden müssen und diese dann nicht in Registern gehalten werden und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, daher eine genauere Ausführung in hexadezimaler Schreibweise:&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t) = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches die Zeitpunkte und den Verlauf von Timer1 darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4: &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AVR-Tutorial: Timer]]&lt;br /&gt;
* [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* [[IRMP]]&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
* [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72326</id>
		<title>High-Speed capture mit ATmega Timer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=High-Speed_capture_mit_ATmega_Timer&amp;diff=72326"/>
		<updated>2013-02-13T22:30:43Z</updated>

		<summary type="html">&lt;p&gt;Nospam2000: /* Hardwareaufbau */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Michael Dreher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--== Zeitlich hochauflösende Signalaufzeichnung ==--&amp;gt;&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]&lt;br /&gt;
Die Timer der ATmega Mikrocontrollerreihe bieten Unterstützung für vielfältige Aufgaben wie PWM, zyklische IRQ Aufrufe oder als Zeitbasis für Messungen. In diesem Artikel geht es darum, diese Timer für hochauflösende Zeitmessungen bei Signalaufzeichnungen zu verwenden wie sie bei der Analyse von Binärsignalen benötigt werden. Es kommt der sogenannte Input Capture Mode (ICP) des Timers zum Einsatz, bei dem ein Timer seinen Wert bei einem Flankenwechsel eines externen Signals zwischenspeichert.&lt;br /&gt;
&lt;br /&gt;
Der Focus des Artikels liegt auf der Lösung der bei der Umsetzung auftretenden Probleme und weniger auf der theoretischen Betrachtung der Timer. &lt;br /&gt;
&lt;br /&gt;
Der Begriff &amp;quot;Highspeed&amp;quot; ist natürlich relativ zur Taktfrequenz des ATmega zu sehen. Bei dem hier realisiertem Projekt werden bis zu 3600 Flankenwechsel eines digitalen Eingangssignal mit einer Auflösung von 62,5 ns gespeichert. Die Zeitspanne zwischen zwei Flanken muss zwischen 3 µs und 65 ms liegen. Der obere Grenzwert kann angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen Messung und Protokollerkennung ==&lt;br /&gt;
Wenn das Protokoll und damit der grobe Verlauf eines externen Signals bereits bekannt ist, kann man sich dessen Eigenschaften zu nutze machen und die Hard- und Software bestmöglich darauf abstimmen. Als Beispiel sei ein IR-Empfänger für Signale von IR-Fernbedienungen genannt.&lt;br /&gt;
&lt;br /&gt;
Fernbedienungen modulieren ihr Ausgangssignal typischerweise mit Frequenzen zwischen 36 und 40 kHz. Es gibt Bauelemente (TSOP1736, 38, 40) um diese Modulation beim Empfang für eine bessere Signalempfindlichkeit auszunutzen (Fremdlichtunterdrückung) und die Modulation rückgängig zu machen um nur das Nutzsignal am Ausgang zu erhalten.&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller bekommt vom TSOP17xx ein bereits aufbereitetes Signal, welches Pulsbreiten von mindestens 200 µs enthält (ein kompletter Burst aus z.B. 10 einzelnen Perioden), was gegenüber einer Einzel-Pulsbreite von 6 µs bei einem modulierten Signal sehr viel einfacher zu verarbeiten ist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Vergleich der Signale von SDP8600 (Channel 0) mit TSOP1738 (Channel 1, Ausgang negiert):&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_comarison_TSOP1738.png]]&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse und Vermessung von unbekannten Signalen wird daher eine sehr viel schnellere und exaktere Erfassung benötigt als bei der Verarbeitung von bekannten Signalen.&lt;br /&gt;
&lt;br /&gt;
Als Untersuchungsobjekt wurde ein IR-Signal gewählt, in der Praxis kann aber jedes beliebige Signal vermessen werden, sofern es sich an die Eingangs genannten zeitlichen Randbedingungen hält. Der Focus der IR-Messung liegt auf der Bestimmung der Modulationsfrequenz und des Puls/Pause Verhältnisses.&lt;br /&gt;
&lt;br /&gt;
Es geht explizit nicht darum ein bestimmtes IR Protokoll (wie z.B. RC5) zu implementieren. Die Erfassung soll möglichst exakt und möglichst schnell sein um auch hochfrequente Signale verarbeiten zu können.&lt;br /&gt;
&lt;br /&gt;
Der Aufbau kann auch dazu dienen eine bestehende Implementierung eines IR-Senders zu überprüfen, quasi als einfacher Ein-Kanal „Logik Analyzer“.&lt;br /&gt;
&lt;br /&gt;
== Hardwareaufbau ==&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Hardware.jpg|Aufbau mit Arduino Leonardo|300px|thumb]]&lt;br /&gt;
Der Hardwareaufbau des IR-Analysators ist denkbar einfach:&lt;br /&gt;
* Ein ATmega-Board. Getestete Arduino Boards siehe Tabelle unten. &lt;br /&gt;
* Ein [http://pdf1.alldatasheet.com/datasheet-pdf/view/90289/ETC/SDP8600.html Honeywell SDP8600 Infrarot Optoschmitt Detektor] (&#039;&#039;&#039;wichtig: im Gegensatz zu TSOP17xx ohne eingebauten Demodulator&#039;&#039;&#039;)&lt;br /&gt;
* Ein Stützkondensator von 0,1 µF parallel zur Spannungsversorgung des SPD8600&lt;br /&gt;
&lt;br /&gt;
Der SDP8600 wird an GND, +5V und Pin ICP1 des ATmega angeschlossen, da der 16-Bit Timer1 verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Beim [http://arduino.cc/en/Main/ArduinoBoardMega2560 Arduino Mega 2560 Board] sind die ICP Pins ICP1 und ICP3 nicht nach außen geführt, daher muss auf ICP4 oder ICP5 ausgewichen werden.&lt;br /&gt;
&lt;br /&gt;
Zum Debuggen habe ich einen Ausgangs-Pin des ATmega an einen Logic Analyzer angeschlossen. Er wird vom Programm gesetzt, wenn ein Flankenwechsel erkannt wird und zurückgesetzt, wenn das Abspeichern des Wertes beendet ist. Diesen Pin bezeichne ich im weiteren als &#039;Dbg Pin&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Verwendete Pins und Arduino Digital Pin Nummern&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! MCU || Arduino Board || ICP Pin || Dbg Pin || Anzahl speicherbarer Werte || Arduino Pin Mapping Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| ATmega328P, ATmega168 || [http://arduino.cc/en/Main/ArduinoBoardDuemilanove Duemilanove], [http://arduino.cc/en/Main/ArduinoBoardUno Uno] || ICP1 / PB0&amp;lt;br&amp;gt;Arduino Pin 8 || PD6&amp;lt;br&amp;gt;Arduino Pin 6 || 1080 || [http://arduino.cc/en/Hacking/PinMapping168 PinMapping168]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32U4 || [http://arduino.cc/en/Main/ArduinoBoardLeonardo Leonardo] || ICP1 / PD4&amp;lt;br&amp;gt;Arduino Pin 4 || PD6&amp;lt;br&amp;gt;Arduino Pin 12 || 1500 || [http://arduino.cc/en/Hacking/PinMapping32u4 PinMapping32u4]&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560 || [http://arduino.cc/en/Main/ArduinoBoardMega2560 Mega 2560] || ICP4 / PL0&amp;lt;br&amp;gt;Arduino Pin 49 || PL6&amp;lt;br&amp;gt;Arduino Pin 43 || 3600 || [http://arduino.cc/en/Hacking/PinMapping2560 PinMapping2560]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Auflösung und Wertebereich der Messungen ===&lt;br /&gt;
Für die Zeitmessung wird der 16-Bit Timer1 verwendet. Dieser hat eine Auflösung von bis zu 1/F_CPU. Beim Arduino entspricht dies 1/16 MHz = 62,5 ns. Für den maximalen Timer-Wert 65535 ergibt dies eine Zeitspanne von 4,096 ms. Längere Zeiträume können bei dieser Auflösung nicht direkt mit dem Hardware-Timer gemessen werden. Für einige Anwendungsfälle ist dieser Zeitraum zu kurz.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten den Zeitraum zu erweitern:&lt;br /&gt;
* Timer Prescaler (Vorteiler) für das Taktsignal des Timers verwenden. Über die Prescaler Bits CS12 bis CS10 in Register TCCR1B kann man das System-Taksignal der CPU um folgende Faktoren runterteilen: 1, 8, 64, 256, 1024&lt;br /&gt;
* Software-Überlaufbehandlung des Timers&lt;br /&gt;
&lt;br /&gt;
Der Nachteil beim Einsatz des Prescalers ist, dass sich die Auflösung dadurch verringert. Um Zeiträume von bis zu 65 ms erfassen zu können, müsste ein Prescaler-Wert von 64 verwendet werden. Dadurch würde die Auflösung von 62,5 ns auf 4 µs sinken. Damit könnte der genannte Zweck, Zeiten in der Größenordnung von 8 µs mit einer Genauigkeit von +-2% genau zu vermessen um die Modulationsfrequenz zu bestimmen, nicht erreicht werden. Aus diesem Grund verwendet dieses Projekt die Überlaufbehandlung.&lt;br /&gt;
&lt;br /&gt;
Die Überlaufbehandlung inkrementiert einen Software-Zähler, wenn der Wertebereich des Timers überschritten wird. Damit können beide Ziele erreicht werden: eine hochauflösende Messung mit einem großen Messbereich.&lt;br /&gt;
&lt;br /&gt;
=== Vergleich Logic Analyzer Messung mit ATmega Messung ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Grafik zeigt die Erfassung zweier Pulse, mit einer ON-Zeit von 4,5 µs und einer OFF-Zeit von 21,6 µs. Die Messung wurde parallel mit dem ATmega und einem Logic-Analyzer durchgeführt und die Kurven übereinandergelegt. Die orangene Linie &#039;LA&#039; zeigt die Logic-Analyzer Messung, die schwarze Linie &#039;AVR&#039; die ATmega Messung. Wie man sieht, weichen die beiden Kurven kaum voneinander ab:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_3Pulse.png]]&lt;br /&gt;
&lt;br /&gt;
Die blaue &#039;Dbg&#039; Kurve zeigt die Rückmeldung vom ATmega Programm an den Logic Analyzer. Hier kann man die Reaktionszeit (0,79 µs) und die Programmlaufzeit für die Bearbeitung der Flanken (2,38 µs) ablesen. Durch den aktivierten noise canceler (Bit ICNC1 in TCCR1B) ist diese Kurve gegenüber den anderen beiden um 4 CPU Zyklen (0,25 µs) nach rechts verschoben.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit ISR ===&lt;br /&gt;
Als erster Ansatz wurde eine ISR (Interrupt Service Routine) verwendet, welche bei jedem Flankenwechsel getriggert wurde. Dies war zu langsam um mit dem Signal schritthalten zu können und wurde daher verworfen.&lt;br /&gt;
&lt;br /&gt;
Der System Overhead beim Aufruf einer ISR in C ist recht hoch, da Register gesichert und wieder restauriert werden müssen (5 Register, insgesamt ca. 13 Zyklen, siehe [[http://www.mikrocontroller.net/topic/212195 diesen Artikel]]), außerdem können andere IRQs ins Gehege kommen und die Erfassung ausbremsen.&lt;br /&gt;
&lt;br /&gt;
Dazu kommt noch, dass für die Speicherung von Zuständen globale Variablen verwendet werden müssen und diese dann nicht in Registern gehalten werden und jedes mal neu geladen werden. Man kann zwar auch globale Variablen in Register legen, diese stehen dann aber im restlichen Programm nicht mehr zur freien Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die Variablen auf die von der ISR und vom normalen Programm zugegriffen wird, muss der type qualifier “volatile“ verwendet werden, was dem Compiler einiger Optimierungsmöglichkeiten beraubt und den Code unter Umständen deutlich langsamer macht.&lt;br /&gt;
&lt;br /&gt;
=== Warten auf Flankenwechsel mit Polling ===&lt;br /&gt;
Die schnellere Variante der Flankenabfrage ist polling des Timer Flag Registers. Der grobe Ablauf des Programms ist sehr einfach. Hier der Pseude-Code für die komplette Erfassungs-Schleife:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
IRQs deaktivieren&lt;br /&gt;
While(Ende nicht erreicht)&lt;br /&gt;
{&lt;br /&gt;
	Warten auf einen Flankenwechsel (Bit ICF1 in TIFR1)&lt;br /&gt;
	Auslesen des Input Capture Wertes (ICR1)&lt;br /&gt;
	Speichern der Zeitdifferenz zum letzten Ereignis in einem Array&lt;br /&gt;
	Trigger für Timer invertieren (TCCR1B ^= _BV(ICES1))&lt;br /&gt;
}&lt;br /&gt;
IRQs erlauben&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schleife für das Warten auf den Flankenwechsel besteht aus nur 3 Assembler Instructions, was deutlich kürzer und schneller ist als die ISR Variante.&lt;br /&gt;
&lt;br /&gt;
Der C-Code&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t tifr;&lt;br /&gt;
while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird vom avr-gcc übersetzt zu&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
loop:&lt;br /&gt;
in      r18, TIFR1&lt;br /&gt;
andi    r18, _BV(ICF1) | _BV(OCF1A)&lt;br /&gt;
breq    loop&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Schleife passiert folgendes:&lt;br /&gt;
# Es wird abgefragt, ob ein Ereignis aufgetreten ist, für welches ein Timer-Wert durch den Input Capture gespeichert wurde &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(ICF1))&amp;lt;/code&amp;gt;&lt;br /&gt;
# Es wird abgefragt, ob der Output Compare erkannt hat, dass ein Differenz-Überlauf passiert ist &amp;lt;code&amp;gt;(TIFR1 &amp;amp; (_BV(OCF1A))&amp;lt;/code&amp;gt;, Details siehe Abschnitt [[#Keine Angst vor Überläufen|Keine Angst vor Überläufen]]&lt;br /&gt;
# Der Wert der UND Verknüpfung wird gespeichert um ihn weiter unten weiterzuverwenden und TIFR nicht erneut auslesen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Der im Arduino Leonardo verbaute ATmega32U4 verwendet keinen externen USB-seriell Wandler sondern implementiert direkt das USB Protokoll. Reagiert ein USB Device einige Zeit nicht auf die Anfragen des USB Hosts, wird es abgekoppelt. Dies passiert z.B. wenn man die IRQ Bearbeitung für einige Zeit abschaltet wie es in diesem Projekt gemacht wird. Ein Workaround ist, die IRQs nur so kurz wie möglich zu sperren, d.h. erst nachdem die erste Flanke erkannt wurde.&lt;br /&gt;
&lt;br /&gt;
=== Zeitmessung ===&lt;br /&gt;
Um die Zeitdifferenz zwischen zwei Ereignissen zu messen gibt es mehrere Möglichkeiten:&lt;br /&gt;
# Nach jedem Ereignis den Timer auf 0 zurücksetzen&lt;br /&gt;
# Den Timer weiterlaufen lassen und immer die Differenz zum letzten Timer-Wert zu bilden.&lt;br /&gt;
Die erste Variante hört sich erst einmal verlockend einfach an. Zwischen dem Erkennen einer Flanke und dem Zurücksetzen des Timers vergehen aber einige µs. Der nächste Timer Messwert wäre genau um diesen Versatz zu klein. Sofern dieser Versatz bekannt ist, kann man ihn herausrechnen, bei einem Test lag er bei mir bei 30 CPU Zyklen. Mit jeder Programm- oder Compileränderungen muss dieser Versatz aber neu bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Variante ist eleganter, da sie den Timer durchlaufen lässt und die Differenz zwischen zwei Timer-Werten bildet. Ein Versatz wird dadurch komplett vermieden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Angst vor Überläufen ===&lt;br /&gt;
Es gibt unterschiedliche Überläufe zu betrachten:&lt;br /&gt;
# Überlauf des Timer Wertes TCNT1. Dies ist kein Problem und wird durch die vorzeichenlose Modulo 2^16 Berechnung der Differenz kompensiert, Rechenbeispiel siehe unten&lt;br /&gt;
# Überlauf der Differenz zwischen zwei Zeitpunkten, d.h. wenn die Differenz zwischen zwei Flanken größer als 65535 ist (Zeitspanne größer als 4,096 ms). Diese Überlaufe müssen mitgezählt werden (in ovlCnt) und als obere Bits der Zeitdifferenz gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele für erfasste Timer-Werte&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Zeitpunkt || Signal || CLKs || Timer1 || T(n) – T(n-1) || ovlCnt || Timer1 (in HEX)&lt;br /&gt;
|-&lt;br /&gt;
| T5 || 0-&amp;gt;1 || 10000 || 10000 ||  || || 0x2710&lt;br /&gt;
|-&lt;br /&gt;
| T6 || 1-&amp;gt;0 || 43600 || 43600 || 33600 || 0 || 0xAA50&lt;br /&gt;
|-&lt;br /&gt;
| T7 || 0-&amp;gt;1 || 206444 || 9836 || 162844 || 2 || 0x266C&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Differenz zwischen T(7) und T(6) muss über die Differenz von Timer1 und ovlCnt bestimmt werden:&lt;br /&gt;
: T(7) - T(6) = (9836 - 43600) MOD 2^16 + 2^16 * 2 (ovlCnt) = 31772 + 2^16 * 2 = 162844&lt;br /&gt;
&lt;br /&gt;
Die Differenz-Berechnung erfolgt Modulo 2^16, da der Datentyp uint16_t verwendet wird. Diese Art der Berechnung ist etwas gewöhnungsbedürftig, daher eine genauere Ausführung in hexadezimaler Schreibweise:&lt;br /&gt;
: Timer1(7) - Timer1(6) = 0x266C – 0xAA50 = 0xFFFF7C1C (als uint32_t) = 0x7C1C (als uint16_t) = 31772&lt;br /&gt;
&lt;br /&gt;
Um den Differenzwerte größer als 65535 zu verarbeiten, muss man die Überläufe der Timer-Differenz mitzählen, was in der Variable ovlCnt passiert. Bei der Differenz zwischen T(7) und T(6) von 162844, wurde der Wert 43600 bei T(6) zwischendrin noch zweimal erreicht, d.h. die Differenz ist um 2*2^16 größer:&lt;br /&gt;
: t_diff(7,6) = (0x266C – 0xAA50) + 2*0x10000 = 0x27C1C = 162844&lt;br /&gt;
&lt;br /&gt;
Mehr zur Theorie der Modulo Arithmetik finden bei den Links unter [[#Siehe auch|Siehe auch]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild, welches die Zeitpunkte und den Verlauf von Timer1 darstellt:&lt;br /&gt;
[[Bild:HighSpeedCaptureAtmegaTimer_Overflows.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;Capture&#039; bezeichnet die Erfassungszeitpunkte T(5) bis T(7), bei welchen Timer1 aufgrund der Flanke von &#039;Signal&#039; den aktuellen Wert speichert. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht „von Hand“ vergleichen muß, ob der Wert 43600 zwischen zwei Flanken nochmal vorkam (die Zeitpunkte mit &#039;ovlCnt++&#039; in der Grafik markiert sind), kann man den Output Compare Wert setzen und dies die Hardware des ATmega machen lassen. Wenn der Timer diesen Wert erreicht, wird das Output Compare Flag OCF1A im Timer Flag Register TIFR1 gesetzt und daraufhin die Variable ovlCnt erhöht.&lt;br /&gt;
&lt;br /&gt;
=== Speicherung der ermittelten Werte ===&lt;br /&gt;
Die Zeit-Differenzwerte werden in einem Array gespeichert. Das für die Wertspeicherung verfügbare RAM hängt davon ab, wie viel Speicher für andere Zwecke benötigt wird (Stack, globale Variablen). Bei Arduino 1.0.3 mit ATmega328P (2048 Byte RAM) bleibt Platz für ca. 870 16-Bit Werte, mit dem ATmega32U4 kommt man auf ca. 1050. Die Puffergröße im Programm ist relativ zur Arbeitsspeichergröße RAMEND festgelegt, d.h. bei größerem Speicher erweitert sich automatisch der Puffer. Der vom Programm belegte Speicher wird durch &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; festgelegt:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
const uint16_t projectRamUsage = 380 + 64; // das absolute Minimum ist 240 + 64&lt;br /&gt;
const uint16_t bufSize ((RAMEND - 0x100 - projectRamUsage) / sizeof(uint16_t));&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Programm erweitert wird, oder in einer anderen Umgebung eingesetzt wird, muss die Konstante &amp;lt;code&amp;gt;projectRamUsage&amp;lt;/code&amp;gt; angepasst werden, da es sonst abstürzt.&lt;br /&gt;
&lt;br /&gt;
Bei einer Auflösung von 62,5 ns können in einem uint16_t Wert maximal Zeiten von 4 ms gespeichert werden. Um den Messbereich zu erweitern greift man zu einem Trick. Bei kurzen Zeitspannen ist die absolute zeitliche Auflösung sehr wichtig, damit die prozentuale Ungenauigkeit nicht zu groß wird. Um bei langen Zeitspannen dieselbe prozentuale Genauigkeit zu erhalten, benötigt man eine geringere absolute zeitliche Auflösung. Dies ist vergleichbar mit der Messbereichsumschaltung bei einem Digitalmultimeter.&lt;br /&gt;
&lt;br /&gt;
Dies könnte man dadurch erreichen, dass man Fließkommazahlen verwendet, welche durch die getrennte Verarbeitung des Exponenten eine automatische Messbereichsumschaltung eingebaut haben. Diese haben aber den Nachteil, dass die Verarbeitung ohne Hardwareunterstützung auf einem Mikrocontroller sehr langsam ist und sie viel Platz benötigten (4 Byte für einfache oder sogar 8 Byte für doppelte Genauigkeit pro Wert).&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist eine spezielle Codierung, des 16-Bit Wertes. Das höchste Bit wird als Umschalter zwischen zwei Messbereichen verwendet:&lt;br /&gt;
* Messbereich 1: Wenn Bit 2^15=0 ist, dann enthält der Wert die Differenz-Zeit ohne weitere Skalierung. Damit sind alle Werte zwischen 0 und 32767 ohne Lücken darstellbar (zeitliche Auflösung 62,5 ns, maximal 2,048 ms).&lt;br /&gt;
* Messbereich 2: Wenn Bit 2^15 den Wert 1 hat, enthält die Zahl die Differenz-Zeit mit einer zeitlichen Auflösung von 2 µs. Der Wertebereich und die zeitliche Auflösung sind also jeweils um den Faktor 32 verschoben. Dadurch sind Zeiten bis zu 65,535 ms darstellbar.&lt;br /&gt;
&lt;br /&gt;
Der Verschiebungsfaktor zwischen den beiden Messbereichen kann über die Konstante RANGE_EXTENSION_BITS (Standardwert: 4 Bits) geändert werden. Es gilt:&lt;br /&gt;
: Verschiebungsfaktor = 2^(RANGE_EXTENSION_BITS + 1)&lt;br /&gt;
&lt;br /&gt;
Um die Zahlen schneller verarbeiten zu können und weniger Schiebebefehle bei der Ablage zu benötigen werden im Messbereich 2 die Bits 2^16 bis 2^19 (der Überlauf-Zähler) in den &#039;&#039;&#039;unteren&#039;&#039;&#039; 4 Bits abgelegt und erst beim Auslesen in die korrekte Position geschoben, siehe Funktion &amp;lt;code&amp;gt;packTimeVal()&amp;lt;/code&amp;gt;. Beim Auslesen der Daten ist dann eine Dekodierung notwendig. Dies erfolgt außerhalb der zeitkritischen Erfassungs-Schleife in der Funktion &amp;lt;code&amp;gt;unpackTimeVal()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Ende der Erfassung ===&lt;br /&gt;
Für den Abbruch der Erfassung gibt es zwei Kriterien:&lt;br /&gt;
# Der Puffer für die Speicherung der Werte ist voll&lt;br /&gt;
# Die maximal darstellbare Zeit wurde überschritten (entspricht einem Timeout, d.h. kein Ereignis für eine bestimmte Zeit). Dies passiert, wenn 65 ms lang keine Flanke erkannt wird.&lt;br /&gt;
&lt;br /&gt;
Am Anfang der Erfassung wird endlos auf eine low-&amp;gt;high Flanke gewartet, da man sonst nach dem Start der Erfassung innerhalb von 65 ms das zu analysierende Signal anlegen müsste.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
=== Performance Hinweise ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
* Umstellung der Schleife von Array-Zugriff auf Pointer&lt;br /&gt;
* TIFR1 nur einmalig auslesen und den bereits verknüpften Wert und nicht den Rohwert speichern&lt;br /&gt;
* Timer nicht zurücksetzen&lt;br /&gt;
* Bei der Nutzung des erweiterten Wertebereichs die Schiebeoperationen aufs Auslesen zurückstellen&lt;br /&gt;
* Keine Sonderbehandlung für den ersten Array-Wert, der eigentlich nicht benötigt wird, aber trotzdem abgespeichert wird.&lt;br /&gt;
* Keine volatile Variablen verwenden&lt;br /&gt;
* Makefile für Arduino Umgebung für Erstellung eines .lst Files&lt;br /&gt;
* Variable (auch globale) in Register legen register uint8_t myVar __asm__(&amp;quot;r13&amp;quot;)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe der Daten ===&lt;br /&gt;
Die erfassten Werte in drei Formaten über die serielle Schnittstelle ausgegeben.&lt;br /&gt;
&lt;br /&gt;
1. In einem kompakten Format, bei welchem die einzelnen High- und Low- Zeiten durch + und - markiert sind, die Werte sind in ns:&amp;lt;c&amp;gt;&lt;br /&gt;
capture[1056 values]=&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799562&lt;br /&gt;
+9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -1855375&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16625 +9625 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
+9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -16562 +9687 -16625 +9625 -16625 +9625 -16625 +9687 -799687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2. [[Bild:HighSpeedCaptureAtmegaTimer_Office.png|Verarbeitung einer Messung in LibreOffice|300px|thumb]]Im CSV-Format welches direkt in OpenOffice oder Excel importiert werden kann um ein Diagramm zu erstellen. Für jede Flanke werden zwei Zeilen ausgegeben um eine senkrechte Linie im Diagramm darzustellen: &amp;lt;c&amp;gt;&lt;br /&gt;
&amp;quot;Time [ns]&amp;quot;;&amp;quot;Signal&amp;quot;;&amp;quot;Duration&amp;quot;&lt;br /&gt;
0;0;0&lt;br /&gt;
1;1;9625&lt;br /&gt;
9625;1;0&lt;br /&gt;
9626;0;16625&lt;br /&gt;
26250;0;0&lt;br /&gt;
26251;1;9625&lt;br /&gt;
35875;1;0&lt;br /&gt;
35876;0;16625&lt;br /&gt;
52500;0;0&lt;br /&gt;
52501;1;9687&lt;br /&gt;
...&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3. Als Zusammenfassung werden die Periodendauer und die Modulationsfrequenz ausgegeben (hierbei werden nur Pulse/Perioden berücksichtigt, die maximal 10% über der kürzesten Periodendauer liegen) :&amp;lt;c&amp;gt;&lt;br /&gt;
Number of sample periods used for average values: 720&lt;br /&gt;
Carrier frequency [Hz]: 38186&lt;br /&gt;
Medium period [ns]: 26187&lt;br /&gt;
Medium pulse width [ns]: 10875&lt;br /&gt;
Duty cycle [%]: 41&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Source Code ===&lt;br /&gt;
Jetzt geht es endlich ans Eingemachte! Der hier abgedruckte Source Code ist ein vereinfachter Ausschnitt mit den Funktionen auf die im Text eingegangen wurde. Es fehlen die Anpassungen für ATmega32U4 und ATmega2560. Das gesamte Projekt mit dem Code für die andere MCUs ist im Abschnitt [[#Downloads|Downloads]] zu finden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
// Copyright (c) 2012 Michael Dreher  &amp;lt;michael(at)5dot1.de&amp;gt;&lt;br /&gt;
// this code may be distributed under the terms of the General Public License V2 (GPL V2)&lt;br /&gt;
&lt;br /&gt;
uint16_t captureData[bufSize]; // the buffer where the catured data is stored&lt;br /&gt;
int16_t captureCount; // number of values stored in captureData&lt;br /&gt;
&lt;br /&gt;
// Wait for a signal on pin ICP1 and store the captured time values in the array &#039;captureData&#039;&lt;br /&gt;
void startCapture(void)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char sreg = SREG;&lt;br /&gt;
  cli(); // disable IRQs&lt;br /&gt;
  register uint8_t ices1_val = _BV(ICES1); &lt;br /&gt;
  register uint8_t tccr1b = TCCR1B | ices1_val; // trigger on rising edge&lt;br /&gt;
  TCCR1B = tccr1b;&lt;br /&gt;
  OCR1A = TCNT1 - 1;&lt;br /&gt;
  TIFR1 = _BV(ICF1) | _BV(OCF1A) | _BV(TOV1); // clear all timer flags&lt;br /&gt;
  register uint16_t ovlCnt = 0;&lt;br /&gt;
  register uint16_t prevVal = 0;&lt;br /&gt;
  register uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A)&lt;br /&gt;
  register uint16_t *pCapDat; // pointer to current item in captureData[]&lt;br /&gt;
&lt;br /&gt;
  for(pCapDat = captureData; pCapDat &amp;lt;= &amp;amp;captureData[bufSize - 1]; )&lt;br /&gt;
  {&lt;br /&gt;
    // wait for edge or overflow (output compare match)&lt;br /&gt;
    while(! (tifr = (TIFR1 &amp;amp; (_BV(ICF1) | _BV(OCF1A))))) {&lt;br /&gt;
    }&lt;br /&gt;
    uint16_t val = ICR1;&lt;br /&gt;
    OCR1A = val; // timeout based on previous trigger time&lt;br /&gt;
 &lt;br /&gt;
    if(tifr &amp;amp; _BV(OCF1A)) // check for overflow bit&lt;br /&gt;
    {&lt;br /&gt;
      if(pCapDat != captureData) // ignore overflow at the beginning of the capture&lt;br /&gt;
      {&lt;br /&gt;
        ovlCnt++;&lt;br /&gt;
        if(ovlCnt &amp;gt;= _BV(RANGE_EXTENSION_BITS))&lt;br /&gt;
        {&lt;br /&gt;
          TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
          break; // maximum value reached, treat this as timeout and abort capture&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
      continue;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    tccr1b ^= ices1_val; // toggle the trigger edge&lt;br /&gt;
    TCCR1B = tccr1b;&lt;br /&gt;
    TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit&lt;br /&gt;
&lt;br /&gt;
    uint16_t diffVal = val - prevVal;&lt;br /&gt;
    if(ovlCnt || (diffVal &amp;amp; 0x8000))&lt;br /&gt;
    {&lt;br /&gt;
      diffVal = packTimeVal(diffVal, ovlCnt);&lt;br /&gt;
      ovlCnt = 0;&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    *pCapDat = diffVal;&lt;br /&gt;
    pCapDat++;&lt;br /&gt;
    prevVal = val;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  // the first array entry contains only the starting time and no time&lt;br /&gt;
  // difference, therefore ist is no longer needed and will be removed&lt;br /&gt;
  captureCount = (pCapDat - captureData) - 1; // correct count&lt;br /&gt;
  for(int16_t i = 0; i &amp;lt; captureCount; i++)&lt;br /&gt;
  {&lt;br /&gt;
    captureData[i] = captureData[i + 1];&lt;br /&gt;
  }&lt;br /&gt;
  SREG = sreg; // enable IRQs&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline uint16_t packTimeVal(uint16_t diffVal, uint16_t ovlCnt)&lt;br /&gt;
{&lt;br /&gt;
  // overflow part is stored in the lower RANGE_EXTENSION_BITS bits and not in&lt;br /&gt;
  // the upper bits because that makes the code smaller here (less shifting)&lt;br /&gt;
  return (0x8000 | ((diffVal &amp;gt;&amp;gt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1))) | ovlCnt);&lt;br /&gt;
}  &lt;br /&gt;
  &lt;br /&gt;
inline uint32_t unpackTimeVal(uint32_t val)&lt;br /&gt;
{&lt;br /&gt;
  if(val &amp;amp; 0x8000)&lt;br /&gt;
  {&lt;br /&gt;
    val = val &amp;amp; 0x7fff;&lt;br /&gt;
    uint32_t valOvl =  (val &amp;amp; (_BV(RANGE_EXTENSION_BITS) - 1)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
    uint32_t valTim =  (val &amp;lt;&amp;lt; 1) &amp;amp; (0x7fff &amp;amp; ~(_BV(RANGE_EXTENSION_BITS) - 1));&lt;br /&gt;
    val = valOvl | valTim;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  return val;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Das Download-Archiv enthält ein Arduino Projekt. Die Kern-Funktionalität (die Erfassung) kommt ohne die Arduino Libraries aus, die Arduino Funktionen werden nur die Ausgabe der Werte verwendet.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimer_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega168, ATmega328 und ATmega32U4. Diese Variante ist zu empfehlen, wenn man den Code lesen und verstehen will, da keine Maros für die Registernamen verwendet werden&lt;br /&gt;
* [[Datei:HighSpeedCaptureAtmegaTimerATmega2560_Source.zip]]&lt;br /&gt;
:SourceCode für ATmega2560. Alle Timer-Registernamen wurden durch Makros ersetzt, so dass man den verwendeten Timer durch die Änderung des zentralen defines CAP_TIM geändert werden kann, worunter leider die Lesbarkeit gelitten hat. Funktioniert natürlich weiterhin mit ATmega168, ATmega328 und ATmega32U4: &lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip Sourcecode] &lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf Schaltplan]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AVR-Tutorial: Timer]]&lt;br /&gt;
* [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]]&lt;br /&gt;
* [[IRMP]]&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://de.wikipedia.org/wiki/Kongruenz_%28Zahlentheorie%29 Kongruenz Zahlentheorie]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Modular_arithmetic Modular arithmetic]&lt;br /&gt;
* [http://www.trans4mind.com/personal_development/mathematics/numberTheory/modularArithmetic.htm Number Theory Modular Arithmetic (Congruences)]&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
* [[Bebilderung|Bebilderung von Artikeln]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/123456789 Forum: Diskussion zu diesem Projekt] &lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
[[Spezial:Kategorien|Kategorien]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lizenz ==&lt;br /&gt;
Dieser Artikel unterliegt der [http://creativecommons.org/licenses/by-sa/2.0/de/ Creative Commons Attribution-Share Alike Lizenz (CC BY-SA 2.0 DE)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:AVR-Tutorial]]&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>Nospam2000</name></author>
	</entry>
</feed>