Hallo Zusammen,
die Timer im STM32 sind sehr komplex und unverständlich,
daher meine, hat schon mal jemand mit einem STM32 eine
Servoansteuerung gemacht?
Danke & Viele Grüße
Bernd
Hallo Zusammen,
habe leider nichts gefunden, bleibt mir garnichts anderes übrig,
als es selbst zu versuchen. Vielleich kann mir ja jemand helfen.
Ich habe gerade ein MiniScript gebaut
Da wird der Interupt leider nicht angesprochen. Kann mir vielleicht
jemand sagen wieso?
(Oder ggf. was die Entwickler sich mit TIM_TimeBaseStructure.TIM_Period
= 0; gedacht haben? In AVR gibts das leider nicht, da läuft zumindest
Servoansteuerung, wenn auch sehr langsam... :))
Wenn jemand etwas weiß, wäre klasse.
Danke & Viele Grüße
Bernd
An der Lib hatte ich kein rechte Freude, weil man sowieso in den
Quellcode reinschauen muss um zu verstehen was der Kram soll. Also kann
ich auch gleich an die Reigister ran, die sind ohnehin besser
dokumentiert.
Ein paar der Parameter ergeben nur bei den erweiterten Timern Sinn.
32MHz / 32KHz gibt 1000, nicht 100.
Du solltest dem Timer den Tipp geben, dass er Interrupts erzeugen soll.
In deinem Fall ist das wohl der Update-Interrupt.
Du solltest dem NVIC dann noch den Tipp geben, dass er die auch
durchlassen soll und eine passable Priorität mitteilen.
Wie wird ein Servo angesteuert? Vor langer Zeit was das mal ein
PWM-Signal, mit Tastverhältnis=Position. Das jedenfalls ist kein
Problem, wobei dafür kein Interrupt verwendet werden sollte, sondern
eben PWM.
Der Timer wird übrigens ohne Takt nicht funktionieren. Von Haus aus ist
beim STM32 alle Peripherie abgeschaltet (=>RCC).
Und ist der Takt wirklich 32MHz? Anfangs kommt der Bursche nämlich mit
dem internen R/C-Oszillator hoch und du darfst erst einmal den Takt
konfigurieren.
Vielleicht solltest du mal in Beispielcode reinschauen. Ein STM32 ist
ein klitzekleines Bischen komplexer als ein AVR. Einfach loslegen ist
nicht drin, das haben Controller dieser Grössenordnung alle so an sich.
Hallo A.K,
vielen Dank. Servoansteuerung läuft bereits auf dem AVR (nur eben
viel zu langsam - ruckelte ziehmlich - seidenweich ist anders). PWM
hatte
ich auch schon gedacht, nur ist die Frage ob das auf 6 Kanälen
gleichzeitig geht. (???)
Für die Ausgabe von 6 ServoImpulsen z.B.
1
servo_out[0]=1500;// PA8
2
servo_out[1]=2000;// PA11
3
servo_out[2]=2500;// PA12
4
servo_out[3]=3000;// PA13
5
servo_out[4]=3500;// PA14
6
servo_out[5]=4000;// PA15
bräuchte man
- Einen Timer mit Output Compare
- PA8 wird eingeschaltet
- Compare = servo_out[i];
- Wenn der Compare abgelaufen ist, wird PA8 ausgeschaltet.
- Der Timer wird alle 21ms (jedenfalls > 21ms) neu gestartet (?) und
Pin wird erhöht (nächster PA11)
Müßte dann ergeben (ein Zeichen (|,.,_) entspricht 500ms)
Bernd schrieb:
> ich auch schon gedacht, nur ist die Frage ob das auf 6 Kanälen> gleichzeitig geht. (???)
Bei dieser Art von Problembeschreibung brauchst du jemanden, der sowohl
STM32 kennt als auch schon ganz genau weiss was dein(e) Servo(s)
benötig(t/en). Was den Helferkreis etwas einschränkt.
Es könnte vielleicht nützlich sein, wenn du bei einer der beiden
Wissensbereichen etwas genauer nachhilfst.
Bei dieser Art von Problembeschreibung brauchst du jemanden, der sowohl
2
STM32 kennt als auch schon ganz genau weiss was dein(e) Servo(s)
3
benötig(t/en). Was den Helferkreis etwas einschränkt.
Wenn die Servos alle 20ms einen Impuls bekommen wie oben laufen die
seidenweich, besser geht es dann nicht. Der Impuls selbst ist dann in
etwa
1,5 bis 2,5ms lang, und wird von dem Servo als Stellvorgabe
interpretiert.
1,5ms = Stellung ganz links, 2,5ms = Stellung ganz rechts.
Viele Grüße
Bernd
Dann wirst du mit 4 PWM-Kanälen pro Timer und wohl mindestens 4 Timern
auch noch mehr als 6 Kanäle problemlos bedienen können.
Und wirst, wenn das nötig sein sollte, die Kanäle auch allesamt synchron
fahren können, weil man die beiden dafür verwendeten Timer über deren
Eventsystem exakt gleichzeitig starten kann.
Ich habe PWM damit schon gemacht, nur wird dir aus dem oben skizzierten
Grund mein Code nicht helfen. Hoffentlich hast du ein Oszi oder
Logikanalysator um zu sehen was rauskommt (Soundkarten-Scope tut es
auch).
Bernd schrieb:
> vielen Dank. Servoansteuerung läuft bereits auf dem AVR (nur eben> viel zu langsam - ruckelte ziehmlich - seidenweich ist anders).
Software-PWM? Neuere AVRs haben zwar 6 Hardware-PWMs, allerdings
teilweise mit 8 Bit Auflösung, was bei einem Delta von 1ms in einer
Periode von 20ms, also bestenfalls 256/20=12 Stufen, natürlich ziemlich
grob wird.
Diesen die Auflösung reduzierenden Effekt kriegt du beim STM32 natürlich
auch, nur eben hier mit beispielsweise 32000/20=1600 Stufen. Was auch
heisst: Timer 64000 und Prescaler 25 ist trotzdem besser.
Hallo,
vielen Dank, ich versuch jetzt erstmal den OutputCompare anzubinden.
PWM ggf. später, wenn es nicht gut läuft. Gleichzeitiges Ansprechen
aller Servos ist auch nicht so gut für die Stromversorgung (Modellbau
BEC).
Viele Grüße
Bernd
Hallo Zusammen,
ich versuche gerade zu meinem 20ms Counter zusätzlich einen Output
Compare einzurichten. Was mir lediglich noch fehlt, einfach ein
Zeitlimit innerhalb der 20ms zu setzen, um dann die entsprechenden
Pins an und auszuschalten.
1
volatiles32myTimerCounter1=0;
2
voidTIM2_IRQHandler(void){
3
4
// Also cleared the wrong interrupt flag in the ISR
5
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
6
7
// TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET
8
TIM_ClearITPendingBit(TIM2,TIM_IT_CC1);// Clear the interrupt flag
9
10
TIM2->CCR1=20;
11
12
myTimerCounter1++;
13
14
}
15
16
intmain(void){
17
18
// ++ Initalisieren
19
20
// Description : Initialize the Timer2 channel-1.
21
// TIM2CLK = 36 MHz, counter clock = 20 ms
22
TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;
23
TIM_OCInitTypeDefTIM_OCInitStructure;
24
25
TIM_TimeBaseStructure.TIM_Period=32000;// auto reaload register
Versuche ich den Counter fürs Compare auf 20 zusetzen, was eigentlich
den Effekt haben müßte, das der Interrupt häufiger aufgerufen wird.
Allerdings bleibt die 20ms Taktung exakt gleich...
Über Hilfe würde ich mich sehr freuen, Timer und STM32 sind echt
schwierig. Im Web finde fast nur Leute mit ähnlichen Problemen,
ohne Lösung.
Wäre klasse wenn man das einmal zum Nachlesen vormachen könnten.
Super, Vielen Dank & Viele Grüße
Bernd
Soll ich meinen Text oben nochmal posten? Ein Interrupt den man nicht
einschaltet interruptet nicht. Den Update-Int kriegst du jetzt, den
CC-Int nicht.
Bernd schrieb:
> Wäre klasse wenn man das einmal zum Nachlesen vormachen könnten.
Das hat ST freundlicherweise schon getan. In den Beispielen zur
Peripherie-Lib. Hast du da mal reingesehen? 16 Beispiele für den Umgang
mit Timern.
Hallo A.K.,
vielen Dank, ja, hab ich natürlich schon durchforstet, ist aber alles
ohne Interruptroutine, das einzige was jetzt da noch fehlen könnte
im Direkten Vergleich wäre
1
TIM_ARRPreloadConfig(TIM2,ENABLE);
Meintest Du das mit "CC-Int" (Compare Interrupt)?
Danke & Viele Grüße
Bernd
Jedenfalls, vor lauter Verzweifelung, hab ich schonmal versucht das ohne
die Lib hinzubekommen.
1
voidTIM2_IRQHandler(void){
2
// Interrupt Flag muß per Software gelöscht werden
3
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
4
myTimerCounter1++;
5
}
6
7
// Counter Max. der ins Auto-Load Register geschrieben wird
8
// Läuft bis 24287, dann 0
9
TIM2->ARR=24287;
10
// Prescaler, Clock Teiler, Counter = Clock/Psc+1
11
TIM2->PSC=255;
12
// Vergleichswert für Counter
13
TIM2->CCR1=4095;
14
// Compare Mode Register
15
// - Freeze - mit OC1REF (definierter PIN?) nichts anstellen
16
// - Auto Preload Reload enable - CCR1 kann verändert werden
17
TIM2->CCMR1=0x0008;
18
// Compare Enable Register
19
// - OC1 Low ?
20
// - OC1 enabled
21
TIM2->CCER=0x0003;// 00 geht auch
22
// DMA Interrupt Enable Register
23
// - Interrupt Enable
24
TIM2->DIER=0x0002;
25
// Controll Register
26
// - Counter Enable
27
TIM2->CR1=0x0001;
28
29
30
while(1){
31
// Zusammen 1 Sekunde...
32
Delay(250);
33
LED2_OFF();
34
Delay(250);
35
LED2_ON();
36
Delay(250);
37
LED2_OFF();
38
Delay(250);
39
LED2_ON();
40
41
statics8toggle=1;
42
43
if(toggle){
44
toggle=0;
45
TIM2->CCR1=10;
46
}else{
47
toggle=1;
48
TIM2->CCR1=20000;
49
}
50
51
rprintf("Result: %d\r",myTimerCounter1);
52
myTimerCounter1=0;
53
}
Interrupt sprich aber auch hier nicht auf eine Veränderung
von TIM2->CCR1 an.
Wenn jemand eine Idee hat, warum nicht wäre super.
Danke & Viele Grüße
Bernd
Du must dem NVIC mitteilen, das er den Interrupt überhaupt weiterleitet
und mit welcher Priorität...
Nochmal die Beispiele studieren...
Als Beispiel
/* Enable the TIM2 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
Und die Timer haben auch nochmal ein Register zum starten und Stopen vom
Timerinterrupt.. Das kann ich jetzt nicht aus den Registern rauslesen.
Benutze da eigentlich ausschließlich die FWLib... Zum initialisieren
funktioniert sie bestens.
Den NVIC hatten wir schon. Offenbar geklärt, sonst käme garkein
Interrupt. Aber der Update-Interrupt kommt ja. Nö, der CC-Interrupt muss
einfach nur freigegeben werden, egal ob mit Lib (TIM_ITConfig) oder
direkt (DIER).
Exakt dieses Thema hatten wir oben beim Update-Int auch schon. Aber
mancher macht den gleichen Fehler eben nochmal.
Hallo,
Ok, super Danke. Läßt sich aber so leider immer noch verändern
1
voidTIM2_IRQHandler(void){
2
3
// Interrupt Flag' muß per Software gelöscht werden
4
TIM2->SR&=~2;
5
TIM2->SR&=~1;
6
7
myTimerCounter1++;
8
}
9
10
intmain(void){
11
12
// ++ Initalisieren
13
14
// Counter Max. der ins Auto-Load Register geschrieben wird
15
// Läuft bis 24287, dann 0
16
TIM2->ARR=24287;
17
// Prescaler, Clock Teiler, Counter = Clock/Psc+1
18
TIM2->PSC=55;
19
// Vergleichswert für Counter
20
TIM2->CCR1=4095;
21
// Compare Mode Register
22
// - Freeze - mit OC1REF (definierter PIN?) nichts anstellen
23
// - Auto Preload Reload enable - CCR1 kann verändert werden
24
TIM2->CCMR1=0x0008;
25
// Compare Enable Register
26
// - OC1 Low ?
27
// - OC1 enabled
28
TIM2->CCER=0x0003;// 00 geht auch
29
// DMA Interrupt Enable Register
30
// - Interrupt Enable
31
// - Update Interrupt Enable
32
TIM2->DIER=0x0003;
33
// Controll Register
34
// - Counter Enable
35
TIM2->CR1=0x0001;
36
37
38
while(1){
39
// Zusammen 1 Sekunde...
40
Delay(250);
41
LED2_OFF();
42
Delay(250);
43
LED2_ON();
44
Delay(250);
45
LED2_OFF();
46
Delay(250);
47
LED2_ON();
48
49
statics8toggle=1;
50
51
if(toggle){
52
toggle=0;
53
TIM2->CCR1=10;
54
}else{
55
toggle=1;
56
TIM2->CCR1=15000;
57
}
58
59
rprintf("Result: %d\r",myTimerCounter1);
60
myTimerCounter1=0;
61
}
Mit dem unterschiedlichen Vergleichswert sollte die gemessenen
Schritte pro Sekunde stark unterschiedlich sein, bleibt aber
relativ konstant bei 47-49 Durchläufe pro Sekunde.
Kann man TIM2->CCR1 einfach so verändern, oder muß dies
noch speziell 'aktiviert' werden.
Hifle wäre super, würde ich mich sehr freuen.
Viele Grüße
Bernd
Bernd schrieb:
> Mit dem unterschiedlichen Vergleichswert sollte die gemessenen> Schritte pro Sekunde stark unterschiedlich sein, bleibt aber> relativ konstant bei 47-49 Durchläufe pro Sekunde.
Wär komisch wenn nicht. Ist beim AVR mit seinen OCRs auch nicht anders.
Pro komplettem Durchlauf des Counters solltest du 2 Interrupts kriegen.
Einen am Ende der Periode (Update) und einen wenn der CC passt. Mit dem
CCR änderst du nur den Abstand zwischen den beiden.
Hallo A.K.
herzlichen Dank für Deine Hifle - mitlerweile geht man einfach davon
aus das nicht funktioniert - sieht aber so aus als ob es läuf :)
War das ein Ding....
Viele Grüße
Bernd
Hallo Zusammen,
ich habe jetzt auch mal versucht, das gleiche Signal per Input Capture
mit STM32 zu erfassen. So penibel wie möglich das Datenblatt studiert,
leider habe ich wohl irgendetwas übersehen.
Bei diesem Code
1
volatiles32mySignal1=0;
2
volatiles32mySignal2=0;
3
4
voidTIM4_IRQHandler(void){
5
6
statics8falling=0;// Starte mit einer steigenden Flanke, siehe CCER Config
7
8
mySignal1=TIM4->CCR1;// bewirkt TIM4->SR &= ~2; Compare Interrupt Flag = 0
9
mySignal2=TIM4->CNT;
10
11
if(falling){
12
falling=0;
13
TIM4->CCER=0x0001;// Umschalten auf steigende Flanke
14
}else{
15
falling=1;
16
TIM4->CCER=0x0003;// Umschalten auf fallende Flanke
// (Produziert SR 1 - Capture Compare Interrupt Flag)
28
// (Produziert SR 9 - Capture Compare Overflow Flag)
29
// - No Prescaler, Jedes Event
30
// - Filter N8 - 8 gleiche Impulse = Capture
31
TIM4->CCMR1=0x0031;
32
33
// Compare Enable Register
34
// - Enable Counter (Counter in CCR1 Speichern)
35
// - Rising Edge (Bit1 = 0 High / 1 Low)
36
TIM4->CCER=0x0001;
37
38
// DMA Interrupt Enable Register
39
// - Interrupt Enable (Produziert SR 1 - Hier aber nicht, da es nur Compare Interrupts gibt)
40
// - Update Interrupt Enable (Produziert SR 0)
41
TIM4->DIER=0x0003;
42
43
// Controll Register
44
// - Counter Enabled
45
// - Update Enabled
46
// - ..
47
TIM4->CR1=0x0001;
48
49
while(1){
50
rprintf("Result: %d %d\r",mySignal1,mySignal2);
51
}
Läuft der Timer komplett durch, mit oder ohne Signal ist das gleiche.
TIM4->CCR1; wo eigentlich der aktuelle Zählerstand zum Capture
gespeichert
sein soll ist immer 0. TIM4->CNT pendelt so zwischen 29 und 32...
Sehr merkwürdig. Auch ohne das umschalten der Flanken (TIM4->CCER)
das gleiche Ergebnis.
Enabled hab ich hoffentlich soweit alles, Status Bits dürften auch
zurückgesetzt sein.
Über Hilfe würde mich super freuen, wenn jemand etwas sieht.
Vielen Dank & Viele Grüße
Bernd
hab nur kurz überflogen, aber hier was zum starten ohne STLib,
generiert periodic interrupts:
Auch hier wieder mein Verweis auf die CMSIS von http://www.onArm.com :-)
1
// Timer 1 init
2
voidtimer1_init(intfrequency)
3
{
4
bit_on(RCC->APB2ENR,TIM1_EN);// enable clocks for Timer 2
5
6
TIM1->PSC=1200-1;// 72MHz/1200 / 60.000 = 1Hz, enough for a 16Bit-Register :-)
7
8
TIM1->ARR=((CPU_CLOCK/1200)/frequency)-1;// load cont auto reload register with period value
9
10
bit_on(TIM1->CR1,DIR);// DIR direction down
11
12
bit_on(TIM1->CR1,URS);// only over/underflow generates an interrupt
Hallo Random,
vielen Dank, geht aber um die Input Compare Configuration,
Timer & Output Compare, steht weiter oben und ist schon fertig.
Viele Grüße
Bernd
Ein Takt (|...|), alle 20ms.
Nur die steigenden Flanken reichen natürlich nicht, man muß im
Interrupt umschalten. Siehe oben
1
// Umstellen auf steigende Flanke (CC1P = 0)
2
//TIM4->CCER = 0x0000;
3
//TIM4->CCER = 0x0100;
4
5
// Umstellen auf fallende Flanke (CC1P = 1)
6
//TIM4->CCER = 0x0000;
7
//TIM4->CCER = 0x0102;
Eigentlich müßte mit Umschalten der Flanken der Interrupt
doppelt so oft aufgerufen werden. Bleibt aber gleich.
Weiterhin, der Interrupt wird immer ca. 13.000x pro Sekunde
aufgerufen, auch wenn kein Signal anliegt.
Man sieht aber deutlich, das sich mit Signal die Werte
verändern, und nicht gleich bleiben. (Signal ist da...)
Kann mir jemand helfen, was hier nicht stimmt?
Würde ich mich sehr freuen.
Danke
Bernd
Ich sehe oben bloss Kommentare, keinen Code.
PWM Messung geht übrigens auch direkt: 13.3.6.
Aber wenn du es schon zu Fuss machen willst: Spricht was dagegen, an
Stelle der Variable "falling" den Pin direkt zu konsultieren?
Und statt den Counter auf 0 zu setzen und damit die Interrupt-Latenz mit
drin zu haben würde ich beide Captures einfach subtrahieren.
Hallo A.K,
Vielen Dank.
Update Interrupt Enable, - möchte ja den Interrupt zur Laufzeit ändern,
Steigende/Fallende Flanke... Ohne habe ich auch schon mal probiert,
die Anzahl der Interruptaufrufe bleibt mit und ohne Umschaltung der
Flanken aber gleich, genau wie mit dem Update Flag.
1
volatiles32mySignal1=0;
2
volatiles32mySignal2=0;
3
volatiles8falling=0;// beginnt mit steigender Flanke
4
volatiles32mybackup=0;
5
6
voidTIM4_IRQHandler(void){
7
8
mySignal1++;
9
mySignal2=TIM4->CCR3;
10
11
if(falling){
12
falling=0;
13
14
// Umstellen auf steigende Flanke (CC1P = 0)
15
TIM4->CCER=0x0000;
16
TIM4->CCER=0x0100;
17
}else{
18
falling=1;
19
20
// Umstellen auf fallende Flanke (CC1P = 1)
21
TIM4->CCER=0x0000;
22
TIM4->CCER=0x0102;
23
}
24
TIM2->SR&=~4;
25
}
26
27
intmain(void){
28
29
// ++ Initalisieren
30
31
TIM4->ARR=65000;
32
TIM4->CCMR2=0x0031;// 0031 mit Rauschunterdrückung
33
TIM4->CCER=0x0100;
34
TIM4->DIER=0x0008;
35
TIM4->CR1=0x0001;
36
37
while(1){
38
Delay(250);
39
LED2_OFF();
40
Delay(250);
41
LED2_ON();
42
43
rprintf("Result: %d \r",mySignal1);
44
mySignal1=0;
45
}
Bzgl. falling, weiß nicht so genau wie das direkt geht,
zum testen reicht es hoffentlich.
Bzgl. dem SignalCounter (mySignal1)
- Ohne angelegtes Signal = 0x, sehr gut
- Mit Signal = 30x pro 500ms, ok
- Mit Flankenwechsel das gleiche, müßte aber rein logisch auf 60x
kommen..
1
TIM4->CCER=0x0000;
2
TIM4->CCER=0x0100;
Beispiele im Netz gibt es beim besten Willen nicht, und die
Libary ist nur Interrupts, gar nich so einfach,
Danke & Viele Grüße
Bernd
Bernd schrieb:
> Update Interrupt Enable, - möchte ja den Interrupt zur Laufzeit ändern,
Kompletter Blindflug? Der Timer funktioniert deutlich besser, wenn man
verstanden hat wie er arbeitet.
Der Update-Interrupt schlägt zu, wenn der Counter bei ARR ankommt und
sich automatisch auf 0 setzt. Mit deiner Capture-Funktion hat er nichts
zu tun, sorgt aber naturgemäss für eine konstante Interruptrate
unabhängig vom Eingangssignal.
Hallo A.K.
vielen vielen Dank. CC3P funktioniert schon besser ... kopfschüttelnd
Wenn ich die Impulslängen messe, liegen die so knapp unter 65.000
und je nach länge gibts dann schon einen Überschlag.
Die Differenz draus zu bilden, ist dann schwierig.
Weißt Du ggf. was man da machen kann?
Danke & Viele Grüße
Bernd
(zum Hintergrund, ich bin von Hause aus PHP Programmierer...)
Bernd schrieb:
> Weißt Du ggf. was man da machen kann?
Timer langsamer zählen lassen.
Überlauf 65535=>0 wird trotzdem passieren, stört aber nicht wenn die zu
messende Zeit stets kleiner ist als die Periode vom Timer.
Hallo A.K,
> Timer langsamer zählen lassen.
Ich finde nur Prescaler (im CCMR Register),
und auf die Counter Geschwindigkeit hat das
irgendwie keinen Effekt, Immer knapp 50.000
Schritte pro 20ms.
Bei jeder zweiten Berechnung habe ich einen
Überschlag....
Könntest Du mir das Register nennen?
Danke & Viele Grüße
Bernd
Bernd schrieb:
> irgendwie keinen Effekt, Immer knapp 50.000> Schritte pro 20ms.
Was doch völlig ausreicht. Das würde sogar ausreichen um die 20ms zu
messen. Aber erst recht für die maximal 2,5ms um die es geht.
Und wenn dabei die ersten Flanke bei 64765 erfolgt und die zweite bei
124, dann war zwar ein Überlauf dazwischen (für einen Überschlag reichen
die 3,3V nicht aus, da brauchts Hochspannung), aber die Differenz lässt
sich trotzdem verwenden. Modulo 65536 eben (ARR=0xFFFF). Bischen denken
beim rechnen.
Hallo A.K,
ich haber leider noch ein Problem mit dem Outputcompare.
Ich möchten den Interrupt nur über den Outputcompare aufrufen,
nicht über Overflow.
1
voidTIM2_IRQHandler(void){
2
3
TIM2->CCR1=45000;
4
TIM2->CNT=0;
5
mycounter++;
6
7
TIM2->SR&=~2;//(1<<UIF);
8
TIM2->SR&=~1;//(1<<CC1IF);
9
}
10
11
TIM2->PSC=14;
12
TIM2->CCR1=20;
13
TIM2->CCMR1=0x0000;
14
TIM2->CCER=0x0000;
15
TIM2->DIER=0x0002;
16
TIM2->CR1=0x0001;
Ergebnis des Counters (mycounter) ist 711 pro Sekunde.
Meine Rechnung
32 000 000 Mio Takte pro Sekunde
Prescaler 14 = 2 285 714
Läuft bis 45 000 = 50.79 Aufrufe pro Sekunde.
Wenn Du mir helfen kannst, wäre ganz klasse.
Ich sitze schon den ganzen abend dran... sooo
komplex ist es nicht.
Danke & Viele Grüße
Bernd
Der CC hat rein garnichts damit zu tun wie weit ein Counter läuft. Der
läuft solange bis er AAR erreicht und fängt dann bei 0 wieder an. Hier
sind es deshalb wohl die vollen 65536, die ein 16bit Counter hergibt.
Und damit passt es dann prima in die Rechnung.
Ob der Interrupt per Update oder per CC kommt ändert nur den Zeitpunkt,
nicht die Häufigkeit. Ein Thema das wir schon einmal geklärt(?) hatten.
Kleine Eselsbrücke falls du dir den Unterschied partout nicht merken
kannst: Es gibt pro Timer einen Counter aber 4 Compare-Channels. Wenn
jeder davon den Counter zurück setzen würde, denn käme von den 4 CCs
immer nur einer dran, der mit dem kleinsten Wert.
Ansonsten kann ich nur wieder empfehlen, die Phase des Blindflugs zu
beenden, indem du dich mal damit befasst was im Timer tatsächlich
abläuft. Mikrocontroller funktionieren besser wenn man versteht was man
tut.
Korrektur: Rechnung passt auch dann nicht. Denkfehler ist es schon, auf
50 kommst damit nie, aber woher die 711 kommen ist unklar.
Schlimmer noch: Wenn in ARR wie hier suggeriert der Reset-Wert von 0
steht, dann läuft der Counter laut Doku garnicht. Es kann also nicht
alles sein was du hier gepostet hast. Da fehlt was.
PS: Eine Kleinigkeit: Das SR setzt man anders zurück. So wird CC1IF
besser mit SR=~TIM_SR_CC1IF zurück gesetzt, da die SR Bits durch
Schreiben von 1 nicht verändert werden (=> rc_w0).
@A.K.:
Wenn man aber zum Compare-Zeitpunkt den Timer per Software wieder auf 0
setzt (TIM2->CNT = 0;), sollte das schon passen. Unabhängig vom APR,
zumindest solange dessen Wert grösser als der von CC1R
Stimmt, das habe ich übersehen. Aber erstens ist das fast immer der
falsche Ansatz, zweitens sollte der Interrupt überhaupt nicht auftreten,
weil bei ARR=0 (Reset-Wert) der Zähler nicht zählt.
Hallo A.K.
soweit verstehe ich das, ich dachte ARR ist Default 0xFFFF.
(Datenblatt -liegt hier- ist 0x0000...).
Fehler liegt allerdings wohl woanders. Mit ARR = 0xFFFF
1
volatiles32mycounter=1;
2
3
voidTIM2_IRQHandler(void){
4
TIM2->CCR1=45000;
5
TIM2->CNT=0;
6
mycounter++;
7
TIM2->SR=~TIM_SR_CC1IF;
8
}
9
10
intmain(void){
11
12
// ++ Initalisieren
13
14
// Servo Out Timer
15
TIM2->ARR=0xFFFF;
16
TIM2->PSC=14;
17
TIM2->CCR1=20;// Compare wird angestoßen
18
TIM2->CCMR1=0x0000;
19
TIM2->CCER=0x0000;
20
TIM2->DIER=0x0002;
21
TIM2->CR1=0x0001;
22
23
while(1){
24
// Zusammen 1 Sekunde...
25
Delay(500);
26
27
rprintf("Nr. %d \r",mycounter);
28
mycounter=0;
29
}
30
}
710 Aufrufe pro 500ms. (gestoppt auf eine Sekunde = 1420)
Halbiert man den 'Zählraum'
1
voidTIM2_IRQHandler(void){
2
TIM2->CCR1=45000;
3
TIM2->CNT=22500;
4
mycounter++;
5
TIM2->SR=~TIM_SR_CC1IF;
6
}
7
8
intmain(void){
9
10
// ++ Initalisieren
11
STM32_Init();
12
13
// Servo Out Timer
14
TIM2->ARR=0xFFFF;
15
TIM2->PSC=14;
16
TIM2->CCR1=20;// Compare wird angestoßen
17
TIM2->CCMR1=0x0000;
18
TIM2->CCER=0x0000;
19
TIM2->DIER=0x0002;
20
TIM2->CR1=0x0001;
21
22
while(1){
23
// Zusammen 1 Sekunde...
24
Delay(500);
25
LED2_OFF();
26
27
rprintf("Nr. %d \r",mycounter);
28
mycounter=0;
29
}
30
}
Ergebnis 1420...
Prescaler 14 stimmt nach meiner Rechung
1
32000000
2
/14
3
----------------------------
4
=2285714.28571429
5
/45000
6
----------------------------
7
=50.7936507936509
= 50kz, 20ms Takt.
Hast Du eine Idee was der Timer da macht?
Danke & Viele Grüße
Bernd
Bernd schrieb:
> Hast Du eine Idee was der Timer da macht?
Jedenfalls ignoriert er den Prescaler, denn wenn du den mal aus der
Rechnung draussen lässt kommt 32000000/45000=711 raus. Was deutlich
dichter dran ist als bisher.
Apropros Prescaler: Wenn 14 dann teilt der durch 15.
Wenn das Interrupt-Flag in SR ganz am Ende der ISR gelöscht wird, dann
ist es aufgrund der internen Verzögerungen ggf. noch seitens des NVIC
sichtbar wenn die ISR die Interrupts wieder freigibt.
Ist aber nicht das eigentliche Problem.
Es scheint keine gute Idee zu sein, im ersten Aufruf der ISR das CNT
Register zu setzen. Sobald das passiert spinnt der Prescaler.
Wenn man das erst später macht ist Ruhe:
Einfachster Workaround: Bevor du den Timer Interrupt via NVIC freigibst,
warte bis SR.UIF gesetzt ist, der Counter des Timers also einmal
komplett durchlief. Siehe Testcase im STM32 Forum unter
"EnableIntAfterUpdate". Ist eine einmalige kleine Verzögerung beim Start
und am Interrupt-Handler brauchst da dann nichts zu ändern.
Damit der Thread nicht ohne Erklärung offen liegen bleibt:
Wie das Handbuch sehr detailliert beschreibt ;-) interessiert sich der
Prescaler erst dann für den Wert in PSC, wenn ein "update event"
erfolgt. Das passiert, sobald der Zähler am Limit ankommt. Wenn man im
Compare Interrupt das CNT Register auf 0 setzt, dann wird das nie
passieren, folglich wird der PSC Wert nie aktiv.
Man sollte als Teil der Initialisierung des Timers manuell einen Update
anstossen (UG-Bit in EGR).
Hallo Bernd,
möchtest du uns den Code, der fertigen Servo Ansteuerung zur Verfügung
stellen? Mache gerade die selbe Prozedur wie du durch.
Gruß Matthias
Bernd schrieb:> Hallo A.K,>> vielen Dank. Servoansteuerung läuft bereits auf dem AVR (nur eben> viel zu langsam - ruckelte ziehmlich - seidenweich ist anders). PWM> hatte> ich auch schon gedacht, nur ist die Frage ob das auf 6 Kanälen> gleichzeitig geht. (???)>> Für die Ausgabe von 6 ServoImpulsen z.B.>>
1
>servo_out[0]=1500;// PA8
2
>servo_out[1]=2000;// PA11
3
>servo_out[2]=2500;// PA12
4
>servo_out[3]=3000;// PA13
5
>servo_out[4]=3500;// PA14
6
>servo_out[5]=4000;// PA15
7
>
>> bräuchte man>> - Einen Timer mit Output Compare> - PA8 wird eingeschaltet> - Compare = servo_out[i];> - Wenn der Compare abgelaufen ist, wird PA8 ausgeschaltet.> - Der Timer wird alle 21ms (jedenfalls > 21ms) neu gestartet (?) und> Pin wird erhöht (nächster PA11)>> Müßte dann ergeben (ein Zeichen (|,.,_) entspricht 500ms)>
Hi Bernd!
Mag sein, daß das ein bischen OT ist aber eine Servoansteuerung von 6
Servos, noch dazu seriell, geht doch am AVR ohne Probleme. Und ohne
Ruckeln oder Zuckeln sondern seidenweich.
Wenn es nicht gerade Neugier oder Freude am STM32 ist, ist das doch für
diese Aufgabenstellung vollkommen overdressed.
LG
Ernst
Ein servo-signal generieren mit ein 16 bit Zaehler kann fiel einfacher :
eine reine PWM-mode mit periode von 20 ms, und die pulsbreite kan dan
mit eine Auflosung von 1 µs laufen : Timer lauft an 1 MHz, zaehlt bis
20.000. 4 kanale pro timer, jedes kanal kan dan eine Wert haben von 1000
bis 2000. Timer starten und loss geht es. Dafur braucht man keinen
Interrupt. Mit mein discovery board kan mit die 4 Timer 16 Servo-signale
generieren.