Thursday, December 17, 2009

Building and programming the IMU

A couple of months ago, I made a post discussing the inertial measurement unit that we're hoping to use on BRB. A month and change after that post, the boards arrived from BatchPCB, and finally, with Christmas upon us, I've had a chance to put the thing together. Assembly was straightforward. Hot air is a must for the gyros, since the pads on the LGA package don't actually come to the edges, and is helpful for several other components (those who have hand-soldered SMD crystals will see how hot air might help). This was my first time using hot air. It's easy enough to get the hang of, and a great tool to have around, so if you haven't got a rework station already, maybe LGA would be a good excuse to make the investment.

Anyway, the final product is shown in the bottom half of the image below. I've made just one modification relative to the schematics in the original post, removing the high pass filters from the gyros. While these may not have been a problem in the final application, with a time constant of about 4.7 seconds, they would have interfered with the calibration discussed below, and at any rate seem to be unnecessary.


Up top is a breadboarded support circuit which supplies 3.3V regulated to the IMU and reads its output via TWI, optionally logging it on the SD card and displaying it on the LCD. I had this lying around from some work I did building TWI motor controllers for another project, and it seemed like the thing to use here.

One surprise from this circuit was that the z-axis magnetic sensor seems to have an unusually large bridge offset, and the opposite polarity from that given in the datasheet. The datasheet suggests that the latter can be corrected using the set/reset straps, but I'm not sure that's possible with the set/reset circuit we've built. At any rate, the device output is within the ADC range, and the polarity can be resolved in software, so I've decided to let it be.

(As a quick note for anybody who might be planning a similar IMU, I'm considering using the HMC5843 for future units. It's digital, with an I2C interface, contains all three axes in one chip, has about the same resolution we're getting with the existing setup with a tenth the cross-axis effect, and would cost about half as much as the 1051/1052 combination. Is anybody aware of a drawback?)

The software is quite straightforward. Atmel has published application notes on using the microprocessor as an I2C master and as an I2C slave. As part of the previous project noted above, I'd built a simple messaging protocol on top of this that I've reused for this project to allow us to easily read and reconfigure the IMU on the fly. An even simpler scheme could certainly be used, perhaps similar to that used via SPI by the accelerometer.

The accelerometer operates in SPI mode 3. The interface is straightforward -- after pulling the select low, an address byte is written whose two high bits control read/write and whether the address is automatically incremented after each byte. SPI transfers data in both directions simultaneously, effectively rotating the slave's one-byte buffer into the master and the master's into the slave. Reads and writes are therefore both performed by stepping the SPI as many times as you'd like, reading or writing successive bytes. Each query is ended by letting the select pin go high again. The accelerometer is activated by writing a 1 to bit 6 or 7 of the CTRL_REG1 register. Until you've done this, you'll find it returns zeros for all axes. Once powered up, the registers at 0x28-0x2D will give you the acceleration along each axis from the device's 12-bit ADC, returned by default as a 16-bit signed integer.

In addition to reading sensor values, the current software includes a timer to ensure that once per second, a set/reset cycle is queued for the magnetic sensors. Resets are performed immediately before the next measurement of the compass value. Each reset consists of a 5 microsecond pulse to pin SR_IN in the schematics, giving a reset at SR_OUT on its rising edge, and a set on its falling edge. These pulses do not correct the bridge offset, but instead desaturate the sensors after any exposure to strong magnetic fields.

That's it! The completed IMU seems to reliably return data for all nine degrees of freedom to a host. At this point, the data is raw. It includes offsets, scale factors, and axis discrepancies unique to each sensor. The axis discrepancies deserve a little elaboration. I can think of four ways that this might creep into the design:
  • The accelerometer would have been very difficult to mount so that its axes correspond to those of the gyros. Instead, we decided to leave the conversion to the software, in favour of a simpler hardware layout.
  • As I've already noted, the z axis magnetic sensor seems to have the opposite polarity from that in the datasheet. Again, this can be easily corrected in software.
  • Because the IMU is built from individual sensors, each placed by hand, there's little reason to believe that the x axis of the gyro will coincide with that of the magnetic sensor, for example, even if they were designed to. If this is significant, it'll have to be calibrated for.
  • Magnetic sensors also exhibit what is called a cross axis effect. This effect arises from distortion of the magnetic field within and around the sensor itself, so that the actual measurement axes may not even be orthogonal.
I'd like to correct those on the IMU, returning calibrated data to the host. How can we manage that without investing in Helmholtz coils and other equipment? More on that (hopefully) in my next post.

Friday, October 30, 2009

Adventures in NAND Land

In a previous post, I talked about setting up an SD card to boot Ångström on the BeagleBoard. One thing that has been bugging me is that I need to hold the USER button on the BeagleBoard in order to boot to the SD card. If I don't do this, it boots partway, but stops before the kernel is loaded. What's going on here, and what can we do about it?

A great starting point if you have questions like this is the OMAP35x Technical Reference Manual. This sprawling document holds the answers to a lot of BeagleBoard questions. Of particular interest is Chapter 25, which details the intialization process. The basic steps of initialization are as follows:
  1. Preinitialization
  2. Power/clock/reset ramp sequence
  3. Boot ROM
  4. Bootloader
  5. OS/application
The USER button on the BeagleBoard controls the state of the SYS.BOOT[5] pin on the OMAP3530. The boot ROM uses this value to decide between one of two boot orders:
  • NAND, USB, UART3, MMC1, if the button is not pressed
  • USB, UART3, MMC1, NAND, if the button is pressed
Once the boot order is selected, the boot ROM goes through the devices in that order, looking for one that is bootable. Once it locates a bootable device, the boot ROM copies the bootloader from the device to on-chip memory and executes it.

To understand the bootloader, it's worth taking a closer look at the Linux boot process, which looks roughly like this:
  1. System startup
  2. Stage 1 bootloader
  3. Stage 2 bootloader
  4. Kernel
  5. Init
You can see there is some overlap with the initialization process above. Step 1 here combines the first three steps of the initialization process; steps 2 and 3 elaborate on step 4 above; and steps 4 and 5 here correspond with step 5 above.

The first-stage bootloader must be small enough to fit in the OMAP3530's on-chip memory. Remember, at this point the device has not initialized external memory, so it has only 64 kB of SRAM to work with. Our first-stage bootloader is called "X-Loader". A signed copy of X-Loader, called "MLO", is the first thing we copied onto the SD card when we set up Ångström. The first-stage bootloader initializes external memory, copies the secont-stage bootloader into memory, and executes it.

Our second-stage bootloader is called "U-Boot". Its job is to load the kernel and root filesystem. On our bootable SD card, "uImage" is the kernel image, and the second partition is our root filesystem.

Now that we understand the contents of the SD card a little better, we can return to the main question: How can we avoid having to hold the USER button on every boot?

My first thought was, if the boot ROM is looking in NAND first, why don't we just put the whole system there, instead of on the SD card? I had come across an article which describes how to do exactly that, so it seemed like a sensible enough starting point.

Ultimately, however, I had some difficulty installing MTD utils. I later discovered that a stable tarball could be found here, but the hiccup stalled me just long enough to wonder, is it even a good idea to put all of this in NAND?

Talking with other developers on the #beagle IRC channel, I realized that the main reason to put the system in NAND is to operate without an SD card attached. This may be useful in a production environment, but for development purposes, the general wisdom seems to be that keeping the bootloaders, kernel, and filesystem on the SD card increases flexibility.

So, why wasn't the SD card we prepared previously booting without the USER button depressed? On the BeagleBoard, the OMAP3530's NAND is partitioned as follows:
  • 0x00000000 to 0x00080000: X-Loader
  • 0x00080000 to 0x00260000: U-Boot
  • 0x00260000 to 0x00280000: U-Boot environment
  • 0x00280000 to 0x00680000: Kernel
  • 0x00680000 to 0x10000000: Filesystem
Without the USER button pressed, the boot ROM will always check NAND first, to see if it's bootable. To ensure that NAND would not be recognized as bootable, erase the contents of the first NAND partition as follows:
  1. Reset the BeagleBoard.
  2. When U-Boot comes up, hit a key to stop autoboot.
  3. At the prompt, enter "nand erase 0 80000".
This will erase the contents of the X-Loader partition. For good measure, you can also erase the contents of the U-Boot environment using "nand erase 260000 20000". The next time it loads, U-Boot will replace the environment settings with defaults.

Finally, it is necessary to update U-Boot's "bootargs" and "bootcmd" as noted on the BeagleBoardBeginners page. With an Ångström SD card in place and the first NAND partition erased, the BeagleBoard now boots from the SD card without the need to hold the USER button down.

Tuesday, October 27, 2009

Git Repository Added

We've been working on BRB with the aim of competing in SparkFun's Autonomous Vehicle Competition. Alas, it looks like we'll be on Baffin Island the day of the competition, so we'll be unable to participate. Nevertheless, we are going to forge ahead, and hopefully will be that much more prepared for the 2011 competition!

I have set up a repository at GitHub, where we plan to keep up-to-date copies of all our schematics, board layouts, and so on. The repository currently contains CadSoft Eagle files for the interface board and the inertial sensor board.

Saturday, October 17, 2009

Introducing the BeagleBoard

Our first step in building BRB will be to put together a navigation system comprised of an IMU, a GPS, and a Kalman filter. Once BRB knows where it is, we will look into moving from place to place.

On the GPS side of things, we will be using a Topcon TG-3 100 Hz GPS which was generously donated by Topcon for another project. We hope this will give us plenty of data to work with.

For the "brains" of the vehicle, we ordered a BeagleBoard from Digi-Key. The BeagleBoard packs a pretty impressive set of features:
  • 600 MHz TI OMAP 3530 processor
  • 128 MB RAM
  • 256 MB NAND flash
  • Expansion port with I2C, SPI, UART
  • SD/MMC card slot
  • Audio, video, USB, and so on
This is arguably overkill for our project, but we wanted to be absolutely certain that we would not run short on horsepower.

One hitch in working with the BeagleBoard is that it uses 1.8 V logic levels, while our peripherals use 3.3 V. This can be overcome with a level translator IC. We've decided to put this IC, as well as a few other interface components, on a separate board. This keeps our design as modular as possible, so that if we want to change one part, we won't need to rebuild everything.

We have broken everything down into four boards, for the time being:
  • The BeagleBoard
  • The IMU board
  • The GPS receiver
  • An interface board
We sent the interface board off to BatchPCB at the same time as the IMU board, so we hope to have both back in the next couple of weeks. The interface board contains a mating connector for the BeagleBoard's expansion header, an ADG3304 level translator IC, a CAT6219 3.3 V regulator, and connectors for the IMU and GPS. The schematic is below:



In the meantime, I have been working on getting the BeagleBoard up and running. The first step is to get the Ångström Linux distribution installed. The BeagleBoard ships without cables or connectors, so the first thing to do is build a serial interface cable. These can be purchased, but they are easy enough to build.

It is possible to get Linux installed on the BeagleBoard using Windows, but support for this method is limited. I highly recommend working with Ubuntu. Much of the community support for BeagleBoard assumes you are using Linux, so things get a lot easier if you are.

Using Ubuntu, BeagleBoardBeginners is an excellent tutorial that will walk you through as far as booting Ångström from an SD card.

While running Ångström on the BeagleBoard is a fun milestone, you may wonder, how do I do anything with it? I found a few particularly illuminating links, which I would like to gather here.

The Beaglebot project includes loads of information on circuits and software needed to run a few servos and motors using a BeagleBoard. A quick browse through the source code will give you an idea of how things work. Basically, everything on the BeagleBoard is exposed as a device, and can be manipulated from the Linux shell, or using common C functions.

I spent some time looking for a program akin to "Hello World", and found a couple of helpful links:
  • A Make article showing how to blink an LED using GPIO.
  • A Google Groups post which takes this even further, showing you how to use GPIO, onboard LEDs, and buttons.
Finally, we are eventually going to need to run a bunch of these commands from within a C program. So how do you compile a C program for the BeagleBoard? You can do this from Ubuntu using Sourcery G++. To find the latest release, follow the "Download" link, then choose "GNU/Linux". Once you've installed Sourcery G++, look for the Getting Started Guide, which includes a small tutorial program.

Next, I want to write a small program to get the GPS running and log its output. We can then do some basic tests to quantify its accuracy.

Friday, October 9, 2009

The Inertial Measurement Unit

The inertial measurment unit (IMU) board for BRB is finally off to BatchPCB. The IMU will interface to a BeagleBoard, which will do the heavy lifting, by TWI.

The IMU is controlled by an ATMega168, and includes a 3-axis LIS3LV02DQ accelerometer, 3-axis gyro consisting of a single-axis LY510ALH and dual-axis LPR510AL, and a 3-axis compass consisting of a single-axis HMC1051ZL and 2-axis HMC1052L. All this for less than (or about) $100.

Schematics are below, starting with the microcontroller and accelerometer (which interfaces with the microcontroller via SPI):


then the compass (with attendant amplifiers and a MOSFET pair for setting and resetting the sensor):


and finally the gyros, for which the full scale will be selectable on the fly as either 400 degrees per second or 100 degrees per second: