I'm using an STM32F4 discovery board for a project and am wondering if I'm approaching the problem correctly. When I press a button, an external interrupt is triggered which runs a routine; without using a delay, this part works fine. As the routine moves a servo then returns it to its original position, a delay is added to allow the servo to catch up with the new PWM output before returning to the original position. When I run the new interrupt routine with the delay, the board locks up. In debug the code appears to stall at the delay loop.
The delay is a simple systick routine. Is it bad practice (thus the reason for my crashes) to put this inside my interrupt, and should I use a different method? For example setting a one shot timer inside the external interrupt routine which returns the servo after a set time?
As you've discovered, interrupt routines are intended to perform quick handling of an external event, and defer additional work to other facilities. This is why, in your case, the delay loop causes the board to lock up: no other work is being performed while the code is sleeping inside the interrupt handler. That is a typical characteristic of all interrupt handlers.
If you need to perform a separate task (moving the servo again), schedule it just like you described:
Assuming you have a spare timer that can generate an interrupt, you can have the interrupt handler setup a timer interrupt handler that starts the next step. I use pointers to functions that are setup and later called by interrupt handlers to advance an interrupt driven process through a sequence of logical steps. In some cases, I'll have a set of hierarchical pointers to functions, where each pointer to function is used to call an end action handler for a logical function. For example, the end action function pointer for a high level function (like a wait for ready function) is set, and the high level function is called. That high level function in turn sets an end action function pointer for a low level function, and calls the low level function to start it. The low level function then sets the interrupt end action function pointer, and starts some type of interrupt driven sequence, advancing the low level interrupt function pointer through a series of interrupt functions to follow a sequence, then calling the high level end action function pointer when the sequence is complete to return back to a step in the high level sequence.
To maintain accurate timing and avoid drift over a period of time, you need to base all "delays" off an original reading of the timer. For each step, you add a fixed count to a variable that was initialized with a original reading of the timer in order to determine the next delay point. For frequencies that aren't an exact multiple of the timer, you can use division to produce a quotient and remainder, accumulating the remainder to round up the quotient as needed for each interval step.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!Donate Us With