Asynchronous serial responses
Part of the new functionality that I’m developing for the serial servo controller for my hexapod robot is for the servo controller to take its time in moving a servo from where it is at the moment to where I want it to be. The idea being that the servo controller can worry about the required pacing of the servo moves and we can ‘step’ from the current position to the required position automatically within the servo controller. Since we’re stepping rather than moving directly to the new position in one go we can stop the servo controller mid move if we detect an obstruction. So, rather than saying to the controller “move servo 0 from position 0 to position 127”. We say “move servo 0 from position 0 to position 127 in 1 step increments”. We can then say “no! stop now wherever you are we’ve hit an obstruction!”.
The incremental move command echoes back the command when the command is accepted by the servo controller and then reports back asynchronously when the move completes. Until the move completes we can always fire a ‘stop that servo’ command into the servo controller and it will stop at the point it has reached. The interesting part of all of this is that the servo controller can send back messages to the controlling computer when it decides to. The question is, how do we do this.
At first it may be tempting to have the PWM code send the asynchronous response. After all that’s the code that knows when a movement sequence has completed so it’s easiest for that code to send the response back to the controlling computer. Unfortunately that way lies garbled serial communications. As I mentioned last time, we have two threads of execution; the serial code which runs as the main loop of the program and the PWM code which runs as an interrupt. The problem with having our PWM code send the asynchronous response is similar to our data access synchronisation issue. Since our PWM code interrupts our serial code the serial code could be doing anything when the PWM code runs. The worst possible thing that the serial code could be doing if our PWM code was responsible for asynchronous serial messages is sending a serial message itself.
Imagine we’ve just sent a command to the serial code and it has processed it and is echoing the command back to the controlling computer. The serial code sends the first byte of the command echo and then the timer interrupt fires. The PWM code runs and an incremental move completes so the PWM code sends an asynchronous serial message, it then returns. The serial code continues where it left off and completes its command echo. Unfortunately the controlling computer gets a garbled message which consists of the first byte of the command echo followed by the asynchronous message from the PWM code and finally the rest of the command echo.
To avoid this situation we will have the serial code as the only code that can access the UART and send data to the controlling computer. Now the PWM code needs to communicate the fact that an incremental move has completed to the serial code which can then send the asynchronous message. We can do this by having the PWM code set a register to a specific value when a incremental move has completed and for the serial code to include a test of this register into its serial read loop. If the value is set then the serial code can scan the serial control data, work out which servo has completed its move and send the appropriate message to the controlling computer.
So, how does the serial code know which servo has completed its movement and how does it only send a response once? A servo that has completed a movement will have an actual position which equals its target position and it will have a non zero ‘move every’ value. We can check through the servos for servos that meet this criteria, send the message and set the ‘move every’ value to zero. The serial code can then clear the signalling register.
Allowing the serial code to clear the register opens up a potential hole. Ideally only the PWM code would set and clear the register that it uses to communicate with the serial code. It would set the register when moves were completed during a PWM setup phase and clear the register if it completed all of the setup phases for all banks without completing any moves. Unfortunately this could leave the register set for 20ms and that would cause the serial code to ‘spin’ uselessly looking for completions that don’t exist. By allowing the serial code to clear the register after it has finished sending the notifications for the completions that are currently available we open up the possibility that the serial code might miss some notifications. Imagine that the serial code has started to process completion notifications, it does this by looping over all of the control data, sending completion notifications and updating the control data. If the serial code is some way through this loop and it gets interrupted the PWM could complete some more moves that are earlier in the control data than the where the serial code is currently located. The PWM code then updates the register to say that there are new completions but when the serial code completes its loop it clears the register and will not then be signalled to process the new completions. One solution for this is for the serial code to clear the register before it begins its loop through the control data, that way it will loop again if the PWM code completes move moves.
That’s the theory anyway…