cancel
Showing results for 
Search instead for 
Did you mean: 

A few questions on goto-statements, encapsulation, etc

arnold_w
Senior
Posted on December 31, 2016 at 23:25

I am working with the STM32F405 microcontroller and I am trying to have a software UART use the same interrupt line (

EXTI15_10_IRQn) as other interrupts (for example, button press interrupts). In a perfect world, my code would look something like this:

void EXTI15_10_IRQHandler(void) {

    clearInterruptFlag();

    if (EXTI->PR & 0x0400) {

        receiveUARTbyte();

    } else if (

EXTI->PR & 0x0800) {

       handleButtonPress();

    } else if (

EXTI->PR & 0x1000) {

        handleOtherExternalInterrupt();

    }

}

However, I need to run the UART at a high speed (115200 Baud) and the system clock at a low speed (to save power). This means I can't use the stack in the interrupt routine, because pushing stuff onto the stack will make me miss the first UART-bit. So, instead of a calling a function, I need to do something else. We all learned in school that we should try to encapsulate our code as much as possible and that's why I don't want a full software UART implementation inside the interrupt routine. I could create a software interrupt (NVIC->STIR) to a dummy interrupt (with more urgent priority than

EXTI15_10_IRQn

) to transfer control to my UART.c file, but isn't there a lot of overhead associated with that? Could I use a goto instead? If so, can I use a goto without locking my

receiveUARTbyte

() function to a specific address? In other works, can I goto a 'global' goto-label somehow? Other thoughts about solving this and at the same time having well-encapsulated code (except using macros)?

5 REPLIES 5
Seb
ST Employee
Posted on January 01, 2017 at 00:38

For a sw uart, a better way would be to use a timer and its input capture and output compare to generate precise timing and less time critical. You would have a bit time say 9 us to save the rx edge timestamp. Need more hw assist? Use dma on timer capture.

Good luck!

arnold_w
Senior
Posted on January 01, 2017 at 03:10

Thanks for your replies.

Clive One, I don't know what LR and auto variables are. Are you saying I should declare my receiveUARTbyte,

handleButtonPress and 

handleOtherExternalInterrupt

 functions as inline and avoid stack variables inside them and then I can feel confident I will sample the first bit as soon as physically possible?

You are correct about the mutuality, I guess it should look something like this instead:

static uint32_t EXTI_PR_temp;    // Use static variable instead of stack variable to avoid spending time pushing the stack.

void EXTI15_10_IRQHandler(void) {

    // Handle time critical interrupt first 

    if (EXTI->PR & 0x0400) {

        receiveUARTbyte();     // Handle UART packet as soon as possible

        while (

EXTI->PR

0x0400) {     

// Clear interrupt flag for UART receive

            EXTI->PR

0x0400;

        }

    }

    

    // Handle the not so time critical interrupts

    EXTI_PR_temp = 

EXTI->PR;

    while (

EXTI->PR

&

EXTI_PR_temp

)

) {     

// Clear interrupt flags for non-timing critical interrupts

        EXTI->PR

EXTI_PR_temp

;

    }

    if (

EXTI_PR_temp

 & 0x0800) {

       handleButtonPress();   

    if (

EXTI_PR_temp

 & 0x1000) {

        handleOtherExternalInterrupt();

    }

}

Seb Marsanne, the thing is, we want to use low power mode (i.e. turning off the system clock) and as I understand it it's not possible to wake the microcontroller through an input capture interrupt. So, the plan it so send one wake-up byte to turn on the clock and then send the 'real' packet.

Posted on January 01, 2017 at 16:38

https://en.wikipedia.org/wiki/Automatic_variable

 

https://en.wikipedia.org/wiki/Link_register

 

The interrupt already eats 12 cycles just entering, allocating stack space entering scope is 1 cycle, and isn't significantly impacted by the size of the allocation. If you call other functions, LR must be stacked.

EXTI->PR

=

0x0400; // not

EXTI->PR

0x0400; and not clear on the value of looping

I'm not sure why using a local copy of the status bit helps. The bigger issue is perhaps that the interrupt doesn't reenter, so when it is in the lower priority service routines it blocks response to the primary. You could perhaps flags those and defer them to a handler that can be preempted.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on January 01, 2017 at 17:51

Using a GPIO output that toggles when entering the EXTI interrupt, how many SYSCLK cycles between the EXTI event (Start bit) and the beginning of the interrupt? Just to make sure there is no other contributor such as PLL stabilisation time, etc...

The timer will start counting as soon as the clock restart so faster than any SW method.

Besides putting the interrupt vectors and handler routine in SRAM to reduce latency, if feasible, simply not to use interrupt and HALT the core and continue the code once the EXTI event occured?

Good luck !

Posted on December 31, 2016 at 23:59

Well the non macro method is __INLINE, see core_cm4.h

I'm not sure goto is any more efficient than a branch or function call. The lack of function calls within a function will mean it doesn't need to push LR.

In terms of local/auto variables what kills you is initialization. ie for a table of CRC constants, use 'static const' so the thing isn't allocated and copied at each entry.

I will note that interrupts are not mutually exclusive, so I'd avoid the if-then-else-if type construct.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..