Encoder zu schnell für einen schlechten Atmega328?

Ich habe mit ein paar Servomotoren gespielt, die ich aus einem Überschuss an Bestückungsautomaten bekommen habe. Bisher habe ich ihre Pinbelegung dekodiert und arbeite daran, den Encoder zu lesen, hier kommt das Problem:

Nach dem, was ich beobachtet habe, ist der Motor mit 4096 Kuadraturimpulsen pro Umdrehung + 1 Indeximpuls pro Umdrehung ausgestattet. Einige Tests in der Arduino IDE haben gezeigt, dass der Motor etwas schneller dreht und der Encoder anfängt, Schritte zu verlieren ...

Ich habe mich entschieden, den Code auf AS7 zu migrieren und den gesamten Arduino-Overhead zu spülen, aber der Chip scheint nicht in der Lage zu sein, damit umzugehen. Korrigiert mich, wenn ich bei folgendem falsch liege:

Mit 2048 cpr (nur mit dem ansteigenden Impuls eines Cuadraturkanals) und einer Drehzahl von 3000 U / min ist eine Umdrehung in 0,02 Sekunden abgeschlossen.

Unter der Annahme der vorherigen 20 ms / 2048 ppr haben wir alle 0,097 ms eine ansteigende Flanke -> 97 uS geben oder nehmen.

Reicht diese Zeit, um die folgende ISR auszuführen?:

#define F_CPU 16000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>

volatile int count;

int main(void)
{

    DDRD  = (0<<PORTD2) | (0<<PORTD3)| (0<<PORTD4); 
    PORTD = (0<<PORTD2) | (0<<PORTD3)| (0<<PORTD4);  
    EICRA = (1 << ISC11) | (1 << ISC01);  // Configure interrupt trigger on rising edge of INT0  
    EIMSK = (1 << INT0); //ebable INT0 
    sei();

    while (1) 
    {

    }
}

ISR (INT0_vect){

    uint8_t i = ((PIND & 0b00010000)>>4);


        if (i == 1) {

            count = count +1;

            }else{

            count = count -1;
        }       

    EIFR = (1<<INTF0);
}

Wenn nicht, wie soll ich es tun ... Dedizierter Zähler-IC?

Danke

**Bearbeiten:** Aufnahme des Logikanalysators, der eine Umdrehung (Index) mit Encoder A vergleicht

ergreifen

Sie haben die MCU-Frequenz nicht erwähnt, aber selbst für etwas wie 1 MHz sind es immer noch ~ 100 Zyklen, was für ISR oben ausreichen muss. Haben Sie keine andere (langsamere) ISR in Ihrem Code oder einen anderen langen Block mit deaktivierten IRQs? Ist das elektrische Signal an den Pins in Ordnung (Endlage des Encoders, Spurinduktivität/-kapazität, Pull-Ups, ...)?
Aber eigentlich ist deine Mathematik daneben. 20 ms / 2048 sind nur 10 us, nicht 100 us. Also, mit welcher Frequenz läuft Ihre MCU?
Wenn Sie jetzt ignorieren, dass die Zeit in Sekunden gemessen wird, nicht in Siemens, wird Ihr ISR mit einem nicht optimierenden Compiler wahrscheinlich bei etwa 4 + 12 + 10 + 12 + 4 = 42 Zyklen liegen. Geben oder nehmen Sie ein paar, während ich schätze. Mit der Korrektur von @ Martin müssen Sie mit 4,2 MHz laufen, um Schritt zu halten, wenn Sie sonst nichts tun. Das sind also realistisch gesehen mindestens 12 MHz, wenn man nicht viel oder sonst etwas tut ... also ...
Ups, vergessen zu erwähnen, im Moment laufe ich auf einem Standard-Arduino Uno mit 16 MHz. @Martin, du hast Recht, ich poste hinterhältig aus dem Büro und habe eine 0 vergessen! Der Code macht nichts anderes als die Avobe-ISR auszuführen, lassen Sie mich den vollständigen Code posten.
@Martin, wie im Oszilloskop zu sehen ist, sehen die Impulse klar und schön aus. Denken Sie nicht, dass es sich um ein Problem mit der Signalintegrität handelt.
Sie sollten Ihr Inkrement/Dekrement direkt vom Bit abhängig machen, ohne die gesamte Zwischenzuweisung. Stellen Sie außerdem sicher, dass Ihr Zugriff auf die Zählung außerhalb der ISR atomar ist - ein Arduino inthat 16 Bit, sodass der Zugriff normalerweise in zwei Schritten erfolgt. Schließlich scheint es eine schlechte Idee zu sein, ein Pick & Place für Experimenter-Teile zu verschrotten - es ist wahrscheinlich mehr wert als Plattform für jemanden, der mit einem Pick & Place spielen möchte, oder als Teile für die Wartung eines vergleichbaren Modells.
Es gibt einige Controller in der PIC-Familie, die für die Handhabung von Encodern ausgelegt sind, und zwar direkt in silico. Vielleicht erwägen Sie einen Wechsel.

Antworten (3)

avr-gccIch habe Ihren Code mit Optimierung durchlaufen -Os(YMMV, wenn Sie einen anderen Compiler, Flags usw. verwenden, aber in könnte ein guter Ausgangspunkt sein) und das Ergebnis zerlegt, hier ist es:

00000090 <__vector_1>:
 2  90: 1f 92           push    r1
 2  92: 0f 92           push    r0
 1  94: 0f b6           in  r0, 0x3f    ; 63
 2  96: 0f 92           push    r0
 1  98: 11 24           eor r1, r1
 2  9a: 8f 93           push    r24
 2  9c: 9f 93           push    r25
1 2 9e: 4c 9b           sbis    0x09, 4 ; 9
2 . a0: 06 c0           rjmp    .+12        ; 0xae <__vector_1+0x1e>
. 2 a2: 80 91 00 01     lds r24, 0x0100
. 2 a6: 90 91 01 01     lds r25, 0x0101
. 2 aa: 01 96           adiw    r24, 0x01   ; 1
. 2 ac: 05 c0           rjmp    .+10        ; 0xb8 <__vector_1+0x28>
2 . ae: 80 91 00 01     lds r24, 0x0100
2 . b2: 90 91 01 01     lds r25, 0x0101
2 . b6: 01 97           sbiw    r24, 0x01   ; 1
 2  b8: 90 93 01 01     sts 0x0101, r25
 2  bc: 80 93 00 01     sts 0x0100, r24
 1  c0: 81 e0           ldi r24, 0x01   ; 1
 1  c2: 8c bb           out 0x1c, r24   ; 28
 2  c4: 9f 91           pop r25
 2  c6: 8f 91           pop r24
 2  c8: 0f 90           pop r0
 1  ca: 0f be           out 0x3f, r0    ; 63
 2  cc: 0f 90           pop r0
 2  ce: 1f 90           pop r1
 4  d0: 18 95           reti

Zahlen vor Befehlsadressen sind meine Ergänzung zur Disassembler-Ausgabe, Anzahl der Zyklen für die Ausführung basierend auf dem AVR-Befehlssatzhandbuch . Wenn ich es richtig zähle, sind es insgesamt 43 Zyklen + 5 Zyklen für die Interrupt-Antwort (+ etwa 3 Zyklen für die Ausbreitung des Pin-Wechsels). Der ISR-Code kann bei Bedarf manuell viel kürzer optimiert werden. Aber es sind immer noch etwa 50 Zyklen, 3 us bei 16 MHz.

PIND wird 12 Zyklen nach ISR-Start gelesen, etwa 20 Zyklen (1,25 us) nach INT0-Flanke. Sollte noch ok sein.

Sie müssen nicht viel Rand, aber es sollte funktionieren. OTOH jeder andere ISR wird ihn wahrscheinlich "töten", da ATmega keinen Interrupt-Controller mit Prioritätsbehandlung hat. Übrigens. In dem Code, den Sie hier einfügen, gibt es keine Verarbeitung von countVariablen, daher muss es selbst zum Testen komplizierter sein. Sind Sie sicher, dass Sie dort nichts tun, was die ISR-Latenz beeinflusst?

Als alternative Lösung – wenn Sie XMega für Ihr Projekt in Betracht ziehen können (vorausgesetzt, Sie möchten AVR verwenden), haben diese Hardware-Unterstützung für Encoder und Sie können damit ohne FW-Interaktion gegensteuern.

Ich werde nicht so tun, als ob ich das Ausmaß Ihrer Erklärung vollständig verstehe, das wird mich einige Zeit zum Lesen und Googlen kosten. Es gibt keine anderen ISRs, zum Debuggen wurde der Wertzähler alle 2 Umdrehungen in der Hauptschleife gespeichert und gedruckt (1 Umdrehung zum Abtasten (keine Störung durch den Uart-Druck) und eine weitere zum Drucken (mit möglichen fehlenden Zählungen aufgrund des Drucks). ) xmega schaue ich mir auf jeden Fall mal an, bin Wirtschaftsingenieur, habe ein bisschen mit Arduinos rumgespielt und wollte dieses Projekt zum Anlass nehmen, etwas mehr dazuzulernen!
@Aleix, der Hinweis zu anderen ISR war eher auf die Zukunft ausgerichtet. Wenn Sie darauf aufbauen möchten, müssen Sie wahrscheinlich einige für die Funktionalität hinzufügen, auf die Sie abzielen. Über das Lesen von Zählern - entweder Sie tun dies mit deaktiviertem ISR (also potenzielles Timing-Problem, wenn Sie nicht aufpassen), oder Sie haben eine Race-Condition, die eine Multibyte- countVariable liest, während ISR sie in der Mitte aktualisieren kann. Wenn Sie einen Off-by-One-Fehler sehen, kann dies möglicherweise durch die Latenz der Erkennung der gesamten Umdrehung im Code verursacht werden.

Zwischen ansteigenden Flanken liegen weniger als 5 us, da zwei Sensoren vorhanden sind. Dadurch bleibt keine CPU-Zeit übrig, um die Informationen tatsächlich für irgendetwas zu verwenden. Außerdem tasten die meisten sauberen Quadratur-Decoder-Schemata zwischen allen 4 Kanten ab, sodass nur 2,5 uS zwischen Kanten/Abtastungen verbleiben würden.

Sie werden wahrscheinlich sehr knapp bei der Zeit sein, um 100% in der Firmware zu erledigen.

Es ist das, was ich befürchte, aber das Drehen mit etwa 1 Umdrehung pro Sekunde scheint die Leistung auf die gleiche Weise zu beeinflussen ...
Wenn es bei 1 U/s nicht funktioniert, stimmt etwas anderes nicht.
Indem nur auf die ansteigende Flanke von einem der Kanäle geschaut wird und nicht verlangt wird, dass ein Übergang des anderen eingreift, wird die Implementierung in der Tat anfällig dafür, Schwingungen über dieser Flanke zu multiplizieren . Dies ist tatsächlich eher bei langsamen Geschwindigkeiten oder manueller Drehung zu sehen als beim normalen Laufen.
Schauen Sie sich die Aufnahme an, ich habe das Oszilloskop bei der Arbeit, leider habe ich hier nur diesen kleinen Analysator ... @ChrisStratton, ich verstehe, dass es alles andere als ideal ist, dies auf diese Weise zu tun, ich habe nur die Hardware getestet. Aber anstatt Schritte zu verlieren, sollte ich Schritte "gewinnen"?
Sie könnten mehrfach rückwärts ruckeln in der Rotation zählen. Bis zu einem gewissen Grad lohnt es sich nicht, Implementierungsdetails eines fehlerhaften Algorithmus zu debuggen, bis Sie den Algorithmus selbst repariert haben.

10us ist selbst für schnellere Taktraten ziemlich schnell.

Möglicherweise möchten Sie den Taktimpuls in einen Timer / Zähler einspeisen und diesen als Vorskalar verwenden, wenn Sie den Motor mit Geschwindigkeiten antreiben. Natürlich ist der Übergang von einem Modus zum anderen schwierig und muss bei langsameren Geschwindigkeiten erfolgen, bei denen Sie ein ausreichend breites Zeitfenster zwischen den Impulsen garantieren können, um den Wechsel durchzuführen.

Das habe ich beim Googeln gesehen, das wird mich sehr ärgern, ich habe sehr wenig Programmierkenntnisse (fast nicht vorhanden) und eine solche Schaltfunktion implementieren zu müssen, riecht nach Frust!
Nach meinem nicht großen Wissen habe ich versucht, Folgendes zu tun: - Holen Sie sich die Gesamtheit von PIND - Maskieren Sie es und verschieben Sie das interessante Bit, um entweder eine Eins oder eine 0 zu erhalten. Könnten Sie das bitte etwas näher erläutern: - Verstanden.
Ich verwende PD4, mache ich falsch, die resultierenden 00010000 um vier Positionen nach rechts zu verschieben?
@Aleix nvm .. mein Fehler. Anscheinend habe ich >> falsch gelesen