This is a place where you can find some modifications that I've made to the Mechaduino closed loop stepper motor driver from Tropical Labs.
Like a few others, I decided to take a stab at doing some optimization on the firmware. It didn't make sense to me that one would have to resort to fixed point math to reduce the execution time on an ARM processor. To that end here is what I've changed to make it run faster, and to clean up the codebase:
- Removed any redundant unused variables that I could find. I should have used splint to do this, as it would be more thorough, but I don't have it set up just now. This doesn't really change much, other than make it easier for someone looking at the code to figure out what is going on.
- Moved global variables inside functions, if they didn't need to be global. Declaring everything that an interrupt routine touches as volatile really hampers the optimizer. Normally, I would only declare something as volatile if it is modified by the interrupt routine. Note that declaring a variable as volatile does not guarantee atomic access to it. This must be handled by the programmer. So if the main line code needs to modify a variable that is used by the interrupt handler, interrupts must be disabled, unless you can guarantee that the write is atomic. I haven't checked to see if this happens anywhere, and usually one has to look at the assembler output to be sure.
- It is not a very good practice to declare everything as global, as the scope of variables should be as limited as is reasonable. Only use a global if it really needs to be used in a bunch of functions (and you don't want to pass it, or it comes from an interrupt), otherwise, declare it locally, so it is put on the stack. This makes debugging much easier, and makes the code more readable.
- Changed the SPI clock for the position sensor from 400 kHz to 10 MHz. This eliminates a bunch of wasted cycles in the PID control loop.
- Changed any critical I/O writes (most of them) to use macros that manipulate the pins directly, rather than using the really slow Arduino library.
- Turned on optimize for speed options on the interrupt handler by using an attribute on the function.
Further optimizations that could be done:
- Using #defines, instead of const variables would be a further improvement in a bunch of places. I haven't delved into this too much yet, but again the compiler can optimize constants used in this way better. I'm not sure where this const declaration of numerical values method came from, though it does seem common in Arduino code, but most C programmers would never do this.
A few other notes:
- Reducing floating point operations helps a great deal.
- Floating point adds, subtracts and compares, are really time consuming compared to multiplies and divides. Avoiding them if possible really helps.
- It would be nice to know where some of the magic numbers come from. I've figured out some of them, and replaced them with the actual calculations instead. One I still don't understand is 0.8726646, which is used in the output() function.
- I've noticed that if I force the motor position off of the setpoint quickly, that it will continue spinning in the wrong direction until stopped. If you then stop it spinning, it will slowly return back to the setpoint. This happens on the original firmware as well. I'm not sure what would cause this, and it might be a result of bad PID settings, though the behaviour seems odd to me.
- I'd really like to know what determines the speed of the motor when returning to the setpoint position. It seems very slow to me, considering that the motor I'm testing on is unloaded.
- There are a couple of I/O pins that are toggled in the PID control code, that I used for benchmarking the code. These can be disabled in Parameters.h, by commenting out the ENABLE_PROFILE_IO definition.
The firmware still uses full floating point, and all functionality has been retained, but I've only really tested the position mode. The net result, is that the execution time of the PID interrupt handler has been reduced from 268us to under 150us. This allows the PID interrupt to be run at 6 kHz, rather than the original 3 kHz, though this doesn't leave much time left over for the main code.
I'm not really much of a github user, so the modified firmware can be downloaded from here: Mechaduino_01ma.zip, if you would like to try it out.
Donate
If this project has been useful to you, I would encourage you to make a donation. This will give me incentive to keep this updated, and to make changes.
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 License