m_walk.asmAssembly code for the Master Processor.
s_walk.asm Assembly code for the Slave Processors.
I have two topics for you to look at in this article: using the SPI communications bus on the 68HC11, and a walking robot I built using SPI techniques. Last year I designed and built a 4-processor HC11 board specifically to help me study SPI communications on the HC11. Once I got this board up-and-running (and talking), I decided to use it as the "brains" for a walking robot. I will be the first to admit that this robot doesn't need 4 processors; a single HC11 would work quite well. However, this proved to be an interesting project, and my experience in robotics is that people don't want to achieve optimal or minimal solutions--they would rather achieve interesting and challenging solutions.
Let's first look at the finished robot and the challenges with walking, then we'll look at the details of the multiple processor communications using the SPI bus.
For a walking robot I wanted a new challenge. The question was, "Which challenge to tackle?". This equates to "How many legs to use for a walking robot?". With 2 legs (unless you have really big feet) you have the dynamic stabilization problem: to successfully walk you are always falling forward. There are also 1-legged robots for the real balance freaks. Six legs gives you two interpenetrated tripods to work with which allows for a very stable robot with various gaits. I wanted something in-between so that I could have fun with some challenges of balance, without making this the focus of the entire project. Thus, I settled on a 4-legged design. Now the question was, "How many degrees of freedom for each leg?". I wanted "enough" freedom, without building something you would find at NASA. To help me decide, I wanted to see the dynamics involved with a 4-legged creature walking--when along came my cat, named Cosine. I followed Cosine around the house and studied his gait and the mechanics he was using while he walked. His gait was front-right, back-left, front-left, back-right; and in a steady walk it looked like 2 degrees of freedom for each leg would work (while I was gathering this data Cosine was sure giving me strange looks). Thus I choose to try 2 degrees of freedom for each leg, implemented with 2 hobby servo motors per leg. One motor rotates the leg in the vertical plane forwards and backwards, while the second motor pivots the leg and the first motor in and out, in effect changing the vertical plane of the leg mechanism.
Figure 1. Front-back and in-out degrees of freedom.
To achieve these two degrees-of-freedom, I designed a shoulder for the RoboCat. The shoulder contains a servo motor for the front-back leg control, and the shoulder attaches to a servo motor which is attached to the robot body, achieving the in-out control of the leg by pivoting the entire shoulder. I wanted to prototype a simple robot as I was discovering more about my design. For the servo motors, I tried the Tower Hobby TS-51, their general-purpose inexpensive hobby servo motor. For the shoulders, I found at a local drug store a 4"x4"x4" clear plastic box which split in the middle into a top and a bottom. To cut the plastic I used a variable-speed Dremel Tool with a cut-off wheel.
Figure 2. Shoulder cuts (left) and servo attachments (right).
I made four shoulders by splitting both the top and the bottom of the box diagonally as shown in Figure 2. I also cut the points off of the inside corner of the shoulder to allow them to be placed closer together. The right part of Figure 2 shows the servo placement within the shoulder. I ended up bending the leg so that it is vertical when the shoulder is dropped by about 25¡; I thought the downward/inward force would work better with the shoulder pivoted in, and I liked how this looked. For the legs, I found four 5.5" aluminum brackets, 5/8" wide and 1/8" thick. These had three 1/2" extensions on them and they were pretty light--sounded like good legs to me! Using the extension, I was able to easily attach a servo-horn to each leg, making four similar (mirrored) shoulder-leg assemblies.
Figure 3. Wire-frame of the body, shoulders, and legs.
For the body, I choose to stay with the clear-plastic-box idea by using a 3.5" x 3.5" x 6.25" box. The top is the top 2 inches of the box. I mounted two servos in the front and two in the back and then attached the shoulder-leg assemblies. Figure 3 shows the body-shoulder-leg configuration, along with the body-mounted servo motors. The servos in the shoulders, as shown in Figure 2, are not shown here.
Figure 4 shows a top-down view of the body, shoulders, and servos. Notice the diamond opening defined by the shoulders; this is where I mount the 4-CPU circuit board, up on top. Inside the body are the batteries, and also an LCD display (which was removed for weight reasons). There is plenty more room inside the body for additional circuitry and sensors. Servos S0, S2, S4, and S6 are mounted on the body and pivot the shoulders. Servos S1, S3, S5, and S7 are mounted in each shoulder and pivot each leg forward and back.
Figure 4. Top view of the body, shoulders, and legs.
Given a body, shoulders, and legs, controlled by 8 hobby servo motors, we now need to control them. Assuming that we can control these 8 servos with some sort of electronics (stay tuned for the HC11 SPI information), what do we tell them to do? Based on watching my cat Cosine, I derived the following walking gait (Figure 5, left side). The numbers show the leg-positions during a four-step cycle.
Figure 5. The RoboCat gaits for a walk, looking from the top. Cosine's gait on the left and the RoboCat 's gait on the right.
Each leg pivots from front to back for 3 time-steps, then gets lifted (pivoted out which lifts it) and brought forward. If we cycle through this pattern, the RoboCat should walk. Well, there were a few more details required to accomplish this: first I needed to know the servo motor positions for each of the desired shoulder/leg positions. I constructed an 8-potentiometer controller, one pot for each servo, to establish the servo positions by hand. I also hooked up an LCD display for the computer to tell me the servo values used. Given this setup, it was a simple matter to position the shoulders and legs in the desired positions and read the values used to control the servos. I gathered data at 3 shoulder positions and 5 leg positions, as shown in Tables 1 and 2.
Table 1. Top view of the body, shoulders, and legs.
|Legs:||90 Deg||front 30||back 30||front 90||back 90|
Table 2. Servo control-values for the legs.
Table 3. The 8 servo values for standing, legs forward (leaning back), and legs back.
Table 3 combines the shoulder and leg data into the three conditions that allow RoboCat to stand, lean backwards, and lean forwards. My first program was just this: cycling back and forth slowly.
Table 4. The 8 servo values for an 8-cycle walk, implementing the "good" RoboCat gait.
To achieve walking, I took the 4-step gait from Figure 5 and generated an 8-step gait, like the one shown in Table 4. A simple program which cycles continuously through these 8 states allows RoboCat to walk quite nicely. By adjusting the delay-length which controls the cycle-time, RoboCat can go from a slow stroll to a fast walk. The actual data in Table 4 implements the final best RoboCat gait.
I encountered various physical considerations while getting RoboCat to walk.
Balance: Most of the time, RoboCat is moving a "lifted" foot while standing on the other three; these three "down" legs form a tripod. If the center of gravity is outside the projected tripod, then RoboCat falls over. Notice in Figure 5 the tripod extent on the left gait, the shaded triangle. Each tripod has a large extent, while the tripods in the gait on the right are much smaller. From a balance point-of-view these smaller tripods are worse, but when taken into account with the weight, the left gait resulted in the legs being spread apart and the robot could not get its legs back underneath it. This was also a problem of having too little torque. To help balancing, I put "feet" on RoboCat, 2" angles of aluminum; this enlarged the effective tripods. I also experimented with different gaits to find one which worked better. I settled on the "good" RoboCat gait as implemented in Table 4.
Weight: Weight was a continual problem. In the final configuration I had removed the LCD display and all other non-essential items. During testing I used a remote power source to save the battery weight. RoboCat can now walk by itself while carrying 4 C batteries, but only on our kitchen floor. While demonstrating RoboCat on other surfaces I needed to use external batteries to reduce the weight to attain good walking. When walking with batteries, their placement is important as they effect RoboCat's center of gravity, and thus the balance.
Motor Torque: As mentioned earlier, motor torque was a problem when trying to recover and get its feet back under it. Perhaps stronger motors would help out here. Other things which could help to reduce the torque on the motors are to shorten the legs and to reduce the pivotal-length from the body motors to the legs. Using the bend in the legs already helped reduce this.
Floor Traction: Since much of the robot's dynamics are a function of weight and balance, traction of the leg to the floor plays an important factor. I ended up dipping RoboCat's feet in a liquid plastic called "Plasti Dip", which is used for dipping tool handles and such. This provided good traction, but caused problems with catching a toe, since the feet would now not slide. Smooth surfaces work well, such as tile floors and desks, but any kind of carpet is out. I've also noticed that the sharp points on RoboCat's toes tend to catch; I will try filing these round to see if it helps.
Use of Plastic: I used the clear-plastic boxes for a "quick-and-dirty" prototype to help me design the robot. However, while working with the prototype, I was pleased with the performance of the plastic parts. I expected this plastic to crack and bend and cause problems, especially in the shoulder-body attachment--it didn't! This plastic proved to be a good light-weight and strong material for RoboCat, and won't be replaced.
I achieved my initial goal with RoboCat: to get a 4-legged robot walking. I now want to spend more time fine-tuning the walking dynamics. A suggestion I received from my son was to add a tail to RoboCat, essentially a weight which could be swung to different positions to assist in the balancing by shifting the center of gravity. This is an interesting idea. I also want to work on getting RoboCat to turn when I want him to (as opposed to when he just happens to). Given this capability, I can add a couple of light (as opposed to heavy) sensors and have a RoboCat which can really get into trouble exploring.
I'm also considering a double-beam arrangement for each of the legs. This would use a pivoting beam placed about 2 inches from the existing servo-controlled beam, with a 2" pivoting foot linked to the end of both of these beams, forming three sides of a vertical parallelogram. The resulting foot would remain parallel to the ground throughout the leg's swing, thus providing better ground traction.
I now want to detail the four-processor 68HC11 board I used for controlling RoboCat. I designed this board as a platform to investigate the HC11's SPI communication capabilities, and implemented it with a custom-built single-sided circuit board.
My 4-CPU board consists of four 68HC11 E1 processors, each set up identically in a minimalistic bootstrap configuration. I used a header configuration similar to the configuration on Marvin Green's BOTBoard, bringing out the port connections to 0.1" headers. The only re-design I did was to have all four CPUs share a common crystal, as shown in Figure 2-9 in Motorola's M68HC11 E Series Technical Data Book. I built this board and tested each HC11 individually; I was now ready to connect the SPI bus between the four CPUs as shown in Figure 6. There are three pins on the master and four pins on the slaves to look at. The MOSI (master out slave in) pins, D3 on all four CPUs, are all connected together. Likewise, the MISO (master in slave out) pins, D2, are all connected together, and the SCK (serial clock) pins, D4, are all connected together. I used wire-wrap wire on the male connectors on the CPU board for the connections. The last slave connection, xto(/SS), pins D5, are used to select the desired slave in a multi slave configuration. With a single slave, this line can be tied low. In a multiple slave configuration, we must only set the D5 pin low on the slave we want to talk to, and keep the other slave's D5 pins high. I controlled this by using pins 1, 2, and 3 on the master's B port, controlling slaves 1, 2, and 3 respectively.
Figure 6. SPI master/slave connections.
Before connecting up the SPI bus, let's consider the various options we have with the SPI communications, such as polled versus interrupt-driven, clock rates, and so on. My intent was to understand the SPI offerings and to select a configuration which will work in a dedicated Single-Master/Multiple-Slave setup; I want to identify one of the CPUs as the master, and maintain it as the master. The other three CPUs will be configured as slaves, and I want to run the same code in each of the slaves. SPI allows for two-way communication, from the master to the slave and also from the slave to the master. In this initial implementation, I am not sending any data from the slave back to the master, but this can be easily added. To check the communications, I choose to always send a 5-byte packet of information from the master to the slaves, consisting of the sequence: $AA, $55, Servo-number, Data, $81. The slave will check for this sequence to insure proper communications. Let's assume that we can successfully get the master to send data on the SPI bus; what must the slave do to receive it?
The slaves will use an interrupt routine to get the SPI information. Being in bootstrap mode, I must copy the interrupt jump vector into RAM (see the Init_SPI code in the s_walk.asm file). You may see in my slave code listing occasional stores to $1004, which is the B port. These stores are used for diagnostics; you can monitor the B port with 8 LEDs and hopefully see life. A slave must use thexto(SS) and MOSI lines for input and the MISO line for output, so we must set the data-direction for these bits on the D port to the proper directions. To set all bits for input, except D2 (the MISO pin), write a $04 to register $1009 (LDAA #%00000100 ; STAA $1009). We must also configure the SPI settings using the SPCR register, $1028. We want interrupts (bit 7=1), the SPI system enabled (bit 6=1), the D output not in a wire-or mode (bit 5=0), configured as a slave (bit 4=0), clock polarity active high (bit 3=0), clock phase select (bit 2=1), and the SPI bit rate (bits 0 and 1 = 00). Thus a $C4 is written to register $1028 (LDAA #%11000100 ; STAA $1028). This specifies that we are a slave using interrupts at the highest data rate. The clock polarity and clock phase need to be the same for the master and the slaves. I choose a setting which looked good--different settings should also work. The last thing the SPI initialization code needs to do for the slaves is to enable interrupts with a CLI instruction.
When a slave processor gets a byte of information on the SPI bus, the interrupt routine is called. This routine blocks if the SPI data is not yet ready, then it reads the SPI data byte which is found at register location $102A. This byte is read and then the interrupt routine is exited.
My SPI implementation uses a 5-byte packet of information. I've identified a 5-byte data-space in RAM to store these 5 SPI data bytes. I clear the last byte and set a pointer to the first byte, and then the main program blocks, waiting for this 5th byte to become something other than zero. The only way this RAM location will change is from the SPI interrupt routine. In the SPI interrupt routine I read the SPI data byte and, if pointing to the first data location, check to insure the first data byte is the $AA. If it is, then this value is stored and the data pointer is incremented. The next 4 interrupts will store the next 4 SPI data values into successive locations in the 5-byte RAM storage. While this is happening, remember that the main program is blocking, waiting for the 5th byte to change. The 5th byte in the SPI packet is $81, which is stored in the 5th RAM data location, which causes the main program to continue. The main program can now process the complete 5-byte SPI message. It confirms that the first 2 bytes are $AA and $55. It then gets the servo number and the servo data and updates the appropriate servo. The servo motors are driven on the slaves using the timer-overflow capabilities on the HC11Ñas detailed by myself in an article in last Summer's TRP. The code I developed last Summer is used here as is.
The servo motor code for both of the slaves which are driving the RoboCat's servo motors is identical code, and is very simple. The code initializes the servo and the SPI bus, then waits for a 5-byte SPI message. This message will update the servo position for one of the slave's 4 servo motors. This continues indefinitely.
OK, the servo code is in place in two of the slave HC11's, which are ready to receive SPI data-packets from the master CPU. What must the master do to communicate with the slaves? We need to initialize the master and then write the servo data to the slaves. The specific slaves are enabled through the B port, bits 1, 2, and 3. To disable all the slaves, we set these bits to 1's (LDAA #%00001110 ; STAA $1004). For SPI communications, the master must be able to write bits 3, 4, and 5 on the D port, so we set the data direction register for the D port to allow this (LDAA #%00111000 ; STAA $1009). We now configure the SPI settings using the SPCR register, $1028. We do not want interrupts (bit 7=0), the SPI system enabled (bit 6=1), the D output not in a wire-or mode (bit 5=0), configured as a master (bit 4=1), clock polarity active high (bit 3=0), clock phase select (bit 2=1), and the SPI bit rate (bits 0 and 1 = 00). Thus a $54 is written to register $1028 (LDAA #%01010100 ; STAA $1028). This specifies that we are a master, not using interrupts, and set at the highest data rate. The clock polarity and clock phase are the same as the slaves. Because we are not using interrupts, we do not need to clear the interrupt flag.
For the master to make RoboCat walk, we want to cycle through a set of eight different leg positions, or settings. I have designed a hierarchy of subroutines, each doing a very simple task, to accomplish this. I have these eight settings defined as eight tables in the code. I point the X register to the desired table and call a subroutine to set the eight servos. The routine DO_8_Servos will send out each of the eight servo values for each position by setting the A and B registers to be the servo value and the servo number, and then calling the subroutine SPI_Servo_AB; SPI_Servo_AB is responsible for setting up which slave to write to. Slave 2 controls servo motors 0 through 3, and slave 3 controls servos 4 through 7. The routine stores in RAM the data for the B port for selecting the desired slave, then calls SPI_Servo to write the 5-byte packet. SPI_Servo writes the $AA, $55, servo-number, servo-data, and $81 bytes in sequence by calling the routine Send_SPI.
Send_SPI reads the SLAVE specifier from RAM and writes this value to the B port, enabling the desired slave. The data for the slave is sent by writing the data from register B to register $102A. I then block, waiting for the SPI data to be sent, by looking at bit 7 in register $1029. Once the SPI transmission is complete the master's data has been sent to the slave, and the slave's data has been simultaneously received by the master. The slave's data can now be read at register $102A if desired. I also disable all three slaves by writing a $0E to the B port--and we're done. We return back to SPI_Servo, which continues writing the 5-byte packet. This returns to DO_8_Servos, which readies data for the next servo.
I have successfully demonstrated the use of the SPI bus for high-speed HC11 communications in a 1-master 3-slave arrangement. I have also tested the returned data received from the slaves, which works as expected. In this configuration, the master processor is responsible for all communications and timing. It should be possible to enhance this by using external interrupt lines on the master which are triggered by the slaves. This way a slave can get the attention of the master without waiting for the master to query it for information. The master would still be controlling the timing and communications, but could be tapped on the shoulder by the slaves. I have a third slave processor on my 4-CPU board which I will be used to process sensor information once I add appropriate sensors.
RoboCat is a great first-stage of a project, incorporating a multi-processor HC11 system with high-speed communications and four discretely controlled 2-degree-of-freedom legs. I am set to add some simple sensors and to work in more detail with the walking and turning dynamics of the system. Perhaps, by reading the other articles in this issue of TRP, I will get sparks of inspiration to address my dynamics, weight, and torque concerns. These aren't critical problems, but they do limit what RoboCat can do. I am quite pleased at having some sort of robotic creature that walks; it does make a nice piece to get the attention of kids--and also adults. I have taken RoboCat out on a few demonstrations; it has performed wonderfully and has been a great crowd-pleaser.
I would like to express thanks to my son Tommy and our cat Cosine for their help and excitement in our robots, review suggestions, and enthusiasm with robots running and walking around the kitchen, and to SRS (the Seattle Robotics Society) for their continued inspiration and support in robotics.
Tom Dickens works for Boeing Computer Services in aerodynamics research computing, and also teaches evenings.
HC11 MCHC11 Reference Manual (M68HC11RM/AD Rev 3): Reference for entire 68HC11 family of devices. THE source for 68HC11 assembly language.
M68HC11 E Series Technical Data (MC68HC11E/D): E series specific hardware and internals reference.
Motorola Contact: Motorola Literature Distribution Center 1-800-441-2447 P. O. Box 20924 Phoenix, Arizona, USA 85036-0924 (or visit your local Motorola sales office, check the phone book)
The HC11 assembly language programs as published here are available here.
m_walk.asm Assembly code for the Master Processor.
s_walk.asm Assembly code for the Slave Processors.