2
votes

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++.

1
did you succeed with polling the gpio first before diving into interrupts? after that did you poll the interrupt status before enabling the interrupt into the core?old_timer
Im kinda new to this, so I'm not sure if I understand you correctly. If you ask if i can get the button status and toggle the led with it.. Yes, i can read the input data bit and manipulate the led. However if i add the EXTI / NVIC part i can't do it anymore (the NVIC enabling part is what breaks it - is what i tested). Second question: Do you mean i should set EXTI to DISABLE and read the status with getITStatus function?Viral
Step through your code in the debugger. Does it behave like you expect? Does configure_PA0 return? Set breakpoints on EXTI0_IRQHandler and toggle_PE14. Do those functions ever execute? Do the conditionals within those functions work like you expect?kkrambo
I tried to debug it and noticed that if I add delay(400); toggle_PE14(); in the while loop it will crash when entering delay for the first time and jump into Default_Handler(void). If delete configure_PA0 call the toggling and delay works as expected. The function returns fine it seems, but it causes this infinite loop.Viral
I am saying dont enable interrupts into the core until you can confirm you have the peripheral and other internals setup first (by polling) First is the gpio configured correctly and can you see the button (poll the gpio input) then next configure the interrupt interface, if possible poll that, THEN start working on an interrupt handler. Sometimes there are more layers, poll each until the last step, most important from the beginning to the end understand how to clear the interrupt (long before you actually interrupt the core). increases your chance of success and dramatically reduces the painold_timer

1 Answers

0
votes

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.