Hi, ich habe jetzt endlich mein Projekt weitesgehnd fertig.
Doch nun habe ich noch zwei Dinge, die naja noch nicht optimal laufen.
1. Wenn ich den Drehgeber langsam dreh, habe ich pro Umdrehung 20000
Flanken gezählt( Langsam = 1 Umdrehungen in ca .2 Sekunden), sobald ich
schneller werde zählt er immer weniger Flanken.
Die Zählroutine habe ich in einem Interrupt.
Den Timerüberlauf habe ich so schnell wie möglich gewählt(laut sprut).
Mein Quarz ist 20 MHz ohne Teiler. Eigentlich müsste der Interrupt so
schnell sein, das er alle Impulse empfägt, oder habe ich einen
Denkfehler?
Hier die Interupt und Timer Einstellung
Für den Zugriff auf die Variable counter in der Routine IntToChar
sollten Interrupts gesperrt werden, damit zwischendurch kein Interrupt
passieren kann.
Je nach Compiler kann es nötig sein die Variable counter als Volatile zu
markieren.
Das case statement in der ISR kann man schneller machen, wenn man dafür
eine Tabelle mit constaten nutzt. 16 Bytes RAM sollten wohl noch übrig
sein. Mit Case gibt das einen kurzen Source code, aber die meisten
Compiler machen daraus eine ganze Menge Vergleiche und Sprünge.
Danke für die Antwort:
In IntToChar hab ich doch counter gesperrt, da ich counter in eine neue
Variable schreibe. Und er denn Code Stück für Stück abarbeitet und so
mit ncounter rechnet!?
Das sollte doch so stimmen, oder!?
zu den Konstanten
meinst da sowas wie
#Define RECHTS1 0b00000001
etc.??
Was wäre eine Alternative für die case Anweisung?
Ich hab schone den Drehgeber Artikel auf µcontroller.d gelesen, also die
schnelle Variante, doch steige leider nicht genau dahinter.
grüße
PicNeuling schrieb:
> Danke für die Antwort:>> In IntToChar hab ich doch counter gesperrt, da ich counter in eine neue> Variable schreibe. Und er denn Code Stück für Stück abarbeitet und so> mit ncounter rechnet!?> Das sollte doch so stimmen, oder!?
Dein Counter ist aber ein signed long, besteht also aus mehreren Bytes.
Was passiert, wenn der Interrupt kommt, wenn von den 4 Bytes 2 bereits
umkopiert sind und 2 noch nicht.
Was mir auch nicht gefällt:
Du fragst counter 2 mal ab. Einmal in der Hauptschleife, einmal in
InttoChar.
Wer garantiert dir, dass da jedesmal derselbe Wert herauskommt?
Deine while Schleife
while(counter_test != counter_old)
{
wird nur maximal 1 mal ausgeführt. Die einzige Möglichkeit für
counter_test seinen Wert innerhalb der Schleife zu ändern, ist hier
counter_old = counter_test;
und damit ist dann auch schon die Endbedingung für die Schleife erfüllt.
Die Schleife ist also in Wirklichkeit keine, sondern ein simples if,
dass sich einen Wolfspelz angezogen hat.
PicNeuling schrieb:
> Den Timerüberlauf habe ich so schnell wie möglich gewählt(laut sprut).> Mein Quarz ist 20 MHz ohne Teiler.
Ob das so ist oder nicht, kann ich mangels PIC-Kentnisse bzw. Datenblatt
nicht sagen.
> Eigentlich müsste der Interrupt so> schnell sein, das er alle Impulse empfägt, oder habe ich einen> Denkfehler?
Benutzt du einen Timer Interrupt zum regelmässigen Nachsehen oder einen
Pin-Change Interrupt?
In deinem Code kommen beide Begriffe vor und aus dem Code werd ich nicht
schlau. Auch die ISR Namen sind so, dass mich das eher an einen
Pin-Change Interrupt erinnert.
> Mir fhelt logisch nicht an, wie ich meine Countervariable 4 mal> speichere, und die erste und die letzte vergleiche, ob deren> DIffernez größer 5 ist!?>> Kann mir jemand auf die Sprünge helfen?
Du brauchst ein Array, welches 4 Werte speichern kann.
signed long LastCounter[4];
Wenn du reihum immer in dieses Array reinschreibst, überschreibt ein
neuer Wert immer den ältesten Wert. Dazu musst du dir nur merken, wo als
nächstes geschrieben werden soll
signed long LastCounter[4];
unsigned char nextLast;
....
LastCounter[ nextLast ] = Counter;
nextLast++;
if( nextLast == 4 )
nextLast = 0;
...
jetzt musst du den jüngesten Wert mit dem ältesten Wert vergleichen.
Welcher ist der älteste? Der mit dem Index in nextLast.
Bsp.
LastCounter[0] 1000
LastCounter[1] 1001
LastCounter[2] 1002
LastCounter[3] 1003
nextLast 0
Die Vorgeschichte der Counter war also
1000, 1001, 1002, 10003
jetzt kommt der neueste Wert, 1004. Gespeichert wird er auf LastIndex[0]
(weil nextLast gleich 0 ist)
LastCounter[0] 1004
LastCounter[1] 1001
LastCounter[2] 1002
LastCounter[3] 1003
nextLast 1
-> der jüngste Wert im Array ist 1004. Das weißt du, weil du ihn ja
gerade hineingeschrieben hast. Der älteste ist 1001 (weil LastCounter[1]
gleich 1001) ist.
Jetzt kommt ein neuer Wert dazu: 1005
LastCounter[0] 1004
LastCounter[1] 1005
LastCounter[2] 1002
LastCounter[3] 1003
nextLast 2
jüngster Wert: 1005. Ältester Wert 1002
Der Case Teil soll ja die Variabel Counter entweder gleich lassen, einen
hochzählen, oder einen Runterzählen. Man kann das auch so sehen, das
immer etwas zu Counter addiert wird, nämlich entweder 0, 1 oder -1. Die
Wert 0,1,-1 kann man in einem konstanten Array als Tabelle (Lookup
Table) haben.
old=new;// Neuen Zustand als Alten Zustand speichern
16
INTCONbits.TMR0IF=0;// Interrupt zurücksetzen
17
}
Das ist mein, so müsste es doch klappen, oder?
anhand der Tabelle
+ Recht
- Links
0 keine Ändernung
F Fehler => 0
binär Bedeutung
00000000 0
00000001 ++
00000010 --
00000011 0
00000100 --
00000101 F
00000110 F
00000111 ++
00001000 ++
00001001 F
00001010 F
00001011 --
00001100 0
00001101 --
00001110 ++
00001111 0
mfg Pascal
> Das ist mein, so müsste es doch klappen, oder?
Ist ganz einfach:
Brenns in den Prozessor und probiers aus.
Das ist ja einer der grossen Vorteile in der Programmierung: Es geht
sehr wenig kaputt. Entweder ein Programm funktioniert oder es
funktioniert nicht :-)
PS:
> habe ich pro Umdrehung 20000 Flanken gezählt
Das kommt mir ein bischen viel vor. Ein bischen sehr viel!
Hi,
ja ich zähle pro Umdrehunge 20000 fallende und steigende Flanken, je
5000 A und 5000 B. Ist sehr genau für eine Art "Drosselklappenstuerung".
Habs so programmiert und ist auch schneller, aber noch etwas zu langsam.
Ich hab einen 20 MHz Quarz => 1 Befehlt dauert 0,2 µs ( 1/20MHz * 4)
Mein TimerO zählt bis 256 => 256 * 0,2µs = 51,2 µs
die Interuptroutine 5 Zeilen Code * 0,2µs => 1µs
Habe ich alle 53 µs einen Interrupt, oder irr ich mich da?
Ist es möglich, dass ich über die Config folgendes mache?!
#pragma config PLLDIV = 5
#pragma config CPUDIV = OSC1_PLL2
#pragma config USBDIV = 1
erst 20MHz durch 5 = 4MHZ für USB darausfolgt 96MHZ USBTakt / 2 =>
PICTakt 48 MHZ?
Daher dann ein Befehlt 0,08µs
.
Stimmt dies Logik, denn so interpretiere ich es auf S. 26 vom Datenblatt
18F2550?!
Ist vielleicht ein Interupt PORTB- On- Change Interrupt schneller?
Kann mir da jemand einen Codefetzen geben, für das Problem!?
mfg
> die Interuptroutine 5 Zeilen Code * 0,2µs => 1µs
Für Hochsprachen wie C/C++ geht diese Gleichung aber nicht auf !!
Jeder Assembler Befehl dauert (vielleicht, ich kenne den PIC nicht gut
genug) gleich lang, aber eine Zeile C wird in viele Assembler Befehle
umgesetzt!
sichrlich hat C etwas overhead ... aber wieviel kann man sich im
avrstudio unter disassebly ansehen ...
so viel mehr code ist das nicht
wenn man in C habwegs vernünftig schreibt ist der resultierende assebler
ebenso klein
Wenn der Interrupt vom Timer erzeugt wird. Dann kommt der Interrupt wohl
alle 51.2 µS. Problematisch wird es erst wenn die ISR länger dauert. Das
sollte bei dem Code wohl noch nicht der Fall sein.
So gut kenne ich den PIC nicht, aber da sollte man auch schnellere
Interrupts hinkriegen. Ein Möglichkeit sollte es sein in der ISR den
Timer auf einen Wert voreinzustellen, so dass der Überlauf früher kommt.
Um zu sehen wie lang die ISR wirklich dauern kann man sich den ASM Code
ansehen, oder das gamze im Simulator laufen lassen. Sollte es für PICs
doch wohl geben.
Update:
So mein Interupr ist auf jeden Fall schnell genug, da ich nur 10
Umdrehungen ( 20000 Flanken pro Umdrehung also 200000) auswerten
muss(jeweils auf die Sekunde bezogen). Und mein Interrupt alle 2,6µs
kommt und die Interruptroutine auf jeden Fall unter 2,4 µs benötigt. Ist
dies schnell genug.
Nun ist aber das Problem. Er verzählt sich. Vorallem bei langen Wegen.
Daher ich starte wenn die Klappe auf dem Boden liegt. Dreh ca. 1
Achtelumdreung ( Wert ist egal), hau sie zurück. Er zeigt mir als
Umdrehungen +0000 an. Sobald ich weiter drehe( Dreiviertelumdrehung),
verzählt er sich. Daher beim wieder Anfahren meinter Nullpostiion zeigt
er -00010. jewils Unterschiede um bis zu 20 - 30 Inkremnte. Er zählt
immer zu viel.
Ich weiß nicht woran es liegen kann ,hat irgendjemand noch eine Idee?
Hab schon die Eingangsignale mit Optokopplern aufbereitet( selbes
Problem)
1
/** I N C L U D E S ************************************************************************************************************************************************************************************/
/** Globale Variablendeklarationen und Definierungen****************************************************************************************************************************************************/
21
signedlongvolatileintcounter=0;// Zähler -2.147.483.648 bis 2.147.483.647
old=new;// Neuen Zustand als Alten Zustand speichern
52
TMR0L=242;// Timer0 Register aufladen auf 242, Zähler zählt nur noch 13 Prozessorschritte => 2,6µs Auflösung für 10 Umdrehungen pro s
53
INTCONbits.TMR0IF=0;// Interrupt zurücksetzen
54
}
55
/** Programm *******************************************************************************************************************************************************************************************/
Mir scheint, die Interruptroutine hat deutlich mehr als 5 Assembler-
Befehle.
Zur bestimmung in "real" würde ich in der Interruptroutine einen Pin
toggeln und mit dem Oszi die wirkliche Zeitdauer zu bestimmen.
Ich glaube aber an ein anderes Problem: Der Inkrementalgeber kommt in
eine Endstellung, wo ein Ausgangssignal wechselt dh. kleine Bewegung um
die Ruheposition.Der Fall scheint mir im code nicht berücksichtigt.
Würde auch besser deine Beobachtung erklären, denn wenn die Interrupts
nicht mehr mitkämen, dann würde ja weniger gezählt.
Grüße gebhard
Hi,
vielen Dank für die Antwort.
Wie würde ich den diesen Zustand abfangen im Code?
Das wäre ja noch ein paar Fragen in der Interuproutine.
Anderes Thema.
Was passiert, wenn mein Interrupt greift und die Kanäle einliest während
einer Flanke!? Vielleicht passiert da der Fehler?
mfg
Also ich hab auch mal einen drehgeber ausgewertet, da nahm ich
2-Flankengesteuerte interrups(interrupt bei steigender und fallender
Flanke). Das ging gut. War aber ein Siemens C161 ,weiß nicht ob du
2Flankengetriggerte Interrupts hast.Sonst mußt halt 2 Interrupteingänge
auswerten, geht auch.
Ich hab den Code noch gefunden, ist eigentlich ganz simpel
void resolver_trap(void) interrupt 0x1A
{
#define TIM_COUNT 0xFFFF-18430
static uint old_system_counter;
static uint old_tim_counts;
uint system_counts;
static ulong speed_counts;
if((P2&0x0400)==0){//Wenn A=0
if((P3&0x0010)==0)res_pos--;//Wenn B=0 runterzählen
else res_pos ++;//Wenn B=1 raufzählen
}
else{if((P3&0x0010)==0)res_pos++;//A=1,B=0,raufzählen
else res_pos --;//A=1,B=1,runterzählen
}
system_counts=(system_counter-old_system_counter);
speed_counts=(ulong)((ulong)(T2-old_tim_counts)+(ulong)(TIM_COUNT*system
_counts));
old_system_counter=system_counter;
old_tim_counts=T2;
}
kanst du sinngemäß übernehmen
Grüße Gebhard
Eine eigene Frage?
Dieser Thread ist über 17 Jahre alt.
Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.