Forum: Mikrocontroller und Digitale Elektronik Timer Interrupt am STM32F103


von Markus M. (mmax)


Lesenswert?

Hi,

Kämpfe jetzt schon eine Weile mit einem Timer Interrupt auf einem 
STM32F103CBT6 dev-board (Maple Mini Nachbau aus Fernost) herum und bitte 
euch mal kurz den Code anzuschauen:

1
#define LED_PIN    GPIO_Pin_1
2
#define LED_PORT   GPIOB
3
4
void IntializeLEDs()
5
{
6
  // GPIO mit Takt versorgen
7
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
8
9
  // GPIO konfigurieren
10
  GPIO_InitTypeDef GPIO_InitStructure;
11
  GPIO_InitStructure.GPIO_Pin = LED_PIN;
12
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
13
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
14
  GPIO_Init(LED_PORT, &GPIO_InitStructure);
15
16
  // Lampe aus
17
  GPIO_SetBits(LED_PORT, LED_PIN);
18
}
19
20
void InitializeTimer()
21
{
22
  TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure; 
23
  NVIC_InitTypeDef NVIC_InitStructure; 
24
25
  // Timer mit takt versorgen
26
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
27
28
  // Timer konfigurieren
29
  TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
30
  TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
31
  TIM_TimeBase_InitStructure.TIM_Period = 1999;
32
  TIM_TimeBase_InitStructure.TIM_Prescaler = 17999;
33
  TIM_TimeBaseInit(TIM2, &TIM_TimeBase_InitStructure);
34
  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
35
36
  // Interruptcontroller konfigurieren
37
  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
38
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
39
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
40
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
41
  NVIC_Init(&NVIC_InitStructure);
42
43
  // aktivieren
44
  NVIC_EnableIRQ(TIM2_IRQn);
45
  TIM_Cmd(TIM2, ENABLE);
46
}
47
48
int main(void)
49
{
50
  IntializeLEDs();
51
  InitializeTimer();
52
  __enable_irq();
53
54
  while(1) {}
55
56
  return 0;
57
}
58
59
void TIM2_IRQHandler(void)
60
{
61
  TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
62
63
  if(GPIO_ReadOutputDataBit(LED_PORT, LED_PIN)) {
64
    GPIO_ResetBits(LED_PORT, LED_PIN);
65
66
  } else {
67
    GPIO_SetBits(LED_PORT, LED_PIN);
68
  }
69
}

Da ich schon unzählige Beispielcodes durchforstet habe, denke ich dass 
mein Code passen sollte. Oder kanns an der Initialisierung (startup 
code) liegen, obwohl das ein- bzw. ausschalten der LED funktioniert.

Danke für durchsehen,
Max

von Markus C. (ljmarkus)


Lesenswert?

irgendwie vermisse ich das SystemInit.

von Markus M. (mmax)


Lesenswert?

Das wird im "startup_stm32f10x_md.s" (Hab die TrueSTUDIO Version aus der 
CMSIS genommen) erledigt. Und grundsätzlich dürfte dann ja auch 
Ansteuern der LED nicht funktionieren ... denke ich mir mal

von Harry L. (mysth)


Lesenswert?

Ich seh da keine SystemClock_Config...

von Markus M. (mmax)


Lesenswert?

Das wird auch in der SystemInit Routine der CMSIS gemacht. Hier ein Link 
zum selben File:

https://github.com/svrnuke/STM32-Library-V3.5/blob/master/Project/STM32F10x_StdPeriph_Examples/DAC/OneChannelDMA_Escalator/system_stm32f10x.c

Hab hab im Makefile nur das Compiler-Flag "-DSTM32F10X_MD" gesetzt und 
der Rest sollte in der StdPeriph_Lib erledigt werden, wenn man das 
passende system_stm32f10x.c mit ein bindet. Und laut Datenblatt ist es 
ein Medium Density Controller. Sollte also auch passen, oder?

von Markus C. (ljmarkus)


Lesenswert?

trotdem muss das aufgerufen werden.
1
int main(void)
2
{
3
  SystemInit();
4
....

von Markus M. (mmax)


Lesenswert?

OK, ich habs eingebaut aber keine Besserung.

Dennoch denke ich dass die SystemInit() noch vor dem Sprung in die 
main() via startup aufgerufen wir.

Hier ein Auszug aus der startup_stm32f10x_md.s:
1
LoopFillZerobss:
2
  ldr  r3, = _ebss
3
  cmp  r2, r3
4
  bcc  FillZerobss
5
6
/* Call the clock system intitialization function.*/
7
    bl  SystemInit
8
/* Call static constructors */
9
    bl __libc_init_array
10
/* Call the application's entry point.*/
11
  bl  main
12
  bx  lr
13
.size  Reset_Handler, .-Reset_Handler

von Markus C. (ljmarkus)


Lesenswert?

versuche mal
1
 
2
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); // <---------
3
  TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
4
  TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
5
  TIM_TimeBase_InitStructure.TIM_Period = 1999;
6
  TIM_TimeBase_InitStructure.TIM_Prescaler = 17999;
7
  TIM_TimeBaseInit(TIM2, &TIM_TimeBase_InitStructure);
8
  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

von Markus M. (mmax)


Lesenswert?

Hat leider auch nix gebracht ... aber ich werds drinnen lassen, sicher 
sauberer.

Danke

von Markus C. (ljmarkus)


Lesenswert?

hier mal eine Timer Init von mir:
1
void timer_init()
2
{
3
  TIM_TimeBaseInitTypeDef    TIM_TimeBaseStructure;
4
  NVIC_InitTypeDef NVIC_InitStructure;
5
6
  // clock enable
7
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
8
9
  /* Time base configuration */
10
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
11
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
12
  TIM_TimeBaseStructure.TIM_Prescaler = 1000-1; // 84Mhz / 1000 = 84kHz
13
  //TIM_TimeBaseStructure.TIM_Period = 8400-1;    // 84kHz / 8400 = 10Hz endgueldig
14
  
15
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
16
  TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
17
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
18
19
  // Interrupt enable for Update
20
  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
21
22
23
  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
24
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
25
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
26
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
27
  NVIC_Init(&NVIC_InitStructure);
28
29
  // start Timer
30
  TIM_Cmd(TIM2, ENABLE);
31
}
32
33
void TIM2_IRQHandler()
34
{
35
  while((TIM2->SR & (1<<0)) > 0) TIM2->SR &= ~(1<<0); // clear UIF Flag
36
37
......
38
}

von Markus M. (mmax)


Lesenswert?

Guten Morgen.

@Markus C.: Danke für deinen Beispielcode aber das Verhalten ändert sich 
nicht.

Ich hab jetzt aber mal die Interruptfunktion rausgenommen und nur den 
Timer laufen lassen. In der while schleife den Timer Counter abgefragt 
und dementsprechend das LED ein oder aus geschaltet:
1
while(1) {
2
    unsigned long count = TIM_GetCounter(TIM2);
3
4
    if( count > 7100 ) {
5
      GPIO_SetBits(LED_PORT, LED_PIN);
6
      
7
    } else {
8
      GPIO_ResetBits(LED_PORT, LED_PIN);
9
    }
10
  }

Und siehe da, das LED blinkt. Also so wies aussieht scheint der Timer zu 
feuern und es dürfte nur der Aufrufer der Interruptfunktion nicht 
funktionieren ... stellt sich nur die Frage weshalb nicht!

von Herbert P. (Gast)


Lesenswert?

Hallo,

Normalerweise ist es notwendig, nach dem Start des Timers das 
Interruptflag zu löschen, weil es zu diesem Zeitpunkt gesetzt ist. Hatte 
ein ähnliches Problem am STM32F411. Mir ist nur nicht klar, ob das ein 
Bug oder ein Feature ist. ;)

Gruß Herby

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Herbert P. schrieb:
> Normalerweise ist es notwendig, nach dem Start des Timers das
> Interruptflag zu löschen, weil es zu diesem Zeitpunkt gesetzt ist.

Jo, da war mal was. In einem älteren Motorkontroller, den ich mal mit 
dem VL Discovery gemacht habe, findet sich folgender Kommentar:
1
void TIM1_UP_TIM16_IRQHandler(void){
2
// mikrocontroller.net : Clear the IT pending bit before doing any GPIO stuff
3
TIM_ClearITPendingBit(TIM1,(TIM_IT_Update | TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3));
4
  LED_GPIO_PORT->BSRR = ERROR_LED_PIN;  // debug
Ich weiss nicht mehr, wo es hier im Forum war, muss ein alter Beitrag 
sein. Gut, hier ist es ein Timer 1 IRQ, aber das Prinzip ist das 
gleiche.

: Bearbeitet durch User
von Markus M. (mmax)


Lesenswert?

Ein TIM_ClearITPendingBit(TIM2, TIM_IT_Update) direkt nach dem 
TIM_Cmd(TIM2, ENABLE) bringt auch nicht ... oder hab ich das falsch 
verstanden?

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Markus M. schrieb:
> Ein TIM_ClearITPendingBit(TIM2, TIM_IT_Update) direkt nach dem
> TIM_Cmd(TIM2, ENABLE) bringt auch nicht

Kannst du gerne machen - wichtig ist aber, das du es in der ISR direkt 
am Anfang auch ausführst, ehe du an den GPIO rumfummelst.

von Markus M. (mmax)


Lesenswert?

Ja, das hatte ich schon drinnen ... siehe meine ersten Post

von Herbert P. (Gast)


Lesenswert?

Markus M. schrieb:
> Ein TIM_ClearITPendingBit(TIM2, TIM_IT_Update) direkt nach dem
> TIM_Cmd(TIM2, ENABLE) bringt auch nicht ... oder hab ich das falsch
> verstanden?

Hamm, ja, so war es gemeint. Überprüfe mal in der Main den Zustand des 
IT-Pending-Bits nach dem Starten des Timers. Das sollte in dem Fall 
Klarheit schaffen.

Herby

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Markus M. schrieb:
> Ja, das hatte ich schon drinnen ... siehe meine ersten Post

Oh, sorry, das hatte ich übersehen. Ich hab allerdings in meiner Timer 
Init Routine noch was gefunden, was evtl. interessant ist:
1
void TimerInit(void) {
2
3
RCC_APB2PeriphClockCmd(TIM1_CLK, ENABLE);
4
/* Initialize basic structures to default */
5
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
6
TIM_OCStructInit(&TIM_OCInitStructure);
7
/* Time base configuration */
8
9
TIM_TimeBaseStructure.TIM_Prescaler = 256;
10
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
11
TIM_TimeBaseStructure.TIM_Period = 0x00FF;
12
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
13
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
14
15
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
16
TIM_UpdateRequestConfig(TIM1, TIM_UpdateSource_Global); // <<< 
17
TIM_UpdateDisableConfig(TIM1,DISABLE);                  // <<<
18
// continue with PWM config here...
Die letzten beiden Zeilen. Warum und wieso darfst du mich im Moment 
allerdings nicht fragen, das ist etwas länger her. Es kann sein, das das 
nur dewegen nötig war, weil ich in der ISR die OC Register update.

: Bearbeitet durch User
von Herbert P. (Gast)


Lesenswert?

Hi,

mir ist noch was aufgefallen an deinem Code. Bist du dir sicher, dass du 
die Priority und die Subpriority beide auf 0x0f setzen willst. Ich bin 
leider grade im Garten und habe die Manuals nicht dabei, aber ich 
gluabe, das passt nicht zusammen. Wenn es dein Projekt zulässt, setze 
beide mal probehalber auf 0.

Herby

von Markus M. (mmax)


Lesenswert?

@Herbert: Hatte auch schon andere Priorität, bringt aber auch nix.

Aber ich habe folgendes festgestellt. Setzte ich in meiner Main das LED 
auf high (wird in der IntializeLEDs() auf low gesetzt), dann 
funktioniert das nur wenn ich die InitializeTimer() nicht aufrufe!
1
int main(void)
2
{
3
  SystemInit();
4
5
  IntializeLEDs();
6
  InitializeTimer();
7
8
  // Funktioniert nur wenn InitializeTimer() auskommentiert
9
  GPIO_SetBits(LED_PORT, LED_PIN);
10
11
  while(1) {
12
    asm("nop");
13
  }
14
15
  return 0;
16
}

Da stimmt doch was nicht!

von Markus M. (mmax)


Lesenswert?

Ich habe jetzt ein zweites Board [1] verwendet und siehe da ... der 
Timer Interrupt funktioniert (mit selben Code)

Jetzt stellt sich mir natürlich die Frage wieso!

Beim andern Board (bei dems nicht läuft) hab ich mehr Flash, und dort 
ist ein DFU Bootloader installiert, mit dem ich via USB flashe. Deshalb 
hab ich das Linker Script angepasst - hier ein Auszug:

1
MEMORY
2
{
3
  FLASH (rx)      : ORIGIN = 0x08005000, LENGTH = 108K
4
  RAM (xrw)       : ORIGIN = 0x20000C00, LENGTH = 17K
5
  MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
6
}

Das hab ich mir aus der STM32-Arduino Erweiterung abgeschaut [2]. Kann 
es sein dass es daran liegt, obwohl ein reguläres schalten der LED 
funktioniert, das Programm also offensichtlich ausgeführt wird?

Das aktuelle Board flashe ich via st-link, da gibts also keinen 
Bootloader.

Bin für Anregungen Dankbar!

[1] 
https://www.tindie.com/products/mmm999/arm-cortex-m3-stm32f103c8t6-stm32-minimum-system-development-board-with-mini-usb/
[2] 
https://github.com/rogerclarkmelbourne/Arduino_STM32/blob/4f2a6028f07cfac35b3564263c57991e7eee7df0/STM32F1/variants/maple_mini/ld/mem-flash.inc

von red cruiser (Gast)


Lesenswert?

Vielleicht mal die Interrupttable Startadresse anpassen?

NVIC_SetVectorTable( APP_VEC_TABLE, APP_VEC_TAB_OFFSET);

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.