Forum: Mikrocontroller und Digitale Elektronik Frage zu Compare Timer des ATTiny 25


von Nn N. (jaytharevo)


Lesenswert?

Hallo,
also es geht um folgendes:

Ich bin gerade dabei mich in die Ansteuerung von Servos ein zu arbeiten.
Bis jetzt ist mir, das auch ganz klar so weit. Ich hänge ein wenig beim 
Erzeugen des Impules (H-Pegel für 1-2ms und nach 20ms wieder von vorn). 
Mit der Delay.h lib. hab ich das auch schon stümperhaft geschafft, 
sodass der Servo von einem Endpunkt zum anderen fährt. Ich will, das 
natürlich ordentlich mit dem Timer programmieren.
Nun zu meiner Frage: Kann ich den Wert des Timer1/Counter Registers 
meines Controllers mit 2 seperaten Werten vergleichen? Zum Beispiel mit 
OCR1A u. OCR1C? OCR1A würde ich für den kurzen Impuls vorbereiten und 
OCR1C für die 20ms, danach sollte ein CTC (Clear Timer on Compare Match) 
ausgelöst werden. Ist sowas überhaupt möglich mit einem 8 Bit Timer? Ich 
glaube schon, ich bezahle, dass halt mit geringerer Auflösung oder?
Also Controller verwende ich einen ATTiny25, hab zZ leider nichts 
anderes hier.

Danke im voraus!

MfG,
Julian :)

von Julian (Gast)


Lesenswert?

Ist, das wirklich so eine schwere Frage?

von Karl H. (kbuchegg)


Lesenswert?

Julian Schild schrieb:

> OCR1A u. OCR1C? OCR1A würde ich für den kurzen Impuls vorbereiten und
> OCR1C für die 20ms

Vergiss die 20ms.
Die sind nicht wichtig.

Wichtig ist der 1-2ms Impuls.

D.h. du stellst deinen Timer so ein, dass du den vernünftig erzeugen 
kannst. Was immer dir dann noch als Pause übrig bleibt, ist dem Servo 
recht. Ob das 10ms, oder 15 oder 20 oder 25 sind, spielt keine Rolle.

von Nn N. (jaytharevo)


Lesenswert?

Danke für deine Antwort! :)

Naja zur Zeit arbeite ich mich ja nur in die Ansteuerung ein und da 
lauft nicht wirklich was anderes, deswegen hab ich grundsätzlich gefragt 
ob, dass möglich ist. Scheint wohl nicht so zu sein. Danke :).


MfG Julian

von Karl H. (kbuchegg)


Lesenswert?

Julian Schild schrieb:
> Danke für deine Antwort! :)
>
> Naja zur Zeit arbeite ich mich ja nur in die Ansteuerung ein und da
> lauft nicht wirklich was anderes, deswegen hab ich grundsätzlich gefragt
> ob, dass möglich ist. Scheint wohl nicht so zu sein. Danke :).

Was dein Timer kann und was er nicht kann, steht im Datenblatt.
Bitte gewöhn dich daran, derartige Dinge immer zuallererst mit dem 
Datenblatt abzugleichen.
Wenn der Timer derartige viele Compare-Register hat, kannst du davon 
ausgehen, dass du auch alle gemeinsam benutzen kannst.

von Nn N. (jaytharevo)


Angehängte Dateien:

Lesenswert?

Zu meiner Verteidigung muss ich sagen, dass ich sehr wohl das Datenblatt 
studiert habe, aber du hast ja recht. Mal schauen ob ich den Auszug 
finde.


Ich habe auch ein kleines "Furz Programm" geschrieben. Hier wird ein 
Servo angesteuert. Die Position wird mit einem externen Poti bestimmt. 
Komisch, dass ich nur 2 Position anfahren kann. Liegt es an der 
Auflösung der 8 Bit welche bei einem Prescaler von 128 @1MHz 1ms 
darstellen?

Danke!

MfG

von Nn N. (jaytharevo)


Lesenswert?

Also ich habs gefunden. Hab dort heute zwar schon mal gelesen, aber nach 
X- Seiten liest man wohl nicht mehr sehr aufmerksam^^.

Es ist, wie vermutet der PWM Mode. Hier werden natürlich zwei 
Vergleichswerte gebraucht.
1
When the counter value match the contents of OCR1A or OCR1B, the OC1A and OC1B outputs
2
are set or cleared according to the COM1A1/COM1A0 or COM1B1/COM1B0 bits in the
3
Timer/Counter1 Control Register A - TCCR1, as shown in Table 12-1.
4
Timer/Counter1 acts as an up-counter, counting from $00 up to the value specified in the output
5
compare register OCR1C, and starting from $00 up again. A compare match with OC1C will set
6
an overflow interrupt flag (TOV1) after a synchronization delay following the compare event.

Compare Mode werde ich wohl den dritten nehmen. Ich will nur auf einem 
Port das Singal haben.

 COM1x1   COM1x0

   1        0     OC1x cleared on compare match. Set when TCNT1 = $00.
                  OC1x not connected.

von Nn N. (jaytharevo)


Angehängte Dateien:

Lesenswert?

Hab jetzt auch meinen Fehler gefunden. Die Datenrichtung für den ADU war 
auf Ausgang und nicht Eingang^^. Somit funktioniert jetzt alles wie es 
soll und ich hab ca. 8 Positionen die ich anfahren kann. Freu mich schon 
auf einen neuen Controller mit weitaus höherer Auflösung ;)! 16- Bit 
Timer FTW!!!

Im Anhang das Programm. Falls es wen interessiert^^.

MfG,
Julian

Danke an Karl der mich wieder mal "gerettet" hat.

von Karl H. (kbuchegg)


Lesenswert?

Julian Schild schrieb:
> Hab jetzt auch meinen Fehler gefunden. Die Datenrichtung für den ADU war
> auf Ausgang und nicht Eingang^^. Somit funktioniert jetzt alles wie es
> soll und ich hab ca. 8 Positionen die ich anfahren kann. Freu mich schon
> auf einen neuen Controller mit weitaus höherer Auflösung ;)!

Ich habs jetzt nicht durchgerechnet und ich weiß auch nicht auswendig, 
welche anderen Prescaler der Tiny25 kann. Aber mit einem anderen 
Vorteiler müsste doch da auch mehr gehen.
Hats du zb keinen Vorteiler 64?


IMHO machst du das immer noch zu kompliziert mit der ganzen 'durchlauf' 
Steuerung.
Lass doch den Timer frei durchlaufen.
Bei Timer Overflow setzt du den Ausgangspin auf 1, beim Compare Match 
löscht du den Pin wieder (genau das was auch eine Hardware PWM ganz von 
alleine machen würde)

Das hier
     if (durchlauf == 0)
      {
       OCR1C = 0x07+(ADC_conversion(2)/128);
      }

    if (durchlauf == 1)
      {
       OCR1C = 0x9B-Compare_Wert;
      }

bei dem du nach dem Puls eine Wartepause bis auf die 20ms einlegst, das 
braucht kein Mensch und am allerwenigsten das Servo. Dadurch 
'verschenkst' du aber Servo-Wegauflösung, weil du päpstlicher als der 
Papst sein willst.

Den Timer stellst du so ein, dass du mit Compare Match Werten in der 
Grösenordnung von vielleicht 64 den Servo-Maximalausschlag erreichst. 
Wenn also die 64 einem 2ms Puls entsprechen (deine Zeiten werden ja nach 
Vorteiler variieren) dann bedeutet das, dass er Timer in rund 8ms wieder 
bei 0 angelangt ist. Die Pause nach dem 2ms Puls ist daher nur 6ms 
gross. Da kann man aber sagen: Und? Wer stört sich daran? Das Servo 
sicher nicht. Das Servo interessiert sich nur für die Pulslänge und 
nicht dafür wie lange die Pause danach ist.
Die oft gelesenen 20ms haben eine ganz andere Ursache, die nicht in den 
Servos begründet liegt. Selbst wenn du das Servo an eine Fernsteuerung 
anschliesst, wird das Servo da keine 20ms sehen, sondern ja nach 
Fernsteuerung andere Zeiten. Ein Servo darf(!) diese Pausenzeit gar 
nicht auswerten, sonst käme es in Teufels Küche.

von Hannes L. (hannes)


Lesenswert?

Tiny25, 1 MHz interner Takt (8 MHZ mit Systemtakt-Vorteiler 1 zu 8).
Timer-Vorteiler 1 zu 8 ergibt 8 µs je Timerschritt.
Das macht bei max. Timerstand von 255 2040 µs, also 2,04 ms.
Die gewünschten 1 bis 2 ms erreicht man mit Werten von 125 bis 250.

Timer frei durchlaufen lassen.
Im Compare-Interrupt:
- Index (wird auch für Pausendauer missbraucht) erhöhen, mit 16
  AND-verknüpfen
- Bitmuster für Servopins aus Array (Index) holen und an Port ausgeben
- Intervall aus Array (Index) holen, Compare-Register auslesen,
  Intervall drauf addieren, ins Compare-Register zurück schreiben

Im Hauptprogramm:
- Kanalwerte ermitteln (ADC, serielle Kommunikation, ...) und in Array
  eintragen

In der Init:
- Array für Bitmuster Portpins mit 16 Elementen einrichten
- Array für Kanalimpulsbreiten mit 16 Elementen einrichten
- nicht benötigte Array-Zellen mit Dummy-Werten füllen, für die
  Bitmuster den Wert, bei dem alle Servo-Impulse aus sind, für die
  Kanalimpulsbreiten den Wert für kurz vor Neutralstellung (um die 200),
  so dass sich etwa 20 ms für die 16 Kanäle ergeben.

Der Int klappert als State-Machine 16 Servos reihum durch. Für jedes 
Servo enthalten die Arrays das I/O-Bitmuster und die Impulsdauer. Es 
werden aber nur die ersten x Servos (etwa bis zu 8) benutzt, bei Dir nur 
2. Die restlichen dienen zum Generieren der Impulspause. Die Impulse der 
einzelnen Servos werden (wie beim Funksender/Empfänger) nacheinander 
generiert. Durch das Bitmuster-Array können die Portpins beliebig 
angeordnet sein. Der Timer läuft frei durch und kann dadurch für weitere 
Dinge genutzt werden, z.B. ICP oder Compare auf der anderen 
Compare-Spur. Es wird nur sehr wenig Rechenzeit (in der ISR) gebraucht.

Natürlich kann man die Arrays auch verkleinern (Servoanzahl + 1) und vor 
dem Zugriff den Index prüfen, aber das beansprucht vermutlich mehr 
Rechenzeit in der ISR als das Durchlaufenlassen mit Dummy-Werten.

...

von Nn N. (jaytharevo)


Lesenswert?

Hey KHB!

Wegen dem Code: Hast du meinen letzten Post nicht gesehen? In diesem hab 
ich die "neue" Version angehängt. Keine Durchlauf mehr usw.... :)!

Ja, der Tiny hat für den Timer1 eine sehr große Auswahl für Prescaler, 
64 wäre dabei. Warum ich 64 nicht genommen habe erkläre ich weiter 
unten.

Karl heinz Buchegger schrieb:
> ...weil du päpstlicher als der
> Papst sein willst.

Hehe, hab lachen müssen ;)!

> Den Timer stellst du so ein, dass du mit Compare Match Werten in der
> Grösenordnung von vielleicht 64 den Servo-Maximalausschlag erreichst.
> Wenn also die 64 einem 2ms Puls entsprechen (deine Zeiten werden ja nach
> Vorteiler variieren) dann bedeutet das, dass er Timer in rund 8ms wieder
> bei 0 angelangt ist.

Den Absatz verstehe ich nicht ganz. Meinst du das ich es so anstellen 
soll, dass ich 64 Positionen zum anfahren habe? Wenn ja, wäre das du mit 
einem anderen Prescaler möglich. Wie du ja beschrieben hast. Aber wie 
kommst du auf die 8ms?

> Die Pause nach dem 2ms Puls ist daher nur 6ms
> gross. Da kann man aber sagen: Und? Wer stört sich daran? Das Servo
> sicher nicht. Das Servo interessiert sich nur für die Pulslänge und
> nicht dafür wie lange die Pause danach ist.

Ich widerspreche dir nur ungern, aber ist es nicht so, dass manche 
Servos bei zu kurzen Pause ebenfalls Probleme bekommen? Komme ja 
eigentlich aus dem Modellbau und hab schon oft davon gehört, dass zu 
kurze Zykluszeiten ebenfalls Probleme verursachen können. Ich hab hier 
nur ein popliges Standardservo von Jamara, was weiß ich was das Ding 
kann^^. Ist ja auch nicht so wichtig.

> Die oft gelesenen 20ms haben eine ganz andere Ursache, die nicht in den
> Servos begründet liegt.

Meinst du die Frametime? Ich denke, das rührt daher oder? Für max. 10 
Kanäle (Servos) aber, das ist hier irrelevant.


Also, es ging einfach nur darum, mich in die Materie ein zu arbeiten, 
deswegen bin ich aus so sturr nach Protokoll verfahren (20ms Frametime). 
Das war auch gut so, denn so hab ich wieder einige Dinger gelernt ;). 
Mir ist es auch nicht um eine großartige Auflösung (Positionen) 
gegangen. Ich wollte einfach wissen wie es funktioniert und ob es dann 
funktioniert wie ich mir, das vorstelle :).
Im nächsten Schritt kommt dann auch ein neuer Controller mit nem netten 
16- Bit Timer :). Den Flaschenhals stellt dann das Servo dar ;)!


@Hannes:

Danke für deine Antwort. Ich nehme mal an, dass ich es so programmieren 
sollte wenn ich mehr Servos ansteuern muss. Ist auch sehr clever 
durchdacht und für mich logisch. Was ich aber noch nicht verstehe ist, 
was du mit dem I/O Bitmuster meinst. Du schreibst ja:

>Für jedes Servo enthalten die Arrays das I/O-Bitmuster und die Impulsdauer.

Also kanns nicht die Impulsdauer sein^^.


Danke so weit!

MfG,
Julian

von Karl H. (kbuchegg)


Lesenswert?

Julian Schild schrieb:

> Wegen dem Code: Hast du meinen letzten Post nicht gesehen? In diesem hab
> ich die "neue" Version angehängt. Keine Durchlauf mehr usw.... :)!

Oh. Sorry.
Da bin ich ins falsche C-File gerutscht


>> Den Timer stellst du so ein, dass du mit Compare Match Werten in der
>> Grösenordnung von vielleicht 64 den Servo-Maximalausschlag erreichst.
>> Wenn also die 64 einem 2ms Puls entsprechen (deine Zeiten werden ja nach
>> Vorteiler variieren) dann bedeutet das, dass er Timer in rund 8ms wieder
>> bei 0 angelangt ist.
>
> Den Absatz verstehe ich nicht ganz.

Du hattest deinen Timer so eingerichtet, dass du die 20ms erreichen 
kannst. Das war allerdings der Code mit dem durchlauf-Mechanismus

> soll, dass ich 64 Positionen zum anfahren habe?

Du willst deinen Timer so einrichten, dass du Zeiten bis 2ms mit einem 
möglichst hohen Compare Wert erreichen kannst. Das ist im Grunde alles. 
Du kümmerst dich nur darum, den 1-2ms Puls erzeugen zu können. Wie lang 
dann die Pause danach bis zum nächsten Puls ist, interessiert das Servo 
nicht


>> Die Pause nach dem 2ms Puls ist daher nur 6ms
>> gross. Da kann man aber sagen: Und? Wer stört sich daran? Das Servo
>> sicher nicht. Das Servo interessiert sich nur für die Pulslänge und
>> nicht dafür wie lange die Pause danach ist.
>
> Ich widerspreche dir nur ungern, aber ist es nicht so, dass manche
> Servos bei zu kurzen Pause ebenfalls Probleme bekommen?

Das müssen dann aber schon sehr kurze Pausen sein.
Von meinen Servos ist da keines dabei

> nur ein popliges Standardservo von Jamara, was weiß ich was das Ding
> kann^^.

Probiers aus :-)

Für einen kurzen Schnelltest kann man ja

  while( 1 ) {
    Port auf 1
    _delay_ms( 1.5 );
    Port auf 0
    _delay_ms( 15.0 );
  }

und dann gehst du sukzessive mit den 15ms kürzer, bis das Servo nicht 
mehr mitmacht.


> Meinst du die Frametime? Ich denke, das rührt daher oder?

Ich denke wir reden vom selben.
Die 20ms kommen daher, dass auf der eigentlichen Funkstrecke eine kleine 
Pause nach der Übertragung aller Kanäle notwendig ist, damit sich der 
Empfänger auf den Anfang und damit auf den ersten Puls (=Servokanal) 
einklinken kann.


> durchdacht und für mich logisch. Was ich aber noch nicht verstehe ist,
> was du mit dem I/O Bitmuster meinst. Du schreibst ja:
>
>>Für jedes Servo enthalten die Arrays das I/O-Bitmuster und die Impulsdauer.
>
> Also kanns nicht die Impulsdauer sein^^.

Doch :-)

Ich erzeug zb. die Impulse für 12 Servos nach genau so einem Schema. Nur 
hab ich keine Bitmuster für die Ports sondern nur die Impulsdauerzeiten 
im Array.
(Übrigens auf dem Timer 2 eines Mega128. Also einem 8-Bit Timer)
1
#define F_CPU  16000000UL
2
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <util/delay.h>
6
7
#define SERVO_ANZAHL 12
8
9
#define CENTER 30
10
11
volatile uint8_t value[SERVO_ANZAHL] =
12
   { CENTER, CENTER, CENTER,
13
     CENTER, CENTER, CENTER,
14
15
     CENTER, CENTER, CENTER,
16
     CENTER, CENTER, CENTER
17
   };
18
19
ISR (TIMER2_COMP_vect) 
20
{
21
  static uint8_t ServoId = SERVO_ANZAHL;
22
23
  if( ++ServoId >= SERVO_ANZAHL )
24
    ServoId = 0;
25
26
  if( ServoId == 0 ) {
27
    PORTF = 0x01;
28
    PORTC = 0x00;
29
  }
30
  else if( ServoId < 6 )
31
    PORTF <<= 1;
32
  
33
  else if( ServoId == 6 ) {
34
    PORTF = 0x00;
35
    PORTC = 0x01;
36
  }
37
  else
38
    PORTC <<= 1;
39
    
40
  OCR2 = 57 + value[ServoId];
41
}
42
43
44
void InitServo()
45
{
46
  TCCR2 = (1<<WGM21) | (1<<CS22);  //Prescale von 256 und CTC mode
47
  OCR2 = value[0];
48
  TIMSK |= (1<<OCIE2);
49
}
50
51
int main(void)
52
{
53
  uint8_t i;
54
55
  DDRF = 0xFF;
56
  DDRC = 0xFF;
57
58
  PORTF = 0x00;
59
60
//  TCCR0 =(1<<WGM01) |(1<<CS00)|(1<<CS01);
61
//  OCR0=125;
62
//  TIMSK|=(1<<OCIE0);
63
64
  InitServo();
65
66
  sei();
67
68
  _delay_ms( 1000 );
69
70
  // beine grundstellung
71
  value[ 0] = CENTER;
72
  value[ 1] = CENTER;
73
  value[ 2] = CENTER;
74
  value[ 3] = CENTER;
75
  value[ 4] = CENTER;
76
  value[ 5] = CENTER;
77
  value[ 6] = CENTER;
78
  value[ 7] = CENTER;
79
  value[ 8] = CENTER;
80
  value[ 9] = CENTER;
81
  value[10] = CENTER;
82
  value[11] = CENTER;
83
  value[12] = CENTER;
84
85
  _delay_ms( 2000 );
86
87
  ...

von Hannes L. (hannes)


Lesenswert?

> Was ich aber noch nicht verstehe ist,
> was du mit dem I/O Bitmuster meinst.

Meine Version erzeugt die Servoimpulse nacheinander.
Dazu wird einundderselbe Timer-Compare benutzt (z.B. CompareXa).
In diesem wird mit einem Index gearbeitet, den man als laufende 
Servo-Nummer betrachten kann. Dieser Index wird einerseits genutzt, um 
die Impulsdauer der Servos auszuwählen, aber auch, um den I/O-Pins Pegel 
zuzuweisen. Und dazu brauche ich die "Bitmuster". Im Bitmuster steht 
(bitweise) der Zustand aller Pins eines Ports für den jeweiligen Index.

Bei Index 0 ist der Impuls für Servo 1 aktiv, also muss das Bit, andem 
Servo 1 hängt, auf 1 gelegt werden.

Bei Index 1 ist der Impuls für Servo 2 aktiv, Servo 1 aber beendet, also 
muss an Bitposition des Servo 1 eine 0 stehen und bei Servo 2 eine 1.

Du hast nur 2 Servos, also muss ab Index 2 an allen Servobits eine 0 
stehen, um den Impuls des Servo 2 zu beenden. Das Durchklappern der 
Dummy-Servos (Index 2 bis 15) erzeugt die Pause bis zum nächsten 
Telegramm.

Diese Art der Ausgangssteuerung ist sehr schnell und effizient und 
erspart viele Fallabfragen. Sie ermöglicht es, die Servopins beliebig 
anzuordnen. Die Bitmustertabelle (Array) muss dabei nichtmal im RAM 
stehen, die kann man direkt aus dem Flash (Progmem) lesen. Auch bei 
größeren Controllern mit mehr als einem Port ist dieses Verfahren 
anwendbar, indem man mehrere Bytes je Eintrag nutzt (ich denke noch in 
Bits&Bytes, da ich in ASM programmiere).

Ich hoffe, Du verstehst nun, was ich mit "Bitmuster" meine. Die 
Bezeichnung habe ich beim Multiplexing von 
7-Segment-Lichtschachtanzeigen eingeführt (Segment-Bitmuster mit 
Ziffernwert als Index), sie ist bei der Erzeugung mehrerer Servoimpulse 
aber genauso zutreffend.

Ich will Dir nicht vorschreiben, wie Du Deine Servos ansteuern sollst, 
sondern nur Denkanstöße geben.
Meine Version hat sich allerdings in den verschiedensten Varianten 
bereits mehrfach bewährt und sie lässt immer genug Ressourcen 
(Rechenzeit, Timer-Funktionalität) für das eigentliche Programm übrig. 
Sie ist halt nur eine im Hintergrund nebenher laufende Ausgaberoutine, 
vergleichbar mit einer im Hintergrund laufenden Multiplexroutine für 
7-Segment-LED-Anzeigen.

...

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.