gismow
Active member
- Registriert
- 03.03.2012
- Beiträge
- 2.206
Moin, moin...
Ich bastel ganz gerne an Platinen um Servosignale auszuwerten und damit dann ein bisschen Unsinn zu treiben. Ein zentrales Element ist das Auslesen der Servosignale an bestimmten Pins um diese dann weiter zu verarbeiten.
Die Funktion pulseIn() liest zwar einen Servowert, allerdings ist es blockierend, somit ist sie für meine Zwecke nicht geeignet.
Also habe ich ein bisschen gelesen und probiert. Das Ergebnis ist ein Grundgerüst zum Auslesen von Servowerten per Interrupt. Der Vorteil ist dass die Anzahl der benötigten Signale theoretisch keine Rolle spielt.
Der einzige Nachteil an der Sache ist, dass hier spezifische Defines für den ATMega328 genutzt werden. Will man einen ATTiny nutzen müssen die Defines entsprechend angepasst werden.
Hier nun ein zusammengestelltes Projekt zur Ermittlung eines Servowerts an Pin 2 eines ATMega328.
Ich bastel ganz gerne an Platinen um Servosignale auszuwerten und damit dann ein bisschen Unsinn zu treiben. Ein zentrales Element ist das Auslesen der Servosignale an bestimmten Pins um diese dann weiter zu verarbeiten.
Die Funktion pulseIn() liest zwar einen Servowert, allerdings ist es blockierend, somit ist sie für meine Zwecke nicht geeignet.
Also habe ich ein bisschen gelesen und probiert. Das Ergebnis ist ein Grundgerüst zum Auslesen von Servowerten per Interrupt. Der Vorteil ist dass die Anzahl der benötigten Signale theoretisch keine Rolle spielt.
Der einzige Nachteil an der Sache ist, dass hier spezifische Defines für den ATMega328 genutzt werden. Will man einen ATTiny nutzen müssen die Defines entsprechend angepasst werden.
Hier nun ein zusammengestelltes Projekt zur Ermittlung eines Servowerts an Pin 2 eines ATMega328.
Code:
#define SERIAL_OUTPUT
// Variablen fuer die Lenkung
volatile uint16_t ReceivedStearValues[4] = {1500, 1500, 1500, 1500}; // Die letzten 4 Werte werden gespeichert um einen
// Mittelwert zu bilden
volatile uint16_t ReceivedStearValuesSum = 6000; // Die Summe der letzten 4 Werte
volatile uint8_t ReceivedStearValuesIndex = 0; // Der Index des naechsten auszutauschenden Wwets
volatile uint32_t LastStearChange = micros(); // Letzter Zeitpunkt des Signalwechsels
volatile uint16_t ActualMeasuredStearValue = 1500; // Der aktuell als letztes ermittelte Wert
volatile uint8_t LastInterruptState = PIND; // Interruptstatus des PortD
// Die Interrrupt Routine wird bei jedem Signalwechsel zwischen HIGH und LOW aufgerufen
// Die Routine beruecksichtigt die HIGH Level und errechnet aus den letzten 4 HIGH Level Werten einen Mittelwert
// Dies glättet mögliche Ausreißer und beseitigt kleinere Schwankungen. Gerade für Servos ist dies wichtig um ein stetes Zucken zu vermeiden
void InterruptStear()
{
uint32_t nMicros = micros(); // Aktuellen Zeitstempel merken
uint16_t nDifference = (uint16_t)(nMicros - LastStearChange); // Die Differenz zum letzten Aufruf errechnen
LastStearChange = nMicros; // Zeitstempel fuer das naechste mal merken
if ( (nDifference > 900) && ( nDifference < 2100)) // Zeiten zwischen 900 und 2100 Mikrosekunden sind das HIGH Signal
{
// Wert in der 4er Liste austauschen und Mittelwert errechnen. Dies erfolgt indem der alte Wert von der Summe subtrahiert wird
ReceivedStearValuesSum -= ReceivedStearValues[ReceivedStearValuesIndex];
// Der neue Wert gemerkt wird
ReceivedStearValues[ReceivedStearValuesIndex] = nDifference;
// Den neuen Wert auf die Summe addieren
ReceivedStearValuesSum += nDifference;
// Den Index auf die naechste Position schieben
ReceivedStearValuesIndex = ( ( ReceivedStearValuesIndex + 1 ) & 0x03 ); // Index erhoehen und ggf. von 4 auf 0 springen
// Den aktuellen Mittelwert errechnen indem die Summe durch 4 geteilt wird
ActualMeasuredStearValue = ( ReceivedStearValuesSum >> 2 ); // durch 4 teilen
}
}
void setup()
{
#ifdef SERIAL_OUTPUT
Serial.begin( 57600 );
#endif
PCICR |= (1 << PCIE2); // Interrupt Port D aktivieren
PCMSK2 |= (1<<PCINT18); // Interrupts fuer Pins 2 aktivieren
sei();
}
void loop()
{
// Hier kann nun mit dem Wert von ActualMeasuredStearValue gearbeitet werden
#ifdef SERIAL_OUTPUT
Serial.println(ActualMeasuredStearValue);
#endif
delay(20);
}
ISR(PCINT2_vect)
{
uint8_t PinState = PIND; // Aktuellen Stetus der Interrupts lesen
uint8_t PinChanges = PinState ^ LastInterruptState; // Mit vorherigem Wert verknüpfen
// Das Ergebnis ist eine Bitliste der ausgelösten Interrupts
if (PinChanges & (1<<PCINT18)) // Ist das Bit für Pin 2 gesetzt?
InterruptStear(); // Ja -> Interrupt auswerten
LastInterruptState = PinState; // Alte Bitmaske merken
}