Forum: Mikrocontroller und Digitale Elektronik EmBitz: Ignorierte Includes und Probleme beim NVIC/GPIO


von Viktor B. (coldlogic)


Lesenswert?

Hi Leute,

Ich brauch mal eure Hilfe. Und zwar versuche ich gerade, ein 
chinesisches BluePill mit einem STM32F103C8T6 drauf zum Leben zu 
bringen. Der Plan war, einen Timer laufen zu lassen, der dann eine LED 
per Interrupt ein- und ausmachen würde. Ja, daran scheitert es schon. 
Die LED bleibt an, der Interrupt wird gar nicht erreicht. Da ich schon 
einige Probleme mit Includes und Defines gehabt habe (man sieht es im 
Code, wo plötzlich z.B ((uint16_t)0x2000)) anstatt GPIO_Bit_13 steht - 
da musste ich dem Präprozessor seine Arbeit abnehmen und direkt das 
eintippen, was in den entsprechenden, eigentlich eingebundenen Dateien 
stand. In einem anderen Projekt werden die Includes auch resolved. Unter 
Verdacht hab ich, dass die Funktion TIM7_IRQHandler gar nicht als 
Interrupt erkannt wird, da die IDE beim Rechtsklick -> find declaration 
gar nichts ausgibt. So wird es aber in einem Tutorial gemacht, z.B. 
https://www.youtube.com/watch?v=K2WAy53sxmQ .

Der Code ist wie folgt:
1
#include "stm32f10x_conf.h"
2
//#include "stm32f10x.h"
3
4
void Init(void){
5
// Hardware initialiser
6
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);               //GPIO C einschalten
7
    GPIO_InitTypeDef gpioInit;
8
    gpioInit.GPIO_Mode = GPIO_Mode_Out_OD;
9
    gpioInit.GPIO_Speed = GPIO_Speed_2MHz;
10
    gpioInit.GPIO_Pin = GPIO_Pin_13;
11
    GPIO_Init(GPIOC, &gpioInit);
12
    GPIO_WriteBit(GPIOC, ((uint16_t)0x2000), Bit_SET);
13
14
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);                //Timer 7 einschalten
15
    TIM_TimeBaseInitTypeDef timInit;                                    //Einstellen
16
    timInit.TIM_CounterMode = TIM_CounterMode_Up;
17
    timInit.TIM_ClockDivision = TIM_CKD_DIV1;
18
    timInit.TIM_Prescaler = 1000;
19
    timInit.TIM_Period = 8400;
20
    TIM_TimeBaseInit(TIM7, &timInit);                                   //Zuweisen
21
    TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE);
22
23
    NVIC_InitTypeDef NVICinit;                                          //NVIC konfigurieren
24
    NVICinit.NVIC_IRQChannel = 55;                                     //Timerinterrupt hinzufügen
25
    NVICinit.NVIC_IRQChannelPreemptionPriority = 0x0F;
26
    NVICinit.NVIC_IRQChannelSubPriority = 0x0E;
27
    NVICinit.NVIC_IRQChannelCmd = ENABLE;
28
    NVIC_Init(&NVICinit);
29
30
}
31
32
volatile unsigned char led = 0;
33
34
    void TIM7_IRQHandler(){
35
    if(led){
36
        GPIO_WriteBit(GPIOC, ((uint16_t)0x2000), Bit_SET);              //PIN13
37
        led = 0;
38
    }else{
39
        GPIO_WriteBit(GPIOC, ((uint16_t)0x2000), Bit_RESET);
40
        led = 1;
41
    }
42
    TIM_ClearITPendingBit(TIM7, TIM_IT_Update);
43
}
44
45
int main(void)
46
{
47
    Init();
48
49
  while(1)
50
  {
51
52
  }
53
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Viktor B. schrieb:
> Unter Verdacht hab ich, dass die Funktion TIM7_IRQHandler gar nicht als
> Interrupt erkannt wird, da die IDE beim Rechtsklick -> find declaration
> gar nichts ausgibt.

Du musst zwischen dem, was Deine IDE Dir erzählt, und dem, was der 
Compiler draus macht, deutlich unterscheiden. Deine IDE mag 
Include-Dateien ignorieren, der Compiler aber macht das mit Sicherheit 
nicht.

von Jim M. (turboj)


Lesenswert?

Mach mal das

TIM_ClearITPendingBit() als erste Anweisung in den Handler und nicht als 
letzte. Ansonsten könnte die Handler Funktion wegen dem Schreibpuffer 
und Tail-Chaining 2x hintereinander ausgeführt werden.

Dann noch:
1
NVICinit.NVIC_IRQChannel = 55;  //Timerinterrupt hinzufügen

Mach aus der 55 mal ein "TIM7_IRQn". Bei den Nummern vertut man sich 
sonst leicht.

von Viktor B. (coldlogic)


Lesenswert?

Rufus Τ. F. schrieb:
> Deine IDE mag
> Include-Dateien ignorieren, der Compiler aber macht das mit Sicherheit
> nicht.

Wie prüft man das? Bzw. wo findet man das kompilierte Assemblerprogramm? 
Ich schätze, dort könnte man nach einer Tabelle suchen?

Jim M. schrieb:
> Mach aus der 55 mal ein "TIM7_IRQn". Bei den Nummern vertut man sich
> sonst leicht.

Dann macht mir die IDE aus "compiled successfully" einen "Could not 
resolve" - wie gesagt, Probleme beim resolven hat die IDE nun mal. Das 
letzte Mal ist mir sowas mit Eclipse passiert, wo ein zweites Einbinden 
der Sources das Problem löste. In EmBitz hatte ich die Option noch nicht 
gefunden. Und die Nummer wurde dreifach verglichen, ist schon die 
richtige.

von Mitlesa (Gast)


Lesenswert?

In der Datei

startup_stm32f10x_ms.S

die auch in deinem Projekt vorhanden sein sollte sind alle
default IRQHandler aufgeführt die es geben sollte, und nicht
mehr. Dort ist aber kein Handler für Timer7 zu finden.

Viktor B. schrieb:
> anstatt GPIO_Bit_13 steht -
> da musste ich dem Präprozessor seine Arbeit abnehmen und direkt das
> eintippen

Nein, du musst nur die richtigen Tokens verwenden.
1
  GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);

von Mitlesa (Gast)


Lesenswert?

Mitlesa schrieb:
> Nein, du musst nur die richtigen Tokens verwenden.

Ist übrigens in den SPL-Dateien (hier stm32f10x_gpio.c) recht
genau im Header beschrieben was du als Parameter geben kannst.
1
/**
2
  * @brief  Sets or clears the selected data port bit.
3
  * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
4
  * @param  GPIO_Pin: specifies the port bit to be written.
5
  *   This parameter can be one of GPIO_Pin_x where x can be (0..15).
6
  * @param  BitVal: specifies the value to be written to the selected bit.
7
  *   This parameter can be one of the BitAction enum values:
8
  *     @arg Bit_RESET: to clear the port pin
9
  *     @arg Bit_SET: to set the port pin
10
  * @retval None
11
  */
12
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)
13
{
14
  /* Check the parameters */
15
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
16
  assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
17
  assert_param(IS_GPIO_BIT_ACTION(BitVal));
18
19
  if (BitVal != Bit_RESET)
20
  {
21
    GPIOx->BSRR = GPIO_Pin;
22
  }
23
  else
24
  {
25
    GPIOx->BRR = GPIO_Pin;
26
  }
27
}

von Mitlesa (Gast)


Lesenswert?

Viktor B. schrieb:
> So wird es aber in einem Tutorial gemacht, z.B.
> Youtube-Video "ARM Cortex EmBitz - LED Blinken Timer ISR  - Tutorial
> Teil #3 (deutsch)" .

Dort ist aber von einem Timer 2 (und nicht Timer 7) die Rede,
so jedenfalls die Unterschrift zum verlinkten Video:

Zitat:

"Realisierung eines interruptgesteuerten LED Blinken mit
Hilfe des Timers 2, welcher an den NVIC (Interrupt Management)
angebunden wird."

Naja, 2 und 7 kann man schon mal verwechseln wenn die Augen im
fortgeschrittenen Alter nachlassen ....

von Mitlesa (Gast)


Lesenswert?

Mitlesa schrieb:
> sind alle
> default IRQHandler aufgeführt die es geben sollte

Die Vektor-Tabelle im Reference Manual (Seite 203, dort kommt
auch Timer 7 vor) ist jedoch grösser als diejenige die in
<startup_stm32f10x_ms.S> gelistet ist ....

Man müsste diese Tabelle also "aufbohren" um an den möglichen
Timer 7 Interrupt zu kommen.

von Viktor B. (coldlogic)


Lesenswert?

So, bin den zahlreichen Tipps von Mitlesa gefolgt.

1) Jetzt bin ich mir nicht mehr sicher, ob ich tatsächlich so einen 
dummen Fehler mit _Bit und _Pin gemacht hab. Die IDE kann das jetzt 
resolven, kann aber auch am Neustart liegen. Ja, die Wahrscheinlichkeit 
ist klein, aber genauso groß wie die davon, dass ich um die eine Stunde 
einen Projekt, wo es richtig war, und einen, wo es falsch war, 
gleichzeitig angeschaut hab und das nicht bemerken konnte.

2) Es gibt keinen Timer 7. Ich wollte nicht alles aus dem Tutorial 
abkupfern und einen anderen Timer verwenden, dabei hab ich aus Versehen 
in den RefMan anstatt in den Datasheet geschaut. Hab mich schon gefreut, 
dass der STM32F103 Timer in Hülle und Fülle bietet. Tja. Habs jetzt mit 
dem Timer 4 probiert.

3) Es läuft immer noch nichts. Das Programm sieht mittlerweile so aus:
1
#include "stm32f10x_conf.h"
2
//#include "stm32f10x.h"
3
4
void Init(void){
5
// Hardware initialiser
6
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);               //GPIO C einschalten
7
    GPIO_InitTypeDef gpioInit;
8
    gpioInit.GPIO_Mode = GPIO_Mode_Out_OD;
9
    gpioInit.GPIO_Speed = GPIO_Speed_2MHz;
10
    gpioInit.GPIO_Pin = GPIO_Pin_13;
11
    GPIO_Init(GPIOC, &gpioInit);
12
    GPIO_WriteBit(GPIOC, ((uint16_t)0x2000), Bit_SET);
13
14
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);                //Timer 2 einschalten
15
    TIM_TimeBaseInitTypeDef timInit;                                    //Einstellen
16
    timInit.TIM_CounterMode = TIM_CounterMode_Up;
17
    timInit.TIM_ClockDivision = TIM_CKD_DIV1;
18
    timInit.TIM_Prescaler = 1000;
19
    timInit.TIM_Period = 8400;
20
    TIM_TimeBaseInit(TIM4, &timInit);                                   //Zuweisen
21
    TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
22
23
    NVIC_InitTypeDef NVICinit;                                          //NVIC konfigurieren
24
    NVICinit.NVIC_IRQChannel = TIM4_IRQn;                               //Timerinterrupt hinzufügen
25
    NVICinit.NVIC_IRQChannelPreemptionPriority = 0x0F;
26
    NVICinit.NVIC_IRQChannelSubPriority = 0x0E;
27
    NVICinit.NVIC_IRQChannelCmd = ENABLE;
28
    NVIC_Init(&NVICinit);
29
30
}
31
32
volatile unsigned char led = 0;
33
34
    void TIM4_IRQHandler(){
35
        TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
36
    if(led){
37
        GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);              //PIN13
38
        led = 0;
39
    }else{
40
        GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
41
        led = 1;
42
    }
43
}
44
45
int main(void)
46
{
47
    Init();
48
49
  while(1)
50
  {
51
52
  }
53
}

Durchs Debuggen sieht es aus, als würde das Programm genau einmal in die 
Interruptroutine springen, aber auch nicht mehr. Studiere gerade das 
RefMan bezüglich autoreload des Timers, vielleicht lädt der Timer aus 
irgendeinem Grund nicht mehr neu?..

von Mitlesa (Gast)


Lesenswert?

Nachdem du den Timer initialisiert hast musst du ihn auch
noch aktivieren:
1
  TIM_Cmd (TIM2, ENABLE);

Dann klappts auch mit dem (wiederholten) IRQ.

von Viktor B. (coldlogic)


Lesenswert?

Yup. Das war es, nun blinkt die LED fröhlich vor sich hin! Danke 
@Mitlesa.

Noch eine Frage: woher soll man das alles wissen? Gibt es zusätzlich zu 
der RefMan des STM32F10x noch ein RefMan für die SPL? Oder wie kommt man 
an die nötigen Anweisungen und deren Bezeichnungen? Nur damit ich nichts 
weiteres übersehe, falls ein Tutorial nicht darauf eingehen wird

von Felix F. (wiesel8)


Lesenswert?

Die SPL bietet dutzende Beispiele, wo zumindest grob alle Peripherals 
behandelt werden.

mfg

von Mitlesa (Gast)


Lesenswert?

Viktor B. schrieb:
> Noch eine Frage: woher soll man das alles wissen? Gibt es zusätzlich zu
> der RefMan des STM32F10x noch ein RefMan für die SPL?

Das "RefMan" ist für  mich die entsprechende Header-Datei
bezogen auf die Hardware mit der ich mich gerade beschäftige.

Als Beispiel hier die Prototypen der <stm32f10x_gpio.h>. Damit
hat man eine sehr kompakte Übersicht was möglich ist.
1
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
2
void GPIO_AFIODeInit(void);
3
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
4
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
5
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
6
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
7
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
8
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
9
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
10
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
11
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
12
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
13
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
14
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
15
void GPIO_EventOutputCmd(FunctionalState NewState);
16
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
17
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
18
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);

Will man es dann genauer wissen genügt ein Blick in die
korrespondierende <stm32f10x_gpio.c>

Alles für mich ausreichend selbsterklärend ... für dich nicht?
Dann kann man immer noch ins echte "RefMan" schauen ...

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.