Forum: Mikrocontroller und Digitale Elektronik Probleme mit Timer Prescaler und OC beim STM32F103


von Thorsten E. (bluescreen)


Lesenswert?

Ich versuche gerade die Timer vom STM32F103 zu verstehen. Ich habe auch 
erfolgreich ein Togglesignal an einem der Pins ausgeben können. Ich 
nutze den Timer 2 Channel 4. Die unten gezeigte Initialisierung bringt 
ihn auch dazu ein Signal am Pin PA3 (T2_CH4) auszugeben, wobei ich es 
aber am Pin PB11 (TIM2_CH4) erwartet habe. Kann man das irgendwie 
umschalten?

Das zweite Phänomen ist aber für mich noch unverständlicher. Schreibe 
ich in den Prescaler etwas anderes als 2 (probiert mit 1, 3, 20, 100) 
passiert gar nichts mehr. Wieso dies? Was mache ich falsch?

Zum Testen habe ich das gleiche mal mit Timer 3 parallel laufen lassen. 
Derselbe Effekt, Prescaler 2 geht, alles andere nicht. Das Hauptprogramm 
läuft weiter, stürzt also nicht ab. Der Fehler tritt nur bei dem Timer 
auf dessen Prescaler ungleich 2 ist. Ist also TIM2 auf 2 und TIM3 auf 
drei, läuft TIM2 aber TIM3 nicht.

Die Beiden Pin Initialisierungen sind nur drin weil es anscheinend zwei 
TIM2_CH4 Ausgänge gibt und ich nicht weiß wie man welchen auswählt 
(s.o.)
1
GPIO_InitTypeDef GPIO_InitStructure;
2
TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
3
TIM_OCInitTypeDef TIM_OC_InitStructure;
4
5
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
6
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
7
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
8
GPIO_InitStructure.GPIO_Pin = GSCLK;
9
GPIO_Init(TLCPORT, &GPIO_InitStructure);
10
11
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
12
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
13
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
14
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
15
GPIO_Init(GPIOA, &GPIO_InitStructure);
16
17
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
18
TIM_TimeBase_InitStructure.TIM_Prescaler = 3;
19
TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
20
TIM_TimeBase_InitStructure.TIM_Period = 1023;
21
TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
22
TIM_TimeBaseInit(TIM2, &TIM_TimeBase_InitStructure);
23
//TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
24
TIM_OC_InitStructure.TIM_OCMode = TIM_OCMode_Toggle;
25
26
TIM_OC_InitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
27
TIM_OC_InitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set;
28
TIM_OC_InitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
29
TIM_OC_InitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
30
TIM_OC_InitStructure.TIM_OutputState = TIM_OutputState_Enable;
31
TIM_OC_InitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
32
//TIM_OC_InitStructure.TIM_Pulse = 50;
33
TIM_OC4Init(TIM2, &TIM_OC_InitStructure);
34
TIM_Cmd(TIM2, ENABLE);

Ergänzung:
Füge ich nach TIM_TimeBaseInit die folgende Zeile ein, nimmt er den 
Prescaler aus dieser Zeile und alles funktioniert. Ist da etwa ein solch 
gravierender Bug in der Standard Peripheral Library? Wenn ja, gibts noch 
mehr?
1
TIM2->PSC=1;

: Bearbeitet durch User
von Christoph (gizmo)


Lesenswert?

Die Pin-Umschaltung beim F1 ist etwas umständlich, du musst du den Takt 
für AFIO einschalten und dann den Gewünschten Remap machen. Welchen du 
brauchst steht im Reference Manual unter GPIO in den AFIO Register 
Definitionen. Für TIM2 CH4 auf PB11 wäre das entweder Partial Remap 2 
oder Full Remap.
1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
2
3
GPIO_PinRemapConfig(GPIO_PartialRemap2_TIM2, ENABLE);
4
// oder
5
GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE);

Was mit den Timern los ist seh ich gerade nicht, probier mal nach dem 
einschalten des Timers ein Update Event zu generieren.
1
TIM_GenerateEvent(TIM2, TIM_EventSource_Update);

Ich hatte mal ein ähnliches Problem auf einem F407, da wollte der Timer 
nicht richtig loslaufen wenn ich kein Update generiert habe.

von Ingo Less (Gast)


Lesenswert?

Thorsten E. schrieb:
> Ist da etwa ein solch
> gravierender Bug in der Standard Peripheral Library?
Das lässt sich leicht prüfen in dem du die Deklaration von der 
TIM_TimeBaseInit öffnest. Dort sollte irgendwo
1
/* Set the Prescaler value */
2
  TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler;
stehen. Wenn es dort nicht steht is irgend etwas faul...

Grundsätzlich kann man sich angewöhnen, jede Struct vor zu 
initialisieren:
1
TIM_TimeBaseStructInit(&TIM_TimeBase_InitStructure);

von Thorsten E. (bluescreen)


Lesenswert?

Es wird immer eigenartiger. Ich habe gerade bemerkt, dass nach dem 
Einschalten auch meine Variante mit dem eingefügten "TIM2->PSC=1;" nicht 
startet. Erst nach dem Flashen startet es. Der Timer der auf Prescaler=2 
steht startet auch nach dem PowerOff/On.

In der TIM_TimeBaseInit steht in der Tat die Zuweisung an PSC drin, 
ebenso wie die Erzeugung eines UpdateEvents. Auch das vorherige 
Initialisieren der TIM_TimeBase_InitStructure hilft nicht.

Muss man vielleicht irgendeine Reihenfolge bei der Initialisierung der 
Timer einhalten? Aber wieso ist es dann vom Wert des Prescalers 
abhängig? Und wie soll ich sowas debuggen?

von Ingo Less (Gast)


Lesenswert?

Eigentlich nicht... Hast du einen Debugger zur Hand?

von W.S. (Gast)


Lesenswert?

Thorsten E. schrieb:
> Es wird immer eigenartiger.

Mach doch um gotteswillen deine Hardwareeinstellungen selber, indem du 
in die betreffenden Register was reinschreibst und laß den ganzen 
ST-Lib-Kram außen vor.

Ich hatte vor einiger Zeit hier schon mal ein Minimalsystem gepostet. 
Such einfach mal nach "STM32F103C8T6.ZIP" und guck dir dort die config.c 
an. Da hast du den ganzen Systemanlauf mit Portverwendung und Takt und 
das alles  ohne ST-Zirkus. Ich krame dir mal so als Beispiel eine 
Initialisierung von TIM15 heraus, die kannst du dann sinngemäß auf deine 
Anwendung ummünzen:
1
  NVIC_ISER0 = 1<<24;        // Int erlauben
2
  TIM15_CR1   = 0;           // gestoppt, keine Besonderheiten
3
  TIM15_CR2   = 0;
4
  TIM15_SMCR  = 0;           // kein Slavemode
5
  TIM15_SR    = 0;           // alle pending Ints aus
6
  TIM15_DIER  = 1;           // Interrupt nur bei Update
7
  TIM15_CCER  = 0;           // Capture/Comp erstmal aus
8
  TIM15_CCMR1 = (7<<12);     // PWM Mode 2 zuerst lo, dann hi
9
10
  TIM15_CNT   = 0;
11
  TIM15_PSC   = 45000;
12
  TIM15_ARR   = 1;           // zählt von 0 bis 1
13
  TIM15_CCR2  = 1;           // ab 1 --> hi
14
  TIM15_BDTR  = 3<<14;       // MOE+AOE .. bei diesem TIM nötig!
15
  TIM15_CCER  = 1<<4;        // CC2 enabled
16
17
// und zum Starten
18
  TIM15_SR  = 0;              // alle pending Ints aus
19
  TIM15_PSC = val_Prescaler;  // starten mit Frequenzvorgabe
20
  TIM15_CR1 = 1;              // starten

So. Das sollte zum Funktionieren ausreichen.

W.S.

von Thorsten E. (bluescreen)


Lesenswert?

Ja, habe ich. Nur funktioniert der anscheinend auch nicht richtig:

1. Springt er im Code wild hin und her wenn ich versuche zeilenweise 
vorzugehen. Umgebung ist Code::Blocks mit OOCD an einem STLinkV2

2. Ausserdem ist ja momentan der Status, das es nach dem Flashen (und 
damit auch nach dem Debuggen) alles läuft, nur nach einem Kaltstart 
nicht. Wie soll ich das dann debuggen

von Thorsten E. (bluescreen)


Lesenswert?

Nun habe ich es hinbekommen. Anscheinend hing es irgendwie an den 
Optimierungseinstellungen des armgcc. Es war die Option -O2 eingestellt. 
Lasse ich die weg, funktioniert es anscheinend.

Dann kann ich mich ja nun dem eigentlichen Problem widmen. :-)
Ich benötige an einem Pin einen Takt von ca. 8MHz. Dieser Takt soll 
genau 4096 Impulse ausgeben. Danach dann an einem anderen Pin einen 
kurzen Impuls und dann wiederholt sich das Ganze. Wer jetzt ein 
Aha-Erlebnis hat: ja es geht um einen TLC5940. Auf dem STM32F103 könnte 
man dazu wunderbar zwei Timer kaskadieren.

Leider ist die Zielhardware aber ein LPC2148 und bei dem sind die Timer 
nicht kaskadierbar. Also muss ich das per Hand machen:

Einen Timer fest auf die 8MHz Takt einstellen. Einen zweiten auch auf 
8MHz und nach 4096 Takten einen Interrupt erzeugen. Die ISR hält dann 
den ersten Timer an, erzeugt den BLANK Impuls, schaltet auf die nächste 
Anzeigezeile und startet den ersten Timer wieder.

Problem könnte werden, dass die ISR länger braucht als einen 8MHz Takt 
und das Ganze dadurch aus dem Tritt kommt. Daher wäre es sinnvoll wenn 
der zweite Timer beim Interrupt angehalten wird und die ISR am Ende ihn 
wieder startet. Damit starten dann beide Timer (fast) gleichzeitig und 
alles ist gut.

Mal sehen ob ich das hin bekomme.

von Nico W. (nico_w)


Lesenswert?

Ich würde mir an deiner Stelle gedanken machen warum das mit einer 
Optimierung nicht mehr geht.

Ich hatte einmal ein ähnliches Problem. Beim Debuggen lief alles. Von 
-O0 wieder auf -Os plötzlich nicht mehr. Mein SPI-Slave benötigt am Ende 
vom letzten Bit zu CS-high 150ns. Die waren dann nicht mehr da. Kurz ein 
paar Takte delay mit reingezaubert und alles lief wieder sauber.

von Ingo Less (Gast)


Lesenswert?

Uch würde mir das mit der Optimierung auch nochmal anschauen...

Zum abderen Problem:
Ist im Timer ein Repetition Counter vorhanden? Wann ja, nutze ihn zum 
zählen deiner Impulse. Wie lang muss der Blank-Impuls denn sein?

von Thorsten E. (bluescreen)


Lesenswert?

Anscheinend hat der LPC2148 keinen Repetition Counter und auch keine 
Möglichkeit, intern zwei Timer zu verbinden um z.B. den einen durch den 
anderen zu starten.

Also bleibt nur die reine Softwarelösung. Funktioniert inzwischen auch 
soweit (auf dem STM32f103). So richtig schlau werde ich aber nicht aus 
den Frequenzen. Mein APB1 Takt läuft auf 72MHz.

Timer 2 tut genau was er soll. Ich habe den Prescaler auf 1 gestellt, 
was wohl soviel heißt wie durch zwei teilen. Den Timer Period habe ich 
auf eins gestellt, dmit zählt er von 0..1, also zwei Takte. Den 
zugehörigen Ausgang auf TOGGLE, der dadurch wieder jeden zweiten Takt 
wechselt. Damit ergibt sich eine Taktfrequenz von 9 MHz:
72MHz  2  2 / 2 = 9MHz

Timer 3 musste ich nun auf einen Vorteile von 7 stellen und den 
Periodenwert auf 4095, wodurch er 4096 Takte zählt.

Schalte ich damit nun den Timer 2 aus und wieder ein erhalte ich 
zwischen zwei Timer 3 Pulsen 4105 Pulse am Timer 2.
Daher habe ich den Periodenwert von Timer 3 nun auf 4085 angepasst und 
damit bekomme ich nun exakt 4096 Timer 2 Impulse pro Sequenz. Da ich 
beide Timer in der ISR stoppe und starte ist das auch unabhängig von der 
Dauer der Aktivitäten innerhalb der BLANK-Phase.

Ich hätte erwartet, dass ich für das gewünschte Ergebnis den Vorteiler 
von Timer 3 auf 8 hätte stellen müssen.

von Ingo Less (Gast)


Lesenswert?

Naja, du wirst eine gewissen Latenz vom Interrupt bis zur Ausführung der 
ISR haben. Wenn du den Prescaler auf 7 stellst, wird durch 8 geteilt. 
Zum Prescalerwert im Register wird immer noch eine 1 dazu addiert.

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.