Marvin

Introduction

This section describes the construction and programming of an autonomous home robot. The purpose of this robot was to test various sensors and motors in a dynamic environment and to spark interest in technology and programming with my children and their friends. Marvin and all its variations was popular at school even though it could not do homework.

Version 1

Frame

The frame for this robot was a simple wooden construction based on a pair of 12" plywood circles and four wooden dowels as shown in the figure below.

Figure: Frame Construction

Locomotion is provided by a pair of 12V DC geared motors attached to the underside of the bottom plywood disc. These motors are attached to a pair of drive wheels made from PVC and a pair of rubber grommets.

To keep the frame from falling over, a pair of castors are attached to the front and back. These are offset by a wooden block so that they fall short of touching the ground (when the frame is perfectly level) by about 3mm. This means that the frame rocks a bit when starting and stopping. However, the offset is needed to prevent the drive wheels from losing contact with the ground where surfaces are uneven.

Drive to the motors is provided by the Lynxmotion Dual H Bridge device shown in the figure below.

Figure: H-Bridge Driver In Operation

The schematic for this H-Bridge device is no longer available (I bought it in 2000). A Version 2.0 of this device can still be found. The figure below shows the interface layout for Version 2.0. My version differed only slightly (the +5V line at the control interface was at the top as opposed to the second from bottom).

Figure: Lynxmotion Dual H-Bridge Motor Driver

Microcontroller

This robot was based initially on the Technologicalarts Adapt912DG128 microcontroller module shown below. The AdaptDG128 was not a very popular board and Techarts recommended moving on with other choices shortly after it came out. However, I took one and pressed it into service on this robot.

Figure: Side-Standing 912DG128

The main features of this microcontroller are shown in the two figures below. The board is appealing because of the large number of I/O pins available. However, I found the existence of two serial ports to be quite useful. The scematic and microcontroller feature set are shown below.

Figures: AdaptDG128 and Feature Set

The microcontroller is mounted on a protoboard as shown in the figure below. Specific functions were selected from each expansion header using individual jumper connectors (which made the design look someone messy and error prone in the presence of kids and cats). The controller is powered using a quadpack of AA batteries into the regulated supply port.

Figure: Microcontroller Mounted on Robot

Sensors

The sensor suite for this robot was pretty simple. It consisted of six GP2D120 infrared distance sensors, one Devantech SRF05 Ultrasonic Transducer (mounted on a pair of servos in a pan-tilt configuration), and an infrared shaft-encoder.

I also included a small keypad and LCD display to allow basic untethered communication.

Schematic

There aren't any surviving schematics for this robot. However, the basic design can be inferred from the file sysinit.asm below.

Coding Environment

This is a lengthy description that applies to almost all of the NXP microcontrollers so I have included it in Annex A.

Source Code

This robot was coded in assembler using GNU CC/AS and other tools as documented in Annex A. I used GCC 3.0.4 (part of release 2.2 of the GNU Development Chain for M6811/M6812 [GNU2.2]). For the IDE, I used Eclipse Version 3.2 with the CDT.SDK-3.1.2 plug-in. The table below provides a sampling of some of the code I wrote (in assembly) for some of the basic functions.

File Description
mainwander.asm A basic program to move freely and avoid obstacles
keywake.asm An interrupt service routine to determine which key on the keypad was pressed.
lcddata.asm Routines to send data to the two-line lcd display.
range.asm A routine to use the sonar transducer.
sysinit.asm The microcontroller's initialization code. Identifies where the various sensors are connected.

Version 2

In an effort to make the robot somewhat more transportable and a little neater for taking to my kid's school, I decided to swap out the wooden frame for plexiglass and to switch to a Nanocore microcontroller.

Frame Construction

I bought and cut several 12 inch wide plexiglass disks in various colours. I chose a green disk and took the wheels from the wooden version and stuck them on. This is shown in the figure below.

Figure: Plexiglass Base with Motor Drive

Next, I fitted the battery below the base and in between the wheels. There wasn't much to do in this case except to re-route some wires. Previously, the battery was on top of the first platform and this made the whole contraption a bit unstable. Having the battery on the bottom lowers the centre of gravity significantly.

Figure: Plexiglass Base with Battery

In order to keep the battery in place, I needed to fit a metal bracket to the plexiglass (the battery is very heavy so a plexiglass frame wouldn't support it). I bought some duct strapping from Home Despot and bent it in my vice as shown below.

Figure: Bending Metal

The figure below shows the battery now held in place by the metal bracket. Something I've learned so far is that plexiglass does not have the tensile strength of wood - tightening down the bracket snapped the plexiglass disc. It's a good thing I got several of them.

Figure: Battery in Place

The next step was to replace the two square wooden pieces from the original robot with a plexiglass equivalent to hold the casters. The beginnings and ends of this are shown in the figure below.

Figure: Plexiglass Box for Casters

I used Lock-Tight for plastic to bond the plexiglass pieces. However, the casters ended up being several millimeters too low which pushed the drive wheels up enough for them to slip if the floor was uneven by any small amount. To fix this, I used a hammer to tap out the plexiglass plate holding one of the casters in and glued it a bit higher. However, this made an ugly mess out of the transparent box at the back. The mess got uglier when I decided to put in screws to add to the strength of the bonds on the plate I moved. I snapped a drill bit into one of the pilot holes and had to abandon it there (or start all over again).

Microcontroller

The selection of a microcontroller for this project was not very objective or scientific. I wanted to have an "all-in-one" solution for a change (i.e. instead of having to solder or wire wrap extensively). I found an "integrated" board that met most of my requirements at Technological Arts (www.technologicalarts.com). This board is shown in the figure below.

Figure: TechArts SSMI Board

This board provides two reasonably high-capacity motor drivers, an RS232 port, a voltage regulator, a microphone, a small speaker, and terminal interfaces for Sharp distance sensors (GPD120) and servo motors.

The only drawback that I found was that the board was designed for the FreescaleM68MOD912C32 package which TechArts sells as the NanoCore12 series. While this device is great for small projects, it is somewhat constrained in terms of accessible I/O because the C32 variant of the HCS12 is mounted on a 32 PIN DIP package. The C32 chip itself only has 48 pins available so some internal functionality is prevented from leaving the device. In addition, because the mounting package has to look like a 32 PIN DIP, even more of the available I/O is selectively filtered.

Sensors

Bumpers

What I learned from the Version 1 of Marvin is that, regardless of the sensors I applied, there was always a case where the robot ran into something and couldn't "see" it. So, I started with building and attaching some bumpers made of keyboard switches as shown in the figure below.

Figure: Keyboard Switch Bumpers

I used two protoboard strips to attach the switches. I had to make judicious use of super glue to get them to stay since the switch posts weren't strong enough withstand the bumping. Each protoboard has all of the switches attached to a common signal so that any one switch press will active the signal. As shown above, I attached one strip on the left and one on the right to provide an indication of contact and a course indication of which side "hit."

I wanted to make Port A accessible for use with the pushbutton sensors wrapped around the front of the robot. To do this, I planned to use internal pullups on that port and make a reading any time a button pulled the input low. However, the setting of the pullups on PUCR for port A seemed to cause the device some difficulty. I removed PUCR from the test code and connected the input directly to +5 and watched my nascent code work perfectly.

Instead of going to the root of the problem, I decided to flow around it by retrofitting the circuit so that +5 is delivered when a switch is pushed (pulldown required externally).

Sharp GPD12 Sensors

The Sharp GPD12 sensors provide an analog signal that is proportional to the distance to an object in their forward sensing lobe out to about 10 inches (on a good day). The analog signal is bounded between 0 and 5 Volts. I hooked these up to the A/D converters on the CPU. Specifically, I added wires to connect AN00 to AN04 from the SSMI board to AD0-AD3 on the 68s12.

The figure below shows the HCS12 board mounted along with a five Sharp distance sensors and a servo mounted Devantech sonar range-finder.

Figure: Mounted Sensor Suite

Display and Keyboard

I tried to get away without having either a keyboard or a display. I was planning to do diagnostics testing using the voice-box. However, I found while I was testing the Sharp sensors that this arrangement would be very inconvenient.

Installing the display and some rudimentary keys posed a problem because I was rapidly running out of space up top and I was reluctant (ie too lazy) to add a second deck as I had with the original design. Therefore, I built a stand-up display mount as shown in the figure below.

Figure: Display Mount

The display mount is the plexi triangle on the left side of the robot. I used glue to put the pieces on but I have been finding that reinforcing with screws is often necessary in many key "high traffic" areas.

I used a couple of four-wire connectors to connect the power and display wires. These wires are shown hanging off to the side in the figure. For now, I think I can slip by the diagnostics phase by using one of the two bump sensors for input so I may not actually have to wire up the switches or the LEDs on the display board.

I hung the 2-line display off of Port B. The display uses the standard HD44780 interface. I use the interface in the nibble mode - that is, bytes are sent one nibble at a time on the high order four bits of Port B. Bit 0 of port B is the "toggle" bit (the E signal) that causes the display to take action on a nibble. Bit 1 is used for mode control - zero indicates an LCD command while 1 indicates LCD character data. I never plan to read from the LCD so the R/W signal is wired low.

The only thing left was to build and install the voice-box on a permanent basis. This adventure will be inserted here shortly.

Schematics

A partial schematic is shown below.

Figure: Partial Schematic

Coding

I coded this robot using Eclipse and the GNU chain for 6811/12. Some of the vagarities of coding for this microcontroller are discussed below.

System Clock Initialization

The Nanocore has an on-board 8 MHz crystal which causes the OSSCLK signal to oscillate at 8MHz. The figure below shows the Clock Generation Circuitry inside the C32.

Figure: CRG

If nothing else is done at initialization time, then the Phase Lock Loop (PLL) is not engaged and the OSCCLK signal is passed on to SYSCLK. The Core will run at 8 MHz and the Bus Clock will run at half this rate (4 MHz).

If the PLL is engaged, then the Core and Bus clocks can be modified according to the following equation:

PLLCLK = 2 * OSCCLK * (SYNR + 1) / (REFDV + 1)

With SYNR=02h and REFDV=00h, the PLLCLK is set to 48MHz and the Bus Clock runs at half this rate (24MHz). This is the maximum rate that can be supported by this device. To actually turn the PLL on requires a bit to be set and some waiting around while the PLL stabilizes. I found some code to do this reliably at Jonathan Volvano's excellent controller web repository. It's called pll.c in my code tree.

System I/O Initialization

The interface ports on the NanoCore can be digitial or special purpose (analog, pulse width modulation, etc) or inputs/outpus depending on their function. However, because the SSMI has hard-wired peripherals, the choice of which NanoCore output is used for what purpose has already been decided. In this little sub-section, I explain how I went about configuring each I/O port on the NanoCore depending on its current (and planned) function.

The motor driver circuit on the SSMI board uses two TLE5206 drivers. Each driver chip requires two digital inputs and provides one digital output. The TLE5206 are driven according to the following truth table:

IN-1IN-2Function
LowLowBrake
LowHighCounterClockwise
HighLowClockwise
HighHighBrake

In order to provide signalling for both driver circuits, NanoCore outputs PT0-PT3 are used on the SSMI board. These bits must be configured as digital outputs. The motor drivers can signal an error condition and these are hard-wired to PM2 and PM3 so these must be configured as digital inputs.

Port M takes two digital inputs from the TLE5206 (as just mentioned). These are PM2 and PM3. PM0 and PM1 are connected to the sonar interfaces and are presumably used to initiate a sonar pulse so these must me digital outputs. On the SSMI board, PM4 is used to drive a speaker. However, I plan to cut W2 on the board and make PM4 and PM5 inputs from the switch banks (bumper switches all around the robot). Therefore I will configure PM4 and PM5 as inputs as well.

We have only accounted for four of the Port T pins so far. PT4 and PT5 are hard-wired to servo interfaces so they must be configured as a PWM output pins (although only PT4 will be used in the short term).

Note that PT5 is supposed to control a servo as well except that there is no way on the C32 for it to connect to the PWM module so it may turn out to be a dud anyways.

PT6 and PT7 will be used as timers to record the time-of-flight (TOF) from Devantech sonar ranging devices (actually, only one will be used in this first design). Therefore, PT6 and PT7 must be digital inputs.

We can now configure Port T and Port M with the following declarations:

Sharp GP2D120 Sensors

The GP2D120 can sense objects out to about 10 cm. When nothing is present to be sensed, the voltage on the signal wire from the GP2D120 is zero. As an object gets closer, the voltage increases to the maximum allowed (VCC). We connect this "analog" voltage signal up to each of the A2D pins and then convert the voltage to a number.

Now, to make the code work for the sensor array, we need to set up the A2D port. Take note that AN0 is used on the SSMI board as a low power sensing circuit (we will deal with that later but we need to keep it in mind as we configure the A2D port).

The A2D on board the C32 is an 8-channel device with 8 or 10 bit resolution. A schematic of this device from the manual is shown below:

At this point, the details of exactly how the A2D works are not important. What the A2D allows us to do is take a measurement of a voltage on each of the input pins AN0-7 in much the same way as using a voltmeter to read a voltage. Instead of giving us a digital readout of the voltage, a digital number representing this reading is stored in the results registers ATD0-7.

The A2D needs a way of figuring out what will be the largest voltage difference to expect on each input line. This voltage different is set on VRH and VRL. Normally, the different is simply set to the power supply rails (+5v and ground) to give us a reference voltage of 5V.

The numbers in each result register can be 8 or 10 bits in size. This is called the resolution of the measurement. If we set the resolution to 8 bits, then each result will be 8 bits long and the voltage range (0-5V) will be divided up into 256 distinct possible values. If we need a greater "resolution" (i.e. we need to divide the voltage range into smaller steps for some reason), we could select 10 bits for each result.

As a final note on the A2D circuit before we configure registers, it takes a certain amount of time for each voltage to be accurately measured. This amount of time is affected by the speed of the CPU. In our case, the reference clock for taking measurements is the Bus Clock (which is 24MHz if the PLL is engaged as discussed last time).

So now, let us whip through each of the registers in the A2D configuration block (chapter 8 of the MC9S12C128V1 manual) and set them up.

ATDCTL0 and ATDCTL1 are reserved and need no configuration.

ATDCTL2: We should set bit 7 to 1 to ensure that normal A2D operation is taking place. Note that this bit is, by default, 0 so the A2D will be off unless we set this bit. Also, we will want to set bit 6 to 1 as well to enable automatic fast flag clearing. Under normal circumstances, when a measurement ("conversion") is complete, a Condition Code Flag (CCF) is set to indicate the completion. In order to clear the flag, we first need to read the A2D status register (ATDSTAT1) and then read the result register. I propose to just read the result whenever I want and not wait for the device to tell me that a new conversion is ready. There are places where waiting would be appropriate. This is not one of them.

ATDCTL3: This register sets the number of conversions that are performed per sequence. What that really means has a lot to do with the way the results buffer is configured and the number of pins that we want to sample. We would like to do 8 conversions in each sequence, one conversion for each input pin, and we would also like to put each result in the appropriate results register (e.g. conversion results for pin AN0 into buffer register ATD0, AN1 into ATD1, etc).

ATDCTL4: As noted earlier, we want to set this device for 8 bit resolution so bit 7 of this register needs to be 1. Because we anticipate our GP2D120 signals to change slowly in relation to our 24MHz bus clock, there is no real reason to want to prescale the input clock so we leave these at their default values.

ATDCTL5: This register controls a number of formatting elements of the results register. Bit 7 is used to left- or right-justify the results in each register. Clearly, we'd like to right justify things so that they have their proper binary values. Leaving bit 6 as a zero will cause the results to be interpreted as unsigned values (which allows the full range of numbers to be used). We need to remember this when we assign a data type to the C values that will be used to hold the results. Now, in order to set the A2D up to convert for us all the time, we need to set bit 5 to 1. Setting bit 4 allows us to sample across all of the available input pins. We can leave the last few bits alone.

This completes the control register setup. With this setup, we can simply ask the A2D for the results in any one of the results registers and it will give us the last sample that it had last taken. In this case, we do not need to use the status registers or set up conversions in advance.

Here is the code snippet that will set up our A2D.

In order to read the values of the A2D conversions, we need to read the lower byte of each result register (ATDDRxL where x={0,1,2,3,4,5,6,7}.

Pan Head for Sonar Transducer

The pan head for the sonar transducer is a Hitec servo. This requires us to set up PWM on at least one output. There are a number of considerations to do this and these are covered below.

Setting PWM on PT4 is straightforward but it does require some analysis. If you're using the 48 or 52 pin version of the C32, then you need to re-route PWM0-PWM4 from port PP to port PT.In the figure below, the output pins shown in bold are not included on the 48 or 52 pin packages so you can't get at the PWM module through port P.

Figure: Ports Available

To get around this problem, it is possible to re-route the PWM pins using the MUX shown in the figure above.This is done using the port T module routing register MODRR shown below (Ref B).

Figure: MODRR for Port T

Set the register bits to 1 to set the associated PWM to the port T pin (e.g., MODRR0=1 makes PT0=PWM0). Since we are going to use PW4, we set MODRR=10h.

Now that the pins are set up, we need to turn on pulse width modulation on the selected pin. This is done through the PWME register as shown in the figure below.

Figure: PWM Enable Register

We set PWME=10h to enable pulse width modulation on PT4.

The starting polarity (high or low) can be specified for each PWM channel using the PWMPOL register shown below.

Figure: Polarity Selection

We will start the polarity for all PWM channels low so we set PWMPOL=0h.

The duty cycle of any PWM channel in the C32 is going to be based on some clock source and all clock signals are based on the bus clock. We've chosen the bus clock to be 24MHz. In the PWM module, two clocks, A and B, are derived from the bus clock by a pre-scale operation. Each of A and B can then be scaled again to provide two more clock sources - SA and SB (scaled A and B).

Each PWM channel can be set to use each of these four clock sources by the setting of the PWMCLK register shown below.

Figure: PWM Clock Selection

The clock that is used as the source of PWM for each channel is set according to the following table:

PCLK0 0=A 1=SA
PCLK1 0=A 1=SA
PCLK2 0=B 1=SB
PCLK3 0=B 1=SB
PCLK4 0=A 1=SA
PCLK5 0=A 1=SA

What should be clear from the table above is that only PWM channels 2 and 3 can make use of either the B or SB clock. This might be something to keep in the back of our mind. For our purposes, we will set PWM channel 4 to use the scaled clock SA. Therefore, we set PWMCLK=10h.

Clock Frequency

To set up the frequency of the chosen reference clock (SA), refer to the following diagram. Note that the diagram only shows the A clock. All computations are the same for the B clock.

Figure: FreqPWMX Calculation

The PWM module uses the Bus Clock as the master clock source. The Bus Clock is then divided or pre-scaled (in Motorola language) to obtain an A and a B clock. The Bus Clock is divided by 2 raised to the power of PCKA or PCKB in the PWMPRCLK register (shown in the figure). The PWMPRCLK contains three bits each for PCKA and PCKB so the maximum scaling possible is 1/(2^7). If PCKA or PCKB is zero, the Bus Clock is passed on to A or B unchanged.

At this point, we can use just the A or B clock signals as the reference for our PWM channel. As we indicated earlier, this is done by setting the PWMCLK register accordingly. In the figure above, this choice is shown as the path marked with a number 1. Following path number 1 causes the A clock to be divided once more - this time by the value in the PWMPERX register (where X represents the channel number for the channel we are setting up). There is one 8-bit PWMPER register per PWM channel. This register specifies the period of pulse width modulation signal in terms of the channel source clock (in this case, the A clock). For example, if the A clock was 10MHz and the PWMER register for the channel being set up was 4, then the frequency of the PWM on that channel would be 10/4=2.5 MHz (in left aligned mode - which we'll talk about shortly).

So what if we set the PWMCLK register to use the SA (or SB) clockinstead of A (or B)? This choice is shown as path number 2 in the diagram and it gives us an opportunity to scale the A (or B) clock further. This results in a scaled A (SA) or scaled B (SB) clock reference.

As shown in the diagram, we divide the A clock by two times the value found inthe prescale register PWMSCLA. Then we do as with path number 1and divide this value by the channel PWMPER register.

If we use the scaled A or B (SA or SB) clocks, then we have three variables to try to fix - PWMSCLA, PWMPRCLK, and PWMPERX. To determine what values to stick in the registers, we need to know a bit about what we plan to do with the PWM and what optimal frequency to set. On the PWM output, I plan to put a Hitec servo and, according to their documentation, a pulse width modulated signal with a 20ms period is optimal. A 20ms period is equivalent to 50Hz (which is really low).

Alignment

We now need to decide if we are using left or centre aligned PWM. I'm going to use left aligned PWM. Let's elaborate a little about this since several register settings are involved. First, left alignment meansthat at the PWM signal snaps to the value we set in the PWMPOL register at the beginning of each period (high or low at the start). I had arbitrarily decided earlier to set the starting value to zero.

To set left aligned PWM, set the appropriate channel bit in the PWMCAE register. A zero sets left alignment but the 9S12C32 resets by default with PWMCAE set to zero - so no work is required here unless we want center aligned channels.

Duty Cycle

The duty cycle of the PWM signal on any given channel is determined by the PWMDTYX register (where X is the channel number). This register determines the percentage of time that the PWM signal is going to spend high or low during a period. This register is intimately related to the PWMPERX register - one of the registers that we need to set in order to obtain the chosen frequency on our PWM channel.

For each PWM channel (in left aligned mode) in the9S12C32 PWM module, there is an 8 bit counter that counts up at the rate determined by the chosen clock (A or SA). Every time this counter is incremented, a comparison is made between it and the PWMDTYX and PWMPERX registers. If the counter is equal to the PWMTDYX register, then the PWM output signal snaps to the state opposite of what was set in the PWMPOL register. If the counter is equal t the PWMPERX register, then the counter is reset to zero and the PWM output snaps to the value set in PWMPOL. This action builds a square wave that is high or low up to the count in PWMDTYX and then switches to the opposite state for the remainder of the period of length specified in PWMPERX.

What does that mean to us? Well, an example in the specs was given as follows: Let A=10 MHz, PWMPOL=0 PWMPERX=4, PWMDTYX=1. Therefore, the frequency of the PWM channel will be 10/4=2.5 MHz which has a 400ns period. Since PWMPOL is zero, the starting state for PWM output will be zero at the start of any new period. And, with PWMDTYX=1, we know that the PWM signal will only be low for the first 1/4 of the total period at which time it will snap high (therefore the duty cycle is (4-1)/4=3/4=75%).

The example illustrates that we do not have significant granularity on our control of the duty cycle. Essentially, we can control the duty cycle only in increments of 1/4 or 25%. If we are using PWM to control the speed of a motor, this limits us to being able to chose between 1 of four speeds. I'd like a bit more granularity, please and thanks.

If we set the period to be as high as possible, then we can, in theory, get up to 255 different steps. Do we need that many? Probably not but it gives us great flexibility. So, let's set PWMPERX to 255 (FFh) and set our duty cycle to be 50% to start. This means PWMDTYX=128 (80h).

Bringing It All Together

Now, assume a lower bound of 50Hz. That is, FreqPWMX=50Hz (from the flowchart above). We chose PWMPERX = FFh to get precision. If we chose to use SA as our base clock, we need to select PWMPRCLK and PWMSCLA. To avoid doing inverse logarithms, let us arbitrarily choose PWMPRCLK = 04h. We can now solve for PWMSCLA:

PWMSCLA=(Bus Clock)/(2*FreqPWMX*PMPERX)*(2^PWMPRCLK)
       = (24,000kHz)/(2*(0.05)*16*255)
       =58
		

This is a reasonable number for use in the initialization and gives us 255 steps in the period over which to modify our duty cycle. So, to initialize, we should set:

An initialization routine for the PWM channel would look something like the following:

Granularity Problems

When I finally sat down to figure out which values of the 8-bit PWMDTY register would correspond to which positions on the servo, I quickly ran into some difficulties.

PWM in the 9S12 involves specifying a period and a duty cycle. The duty cycle is a percentage of the period.

Figure: Duty Cycle to Period

As the figure above shows, the duty cycle is specified by the PWMDTYx register and the period is specified by PWMPERx register (in conjunction withclock settings).

Herein lies the problem: The servo needs a period of 20 ms but the motion of the servo is governed by the variation in the duty cycle around a center of roughly 1.5 ms. Recall that I set the 8-bit PWMPER register and the prescale clocks so that the period was 20 ms. The 8-bit PWMDTY is then spread acrossthe whole 20 ms period.

That means that each bit corresponds to:

20ms/256bits=0.078ms/bit

In a perfect world, a full traversal of the servo from left to right can be commanded by a change in duty cycle of about 1 ms (approximately 1.0 ms to 2.0 ms). That means that the full range of motion results from just:

1ms/0.078ms/bit=12 bits

out of the whole 256 bits worth.

To increase the granularity, I changed to using 16 bit PWM by makingthe following changes:

  • DDRP = 0x02; Sets the P register for the 9S12 as an output. P holds the PWM functionality.
  • PWMCTL = 0x10; Concatenates pins 0 and 1 for PWM. Note that 0 represents the high order byte and 1 represents the low order byte. Now the output comes from pin 1.
  • PWME = 0x03; Enable both pins 1 and 0 for PWM.
  • PWMPOL = 0x02; Start with high polarity and go to low.
  • PWMCLK = 0x00; This is a key instruction. It sets the clock source for PWM channels 0 and 1 to A (not SA). Therefore, the scaling factors for SA do not come into play.
  • PWMCAE = 0x00; Pulse width modulation in left aligned mode.
  • PWMPRCLK = 0x03;
  • PWMPER0 = 0xea;
  • PWMPER1=0x60;
These last three changes are interrelated. Our target frequency is 20 ms = 50 Hz. From page 377 of ref A:
Target Frequency=(Clock Source)/(PWMPERx)

Our clock source = 24,000,000 Hz. Therefore:

PWMPERx=24,000,000/50=480,000d

It should be obvious that, with a 16-bit PWMPERx register alone, we can't command a 20ms period because we can only reach 65,536d.

We need to divide the bus clock down in order to be able to divide the period up among the whole range spanned by the PWMPER register. How much?

480,000d/65,536d=7.32

There is a way to divide the clock source by 8 (closest to 7.32). This is done by setting PWMPRCLK = 0x03 (see table 12-7 of ref A). Of course, because of the round-off, PWMPER cannot be 65536d=0xFFFF. We need to figure out what it can actually be:

PWMPERx=(24,000,000/8)/50=60000d=0xEA60

Hence, we set PWMPER0 = 0xEA and PWMPER1=0x60; To set the hitec servo, we need to swing between about 1 ms and 2 ms duty cycle. Out of 20 ms, this represents 1/20=5% and 2/20=10%. Therefore:

Far left = EA60*5%=0x0BB8;
Far right= EA60*10%=0x1770;
Center   = EA60*7.5%=0x1194; 

So now we have a much finer granularity in commanding the position of the servo as long as we stay between 0x0BB8 and 0x1194. That's potentially 1500 increments. Of course, we don't really need all that much detail given that everything else is so imprecise. I'll probably only use about 50. However, I now have the option.

Bumper Code

The bumpers consist of two strips of old keyboard switches connected in parallel. Each strip has a single LED on it. The simple schematic is shown below.

Figure: Bumper Schematic

When any one switch is closed on the bumper, a circuit is created. If one side of the circuit is connected to ground and the other to an input on the NanoCore then, in theory, I should be able to detect when a switch has been closed. I keep an eye on the input wire and as long as it is logic 1, then the switch has not been closed. When a switch does get closed, the input gets connected to ground and I read a logic 0.

What this means from a controller perspective is that I need to connect the bumper to an input that has an internal pull-up resistor (or I need to connect one).

Pull-Up Resistors

The purpose of a pull-up or pull-down resistor is to make sure that a certain voltage exists at the input of a device when the input is left "floating" - that is, when the input is not explicitly connected to either the supply voltage (e.g. +5V) or ground. A floating input can happen when an input to a device is connected to a switch. When the switch is open, it essentially creates a dangling wire.

The figure below shows an input pin on some device. A pull-up resistor is connected between this input pin and +5V through a resistor R. A switch is also connected to the circuit. One terminal is connected to ground and the other to the bottom of the resistor R (which also connects to the input to the device). The switch is currently open.

Figure: Pullup Resistor in an Open Circuit on a Device

This input pin has the characteristic that it looks like it has an infinite resitance (also called impedance) from the outside. That is, if you connected a wire to the input pin, it would look like the wire wasn't connected to anything. In other words, current cannot flow down the wire into the input because it looks like an open circuit. Complimentary Metal Oxide Semiconductor (CMOS) circuits have the property of looking like an infinite resistance (impedance).

Therefore, for our purposes, we could simply pretend like the Device in the figure above doesn't even exist. All we are concerned with is what the voltage will be at the bottom of the resistor R - where the input pin connects. This is shown in the figure below.

Figure: Pullup Resistor in an Open Circuit on a Device

Ohm's law says the following: V=IR (voltage=current times resistance). In the circuit above, no current flows because there is no closed path between +5V and ground. Therefore, the voltage dropped or consumed by the resistor is zero. In other words, the resistor looks just like a piece of wire. The voltage at both ends is +5V. So, the voltage at the input pin (connected to the bottom of the resitor) is +5V.

If we close the switch as shown below, we suddenly have a current flow. None of it goes into the input pin though because it looks like a dangling wire (infinite resistance).

The voltage must go from +5V to 0V as it makes its way through this circuit so where does it get "dropped". In fact, the only device that reacts to the current is the resistor. All 5 volts are dropped across this resistor with the result that the voltage at the input pin is 0V.

Figure: Closed Circuit Pullup

It may seem like a silly proposition, but, why don't we just connect the input pin directly to +5V (i.e. forget the resistor). If we did that and closed the switch, we would essentially short the power supply by connecting the positive terminal to the negative terminal. If you've ever put a spanner across a +12V battery or licked your finger and stuck it into a wall socket, you are well aware of the result. Maximum current flows (because there is no resistance) and things melt. Nope. The resistor is definitely needed.

In addition to having pull-ups, the bumpers needed to be connected to an input port that wasn't already taken up on the SSMI board. I eventually settled on using PE0 and PE1. These pins are used for IRQ and XIRQ and the literature states that in single chip mode, they become general purpose inputs with pull-up resistors already enabled. It seemed like a perfect marriage.

However, over the course of several hours, I discovered that these inputs are operational even in single chip mode (which, in hind sight, makes sense). It took a while to realize that my code was being vectored to an ISR - but only on the occasion of a button push which made debugging a bit challenging.

I solved half of the problem by disabling IRQ in the code with:

XIRQ is disabled by default on power up but is then enabled explicitly by clearing the X bit in the CCR. Once cleared, the X bit can not be set. If I never clear the X bit, it should always be a general purpose input - so why was it misbehaving?

A quick and random check of the X bit revealed that it had been cleared... but by who?

The list file is the only place to be perfectly sure of any code. I found the perp in the _premain routine:

The TAP instruction sets the CCR with the contents of the A register. Clearly, I didn't want this to happen.

Some previous work told me that crt1.o or crt0.o was responsible. I went to my "old faithful" site at The Encoder for assistance and, of course, found it. At that site, it advises creating your own _premain function to override anything performed by the compiler. The first time I tried, it failed. So I cut and pasted code from the encoder into my source and discovered that the little underline at the start of premain is actually two underlines!! You just can't tell becuse there is no space between them.

I've used the premain function to turn off the COP:

Now my listing file shows:

Voice Audio

I performed a hack on the SP03 voice module as outlined on the Acroname web site. This mod allowed the voice module to be piped out to a larger speaker with more power. Now the robot can be heard across the room. The only problem is that the previously inaudible noise floor can be clearly heard. I tried to reconfigure my board a bit to see if I could cut the electrical noise but to no avail....

Figure: Modified SP03

The figure above shows the new speaker wired to the main platform. I just wonder how long it will take before my son notices that one of his computer speakers doesn't work...

Amphibious Assault

During the development of this robot, my son's grey forces successfully defended my lab from several amphibious assaults by the evil Tan Army...