Viral
Viral

Reputation: 31

STM32F303VCT6 external interrupt with PA0 button won't toggle LED

I want to use a button connected to PA0 as an external interrupt to toggle LED on PE14 on button press. However calling the configure_PA0 function doesn't seem to work.

I did a simple blinking instruction in while loop to test and it turns out when I call configure_PA0 the LED stays ON all the time.

Without calling it, the LED will blink just fine so I think it must be something wrong with this function.

#include "stm32f30x.h"

void delay(volatile uint32_t count){
    while(count > 0 )
        count--;
}

void init_LED(){ //init led on PE14
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE);
        GPIO_InitTypeDef GPIO_InitStructure;
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
        GPIO_Init(GPIOE,&GPIO_InitStructure);
}
void configure_PA0(void) {

    GPIO_InitTypeDef GPIO_InitStruct;
    EXTI_InitTypeDef EXTI_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
//PA0 as button init
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

//EXTI init
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
    EXTI_InitStruct.EXTI_Line = EXTI_Line0;
    EXTI_InitStruct.EXTI_LineCmd = ENABLE;
    EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_Init(&EXTI_InitStruct);

//NVIC init
    NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x00;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
}


void toggle_PE14(){
    if(GPIO_ReadOutputDataBit(GPIOE,GPIO_Pin_14) == 0)
        GPIO_SetBits(GPIOE,GPIO_Pin_14);
    else
        GPIO_ResetBits(GPIOE,GPIO_Pin_14);
}

//handle pa0 interrupt
void EXTI0_IRQHandler(void) {
    if(EXTI_GetITStatus(EXTI_Line0) != RESET){
        toggle_PE14();

        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}


int main(void) {

    init_LED();
    configure_PA0();

    while (1) {
        delay(400);
    }

    return 0;
}

UPDATE I fixed it by putting handler definition into extern "C" { .. } brackets. Apparently you have to do that if you code in C++.

Upvotes: 3

Views: 997

Answers (1)

HelpingHand
HelpingHand

Reputation: 1468

UPDATE ...

First, thanks for letting other readers know! Let me explain the causal relationship of these findings:

... I fixed it by putting handler definition into extern "C" { /* .. */ } brackets. Apparently you have to do that if you code in C++.

When programming in C++ (i.e., for those language features that are both valid in C and C++, when using a C++ compiler to build the program) there is a difference between C++ symbols and C symbols in the object code used by the linker. In a nutshell, this is done because C++ identifiers must be extended by some type information in order to support polymorphism. Details are explained here.

Any C++ function which is defined without extern C qualifiers will get an extended ("mangled") symbol. This also applies to "ambiguous" functions fed into the C++ compiler like in the present case.

Any extern C function (and any function translated by a C compiler, if one mixes compilers) will turn into a linker symbol with an unmangled name (usually only extended by some underscores, depending on the toolchain used).

The point is: Assembler functions behave mostly like C functions - function symbols referenced/defined in assembler code will be passed to the linker just as they are. Usually (and in the present example), this is also the case for the definition of the STM32 interrupt vector table (both following code snippets are taken from startup_stm32l476xx.s):

g_pfnVectors:
    .word   _estack
    .word   Reset_Handler
    .word   NMI_Handler
    .word   HardFault_Handler
    .word   MemManage_Handler
    .word   BusFault_Handler
    .word   UsageFault_Handler
    .word   0
    /*...*/
    .word   PendSV_Handler
    .word   SysTick_Handler
    /*...*/
    .word   EXTI0_IRQHandler
    .word   EXTI1_IRQHandler
    /*...*/

and for the weak function definition linking to the DefaultHandler in default STM32CubeF3 / STM32CubeMX code

/*******************************************************************************
 *
 * Provide weak aliases for each Exception handler to the Default_Handler.
 * As they are weak aliases, any function with the same name will override
 * this definition.
 *
 *******************************************************************************/

.weak   NMI_Handler
.thumb_set NMI_Handler,Default_Handler

/*...*/

.weak   EXTI0_IRQHandler
.thumb_set EXTI0_IRQHandler,Default_Handler

.weak   EXTI1_IRQHandler
.thumb_set EXTI1_IRQHandler,Default_Handler

/*...*/

This means that the linker found

  1. a reference to C symbol EXTI0_IRQHandler in the vector table,
  2. a (weak but unique) definition of C symbol EXTI0_IRQHandler
  3. a (non-weak but mismatching) definition of a mangled C++ symbol based on EXTI0_IRQHandler.

It matched (1.) with (2.) and discarded (3.) for not being referenced, so the first interrupt produced at the external pin threw the MCU into the endless loop DefaultHandler and the LED stopped blinking.

Upvotes: 0

Related Questions