2022-07-24 03:30 PM
I have an app with a pretty simple non blocking state machine that works pretty well. There are a bunch of UART and I2C devices along with a couple of timers that all have ISRs. When an ISR gets called, it puts its data along with an identifier and a timestamp in a FIFO queue. The main state machine processing loop gets any data in any FIFO, does a bunch of calculations, sends commands to various other devices and then does a HAL_Delay(***) for 10 to 1000 milliseconds depending on what state it's in and goes through the cycle again. Power consumption is important, so instead of HAL_Delay I'd like to drop into a low power state and wait for the next interrupt to fire, handle the interrupt and then wake up the main state machine processing loop. In order to do that, I think I need to fire another interrupt from any ISR that needs to wake up the main state machine processing loop. I would also like to post a message somewhere that tells the main state machine processing loop what ISR generated the software interrupt. Is there a way to generate an interrupt from software that also includes some kind of ID that identifies where the interrupt came from? Maybe just a couple of ID characters that I define. Thanks
Solved! Go to Solution.
2022-07-24 10:56 PM
If MCU is in sleep mode, an interrupt will wake it up and the IRQ handler is running, processing input data. When the IRQ handler returns, the MCU stays awake and continues execution of the main loop (unless you use the special sleep on exit feature). So if the main loop reads fro m the FIFO until empty, that should work. When the main loop is done, it sets the MCU to sleep mode again using WFI.
For delays, HAL_Delay is subotimal because it needs a periodic interrupt (waking up the MCU at each tick). You can replace it by using a hardware timer and suspend/resume the SysTick on sleep/wakeup.
Depending on the overall complexity and dynamics of your code you should also consider using an RTOS.
hth
KnarfB
2022-07-24 09:14 PM
The proposed scheme is probably going to be a dead end. It seems to try to implement a queue of events windows like which desfroy the critical timings an interrupt is built for, not just to wake up from low power mode.
All time consuming tasks should be in main loop and not time critical. They will need sw fifo from sources and destinations. Grouping them with id (nvic source id) requires time to dispatch, and priority is lost. You could implement an interrupt based state.machine kicked by interrupt sources. Fpr power saving, how about gear down the core frequency in 1/2 or 1/8 as first step, if peripheral clocks are independent of core's?
2022-07-24 10:56 PM
If MCU is in sleep mode, an interrupt will wake it up and the IRQ handler is running, processing input data. When the IRQ handler returns, the MCU stays awake and continues execution of the main loop (unless you use the special sleep on exit feature). So if the main loop reads fro m the FIFO until empty, that should work. When the main loop is done, it sets the MCU to sleep mode again using WFI.
For delays, HAL_Delay is subotimal because it needs a periodic interrupt (waking up the MCU at each tick). You can replace it by using a hardware timer and suspend/resume the SysTick on sleep/wakeup.
Depending on the overall complexity and dynamics of your code you should also consider using an RTOS.
hth
KnarfB
2022-07-25 09:23 AM
@KnarfB That makes sense and gives me another way to think about this. I'll have to write some test code to better understand the timing.
For example, if I've fired off a HAL_UART_Receive_DMA(&gps1UART, gps1RXBuffer, 256) and go to sleep is the sequence something like this? 1) a character shows up at the UART and wakes up the MCU (I'm using a STM32H7A3 by the way) 2) the ISR for HAL_UART_Receive_DMA() puts the character in memory, 3) If the number of characters received == 256 the HAL_UART_RxCpltCallback() function fires and it can put a message on a FIFO the main state machine processing loop can read. 4) When all the interrupts are handled, the main loop can check the FIFO to figure out what it needs to do.
Does that sound about right?
2022-07-25 10:35 PM
In sleep mode (note that there are other low power modes), the ARM Cortex-M core sleeps (is not clocked), but the peripherals (USART, DMA, ...) do not sleep. and, DMA transfer from USART to memory buffer is completely in hardware, without using code, no ISR, no core wakeup. Only when DMA finishes (or errors), the core is woken up and continues execution. In you example, this will be after 256 chars received.
Note, there is also HAL_UARTEx_ReceiveToIdle_DMA which stops as soon as the serial line is idle. This is useful for receiving messages of unknown length.
2022-07-25 11:36 PM
@KnarfB I'm aware of the Idle line interrupt and the STM32H7A3 that I'm using also has a character match interrupt so I can end a DMA transfer when the terminating line feed character shows up. Thanks for all the help.