HOW TO DO EVERYTHING POSSIBLE WITH PWM USING JUST TWO I/O PINS.

3-pin controlled H-bridges such as the L293 and the sn754410 are very powerful in that you can coast, brake, go forward, reverse and everything using three control pins one of which (the enable pin) is tied to the PWM signal. This makes it very easy to program. But these sort of chips have very low max PWM frequencies and/or are old, don't use MOSFETS or just suck down the power to drive their logic.

Bummer.

You could move to a larger bridge such as the MC33887 but they are 4-5 times the cost of the sn754410 and are still limited to 10 Khz PWM frequency. This and their physical size can also be a bummer if you just need to do small currents such as in a nanosumo. Also, ever try to hand solder the SOIC-54 pins?

The Vishay si9988 which is used in the extremely cool megabitty is small and inexpensive but uses locked-anti phase PWM do do it's thing. If you have teeny tiny coreless motors with very low inductance like on a nanosumo this can be hard on your batteries. You could switch the pins and alternate between drive and brake but that requires a motherboard mod.

H-bridges such as the Vishay SI9986/87 are cool since it can do brake, coast as well as the usual forward and reverse. However, it only has two control pins. This makes it problematic trying to hook up a PWM pin to it. We'd either need some form of additional bits of logic or have to use a programming trick which uses up too many of the CPU's cycles.

This is where timers like those on the mega88 with dual compare units and output pins come in handy. We can use those two pins to have our cake and eat it too. Depending on whether we want coasting, braking, proportional brake or proportional forward/reverse we set the pins up a certain way.

The reason this is an issue is that, in effect, the PWM channel and polarity will move from pin to pin based on the mode you want. Without external logic it is hard to move the PWM signal to a different pin on the hbridge. Until now.

To illustrate, let's take a look at the SI9986 chip. It has this sort of logic table:

WHAT WE GIVE IT  | WHAT IT PUTS OUT    | WHAT WE GET ON THE MOTOR
input A  input B | output A  output B  |
-----------------+---------------------+--------------------------
   0        0    |    0         0      | BRAKE
   0        1    |    0         1      | FORWARD
   1        0    |    1         0      | REVERSE
   1        1    |   hiz       hiz     | COAST

This chip can give us every function we want and 1 amp current capability in an SO-8 package to boot! We just need to think about how to interface to it.

If, for example, we want our PWM cycle to alternate between forward and coast. So, when TCNTx is less than the OCx register we want a 0 and 1 on the outputs. When TCNTx is greater than or equal to OCx we want hiz on the outputs. Output B is easy since we can just leave a 1 on it in this mode. Output A requires a little thought. We want the motor to drive forward when we have a ZERO on the input instead of a 1 which is how we would normally do it. However, that's no big deal. The mega88 allows us to switch to INVERTED PWM by just twiddling the COMxy bits. Now it will be a zero during the "drive" portion of the PWM cycle.

If we want to do the same thing but have the motor run in reverse we do the same operation but swap function of the I/O pins. What that means is that the pin we send PWM signal to will CHANGE when we reverse directions! This is why we we need to do this with a timer that has dual outputs. We will use the COMxy bits to alter what appears on the pins.

Here is the complete table of how to make it all work. "inv" means inverted PWM (i.e. set on compare match and clear at top). "normal" means what we normally expect to see with PWM (clear on compare match and set at top).

 
        WE WANT             | THEN DO THIS
----------------------------+-------------------------
when:
PWM ON        PWM OFF       | OCRxA  OCRxB  comments
----------------------------+-----------------------------
forward       coast         | 1      inv    0 = coast, 1+ = more drive
reverse       coast         | inv    1  
forward       brake         | 0      normal
reverse       brake         | normal 0
brake         coast         | inv    inv    0 = no brake, 1+ = more brake
forward       reverse       | normal inv    locked antiphase
reverse       forward       | inv    normal locked antiphase

One of the coolest things about this technique is that it doesn't require funny code inside an interrupt to handle the pins. All you have to do is adjust the COMxy values for the pins when you set the motor direction and whether you want to brake/coast during the off cycle.

Go HERE to get the source code for the setmotors function that does it all! Well, almost all, I haven't bothered implementing locked anti-phase yet. The input pins of the 9986 are hooked up to OC1A and OC1B.

Once the ports have been set up all that needs doing is setting the duty cycle. The only difference here as well is that both OCRxy must be set to the same value at the same time.

Some examples (assume 8 bit PWM):

setmotor(FORWARD, COAST);
OCR1A = 128;
ORC1B = 128;

will tell the left motor to drive forward during the "hi" part of the duty cycle and coast when not.

setmotors(FORWARD, BRAKE);
OCR1A = 192;
ORC1B = 192;

This does the same but brakes instead of coasting when not driving forward.

Locked anti-phase (if implemented) would just be enabled like so:

setmotors(FORWARD, REVERSE);

With OCR1A/B set to the halfway point the motor doesn't turn; at either extreme the motor is full blast one way or the other.



to email Craig send to climber at shaw.ca (replace at with @ and remove spaces).
Return to Craig's Electronics page.
Return to Craig's main page.

Last modified: June 12, 2007 - broke 2 drill bits today - THAT BITES