STM32 - Handling ISR in assembly using Keil - Page 1
Forum section: electronics / general

author: ppTRN
author_rank: Regular Contributor
timestamp: March 11, 2025, 10:51:30 am
content: Hi everyone,I wrote some code that generate a two square waves, one in phase and the other shifted 90° (quadrature) from a reference signal, feeded in GPIOF0.The code WORKS, but there are some delays in the wave generations and most importantly some GITTER that I want to get rid of.Here's the C ISR for reference:Code:void EXTI0_IRQHandler(){EXTI->PR |= 1<<0;// Clear pending EXTI0 interrupt flagif(acquisition == 0){// First edge - Timer is startedTIM17->CR1 |= 1<<0;acquisition = 1;}else if(acquisition == 1){// Second edge - Result is readyoscTime = TIM17->CNT;TIM17->CR1 &= ~(1<<0);TIM17->CNT = 0;acquisition = 0;}TIM15->CCR2 = K;// First threshold for ITIM15->CCR1 = K+oscTime/4;// First threshold for QTIM15->CNT = 0;// Timer 15 resettedTIM15->CR1 |= 1<<0;// Timer 15 started}This code measures the frequency of the input signal (that may slightly change) and updates two compare values that generates both signals. Again, this is tested and works.Since the ISR handles variables, they are all defined in the main.c asCode:volatile int acquisition = 0;volatile int oscTime = 6000;volatile int K = 50;I want to implement the same ISR in the assembly startup .s fileCode:EXTI0_IRQHandlerPROCIMPORT KIMPORT oscTimeIMPORT acquisitionLDR R0, =K; Address of KLDR R1, ; Value of KADD R1, #1; Increment value of KSTR R1, ; Store new value into KENDPNow, this code appears to be working. I made it only to test if the variable were visible. It just increment K every time the interrupt is triggered.My questions are:- Is it worth it? I mean, do you think there is the possibility to optimize the C code?- Does the ISR follows the APCS standard? What are the register that I need to preserve?

author: xvr
author_rank: Frequent Contributor
timestamp: March 11, 2025, 02:09:36 pm
content: QuoteNoQuoteIn general it is possible, but in your case C compiler should do all optimization itself. Your ISR is quite primitive, and optimizer should handle it well (but don't forget to turn on optimization in compiler)Run ISR source through C comiler to asm (-S option for gcc) and take a look in result - is it anyting to optimize?QuoteAll that your ISR will change.

author: langwadt
author_rank: Super Contributor
timestamp: March 11, 2025, 05:07:13 pm
content: Quote from: xvr on March 11, 2025, 02:09:36 pmonly R4 to R11 the rest is handled automatically

author: ozcar
author_rank: Frequent Contributor
timestamp: March 12, 2025, 01:44:50 am
content: With only the ISR to see, I can't work out what exactly you are doing.Are TIM15 & TIM17 counting at the same rate?You enable the TIM15 counter every time the ISR is called. Does the TIM15 counter ever get disabled?Does "K" ever get changed?

author: Dan N
author_rank: Contributor
timestamp: March 12, 2025, 01:18:01 pm
content: Quote from: ppTRN on March 11, 2025, 10:51:30 amYou didn't give the cpu part # but most likely that will clear all pending EXTI interrupts. Just do: EXTI->PR = 1<<0;To make your timing more consistent use the timer hardware features to replace some of your ISR code. Read your cpu's reference manual to learn how to automatically start counting from 0 on a trigger edge and capture the count value on another trigger edge into a capture register like CCR1.

author: ppTRN
author_rank: Regular Contributor
timestamp: March 12, 2025, 03:30:49 pm
content: Thank you all for you answares. I guess you all could have a more detailed view if I explain better. See image attached for a complete view of the system behaviour. Also, I clearely need to attach also the ISR of the timer that actually switches the pins, not only the one from the timer that measures the input frequency. My bad:Code:void EXTI0_IRQHandler(){EXTI->PR |= 1<<0;// Clear pending EXTI0 interrupt flagif(acquisition == 0){// First edge - Timer is startedTIM17->CR1 |= 1<<0;acquisition = 1;}else if(acquisition == 1){// Second edge - Result is readyoscTime = TIM17->CNT;TIM17->CR1 &= ~(1<<0);TIM17->CNT = 0;acquisition = 0;}TIM15->CCR2 = K;// First threshold for ITIM15->CCR1 = K+oscTime/4;// First threshold for QTIM15->CNT = 0;// Timer 15 resettedTIM15->CR1 |= 1<<0;// Timer 15 started/*Work in progress*///nextQ = K+3*oscTime/2;// Already evaluating the next threshold for Q//nextI = K + oscTime/2;// Already evaluating the next threshold for I}Code:void TIM1_BRK_TIM15_IRQHandler(){if(TIM15->SR & 1<<0){/* **** UPDATE EVENT **** */TIM15->SR &= ~(1<<0);/* ********************** */}else if(TIM15->SR & 1<<1){/* **** QUADRATURE COMPARE MATCH **** */TIM15->SR &= ~(1<<1);if(TIM15->CCR1 == K+oscTime/4){GPIOB->BSRR = 1<<1;// Q signal is highTIM15->CCR1 = K+3*oscTime/4;// Update the compare value}else if(TIM15->CCR1 == K+3*oscTime/4){GPIOB->BSRR = 1<<17;// Q signal is lowTIM15->CR1 &= ~(1<<0);// TIM15 is stopped. It will be started again by EXTI0 TIM17.}/* ********************************** */}else if(TIM15->SR & 1<<2){/* **** PHASE COMPARE MATCH **** */TIM15->SR &= ~(1<<2);if(TIM15->CCR2 == K){GPIOB->BSRR = 1<<21;// I signal is lowTIM15->CCR2 = K+oscTime/2;// Update the compare value}else if(TIM15->CCR2 == K+oscTime/2){GPIOB->BSRR = 1<<5;// I signal is high. Next compare value will be set by EXTI0 TIM17}/* ***************************** */}}Quote from: xvr on March 11, 2025, 02:09:36 pmGot it. I took a look at the disassembled output from a O3 optimization from the compiler. It is quite messed up but I can see that it takes a lot of instruction to evaluate the if conditions of the TIM15 ISR. I think I will not let it calculate the comparison but rather evaluate "by hand" before. See "work in progress" comment on the code.Quote from: langwadt on March 11, 2025, 05:07:13 pmThaught so. But at this point I would rather not do anything in assembler. I hope I will resort to a solution using only C.Quote from: ozcar on March 12, 2025, 01:44:50 amNow I posted also the ISR of the timer that actually uses the compare values to toggle the pins. You can see that TIM15 gets disabled there. My bad for not posting it before.K is constant for now, it will be dinamic in the final version. It is used to shift the generated signal in respect of the reference input signal.Quote from: Dan N on March 12, 2025, 01:18:01 pmThis is a lazy and straightforward solution, I admit that. I hope I can obtain some decent resoult from this approach rather than dive into the timer hardware features. They have a lot of options for waveform generation and i am sure that there is a way of doing what I need purely in hardware, but this would require a lot of studying of the reference manual. I keep that as a last resource.Anyway, I figured that maybe the delays I experience are due to the fact that I evaluate "on the fly" the future CCR thresholds. Maybe if I evaluate them as soon as oscTime is available I will save some time.You can find attached a simple schematic representation of the system signals.EDIT: I am using an STM32F303K8Thanks again for your precious contributions!

author: ozcar
author_rank: Frequent Contributor
timestamp: March 12, 2025, 10:26:48 pm
content: Quote from: ppTRN on March 12, 2025, 03:30:49 pmThat is going to make a much bigger difference than trying to optimise the ISRs.Maybe this will help:https://deepbluembedded.com/stm32-pwm-phase-shift-timer-synchronized-example-code/