This article was previously published on lhexapod.com as part of my journey of discovery into robotics and embedded assembly programming. A full index of these articles can be found here.
My current design is very successful in doing what I set out to do, which was to give priority to the PWM generation aspect of the code. We’re generating a rock solid PWM signal. Unfortunately this is at the expense of the correctness of the serial communications and that just isn’t right (although the original code had these same serial comms problems too!).
I don’t often experience comms corruption problems right now, but the system is relying on luck to some extent and that, IMHO, is a bad design. However, I don’t expect for this new design sketch to see the light of day as actual code until after I’ve finished the final new commands using the old design and, perhaps, after I’ve actually moved on to building the ‘gait controller’ and I have something that walks (or perhaps have something that could make something walk if I had that mechanical part of the equation finished…).
Anyway; I figure that the only way for me to get the serial communications to be robust is to use interrupt driven comms. This needs to have a buffer that is bigger than a single command and it needs to use some form of flow control (and still expect to get more data arriving after signalling that the sender should stop to allow for latency in processing the flow control signal) and it should also deal with the buffer filling… Once I have that, I expect the comms will be as rock solid as the PWM generation as they can’t be affected by the fact that we’re processing a command that has already arrived. The serial comms will have priority over the processing of complete commands.
Unfortunately, getting to that point with the serial comms means that the PWM generation will no longer be rock solid as the serial interrupts could occur at just the point when the timer interrupt should occur for a pulse switch off and that will mean that the PWM signal lasts fractionally longer than it should and therefore has the potential to have a jitter… Although the timer interrupt will have priority over the serial interrupt a timer interrupt can still be delayed by the time it takes for the serial interrupt to complete its work. So, the PWM code will also need adjusting to work in a world where there are more interrupts occurring than just the timer interrupt that controls the PWM signals.
What I figure I should do is this… Firstly I need to factor into the design the fact that there are two kinds of timer interrupt. The first kind, the setup interrupt, is not time critical. The second kind, the pulse switch off interrupt, is time critical. The first kind of timer interrupt can turn interrupts back on as the first thing that it does; it will allow a nested serial interrupt to occur whilst it’s processing. That way it doesn’t block serial interrupts at all and therefore it doesn’t affect the serial comms at all. There’s plenty of time between the setup interrupt handler starting and the point when it needs to set the first ‘switch off’ timer and I’m happy (at this stage at least) to assume that this will work fine even in we’re being bombarded with inbound serial data at max speed for the 9600 link. If I up the baud rate then we may need to get a bit more measurey with this part and work out if we can still assume that we have enough time to do all the required work.
The time critical ‘switch off’ timer interrupts could be protected by a new kind of non time critical timer interrupt… Basically we set the timer for X before the critical moment (where X is more than the length of time that it takes for a serial comms interrupt to process completely in the worst case scenario) and have the timer that goes off at that point turn off serial interrupts and set the new (time critical) timer. We then know that the time critical interrupt can’t be delayed by anything (just like now). Once this interrupt is complete we can turn on serial interrupts again, process any serial data that arrived whilst interrupts were off and all is well. The devil is in the detail (as ever) and there will be some complexity in working out how to deal with time critical interrupts that occur very close together…
Once we have a design where there are only key periods when the timer interrupt can’t be delayed we open the door on being able to disable interrupts completely at some points during the programs execution (i.e. at any point when a time critical interrupt is not pending). Obviously this would be just for short periods of time, but we can then update multi-byte data values by turning off interrupts globally (when we’re allowed). This would allow us to let the serial protocol work in terms of actual pulse times rather than 0-254 values as we would be able to update current and target positions that take more than one byte to represent. A single bit which is set when the non time critical timer interrupt is configuring things for the time critical interrupt can then be used to build a spin lock to prevent global interrupts being disabled at the wrong time…
There’s also one other point where serial interrupts must be disabled and that is when we’re working with the new style serial command buffer that can contain more than just a single command. We will need to turn off serial interrupts to be enable us to remove a processed command from the buffer and shuffle any extra data to the front.
This design ramble was brought to you by several hours driving on the M25, followed by Wells Bombardier English Premium Bitter, some pasta, and an episode of House, so I reserve the right to throw it away when I’m sober… And now back to our scheduled development…