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 :)
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.
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
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.
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
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.
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.
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.
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. ...
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
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 | ...
|
> 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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.