Hallo
Selbstverständlich kann der Mega32 auch drei (oder mehr) RC-Signale messen. Im oben erwähnten Thread habe ich weiter unten die Funktion nochmals näher beschrieben. Der eigentliche Messvorgang geschied in der ISR. Weil beim RP6 die Timer und ihre ISR schon alle belegt sind verwende ich in diesem Beispiel meine eigene abgespeckte Library (rblib). Man könnte auch die orginale Lib verwenden, dann muss man aber die ISR in RP6RobotBaseLib.c ändern. Aber dazu später mehr, erstmal die Grundlagen zum Impulslängenmessen:
Die Servoimpulse sollten zwischen 0,5 und 1,5ms lang sein (je nach Doku auch 1-2ms). Gehen wird mal von 1ms Impulslänge und 100 als gewünschten Positionswert für die Servomitte aus. (Ohne den Totpunkt des Servos zu beachten wäre das eine Auflösung von ca. 100 Schritten.) Dann muss eine Positionseinheit 1ms/100=10µs dauern. Deshalb muss die ISR alle 10µs aufgerufen werden, bei jedem Aufruf wird ein temporärer lokaler Zähler erhöht, solange der Impuls am Eingang erkannt wird. Wenn das Ende des Impulses erkannt wird, wird der Zählerinhalt in eine globale Variable kopiert und steht damit überall im Programm zur Verfügung. Der Zähler wird wieder auf 0 gesetzt und wartet auf den Start des nächten Impulses. Als Code für die ISR sieht diese Funktion bei mir so aus:
Code:
if (PINC & 1) rc_temp_dir++; else
if (rc_temp_dir) { rc_input_dir=rc_temp_dir-1; rc_temp_dir=0; }
if (PINC & 2) rc_temp_pwr++; else
if (rc_temp_pwr) { rc_input_pwr=rc_temp_pwr-1; rc_temp_pwr=0; }
if (PINA & 16) rc_temp_pwr++; else
if (rc_temp_pwr) { rc_input_pwr=rc_temp_pwr-1; rc_temp_pwr=0; }
Wenn am Eingang ein High anliegt wird der Zähler (rc_temp_xxx) erhöht . Ist der Eingang nicht gesetzt (else-Zweig) und der Zähler gleich 0 (rc_temp_xxx ist dann false) passiert nichts, weil wir auf den Impulsstart warten. Ist der Zähler allerdings ungleich 0 wurde etwas gezählt. Dann wird der Zähler in die globale Input-Variable (rc_input_xxx) kopiert und anschliesend gelöscht. Das ist dann der Status "warten auf Impulsstart".
Diese Beispiele verwenden die Pins SDA, SCL und E_INT1. Die sind alle direkt am XBUS verfügbar (Pin 8,10 und 12?). Vom RC-Empfänger müssen die PWM.Signale jeweils mit einem Mega32-Eingang verbunden sein, sicherheitshalber über einen Schutzwiderstand (ca. 1k?), zusätzlich müssen Vcc und GND vom RP6 und dem Empfänger verbunden sein. Man könnte aber auch andere Pins verwenden, ADC0/1 wäre noch günstig
Soweit alles klar? Jetzt wird's nämlich noch etwas komplizierter: Da bei meinem Beispiel mit meiner Lib der Timer0 frei ist sieht die ISR bei mir so aus:
Code:
ISR(TIMER0_COMP_vect)
{
if (PINC & 1) rc_temp_dir++; else
if (rc_temp_dir) { rc_input_dir=rc_temp_dir-1; rc_temp_dir=0; }
if (PINC & 2) rc_temp_pwr++; else
if (rc_temp_pwr) { rc_input_pwr=rc_temp_pwr-1; rc_temp_pwr=0; }
}
Der Timer wird im CTC-Mode betrieben. Das Timerzählregister wird mit jedem Timertakt erhöht und mit einem Grenzwert (OCR0) verglichen. Wenn Timerzählregister und der Grenzwert gleich groß ist wird das Timerzählregister auf 0 gesetzt und die ISR aufgerufen. (btw. wenn nun der Grenzwert erneut erreicht wird BEVOR die ISR beendet ist spricht man von einem Überlauf) Das Setup des Timers sieht dann so aus (weiter Infos im Datenblatt des Mega32):
Code:
TCCR0 = (0 << WGM00) | (1 << WGM01); // CTC-Mode
TCCR0 |= (0 << COM00) | (0 << COM01); // ohne OCR-Pin
TCCR0 |= (0 << CS02) | (1 << CS01) | (0 << CS00); // prescaler /8
TIMSK = (1 << OCIE0); // Interrupt ein
OCR0 = 10;
(eigentlich fehlt hier noch ein sei() ;)
Wichtig ist der Prescaler. Der Timer läuft nicht direkt mit dem Systemtakt des Microkontrollers, sein Takt errechnet sich aus dem Systemtakt geteilt durch den Prescaler, beim RP6 sind das 8MHz/8=1MHZ. Da nach zehn Timerzähltakten dann der Interrupt ausgelöst wird (OCR0=10) kommen wir so auf 1MHZ/10=100kHz oder eben 10µs zwischen den Interrupten.
In der RP6-Library wird der Timer aber mit OCR0=99 konfiguriert, das ergibt einen 10kHz-Takt und 100µs zwischen den Interupts. Wenn man nun die Lib-ISR einfach mit den RC-Zählfunktionen ergänzt würden die Messwerte um den Faktor 10 kleiner sein als bei meinem Beispiel:
Code:
ISR (TIMER0_COMP_vect)
{
if (PINC & 1) rc_temp_dir++; else
if (rc_temp_dir) { rc_input_dir=rc_temp_dir-1; rc_temp_dir=0; }
#ifdef POWER_ON_WARNING
(RC-Code in der Datei RP6RobotBaseLib.c einfügen)
Das würde immer noch eine Auflösung der Impulslängen in 10 Schritten ermöglichen.
Für eine höhere Auflösung müßte man in der RP6-Lib den OCR0-Wert verkleinern. Dadurch würden aber einige andere RP6-Funktionen beeinflußt werden, z.B. Sleep() und mSleep(), die Stopwatches und wasweisichnoch. Außerdem würde zehnmal mehr Interupts ausgelöst werden. das würde das System ausbremsen und da die ISR in der RP6-Lib recht lang ist würde dies auch zu einem Überlauf nach dem anderen führen.
Ächz. Der Beitrag ist doch etwas länger geworden.
Gruß
mic
Lesezeichen