Reputation: 31
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
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
EXTI0_IRQHandler
in the vector table,EXTI0_IRQHandler
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