Tester schrieb:> Was wird in dieser folgenden Zeile genau gemacht:
Ein Wert berechnet
>
1
>OCR0=(uint8_t)(XTAL/64.0*1e-3-0.5);// 1ms
2
>
XTAL ist die Taktfrequenz, also zb 4000000 für 4Mhz
64 korrespondiert mit dem Timer Prescaler von 64
1e-3 ist die wissenschaftliche Exponentenschreibweise von 0.001
also einem Tausendstel. Da eine Millisekunde eine tausendstel
Sekunde ist, könnte da ev. ein Zusammenhang bestehen?
Und die 0.5: die sind einfach nur Rundungskorrektur, wenn das floating
Point Ergebnis wieder auf einen Integer gebracht wird.
Wenn ein Timer in 1 Sekunde von 0 bis XTAL/64 zählen kann, wie weit (x)
muss er dann zählen, damit er genau nach 1 tausendstel Sekunde damit
fertig ist?
XTAL/64 ..... 1
x ..... 0.001
-------------------------------------
XTAL/64 * 0.001
x = ----------------------
1
Danke für die ultraschnelle Antwort!
Ich möchte aber einmal mein Problem schildern:
Wenn ich es so hier habe: (wie im Beispielcode)
TCCR0 = 1<<WGM01^1<<CS01^1<<CS00; // CTC, XTAL / 64
OCR0 = (uint8_t)(XTAL / 64.0 * 1e-3 - 0.5); // 1ms
und so abändere dass ich den Vorteiler auf 8 stelle aber auch bei der
Rechnung die 8 anpasse dann müssten die Interrupts doch wieder in
gleichen Zeitabständen auslösen:
TCCR0 = 1<<WGM01^1<<CS01^0<<CS00; // CTC, XTAL / 64
OCR0 = (uint8_t)(XTAL / 8.0 * 1e-3 - 0.5); // 1ms
Tun sie aber nicht!!??
Wieso benötige ich das (uint8_t)?
Tester schrieb:> und so abändere dass ich den Vorteiler auf 8 stelle aber auch bei der> Rechnung die 8 anpasse dann müssten die Interrupts doch wieder in> gleichen Zeitabständen auslösen:
Bei welcher Taktfrequenz?
Schon mal nachgerechnet, welcher Zahlenwert dann bei der Rechnung
rauskommt?
Alles über 255 ist: game over. you loose
Auch sollte der Zahlenwert, der da rauskommt so sein, dass OCR0 * 8
einen Wert von größer, sagen wir mal 200, ergibt. Ansonsten verbringt
dein µC seine Zeit fast nur noch im Interrupt.
Und zu guter letzt:
Lass dich nicht von der 1 Millisekunde verrückt machen. Die spielt keine
große Rolle, sondern ist einfach nur ein Kompromiss zwischen "es wird
oft genug nachgesehen" und "aber auch nicht so oft, dass nur noch
nachgesehen wird". Wenn deine ISR alle 2 Millisekunden oder alle 0.5
Millisekunden aufgerufen wird, dann ist das auch gut.
Ok sorry! Habe hier was verwechselt.
Nur eines noch:
Im Beispielcode ist XTAL mit 8000000 angegeben.
XTAL/64 * 0.001
x = ----------------------
1
Durch den Teiler zählt er nun in 1s auf 125000.
--> In 1ms auf 125
Deshalb OCR0=125;
Müsste so sein oder?
Und die -0.5 wahrscheinlich damit wenn das Ergebnis nicht rund ist der
Interrupt sicherheitshalber minimal früher kommt oder?
Richtig?
Was hat es noch genau mit dem (uint8_t) auf sich.
Wenn ich das (uint8_t) weglasse müsste es doch auch funktionieren??
Danke dir nochmals!
Tester schrieb:> Im Beispielcode ist XTAL mit 8000000 angegeben.>> XTAL/64 * 0.001> x = ----------------------> 1>> Durch den Teiler zählt er nun in 1s auf 125000.>> --> In 1ms auf 125>> Deshalb OCR0=125;>> Müsste so sein oder?
Ja.
> Und die -0.5 wahrscheinlich damit wenn das Ergebnis nicht rund ist der> Interrupt sicherheitshalber minimal früher kommt oder?> Richtig?
Eher nicht.
Wenn bei der Division 256.3 rauskommt, dann retten die 0.5 die Sache
noch, indem sie das Ergebnis in den erlaubten Bereich bis 255 drücken.
Ich denke, das wird die Absicht dahinter sein.
Wie gesagt: Häng dich nicht an der 1 Millisekunde auf. Ob die exakt
stimmt oder nicht, oder ob du dich eher im Bereich von 2 Millisekunden
oder 0.5 Millisekunden bewegst, spielt überhaupt keine Rolle.
> Was hat es noch genau mit dem (uint8_t) auf sich.> Wenn ich das (uint8_t) weglasse müsste es doch auch funktionieren??
probiers aus.
Manche Compiler warnen dann:
"Zuweisung einer Floating Point Zahl an einen Integer. Die Kommastellen
gehen verloren, weil sie einfac abgeschnitten werden. Willst du das
wirklich?"
Und die C Art zu sagen "Ja das will ich" besteht darin, den Cast selbst
zu machen.
Wie isst man einen Elefanten? - In kleinen Stücken...
Wenn Dir die Formel zu abstrakt und unverständlich ist, dann zerlege die
Aufgabe in so kleine Stücke, dass Du jeden Teil davon nachvollziehen
kannst.
Du hast:
- eine Taktfrequenz,
- einen maximalen Timer-Zählumfang,
- verschiedene Timer-Vorteiler-Stufen.
- eine Wunschvorstellung für das Intervall zwischen den
Interrupt-Aufrufen
Du suchst:
- den optimalen Timer-Vorteiler,
- den Compare-Wert, der bei dem gewählten Vorteiler das gewünschte
Intervall ergibt.
Rein logisch, ohne jede unverstandene Formel, kann man erstmal schaun,
wie groß das gewünschte Intervall ist, wenn man es micht in
Millisekunden, sondern in CPU-Takten betrachtet. Bei 8 MHz CPU-Takt
entspricht eine Millisekunde (bzw. 1 kHz) 8000 Takte.
Du musst also _alle 8000 CPU-Takte einen Interrupt auslösen._
Nun entscheide Dich für einen der verfügbaren Timer und schau Dir dessen
maximalen Zählumfang an.
Ein 16-Bit-Timer kann bis 65535 zählen, 65536 entspricht 0. Damit
könntest Du die 8000 Takte ohne Probleme abzählen, der Timer kann ohne
Vorteiler arbeiten, also direkt die CPU-Takte zählen. Du setzt dazu den
Compare-Wert auf 8000 und wählst (falls vorhanden) den CTC-Mode (Clear
To Compare-match). Das macht natürlich nur Sinn, wenn der Timer keine
anderen Aufgaben zu erledigen hat.
Willst Du den 1ms-Takt aber mit einem 8-Bit-Timer erzeugen, dann hast Du
nur einen Zählumfang von 256 (0 bis 255, 256=0). Damit kannst Du erstmal
keine 8000 Takte abzählen... Da war doch aber noch der Vorteiler, mit
dem man den Timer langsamer laufen lassen kann. Um 8000 CPU-Takte zählen
zu können, bräuchte man einen Vorteiler von 31,25 (8000 durch 256 =
31,25). Den gibt es aber nicht, der nächste verfügbare Vorteiler ist 64.
Teilt man die 8000 Takte durch den Vorteilerwert von 64, dann erhält man
125, das bedeutet, dass der Timer, der ja nur bei jedem 64. CPU-Takt
einen Schritt weiter zählt, _nach jeweils 125 Takten einen Interrupt
auslösen soll_. Hierzu bietet sich (bei neueren AVRs) einer der
Compare-Interrupts an, der oftmals auch mit CTC-Mode ausgestattet ist.
Die Formel macht übrigens nichts weiter, als all diese Zusammenhänge
zusammenzufassen und den Compare-Wert vom Preprozessor ausrechnen zu
lassen. Dies ist eine immense Arbeitserleichterung für den, der die
Zusammenhänge mit links und 40 Fieber beherrscht.
Für den Neuling besteht allerdings bei unbekümmerter Benutzung der
Formel die Gefahr, dass der (im Hintergrund) ermittelte Wert nicht mehr
in den Wertebereich der Ergebnisvariable (hier Byte) passt und dadurch
(oft unbemerkt) unsinnige Werte entstehen. Das könnte einerseits sein,
dass der Wert zu groß wird und die oberen Bits abgeschnitten werden,
andererseits kann der Wert zu klein werden, wodurch die Nachkommastellen
abgeschnitten werden und der Wert ganz schön daneben liegt. Dazu kommt
noch die Tatsache, dass nicht jeder Preprozessor mit Fließkommazahlen
rechnet. Der Preprozessor des AVR-Studios (bei ASM-Programmierung)
rechnet meines Wissens nach z.B. nur mit 32-Bit-Ganzzahlen (long
integer), da würde die Formel wohl nicht das gewünschte Ergebnis
liefern.
Hallo nochmals!
Leider schnall ich es noch nicht ganz bzw. mein Drehgebertest
funktioniert nur so halb: Wenn ich meinen Motor drehe (der mit zwei
Hallsensoren und Magnetscheibe ausgestattet ist) werden die Schritte in
eine Richtung inkrementiert(wobei noch Schritte verloren gehen), in die
andere Richtung werden die Schritte zwar dekrementiert allerdings um
einen viel viel größeren Faktor. (unbrauchbar)
Die Umwandlung von gray nach binär ist mir klar.
Gray-Code Binär-Code
00 00 (0) o.k.
01 01 (1) o.k.
11 10 (2) ändern über XOR
10 11 (3) ändern über XOR
Dann wird nachgesehen ob es eine Änderung der Pegel an den Hallsensoren
gegeben hat.
Die if-funktion wird nur erreicht wenn die Differenz 1 ist sprich kein
Schritt verloren gegangen ist.
1
ISR(TIMER0_COMP_vect)// 1ms for manual movement
2
{
3
int8_tnew,diff;
4
5
new=0;
6
if(PHASE_A)
7
new=3;
8
if(PHASE_B)
9
new^=1;// convert gray to binary
10
diff=last-new;// difference last - new
11
if(diff&1){// bit 0 = value (1)
12
last=new;// store new as next last
13
enc_delta+=(diff&2)-1;// bit 1 = direction (+/-)
14
}
15
}
Könnt ihr versuchen mir diesen Teil:
1
diff=last-new;// difference last - new
2
if(diff&1){// bit 0 = value (1)
3
last=new;// store new as next last
4
enc_delta+=(diff&2)-1;// bit 1 = direction (+/-)
auf eure Art zu erklären?
Bedeutet "read two step encoders" eigentlich schon, dass man diese
Funktion nimmt wenn man zwei Hallsensoren an einer Magnetscheibe zur
Auswertung hat, oder?
Was wird hier noch aus eurer Sicht gemacht?