How to code latch switch function using STM32? - 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.

Related

STM32L4A6 - ISR is processed twice

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!

How to make LED blink at second press on push button?

I am very new to stm32, I have STM32F411RE Nucleo and I work in STM32CubeIDE. First I need to turn on the LED with the first click on the push button, and the LED stays on until the next click. On the second click, the LED starts blinking with a frequency of 10 Hz, then I need a third click, a fourth, and so on. I have written the code for the first one and it works but I don't know how to make the LED do something with every click on the push button. I don't know how to write the program so it recognizes when a button is pressed for the second, third, or fourth time...
So far I have this:
// read the status of the GPIO button pin
button_val = HAL_GPIO_ReadPin(GPIOC, B1_Pin);
// check if it is high or low
if(button_val == PRESSED) // the button is pressed
{
// if pressed - turn led on
HAL_GPIO_WritePin(LD2_GPIO_Port,LD2_Pin, SET);
// if not pressed - turn led off
// HAL_GPIO_WritePin(LD2_GPIO_Port,LD2_Pin, RESET);
}
When I run this the LED lights up when I press the button. Then I have this code for the second press on the button but it doesn't work.
if(button_val == SECONDPRESSED)
{
// turn led on
HAL_GPIO_WritePin(LD2_GPIO_Port,LD2_Pin, SET);
HAL_Delay(1000);
// turn led off
HAL_GPIO_WritePin(LD2_GPIO_Port,LD2_Pin, RESET);
HAL_Delay(1000);
}
This is a general programming question and not really a STM32-specific issue, since the necessary STM32 hardware input/output seems to work already in your case.
In the STM32F4xx HAL driver code it can be seen that HAL_GPIO_ReadPin() returns a value of type GPIO_PinState, which can be in two states, GPIO_PIN_SET or GPIO_PIN_RESET, GPIO pins are either in HIGH or LOW state.
Now, to make the program cycle through different program states becomes a matter of software programming, there are no hardware specific functions involved. To realize the desired behavior, a very basic state machine could be used for example, using the following states:
typedef enum {
PROG_STATE_INIT = 0,
PROG_STATE_FIRST_PRESS,
PROG_STATE_SECOND_PRESS,
PROG_STATE_THIRD_PRESS,
PROG_STATE_FOURTH_PRESS,
_PROG_STATE_COUNT
} prog_state;
Then, in the main loop, the program could cycle through the states for example like this:
static prog_state _prog_state = PROG_STATE_INIT;
static GPIO_PinState _pin_state_prev = GPIO_PIN_RESET;
const GPIO_PinState pin_state = HAL_GPIO_ReadPin(GPIOC, B1_Pin);
if (pin_state == GPIO_PIN_SET && pin_state != _pin_state_prev) {
++_prog_state; /* next state */
if (_prog_state >= _PROG_STATE_COUNT) {
_prog_state = PROG_STATE_FIRST_PRESS;
}
}
_pin_state_prev = pin_state;
switch (_prog_state) {
case PROG_STATE_INIT:
/* do nothing here, LED is already off */
break;
case PROG_STATE_FIRST_PRESS:
/* turn LED on and let it stay on */
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
break;
case PROG_STATE_SECOND_PRESS:
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
HAL_Delay(50); /* ~10 Hz blinky */
break;
case PROG_STATE_THIRD_PRESS:
/* TODO */
break;
case PROG_STATE_FOURTH_PRESS:
/* TODO */
break;
case _PROG_STATE_COUNT:
default:
/* unexpected */
break;
}
Like already mentioned in the comments above, software/hardware button debouncing might be needed. But for this exercise, it can also be OK to simply add a short delay to the main loop, so that fast GPIO state changes have no effect.
And to expand on the exercise, GPIO EXTI interrupts could be used to react to GPIO state changes. And it might be interesting to set up a timer/counter to let the LED blink precisely at 10 Hz, and control it via the state machine.

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

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

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.