STM32: Unable to exit interrupt handler for UART interrupt - c

I'm implementing a simple UART receive-transmit protocol on STM32F103, the library/boilerplate code I'm using here is LL, not HAL (as HAL includes insane amounts of overhead)
My problem is that after successfully entering the interrupt handler "USART1_IRQHandler" it keeps cycling on forever.
My code is here:
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
int ii = 0;
for(ii=0; ii<4; ii++){
LL_mDelay(40);
LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_5);
LL_mDelay(40);
LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_5);
}
uint8_t cc = LL_USART_ReceiveData8(USART1);
LL_USART_TransmitData8(USART1, cc);
LL_mDelay(130);
//LL_USART_ClearFlag_RXNE(USART1);
//NVIC_ClearPendingIRQ( USART1_IRQn );
/* USER CODE END USART1_IRQn 0 */
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
and in main.c I have:
LL_USART_EnableIT_RXNE(USART1);
while (1)
{
LL_mDelay(300);
LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_5);
LL_mDelay(300);
LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_5);
//LL_USART_EnableIT_TC(USART1);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
The GPIO_Toggle commands are just there to blink an led so I know what's going on. Here's waht happens:
When I power the MC up, it enters the main loop and blinks slowly.
When I send something (~10 bytes) through UART, the led starts blinking fast, indicating that it has entered the interrupt handler. PProblem is that it never stops and keeps spinning around in the interrupt handler.
I've tried using the now commented functions
LL_USART_ClearFlag_RXNE(USART1);
NVIC_ClearPendingIRQ( USART1_IRQn );
either alone or in combination but they have absolutely no effect on anything.
What am I doing wrong?
How can I exit the handler?

Actually everything in your USART interrupt handler is wrong.
You do not check what has caused the interrupt.
If it is RXNE flag you should just load the value from the DR register. You do not need to clear any flags. If it is TXE flag you can store the data to the DR register. You cant clear this flag any other way. If you do not have any data to send you need to disable the TXE interrupt. Otherwise it will trigger continuously.
You can't just read and write the data register when you want to. You need to know if you are allowed to
You should also control the error statuses.
You must not use any delays in the interrupt routines. Keep it as fast as possible.
Do not touch NVIC exept for enabling and disabling the interrupts, as for now you do not know what it is for.

The system time utilised for controlling delays is updated by a periodic sysTick interrupt. If the RXNE interrupt has a higher priority than the sysTick interrupt it won't be handled while you're inside your RXNE IRQ handler, so the time will never increment and your delay end time will never be reached. Depending on how your delay is implemented, it may just put the CPU in a spinlock that can never exit.

Related

Clearing or preventing pending interrupts in an ISR

Summary:
An ISR necessarily causes its own trigger pin to toggle randomly multiple times. These toggles (during the ISR) should be ignored, but aren't, and result in another interrupt to be set as pending and get executed afterwards.
I have a serial bit-bang device that I read via an interrupt. Device has a data pin and a "clock" pin. Data pin is HIGH by default. When the device is ready to be read, it pulls this data pin LOW. After this falling edge, each pulse on the "clock" pin shifts one bit out to the data pin.
An interrupt triggers on the falling edge of the data pin, and ISR bangs 24 bits of data out of the same data pin. Therefore, additional random falling edges on the data pin causes another interrupt to be set as pending. Which triggers immediately after the actual ISR has returned, resulting two consecutive interrupts being run per one "real" interrupt.
I have tried multiple ways to disable interrupts and/or clear pending interrupts, none of which seem to have any effect whatsoever. I suspect that this is because manipulating interrupt related registers is not allowed/or ignored in an ISR.
The device is Atmel ATSAMD21. (ARM Cortex M0+). Code is built under Atmel Studio with optimisation level -Og.I am okay with using ASF and/or SAM libraries/definitions, ARM CMSIS or baremetal register manipulation. Whichever happens to work.
Here is what I tried so far:
void interrupt_cb ( void )
{
// Trying to disable interrupts
// Executed at the beginning of the ISR
NVIC_DisableIRQ(EIC_IRQn);
ext_irq_disable( <pin> );
__disable_irq();
// body
// < code that results in same pin >
// < that the interrupt is triggered >
// < to be toggled randomly. >
// Trying to clear pending interrupts
// Executed just before the ISR returns.
NVIC_ClearPendingIRQ( EIC_IRQn );
NVIC->ICPR[0] |= 4; //probably same as the above
EIC->INTENCLR.bit.EXTINT2 = 1;
}
Or a combination of these commands.

need to use delay in a interrupt function using an STM32F4

so i'm using an STM32F4 based bare bone board (Black Pill) to run a program for my project
i m using the STM32CubeIDE for code generating
Current Overtime cases explanatory
the figure you just saw, is a graph i made simply on paint to explain the post
my project revolve around inductance load protection against short circuits, (doesn't matter but just clarification)
i m using interrupts, where the first interrupt triggers once the current reaches a reference 1 value
second interrupt triggers once the if reaches Value Reference 1
since current noises can't be filtered in my case, I have to avoid the triggering of instruction of int 2
there for I put a delay that is a bit bigger then the noise period (about 100ns)
if delay ended and int trigger is still on (high) , shut down the system (change the output)
if delay ended and int trigger is off (low), keep the system running (keep initial output)
this is the code i came up with so far
enter code here
I believe what you're looking for is a "Timer" and some interrupt handling magic. I will expand a little.
If your interrupt is OFF (in NVIC only, the rest is configured), but an interrupt triggering event occurred, the interrupt will NOT fire (obviously). But if you enable the interrupt in NVIC after that, it will fire immediately.
Example:
You set up a GPIO as input, you setup EXTI (external interrupt) and SYSCFG (binding port to EXTI), basically, you make a rising edge interrupt
In NVIC the corresponding interrupt is OFF
Rising edge happens on GPIO, immediately goes back down to LOW
You enable an interrupt in NVIC
Interrupt fires (even if the input never had a rising edge after NVIC interrupt was turned on)
My idea is the following.
In the interrupt 1 handler, you do 2 things.
Disable interrupt 2 in NVIC
Launch a delay via Timer with interrupt.
When interrupt 1 fires, it immediately disables interrupt 2 and enables timer. The timer eventually fires its own interrupt, where it enables interrupt 2 in NVIC. If interrupt 2 event happened, the interrupt 2 handler will be called immediately. If not, interrupt 2 will not fire.
During all this waiting your MCU is free to do whatever it wants, full interrupt implementation.

While loop inside STM32F3xx HardFault handler does not execute forever as expected

I would like to find the cause of mysterious microcontroller resets that I am seeing in my project. The microcontroller I am using is an STM32F3 device with STM32F3xx driver library.
My setup also includes a J-link Ultra+ debugger, however frustratingly I have not yet figured out how to reproduce the resets. They occur almost always after leaving the device running for 12+ hours, and do not seem to occur when connected on a programming jig designed to interface with the debugger.
I believe that when the resets occur, the HardFault handler will be called. I have a number of these devices, and given I cannot connect them all to the debugger and the resets occur at inopportune times, I would like to be able to send some information via a serial line from the HardFault handler. The serial line would then be observed by an external device.
Further complicating things is the lack of unused UART pins. I am attempting to create a poor man's UART by flicking a GPIO on and off in the hard fault handler, with delays in between. To begin with, I just want to figure out how to flick this LED on and off with 50% duty cycle. My code currently looks something like this:
/**
* #brief This function handles Hard fault interrupt.
*/
void HardFault_Handler(void)
{
/* USER CODE BEGIN HardFault_IRQn 0 */
GPIO_InitTypeDef GPIO_InitStruct = {LED_Pin, GPIO_MODE_OUTPUT_PP,
GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, 0};
HAL_GPIO_Init(LED_Port, &GPIO_InitStruct);
/* USER CODE END HardFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
HAL_GPIO_WritePin(LED_Port, LED_Pin, GPIO_PIN_RESET);
HAL_Delay(10);
HAL_GPIO_WritePin(LED_Port, LED_Pin, GPIO_PIN_SET);
HAL_Delay(10);
/* USER CODE END W1_HardFault_IRQn 0 */
}
}
I am testing this by triggering a divide-by-zero reset. I have disabled all watchdogs yet I am finding that when this while loop is entered, it does not while forever, in fact the device restarts when it first hits a HAL_Delay() command.
Questions:
What might be causing the resets upon reaching HAL_Delay()?
Can I execute instructions indefinitely in the HardFault handler, or is there a limited amount of time I have to do things before a reset occurs?
Can I use timers in the hard fault handler?
Thanks very much for your help.
HAL_Delay(10); uses systick interrupt. When in HF you will not get any interrupts and this function will wait forever and your diodes will not flash. You need to delay another way, for example:
for(unsigned x = 0; x < 100000; x++) asm("");
Can I execute instructions indefinitely in the HardFault handler, or
is there a limited amount of time I have to do things before a reset
occurs?
Yes you can stay there as long as you wish. But no interrupts!!!!
Can I use timers in the hard fault handler?
Yes, but no timer interrupts. Basically, you can use all the peripherals if you wish.
What might be causing the resets upon reaching HAL_Delay
Hard to say. Did you enable watchdog?
Basically do not use HAL in HF as most of the functions use HAL_Delay internally. Program bare registers instead

How do I know which pin is triggering the interrupt if multiple pins share the same interrupt?

I am writing code with the STM323 ide and using the STM32f303re nucleo board. I configured pin PA7 as an interrupt, however it does not have its own interrupt handler like previous development boards i've worked with.
As you can see, this one interrupt handler handles interrupts from pins 9-5, thus if any of these pins are triggered they will call the same interrupt. I need to be able to perform different functions depending on which pin is triggered. Is there a way to know which specific pin was causing the interrupt?
You can use EXTI_GetITStatus, to check which line causing interrupt.
/* Handle PA7 interrupt */
void EXTI9_5_IRQHandler(void) {
/* Make sure that interrupt flag is set */
if (EXTI_GetITStatus(EXTI_Line7) != RESET) {
/* Do your stuff when PA7 is changed */
/* Clear interrupt flag */
EXTI_ClearITPendingBit(EXTI_Line7);
}
}
Do not forget to clear flag after.
For the cases when multiple pins share an interrupt, when the interrupt fires, you need to check what pin specifically caused an interrupt. Unlike pin 1 interrupt, where the interrupt itself means it was pin 1 and you can process it right away, in this case an interrupt means "it's either pin 5, or 6, or 7, or 8, or 9", so in the ISR you need to check "was it pin 5? or 6?..."
I think it's a good opportunity to look directly into the registers of EXTI peripheral.
If you open the reference manual of your MCU on page 299, section 14.3.6, you can see this EXTI_PR1 register holds pending bits for lines 0..31. Those bits are marked as rc_w1, which from the start of the document means (reference manual, page 46):
read/clear (rc_w1)
Software can read as well as clear this bit by writing 1. Writing ‘0’
has no effect on the bit value.
So the logic is the following: if an interrupt of lines 5..9 occurred, you need to check what bit specifically in that register is set to 1, and then you write 1 there to reset it. This will clear the flag.
void EXTI9_5_IRQHandler(void)
{
if(EXTI_PR1 & (1U<<5U)) //check if it's line 5, returns 0 if PR5 is 0, otherwise returns non-zero, which is true
{
EXTI_PR1 |= (1U<<5U); //write 1 to that bit to clear it so interrupt doesn't fire again once ISR is finished
do_stuff_if_it's_pin5();
}
}
Alternatively, instead of 1U<<5U you should be able to use EXTI_PR1_PR5, so the code would look a little easier to read, like this:
void EXTI9_5_IRQHandler(void)
{
if(EXTI_PR1 & EXTI_PR1_PR5) //check if it's line 5, returns 0 if PR5 is 0, otherwise returns non-zero, which is true
{
EXTI_PR1 |= EXTI_PR1_PR5; //write 1 to that bit to clear it so interrupt doesn't fire again once ISR is finished
do_stuff_if_it's_pin5();
}
}
This is what the functions provided by #Lime7 do behind the scenes, I suspect (can't check, but it makes logical sense).
I don't have that microcontroller to test it, but it should work.
Other answers already covered the interrupt flags. I'm going to add some info regarding CubeMX and HAL, since it looks like you are using them.
When you enable a GPIO interrupt in CubeMX, the code generator adds a line in the appropriate IRQ handler function. When multiple interrupts are enabled, the generated code looks like so (comments removed):
void EXTI9_5_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_7);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_8);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9);
}
The HAL_GPIO_EXTI_IRQHandler() function checks the interrupt status for the given pin and calls HAL_GPIO_EXTI_Callback() function if the flag is set.
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);
}
}
In the HAL flow, you don't typically write code directly in the IRQ handler, but implement the HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) function. The pin that triggered the interrupt is provided as a parameter for your convenience.

How do I reset the STM32 HAL UART driver (HAL) state?

I know one can enable a UART receive interrupt using
HAL_UART_Receive_IT(&huart2, (uint8_t *)rx_buffer, expectedNumberOfBytes)
But once started how does one stop it, "manually"?
We can disable the UART interrupt using HAL_NVIC_DisableIRQ() (ex: HAL_NVIC_DisableIRQ(USART1_IRQn)). This will prevent it from raising an interrupt, but the state set by the function HAL_UART_Receive_IT which is HAL_UART_STATE_BUSY_RX needs to be set back to HAL_UART_STATE_READY for the uart handle to go back to a state that can accept a new HAL_UART_Receive_IT() call.
Question
How do I reset the state of the UART interrupt if I wish to disable a Rx interrupt after some time?
Stack Overflow questions do not address how to reset the state; I have referred to these questions:
Disabling interrupt in interrupt handler STM32F407
https://electronics.stackexchange.com/questions/100073/stm32-usart-rx-interrupts
I could use USART_ClearITPendingBit() or USART_ITConfig() but these are defined as private functions by STM's HAL library. So should I use them?
How [do I] reset the state of the UART interrupt if [I] wish to disable a Rx interrupt after some time[?]
(See it's usage in "stm32f4xx_hal_uart.c", for example.)
The huart->RxState member of a uart handle struct is really only used internally by the HAL library when doing things such as HAL_UART_Receive(), HAL_UART_Receive_IT(), HAL_UART_Receive_DMA(), (and many other internal functions like this), etc. If you manually implement your own interrupt-based and ring-buffer-based UART Tx and Rx calls, however, which is the preferred way to do it, this member is completely meaningless and it doesn't matter what you do with it, as it is used only inside HAL library function calls and HAL ISR handlers (neither of which you have to use), and really has nothing to do with the register-level interrupts and things directly.
By digging around the source code in stm32f4xx_hal_uart.c (for example), however, here are a couple valid options you can use:
1. How to reset huart->RxState to HAL_UART_STATE_READY:
Call HAL_UART_Init(). By inspecting its source code, you'll see it calls huart->RxState= HAL_UART_STATE_READY; before returning.
Just manually set huart->RxState = HAL_UART_STATE_READY; So long as you know you have properly stopped the interrupt-based receive in the middle of its processing, this is perfectly valid.
Let's take this further, however.
Imagine you are using UART7 on an STM32F4. Therefore, in your stm32f4xx_it.c interrupt handler file, you'll see the following code auto-generated by STM32CubeMX:
/**
* #brief This function handles UART7 global interrupt.
*/
void UART7_IRQHandler(void)
{
/* USER CODE BEGIN UART7_IRQn 0 */
/* USER CODE END UART7_IRQn 0 */
HAL_UART_IRQHandler(&huart7);
/* USER CODE BEGIN UART7_IRQn 1 */
/* USER CODE END UART7_IRQn 1 */
}
Let's go over some layers of disabling/enabling interrupts.
2. From broadest to narrowest scope, here are several ways to disable/enable the USART Rx interrupt:
You can disable/enable ALL interrupts, including this UART7_IRQHandler(), using these ARM-core CMSIS calls:
__disable_irq();
__enable_irq();
Source: https://stm32f4-discovery.net/2015/06/how-to-properly-enabledisable-interrupts-in-arm-cortex-m/
So, you could do the following to disable the interrupt, reset the RxState, and then start up the interrupt-based receive again when ready:
__disable_irq();
huart7->RxState= HAL_UART_STATE_READY;
__enable_irq();
HAL_UART_Receive_IT(&huart7, (uint8_t *)rx_buffer, expectedNumberOfBytes);
You can disable/enable ONLY the UART7_IRQHandler() interrupts (all 10 types of uart7 interrupts connected to this interrupt vector, including Tx-related, Rx-related, error-related, etc), using these STM32 HAL calls:
HAL_NVIC_DisableIRQ(UART7_IRQn);
HAL_NVIC_EnableIRQ(UART7_IRQn);
Then, do the same as just above except use these calls to disable/enable the interrupts instead.
If you dig down into the implementation of HAL_UART_IRQHandler(), however, which is called by UART7_IRQHandler(), you'll see that it only calls the interrupt-based receive handler, UART_Receive_IT(), if both the USART_SR_RXNE bit ("Receive Not Empty", inside the USART Status Register) and the USART_CR1_RXNEIE bit ("Receive Not Empty Interrupt Enable", inside the USART Control Register 1), are both set. The RXNE bit is set whenever a byte comes in, and is cleared whenever you read the data register or write a zero to it. The interrupt-enable bit is something you have full control over to disable this UART receive interrupt, and if you clear this bit manually, you will disable the receive interrupt withOUT disabling any other type of interrupt associated with this USART. This is the best way to do it, as there are 10 interrupt sources associated with this UART. In other words, clearing this bit not only causes the check inside HAL_UART_IRQHandler() to fail, but it also prevents the receive interrupt from happening in the first place! Refer to the Reference Manual RM0090 Rev 16, for example:
p969:
p1009:
p1011:
p1015:
p1013:
So, to disable/enable the USART Receive Not Empty interrupt only, do the following. Refer to the Control Register (USART_CR1) on p1013, shown just above.
// Disable the USART Receive Not Empty interrupt
CLEAR_BIT(huart7.Instance.CR1, USART_CR1_RXNEIE);
// Enable the USART Receive Not Empty interrupt
SET_BIT(huart7.Instance.CR1, USART_CR1_RXNEIE);
Now, you could do the following to disable the USART Receive interrupt, reset the HAL RxState, and then start up the interrupt-based receive again when ready:
CLEAR_BIT(huart7.Instance.CR1, USART_CR1_RXNEIE);
huart7->RxState= HAL_UART_STATE_READY;
SET_BIT(huart7.Instance.CR1, USART_CR1_RXNEIE); // This call isn't actually necessary, as this bit is set inside `HAL_UART_Receive_IT()` as well
HAL_UART_Receive_IT(&huart7, (uint8_t *)rx_buffer, expectedNumberOfBytes);
3. How to (awkwardly) use HAL_UART_Receive_IT() for continual interrupt-based receiving.
TODO
4. Why HAL_UART_Receive_IT() really isn't a very useful function after-all.
TODO
5. How to manually configure your own interrupt-based UART Tx and Rx ISRs and functions.
TODO
You can use HAL_UART_Abort_IT.
Most UARTs clear any pending Receive interrupt when the program reads from the holding register. So my answer would be: simply read the data register after disabling interrupts, and ignore the result.
I haven't had a chance to try this on my STM32 yet, but...
There is a function static void UART_EndRxTransfer(UART_HandleTypeDef *huart)
in the HAL library that does the following:
Disable RXNE, PE and ERR interrupts
restore huart->RxState to Ready
I found that function in the stm32f7xx_hal_uart.c file. However, it is defined as static, so I just copied over the definition into the file where I used it. It might be a bit hacky but it worked for me.

Resources