Hallo, ich habe einen Atmega16 auf einem STK500 (ohne Quarz, daher Nutzung 1MHz Taktes des µCs) zur Verfügung und möchte 2 Timer verwenden, die in untersch. Intervallen je ihre Interrupt Routine (ISR) aufrufen. 1. Frage: Kann es in einem Programm mehr als zwei ISRs geben? 2. Frage: Ist ein PWM gesteuerter Timer in Kombination mit dem STK500 genauer als einer ohne PWM, wenn wie oben gesagt nur der µC interne 1MHz Takt verwendet werden kann? 3. Frage: Jetzt interessiert mich aber noch, ob ich den "8-Bit Timer2 mit PWM und asynch. Operation" genauso verwenden kann wie den "8-Bit Timer1 mit PWM". Ich würde mich über jede Hilfe freuen, da ich sowohl zum ersten Mal einen Timer als auch einen Interrupt verwenden möchte. twizzler P.S.: Ja, das Datenblatt zum Atmega16 habe ich zur Hand. Ich würde aber nicht fragen, wenn ICH meine Probleme allein damit lösen könnte.
OK, ich glaube mittlerweile verstanden zu haben, dass der PWM Betrieb für meine Anwendung am besten geeignet zu sein scheint. Der 16-Bit Timer erlaubt ebenfalls einen PWM Mode. Dann nehme ich nun diesen, oder? Schließlich benötige ich hier kein Prescaling Faktor und kann daher mein Signal µs genau bestimmen.
was willst du eigentlich genau machen?? du brauchst keine PWM um einen Timer zu nutzen, die PWM (Pulsweitenmodulation) ist lediglich eine Funktion, die der Timer zur verfügung stellt....Datenblatt... Für das was du machen willst reicht ein timer der einen compare match interrupt auslöst.... unter umständen reicht ein timer, wenn er 2 compare register besitzt. und nun zum prescaler (vorteiler) den kannst du einsetzen um längere zeiten verwirklichen zu können... ein Beispiel wenn du einen uC verwendest mit 1MHz Systemtakt 1/1MHz = 1us das heisst ohne prescaler kannst du mit einem 8Bit Timer (0-255) genau 256us messen.... der Prescaler macht also schon sinn.... erzähl mal was du genau machen willst...
PS such dir vieleicht mal n tutorial zum Thema Timer, und Interrupts.... Das hilft dir zu verstehen wie der uC denkt....
@Jan Herzog Den PWM halte ich für sinnvoll, da ich ein Servo ansteuern möchte. Und mit dem PWM könnte man ein Rechteck Signal generieren. Und ich denke, dass ich die Länge des Rechtecks in der main beeinflussen kann, oder? Den Prescaler brauche ich beim 16 Bit Timer deshalb nicht, weil meine längste Zeitdauer 20ms - s. Servo - nicht überschreitet, oder?
@Jan Herzog Die Interrupts habe ich mir für die erste Variante mit den zwei Timern als scheinbar sinnvoll ausgedacht. Der erste Timer sollte das High Level Signal an einem beliebigen Portpin einschalten und der zweite Timer sollte für die verlbeibende Zeitdauer der insg. 20ms den Pin wieder auf Low setzen. Benötigt für solche Dinge keinen Interrupt? Erübrigt sich die Interrupt Sache, wenn ich einen PWM verwende?
welche Variante, würdet ihr mir empfehlen? Die zwei-Timer-Variante oder PWM-Variante?
dafür brauchst du definitiv nicht 2 timer. wenn du eine periodendauer von 20ms hast und dein high zwischen 0 und diesen 20ms variiren können soll, nimmst du einen timer den du so einstellst, dass die periodendauer eben genau 20ms ist, und benutzt im im compare match mode (oder so ähnlich). dann stellste die ganze geschichte noch so ein das der output compare pin passend getoggelt wird, und du musst in deiner main nur das compare register passend nachladen
Du kannst nur mit Timer 1 arbeiten. Timer ohne Vorteiler als PWM mit TOP-Wert in ICR1 (hier 20000 -> 20 ms) und OCRA und B als PWM-Wert (sinvoll sind beim Servo 1000-2000 -> 1-2 ms) Pin wird gesetzt PD4 für OCRB PD5 für OCRA. Mit der Vorlage sollte es gehen.
1 | //ICC-AVR application builder : 17.05.2009 22:15:11
|
2 | // Target : M16
|
3 | // Crystal: 1.0000Mhz
|
4 | |
5 | #include <iom16v.h> |
6 | #include <macros.h> |
7 | |
8 | //Signal bit definitions
|
9 | #define PWM_B 4 //PD4
|
10 | #define PWM_A 5 //PD5
|
11 | |
12 | void port_init(void) |
13 | {
|
14 | PORTA = 0x00; |
15 | DDRA = 0x00; |
16 | PORTB = 0x00; |
17 | DDRB = 0x00; |
18 | PORTC = 0x00; //m103 output only |
19 | DDRC = 0x00; |
20 | PORTD = 0x00; |
21 | DDRD = 0x30; |
22 | }
|
23 | |
24 | //TIMER1 initialize - prescale:1
|
25 | // WGM: 14) PWM fast, TOP=ICRn
|
26 | // desired value: 20mSec
|
27 | // actual value: 20,000mSec (0,0%)
|
28 | void timer1_init(void) |
29 | {
|
30 | TCCR1B = 0x00; //stop |
31 | TCNT1H = 0xB1; //setup |
32 | TCNT1L = 0xE1; |
33 | OCR1AH = 0x4E; |
34 | OCR1AL = 0x1F; |
35 | OCR1BH = 0x4E; |
36 | OCR1BL = 0x1F; |
37 | ICR1H = 0x4E; |
38 | ICR1L = 0x1F; |
39 | TCCR1A = 0xF2; |
40 | TCCR1B = 0x19; //start Timer |
41 | }
|
42 | |
43 | //call this routine to initialize all peripherals
|
44 | void init_devices(void) |
45 | {
|
46 | //stop errant interrupts until set up
|
47 | CLI(); //disable all interrupts |
48 | port_init(); |
49 | timer1_init(); |
50 | |
51 | MCUCR = 0x00; |
52 | GICR = 0x00; |
53 | TIMSK = 0x00; //timer interrupt sources |
54 | SEI(); //re-enable interrupts |
55 | //all peripherals are now initialized
|
56 | }
|
57 | |
58 | //
|
59 | void main(void) |
60 | {
|
61 | init_devices(); |
62 | //insert your functional code here...
|
63 | }
|
In der main brauchst du nur die PWM-Werte setzen. Es gibt jedoch schönere Verwendungen für einen 16 Bit-Timer. gruß avr
wie wärs mit prescaler und nem 8 bit timer?? reichen 8 bit Auflösung?
Hi >TCCR1B = 0x00; //stop > TCNT1H = 0xB1; //setup > TCNT1L = 0xE1; > OCR1AH = 0x4E; > OCR1AL = 0x1F; > OCR1BH = 0x4E; > OCR1BL = 0x1F; > ICR1H = 0x4E; > ICR1L = 0x1F; > TCCR1A = 0xF2; > TCCR1B = 0x19; //start Timer Zumindest mal ein Beispiel, wie man es nicht macht. MfG Spess
Eigentlich ja, aber: Signal 1 ms immer ein, 0-1 ms ein, 18-19 ms aus = 20 ms Die Info ist die Dauer des Impulses (1-2 ms) bzw. die Dauer (0-1 ms) nach dem Grundsignal (1 ms). Die 18-19 ms danach sind uninteresant (aber wichtig). Wenn ich die 20 ms mit 8 Bit auflöse erhalte ich 78 µs als Einheit. Der Bereich 0-1 ms der die Info enthält hat dann nur noch 13 Stufen. Bei einem Servo mit 180° sollten es aber deutlich mehr sein. Man muß also Aufteilen in Signal und Verzögerung. Daher ist der Weg oben mit den 16 Bit eigentlich sehr genau und einfach. Für die anderen Wege gibt es hier im Forum viele Beispiele (Suchfunktion!). gruß hans
Ich sehe den letzten Beitrag von avr als schlüssig an. Zudem reichen mir die 8-Bit tatsächlich nicht. @Spess: Was meinst du mit "So sollte man es NICHT machen"?
Hi
>Was meinst du mit "So sollte man es NICHT machen"?
Es gibt Bitdefinitionen, die man benutzen sollte:
TCCR1B = 1<<CS12|1<<CS11 ist wesentlich aussagekräftiger als
TCCR1B= 0x6
Hat nichts mit falsch oder richtig zu tun.
MfG Spess
@avr ich habe gerade deinen Code kompilieren wollen und festgestellt, dass ich die macros.h nicht habe. Ich habe CLI() durch cli() und SEI durhc sei() ersetzt und die macros.h durch die interrupt.h ersetzt. Ist das auch OK? Kompilieren ist so jedenfalls möglich. Wofür sind die defines? #define PWM_B 4 //PD4 #define PWM_A 5 //PD5 Muss ich den Servo wirklich an PD4 und PD5 anschließen? Ich vermute der PWM_B Wert ist für das variable 0...1ms Signal gedacht. Wie setze ich PWM_B beispielsweise auf 0,5ms; etwa so? PWM_B = 500; LG twizzler
twizzler schrieb: > Wofür sind die defines? > > #define PWM_B 4 //PD4 > #define PWM_A 5 //PD5 > > Muss ich den Servo wirklich an PD4 und PD5 anschließen? > > Ich vermute der PWM_B Wert ist für das variable 0...1ms Signal gedacht. > Wie setze ich PWM_B beispielsweise auf 0,5ms; etwa so? PWM_B = 500; Nein. Du weist einen Wert an das OCR1A oder OCR1B Register zu. Für weitere Details verweise ich auf das Datenblatt zu deinem Prozessor. Im Kapitel über den Timer 1 findest du auch eine ausführliche Beschreibung zur PWM, welche Register beteiligt sind und wo die Signale am µC herausgeführt sind. > void timer1_init(void) > { > TCCR1B = 0x00; //stop > TCNT1H = 0xB1; //setup > TCNT1L = 0xE1; > OCR1AH = 0x4E; > OCR1AL = 0x1F; > OCR1BH = 0x4E; > OCR1BL = 0x1F; > ICR1H = 0x4E; > ICR1L = 0x1F; > TCCR1A = 0xF2; > TCCR1B = 0x19; //start Timer > } Na denn, viel Spass beim Aufdröseln in die einzelnen Bits und identifizieren mit dem Datenblatt, was da eigentlich alles eingestellt und freigeschaltet ist. Man kann das ganze auch so schreiben void timer1_init(void) { TCCR1B = 0x00; //stop TCNT1 = 0xB1E1; //setup OCR1A = 0x4E1F; OCR1B = 0x4E1F; ICR1 = 0x4E1F; TCCR1A = 0xF2; TCCR1B = 0x19; //start Timer } Das hat dann den Vorteil, dass sich der Programmierer nicht um die korrekte Beschreibung eines 16 Bit Registers kümmern muss, sonder der Compiler sich um die richtige Reihenfolge kümmert (erst High, dann Low). Das Aufdröseln der Bits in TCCR1A und TCCR1B hingegen überlass ich dem Leser :-) > Die 18-19 ms danach sind uninteresant (aber wichtig). Nicht wirklich. Den meisten Servos ist das völlig egal, ob da eine 18 ms Pause hinten nach kommt oder nicht. Servos interessieren sich sowieso nur für die Länge des Pulses. Die Wiederholzeit von 20ms kommt ja nicht aus einem Servoproblem an sich, sondern ist entstanden, weil man einige Servopositionen hintereinander über die Funkstrecke schicken muss und trotzdem noch erkennen können muss, welcher Puls zu Servo 1 gehört. Am Funkweg sind einfach nur alle Puls hintereinander angeordnet. Jeder Puls kann maximal 2 ms lang sein, so dass man bei 8 Servos dafür maximal 16 ms (plus ein paar Zerquetschte) für die 8 Pulse brauchte. Dann noch eine etwas längere Pause (für den Frameanfang) und fertig war der Übertragungsframe. Der Empfänger pfriemelt da gar nicht lange rum, sondern lässt einfach einen Zähler mitlaufen. Mit jedem Pulsende wird der nächste Puls auf den nächsten Ausgang durchgeschaltet, bei einer längeren Pause wird der Zähler wieder auf 0 gesetzt. Und so kommt es, dass an einem Servoausgang am Empfänger nur alle 20ms ein Puls auftaucht. Aber wie gesagt: Analogservos ist diese Zeitdauer ziemlich egal. Lediglich Digitalservos werten diese Wiederholrate manchmal aus um zu erkennen, ob sie vielleicht einem Störsignal nachlaufen, oder ob die Pulslänge tatsächlich von einem Sender stammen kann.
@Karl Heinz Buchegger Jetzt bin ich wohl der entdröselmeister! ;-) void timer1_init(void) { TCCR1B = 0x00; //stop TCNT1 = 0xB1E1; //setup, 45537 (?) OCR1A = 0x4E1F; //19999 OCR1B = 0x4E1F; //19999 ICR1 = 0x4E1F; //19999 TCCR1A = 1<<COM1A1|1<<COM1A0|0<<COM1B1|0<<COM1B0|0<<FOC1A|0<<FOC1B|1<<WGM11|0<<WG M10; TCCR1B = 0<<ICNC1|0<<ICES1|1<<WGM13|1<<WGM12|0<<CS12|0<<CS11|1<<CS10; //start Timer } Kompilieren funktioniert schon mal. Danke übrigens für deine Antwort bezüglich der PWM-Wert Änderung. Weißt du zufällig auch warum die o. angefragten #defines dann noch wichtig sind, obwohl sie nun im Code nicht mehr vorkommen. Auch die Antwort auf die Frage, ob die Servos wirklich an beide Pins PD4 u. 5 anzuschließen sind, würde mich noch sehr interessieren. Das war es erstmal an weiteren Fragen.
Scheinbar habe ich da irgendwelche Verständnisschwierigkeiten. Nach dem Datenblatt sieht es so als entspräche PD4 oder PD5 dem OCR1A oder OCR1B Signal. Wobei sich letztere scheinbar nur dadurch unterscheiden, dass sie invers zueinander sind. So entschied ich mich zunächst für PD4 als Steuerleitung für mein Servo. Allerdings tat sich da nichts. Wenn ich eine LED über PD4 ansteuere leuchtet diese aber permanent bzw. mit einem 20ms Takt. Habe leider kein Oszilloskop zur Hand. Warum zappelt mein Servo nicht? Und wieso beträgt der TCNT Wert 45537 anstatt 20000? Müssen die Datenblätter so verteufelt undurchsichtig sein? Bitte gebt mir einen weiteren Hinweis!
twizzler schrieb: > Scheinbar habe ich da irgendwelche Verständnisschwierigkeiten. Nach dem > Datenblatt sieht es so als entspräche PD4 oder PD5 dem OCR1A oder OCR1B > Signal. Bingo > Wobei sich letztere scheinbar nur dadurch unterscheiden, dass > sie invers zueinander sind. Das sind 2 voneinander unabhängige PWM Einheiten (ok, sie teilen sich den TOP Wert, da sie beide am gleichen Timer beheimatet sind). Ich habe jetzt die Konfigurationsbits nicht studiert. Kann natürlich sein, dass die so eingestellt sind, dass an der einen das inverse der anderen raus kommt. > Warum zappelt mein Servo nicht? Und wieso beträgt der TCNT Wert 45537 > anstatt 20000? Als erstes musst du mal ergründen, welcher PWM Modus eingestellt ist. Was ist der TOP Wert (wie weit zählt der Timer). Wie schnell taktet dein µC? Davon ausgehend musst du zurückrechnen, welcher Wertebereich in OCR1A (bzw. OCR1B) dem Bereich 1 bis 2 ms entspricht.
OK. WGM13 ist gesetzt, WGM12 und WGM11. Das ist MOdus 14. In diesem Modus zählt der Timer von 0 bis ICR1. Ist ICR1 erreicht, wird der Timer auf 0 gesetzt und es geht wieder von vorne los. Das vorladen des TCNT1 kannst du ignorieren. Das ist nur beim ersten Durchlauf interessant und man hätte TCNT1 in diesem konkreten Fall genausogut einfach auf 0 setzen können. Wird dann der Overflow erreicht zählt der Timer immer von 0 bis 19999. (65536 - 19999 = 45537)
Mit dieser Belegung: TCCR1A = 1<<COM1A1|1<<COM1A0|0<<COM1B1|0<<COM1B0|0<<FOC1A|0<<FOC1B|1<<WGM11|0<<WG M10; sollte sich eigentlich am B-Kanal gar nichts tun. COM1A1 und COM1A0 sind so geschaltet, dass "Set OC1A on compare match, clear OC1A at BOTTOM" d.h Der Timer zählt fröhlich vor sich hin. Immer von 0 bis 19999 (weil in ICR1 19999 drinnen steht). Wenn der Timerwert (in TCNT1) gleich OCR1A ist, dann wird der Ausgang OC1A auf high geschaltet. Erreicht dre Timer die 19999, wird er im nächsten Takt zu 0 und gleichzeitig wird der Ausgang OC1A wieder zu 0. Wenn du also einen 1 ms Puls haben willst, musst du den OCR1A Wert so einstellen, dass der Timer noch 1 ms braucht, ehe er 19999 erreicht. Wenn 19999 tatsächlich 20 ms entsprechen, dann sind 1ms ein Wert von 1000. Du musst OCR1A also auf 18999 stellen. Denn dann braucht der Timer noch genau 1ms bis der TOP Wert 19999 erreicht wird. Alternativ kannst du die ganze Schose natürlich umstellen, sodass der OC1A Pin bei einem Timerwert von 0 auf 1 geht und bei erreichen des Comparewertes wieder auf 0 fällt. Das wär dann COM1A1 auf 1, COM1A0 auf 0 OCR1A auf 1500 ergibt dann einen 1.5 ms Puls, der sich alle 20 ms wiederholt. Alles bei 1 Mhz gerechnet (ich hab jetzt die Zahlen von avr nicht nchgerechnet)
@Karl Heinz Buchegger Vielen Dank für deine sehr ausführlichen Hinweise. Hat mir sehr weitergeholfen. Ich habe deinen letzten Tip befolgt und die Schose umgedreht, d. h. COM1A1 auf 1, COM1A0 auf 0 Für's erste komme ich soweit zurecht. Vielen Dank für jede Hilfe von Euch allen!!!
Ich habe zunächst eine ganz einfache Routine geschrieben: Servo ganz links, dann 5 Sek. später ganz rechts, dann 5 Sek. später wieder ganz links usw. Häufig brummt oder vibriert der Servo beim Verharren an einer Position oder beim Rüberfahren zur nächsten Position. Manchmal erfolgt das Verharren oder Rüberfahren auch ohne brummen. Ich kann da keine Regelmäßigkeit feststellen. Ich habe bisher zwei verschiedenen Servos unterschiedlicher Bauart getestet. Das Verhalten ist bei beiden identisch. Kennt das jemand von Euch?
twizzler schrieb:
> Kennt das jemand von Euch?
Ja.
Das kommt auch bei 'normaler' Benutzung von Servos mit einer
Fernsteuerung vor. Im Servo können billige Potis verbaut sein, bei denen
der Schleifer ein klein wenig hakt und das Poti auf kleine Stellversuche
nicht gleich reagiert und sich der Widerstandswert in kleinen Sprüngen
ändert. Die Folge ist, dass die Servoelektronik ständig am Nachregeln
ist. Es ist nicht unüblich, dass ein Servo an einer Position knurrt und
an einer Position gleich daneben völlig ruhig ist. Auch ein
schwergängiger Kraftabtrieb vom Servo (Bowdenzüge die schwergängig
verlegt sind) kann dieses Phänomen verursachen. Oder ein Servo welches
über die Endstellung gefahren ist sodass die interne Überdrehsicherung
im Getriebe (ein Zapfen) an den Endpositionen in seiner Nut ansteht.
Das ist ja blöd. Dann habe ich für 35 € einen "Billigservo" (Segelwindenservo) erstanden! Ich werde mal größere Schrittweiten testen. Wenn aber alles nichts hilft, kann ich nur hoffen, dass er trotz der großen Regelanstrengung 48 Stunden am Stück durchhält. Danke für die Antwort!
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.