Hi, erstmal ein großes Lob an alle, die hier imm fleißig lesen und helfen. Hat mir schon paarmal geholfen:) Aber nun hab ich ein Problem mit meinem Atmega 16. Der controller soll, wenn mal alles so klappt wie's soll: - über einen reed kontak ein relais ein- bzw. ausschalten - 3 RC Kanäle einlesen bzw. erkennen - Schaltfunktionen in Abhängigkeit vom ankommenden RC Sigal ausführen - viell noch die akkuspannung erkennen bzw. überwachen - diese funktionen aber nur ausführen bzw. nicht ausführen sobald der reed kontake betätigt wurde Habe zwar schon etwas Erfahrungen sammeln können, komme jedoch seit ein paar tagen nicht weiter. Liege ich erstmal richtig, dass ich mit einem Timer nur jeweils einen RC Kanal erfassen bzw. auswerten kann? Komme ich da unter Umständen besser, wenn ich das RC Summensignal auswerte, wenn ich es schaffe das aus dem Empfänger zu bekommen? Habe zuerst einmal versucht ein RC Sigal über den INT0 Eingang versucht zu erkennen bzw. auszuwerten, was mir mit Hilfe des Forums hier auch gut gelungen ist. Sobald ich aber auch den INT1 nutzen will geht garnix mehr. Speziell sobald ich nach INT0 bzw. ISC00 das Semikolon und die Schrägstriche entferne GICR = (1<<INT0);// | (1<<INT1); // INT0/INT1 enable MCUCR = (1<<ISC01) | (1<<ISC00);// | (1<<ISC11) | (1<<ISC10); //Int0/INT1 steigende Flanke Sehe echt nicht so recht durch dabei. Hier noch der Rest des Programms, vielleicht kann mir einer von euch alten Hasen helfen. Vielleicht habt Ihr auch noch paar Tips, wie ich die oben genannten Funktionen gut realisieren kann bzw. könnte. #include <avr/io.h> #include <util/delay.h> #include <inttypes.h> #ifndef F_CPU #warning "F_CPU war noch nicht definiert, wird nun mit 10000000 definiert" #define F_CPU 10000000UL /* Quarz mit 10 Mhz */ #endif #include <avr/interrupt.h> volatile uint8_t Pulsdauer0 = 0; volatile uint8_t Pulsdauer2 = 0; void (steigende_Flanke0)(void); void (fallende_Flanke0)(void); void (steigende_Flanke2)(void); void (fallende_Flanke2)(void); SIGNAL(SIG_INTERRUPT0) { if(MCUCR == 0x03) // Testen auf steigende Flanke { steigende_Flanke0(); return; } if( MCUCR == 0x02) // Testen auf fallende Flanke { fallende_Flanke0(); return; } } void (steigende_Flanke0)(void) { TCNT0 = 0; // Timer Reset TCCR0 = (1<<CS02); // Prescaler 1/256 MCUCR = (1<<ISC01) | (0<<ISC00); // INT0 fallende Flanke } void (fallende_Flanke0)(void) { TCCR0 = 0x00; // Timer0 Stoppen Pulsdauer0 = TCNT0; // Timer Wert = Pulsdauer0 MCUCR = (1<<ISC01) | (1<<ISC00); // INT0 auf steigende Flanke } /*SIGNAL(SIG_INTERRUPT1) { if( MCUCR == 0x0c) // Testen auf steigende Flanke { steigende_Flanke2(); return; } if( MCUCR == 0x04 ) // Testen auf fallende Flanke { fallende_Flanke2(); return; } } void (steigende_Flanke2)(void) { TCNT2 = 0; // Timer Reset TCCR2 = (1<<CS22) | (1<<CS21); // Prescaler 1/256 MCUCR = 0x04; // INT1 fallende Flanke } void (fallende_Flanke2)(void) { TCCR2 = 0x00; // Timer0 Stoppen Pulsdauer2 = TCNT2; // Timer Wert = Pulsdauer2 MCUCR = 0x0c; // INT1 auf steigende Flanke } /* int main (void) { DDRA = 0xff; // PORT A komplett Output DDRC = 0x00; // PORT C komplett Input PORTC = 0xff; // PORT C Pullup's aktiv DDRD = (0<<PD2) | (0<<PD3); PORTD = (1<<PD2) | (1<<PD3); GICR = (1<<INT0);// | (1<<INT1); // INT0/INT1 enable MCUCR = (1<<ISC01) | (1<<ISC00);// | (1<<ISC11) | (1<<ISC10); //Int0/INT1 steigende Flanke TCCR0 = (1<<CS02) | (1<<CS00); //Prescaler = FCPU/1024 Timer 0 TCCR2 = (1<<CS22) | (1<<CS21) | (1<<CS20); // Prescaler = FCPU/1024 Timer 2 TIMSK = (1<<TOIE0) | (1<<TOIE2); //Enable Overflow Interrupt Enable TCNT0 = 0; // Initianlisiere Counter 0 TCNT2 = 0; // Initianlisiere Counter 2 sei(); // global Interupt enable while(1) { if (Pulsdauer0 >= 38 && Pulsdauer0 < 45) { PORTA |= (1<<PA2); } else if (Pulsdauer0 >= 45 && Pulsdauer0 < 65) { PORTA &= ~((1<<PA1) | (1<<PA2)); } else if (Pulsdauer0 >= 65 && Pulsdauer0 < 79) { PORTA |= (1<<PA1); } /* if (Pulsdauer2 >= 38 && Pulsdauer2 < 50) { PORTA |= (1<<PA4); } else if (Pulsdauer2 >= 50 && Pulsdauer2 < 58) { PORTA &= ~((1<<PA3) | (1<<PA4)); } else if (Pulsdauer2 >= 58 && Pulsdauer2 < 79) { PORTA |= (1<<PA3); } */ } }
Mapamann schrieb: > Hi, > > erstmal ein großes Lob an alle, die hier imm fleißig lesen und helfen. > Hat mir schon paarmal geholfen:) > > Aber nun hab ich ein Problem mit meinem Atmega 16. > > Der controller soll, wenn mal alles so klappt wie's soll: > > - über einen reed kontak ein relais ein- bzw. ausschalten > - 3 RC Kanäle einlesen bzw. erkennen > - Schaltfunktionen in Abhängigkeit vom ankommenden RC Sigal ausführen RC ist ein Funksignal einer Fernsteuerung? > - viell noch die akkuspannung erkennen bzw. überwachen > - diese funktionen aber nur ausführen bzw. nicht ausführen sobald der > reed kontake betätigt wurde > > > Habe zwar schon etwas Erfahrungen sammeln können, komme jedoch seit ein > paar tagen nicht weiter. > > Liege ich erstmal richtig, dass ich mit einem Timer nur jeweils einen RC > Kanal erfassen bzw. auswerten kann? > Komme ich da unter Umständen besser, wenn ich das RC Summensignal > auswerte, wenn ich es schaffe das aus dem Empfänger zu bekommen? Kann ich nicht beantworten, weil ich nicht weiss, wie die RC Signale einzeln und als Summe aussehen. Die These 1 Timer = 1 Kanal bezweifle ich. Wenn die Timerschritte klein genug sind, sollten die Timerroutine auch mehrere Eingänge abfragen können. > Habe zuerst einmal versucht ein RC Sigal über den INT0 Eingang versucht > zu erkennen bzw. auszuwerten, was mir mit Hilfe des Forums hier auch gut > gelungen ist. Die Auswertung über INT ist ein anderes Verfahren als über TIMER. > Sobald ich aber auch den INT1 nutzen will geht garnix mehr. > Speziell sobald ich nach INT0 bzw. ISC00 das Semikolon und die > Schrägstriche entferne > > GICR = (1<<INT0);// | (1<<INT1); // INT0/INT1 enable > MCUCR = (1<<ISC01) | (1<<ISC00);// | (1<<ISC11) | (1<<ISC10); > //Int0/INT1 steigende Flanke Das reicht noch nicht. Es muss mindestens eine weitere ISR Funktion für den zweiten Interrupt vorhanden sein. Im Beispiel unten ist diese auskommentiert: > /*SIGNAL(SIG_INTERRUPT1) > { > if( MCUCR == 0x0c) // Testen auf steigende Flanke > { > steigende_Flanke2(); > return; > } > > if( MCUCR == 0x04 ) // Testen auf fallende Flanke > { > fallende_Flanke2(); > return; > } > } > void (steigende_Flanke2)(void) > { > TCNT2 = 0; // Timer Reset > TCCR2 = (1<<CS22) | (1<<CS21); // Prescaler 1/256 > MCUCR = 0x04; // INT1 fallende Flanke > } > void (fallende_Flanke2)(void) > { > TCCR2 = 0x00; // Timer0 Stoppen > Pulsdauer2 = TCNT2; // Timer Wert = Pulsdauer2 > MCUCR = 0x0c; // INT1 auf steigende Flanke > } > > /* Anm.: Die Kommentarendezeichen hier sind falsch. Das kann kein lauffähiger Code sein.
Stefan B. schrieb: >> Liege ich erstmal richtig, dass ich mit einem Timer nur jeweils einen RC >> Kanal erfassen bzw. auswerten kann? >> Komme ich da unter Umständen besser, wenn ich das RC Summensignal >> auswerte, wenn ich es schaffe das aus dem Empfänger zu bekommen? > Kann ich nicht beantworten, weil ich nicht weiss, wie die RC Signale > einzeln und als Summe aussehen. Vermutlich meint er ein PWM-Signal wie dieses: http://de.wikipedia.org/wiki/Funkfernsteuerung#Vollelektronische_AM_.2F_FM-Proportionalsteuerung_27.C2.A0MHz_.E2.80.93_40.C2.A0MHz Da die Kanäle Zeitschlitze innerhalb eines PWM-Datenstroms sind, benötigt man nur einen Timer-Kanal für die Auswertung. Was das Summensignal dabei sein soll, erschließt sich mir aber nicht so ganz.
>Liege ich erstmal richtig, dass ich mit einem Timer nur jeweils einen RC >Kanal erfassen bzw. auswerten kann? Wenn "RC-Kanal" ein einzelner Servokanal ist, dann ja. >Komme ich da unter Umständen besser, wenn ich das RC Summensignal >auswerte, wenn ich es schaffe das aus dem Empfänger zu bekommen? Das Summensignal ist die PWM-Folge mit der Info für alle Kanäle. Insofern lässt sich daraus mit einem Timer alles dekodieren. Oliver
Hi, erstmal Danke für eure Antworten und sorry für meine undeutliche Ausdrucksweise. Ich meinte natürlich das Signal einer Fernsteuerung und mit Summensignal das PWM Signal, wie Jörg es vermutet hat.Ich werde versuchen mich in Zukunft besser auszudrücken. Bin auch inzwischen soweit (mit etwas Copy und Paste ;) ), dass ich das PWM Summensignal am INT 0 Eingang anliegen habe und auswerten kann.Jedoch habe ich dazu noch ein paar Fragen. Die Pulslänge des jeweiligen Kanals wird in ein Array namens cSumSig eingelesen. Habe einen 10MHZ Quarz an meinem Atmega16 mit Prescaler 256. Also taste ich ja das Summensignal mit 39 KHz ab, oder? Der Prescaler von 64 ist ja dann zu klein, weil der timer ja eine max. Auflösung von 8 Bit hat, oder? Meine vorerst letzte Frage ist folgende. Da ich wie gesagt relativ neu in die Materie eingestiegen bin, frage ich in meinem Programm in der main Funktion das Array cSumSig[i] mit einer if/else Anweisung ab. Vorerst gehen die jeweils an Port A angeschlossenen Leds dabei an bzw. bleiben aus. Das sieht dann in etwa so aus: if (cSumSig[4] >= 35 && cSumSig[4] < 57) { PORTA |= (1<<PA3); } else if (cSumSig[4] >= 57 && cSumSig[4] < 60) { PORTA &= ~((1<<PA2) | (1<<PA3)); } else if (cSumSig[4] >= 60 && cSumSig[4] < 80) { PORTA |= (1<<PA2); } if (cSumSig[5] >= 35 && cSumSig[5] < 57) { PORTA |= (1<<PA1); } else if (cSumSig[5] >= 57 && cSumSig[5] < 60) { PORTA &= ~((1<<PA0) | (1<<PA1)); } else if (cSumSig[5] >= 60 && cSumSig[5] < 80) { PORTA |= (1<<PA0); } Kann mir aber vorstellen, dass es da sicher etwas "eleganteres" gibt. Hat einer von euch nen Tip dafür?
Mapamann schrieb:
1 | if (cSumSig[4] > 34) |
2 | {
|
3 | if(cSumSig[4] < 57) |
4 | PORTA |= (1<<PA3); |
5 | |
6 | else if (cSumSig[4] < 60) |
7 | PORTA &= ~((1<<PA2) | (1<<PA3)); |
8 | |
9 | else if (cSumSig[4] < 80) |
10 | PORTA |= (1<<PA2); |
11 | }
|
12 | |
13 | |
14 | if (cSumSig[5] > 34) |
15 | {
|
16 | if (cSumSig[5] < 57) |
17 | PORTA |= (1<<PA1); |
18 | |
19 | else if (cSumSig[5] < 60) |
20 | PORTA &= ~((1<<PA0) | (1<<PA1)); |
21 | |
22 | else if (cSumSig[5] < 80) |
23 | PORTA |= (1<<PA0); |
24 | }
|
> Kann mir aber vorstellen, dass es da sicher etwas "eleganteres" gibt.
Nicht wirklich.
Das einzige was du ausnutzen kannst.
Wenn in einer x kleiner y Abfrage der else Zweig genommen wird, dann
steht bereits fest, dass x größer gleich y sein muss.
(Was soll eigentlich passieren, wenn deine Signale kleiner 35 bzw.
größer 80 sind? An den Ausgängen änderst du ja in diesen Fällen nichts
Da du Relais schalten willst (offenbar mit einem Zwischenzustand, in dem
kein Ausgang High ist): Was hältst du von
1 | if(cSumSig[4] < 57) |
2 | PORTA |= (1<<PA3); |
3 | |
4 | else if (cSumSig[4] > 60) |
5 | PORTA |= (1<<PA2); |
6 | |
7 | else
|
8 | PORTA &= ~((1<<PA2) | (1<<PA3)); |
9 | |
10 | if(cSumSig[5] < 57) |
11 | PORTA |= (1<<PA1); |
12 | |
13 | else if (cSumSig[5] > 60) |
14 | PORTA |= (1<<PA0); |
15 | |
16 | else
|
17 | PORTA &= ~((1<<PA0) | (1<<PA1)); |
Ich würde die input capture Funktion eines Timers für die Abtastung benutzen. Damit hast du einen Zeitstempel in Echtzeit, und musst dann nur noch die Auswertung hintendran machen. Innerhalb der ISR schaltest du dabei die jeweilige Flanke fürs input capture um.
Hallo, ihr seit ja heute richtig schnell mit dem Antworten :) @Kbuchegg Vorerst geht es nur darum, dass ich verstehe was mit diesem Quellcode in meinem Controller geschieht, bzw. was ich damit machen kann. Aber es ist in der Tat korrekt, weil kleiner 34 bzw. größer 80 müsste ich nicht abfragen. Später soll die fertige Schaltung in einem Modell U-Boot seine Dienste verrichten. Eine Schaltfunktion (positv) soll dabei die Beleuchtung einschalten, negativ auch diese Beleuchtung plus Suchscheiwerfer.(Mit Beleuchtung sind in dem Fall die Positionslichter für Steuerbord und Backbord gemeint) Die Zweite Schaltfunktion dann die Torpedoklappen über ein Servo öffnen bzw. schließen. Und die dritte Schaltfunktion einen Getriebemotor ansteueren, der das Seerohr und den Schnorchel aus- bzw. einfährt. Hoffe ich bekomme das alles hin ;) @dl8dtl Werde ich mir mal anschauen. Meinst du mit "Abtastung" das PWM Summensignal und findest es wäre eine "bessere" Lösung für meine Aufgaben? Möchte halte nicht paarmal anfangen, weil sich später herrausstellt, dass ich dass so nicht realisieren kann, da mir ja etwas die Erfahrung fehlt alles gleich auf einmal zu Überblicken.
Mapamann schrieb: > Die Pulslänge des jeweiligen Kanals wird in ein Array namens cSumSig > eingelesen. Habe einen 10MHZ Quarz an meinem Atmega16 mit Prescaler 256. > > Also taste ich ja das Summensignal mit 39 KHz ab, oder? > > Der Prescaler von 64 ist ja dann zu klein, weil der timer ja eine max. > Auflösung von 8 Bit hat, oder? Dazu muss man deinen Code oben anschauen. 1/ Deine Zeitmessung benutzt keine eigene Timerinterruptroutine, sondern du stellst beim Auftreten einer Flanke den Timertakt ein (10 MHz/256) und nullst das Timerzahlregister. Dann schaust du beim Auftreten der entgegengesetzten Flanke nach, welcher Wert im Timerzählregister steht. Der Timertakt ist in der Tat ca. 39 kHz (10000000/256 Hz = 39062,5 Hz) 2a/ Du arbeitest mit Timer0, d.h. einem 8-Bit Timer. Du könntest auch mit einem anderen Timer auf 16-Bit Zählrange kommen. 2b/ Oder du lässt einen Overflow bei Timer0 zu und zählst in der entsprechenden Timer0-Overflow-ISR deren Anzahl. Die Gesamtzahl der Timerticks ist dann Anzahl der Overflows * 256 + Zahl im Timerzählregister. Die 256 hier ist wenn die Overflows erzeugt werden, wenn der volle Bereich des Timerzählregisters durchlaufen wird (0 bis 255).
Mapamann schrieb: > Meinst du mit "Abtastung" das PWM Summensignal und findest es wäre eine > "bessere" Lösung für meine Aufgaben? Ja, ich würde es so machen. Das Signal ist vergleichsweise langsam, daher ist eine Softwaredekodierung des Summensignals nicht sehr schwierig. Die kürzestmögliche Zeit ist ja immerhin 500 µs, das sind selbst bei einem mit nur 1 MHz getakteten AVR noch ca. 400 Befehle, die man da abarbeiten kann. > Möchte halte nicht paarmal anfangen, ... Illusion. :-) Man fängt sowieso jedes Projekt zweimal an. ;-) Das ist aber auch nicht schlimm, der erste Versuch ist halt die Problem- analyse, der zweite führt dann zum eigentlichen Ziel. Für den ersten Teil benutzt man irgendein Entwicklungssystem, damit man in der Verdrahtung möglichst flexibel ist.
Hallo, ich mal wieder. Hatte das Programm soweit am laufen mit dem PWM Signal am Int 0. Möchte aber nun dem Verständniss halber die Sache auch mit dem Interrupt Capture probieren. Leider fehlen mir dabei wieder ein paar Zusammenhänge bzw. die Erfahrung bei der Umsetzung. Der Code im Anhang ist erstmal das, was ich mir bis jetzt gebastelt habe, fehlt noch viel, ich weis. Aber wie gesagt habe ich ein paar Verständnissprobleme bzw. Fragen. Ich versuche mal diese zu erklären Habe die Thoerie bzw. Datenblatt so verstanden: Bei dem PWM Signal gehe ich von dem hier aus: http://www.mftech.de/ppm.htm IPC enable --> init mit steigender Flanke --> Globale Interrups enable --> bei erster steigender Flanke gehts in die "ISR(TIMER1_CAPT_vect)" (ist das soweit richtig?) Dabei soll aber der erste Impuls nicht ausgewertet werden, weil dieser ja der Startimpuls ist.Somit gehts erstmal in den "else Zweig" (auch richtig?) Nächster Innterrupt ist wieder steigende Flanke Kanal eins, es geht also wieder in die ISR, diesmal der "if Zweig". (richtig?) Und nun kommen auch schon meine Fragen. Zählt TCNT1 jetzt ab dem Interrupt von Kanal 1 steigende Flanke? Wann muss ich die Umschaltung auf die fallende Flanke machen? Kann ich dann einfach so den Wert von ICR1 in csumme[ikanal] lesen? Vorstellen könnte ich mir das in etwa so: ISR(TIMER1_CAPT_vect) { if(ipuls==1) { // Nicht vor dem ersten Puls das zählen beginnen TCCR1B &= ~(1<<ICES1); // fallende Flanke csumme[ikanal] = ICR1; // Pulslänge im Array speichern TCCR1B |= (1<<ICES1); // steigende Flanke ikanal++; // Counter für nächsten Puls erhöhen } else { ipuls = 1; // Messung nach erstem Puls freigeben } TCNT1 = 0; // Counter zurücksetzen } Muss ich den Counter jedesmal auf 0 setzen? Den nächsten Startimpuls wollte ich wieder mit einem if abfangen, in etwa so (wenn es so bleibt das TCNT1 immer wieder 0 gesetzt wird): if (ICR1 >= 138) // Impuls größer 2,2ms bei T= 16µs nach prescaler // 2,2 dabei willkürlich gewählt, mit "etwas Luft" // zu den 2ms für 100% { ikanal=0; ipuls=0; } Jedoch weis ich nicht so recht, wie ich das mit in die ISR bekomme?!?! Letzte Frage vorerst, bezieht sich auf dem im Datenblatt erwähnten "Time Stamp". Wie muss ich das verstehen? Werden beim Interrupt Capture nicht "die Perioden gezählt" bis zum nächsten Interrupt? (meine das im Vergleich dazu, das selbe mit dem INT0 Eingang zu realisieren) So, werd dann mal weiter probieren, vielleicht bekomme ich zwischenzeitlich schon selbst einen Lichtblick:)
>Zählt TCNT1 jetzt ab dem Interrupt von Kanal 1 steigende Flanke?
TCNT1 zählt im Input-Capture-Mode überhaupt keine Flanken. Das
funktioniert anders:
Der Zähler zählt (am einfachsten im Normal-Modus) völlig frei und
ungestört mit dem eingestellten Zählertakt vor sich hin. Bei jeder
passenden Flanke am ICP-Pin wird automatisch und "sofort" der gerade
aktuelle Zählerstand in das ICR1-Register kopiert, und danach der
Capture-Interrupt ausgelöst (falls freigegeben). Die vergangene Zeit
zwischen zwei Flanken ist demnach die Differenz zwischen dem aktuellen
und dem vorherigen ICR1-Wert.
In der ISR musst du also das ICR1-Register auslesen und irgendwo global
speichern. Falls du eine Impulsbreite messen möchtest, muß hier auch von
steigender auf fallende Flanke umgeschaltet werden (und umgekehrt). Dazu
bitte das Datenblatt lesen. Auswerten würde ich das alles in der
main-loop, entweder nach jedem Impuls, oder komplett nach Ende eines
Telegrams.
Bei der Differenzbildung musst du beachten, daß der Zähler zwischen zwei
Flanken überlaufen kann. Wenn sichergestellt ist, daß das nur einmal
passieren kann, ist es einfach, ansonsten brauchst du auch noch den
Overflow-Interrupt, um die Anzahl der Überläufe mitzuzählen. Dabei gebt
es dann den Sonderfall, daß Overflow und Flanke gleichzeitig auftreten,
aber auch das kriegst du mit etwas Nachdenken und Datenblatt lesen in
den Griff.
Oliver
@ Oliver Danke erstmal für die Auskunft. Wenn ich richtig liege, sollte mein Zähler ja im Normal Mode mit Prescaler 256 konfiguriert sein. Hmm, bei max Länge 22,5 ms des kompletten Impulsdiagramms bzw. max Länge eines Impulses kann mein Zähler doch garnicht überlaufen, oder? Hmm, stehe trotzdem noch etwas auf dem Schlauch. Der Timer1 Wert wird bei nem interrupt in das ICR1 Register geschrieben. Trotzdem ist das doch eine bestimmte Bitfolge bei z.B. 1,5ms oder? Mir fehlt da immernoch die "Brücke" zu dem "Zeitstempel".
Mapamann schrieb: > Der Timer1 Wert wird bei nem interrupt in das ICR1 Register geschrieben. Nein, er wird beim Eintreten der input capture Bedingung in das ICR1 geschrieben. Optional kannst dich darüber dann mit einem Interrupt informieren lassen (und das solltest du auch so tun :). > Trotzdem ist das doch eine bestimmte Bitfolge bei z.B. 1,5ms oder? Das input capture Ereignis ist eine Flanke aus deinem Bitstrom. Diese hast du aber mit einem genauen (so genau wie dein Timer halt ist) Zeitstempel. In der ISR schaltest du dann jeweils die Flanke fürs input capture um, damit du bei der entgegengesetzten Flanke wieder ein solches Ereignis erhälst. Aus der Zeitdifferenz zweier aufeinanderfolgender Ereignisse weißt du die Zeit, die zwischen beiden lag. Rundrum muss dann noch eine einfache state machine, die die lange Lücke zwischen den Zyklen erkennt. Wenn man diese das erste Mal erkannt hat, kann man anschließend den gemessenen Impulsbreiten die Kanäle zuordnen.
> Optional kannst dich darüber dann mit einem Interrupt informieren lassen (und das solltest du auch so tun :). Heist die entsprechende ISR wird aufgerufen? > In der ISR schaltest du dann jeweils die Flanke fürs input capture um, damit du bei der entgegengesetzten Flanke wieder ein solches Ereignis erhälst. ISR(TIMER1_CAPT_vect) { if(ipuls==1) { // Nicht vor dem ersten Puls das zählen beginnen if (TNCT1 >= 1407) { ikanal=0; ipuls=0; TNCNT1 =0; } else { TCCR1B &= ~(1<<ICES1); // fallende Flanke cli(); csumme[ikanal] = ICR1; // Pulslänge im Array speichern TCCR1B |= (1<<ICES1); // steigende Flanke sei(); ikanal++; // Counter für nächsten Puls erhöhen } } else { ipuls = 1; // Messung nach erstem Puls freigeben } } Habe ich damit probiert, aber die Kanalzuordnung scheint dabei noch nicht zu passen.
Mal ein Beispiel: Timerclock 1ms, Normalmodus, Timer wird gestartet, also zählt der los: 0 1 2 3 ...usw. bis 65535 0 (Überlauf) 1 2 3 ... Jetzt legst du an den ICP-Pin eine Rechteckfrequenz mit 100Hz an, d.h. alle 10ms kommt eine steigende Flanke. Interrupt Capture auf steigende Flanke, zur Vereinfachung schalten wir nicht um. Der Zähler hat natürlich in der Zwischenzeit weitergezählt... ... 39 40 41 42 => hier kommt jetzt als Beispiel die erste steigende Flanke. Damit wir der Wert 42 nach ICP1 geschrieben. Im der jetzt folgenden ISR kannst du den Wert auslesen. Währenddessen zählt der Zähler, du ahnst es schon, fröhlich weiter: 43 44 45 46 47 48 59 50 51 52 => hier kommt jetzt die nächste Flanke: Also wird 52 ins ICP1 geschrieben. Die Zeit zwischen zwei Flanken beträgt damit 52-42=10 Timertakte. Das geht jetzt immer so weiter, bis ... 65531 65532 => Flanke, ICP1 = 65532 65533 65534 65535 0 (Überlauf) 1 2 3 4 5 6 => Flanke, ICP1 = 6. Der Abstand beträgt 6-65532+65536 = 10. Oliver
Ok, das habe ich gepeilt. ich kann aber den Timer doch auch wieder null setzen oder muss ich den überlaufen lassen?
Mapamann schrieb: > Ok, das habe ich gepeilt. > > ich kann aber den Timer doch auch wieder null setzen oder muss ich den > überlaufen lassen? Können tust du schon. Nur wollen tut man nicht. Der Timer soll in Ruhe einfach weiterzählen, möglichst ohne irgendeine Störung. Um beim vom Oliver gebrachten Beispiel zu bleiben. Timer zählt 0 1 2 3 <- jetz kommt die erste Flanke 4 5 <- jetzt reagiert dein Programm darauf un setzt den Timer auf 0 0 1 2 3 4 5 6 7 <- dem externen Signal war dein Timer auf 0 setzen völlig egal, das kommt trotzdem nach der vorgegebenen Zeitdauer Ergebnis: Laut deiner Rechnerrei sind 7 Timerticks von einer Flanke zur nächsten vergangen. In Wirklichkeit waren es aber 10. Der Timer ist nur schon bis zur 3 gekommen, ehe dein Programm den Timer wieder auf 0 gesetzt hat.
Hi... probiere jetzt seit gestern, aber bekomme das in kein C Konstrukt rein. Hat einer nen Tip bzw. Ansatz?
Mist, kein Bearbeiten Button... Wollte noch Hinzufügen, das ich eben Probleme habe die lange Lücke bzw, den langen Impuls zu erkennen und somit die Kanäle richtig zu zu ordnen...
Grundregel: Wenn ein Programm bei dir nicht so richtig funktioniert und du Hilfe bei deinem Programm benötigst: Zeig das Programm.
Jörg Wunsch schrieb: > schaltest du dabei die jeweilige Flanke fürs input capture um. Ich denke, dass braucht man gar nicht. Das rc-Signal für einen Kanal geht immer von einer fallenden Flanke bis zur nächsten fallenden Flanke (oder umgekehrt, je nach System) http://www.sprut.de/electronic/modell/allgemein/index.htm#1 Es reicht also völlig die Zeit zwischen 2 gleichartigen Flanken zu messen. Liegt diese Zeit über einem Grenzwert, dann ist die nächste Flanke diejenige mit der Kanal 0 beginnt.
Karl heinz Buchegger schrieb: > Das rc-Signal für einen Kanal geht immer von einer fallenden Flanke bis > zur nächsten fallenden Flanke (oder umgekehrt, je nach System) Ich nahm bisher an, die Abstände der steigenden Flanken wäre von Kanal zu Kanal immer gleich, und die Impulsbreite bestimmt den Kanalwert. OK, habe mir die Beschreibung bei Wikipedia nochmal durchgelesen, stimmt, dann muss man nur eine Flanke detektieren.
Jörg Wunsch schrieb: > Karl heinz Buchegger schrieb: > >> Das rc-Signal für einen Kanal geht immer von einer fallenden Flanke bis >> zur nächsten fallenden Flanke (oder umgekehrt, je nach System) > > Ich nahm bisher an, die Abstände der steigenden Flanken wäre von > Kanal zu Kanal immer gleich, und die Impulsbreite bestimmt den > Kanalwert. Dachte ich eigentlich auch immer. Aber wenn man sich das mal überlegt, ist es so wirklich einfacher. Zur Ausswertung benötigt man nur ein Schieberegister. Wir der lange Zeitslot erkannt, kommt eine 1 in das Schieberegister. Mit jedem Puls im Datenstrom wird diese 1 weitergeschaltet. Durch die variable Zeitdauer von einem Puls zum nächsten entsteht dabei an jedem Ausgang des Schieberegisters automatisch ein 1 Puls in genau der zeitlichen Länge, die von einem Puls zum nächsten vergangen ist. Würde man die benötigte Zeitdauer in der Pulsbreite selbst codieren, wäre das viel aufwändiger. (Und jetzt ist mir auch schön langsam klar, warum Fernsteuerungsimpule eine minimale Länge von 1 ms haben. Man braucht ganz einfach eine untere Zeitgrenze für den 'Weiterschaltpuls'. Alles in allem ist das eigentlich ziemlich trickreich :-)
Ich lehn mich jetzt mal aus dem Fenster. Mach ich ungern, weil ich das hier momentan nicht testen kann. Aber IMHO müsste das so funktionieren (oder zumindest dicht drann sein :-)
1 | #include <inttypes.h> |
2 | #include <avr/interrupt.h> |
3 | #include <avr/io.h> |
4 | #define F_CPU 16000000UL // Quarz mit 16 Mhz
|
5 | |
6 | |
7 | volatile uint16_t value[16]; |
8 | volatile uint8_t kanal = 255; |
9 | |
10 | uint16_t prevCapture; |
11 | |
12 | |
13 | ISR( TIMER1_CAPT_vect ) |
14 | {
|
15 | uint16_t thisCapture = ICR1; |
16 | uint16_t time = thisCapture - prevCapture; |
17 | |
18 | if( kanal < 16 ) |
19 | value[kanal++] = time; |
20 | |
21 | if( time > 169 ) // Pulsdauer über 2.5 ms? |
22 | kanal = 0; // endlich: der lange Puls; der nächste Capture |
23 | // wird für Kanal 0 sein
|
24 | |
25 | prevCapture = thisCapture; |
26 | }
|
27 | |
28 | int main(void) |
29 | {
|
30 | DDRD &= ~(1 << PD6); // PD6 Input ICP |
31 | PORTD |= (1<<PD6); // Pull up on PD2 |
32 | DDRA = 0xff; // PORT A komplett Output |
33 | |
34 | |
35 | TIMSK |= (1<<TICIE1); // Capture Interrupt enable |
36 | TCCR1B |= (1<<CS12); // Prescaler 256 Timer 1, Fallende Flanke Capture |
37 | |
38 | sei(); // Globale Interrupts erlauben |
39 | |
40 | |
41 | for(;;) |
42 | {
|
43 | if( kanal < 16 ) // gibts gültige Werte? |
44 | {
|
45 | if( value[5] < 90 ) |
46 | PORTA |= 1 << PA3; |
47 | else
|
48 | PORTA &= ~( 1 << PA3 ); |
49 | |
50 | if( value[6] < 90 ) |
51 | PORTA |= 1 << PA4; |
52 | else
|
53 | PORTA &= ~( 1 << PA4 ); |
54 | }
|
55 | }
|
56 | }
|
Worauf ich jetzt nicht speziell geachtet habe: Das beim erstmaligen Durchlauf durch die Kanäle keine ungültigen Werte ausgewertet werden. Die Zurdnung zu den Kanälen synchronisiert sich allerdings selbst. Nur beginnt die Hauptschleife schon das value Array auszuwerten, wenn der erste Kanal-0 Impuls erkannt wurde und noch keine gültigen Werte im value Array stehen. Beim nächsten Kanal-0 Impuls ist dieser Spuk dann allerdings vorbei und value enthält auf allen erkannten Kanälen gültige Werte.
>Wollte noch Hinzufügen, das ich eben Probleme habe die lange Lücke bzw, >den langen Impuls zu erkennen und somit die Kanäle richtig zu zu >ordnen... Nach dem Einschalten prüfst du jeden reinkommenden Impuls auf seine Länge. Ist der kürzer oder gleich 2ms, machst du gar nichts. Erst, wenn einer auftaucht, der (deutlich) länger ist als 2ms, hast du Kanal 1 gefunden, und fängst mit der Auswertung an. 2ms enstpricht bei 10MHz CPU-Takt und Vorteiler 256 etwas über 78 Zählerschritten. Ich würde da auf mindestens 2,5ms testen, also auf 100 Zählerschritte. Ab jetzt kommen der Reihe nach die Werte für die einzelnen Kanäle. Wiviele das sind, hängt vermutlich von deiner Fernsteuerung ab. Nicht verkehrt ist es, nach jeder Impulsfolge erneut auf die lange Pause zu prüfen, und nur dann neue Werte für die Kanäle anzunehmen, wenn diese auch da war. Es könnte ja mal ein Impuls verloren gehen, dann käme deine Auswertung aus dem Tritt. Oliver
Hi... erstmal wieder Danke für die Hilfe. Hab das Programm von Karl Heinz mal kompiliert und gebrannt, klappt aber nicht.Die LED's reagieren nicht auf meine Fernsteuerung. Ist aber auch nicht schlimm, will es ja im Prinzip selbst hin bekommen. Habe eben nur Probleme das in eine C Syntax zu packen (mangels Erfahrung denke ich). Muss aber jetzt erstmal los, kann also erst heut Nachmittag weiter machen. Werde auf jeden Fall berichten.
Mapamann schrieb: > Hi... > > erstmal wieder Danke für die Hilfe. > > Hab das Programm von Karl Heinz mal kompiliert und gebrannt, klappt aber > nicht.Die LED's reagieren nicht auf meine Fernsteuerung. Ich hab noch einen Fehler nachträglich korrigiert. War value[kanal] = time; soll sein value[kanal++] = time; Hast du diese Änderung mitbekommen? Die Timerwerte hab ich nachgerechnet und angepasst. Bei 16Mhz Taktfrequenz und Vorteiler 256 zählt der Timer bei 2.5 ms bis ~170. 1.5 ms (also Servomitte) müsste irgendwo bei 90 sein. Wenn dein µC mit 16Mhz läuft, sollte das Timing daher stimmen. Edit: Ach ja, sehs gerade. Auf das value Array wird nicht atomar zugegriffen. Da das 16 Bit ist, sollte man das machen. Obwohl, bei den Werten ist das High Byte sowieso immer 0. Also könnte man value durchaus auch auf uint8_t bringen.
Hi... und erstmal wieder Danke Karl Heinz. Habe im Anhang nochmal das aktuelle Programm. Wieso ist value bei dir mir [16] Elementen deklariet? Ich möchte doch "nur" 8 Kanäle + Startimpuls, also sollten doch 9 reichen oder? Bei der Initialierung von ikanal muss ich nur darauf achten, das der Wert größer als der höchste ikanal Wert ist, damit beim ersten Interrupt ikanal genullt wird? Soweit klappt damit auch alles, außer das sich komischerweise in isumme[8] der erste Kanal der Fersteuerung befindet und bei Kanal 2 bis 8 jeweils immer die Led an PA0, zusätzlich zu der jeweiligen Led, die den Kanal sybolisieren soll, mit angeht. Daran rätsel ich schon ne weile.
Mapamann schrieb: > Hi... > > und erstmal wieder Danke Karl Heinz. > > Habe im Anhang nochmal das aktuelle Programm. > > Wieso ist value bei dir mir [16] Elementen deklariet? Weil ich mir beim definieren des Arrays noch nicht sicher war, wie ich ein Überlaufen verhindern werde. Und in solchen Fällen gebe ich gerne noch eine kleine Zugabe :-) > Ich möchte doch "nur" 8 Kanäle + Startimpuls, also sollten doch 9 > reichen oder? Yep. > Bei der Initialierung von ikanal muss ich nur darauf achten, das der > Wert größer als der höchste ikanal Wert ist, damit beim ersten Interrupt > ikanal genullt wird? Genauso das ist die Idee in meinem Code. ikanal fängt mit einem großen Wert an. In der ISR wird der Wert nur dann abgespeichert, wenn ikanal im erlaubten Bereich ist. In diesen erlaubten Bereich kommt es, sobald der erste laaaange Puls vom RC-Eingang erkannt wird. Dadurch synchronisiert sich der ganze Klapperatismus selbsttätig auf den ersten Kanalimpuls
Gut, also habe ich ja zumindest schon ein bisschen was gelernt bzw. verstanden. Freut mich:). Aber warum die LED an PA0 immer mit angeht bzw. Kanal 1 in isumme[8] liegt kannst du dir auch nicht erklären?
Mapamann schrieb: > Gut, > > also habe ich ja zumindest schon ein bisschen was gelernt bzw. > verstanden. > Freut mich:). > > Aber warum die LED an PA0 immer mit angeht bzw. Kanal 1 in isumme[8] > liegt kannst du dir auch nicht erklären? Ehrlich gesagt: nein in isumme[8] müsste meinem Verständnis nach eigentlich die Länge des Synchronisierpulses liegen (wenn dein Sender 8 Kanäle sendet). Kanal 1 müsste sich in isumme[0] wiederfinden. Welche RC-Steuerung benutzt du?
Ich überleg gerade was sich ändern würde, wenn in deinem Empfänger der Ruhepegel nicht high sondern low ist, und man auf die steigende Flanke synchonisieren müsst. Du hast nicht zufällig ein Oszi, an dem du dir das Signal mal ansehen kannst?
Karl heinz Buchegger schrieb: > Ehrlich gesagt: nein > > in isumme[8] müsste meinem Verständnis nach eigentlich die Länge des > Synchronisierpulses liegen (wenn dein Sender 8 Kanäle sendet). Kanal 1 > müsste sich in isumme[0] wiederfinden. > > > Welche RC-Steuerung benutzt du? Das dachte ich mir eben auch. Habe die Synchronisation gleich schon auf steigende Flanke umgestellt. Ist aber trotzdem bei beiden das Selbe. Sender sendet 8 Kanäle, weil ist erstens ne 8 Kanal Funke und zweitens hat's ja, als ich das selbe über den Int0 relisiert hab, wunderbar geklappt (auch steigende Flanke). Habe ne Graupner MC 16/20, Empfänger Graupner C17 Umbau des C17 wegen dem Summensignal hier: http://www.mikrokopter.de/ucwiki/c17?highlight=%28%28----%28-%2A%29%28%5Cr%29%3F%5Cn%29%28.%2A%29KategorieEmpf%C3%A4nger%29 >Du hast nicht zufällig ein Oszi, an dem du dir das Signal mal ansehen >kannst? Oszi leider (noch) keins :(
Hab jetzt seit gestern gesucht und probiert, aber konnte nicht wirklich herraus finden, wieso es nicht klappt. Werd mich später nochmal dran machen, vielleicht finde ich da was. Möchte mich jetzt auch mal an das ansteuern von Servos wagen. Habe ja durch den Zeitstempel die Impulslängen der einzelnen Kanäle in isumme[] gespeichert. Habe Timer 2 so initialisiert: void RC_timer2_init(void) { TCCR2 |= (1<<WGM21) | (1<<COM20) | (1<<CS22) | (1<<CS21); // Timer 2 CTC Mode, Toggle on Compare Match, 256 Prescaler TIMSK |= (1<<OCIE2) | (1<<TOIE2); // Timer 2 Compare Match und Overflow Interrupt enable } Wie ein Servoimpuls aussehen muss weis ich. Nur habe ich ein Problem dabei die Pause zu programmieren. Timer 2 zählt ja nur bis 255 und bei einem Prescaler von 256 und 16MHz macht das ne max Auflösung von etwa 4ms. Damit kann ich ja die 1-2ms machen, aber die Pause dann wohl nur wenn ich etwa 4 oder 5 Überläufe zulasse und dann wieder den Servoimpuls am OC2 Pin ausgebe.Wie verknüfte ich da am besten die Compare und Overflow ISR vom timer 2?
Hi, ich mal wieder:) Nehme inzwischen die Auswertung wieder mit dem Int0 vor. Da klappt alles wunderbar. Habe's auch geschafft ein Servo mittels Timer1 im CTC Mode anzusteuern. Klappt auch wunderbar. Habe allerdings noch ein kleines problem. Ensprechendes Servo soll dann später über einen Kippschalter eine feste Position anfahren. Da soll aber jedoch nich ruckarig passieren, sobald der Schalter umgelegt wurde, sondern soll das Servo "langsam" diese Position anfahren. Bin allerdings noch nicht ganz dahinter gestiegen wie ich das in C realisieren kann. Mit einem Delay in einer ISR arbeiten ist ja "bähhh"...soviel habe ich schon gelesen;) im Anhang mal ein Versuch von mir das zu realisieren...aber es funktioniert irgendwie nicht(wenn, dann ist das sowieso erstmal nur der Fall für Impuls > 1,5ms) Wenn Kanal in "Mittelstellung", dann solls im oberen if Zweig bleiben. Wenn > Mittelstellung, gebe ich mir die Registerwerte für die jeweiligen Impule bzw. Pausen vor. Aber so ganz klappt das eben noch nicht. jemand nen Tip, wie ich so nen Soft drive realisieren kann? Oder ist einfach nur meine selbst gewählte Auflösung (immer +0,1ms) zu groß? Ach ja..hier noch der Kopf: volatile unsigned char cSumSig[8]; volatile uint8_t iCounter=0,iValid=0, ipause=0, isoft=0; volatile uint8_t isdrive=0;
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.