PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : RC Summensignal erzeugen



YaNnIk
24.02.2010, 20:35
Hi Leute,

Ich muss aus mehreren einzelnen PPM Signalen ein Summensignal erstellen.

So ein Summensignal sieht so aus :

http://www.mftech.de/pics/ppp-schema2.gif
Ein komplettes PPM-Frame hat eine Länge von 22,5 ms. Es besteht aus einer überlangen Startinformation und 8 Kanalinformationen. Auf jeden Impuls folgt eine Stoppinformation mit 0,3 ms Länge. Die Länge eines Kanalimpulses beträgt 0,7 ms bis 1,7 ms und entspricht der Knüppelposition. Somit stammt die Abbildung von einem Sender mit Kanal 1 auf Vollausschlag rechts, Kanal 2 auf Vollausschlag links, Kanal 3 auf Mitte, .. Der Startimpuls ergänzt die 8 Kanalimpulse, so dass sich die Gesamtlänge von 22,5 ms ergibt.

Die Gesammtlänge von 22,5ms ist allerdings nciht so entscheident, das Signal kann ca. 18 bis 30ms lang sein..


So, ich habe schon viel Hirnschmalz hineininterpretiert, aber ich habe keine Idee wie ich so ein Signal aus einzelnen PPM signalen erzeugen kann..


Kann mir da jemand weiterhelfen??????

Danke im Vorraus ;-)

Mfg Yannik[/i]

wkrug
24.02.2010, 21:56
Der erste und eigentlich schwierigere Schritt ist, die einzelnen Impulslängen der ankommenden Kanäle zu messen.

Das mach ich üblicherweise mit flankengetriggerten Interrupts, bei denen Ich dann das sensing umschalte -> Einmal steigende Flanke, Dann wieder fallende Flanke.

Da Du anscheinend gleich 8 von diesen Impulslängen messen willst, brauchst Du einen Controller der 8 geeignete Interuptquellen hat.
Da dürften sich Controller eignen, die Pin Change Interrupts unterstützen.

Die Signalausgabe ist am einfachsten über Comparematch Interrups an einem 16Bit Timer zu lösen.
Du lässt einen Zähler mitlaufen, der die aktuelle Kanalposition darstellt.
Bei einem Comparematch Interrupt wird zum aktuellen Zählerstand die Counts bis zum nächsten gewünschten Ereignis addiert.
Dieser Wert wird dann im Comparematch Register des Timers abgespeichert.
Das machst Du so lange, bis alle Impulse + Pause Übertragen wurden, dann stellst Du diesen Framecounter wieder auf 0 und es beginnt alles wieder von Vorne.

Beispiel !!!
Startbedingung, Kanal 1 wird übertragen:
Comparematch Interupt tritt auf.
Der Impulsausgang wird auf 1 geschaltet.
Der TCNT wird ausgelesen, es werden die Counts bis zum nächsten Comparematch Interrupt ( sagen wir mal 1,8ms ) zu diesem Wert dazu gezählt.
Dieser Wert wird ins Comparemach Register eingespeichert.
Die Comparematch Interrupt routine wird verlassen.

Der nächste Comparematch Interupt tritt nach 1,8ms auf.
Der Impulsausgang wird auf 0 gesteuert.
Das TCNT Register wird ausgelesen.
Dazu werden die Counts bis zum nächsten gewünschten Interrupt dazu addiert - In Deinem Fall wären das 0,2ms.
Dieser Wert wird im Comparematch Register des Counters abgespeichert.
Der Impulszähler wird Inkrementiert.
Die Comparematch Interrupt routine wird verlassen.

Beim nächsten Comparematch Interrupt also nach 1,8+0,2ms gehts dann so weiter bis alle Impulse + Startpause übertragen wurden.
Dann wird einfach der Framecounter auf 0 gesetzt und das Spielchen beginnt von vorne.

Das funktioniert sehr gut und läuft praktisch im Hintergrund, weil die komplette Impulsausgabe im Comparematch Interrupt stattfindet.

Der gleiche 16 Bit Timer kann dann auch gleich für die Impulslängenmessung der eingehenden Impulse verwendet werden, weil das TCNT Register ja nicht verändert, sondern nur gelesen wird.

Die Impulsausgabe Funktion läuft auch problemlos über die 0 Stellung des Counters drüber. Also ein Null Stellen des TCNT Registers ist absolut nicht nötig und sogar kontraproduktiv, auch wenn es für das Verständnis einfacher wäre am anfang jedes Frames das Register zu Nullen!

Nachtrag!
Wenn Du den Controller mit 8MHz laufen lässt, einen 16Bit Timer mit Prescaler 8 nimmst, dann entsprechen 1000 Counts genau einer ms.
Also ein Count ist dann 1µs lang.
Die Servoauflösung entspricht dabei ca. 1000 Schritten, was die meisten Servos ohnehin nicht mehr auflösen können.

YaNnIk
24.02.2010, 22:07
Da Du anscheinend gleich 8 von diesen Impulslängen messen willst, brauchst Du einen Controller der 8 geeignete Interuptquellen hat.
Da dürften sich Controller eignen, die Pin Change Interrupts unterstützen.


Also genauergesagt muss ich nur 2 PPM Signale messen, die dann nachher auf Kanal 5 und 6 erscheinen sollen..



Beispiel !!!
Startbedingung, Kanal 1 wird übertragen:
Comparematch Interupt tritt auf.
Der Impulsausgang wird auf 1 geschaltet.
Der TCNT wird ausgelesen, es werden die Counts bis zum nächsten Comparematch Interrupt ( sagen wir mal 1,8ms ) zu diesem Wert dazu gezählt.
Dieser Wert wird ins Comparemach Register eingespeichert.
Die Comparematch Interrupt routine wird verlassen.

Der nächste Comparematch Interupt tritt nach 1,8ms auf.
Der Impulsausgang wird auf 0 gesteuert.
Das TCNT Register wird ausgelesen.
Dazu werden die Counts bis zum nächsten gewünschten Interrupt dazu addiert - In Deinem Fall wären das 0,2ms.
Dieser Wert wird im Comparematch Register des Counters abgespeichert.
Der Impulszähler wird Inkrementiert.
Die Comparematch Interrupt routine wird verlassen.

Beim nächsten Comparematch Interrupt also nach 1,8+0,2ms gehts dann so weiter bis alle Impulse + Startpause übertragen wurden.
Dann wird einfach der Framecounter auf 0 gesetzt und das Spielchen beginnt von vorne.


Ähm, ja, ich mag auch Toastbrot.

So ganz check ich das noch nicht :-s




Das funktioniert sehr gut und läuft praktisch im Hintergrund, weil die komplette Impulsausgabe im Comparematch Interrupt stattfindet.


Daraus schließe ich mal, das du irgendwo schonmal sonen Code verwendet hast, könntest du den mal posten?


Also einen Code um an die RC Werte zu kommen habe ich bereits (Den habe ich vorher schonmal verwendet.. Allerdings kam es da nciht auf die Auflösung an, die hier nur ca. 70Schritte beträgt. .Aber das könnt man ja durch verwendung eines 16Bit Timers ändern.. :




$regfile = "m16def.dat"
$crystal = 4000000

Config Portd.2 = Input

Dim Lesen1 As Bit
Dim Gyro_gier As Byte

Lesen1 = 0

Config Timer0 = Timer , Prescale = 64
Enable Timer0
Stop Timer0

Config Int0 = Change
On Int0 Messen1
Enable Interrupts
Enable Int0

Do

Loop

End

Messen1:

If Lesen1 = 0 Then
Start Timer0
Lesen1 = 1
Else
Stop Timer0
Gyro_gier = Timer0
Timer0 = 0
Lesen1 = 0
End If

Return

wkrug
24.02.2010, 22:23
Den kann Ich Dir schon schicken, der ist aber in "C", das wird Dir bei BASCOM nicht viel nützen.


Ähm, ja, ich mag auch Toastbrot.
Kurz gesagt du bringst den Controller dazu, bei jedem gewünschten Pegelwechsel am Summenimpulsausgang eine Comparematch Interrupt zu erzeugen.


Also einen Code um an die RC Werte zu kommen habe ich bereits (Den habe ich vorher schonmal verwendet.. Allerdings kam es da nciht auf die Auflösung an, die hier nur ca. 70Schritte beträgt. .Aber das könnt man ja durch verwendung eines 16Bit Timers ändern.. :
... und zwar drastisch. Auch hier kommst Du auf 1000 Schritte!

YaNnIk
24.02.2010, 22:24
Hm ne, dann lass es lieber, mit C kann ich garnix anfangen =/

wkrug
24.02.2010, 22:41
Ich versuchs trotzdem mal mit Code!
Das ist nur der Code für den Comparematch Interrupt.
Die Timer Initialisierung und die Initialisierung der Variablen, sowie die Interruptfreigabe müssen natürlich auch gemacht werden.



Comparematch Interrupt Routine
if(pinb.0==0) // Hier werden die Pulslängen erzeugt
{
portb.0=1;
OCR1A=TCNT1+Pulselengh[channel]-200;
}
else
{
// Hier werden die Pausen zwischen den Impulsen erzeugt
portb.0=0;
OCR1A=TCNT1+200;
channel++;
if(channel>8){channel=0;};
};

Die indizierte Variable Pulslengh hat dabei 9 Integer ( 16Bit ) Speicherplätze.
Im Speicherplatz 0 ist die Länge der Pause drin, im rest die gewünschten Impulslängen.

Mehr ist das nicht.
Ich hab den Code jetzt mal kurz aus dem Kopf aufgeschrieben und hoffentlich nichts vergessen.

YaNnIk
24.02.2010, 22:46
Mal als grundlegende Frage.. Was sind Comparematch Interrupts ??? Hab dazu irgendwie nix geunden... Ich nehme mal an "TCNT Register" erklärt sich damit dann auch?!

wkrug
25.02.2010, 10:10
Oh je, da müssen wir ja ganz von vorne anfangen.
Guck mal ins Datenblatt eines ATMEGA Controllers wie dem ATMEGA 8.
Der hier interessante Teil beginnt auf Seite 74.
Das TCNT1 Register ist ein Zähler der im Timer1 eingebaut ist.

Diesem Register ist ein sogenannter Prescaler vorgeschaltet, der einen ankommenden Takt teilt und mit diesem geteilten Takt dieses TCNT1 Register hochzählt.
Ebenfalls in diesem Timer enthalten sind 2 Comparematchunits.
Das OCR1A Register gehört zur Comparematchunit A.
Das OCR1B gehört zur Comparematchunit B.

Für Dich ist vorerst nur mal die Comparematchunit A interessant.

Mit deinen BASCOM Befehlen musst Du den Timer Initialisieren und den Comparematch Interrupt A freigeben.
Als weiteres müssen natürlich die Interrups grundsätzlich freigegeben werden.
War das nicht in BASCOM ENABLE Interrupts ?

Erreicht das TCNT1 Register den Wert, der im OCR1A Register abgelegt ist, wird der Comparematch A Interrupt getriggert und die dazugehörige Routine ausgeführt.

Das wars mal so weit es Dich jetzt konkret betrifft.

Dieser Timer 1 ist sehr umfangreich.
Die komplette Beschreibung dieses Timers kannst Du im Datenblatt von Seite 74 bis 101 nachlesen!

YaNnIk
25.02.2010, 12:30
Das TCNT1 Register ist ein Zähler der im Timer1 eingebaut ist.


Also ist das einfach der Wert, den der Timer momentan hatt?!?



Diesem Register ist ein sogenannter Prescaler vorgeschaltet, der einen ankommenden Takt teilt und mit diesem geteilten Takt dieses TCNT1 Register hochzählt.

Das mit dem Prescaler ist mir klar..



Ebenfalls in diesem Timer enthalten sind 2 Comparematchunits.
Das OCR1A Register gehört zur Comparematchunit A.
Das OCR1B gehört zur Comparematchunit B.

Für Dich ist vorerst nur mal die Comparematchunit A interessant.

Mit deinen BASCOM Befehlen musst Du den Timer Initialisieren und den Comparematch Interrupt A freigeben.
Als weiteres müssen natürlich die Interrups grundsätzlich freigegeben werden.
War das nicht in BASCOM ENABLE Interrupts ?

Erreicht das TCNT1 Register den Wert, der im OCR1A Register abgelegt ist, wird der Comparematch A Interrupt getriggert und die dazugehörige Routine ausgeführt.


Also wird einfach eine Interrupt Routine (vom Interrupt COMPARE1A) gestartet, sobald Timer1 den Wert von OCR1A erreicht!?!

Hmm okay, soweit glaub ich ist das klar..


So, ich glaube mit den Vorraussetzungen komm ich schon etwas weiter..

Aber jetzt zurück zum eig. Summensignal.. Das hab ich noch nicht so gaz verstanden.. So im groen und ganzen weiß ich was dein Code usw. bewirken soll, aber so ganz kann ich noch nix damit anfangen..

Also mal Schritt für Schritt..

Als erstes konfiguriere ich Timer1 :


Config Timer1 = Timer , Prescale = 8
Enable Timer1

Dann konfiguriere ich den interrupt :


Enable OC1A
On OC1A Comp_ISR

So und hier liegt jetzt langsam mein Problem...

Da check ich langsam das alles nicht mehr so ganz...

Also wie gesagt so im groben ist mir klar was passieren soll :

OC1A wird ausgelöst, wenn der Summensignal Ausgang auf Low ist, wird er auf High geschaltet und dann wird OC1A = Timer1 + Pulslenge festgelegt und die ISR beendet. Sobald dann die Pulslenge erreicht ist, wird die ISR erneut gestartet, aber da der Summensignal Ausgang auf High ist, wird OC1A = Timer1 + 300 festgelegt um die 0,3ms pause festzulegen.

Und das ganze dann immer durch...

Jetzt weiß ich nur nciht so ganz genau wie ich das im Endeffekt umsetzen soll...

Und eins noch : Die Lenge des "Start-High-Pegels" muss sich ja verändern, damit die Gesammtlenge des Summensignals immer gleich bleibt, die kann man das lösen???

EDIT : Timer1 ist ja dadurch schon belegt, wie kann ich dann die Eingangsimpulse mit einer anständigen Auflösung messen??

wkrug
25.02.2010, 17:26
OC1A wird ausgelöst, wenn der Summensignal Ausgang auf Low ist.
Fast richtig. Der OC1A wird bei jeder gewünschten Änderung des Ausgangssignales aufgerufen.
Also bei L->H Wechsel ebenso wie beim H->L Wechsel.
Das Kriterium welcher Teil der Interruptroutine aufgerufen wird, wird aus dem PINB.0 gewonnen. Man könnte hier ebenso den PORTB.0 abfragen.



...wird er auf High geschaltet und dann wird OC1A = Timer1 + Pulslenge festgelegt...
minus der Länge die die kurze Pause zwischen 2 Impulsen haben soll!



Sobald dann die neue Pulslenge erreicht ist, wird die ISR erneut gestartet, aber da der Summensignal Ausgang auf High ist, wird OC1A = Timer1 + 300 festgelegt um die 0,3ms pause festzulegen.

Im nächsten OC1A Interrupt wird dann die Pause abgearbeitet.
Es wird hier nicht auf irgendein Ereignis gewartet, sonst gibts Probleme.
Auf irgendein Ereignis in einer Interruptroutine zu warten ist ein absolutes NO GO!!!

wkrug
25.02.2010, 17:44
Timer1 ist ja dadurch schon belegt, wie kann ich dann die Eingangsimpulse mit einer anständigen Auflösung messen??
Hatte ich auch schon geschrieben.
Da das Zählregister des Timers 1 ( TCNT1 ) durch diese Methode der Impulserzeugung nie neu beschrieben wird, kann man durch eine Interruptroutine ( ICP, INT0, INT1, INT2 ) den Wert dieses TCNT1 Registers auslesen.

Bei der steigendan Flanke, also am Anfang eines Impulses wird das TCNT1 Register ausgelesen und dieser Wert in einer Variable zwischengespeichert. Im gleichen Programmteil wird das Interrupt Sensing auf fallende Flanke umgestellt.

Geht dann das Singal wieder von H->L wird dieser Interrupt also erneut getriggert. In dieser Interruptroutine wird dann aber ein anderer Teil angesprochen. Das kann man über ein Flag machen, oder die Sensing Bits direkt auslesen.
In diesem anderen Teil wird erneut das TCNT1 Register gelesen und der im vorherigen Interrupt ermittelte Wert subtrahiert. Das Ergebnis ist das Äquvalent der Länge des Einganspulses. In meinem Beispiel als 1000 für 1ms.
Nun wird in diesem Programmteil das Sensing wieder auf steigende Flanke für den nächsten Servoimpuls umgestellt und fertig.

Da ein ATMEGA8 nur 4...5 geeignete Interuptquellen besitzt, kannst Du so auch leider nur 4...5 Einzelimpulse verarbeiten.
Drum hatte ich den Vorschlag mit einem Controller mit Pin Change Interrupt Möglichkeit vorgeschlagen, auch wenn das Handling der Pinchanges ein wenig schwieriger ist.

Die Comparematch Unit B des Timers 1 ist dabei noch gar nicht genutzt. Die könnte auch noch zur Erzeugung einer weiteren Impulskette verwendet werden. ;-)

Ist das nun Genial ??


Und eins noch : Die Lenge des "Start-High-Pegels" muss sich ja verändern, damit die Gesammtlenge des Summensignals immer gleich bleibt, die kann man das lösen???

Erstens dürfte hier ein fester Wert reichen.
Das wurde schon bei den allerersten Fernsteuerungen so gemacht.
Zweitens - Wenn Du immer gleiche Framelängen willst, kannst Du auch alle bisherigen Impulslängen addieren und den daraus ermittelten Wert von der gewünschten Framelänge subtrahieren.
Der Rest ist die Länge der Startsequenz.
Da das eigentlich nur eine kleine Änderung ist, kannst Du ja mal zum Spaß beide Methoden ausprobieren.