08M dual software PWM
If you've ever tried to get speed control using a picaxe 08M connected to a L293D, like on the picaxe 08M motor driver board, the first thing you'd notice would be that the 08M has only one PWM output. As the robot I'm currently working on uses a picaxe 08M to drive the motors, I'd thought I'd give software PWM a shot. After a lot of reading, tweaking and frustration I finally got more or less what I wanted.
The result is a dual channel 500Hz PWM motor controller that can be used to drive the little GM10 motors at 100 speed steps. I know 500Hz isn't very impressive, but at least with the GM10 motors (that Frits! uses on his little 8) it works like a charm and I suspect lots of other small motors can be used with this setup.
To get a two channel PWM we need to use a loop that has as few statements in it as possible. No assignments, no IF..THEN statements, no calculations and no GOSUBs. This is important because all those statements take a long time in when you use a picaxe. According to the manual, the average time it takes to run one basic command is .... wait for it... 1 millisecond (or .5 mS at 8Mhz) which is amazing as the chip runs 1000 times as many assembly instructions per second.Of course, very simple instructions, like inc b0, will take much less than 1mS.
The first half of the loop we control the left motor pwm and the second half is used for the other motor. In the attached code, pins 1 and 4 are used for the left motor. We pulse pin 1 (power pin) on and off to get the PWM output and pin 4 (direction pin) is used to determine which way the motor should spin.
To get 90% PWM for the left motor, pin 1 should be high 90% of the full loop.
This means we leave the power pin high 80% of the left motor half of looptime and 100% of the right motor looptime.
The loop starts with pin 1 high and pin 4 low, spinning the motor. The pulsout command is used on the power pin(switch-time) Note that using pulsout on an output pin that is high, will actually pull the pin low. In this case for 20% of this loop-half
Now the program needs to pause for the rest of this half of the loop. Because the smallest pause at 8Mhz is .5mS, we'll use pulsout to get more accuracy. We toggle the power pin, making it low if it was high and vice versa, and then use pulsout again: this time the pulsout will put the pin in the orginal position for the duration we want. This effectively pauses the original pin positions for the duration of the pulse. (nap time)
To make sure the power pin stays in the original position for the remainder of the loop, it is toggled again at the end of this loop half. The other half of the full loop is used in the same way for the right motor.
To get a 40% PWM, the power-pin is toggled (turning the motor off) before the loop starts and the switch-time and nap-time are swapped. Now the right-motor half of the full loop (red bar in the diagram), the left motor is off. When the power-pin is pulsed during switch-time, instead of turning the motor off, it will turn the motor on for 80% of this loop half.
The code for the loop looks like this
pulsout LM_power_pin, LM_switch_time 'leftmotor pulse the output pin
toggle LM_power_pin 'set pin wrong position
pulsout LM_power_pin, LM_nap_time 'leftmotor pulse the output pin to the correct position to pause
toggle LM_power_pin 'set ping right again
pulsout RM_power_pin, RM_switch_time 'rightmotor pulse the output pin
toggle RM_power_pin 'set pin wrong position
pulsout RM_power_pin, RM_nap_time 'rightmotor pulse the output pin to the correct position to pause
toggle RM_power_pin 'set ping right again
This causes a problem when the motor should be off all the time. Because the toggle command is used, the motor will be on for a very short while each loop, which is anoying. To remedy this, the first half of the loop is replaced by a 1mS pause if the switchtime=0. Like I said, we can't afford IF..THEN statements inside the loop so this condition is placed outside the loop leaving 4 loops.
- if both left and right switchtimes = 0 --> pause 2mS
- left switchtime=0 AND right switchtime >0 --> pause 1mS , pulse right motor
- left switchtime >0 AND right switchtime = 0 --> pulse left motor, pause 1mS
- both switch times > 0 --> pulse left motor, pulse right motor.
To reverse: we start out as before only with all the pins reversed. Because of the way pulsout and toggle commands are used, this works just fine.
The rest of the code is used to translate incoming commands to the right pulse lengths. Commands are send using serial on pin 3. Both differential steering (left speed + right speed) and RC-car steering (speed + direction) are supported, although the latter has no option to reverse the motors. The program is 255 bytes so there is no room for extras.
'motorcmds / commands to be received
' commands are send as a 5 byte message => ESC, CHIPID, CMD, ARG1, ARG2
' ESC = constant 27 to indicate start of the message
' CHIPID = should be equal to the constant chipid of the motor controller (i.e. 1 in this case)
' CMD = motor command
' ARG1 = either left speed or general speed depending on the CMD
' ARG2 = either right speed, direction or is ignored ; depending on CMD
For more info: study the code as it is reasonably well documented
Sorry for the long text ;)
The video shows the 3x08M robot whith one 08M sending increasing and decreasing speeds to the 08M that's used as a motor controller.