cancel
Showing results for 
Search instead for 
Did you mean: 

ATOMIC_SET_BIT() stopping code execution

DavidNaviaux
Senior II

HI

I am using the STM32G474 MCU and ran into a problem with my code stopping execution.

In my application, I have a level 0 priority interrupt that takes 1.34us to execute that occurs once every 6.66us.  In the final code, I will need 3 of these running, 1 every 2.2us (still leaving about 37% of the MCU bandwidth for other tasks).

When I have 2 of these interrupts every 6.66us, all seems to work fine, but when I add a third, my code stops executing.  After a lot of debugging, I have found that my code is stuck at the following line of code:

 

      ATOMIC_SET_BIT(huart->Instance->CR3, USART_CR3_TXFTIE);

 

 

in the HAL_UART_Transmit_IT() function.  Normally, when writing low level interrupt handlers for serial I/O, when I need to change the UART interrupt flags, I would disable interrupts make the change then enable the interrupts again.  Doing that, it is very clearly safe for a single core MCUs like the STM32G474.  However, when I examined the STM driver code for this function, it uses the ATOMIC_SET_BIT() macro that I am not familiar with.

I have tried to switch to DMA serial I/O but it had the same problem; it worked with 2 1.34us interrupts every 6.6us but not with 3.

I'd like to not use this macro for modifying the UART interrupt flags, but it is in the driver file supplied by STM and when I modify it, it forgets my modifications the next time I compile.

Could someone explain what is going on here, and even better, how I can solve my problem?

 

13 REPLIES 13

1. To properly control the DSMPS PWM duty cycle, I need to update it before every PWM pulse.  To do this, each PWM pulse triggers the ADC in DMA mode to read the driver's output voltage and current that generates an interrupt.  The interrupt handler uses those to pass through a 2-pole, 3-zero filter function to come up with the next PWM pulse duty cycle.  The filter function has 7 floating point multiplies and a couple duty cycle limiting comparisons.  From Flash, it takes 1.47us to execute using the filter function defined to be an inline function.  It is critical that the ADC sampling be very accurately timed with the PWM pulses and that the calculation for the next PWM pulse happen before the next ADC interrupt. 

2. My UART is used to process Modbus commands at 230.4kHz over a half-duplex RS485 line.  Generally, this requires 3 ISRs, one for receiving data, one for transmitting data, and one to disable the RS485 driver after the final character is transmitted.  Yes, it's not much code but it is very easy to make a mistake.  I know that the STM32G474 MCU has a Modbus capability, but it will be simpler to just use it as a standard UART.  I may take advantage of its RS485 driver enable capability though.

3. I realize that much of the HAL interrupt handling is to allow the same function to be used whether you are using interrupts or DMA.  For the UART, will only be using interrupts, so it should not be too complicated.

Thank you.

KnarfB,

It takes about 1.49us to execute the ADC filtering code to calculate the next PWM duty cycle.  The tight timing requirement between the PWM pulses and the ADC requires that this be handled in the highest priority interrupt.  I have 3 separate drivers, each with their own PWM output.  The PWM pulses for the three drivers are guaranteed to not overlap.  I don't have a lot of experience with time yet on this MCU, but I thought 1.49us for all of that was very fast.  I'm working on moving the interrupt callback function to CCM RAM as suggested.  No luck yet, but I am anxious to see how much faster it will be when executing out of CCM RAM.

I'm assuming that the floating point registers would have to be saved before the interrupt code is executed since I am using floating point extensively within my non-interrupt related code.  The three DMA transfer complete interrupts handle the DMA relates stuff then all call the same callback function that I am relocating to CCM RAM.  There is not much overhead in the HAL_DMA_IRQHandler() that I can see.

I'll review the FPU tips you included in the link.

Thanks again,

David

 

I have successfully moved the callback function to CCM SRAM and it cut the execution time almost in half, to 790ns (from 1.47us).  It now works at 150kHz PWM frequency without any UART custom code.

Thanks all for helping.  I still have some optimization that I can do, but I am happy that it works as I had planned.

- David

DavidNaviaux
Senior II

Since I thought that might be useful for someone with similar problems, I will summarize the ultimate solution inspired by the responses from TDK, gbm, and knarfB.

Design summary:

1. This design is a driver for an endoscope.  It allows the color temperature (in degrees Kelvin) of the emitted light and intensity to be specified and will adjust the current in the three power LEDs (white, red, and blue) as required.  Regulated currents can be set from 0.1A to 15A, each being regulated by its own synchronous buck converter.

The PWM signals for the drivers are non-overlapping (120 degrees out of phase from each other).  The PWM frequency is 150kHz.  The regulators use voltage mode control (as opposed to peak-current mode control) since transient response is not important.  The control algorithm required uses a 3-pole, 3-zero digital filter with coefficients determined by special software from Biricha Digital Power LTD (https://biricha.com/ ).

Problem:

1. The problem was that in my original firmware, there was only about 33% of the MCU bandwidth left over for none-DSMPS functions (790ns every 2.2us) and USB, the UART, and writing to flash did not work when all three drivers were on at the same time.  With a lot of debugging, the problem turned out to be related to the ATOMIC_SET_BIT() macro used by STM which was locked in an infinite loop which was the original reason for the post.

Solution:

1. moved the ADC DMA interrupt handler to CCM RAM which increased the available MCU bandwidth to 64%.  This allowed the USB and UART to function correctly.  However, I was still not able to write to flash. The flash is used to store user programmable options.  

2. Only allowed writing to flash when the endoscope not emitting light.  When not emitting light, the time intensive digital filter is not being executed which leaves almost 100% of the bandwidth available.

3. Future improvements that I would like to implement, write my own low level UART driver to avoid using the time intensive HAL UART functionality.

Thanks to TDK, gbm, and knarfB for all of their help.

-David