Thursday, 7 April 2011

Servo controller with eeprom and playback

Having already successfully create a simple servo board with eeprom and playback we looked at how to improve on a relatively simple design.

We've decided to use a larger eeprom, Atmel's AT45DB041D is a massive 4Mbit device (512kb) with a simple-to-use SPI interface. There are other eeprom chips we could have used, but we are already familiar with this one, from a previous project, and how it's memory space is laid out and addressed (256-byte pages).

In the original servo controller, we had problems getting the playback to sync up with the onscreen editor - mainly because of the way playback was coded.
In this board, we're going to use the same approach for playback as we did for the parallel servo processing. That is, we're going to keep a timer running and compare it's value to a required value. When the running timer value exceeds the required value, we will perform an action. Because we already have a timer raising an interrupt every millisecond and we monitor for 20ms having passed (when all the servo pins are reset to start their repeating signals) it makes sense to use this interrupt for playback timing too.

Simply put, every time we reset the servo output pins, we'll increase an internal (two-byte) counter. So our counter increases by one every 20ms. Using a two-byte counter, that goes up to 65,535 this means we can create playback animations that are 65535/50 = 1,310 seconds in length (almost 22 minutes).

In eeprom, we store animations as a series of servo commands, written as four-byte instructions:
  • The first two bytes are the "timecode" of when the animation is to play - i.e. the counter number when the next servo needs to move (so at every 20ms or 50hz, to move a servo after 1 second, the counter number will be 50. To move another servo after 3.4 seconds, the counter number will be 3.4*50 = 170, and so on).
  • The next byte is the servo number to move
  • The final byte is the degree of movement required.

During the processing part of the main code loop, we can read four bytes from eeprom and compare the current timecode counter (which is updated on an interrupt) to the required timecode for the next servo. When the current timecode counter value exceeds the servo movement timecode, we move the servo in accordance to the other two bytes. When this is done, we can read another four bytes from eeprom.
In doing this, we can create an animation by listing which servos need to move by how much and after how much time. If two or more servos need to move together, the instructions would actually be carried out sequentially (one after the other) but the delay between them would be so small, as to appear to have been carried out at the same time.

Example data for animation playback:
50,1,90 (after one second, move servo one to extreme left)
100,2,120 (after two seconds, move servo two to about 30 degrees)
150,3,200 (after three seconds, move servo three to extreme right)
200,2,200 (after four seconds, move servo two to extreme right)
250,4,150 (after five seconds, move servo four to about mid-point)

We could continue this sequence, for example

300,3,100 (after six seconds, move servo three to extreme left)
300,1,200 (after six seconds, move servo one to extreme right)

Consider the "check-timecode" approach with the data above:
When the timecode counter hits 301, this value is greater than the 300 in the first instruction, so servo three is moved to its new position. Now, we read in the next set of values. Even if the timecode has increased (to 302 or greater) this is still more than the 300 in the second instruction. So servo one is moved to the extreme right and the next values are read from eeprom.

Normally, the timecode counter value will not have had time to increase between moving the two servo values but we can see from the example above that even if it does, the second servo is only about 1/50th of a second late moving and the servo controller board soon "catches up" and eliminates any delay.

The only requirement for this approach to work is that the data stored in eeprom is already sorted by timecode (with earliest instructions first in the list). Because we're going to use some custom built software for allow the user to build their animations, we can get the PC/host computer to do this sorting for us and don't need to worry about implementing a bubble sort in a microcontroller!