Forum: Mikrocontroller und Digitale Elektronik STM32 INT wird sofort ausgeführt


von Louis (Gast)


Lesenswert?

Hallo Leute,

folgendes Phänomen beschäftigt mich seit heute Vormittag auf einem 
STM32F030:

Ich versuche eine Library für die Timer der uC zu schreiben, der 
Interrupt wird jedoch viel zu schnell ausgelöst (bei CNT=24 statt 300). 
Der Fehler tritt bereits mit folgendem Code auf:

1
void Config_TIM16 (void){
2
3
 RCC ->APB2RSTR |= RCC_APB2RSTR_TIM16RST;
4
5
 __NOP(); __NOP(); __NOP();
6
7
 RCC ->APB2RSTR &= ~RCC_APB2RSTR_TIM16RST;
8
9
 RCC ->APB2ENR |= RCC_APB2ENR_TIM16EN;
10
11
 TIM16->ARR = (unsigned short) 300;
12
13
 TIM16->PSC = 0;
14
15
 TIM16->DIER = TIM_DIER_UIE;                                                                  
16
    
17
 TIM16->CR1 |= TIM_CR1_CEN;
18
 
19
 Timer_NVIC_control(TIM16,TIMER_IRQ_ENABLE);
20
21
}

Hat jemand eine Idee, was da genau passiert bzw. was ich falsch mache?

von m.n. (Gast)


Lesenswert?

Louis schrieb:
> Interrupt wird jedoch viel zu schnell ausgelöst (bei CNT=24 statt 300)

Und wie kommst Du darauf? Eine ISR ist ja nicht zu sehen.
Sind es vielleicht 324, mit der fehlenden 100er Stelle?
[Beim AVR funktioniert das immer einwandfrei ;-)]

von Peter D. (peda)


Lesenswert?

Man muß Code nicht mit Leerzeilen aufblähen.
Man sollte aber Code kommentieren, den man anderen zeigt oder wenn man 
später selber noch durchsehen will.

Allgemein:
- Timer stoppen
- Einstellungen vornehmen
- Pending Flag(s) löschen
- Interrupt(s) freigeben
- Timer starten

von Horst (Gast)


Lesenswert?

Wie ist 'unsigned short' definiert?
Als uint8_t würde das 44 ergeben.

von da1l6 (Gast)


Lesenswert?

Hallo

Du solltest das Timer-Interrupt-Flag löschen bevor du den Interrupt 
aktivierst.

da1l6

von Louis (Gast)


Lesenswert?

1
void Config_TIM16 (void){
2
3
 RCC ->APB2RSTR |= RCC_APB2RSTR_TIM16RST;    // reset timer
4
 __NOP(); __NOP(); __NOP();
5
 RCC ->APB2RSTR &= ~RCC_APB2RSTR_TIM16RST;   // clear reset bit
6
 RCC ->APB2ENR |= RCC_APB2ENR_TIM16EN;       // timer enable
7
 TIM16->CR1 &= ~TIM_CR1_CEN;                 // stop timer 
8
 TIM16->ARR = (unsigned short) 300;          // load auto-reload register
9
 TIM16->PSC = 0;                             // prescaler = 0 
10
 TIM16->SR &=~ 0xFFFF;                       // clear timer INT flags 
11
 TIM16->DIER = TIM_DIER_UIE;                 // update interrupt enable
12
 NVIC_ClearPendingIRQ(TIM16_IRQn);           // clear pending flags
13
 TIM16->CR1 |= TIM_CR1_CEN;                  // start timer 
14
 Timer_NVIC_control(TIM16,TIMER_IRQ_ENABLE); // activate NVIC interrupt
15
}
16
17
void TIM16_IRQHandler (void){
18
 TIMx->SR &=~ 0xFFFF;
19
}

m.n. schrieb:
> Und wie kommst Du darauf? Eine ISR ist ja nicht zu sehen.

Ich habe einfach einen Breakpoint im ISR gesetzt und den CNT des Timers 
angesehen. Der Timer bleibt beim Erreichen des Breakpoints stehen wegen 
folgendem Code:
1
RCC->APB2ENR |= RCC_APB2ENR_DBGMCUEN;
2
DBGMCU->APB2FZ|= DBGMCU_APB2_FZ_DBG_TIM16_STOP;

m.n. schrieb:
> Sind es vielleicht 324, mit der fehlenden 100er Stelle?

Leider nicht.

Peter D. schrieb:
> Man muß Code nicht mit Leerzeilen aufblähen.
> Man sollte aber Code kommentieren, den man anderen zeigt oder wenn man
> später selber noch durchsehen will.

Sorry, habe es korrigiert.

Horst schrieb:
> Wie ist 'unsigned short' definiert?

Sind 2 Bytes lang, also 16bit.

Peter D. schrieb:
> Allgemein:
> - Timer stoppen
> - Einstellungen vornehmen
> - Pending Flag(s) löschen
> - Interrupt(s) freigeben
> - Timer starten

Code wie oben geändert, leider keine Verbesserung

da1l6 schrieb:
> Du solltest das Timer-Interrupt-Flag löschen bevor du den Interrupt
> aktivierst.

Zeile eingefügt, leider trotzdem keine Besserung. Ich lande dauerhaft 
bei CNT = 24 statt 300 im Interrupt.

von Theor (Gast)


Lesenswert?

Das ist nur eine Vermutung, aber vielleicht geht es um den Zeitablauf.

Es gibt einen Zeitpunkt zu dem die Überstimmung zwischen Timer und 
Register eintritt und einen zweiten, späteren Zeitpunkt, zu dem der 
Breakpoint in der ISR aktiv wird.

Soweit ich das verstehe, wird das DBGMCU erst wirksam, wenn der 
Breakpoint aktiv wird (unter Vorbehalt).

Kann ja sein, dass das im Vergleich zur Timerfrequenz sehr lang ist.


Ansonsten würde ich vorschlagen, einmal einen kompletten, kompilierbaren 
Code zu posten, bei dem das Problem auftritt. Ausserdem Angaben darüber 
wie der STM seinen Takt bekommt und woher.

von Louis (Gast)


Lesenswert?

Theor schrieb:
> Ansonsten würde ich vorschlagen, einmal einen kompletten, kompilierbaren
> Code zu posten, bei dem das Problem auftritt. Ausserdem Angaben darüber
> wie der STM seinen Takt bekommt und woher.

Alles klar, bin dabei es zusammenzustellen.

von Louis (Gast)


Angehängte Dateien:

Lesenswert?

Anbei der Code sowie der Fehler.

Kompiliert mit der aktuellen Keil MDK (V5.23.0.0)

von Louis (Gast)


Angehängte Dateien:

Lesenswert?

und der Fehler

von ui (Gast)


Lesenswert?

0x12 ist in meiner Rechenweise 18 und nicht 24.

von Sebastian K. (sek)


Lesenswert?

Du kannst nicht erwarten, dass das Timer CNT Register auf 0 steht, wenn 
der Breakpoint im Interrupt greift. Da hängt zum einen die Timerfrequenz 
mit drin, zum anderen dauert es bis die Interrupt Routine ausgeführt 
wird oder es muss im Worst Case noch ein andere Interrupt Routine vorher 
abgearbeitet werden.

Mache doch mal folgenden Versuch: Belasse den Breakpoint dort wo er ist 
und lass das Programm ein paar mal laufen, bis es wieder auf den 
Breakpoint läuft. Wenn das CNT Register am Breakpoint mehrfach auf 0x12 
steht, weißt du das es die Latenz zwischen Interrupt Request und 
Ansprechen des Breakpoints ist.

von m.n. (Gast)


Lesenswert?

Louis schrieb:
> Ich habe einfach einen Breakpoint im ISR gesetzt und den CNT des Timers
> angesehen. Der Timer bleibt beim Erreichen des Breakpoints stehen wegen
> folgendem Code:

Der Timer bleibt beim Eintritt in die ISR nicht stehen. Wie vermutet 
sind es 324 bzw. 318 mit gelöschtem Überlauf.

von Peter D. (peda)


Lesenswert?

Wenn ich das richtig sehe, läuft der Timer mit vollem CPU-Takt, d.h. die 
18 Zyklen sind einfach die Zeit, um in den Interrupt zu springen.
Und Autoreload dürfte bedeuten, nach 300 fängt er wieder bei 0 an.
Nimm mal einen Vorteiler 1:64.

von Theor (Gast)


Lesenswert?

Nanu?

Das habe ich doch oben geschrieben. Louis: Hast Du das nicht nach 
geprüft, bevor Du den Code gepostet hast?


Zitat:

Das ist nur eine Vermutung, aber vielleicht geht es um den Zeitablauf.

Es gibt einen Zeitpunkt zu dem die Überstimmung zwischen Timer und
Register eintritt und einen zweiten, späteren Zeitpunkt, zu dem der
Breakpoint in der ISR aktiv wird.

Soweit ich das verstehe, wird das DBGMCU erst wirksam, wenn der
Breakpoint aktiv wird (unter Vorbehalt).

Kann ja sein, dass das im Vergleich zur Timerfrequenz sehr lang ist.

von Louis (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Leute,

anscheinend sah ich keinen Wald vor lauter Bäumen...

Aus irgendeinem verdrehtem Grund habe ich erwartet die 300 in der CNT 
sehen zu können. Somit habe ich wohl den Titel "Depp des Tages" sicher.

Danke Euch für die Hilfe!

Beigefügt ein Bild vom Oszi mit dem Code unten. Die 38us ergeben mit 
8MHz Takt genau 304, das scheint zu passen.
1
#include "stm32f0xx.h"
2
3
extern void Config_TIM16 (void);
4
5
int main(void){
6
7
  RCC->APB2ENR |= RCC_APB2ENR_DBGMCUEN;
8
  DBGMCU->APB2FZ|= DBGMCU_APB2_FZ_DBG_TIM16_STOP;
9
  
10
  RCC -> AHBENR |= (RCC_AHBENR_GPIOAEN);    // activate clk for GPIOA
11
  
12
  GPIOA->OTYPER  &= ~ (1UL<<4);             // set pin push-pull
13
  GPIOA->OSPEEDR |=   (3UL<<((4)*2));       // set pin high-speed             
14
  GPIOA->BSRR     =   (1UL<<(4));           // set pin high 
15
  GPIOA->MODER   |=   (1UL<<((4)*2));       // set pin as GPIO
16
  
17
  Config_TIM16();
18
  
19
  GPIOA->BSRR     =    (1UL<<((4)+16));     // set pin low
20
  
21
  while(1){__NOP();} 
22
23
#pragma diag_suppress 111  
24
  return (0);
25
}
26
27
void Config_TIM16 (void){
28
29
 RCC ->APB2RSTR |= RCC_APB2RSTR_TIM16RST;    // reset timer
30
 __NOP(); __NOP(); __NOP();
31
 RCC ->APB2RSTR &= ~RCC_APB2RSTR_TIM16RST;   // clear reset bit
32
 RCC ->APB2ENR |= RCC_APB2ENR_TIM16EN;       // timer enable
33
 TIM16->CR1 &= ~TIM_CR1_CEN;                 // stop timer 
34
 TIM16->ARR = (unsigned short) 150;          // load auto-reload register
35
 TIM16->PSC = 0;                             // prescaler = 0 
36
 TIM16->SR &=~ 0xFFFF;                       // clear timer INT flags 
37
 TIM16->DIER = TIM_DIER_UIE;                 // update interrupt enable
38
 NVIC_ClearPendingIRQ(TIM16_IRQn);           // clear pending flags
39
 TIM16->CR1 |= TIM_CR1_CEN;                  // start timer 
40
 NVIC_EnableIRQ(TIM16_IRQn);                 // activate NVIC interrupt
41
}
42
43
void TIM16_IRQHandler (void){
44
  
45
  GPIOA->BSRR     =   (1UL<<(4));           // set pin high  
46
  TIM16->SR &=~ 0xFFFF;                      // reset all flags
47
  GPIOA->BSRR     =    (1UL<<((4)+16));      // set pin low
48
  TIM16->CR1 &= ~TIM_CR1_CEN;
49
}

Peter D. schrieb:
> Nimm mal einen Vorteiler 1:64.

Habe ich in das PSC geschrieben, doch es zeigte leider keine Wirkung. 
Gibt es da was Besonderes zu beachten?

Theor schrieb:
> Nanu?
>
> Das habe ich doch oben geschrieben. Louis: Hast Du das nicht nach
> geprüft, bevor Du den Code gepostet hast?

Sorry, ich habe anscheinend den ganzen Tag einem nicht existenten Fehler 
nachgejagt... Brett vorm Kopf halt.

von Darth Moan (Gast)


Lesenswert?

Moin,

zum PSC: Ich kenne den F030 jetzt nicht so (hab F407).
Aber es sieht fuer mich so aus, als ob du den Timer in der ISR
sofort wieder stoppst. Also nur 1 mal laufen laesst.
Ich hatte das Problem, das der PSC Wert erst nach dem ersten
Update Event wirksam wird. Das war der erste ueberlauf (bei mir).
Da schaltest du in der ISR aber den Timer wieder ab.
Meine Einstellung sah dann so aus:
    __TIM5_CLK_ENABLE();
    /* TIM5 counts up in steps of 10us to provide a 32bit time stamp 
value */
    TIM5->PSC   = 839;
    TIM5->CR1   = TIM_CR1_CEN;
    TIM5->EGR   = TIM_EGR_UG;
Dann ging es. Ich habe keine ISR weil der Timer einfach frei laeuft.

Koennte das bei dir mit dem PSC auch so sein?

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.