Note: these notes only cover programming the motors on the miniboard. There's a good description of the theory of operation of an H-Bridge, written by Jim Brown of the Dallas Personal Robotics Group, at http://www.dprg.org/tutorials/1998-04a/. In this class we're more concerned with using them than with how they work, though.
The miniboard uses the eight-bit digital output port located at
address $1004 to control the motors (and the associated
LEDs)
This port is wired to the three chips to the right of the CPU chip on the board: four of the inverters on the 74HC04 hex NOT gate, and the two L293 dual H bridges.
The way it's connected is that the four high-order bits are used to turn one or more motors on or off, and the low-order four bits are used to select the corresponding motor's direction. So it looks like this:
$1004: |
|
and the meanings of the bits (going left to right) are:
| Bit 7: | Motor 4 Enable |
| Bit 6: | Motor 3 Enable |
| Bit 5: | Motor 2 Enable |
| Bit 4: | Motor 1 Enable |
| Bit 3: | Motor 4 Direction |
| Bit 2: | Motor 3 Direction |
| Bit 1: | Motor 2 Direction |
| Bit 0: | Motor 1 Direction |
The motor output port header on the miniboard looks like this:
There's a single piece of header strip with twelve connections. The first three are used for motor 1, the next for motor 2, the next for motor 3, and the last for motor 4. There are also two LEDs associated with each motor: turning a motor on in direction 0 also turns on its green LED, while turning it on in direction 1 also turns on its red LED. We actually only use the outer two motor pins for each motor: the middle pin is wired directly to the battery, so it always has +9 volts on it.
The way we control which motors are on (and off), and which direction they're turning is by turning bits on and off in the motor control register. So, if we were to execute the code
ldaa #%10000000
staa $1004
we would turn on motor 4, and the green motor 4 LED. At the same time, we'd be turning off motors 1 through 3.
The code
ldaa $%11001000
would turn on both motors 3 and 4; motor 4 would have its red LED turned on while motor 3 would have its green LED turned on. Motors 1 and 2 owuld be turned off.
Trying to read those binary strings makes my eyes hurt. So, instead we can (and should) define symbols that will let us write more readable code. Here are some suggestions
If we define
MOTORS equ $1004
then instead of writing
staa $1004
we can write
staa MOTORS
Similarly, we can define the meanings of all the bits with something like
ON4 equ %10000000
ON3 equ %01000000
ON2 equ %00100000
ON1 equ %00010000
RED4 equ %00001000
RED3 equ %00000100
RED2 equ %00000010
RED1 equ %00000001
Now those earlier examples turn into
ldaa #ON4
staa MOTORS
and
ldaa #ON4|ON3|RED4
staa MOTORS
which I, at least, find a lot easier to read!
Note on or'ing values together: In the example above, I used the
"|" symbol to "or" the bits together. The "|" is the C "bitwise-or"
operator, so it'll combine all the bits. You do need to remember
that you can't have spaces in the expression (if you do, the
assembler will assume the expression is over and won't include
anything after it). The way I wrote it combines the bits together,
and then says the whole thing is in immediate mode. I could have put
#'s on all the others too, but that doesn't really matter.
The code I've given you so far for motor control requires you to construct the entire motor control word in the A accumulator, and write that out to the motor port. If you were to write something like
ldaa #ON4
staa MOTORS
ldaa #ON3
staa MOTORS
with the intent of turning on both motors, it wouldn't work: you'd be turning off motor 4 when you were turning on motor 3.
There are instructions that let you set and clear individual bits
in the motor ports, however: the instructions are bset
and bclr.
bset
bset loc mask
What this does is to perform the logical OR of the old contents of the location and the mask: it sets the bits you specify.
So, for instance, if the value of memory location
01 is $aa, executing
bset 01 #$f0
will leave the four least-significant bits unchanged, but will set the most-significant four bits:
10101010old value of location01, in binary| 11110000mask, in binary11111010new value of location01, in binary
So the new value of the memory location is $fa
bclr
bclr loc mask
What this one does is to perform a logical AND of the old contents of the location and the inverse of the mask: it clears the bits you specify.
So, to continue the example, if we were now to execute a
bclr 01 #$0f
we would clear the least-significant four bits, but leave the most-significant bits unchanged:
11111010old value of location01, in binary& 11110000mask, in binary11110000new value of location01, in binary
So the new value of the memory location is
$f0 (notice that the bits I set in the
instruction are the ones that got cleared when the
instruction was executed)
One problem with both of these instructions is that they only support direct and indexed addressing: they don't do extended, which is what we need to modify the motor port. The solution to this is another application of indexed addressing: we can point one of the index registers to the IO block, and use an offset from it to point to a particular device in the IO block. The code looks like this:
* establish symbols to work with the motors.
IO equ $1000
MOTORS equ 4
ON4 equ %10000000
RED4 equ %00001000
org EEPROM
start
ldx #IO * point X index register at IO block
bset MOTORS,x ON4 * turn motor 4 on
bset MOTORS,x RED4 * turn it red
Of course, this example isn't complete: it only shows a couple of the symbols for the motor bits, and it only shows setting up the pointer to the IO block and turning on a motor. For that matter, the way it turns the motor on (and red) is less efficient than it could be; given the particular example, there is no particular reason not to have simply turned the motor on and red in a single instruction.
An implementation note: the way the HC11 implements these instructions is to read the old contents of the motor port, make the change, and write the new value out. That's why the simulator will ask you for an old value of the motor port whenever you use this instruction.