Forum: Mikrocontroller und Digitale Elektronik Tiny25 Problem mit simpler Rechnung


von Gast (Gast)


Lesenswert?

Hallo,

ich bastele grad an einer kleinen Servoansteuerung mit den zwei 
8-Bit-Timern des Tiny25.
1
volatile int iDeg = 200;
2
3
ISR(TIMER0_COMPA_vect) {
4
  
5
  PORTB = 0b00000011;
6
7
  if(iDeg == 99) iDeg = 200;
8
9
  TCCR1 = 0b10010111; //CTC an & Prescaler 64
10
  OCR1A = 8000000 / ((iDeg * 5) * 64); 
11
12
  while(TCNT1 != OCR1A) { asm volatile ("nop"); } 
13
 /* Nichts tun und warten, bis TCNT1 == OCR1A, 
14
 da Port dann toggelt. Dann weiter und Timer 
15
 erstmal deaktivieren */
16
  
17
  TCCR1 = 0x00; // Timer deaktivieren
18
19
  iDeg--;
20
}

Eigentlich gehts mir um die Zeile
1
OCR1A = 8000000 / ((iDeg * 5) * 64);

Verbalisiert: Die Rechnung ergibt, bei iDeg = 200, im ersten Durchgang 
125. 8000000 (Takt in Hz.) / 64 (Prescaler) / 125 (Wert OCR1A) = 1000Hz, 
also 1ms/Pulslänge. Bei iDeg = 100 sollte 250 im OCR1A stehen, also 2ms 
Pulslänge. Sollte im Prinzip also halbwegs zur Servosteuerung hinhauen.

Mir ist klar, dass solche mathematischen Operationen auf einem AVR wenig 
sinnvoll ist, merkt man auch daran, dass für diese eine Zeile gut 100 
Takte draufgehen, aber was solls. Nur rechnen sollte er halt richtig.
Beim ersten Schleifendurchlauf komme ich, lt. 
AVR-Studio-Simulator-IO-Watch, auf den Wert 168. Eigentlich solltens 
aber 125 sein. Bei nachfolgenden Durchläufen bleibt die Rechnung nach 
wie vor falsch.

In Hardware hab ich das Ganze übrigens noch nicht getestet. Kann mir 
aber irgendwie nicht vorstellen, dass der AVR-Simulator (2) sowas nicht 
hinbekommt.

Danke & Gute Nacht.

von Hmm (Gast)


Lesenswert?

Ich meine hier gleich mehrere Punkte zu sehen die falsch bzw. kritisch 
sind.

Zunächst einmal sollte Code in einer Interrupt-Routine so kurz wie nur 
irgend möglich sein.
Das ganze
1
  
2
  PORTB = 0b00000011;
3
4
  if(iDeg == 99) iDeg = 200;
5
6
  TCCR1 = 0b10010111; //CTC an & Prescaler 64
7
  OCR1A = 8000000 / ((iDeg * 5) * 64); 
8
  iDeg--;
sollte (und kann vorher erledigt werden. Das verkürzt die Zeit im 
Interrupt und vermeidet irgendwelche (esoterisch anmutenden) Effekte.

Dann ist natürlich die Berechnung
1
  OCR1A = 8000000 / ((iDeg * 5) * 64);
wiel aufwendiger geschrieben als nötig und sie benötigt auch mehr 
Variablenplatz als nötig.
In dem Ausdruck kann man die Division von 8.000.000 durch 5 und durch 64 
vorwegnehmen. Das sollte allerdings auch der Comiler schon tun. Wenn 
aber 8.000.000 dastehen soll, dann wenigsten als Long oder Unsigned long 
(U resp. UL). Sonst gibt das Murks.
Dann bleibt immer noch 25.000 / iDeg übrig. Das aber passt nicht mehr in 
eine 8-Bit Variable. Der Zwischenwert wird also in einem Int gebildet, 
das nicht mehr in ein 8-Bit Register zu laden ist. Das sollte mindestens 
eine Warning geben. Irre ich mich oder hast Du die ignoriert?

Soweit ich informiert bin kann man Servos (Modellbauservos) mit eine PWM 
ansteuern. Dazu gibt es hier auch einige Lösungen und Diskussionen.
Das ist der Hauptpunkt. Nutze eine PWM und nicht diese Art von 
Implementierung. Das ist unnötig kompliziert und wenn man noch wenig 
Ahnung hat eher kontraproduktiv, wie Dein Code beweist.

von Peter D. (pdiener) Benutzerseite


Lesenswert?

Das liegt an einem Überlauf einer Puffervariable bei der Berechnung. 
Wenn du diese groß genug (long) wählst, geht es:
OCR1A = 8000000 / (((long)iDeg * 5) * 64);

Abgesehen davon benötigt das jetzt 757 clockcycles.

Das benötigt nur 234 Cycles und macht exakt das gleiche:
OCR1A = 25000 / iDeg;

Und noch viel schneller wäre es mit einer Tabelle, so wie ich das 
verstehe, gibt es ja nur 101 gültige Werte für iDeg.

Grüße,

Peter

von Matthias S. (matthias00)


Lesenswert?

> Zunächst einmal sollte Code in einer Interrupt-Routine so kurz wie nur
> irgend möglich sein.

Danke für den Hinweis, ist aber bekannt.
Ist halt eine recht frühe Version. Wäre vielleicht irgendwann behoben 
worden. Ist hierbei aber nicht wirklich kritisch. Der Tiny soll nichts 
Anderes machen, als ein wenig am Servo rumdrehen.

> Dann bleibt immer noch 25.000 / iDeg übrig. Das aber passt nicht mehr in
> eine 8-Bit Variable.

Das Ergebnis sollte eigentlich schon in eine 8-bit-Variable passen. Die 
25000 aber wohl eher nicht. Stimmt.

> Das sollte mindestens eine Warning geben. Irre ich mich oder hast Du die 
ignoriert

Nein, gibt keine Warnung. Eben nochmal getestet.

> Soweit ich informiert bin kann man Servos (Modellbauservos) mit eine PWM
> ansteuern.

Mag sein dass ich oberflächlich gesucht habe, aber alles was auf 
PWM-basierende, was ich finden konnte, setzt 16-Bit-Timer voraus. Die 
hat mein Tiny25 leider nicht. Der Wiki-Artikel 
(http://www.mikrocontroller.net/articles/Modellbauservo_Ansteuerung) 
machts übrigens, soweit ich das überblickt habe, auch ohne PWM.

> Und noch viel schneller wäre es mit einer Tabelle, so wie ich das
> verstehe, gibt es ja nur 101 gültige Werte für iDeg.

Ja, gut möglich. Darf ausnahmsweise aber mal ignoriert werden. Wie 
gesagt, der Tiny tut nix anderes, als den Servo anzusteuern. Selbst die 
Genaugigkeit spielt hierbei kaum eine Rolle. Hauptsache es bewegt sich 
überhaupt.

von Oliver (Gast)


Lesenswert?

>Der Wiki-Artikel
>(http://www.mikrocontroller.net/articles/Modellbaus...)
>machts übrigens, soweit ich das überblickt habe, auch ohne PWM.

Und warum machst du es dann nicht so wie im Wiki-Artikel?

>Wie
>gesagt, der Tiny tut nix anderes, als den Servo anzusteuern. Selbst die
>Genaugigkeit spielt hierbei kaum eine Rolle. Hauptsache es bewegt sich
>überhaupt.

Mit an Sicherheit grenzender Wahrscheinlichkeit ändert sich dieser Teil 
der Anforderungen an das Programm ziemlich bald :-) Spätestens dann 
brauchst du doch eine eingermassen sinnvolle Softwarearchitektur.

Oliver

von Matthias S. (matthias00)


Lesenswert?

Oliver wrote:
>>Der Wiki-Artikel
>>(http://www.mikrocontroller.net/articles/Modellbaus...)
>>machts übrigens, soweit ich das überblickt habe, auch ohne PWM.
>
> Und warum machst du es dann nicht so wie im Wiki-Artikel?


Weil der mir irgendwie auch nicht allzu optimal vorkommt.
Mal Nachrechnen:

Ein OCR1A-Registerwert von 2375 stellt, lt. Kommentar, die 
Servomittelstellung/einen Impuls von 1,5ms dar.

1
1.000.000 / 8 / 125 = 1000 Hz/Compare Matches/Sek.
 Der erste Compare Match schlängt also nach 1ms auf.
In der ISR wird dann die Differenz aus 20ms (repräsentiert durch den 
Wert 2500 im OCR) und der vorherigen Pulslänge berechnet.
1
1.000.000 / 8 / 2375 = 52,63 Hz/Compare Matches/Sek.
Der zweite Compare-Match nach 0,019s (19ms).
20 ms haben wir dann zusammen. Mittelstellung kann das aber eigentlich 
nicht sein.

(Ich habe jetzt übrigens angenommen, dass die Variable da schon nicht 
ganz optimal initialisiert wurde - wenn wir den ersten Compare Match bei 
2375 erwarten, vergehen bis dahin nämlich 19ms. Der eigentliche 
High-Impuls kommt erst danach. Und lt. Diagramm des Autors liegt der 
Impuls ja zu Anfang der 20ms. Es vergehen also mindestens 19ms, bis 
überhaupt etwas verwertbares am Servo anliegt. Dann lieber gleich 2500 - 
2375 nehmen. Dann kommt der erste Interrupt nämlich nach 1ms. Siehe 
Rechenbsp. oben)

Mit

> Prozessortakt / 8 (1000000Hz / 8 = 250000Hz)

im Fliesstext bin ich irgendwie auch nicht ganz einverstanden.

Versteh ich das Ganze einfach miss? Sollte dem so sein, möchte ich mich 
höflichst beim Autoren entschuldigen.
Ansonsten sollte da mal jemand drüberschauen - trägt irgendwie zur 
Verwirrung bei.

von Matthias S. (matthias00)


Lesenswert?

Fürs EDIT ists leider schon zu spät. Habe den Wiki-Code mal simuliert. 
Ich komme auf 1037µS bis zum Eintritt des ersten OCR-Interrupts. Das 
kann eigentlich nicht Mittelstellung sein. Funktioniert aber nur, wenn 
ich bei der Initialisierung des Registers das obere Byte außen vor 
lasse. Also so:
1
OCR1AL = 125;

Ansonsten lande ich schon nach ein paar µS im Interrupt. Kann ich 
irgendwie nicht nachvollziehen.

Aber meine Vermutung, dass da irgendwas nicht iO ist mit dem Code, 
sollte hinhauen, oder?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.