Due to shear stubbornness I only brought one suitcase to Colorado. This means no soldering iron or useful tools. All I can do is SolidWorks and software. However, I've been doing a lot of CAD lately so I changed gears and took some time digging deeper into the inner workings of PWM signals. This is what I found:
Hardware, or timer-generated, PWM on AVR chips can be broken into two main categories. There is fast PWM and phase correct PWM. In fast PWM mode, the timer counts up from 0-255 (on 8-bit timers) and toggles the output once the compare register, OCRnx value, is reached. The output starts HIGH and switches LOW when this value is reached, meaning the higher the OCRnx value, the higher the duty cycle or time that the output is held HIGH. The graphs below represent the input sawtooth wave with two compare registers (most timers have two), an output PWM signal running 30% duty cycle, and one at 70% duty cycle, respectively.
I wrote a MATLAB program to generate these waveforms; it will become more apparent why when explaining phase correct PWM.
There is however a problem with fast PWM, for certain applications. The PWM signals above emulate the two outputs from a single timer. As can be seen above, the pulses start at the same time but at different duty cycles they do not stop synchronously. This is alright for controlling a single motor or dimming an LED, but for a pair of motors, this introduces error. Say for example there were a drivetrain with two motors, one left and one right (hmm kind of like TOBL). If TOBL were to steer left by running at 50% duty cycle on the left side and 100% on the right for x time, and repeat the other way around for the same amount of time, it would not end up at exactly it's original heading. In TOBL's case, with such a wide track and short wheelbase, this error is amplified.
Ah hah! There is a solution to this issue called phase correct PWM. In this mode the timer counts from bottom to top and then top down to bottom again, toggling from HIGH to LOW crossing of the compare value on the way up, and from LOW to HIGH on the way down. This process looks like this:
There are a couple differences in these waveforms, starting with the triangle wave input. Because the timer has to count up and down the period of each duty cycle is doubled, or the frequency is halved, making it clear why fast PWM is named as such. You might also notice that now neither of the pulse edges align. However, more importantly, the pulses are centered around the top value. Returning to the two-motor vehicle example, now using phase correct PWM, if the signal instruction were suddenly stopped, both motors would stop the same percentage of the way into their duty cycle. While this doesn't entirely eliminate the error, it is a lot better than fast PWM.
On most timers fast PWM is the default and to change it you need to reset the timer/counter control register value to make the switch to phase correct. For the Arduino, which TOBL is based off, this looks like this:
TCCR2A = _BV(WGM20);
...for timer2. Of course there is a lot of other cool stuff to control at this level, so while doing this it may also be of interest to fiddle with the other registers. Setting the two outputs of the timer to non-inverting, the waveform mode to phase correct, and the prescale value to 256 would be this:
TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20);
TCCR2B = _BV(CS22);
The prescale value is useful because it adjusts the PWM frequency (for fast 8-bit, clock speed/prescale/255 = PWM frequency). So, while I may have cut the frequency in half by selecting phase correct mode, I can do the same to the prescale value to maintain the same PWM frequency. This is less important for motor control and more important for things like servos, which usually run at about 30-50Hz.
Anyways, the point of this tangent is that PWM can be a lot more complex than analogWrite(), and can be easily modified for any application. TOBL2 software now implements phase correct PWM, though I can't do much with it until I get a rolling chassis. If you're interested in the m.file that generated the graphs above, you can get it here. It's fun to play around with and see what's going on under the hood.