Skip to main content
Graduate II
November 12, 2024
Solved

Delay, multi milliseconds, on the STM32F0, again..

  • November 12, 2024
  • 12 replies
  • 9193 views

where I wrote it among the codes.. // Here I have to wait 500ms.
Can you help me to prevent the timers from being affected by this wait?
Can you give me the codes that I will add. Because I am very confused..

I tried many things but they all stopped the interrupts..

 

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{

	if(htim->Instance==TIM1)
	{
		
	if(input1==0) 
	{
		HAL_GPIO_WritePin(GPIOA, GPIO_Pin_1, GPIO_PIN_SET);
		// Here I have to wait 500ms.
		HAL_GPIO_WritePin(GPIOA, GPIO_Pin_1, GPIO_PIN_RESET);
	}

}

 

 

 

 

 

    This topic has been closed for replies.
    Best answer by Karl Yamashita

    Let's assume you're only looking a 3 inputs and controlling 2 outputs. 

    Create a data structure to represent the look-up table

    typedef union
    {
    	struct
    	{
    		uint8_t data[2]; // byte 0 is current state, byte 1 is last state
    	}Byte;
    	struct
    	{
    		uint8_t pa1:1;
    		uint8_t pa2:1;
    		uint8_t pc13:1;
    		uint8_t :5;
    	}Status;
    }InputStatus_t;

     

    Using EXTI for the input pins, we can use the HAL GPIO callbacks to set the input pin states. I've tested on a Nucleo-G071RB which uses a Rising and Falling callback where some STM's use 1 callback, but concept is the same.  

    /*
     * Description: Update pin status for specific input.
     * Input: pin data structure, pin to update, the pin state
     */
    void GPIO_UpdatePinStatus(InputStatus_t *input, uint8_t pin, GPIO_PinState pinStatus)
    {
    	switch(pin)
    	{
    	case 0:
    		input->Status.pa1 = pinStatus;
    		break;
    	case 1:
    		input->Status.pa2 = pinStatus;
    		break;
    	case 2:
    		input->Status.pc13 = pinStatus;
    		break;
    	default:
    		// invalid pin
    		break;
    	}
    }
    
    /*
     * Description: Functions as a HAL GPIO callback.
     * Note: no sw debounce incorporated.
     * See YouTube video to learn how to debounce push buttons that use EXTI
     * https://www.youtube.com/watch?v=o0qhmXR5LD0
     */
    void GPIO_Callback(uint16_t GPIO_Pin)
    {
    	if(GPIO_Pin == PA1_Pin)
    	{
    		GPIO_UpdatePinStatus(&inputs, 0, HAL_GPIO_ReadPin(PA1_GPIO_Port, PA1_Pin));
    	}
    	else if(GPIO_Pin == PA2_Pin)
    	{
    		GPIO_UpdatePinStatus(&inputs, 1, HAL_GPIO_ReadPin(PA2_GPIO_Port, PA2_Pin));
    	}
    	else if(GPIO_Pin == PC13_Pin)
    	{
    		GPIO_UpdatePinStatus(&inputs, 2, HAL_GPIO_ReadPin(PC13_GPIO_Port, PC13_Pin));
    	}
    }
    
    /*
     * STM32G071 HAL driver uses Rising and Falling callbacks where other STM32's use 1 callback.
     */
    void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
    {
    	GPIO_Callback(GPIO_Pin);
    }
    
    void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
    {
    	GPIO_Callback(GPIO_Pin);
    }

     

    By using a data structure to represent the look-up table, we can use switch-case to determine what state the input pins are. By checking for current pin state versus the last pin state, you can avoid having case 0 being called again. For case 0, PB12 is low, and PA10 is high. A timer callback is started and after 500ms, it turns Off PA10.

    void GPIO_Check(InputStatus_t *input)
    {
    	if(input->Byte.data[1] != input->Byte.data[0]) // check for change, else do nothing.
    	{
    		input->Byte.data[1] = input->Byte.data[0]; // copy current state to last state
    
    		switch(input->Byte.data[0]) // index of truth table
    		{
    		case 0:
    			PB12_Off(); // pin is low
    			PA10_On(); // pin is high
    			TimerCallbackTimerStart(&timerCallback, PA10_Off, 500, TIMER_NO_REPEAT); // start timer to turn off PA10
    			break;
    		case 1: // all other case, PA12 is On (high state)
    		case 2:
    		case 3:
    		case 4:
    		case 5:
    		case 6:
    		case 7:
    		default:
    			PB12_On(); // pin is high
    			break;
    		}
    	}
    }

     

    And the code for the GPIO outputs

    void PA10_On(void)
    {
    	HAL_GPIO_WritePin(PA10_GPIO_Port, PA10_Pin, GPIO_PIN_SET);
    }
    
    void PA10_Off(void)
    {
    	HAL_GPIO_WritePin(PA10_GPIO_Port, PA10_Pin, GPIO_PIN_RESET);
    }
    
    void PB12_On(void)
    {
    	HAL_GPIO_WritePin(PB12_GPIO_Port, PB12_Pin, GPIO_PIN_SET);
    }
    
    void PB12_Off(void)
    {
    	HAL_GPIO_WritePin(PB12_GPIO_Port, PB12_Pin, GPIO_PIN_RESET);
    }

     

    The working code is on Github https://github.com/karlyamashita/Nucleo-G071RB_GPIO_Delay500/wiki

     

     

     

     

    12 replies

    Super User
    November 12, 2024

    It's a bad idea to wait in the interrupt (and the callback is part of the interrupt).

    The proper way to solve this is to set a flag (marked volatile) in the interrupt/callback, and in the main loop then perform the pin writes and delay upon that flag.

    JW

    Graduate II
    November 12, 2024
    HAL_TIM_PeriodElapsedCallback

    or any other ISR callbacks is executed in ISR context and can be interrupted only with ISR with higher priority.

    Then if you place 500ms here any other same or lower ISR is not executed and blocked and main code too.

    Are you sure for this?

    Super User
    November 12, 2024

    HAL_Delay would work here if you set the priority of the timer interrupt lower than SysTick. As @waclawek.jan pointed out, spending long time in an interrupt handler is often considered bad style. Alternatives, besides the already suggested shifting to the main loop, could be: using another hardware timer in one pulse mode for firing a precise pulse in a timely manner or even using a RTOS with software timers for placing events in the scheduler time line.

    In the end, it all depends on the overall design of your code/system.

    hth

    KnarfB

    Super User
    November 12, 2024

    @KnarfB wrote:

    HAL_Delay would work here if you set the priority of the timer interrupt lower than SysTick.


    But could still block other interrupts - depending on their priority.

    @XooM - As the others have said, it is generally a Bad Idea to have blocking delays (especially long blocking delays) in an interrupt handler.

    Here is a better way:

    https://community.st.com/t5/stm32-mcus-embedded-software/how-to-use-hal-gettick/m-p/741355/highlight/true#M56827

    https://community.st.com/t5/stm32-mcus-embedded-software/how-to-use-hal-gettick/m-p/741449/highlight/true#M56836

     

    Graduate II
    November 12, 2024

    Looks like your just want to toggle a GPIO? You can use the Systick to accomplish this. Mix the Systick with a timer callback and you can do many cool things. See this video https://www.youtube.com/watch?v=o0qhmXR5LD0

     

    Graduate II
    November 12, 2024

    As was explained/suggested to you yesterday, in one of your multiple threads on materially the same topic, perhaps just flag that you SET the pin, and have a structure/queue which can down count from 500 in the SysTick / 1 KHz / 1ms task that it needs to RESET the pin when 500ms have elapsed.

    You REALLY need to break this one-dimensional thinking where every thing needs to occur in sequential code execution.

    The CALLBACK is done under INTERRUPT CONTEXT, you shouldn't be dwelling here for 1/2 SECOND.

    If the SysTick has a pre-emption level above this it can continue to count, but like I said doing it this way is just bad thinking.

    Graduate II
    November 12, 2024
    volatile uint32_t Tick_PA1_On; // Time it was turned ON
    volatile int PA1_On = 0; // Flag that is is ON
    
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
     if(htim->Instance==TIM1)
     {
     if(input1==0)
     {
     HAL_GPIO_WritePin(GPIOA, GPIO_Pin_1, GPIO_PIN_SET); // Turn ON PA1
     Tick_PA1_On = HAL_GetTick();
     PA1_On = 1;
     }
     }
    }
    
    ...
    in main loop, or SysTick Handler ??
    
     if (PA1_On)
     {
     if ((HAL_GetTick() - Tick_PA1_On) >= 500) // On for 500ms, turn it off now
     {
     HAL_GPIO_WritePin(GPIOA, GPIO_Pin_1, GPIO_PIN_RESET); // Turn OFF PA1
     PA1_On = 0;
     }
     }
    XooMAuthor
    Graduate II
    November 13, 2024

    Since my English is not very good, sometimes I can't understand what you say very well. I translate but it is not very accurate either. Short code outputs like the ones you gave me now speed up my understanding. Thank you very much. I will turn on my computer and try this in a moment. Now my way of thinking has changed a little.

    XooMAuthor
    Graduate II
    November 15, 2024

    I tried to upload it here. I hope you can see it. 
    The file is attached. How do I fix the errors?

    Graduate II
    November 15, 2024

    @XooM wrote:

    I tried to upload it here. I hope you can see it. 
    The file is attached. How do I fix the errors?


    Look at my main.h file. It has an extern for the timercallback and some includes

    Graduate II
    November 17, 2024

    You must not have read the last sentence in the Wiki? It says you need to add a call to TimerCallbacktick. You should have a file stm32fxxx_it.c in the src folder that has the SysTick_Handler

    void SysTick_Handler(void)
    {
     /* USER CODE BEGIN SysTick_IRQn 0 */
    
     /* USER CODE END SysTick_IRQn 0 */
     HAL_IncTick();
     /* USER CODE BEGIN SysTick_IRQn 1 */
     TimerCallbackTick(&timerCallback);
     /* USER CODE END SysTick_IRQn 1 */
    }

    Yes, the Timercallback.c/h can be used for any microcontroller. You're not limited to the Systick at 1ms.

    You could also create a Timer interrupt for 100us and use both Timercallback instances, one for 1ms, and the other 100us.  

     

     

    XooMAuthor
    Graduate II
    November 17, 2024

    I guess I'm really ***. No matter what I do, it doesn't work.

    TimerCallbackTimerStart(&timerCallback, PA10_Off, 500, TIMER_NO_REPEAT);
    doesn't work..
    PA10_off function doesn't work.

    Graduate II
    November 17, 2024

    Does PA10 turn on?

    Attach your project

    XooMAuthor
    Graduate II
    November 17, 2024

    I couldn't do it with TimerCallback either
    It didn't work like this either..
    If PA2 is 0, PB14 should be 1 and PB14 should be 0 again for 500ms.

    Graduate II
    November 17, 2024

    You uploaded the wrong project. It doesn't have the TimerCallback files.

    Graduate II
    November 17, 2024

    If you removed the files then how am i supposed to see what errors occurred? 

    XooMAuthor
    Graduate II
    November 17, 2024

    I added it in the message above and uploaded it again

    Graduate II
    November 18, 2024

    Your project still looks the same. Your while loop is empty so not much is going to work.

     

    KarlYamashita_0-1731887068373.png

    Your project tree should look like this

    KarlYamashita_2-1731888214248.png

     

    You don't have GPIO set up correctly for the ones that use EXTI. Only the pins that you are polling use Input mode.

    I highlighted from my IOC what it should be like.

    KarlYamashita_1-1731887940217.png

     

    The TimerCallback.c/h and PollingRoutine.c/h just drop in your project. The only thing you need to do is change the GPIO names i've used to the name you used.

     

    XooMAuthor
    Graduate II
    November 18, 2024

    I did everything you said, but the errors are not decreasing.
    Can you look at my project file? Please, I'm stuck and can't continue.

    note1: I recreated the project. I added the file, but I can't solve the errors.

    note2: I enabled NVICs and added the project again.

    Graduate II
    November 18, 2024

    @XooM wrote:

    I don't have a program in the while loop. My program is in timer1 and timer16. When I do what you said, it gives an error, so I added the file.

    I did everything you said but the errors are not decreasing.
    Could you please look at my project file? Please I am stuck and cannot proceed.


    What error? Added what file? Explain in more detail.

    If you're talking about the latest project you've uploaded, did you even look at my main.h file? 

    extern and #include are needed just like i have in my main.h which you need to add to your main.h

     

    .