RC Summensignal per Microcontroller einlesen
Einige Modellbauempfänger bieten die Möglichkeit, auf einer Signalleitung alle Servosignale als Summensignal auszugeben. Dieses Summensignal soll per Microcontroller eingelesen werden, um selbst Servos, Motoren, Lichter und Spezialfunktionen zu steuern.
Die Übertragung des Lehrer-Schüler-Signals zwischen 2 Sendern findet oft ebenfalls in Form eines Summensignals statt.
Analyse des Summensignals per Oszi
[1] Signalverhalten
|
![]() |
[2] Periodendauer ca. 22.5ms
|
![]() |
[3A] CH1=Min
|
![]() |
[3B] CH1=Min
|
![]() |
[4A] CH1=Max
|
![]() |
[4B] CH1=Max
|
![]() |
[5] Pause: ca. 9ms
|
![]() |
[6] Einstellung am Sender
|
Im Video [1] sieht man, dass bei der Bewegung eines Knüppels/Schalters jeweils ein "Balken" im Signal seine Pulsdauer ändert.
Ein kompletter Durchgang hat eine Periodendauer von ca. 22.5ms - das ist an diesem Sender einstellbar [6] und passt mit der Messung überein.
Bilder [3A] und [4A]: Wenn Kanal1 auf Minimum steht, ist die Dauer des HIGH-Pulses bei 0.68ms. Bei Knüppelstellung "Maximum" sind es 1.7msec.
Die Messung inklusive nachfolgendem LOW-Pegel in [3B], [4B] stimmt mit 1ms bzw. 2ms dann wiederum exakt mit den PPM Minimal- bzw. Maximal-Pulsdauern der Modellbauservos überein.
Die Low-Pegel sind somit 300µs lang, ebenfalls in Übereinstimmung mit der Einstellung am Sender [6].
Die Messung der Pause [5] muss mit Vorsicht betrachtet werden: da die Periodendauer des Gesamtsignals konstant ist, schwankt die Pause mit den Werten der einzelnen Kanäle.
Wären alle 8 Kanäle auf Maximum, sind 8*2ms = 16ms belegt. Bei einer Periodendauer von 22.5ms sind von der Pause dann nur noch 6.5ms übrig.
Die Amplitude beträgt 3.3V. Das sollte ein 5V Atmega/Arduino-Microcontroller noch als HIGH-Pegel interpretieren können.
Umsetzung in Software
Softwaredesign
Um das Singal mit möglichst wenig Rechenpower verarbeiten zu können, soll der Input Capture Mode des Atmega verwendet werden.
Damit kann die Dauer einzelner HIGH/LOW-Pegel gemessen werden, es wird jeweils ein Interrupt getriggert. Diese gemessene Signaldauer (des HIGH-Pegels) kann dann mittels State Machine ausgewertet und den einzelnen Kanälen zugeordnet werden.
![]() |
State Diagram Version 1
|
Zuerst wird im Zusand "wait for initial pulse" auf einen langen Impuls ("Pause") gewartet. Danach folgen die einzelnen Kanäle: falls die Dauer gültig ist (1..2ms) wird zum nächsten Kanal gesprungen. Andernfalls geht es zurück zum Anfang, da das Signal ungültig war.
Dieser Ansatz kann noch vereinfacht werden: da alle States zum Einlesen eines Kanals bis auf die Kanalsnummer identisch sind, kann auch eine Zählervariable für die Kanalnummer verwendet werden. Dann lassen sich die Zustände zu einem zusammenfassen:
![]() |
State Diagram Version 2
|
Das liest sich einfacher, und lässt sich auch deutlich eleganter ohne copy-paste-code programmieren.
Atmega: Input Capture
Beim Interrupt Capture Mode schreibt die Hardware beim Ändern des Pin-Levels den aktuellen Timerwert in ein Register, und löst einen Interrupt aus.
Dadurch kann mit wenig Rechenleistung eine sehr präzise Zeitmessung vorgenommen werden.
Theoretisch ist mit einem Atmega bei 16MHz und einem Timer-Vorteiler von 1 eine Auflösung von t = 1/f = 1/16MHz = 62.5nsec möglich.
Für den ersten Test mit Vorteiler 256 ergibt sich ein Timer-Takt von 62.5kHz und somit 16µs pro Tick.
Beim Atmega328 kann nicht jeder Pin für Input Capture verwendet werden. Es gibt sogar nur genau einen Pin: PB0.
Im Register TIMSK muss das Bit ICIE1 gesetzt werden, und beim Timer1 über TCCR1B der Vorteiler gewählt werden. Damit ist Input Capture bereits aktiv und der Interrupt wird getriggert.
Besonderheit am Arduino: Der Arduino-eigene Init-Code setzt TCCR1A (wird für Analogmessung benötigt). Damit der Timer1 volle 16bit zählt, muss TCCR1A zurück auf 0 geschrieben werden.
Die Testsoftware für Input Capture per Interrupt:
Die Intervalle zwischen steigenden Flanken an PB0 wird eingelesen, und die letzten 20 Messwerte als Tabelle über die Serielle Konsole (Arduino USB) ausgegeben.
Download Sourcecode: Input Capture Test
Speicherbedarf laut Compiler:
FLASH: | 3528 Byte von 30720 Byte | 11% |
---|---|---|
RAM: | 332 Byte von 2048 Byte | 16% |
![]() |
Screenshot Konsolenausgabe. 10 Ticks -> 160µs
|
![]() |
Funktionsgenerator: 160µs Signal
|
Summensignal Testprogramm:
Auf einem 16bit-Microcontroller ist das längste ohne Überlauf messbare Intervall (2^16 - 1) = 65535 Ticks.
Die Taktfrequenz des Counters muss so gewählt werden, dass die lange 9ms-Pause sicher ohne Überlauf messbar bleibt.
Je höher die Taktfrequenz des Counters, desto höher die Auflösung des erfassten Signals. Das bedeutet, mit höherem Takt wird die Auflösung der Servos "feiner".
Nehmen wir die Pause mit 10ms an. Das sind 10.000µs.
Mit einem Clock-Vorteiler von 256 ergeben sich wie oben erwähnt 16µs pro Tick. Um 10ms zu messen verstreichen 625 Ticks -> die Frequenz kann noch erhöht werden.
Vorteiler | Zeit pro Tick | Ticks für 10ms | Maximaler Messbereich mit 65535 Ticks |
---|---|---|---|
1024 | 64µs | 156,25 | 4,2 Sekunden |
256 | 16µs | 625 | 1 Sekunde |
64 | 4µs | 2500 | 262 msec |
8 | 0,5µs | 20000 | 32.7 msec |
1 | 0,0625µs | 160000 | 4.1msec |
Mit einem Clock-Vorteiler von 8 wird der Messbereich somit optimal genutzt.
Das Servosignal von 1..2ms hat eine Breite von 1ms. Diese kann mit den 0.5µs-Schritten in Form von 2000 unterscheidbaren Werten gemessen werden. Das wären beinahe 11 Bit Messauflösung, absolut akzeptabel.
Das Summensignal wird am Pin PB0 des Arduino Micro eingelesen, und auf der Konsole (USB-Terminal) ausgegeben.
Download Sourcecode: Summensignal
FLASH: | 3772 Byte von 30720 Byte | 12% |
---|---|---|
RAM: | 279 Byte von 2048 Byte | 13% |
![]() |
Screenshot Konsolenausgabe: Einlesen des Summensignals
|
Konsolenausgabe in Aktion
|
Die Konfiguration erfolgt über die Konstanten in SumSig_Cfg.h. Es können die Timings des Signals sowie die Anzahl Kanäle angepasst werden.
Wenn der Empfänger es unterstützt, können also auch mehr als 8 Kanäle per Summensignal übertragen werden!
Da das Einlesen der Werte mittels Interrupt stattfindet, muss berücksichtigt werden, dass auch während der Verarbeitung der Werte ein Interrupt stattfinden kann.
Parallele Zugriffe können inkonsistente Daten erzeugen - in unserem Fall würde sich das durch springende Servopositionen bemerkbar machen.
Abhilfe schafft das "Double Buffer"-Konzept: im RAM werden 2 Datensätze abgelegt. Einer wird beim Lesen verwendet, der andere mit frisch eingelesenen Daten befüllt.
Sobald der Datensatz komplett ist, werden die Zeiger auf die Datensätze vertauscht: Beim nächten Durchgang wird dann der jeweils andere gelesen/beschrieben.