STM32L4A6 - ISR is processed twice - stm32

Issue
I have an input signal that shall trigger an interrupt. The ISR then shall toggle an output pin 24 times.
Having it set up stright forward I am facing the issue that the ISR is executed twice.
System
IDE: STM32CubeIDE v1.10.0
uC: STM32L4A6 (NUCLEO-L4A6ZG)
Pin config:
PC12 (DATA_READY_NEG_EDGE) = External interrupt, falling edge (externally pulled down with 100k and 100n)
PC11 (ADC_SPI_CLK) = Output
Code
The auto code generator pulls out the following code
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
The callback function looks like this (but I don't think that this is part of the issue):
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == DATA_READY_NEG_EDGE_Pin) // INT Source is pin PC12
{
HAL_GPIO_TogglePin(LED_RED_GPIO_Port, LED_RED_Pin); // Toggle LED
for (int var = 0; var < 24; ++var)
{
HAL_GPIO_TogglePin(ADC_SPI_CLK_GPIO_Port, ADC_SPI_CLK_Pin);
HAL_GPIO_TogglePin(ADC_SPI_CLK_GPIO_Port, ADC_SPI_CLK_Pin);
}
}
}
This way the interrupt is falsely triggered twice.
Interchange the sequence of "CLEAR" and "Callback" function call in the IRQHandler, the ISR is correctly only called once. But this is a hack and every time I generate the code it is reverted again, so for sure not a solution.
What issue do I have and what could be the workaround?
Thx for any support!

Related

Does HAL_NVIC_SetPendingIRQ call the ISR to execute?

I am really new to STM32 world so I came across this while reading:
void HAL_NVIC_SetPendingIRQ(IRQn_Type IRQn);
This will cause the interrupt to fire, as it would be generated by the hardware. A distinctive feature
of Cortex-M processors it that it is possible to programmatically fire an interrupt inside the ISR
routine of another interrupt.
I got this from the book Mastering STM32 (by Carmine Noviello page 208). From this I have understood that If we set this pending bit even from the main function, then the interrupt is generated.
So to try this out, I have written this code:
while (1)
{
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);
for(int i = 0; i <10000000; i++);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET);
for(int i = 0; i <10000000; i++);
HAL_NVIC_SetPendingIRQ(EXTI0_IRQn);
}
}
along with this call back function
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_PIN){
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_15);
}
I have programmed GPIO_PIN_0 as source of interrupt and when I press the push button connected to PA0 the Interrupt works perfectly i.e. ISR is executed. To my surprice HAL_NVIC_SetPendingIRQ function doesn't generate interrupt. I don't understand why?
More Info:
I am using STM32F411VET6 DISCO board
I am using STM32CubeIDE to program the board
Thank you #Tagli. I have found the function HAL_GPIO_EXTI_IRQHandler inside stm32f4xx_hal_gpio.c file. Defalult definition was like this:
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
I got why the GPIO was not being toggled. It was the same reason you have commented above.
I have modified to prove it.
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
HAL_GPIO_EXTI_Callback(GPIO_Pin);
if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
Now the callback function is called when the HAL_NVIC_SetPendingIRQ(EXTI0_IRQn); is being called

How to code latch switch function using STM32?

I am looking a code to get the latch switch function using STM32.
The below code which I have tried is working in stm32 but only a push button function without latch.
while (1)
{
if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)== GPIO_PIN_RESET )
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
}
else
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
}
}
Can some one help me to make the GPIOA,GPIO_PIN_5 pin high always on the first press of the button and the GPIOA,GPIO_PIN_5 low always at the second press ?
The function will be similar as in the below video https://www.youtube.com/watch?v=zzWzSPdxA0U
Thank you all in advance.
There are several problems with the code. There is no memory function and you are reading the button at max speed.
This is fixed by sleeping for a period of time to allow for human reaction speed and button noise. You also need a variable to store the previous state.
while (1)
{
if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)== GPIO_PIN_RESET )
{
static bool state = false;
if(state == false)
{
state = true;
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
}
else
{
state = false
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
}
while(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)== GPIO_PIN_RESET){} // wait for button to be released, otherwise it will keep toggling every 500ms
}
delay_ms(500);
}
This is C++ code as it uses bool. int with the values 1 and 0 can be used for C.
What is done is a variable state is declared and kept in heap memory because of the static keyword. (Instead of stack memory which would be destroyed when the scope of the outer if statement is exited) It is initialized to false and then updated when you press the button.
Possible (crude) solution:
#include <stdbool.h>
#define BUTTON_DOWN() (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_RESET)
#define LED(on) HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, (on) ? GPIO_PIN_SET : GPIO_PIN_RESET)
static bool _pressed_before = false;
static bool _led = false;
/* somewhere in main loop */
{
const bool pressed = BUTTON_DOWN();
if (pressed && !_pressed_before) { /* button pressed? */
_led = !_led; /* toggle LED state */
LED(_led);
}
_pressed_before = pressed; /* remember state */
}
Some notes:
Instead of constantly polling the state, you could use an external GPIO interrupt (search for GPIO EXTI). And it is almost always necessary to use hardware debouncing on the button pin (RC filter) and/or use software debouncing to prevent falsely detected edges. - Also: This question is not really STM32 / hardware specific, so you could find more general answers by searching the webs more broadly on these topics.

STM32 BluePill LED flasher acts strange using HAL_GPIO_TogglePin

I have two simple LED flasher programs the flash at 1 sec intervals for my BluePills.One method uses HAL_GPIO_TogglePin to toggle the LED state and the other uses Set and Reset to toggle the LED state. The Set and Reset functions as expected but the HAL_GPIO_TogglePin definitely is working but it has a almost PWM effect part of the time. I have tried both methods on 2 different Bluepills with same results for both.
while (1)
{
if (__HAL_TIM_GET_COUNTER(&htim1) >= 32000)
{
HAL_GPIO_TogglePin(User_LED_GPIO_Port, User_LED_Pin);
}
and
while (1)
{
if (__HAL_TIM_GET_COUNTER(&htim1) >= 32000)
{
HAL_GPIO_WritePin(User_LED_GPIO_Port, User_LED_Pin, GPIO_PIN_SET);
}
else {
HAL_GPIO_WritePin(User_LED_GPIO_Port, User_LED_Pin, GPIO_PIN_RESET);
}
Thanks Codo, you were 100% right. I modified it to
if ((__HAL_TIM_GET_COUNTER(&htim1) == 32000) || (__HAL_TIM_GET_COUNTER(&htim1)==0))
{
HAL_GPIO_TogglePin(User_LED_GPIO_Port, User_LED_Pin);
timer_val = __HAL_TIM_GET_COUNTER(&htim1);
}
Funny thing is I originally followed the Digikey example and it did the same thing.
while (1)
{
// If enough time has passed (1 second), toggle LED and get new timestamp
if (__HAL_TIM_GET_COUNTER(&htim16) - timer_val >= 10000)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
timer_val = __HAL_TIM_GET_COUNTER(&htim16);
}
oh well, for what i want, the Set Reset works better anyways. Thanks

How to setup the standby mode on a STM32F4 MCU running an RTOS and waking it up after?

I like to put my STM32F412 into deep sleep mode and wake it after by pressing a button. This code should run together with an RTOS(Zephyr). So when executing the code, to put the device into deep sleep, other tasks etc. are active.
So I am looking for a bullet proof approach, that makes it sure that the STM32F412 goes to standby and wakeup after.
so far my (not working code):
#define POWER_WAKEUP_PIN LL_PWR_WAKEUP_PIN2
// set PC0 as input gpio
LL_GPIO_SetPinPull(GPIOC, LL_GPIO_PIN_0, LL_GPIO_PULL_NO);
LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_0, LL_GPIO_MODE_INPUT);
// activate EXTI line 0
LL_EXTI_InitTypeDef EXTI_InitStruct = {0};
LL_EXTI_DisableIT_0_31(LL_EXTI_LINE_ALL_0_31);
EXTI_InitStruct.Line_0_31 = LL_EXTI_LINE_0;
EXTI_InitStruct.LineCommand = ENABLE;
EXTI_InitStruct.Mode = LL_EXTI_MODE_EVENT;
EXTI_InitStruct.Trigger = LL_EXTI_TRIGGER_RISING;
LL_EXTI_Init(&EXTI_InitStruct);
// put to standby
LL_PWR_DisableWakeUpPin(POWER_WAKEUP_PIN);
LL_PWR_ClearFlag_WU();
LL_PWR_EnableWakeUpPin(POWER_WAKEUP_PIN);
LL_PWR_SetPowerMode(LL_PWR_MODE_STANDBY);
LL_LPM_EnableDeepSleep();
__WFI();
Its using the stm32 LL HAL. Any ideas what is missing
I found a working solution. It consists of 2 parts:
add an idle thread that calls "__WFI()". In my case I use the main thread of Zephyr and set its prio to the lowest of the system threads. If nothing to do for the system this thread is active and it does nothing else than sleeping.
setup a function that enables the RTC which triggers a wake up event after some time (in my case 1sec). Put the MCU to sleep mode with . Clk the wakeup circut via RTC. After the wakeup event check the wakeup condition is checked. In my case I check the state of the wakeup pin and an other pin. If the condition is full filled, a reset is generated.
Following some code snipes for the ZephyrRTOS:
void rtc_setupDeepsleepWakeUp(bool on) {
if (true == on) {
/*
Programming the wakeup timer
The following sequence is required to configure or change the wakeup timer auto-reload
value (WUT[15:0] in RTC_WUTR):
1. Clear WUTE in RTC_CR to disable the wakeup timer.
2. Poll WUTWF until it is set in RTC_ISR to make sure the access to wakeup auto-reload
counter and to WUCKSEL[2:0] bits is allowed. It takes 1 to 2 RTCCLK clock cycles
(due to clock synchronization).
3. Program the wakeup auto-reload value WUT[15:0] and the wakeup clock selection
(WUCKSEL[2:0] bits in RTC_CR).Set WUTE in RTC_CR to enable the timer again.
The wakeup timer restarts down-counting. Due to clock synchronization, the WUTWF
bit is cleared up to 2 RTCCLK clocks cycles after WUTE is cleared.
note on step 3:
32768Hz -> 32768 decrements per second
now calc the value for the timer
32768/ 16 = 0x800
0x800 -> counter -> 1sec
*/
LL_RCC_EnableRTC();
HAL_RTCEx_SetWakeUpTimer_IT(&rtc.hrtc, 0x800, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
irq_enable(RTC_WKUP_IRQn);
} else {
HAL_RTCEx_DeactivateWakeUpTimer(&rtc.hrtc);
irq_disable(RTC_WKUP_IRQn);
}
}
void power_sleep(void) {
__WFI();
}
#define THREAD_PRIO_idle 12
void power_deepSleep(void) {
unsigned int key;
LOG_INF("preparing device to deep sleep");
power_disableAllPeriphals();
power_clearAllInterrupts();
gpio_enableWakeupButton();
rtc_setupDeepsleepWakeUp(true);
for (;;) {
HAL_SuspendTick();
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
HAL_ResumeTick();
// break condition of low power mode
// button pressed or usb insert
if (
(0 == gpio_get_powerButton_state()) ||
(1 == gpio_get_vbus_state())
) {
rtc_setupDeepsleepWakeUp(false);
sys_reboot(SYS_REBOOT_COLD);
}
}
rtc_setupDeepsleepWakeUp(false);
sys_reboot(SYS_REBOOT_COLD);
LOG_ERR("ahhhh something went wrong");
}
void main(void)
{
power_recoverFromDeepSleep();
LOG_INF("start");
...
LOG_INF("start completed");
LOG_DBG("set main prio to lowest(idle)");
k_thread_priority_set(k_current_get(), THREAD_PRIO_idle);
while(1) {
power_sleep();
}
}

Using signal() inside handler function

I have taken this example from GNU library. And I wonder why they call signal() function twice, first time in main() when setting up the signal handler and second time inside handler function itself.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
/* This flag controls termination of the main loop. */
volatile sig_atomic_t keep_going = 1;
/* The signal handler just clears the flag and re-enables itself. */
void
catch_alarm (int sig)
{
keep_going = 0;
signal (sig, catch_alarm);
}
void
do_stuff (void)
{
puts ("Doing stuff while waiting for alarm....");
}
int
main (void)
{
/* Establish a handler for SIGALRM signals. */
signal (SIGALRM, catch_alarm);
/* Set an alarm to go off in a little while. */
alarm (2);
/* Check the flag once in a while to see when to quit. */
while (keep_going)
do_stuff ();
return EXIT_SUCCESS;
}
Now my code...
void createTimer(long freq_nanosec)
{
timer_t timerid;
struct sigevent sev;
struct itimerspec timerint;
struct sigaction saction;
/* Establish handler for timer signal */
saction.sa_flags = 0;
saction.sa_handler = OnTimer;
sigemptyset(&saction.sa_mask);
sigaddset (&saction.sa_mask, SIGIO);
if (sigaction(SIGALRM, &saction, NULL) == -1) error("sigaction");
else printf("OnTimer handler created\n");
/* Create real time signal */
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGALRM;
sev.sigev_value.sival_ptr = &timerid;
if (timer_create(CLOCKID, &sev, &timerid) == -1) error("timer_create");
else printf("timer ID is 0x%lx\n", (long) timerid);
/* Arm the timer */
timerint.it_value.tv_sec = timerint.it_interval.tv_sec =
freq_nanosec / 1000000000;
timerint.it_value.tv_nsec = timerint.it_interval.tv_nsec =
freq_nanosec % 1000000000;
if (timer_settime(timerid, 0, &timerint, NULL) == -1)
error("timer_settime");
else printf("Timer armed\n");
}
From the man page for signal, we see that when a signal arrives:
first either the disposition is reset to SIG_DFL, or the signal is blocked (see Portability below), and then handler is called with argument signum.
So after the signal arrives, further signals will revert to default behavior. In your sample code, the handler is choosing to re-set the signal handler so further signals will be handled in the same manner as the first.
This is noted in the comment for the function catch_alarm in the code you found.
There are two popular versions of signal, which differ in whether the disposition of a signal is reset to the default when the handler is called, and whether a signal is blocked for the duration of its handler's execution.
The standard says those two behaviors are implementation-defined. The first code sample
void
catch_alarm (int sig)
{
keep_going = 0;
signal (sig, catch_alarm);
}
is assuming that the implementation may reset the signal disposition to the default when the handler is called. That's like calling signal(sig, SIG_DFL) in the first line of the handler. You almost never want that, because the next time a SIGALRM signal comes in, the default action is for the program to be killed. So the handler calls signal(sig, catch_alarm) to re-establish itself as the handler.
Your second code sample
saction.sa_flags = 0;
saction.sa_handler = OnTimer;
sigemptyset(&saction.sa_mask);
sigaddset (&saction.sa_mask, SIGIO);
if (sigaction(SIGALRM, &saction, NULL) == -1) error("sigaction");
uses sigaction, which is generally preferred over signal because you can specify exactly the behavior you want. The standard says
new applications should use sigaction() rather than signal().
When .sa_flags has the SA_RESETHAND flag on, the disposition of the signal is reset to the default when the handler starts, just like in (one version of) signal.
But in your case, that flag is off because you set .sa_flags to 0, so you don't need to write any code to re-establish the handler.