L'Hexapod: A timer driven PWM servo controller - part 2

Previously published

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.

In part1 of this timer driven PWM servo controller I built some code which uses Timer1 in the ATTiny2313 to generate 64 PWM signals. The code in part 1 worked from hard-coded dummy data. The code presented here shows how we can create the data that the PWM generation code needs to run.

The timer driven PWM code presented here works in terms of two byte pulse durations and a 1 byte value that determines which pulses are still being generated. Thus each unique pulse duration is represented by three bytes of PWM control data. Finally we have a final duration that uses up any remaining time so that each batch of pulses takes 2.5ms to generate. This means that we need at most 9x 3 byte sets of control data. The serial I/O code which we’ve yet to write works with single byte servo control values between 0 and 254 with 127 being the centre position (a pulse of 1.5ms for Hitec servos). So the code we need to write needs to take 8 bytes of servo control data from the buffer that the serial code manages and process it in the following way:

  • Copy the 1 byte control values from the serial data store into the PWM data store and associate a value that represents the pin that needs to be turned off to control this particular servo. We now have a series of eight two byte structures which consist of the 1 byte control value and the 1 byte pin value.
  • Sort the two byte structures into ascending order based on the value of the 1 byte control value.
  • Merge the pin vales of duplicate control values, so, for example, if we have a value of 127 for pin 1 and a value of 127 for pin 2 we would end up with a single two byte structure that represents a value of 127 for pins 1 and 2. This leaves us with between 1 and 8 two byte control structures.
  • Merge the pin values of the earlier structures with those of the later structures; the idea being that if the first servo to be switched off is on pin 1 and it switches off at a control value of 100 and the next servo to switch off is on pin 2 and it switches off at 105 then the pin control value for the second data structure should be turning off pins 1 and 2. As each pulse duration is processed more pulses are stopped and once a pin has been switched off it stays switched off.
  • Make the control values relative to each other rather than absolute. If the first control value is 100, the second is 105 and the third is 107 then we need to change the second to be 5 and the third to be 2.
  • Expand the control values into two byte pulse durations in us. To do this we multiply the single byte control values by 6 to get a range of pulse durations from 0-1524.
  • Add an initial base value to the first pulse such that a control value of 127, which gives a pulse duration of 762 is adjusted to give a pulse duration of 1500. For our 8MHz clock this means that we need to add 1500-762 = 738 to the first pulse duration.
  • Calculate the duration of the longest pulse, we do this by looping over the series of pulse durations and adding them up.
  • Calculate a final timeout to take the longest pulse duration to 2.5ms. Simply subtract the longest pulse duration that we calculated in 8 from 2500.
  • Adjust all of the durations to allow for the time that the interrupt handler takes to get from being called to the point where the pulses are actually turned off. In our case we subtract 1 from all of the durations.

The result is a series of between 1 and 8 three byte structures, each with 2 bytes of pulse duration (stored in high byte, low byte order) and one byte of accumulated pins that remain on. Following this (immediately after the last structure that is in use for pulse control) we have a further three byte structure with the final duration and a sentinel value of 0xFF. This is exactly the data that is needed for the timer driven PWM servo controller that I presented in part 1. The code that can be downloaded from here is just the data calculation code. It contains dummy test data that is sorted, merged and adjusted and with appropriate break points you can watch the progress of the data conversion. All of this code takes the place of the dummy data load in the PWM setup handler from part 1.