Friday, September 28, 2012

A review of the ADXL345 and BMA180

First off, the ADXL345 is exactlty what it claims to be. It's my own fault for not reading the data sheet, or rather reading it but not comprehending the information in it.

So, my first experience with digital accelerometers was with the Bosch BMA180. The part had 14 bits of precision and 6 scale settings, from 1g to 16g. When you switched to the lower scales, you got more precision, as expected.

I finally got all 11 DoFs working on the 11DoF board. The first SPI part I broughs up was the ADXL345, so I learned about its SPI protocol that way. For one thing, those of you used to I2C, SPI is different! For instance, on both the ADXL345 and the gyro on the 11DoF, the L3G4200D, the register addresses are six bits. This is fine, since that is a big enough space. You send this address as the first word of any transfer in either direction from these devices. However, both devices use 8 bit words in the protocol. The other two bits control the direction of transfer for the other words, and whether the device is to expect more than one word -- that is, whether it should increment the register address pointer for the next words.

For instance, say you want to read all the measurement registers in the ADXL345. The register map says that this is registers 0x32 through 0x37. Since SPI is a full-duplex protocol, every time you send a byte, you receive a byte. So, send 0x32, ignore what you receive, then send six more bytes (doesn't matter what, I used 0) to read 6 bytes that you care about. Right? Almost, but not close enough. I did this first with the ID register, and it worked, sometimes. But, when I actually tried to read the data, I got back the same word six times. What gives? First, bit 7 of the address is read/#write. You have to set it to read the register, otherwise it interprets the MOSI data as data to be written to the registers. The data registers are read-only, so the part ignored me. Next, bit 6 is the multibyte flag. Set this bit if you are going to read/write multiple registers in one transaction (one continuous #cs assert). Doing this will cause the part to increment the address pointer each time it sends or receives 8 bytes. Since I set neither of these, my part became very confused, since I was telling it to write to a single read-only register six consecutive times.

tl;dr - Tell it you are using address 0xF2, then read 6 more times to get the 6 data registers.

This is an unnamed protocol layered on top of SPI, which by itself knows nothing of registers. It happens that the gyro uses the same protocol, so turning it on was a simple matter of verifying that the protocol was the same, and copypasting the code.

Now for the review. The ADXL345 has a programmable range, with choices ±2, 4, 8, and 16g. We will need 16g for the rocketometer. It has a readout precision of 13 bits, almost equal to the 14 bits the BMA180 gives. Now for the bad part. The readout precision is only 12 bits for 8g, 11 bits for 4g, and 10 bits for 2g. It is as if the part was always running at 16g, but reporting saturation if it was out of its current range.

I might as well use an analog part if I am only going to get 10 bits. I had always known this, but only realized the significance when I finally got it up and running, and only got about 250DN out of the part in the 1g field. So, the ADXL gets 2 of 5 stars, not recommended on the Chizumatic scale.

The BMA180 is what I used before, in flight. It has a programmable range, with choices ±1, 1.5, 2, 3, 4, 8, and 16g. It produces 14 bits of precision, and this 14 bits is constant across all ranges, so if you use the 1.0g range, you actually have zoomed in, and get better resolution than when you are at 16g. I can't speak to its accuracy, so it gets 4 of 5 stars, not recommended. Why not? The part was discontinued without a suggested replacement as of today, and is no longer available on Digikey. In fact, I am hoarding three of them still in their cut tape, purchased from Sparkfun today at great expense, not even on a breakout board. None of the other BMA accelerometers are as good, and none of the Analog Device accelerometers are as good either.

Thursday, September 27, 2012

8 DoFs so far...

I have gotten the ADXL345 accelerometer, HMC5883L compass, and BMP180 pressure/temperature sensors working with the Loginator and 11DoF board. This means that all SPI and I2C signals work on both boards. Last thing to do is to get the STMicro L3G4200D gyroscope working.

I just saw a board basically identical to the 11DoF (same sensors except a BMP085). The problem is that it is all done up in I2C.

Friday, September 21, 2012

Starting up an ARM processor with 90% C code

Now why would you want to do a darn fool thing like that? Startup.S works fine, why mess with it?

One word: Understanding.

Let's face it: Assembly is hard to read. Especially ARM assembly, with its special registers, shift-operands, and treating memory (load/store) fundamentally different from registers (mov). You can't get more than a few lines through an assembly listing without having to crack the ARM ARM.

Besides, I have a philosophy to use a consistent language throughout a program to the extent possible. So, for the embedded stuff, it's C++ when you can, C when you have to, and asm only when you really have to.

So what stands in the way? Mostly it's the fact that the toolchain makes it difficult to put things exactly where you want:

  1. The interrupt vector table. This is what keeps the code from being 99% C instead of 90%. On an x86 processor, the interrupt vector table is purely a list of addresses. Upon interruption, the processor looks up the correct vector, and loads it straight into the instruction pointer, causing the processor to branch to the handler. In many other processors, the table is actually code. When an interrupt happens, the processor jumps to the correct slot in the table and executes it. Usually this is a jump to where the handler really is. In ARM, there isn't enough space for a long jump in a single instruction, so each vector says to load the program counter with another value from memory, usually in a table located right after the true interrupt table. In any case, C just isn't a good language for building the table. What we do then is use inline asm. We make a function called vectorg which is purely inline asm. The first half is instructions, specifically the long jump instructions with embedded pointers to the second half, which is the address table. This is populated with symbols, so the linker can patch it up.
  2. Putting things where we want. The old Turbo Pascal had a mechanism for assigning a variable to a particular point in memory. In asm, this is easy: just define a symbol with a hard-coded address. This just isn't possible in C. So, we need cooperation from the linker. We need to specify the linker script. In particular, we need to say that a particular named ELF section is to be linked right at the beginning of flash, and then make sure that the table is in fact in that section, at the beginning of it. The easiest way to do that is to turn on -ffunction-sections when compiling, then link .text.vectorg at the beginning. The source code and the linker script have to agree on this.
  3. Registers - This is the other 1%. To set up the program, the reset handler has to set up the stacks, move the initialized data from flash to RAM, zero out the uninitialized data, and call all the constructors for global objects. But, in order to set up the stacks, the code has to be able to set the CPSR register, so it can flip through modes and set each mode's stack register. It also has to be able to write directly to the stack register itself.

Wednesday, September 19, 2012

Around the world in (considerably less than) 80 hours

A thought experiment. I would need to go home and throw some stuff in a backpack, and get my passport, but this is doable. It was 2012 Sep 19 11:00am MDT as I searched.

Layover From Depart Arrive Flight Time
Airport TZ UTC rel Local UTC Local UTC

Boulder Mountain Daylight Time -6 2012 Sep 19 11:00AM 2012 Sep 19 17:00 2012 Sep 19 11:00AM 2012 Sep 19 17:00 Look up flight 00h00m
09h45m Denver (DEN) Mountain Daylight Time -6 2012 Sep 19 08:45PM 2012 Sep 20 02:45 2012 Sep 20 12:35PM 2012 Sep 20 11:35 American 6169 as British Airways 218 08h50m
02h10m London (LHR) British Summer Time 1 2012 Sep 20 02:45PM 2012 Sep 20 13:45 2012 Sep 21 12:50AM 2012 Sep 20 20:50 Etihad 20 07h05m
01h35m Abu Dhabi (AHU) Gulf Standard Time 4 2012 Sep 21 02:25AM 2012 Sep 20 22:25 2012 Sep 21 03:25PM 2012 Sep 21 07:25 Etihad 424 09h00m
07h05m Manila (MNL) Philippine Time 8 2012 Sep 21 10:30PM 2012 Sep 21 14:30 2012 Sep 21 08:00PM 2012 Sep 22 03:00 Philippine 104 12h30m
09h50m San Francisco (SFO) Pacific Daylight Time -7 2012 Sep 22 05:50AM 2012 Sep 22 12:50 2012 Sep 22 09:21AM 2012 Sep 22 15:21 United 729 02h31m
1d06h25m Total Layover

Total trip time 2d12h36m Total flight time 1d15h56m
Also as of 11:00am, this flight had a cost of $5,351.89. I couldn't swing that right now, and I have work to do for the next few days, but Phileas Fogg wouldn't have any such trouble. He had £20000 cash in his pocket, and this trip would cost about £55.16 There are probably possible trips with tighter connections. There are surely trips that are cheaper with a longer lead time -- I found one in January for ~$3500.

This trip is definitely around the world. It crosses all the meridians. But, it is only 32833km as the crow flies. I have heard that a trip around the world must cover a distance longer than one of the tropic circles (36787km). This trip doesn't cut it if it follows the great circle route, but if the actual routing is 10% inefficient then it counts.

Wednesday, September 12, 2012


Here is a new cool thing: MathJax

\[x=\frac{-b\pm\sqrt{b^2-4ac}}{2a} \M{A} \MM{P}{^-_i}\]

Mathjax is apparently a TeX implementation written entirely in Javascript. It looks like it scans the source code of your page, looks for delimited equations, then interprets the TeX within and renders it using math fonts.

Instructions for how to get it to work for Blogspot are here.

Now I get to go through all of my Kalman filter stuff and fix the math there into something actually readable.

Wednesday, September 5, 2012

Complete Precision

Project Precision has reached its successful conclusion. I now have a physical hardware clock with an hour, minute, second, and third hand, and enough accuracy to justify needing a third hand.

As I have mentioned before, I noticed that an Arduino Nano has precisely the pinouts necessary to drive a charlieplex with 240 lights. The interesting thing is how few leftover resources there are. There are two analog inputs that are useless in this design. Every single other pin is used. I even considered giving up the crystal inputs to get two more digital pins.I had to include a digital multiplexer since the ATMega only has one serial port, and it needs to listen to both the USB port and the GPS.

As I said before, I will not make one for you for less than $300. The parts alone cost almost that much. However, I am going to publish everything you need to make one yourself.

This is the Digikey part list:

Quantity Digikey Part Number Part Value Case Placement Price per Min quantity Ext Price
2 445-7483-1-ND Capacitor 4.7uF Ceramic 0603 C010 C418 $0.24000 1 $0.48
2 478-6025-1-ND Capacitor 18pF 2% NP0 Ceramic 0603 C407 C408 $0.40000 1 $0.80
2 445-1316-1-ND Capacitor 100nF Ceramic 0603 C420 C502 $0.10000 1 $0.20
61 754-1359-1-ND LED Red 320mcd LED 0603 D000-D059 D502 $0.14040 1 $8.56
60 754-1124-1-ND LED Yellow 150mcd LED 0603 D100-D159 $0.11160 1 $6.70
60 350-2036-1-ND LED Green 300mcd LED 0603 D200-D259 $0.51840 1 $31.10
61 350-2037-1-ND LED Blue 140mcd LED 0603 D300-D359 D501 $0.48960 1 $29.87
4 CRA4S847CT-ND Resistor Pack 47 CRA04 R1 R2 R3 R4 $0.04300 10 $0.43
2 P680GCT-ND Resistor 680 SMD 0603 R501 R502 $0.10000 1 $0.20
1 CRA4S810KCT-ND Resistor Pack 10k CRA04 R606 $0.04300 10 $0.43
1 SW1021CT-ND Switch SPST B3U-1100P S429 $1.03000 1 $1.03
1 ATMEGA328P-15AZCT-ND Microcontroller ATMEGA328P 32-TQFP U401 $6.45000 1 $6.45
1 768-1007-1-ND USB interface FT232RL 28-SSOP U501 $4.50000 1 $4.50
1 NC7SZ157P6XCT-ND Multiplexer Noninv 2 input SC-70-6 U602 $0.41000 1 $0.41
1 887-1319-1-ND Crystal 16MHz 7M Y401 $1.69000 1 $1.69

Lights $76.23

Rest $16.62

Total $92.85
This costs on the order of $100, but the vast majority of the cost is in the 242 lights (240 for the hands, 2 for the TX/RX indicator). I get the above prices today from Digikey with no tax or shipping added on.You can get cheaper lights if you are satisfied with not using green or blue.

You will also need some connectors for the boards:
Sparkfun Female Header Pack - a set of two 6-pin and two 8-pin sockets. You will need this complete set, plus another 6- or 8-pin that will be cut down to 4 pins. You might as well get two of these sets, since they are cheap
Two Arduino 6-pin stackable headers
A strip of male straight headers and male right-angle headers. You need 32 pins' worth of straight headers and 4 of right-angle headers.
A long USB-A plug to USB-Anything cord. You are going to cut the cord off as far from the A end as possible.You will also need a way to connect this to the 4-pin right angle connector. I used a 5x2 ribbon connector (yes, 5, even though only 4 are needed. It's what I had on my bench at the time.).

While we are going through the Sparkfun shopping list, I recommend getting the UP-501 GPS receiver. In principle, any GPS receiver can work, and you can even set the clock over USB and have it run free without any GPS at all, but then you don't get sufficient precision to justify the third hand. The socket on the circuit board is designed for this UP501, and it fits nicely on the back of the board in between the four screws. If you use another GPS, you will need to make a connector for it. Get one that runs at 3.3V (or has a voltage adaptor) and one that has a PPS signal.

Finally, you need the light pipe parts. Ponoko does great work, but it is quite a bit more expensive than just the bare plastic sheets cost. The light pipes as I designed them are exceptionally fragile, and I forgot to put tabs on the light pipes to connect them directly to the four screws. If I were to make another clock, I would fix the latter flaw. As it is, the light pipes have holes for each LED, and these are used to hold the hands in place.

The firmware is plain ordinary Arduino code. There are two separate sketches, one to test each light in a controlled condition, and one to actually be a clock. The Charlieplex driver is put into a library so that the test code tests the same code that the clock code uses.