PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : RC Empfänger an ATMega16



Jeti
17.04.2013, 22:03
Hallo an Alle!

wie manche ja vielleicht schon wissen bin ich in letzter Zeit stark am rumprobieren um mehr Verständniss für die Mikroelektronik zu bekommen. Nun wollte ich einmal einen standard RC-Empfänger durch einen ATMega16 auswerten lassen. Dazu habe ich mir folgendes Prinzip überlegt/abgeschaut:

Timer1 soll genau 10µs brauchen um einen Überlauf zu erzeugen. Wenn nun eine positive Taktflanke vom Empfänger kommt soll der Mikrocontroller die Überläufte zählen die bis zur nächsten negativen Taktflanke auftreten und daraus auf die Pulsweite schließen. Die Pulsweite soll nun über ein LCD-Display ausgegeben werden.

Zur Schaltung kann ich noch ergänzen das ich die GND´s der Batterie die den Empfänger antreibt mit dem GND der Platine verbunden habe (ein Fehler den ich sonst immer falsch gemacht habe).

Nun möchte ich vor dem Code noch das Problem beschreiben:
Der Zählerstand ändert sich nicht wenn ich den Stick der Fernbedingung verschiebe. Dazu folgt das die Zahl ein Zufallswert ist, der jedoch konstant bleibt und entweder im Bereich von 600 oder 2230 liegt. Ein Signal vom Empfänger kommt aber denn wenn ich die Fernbedinung ausschalte dann beginnt der Zahlen-Wert durch das Rauschen im Empfänger an zu springen.

Das Projekt arbeitet mit 8MHz... jetzt der Code (dazu sei Gesagt das die LCD-Befehle in anderen Projekten funktionieren)



#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include "lcd_tools.h"
#include <util/delay.h>
volatile int inizialisierung;
volatile uint16_t counter=0;
volatile int posneg;
volatile uint16_t Zeit=0;

//LCD Ausgabe
void ausgabe()
{
char Z[10];
lcd_gotoline(1);
lcd_writetext("Kanal 1:");
lcd_cls_line(2);
lcd_gotoline(2);
itoa(Zeit,Z,10);
lcd_writetext(Z);
}



// Timer/Counter1 Start und Ende
ISR (TIMER1_CAPT_vect)
{
if (posneg<1)
{
counter=0; //counter zurücksetzen
TCCR1B&=~ (1<<ICES1); //auf fallende Flanke triggern
posneg=1;
}
else
{
cli();
Zeit=counter; // Zeit im µs berechnen
TCCR1B|= (1<<ICES1); //auf steigende Flanke triggern
ausgabe (); // Ausgabe auf Display
posneg=0;
sei();
}
}

// Timer/Counter1 Vergleichszähler
ISR (TIMER1_COMPA_vect) // je 10µs einmal
{
counter++; // Überläufe zählen
}


int main(void)
{
lcd_ini();
TIMSK |= (1 << TICIE1) | (1 << OCIE1A); // Input Capture Interrupt Enable+Output Compare A Match Interrupt Enable
OCR1A = (uint16_t)(0x50); // Vergleichswert 10µ Sekunden
TCCR1B |= (1 << WGM12); // CTC Modus
TCCR1B |= (1 << ICNC1) | (1 << ICES1); // Noise Canceler + steigende Flanke an ICP1
TCCR1B |= (1 << CS10); // Prescaler: 1
lcd_gotoline(1);
lcd_writetext ("Kalibrierung");
_delay_ms(1000);
inizialisierung=0; // Kalibrierung erforderlich
posneg=0; // warten auf positive Flanke
lcd_cls();
sei(); // IRQs enable

while(1)
{

}
}


Die Variable "inizialisierung" soll später für weitere Funktionen genutzt werden.Im Moment ist sie nutzlos. Genauso wie die Ausgabe "Kalibrierung" auf dem LCD.

Danke schon mal für die Hilfe und den Hirnschmalz.

Viele liebe Grüße
Jeti

wkrug
17.04.2013, 22:53
Ich würde für diese Aufgabe den ICP Interrupt nutzen.
Zuerst wird das sensing auf steigende Flanke gestellt.
Wenn die dann kommt wird das ICP Timer register ausgelesen und das Sensing auf fallende Flanke umgestellt.
Tritt die dann auf wird das ICP Register wiederum ausgelesen un der vorherige Wert abgezogen.
Das Sensing muss dann für den nächsten Impuls wieder auf steigende Flanke umgestellt werden.
Bei 8MHz Taktfrequenz und einen Prescaler von 8 kriegst Du damit einen Auflösung von 1µS.
Du müsstest dann allerdings einen 16Bit Timer nehmen, weil der Zählerstand ja immer über 255 liegt.

Die ermittelte Impulslänge kann dann in einer Variable abgespeichert werden. Ich würde dann noch ein Flag ( Bit Variable ) setzen, damit dieser neue Wert im Hauptprogramm bekannt gemacht wird und verarbeitet werden kann.

Das System läuft gut und scheinbar im Hintergrund, da alle zeitkritischen Operationen im Interrupt stattfinden, also Laufzeiten im Hauptprogramm keinen Einfluss auf die Impulsauswertung haben.

Jeti
18.04.2013, 10:13
Danke für die Antwort, aber ich glaube das (oder so etwas ähnliches) versuche ich doch gerade oder? Der Empfänger hängt in meinem Fall am IPC1 also PD6. Und da ich die Anzahl der Überläufe zähle die jeweils 10µs lang sind müsste ich doch eigentlich auch auf eine Antwort kommen... ist halt nicht ganz so genau :)...

Searcher
18.04.2013, 11:16
Hallo,
wie ist der Empfänger am Mega16 PD6 angeschlossen? Kann es sein, daß der Empfängerausgang ein Open Collector ist und Du da noch einen Pullup Widerstand brauchst - eventuell den internen des Mega16 nutzen?

Gruß
Searcher

Jeti
18.04.2013, 11:32
Ich habe im Moment einfach nur das Kabel an den Pin angeklemmt... also ohne irgendwas. Ich habe mich auch schon gefragt ob ich da noch einen Widerstand brauche aber ich hätte eher einen widerstand zwischen Signal und GND (Pulldown?!?) geklemmt... Wie kann ich denn denn den internen Pullup aktivieren oder muss ich wirklich einfach einen Widerstand anklemmen?

Gruß

PS: Was der Empfänger für einer ist kann ich nicht mehr sagen. Er ist ziemlich alt. Bestimmt 15 Jahre oder mehr

Searcher
18.04.2013, 11:41
Hi, bei RC Empfängern speziell kenne ich mich nicht aus. Üblich sind Open Collector bei NPN Transistoren. Bedeutet, Du brauchst einen PULLUP Widerstand. Also einfach einen 10k Widerstand von PD6 nach 5V Vcc des Mega16 schalten. Alternativ kannst Du den internen Pullup des Mega16 einschalten. Im PORTD Register das PD6 Bit auf eins setzen bei als Eingang konfigurierten PD6; PD6 ist ja schon in Deinem obigen Programm defaultmäßig auf Eingang, also noch PD6 im PORTD setzen.

Gruß
Searcher

Jeti
18.04.2013, 12:56
Ich habe das mit dem Pin PD6 ausprobiert. Leider hat es das Ergebnis nicht verändert. Ich werde nun bei gelegenheit mal einen 10k Widerstand nach VCC schalten. Mal schauen ob das was ändert, dürfte es ja aber eigentlich dann nicht mehr. Sonst noch ideen was falsch sein könnte?

Nachtrag:
Ich habe noch ein bisschen rumprobiert und mit ist dabei folgendes Aufgefallen. Es gibt nur zwei Zählerstände die jetzt noch ausgegeben werden. Der eine ist 440... der sagt mir garnicht. Unterbreche ich die gemeinsame GND versorgung von Board und Empfänger und stelle sie wieder her, wechselt dieser zu 2060 und umgekehrt (wobei der Wert nicht bei jeder unterbrechung wechselt). 2060 wären ja dann laut programm 2060 Überkäufe mit je 10µs also genau 20,6ms... kann das sein das der Controller immer nur eine taktflanke auswertet? Entweder positiv oder negativ?

Searcher
18.04.2013, 13:21
// Timer/Counter1 Start und Ende
ISR (TIMER1_CAPT_vect)
{
if (posneg<1)
{
counter=0; //counter zurücksetzen
TCCR1B&=~ (1<<ICES1); //auf fallende Flanke triggern
posneg=1;
}
else
{
cli();
Zeit=counter; // Zeit im µs berechnen
TCCR1B|= (1<<ICES1); //auf steigende Flanke triggern
ausgabe (); // Ausgabe auf Display
posneg=0;
sei();
}
}


Noch 'ne Idee. Wenn die Ausgabe läuft, dauert das ziemlich lange. Die Impulse mit steigenden und fallenden Flanken treffen trotzdem noch am PD6 ein und das ICP Interruptflag wird gesetzt. Sobald das sei() kommt, wird deshalb sofort die ISR erneut aufgerufen von einer lange vorher aufgetretenen Flanke und verfälscht das Ergebnis?
Vor sei() würd ich durch eine Eins Schreiben des ICF1 Bits im TIFR Register das Interruptflag sicherheitshalber löschen.

Welche Länge können denn die Impulse haben, die vom RC-Empfänger kommen?

Wenn Du nicht weis ob es ein Open Collector Ausgang ist, schadet es nichts, trotzdem den internen Pullup einzuschalten.

Gruß
Searcher

Jeti
18.04.2013, 21:01
Danke Searcher!

Das war die Lösung. Seit dem setzen des ICF1 Bits im TIFR Register funktioniert alles einwandfrei.

Super!

Gruß
Jeti

Searcher
19.04.2013, 08:14
.........http://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcT-ma3HjhaGFgXTiqrftHx4hL_ipiJLifo_pAkXNQRTeUm1x6AioB SK7QQ


Wenn die dann kommt wird das ICP Timer register ausgelesen und das Sensing auf fallende Flanke umgestellt.
Tritt die dann auf wird das ICP Register wiederum ausgelesen un der vorherige Wert abgezogen.

Danke für die Antwort, aber ich glaube das (oder so etwas ähnliches) versuche ich doch gerade oder?
Hallo Jeti,
in Deinem Programm nutzt Du nur den Interrupt, den die Interrupt Capture Funktion zur Verfügung stellt. Das Gleiche könntest Du auch mit den INt0, INT1 oder INT2 Interrupts erreichen. Die schöne Funktion, die die Input Capture Funktion noch zur Verfügung stellt, ist, daß bei dem Capture event nicht nur das Interrupt Flag gesetzt wird, sondern auch gleichzeitig der Timerstand in das ICR1H/ICR1L Register kopiert wird.

Wenn der Timer1 nun zB mit 1MHz laufen würde, bei steigender Flanke der Input Capture zuschlägt und man dann ICR1H/ICR1L sichert, auf fallende Flanke umschaltet und beim nächsten Input Capture den gesicherten Wert mit dem neuen aktuellen ICR1H/ICR1L Wert verrechnet, hat man eine Auflösung von 1µs.

Außerdem sollte man die ISR so kurz wie möglich halten. Die Anzeigefunktion also irgendwie in die main plazieren.

Gruß
Searcher