![]() |
|
Doug Leppard (DLeppard@CCCI.Org )
Benjamin Leppard (Benjamin@Leppard.Com)
This is the seventh in a series of articles outlining the steps I have taken to build my robot. These articles are focused on a robot that may compete in the Fire Fighting contest, be able to roam around the house, but mostly be a fun learning tool. The 68HC912B32 is the processor, so these articles will be on interfacing to the B32 various mechanical devices and sensors using SBasic as the programming language.
"Setting the Stage for Building a Robot," we set goals for the robot and did research. From the goals and research we will make fundamental decisions on what our robot will be like.
"Choosing the CPU, Language and Basic Shape," using our goals we choose what microprocessor, language and what the fundamental shape of the robot.
"Motorolas S-Record" looking at the S-Record format that Motorola uses to load data and programs.
"Booting up the 68HC912B32 for the first time" described in detail how to boot the B32 up and run a program.
"State of the Union" gives an overview of what state the robot was in August 1999.
"Multiplexing Five Sonar Modules" goes into how to and the reasons of putting five Polaroid sonar transducers and wiring them together.
"Drivers for Sonar & Mapping" shows the software to drive the sonar units, and how it can map a room when tied to a PC.
One subject that I have not written on is wheels and wheel encoders. I had planned on this being one of my first articles, I avoided this because I was always frustrated with my robot's wheels, but really mostly with my wheel encoders. So I decided to change my robot's wheels and buy motors with built in encoders. The previous wheels worked quite well, but the wheel encoders were touchy and not very accurate. They had only 24 segments and were not even in their spacing. I tried and tried to make it work but decided a total redesign was in order. Not unusual in robotics.
I did a search on the net and sifted through many sites that led me to buying a pair of Pittman motors with built in wheel encoders. The motor is part of their 9000 series (9234). The motor encoders were very easy to connect. All that was needed was to connect to the ground, Vcc (5V) and the signal lead gave a perfect square wave signal when the wheel was moving. You can connect the encoders directly into a MCU port without signal conditioning.
The encoders put out 2,950 clicks per shaft revolution which is a lot of accuracy. This is over 100 times more than my previous wheels. The encoders clicks were operating in the sub mili-second timing. Therefore, it became apparent that hardware interrupts were needed to count the clicks from the encoders.
If you have not worked with interrupts before I strongly recommend Kevin Ross article "Interrupts on the 68HC12". I had a basic understanding of interrupts before but Kevin's article helped me a lot especially from the perspective of the HC12 MCU.
Studying the specs for the B32, it can be seen it has two hardware interrupts, IRQ and XIRQ,that would be useful for tracking the left and right wheel encoders. Searching the SRS web pages and specifically Kevin Ross articles, I realized nothing was written on these interrupts. Studying the Motorola data book on the B32, it didn't have enough information on these interrupts. So, I searched the Motorola site and eventually came across the document 68HC12B/D. Clicking on the name of the document will hopefully allow you to download the document. It is a big 4.3mb PDF file. But the good thing about that is I could search the document for IRQ to read all there was about the interrupts. It has a lot of great information in it.
I found out that the IRQ could be set up as edge triggered or level triggered interrupt and the XIRQ was level only. Edge triggered interrupt means that the interrupt is triggered on the edge of the signal and level sensitive interrupt is when the signal is a certain level, in this case low, it will cause an interrupt. They do this so that several lines can be tied together and the interrupt can be shared. Also, it often is used for power loss detection.
But for our case we need edge triggered and not level sensitive interrupts. I want to use both interrupts, one for each of the wheels. Therefore hardware will need to be added to the XIRQ interrupt to make it an edge triggered interrupt.
I look in my junk boxes of ICs for a flip flop. I come across a 4013 dual D flip flop, any flip flop will work. A flip flop when it is triggered will turn on or off depending on how it is wired. I have wired it so that on the rising edge of the encoder signal, it turns on the flip flop and the Q not goes low. It doesn't matter if the flip flop is triggered by the rising or falling edge, as long as it is triggered. This signal is then fed into the XIRQ port (port E bit 1). Now the interrupt can be fired and we can know to count the encoder click. But the flip flop must be turned off so we use port A bit 0 and set it to high to reset the flip flop low again to be ready for the next cycle.
Since IRQ is edge triggered we don't have to worry about all this, it is done for us.
Table how 4013 flip flop is wired
4013 pin number | Purpose of pin | Comment |
14 | Vcc 5V | |
7 | ground | |
9 | data | hooked to 5V. Pins 8 and 9 programs the flip flop to flip on when clocked. |
8 | set | hooked to ground. |
11 | clock | Encoder signal hooked to this pin. Encoder clocks the flip flop to on. |
12 | Q not | Hooked to XIRQ port (portE bit 1). Q not goes low when flip flop clocked on. |
10 | reset | Hooked to MCU output port (porta bit 0) to reset flip flop again. |
Now we need to write some routines to make this work. When the B32 first comes on IRQ and XIRQ are set to level sensitive interrupts and the port is not connected to the interrupt circuitry. So this must be changed and also set XIRQ to a non-maskable interrupt.
At the very beginning of every program I run an initialization routine that sets things up. The very first thing I do is setup these interrupts. The following are some of the routines that are in the initialization routine that works with the interrupts.
'------- set IRQ interrupt ---------- 'do immediately or may get hung up pokeb INTCR,%11100000 'Set IRQ bit 7 to 1 to cause IRQ to fire only falling edge not level sensitive 'bit 6 connected to interrupt logic
Note this code must be done immediately or it will get hung up. Normally IRQ is
level not edge interrupt, so once hooked up and the signal happens to be low, it will
start firing over and over again and you will get hung up in that routine. By
setting bit 7 of INTCR it will make IRQ edge triggered. Bit 6 connects the
interrupts to the interrupt logic therefore turning them on and making them available to
the MCU.
Next we want to reset the flip flop that is connected to the XIRQ. That is done below:
'------- Port A setup --------------------- pokeb DDRA, %11100001 'set the port A bits for out and in 'set bit 7 high for write, bit 6 high for normal output flow, bit 5 fan off 'bit 0 reset 4013 for the XIRQ interrupt pokeb portA, %11100001 'reset 4013 pokeb portA, %11100000 'now ready for being clocked
The first line sets portA for either input or output. Port A bit 1 makes it an
output port, bit 0 makes it an input port. The last lines resets the 4013 and gets
it ready for data.
Finally we need to setup XIRQ, this routine sets it to be non-maskable.
'------- XIRQ set up --------- 'clear CCR bit 6 to make XIRQ non-masked asm ANDCC #%10111111 endasm
This need to be done in assembler since this is not done with external ports and Sbasic
does not have a command for this. The HC12 had a command to load the next byte into
this register which made it easy.
Now we are ready to start counting encoder clicks. When an interrupt occurs it jumps to the interrupt table then this table points the interrupt to your routine. I use the boot loader with my B32 therefore it has a secondary table. See my article on Booting up the 68HC912B32 for the first time, see the section on vectors, this explains all this.
Below is my jump table for all the various interrupts that I use:
'this is so that when the command to jump to flash it will jump to start of program org $F7FE asm dw $8000 'set reset point to start of program endasm 'Real Time Interrupt (RTI) org $F7F0 asm dw $8009 '$8031 endasm 'timer overflow interrupt org $f7de asm dw $802c endasm 'IRQ org $f7f2 asm dw $803b endasm 'XIRQ org $f7f4 asm dw $804e endasm
The above code loads the location in my code of the interrupt routines into the secondary
interrupt table. If the routines and code above them are changed then the jump
location must be changed. I do this by first compiling the program and looking at
the listing (.lst) of that program. It will give what the location of the routines
are that can be loaded into these locations. Now compile again to be ready to run
the program. This has to be done only when the routines are changed which is not
very often. Interrupt routines should always be simple and should not have much code
and therefore not changed much.
Actual routines that process the interrupts are very simple. I have two variables that I use for each wheel to keep track of the encoder count. For the right wheel it is rwheelcnt and update_rwheelcnt. Each time the interrupt fires it comes to this routine and these variables are incremented by 1. rwheelcnt is used to keep track the total number of clicks of the encoder for various distance readings. The update_rwheelcnt is used in a routine that tells the speed and used in it's X and Y coordinates. The $f7f2 tells what interrupt it is.
interrupt $f7f2 'IRQ for wheel encoder rwheelcnt=rwheelcnt+1 update_rwheelcnt=update_rwheelcnt+1 end
The next routine is similar except it has additional code to rest the flip flop and code
to adjust the difference between the wheel reading. When I did measurements, I
discovered that one wheel would give 312 clicks per inch traveled and the other wheel
would give 316 clicks per inch. This most likely because the wheels are not
precisely the same size. This is only about 1% off but still is off.
So I have a constant clicks_adjust that is set at 82. Then I have a counter
clicks_adjustcnt that counts down to 0. So every 82 clicks it ignores a count from
the encoder and makes the two wheels come out the same. So both wheels will have 312
clicks per inch traveled. The pokeb routines reset the 4013 flip flop to make it
ready for another cycle.
interrupt $f7f4 'XIRQ for wheel encoder 'note click_adjustcnt is used to ignore click_adjust clicks to make the 'wheel counts come out the same clicks_adjustcnt=clicks_adjustcnt-1 if clicks_adjustcnt=0 clicks_adjustcnt=clicks_adjust else lwheelcnt=lwheelcnt+1 update_lwheelcnt=update_lwheelcnt+1 endif pokeb portA, peekb(porta) or %00000001 'reset 4013 pokeb portA, peekb(porta) and %11111110 'now ready for being clocked end
My previous wheels were 4.5 inch diameter. But this was too large for the new motors that had a low gear ratio 5.9:1. So I went with 2.25 inch wheels which turned out just about right. As I said earlier, through testing it turned out that the wheels would give me about 312 encoder clicks per inch traveled. I discovered this by dragging the robot by hand across the floor along the linoleum that had 12 inch squares. I dragged it 72 inches and then divided the inches into the number of recorded encoder clicks recorded. It did this a number of times and took the average reading.
I love the new wheels. The motors are very quiet. My old motors were electric screw driver motors, they worked but were very noisy. I never have to worry about missing an encoder count and I am now playing around with dead reckoning techniques. That combined with on board sensors gives the robot a better idea where it is in the house.
Drop me a line and let me know if you found this helpful, also if there are other areas you want me to write on let me know. I am thinking about writing on the telemetry (radio modems) next.