Wishful Coding

Didn't you ever wish your computer understood you?

ADNS-9800 hookup guide

Ever since I made my wooden mouse using this ADNS-9800 breakout, I’ve been getting emails and comments asking for help connecting the ADNS-9800 to a Teensy and programming it.

So this is my attempt at writing down all stuff you need to know. I promise it’s not hard if you know anything about Arduino and programming. If you don’t, you might want to reconsider your mouse-building plans.

It seems to be a bit confusing to some people how to connect the wires. On the breakout is a 2x4 header with labels, and the author has helpfully provided a list of what they do. (with a few minor additions of mine)

  • MI = MISO (Master In, Slave Out) (DIN on Teensy 3)
  • MO = MOSI (Master Out, Slave In) (DOUT on Teensy 3)
  • SS/SC = Slave Select / Chip Select
  • SC/SCK/SLCK = SPI Clock
  • MOT = Motion indicator (active-low interrupt line)
  • AG = Analog Ground (connect to common ground near power supply)
  • DG = Digital Ground (connect to common ground near power supply)
  • VI = Voltage in, up to +6V (See note about voltage)

Next you find the pinout for your Teensy and match up the labels.

Teensy pinout

It should be noted that slave select and the motion pin can be connected to any GPIO at all. Although a pin with an interrupt is preferred for the motion pin.

Another important fact is that the Teensy 3 runs at 3.3v while the Teensy 2 runs at 5v. From the author:

If you are using Arduino or another similar microcontroller that has a native 5V core voltage, you’ll need to activate 5V mode by cutting the tiny traces between the three sets of exposed pads on the 3.3V side of the board and adding three solder bridges to the exposed pads on the 5V side of the board.

Next up, you install Teensyduino, read the example code or the code for my mouse and adapt it for your needs.

You’ll see there is one file with a binary blob that is the firmware for the sensor. This is loaded at startup by the other file containing the main program.

At the top you’ll find a list of all the registers, the meaning of which can be found in the datasheet.

Then the SPI is initialised and perform_startup is called to start the sensor. Finally a FALLING interrupt is attached to the MOT pin. This triggers when the sensor has moved. When this happens you pull low the CS and read the motion registers with adns_burst_motion.

At the bottom of my code are a bunch of functions named adns_*. They are mostly copied from the example code and can be used to to read/write registers on the sensor.

Good luck!

Pepijn de Vos

Flight controls don't work the way you think they do

flight axis

A plane has 4 basic controls; Rudder, elevator, aileron, and throttle. While these primarily make you yaw, pitch, roll, and accelerate, you can’t exactly say you go up and down with your elevator.

Take a glider plane. It has an ideal gliding speed of around 90km/h. Any faster or slower and you fall faster.

If you pitch up, your speed decreases, and so does the lift of your wings. If you pitch down, the increased air speed generates more lift, so you don’t actually fly downwards that much.

Your engine increases your thrust. The rules above still hold true; More speed, more lift. As a side effect, the increased lift makes you pitch up, and the torque of the engine makes you roll a bit.

I would go as far as to say that your throttle controls your altitude and your elevator controls your speed, rather than the other way around. The truth lies somewhere in between.

When you use your rudder to yaw the plane, the outer wing moves faster than the inner wing, generating more lift and causing your plane to roll.

When you roll the plane with your ailerons, the lift is directed sideways, causing you to move sideways. Again, your tail wing pushes the nose in the wind and causes you to yaw. What’s more, diverting some of your lift sideways causes you to pitch down when rolling.

So to make a good flat turn, you need to roll, keep the plane level, and the nose in the wind, using all 3 control surfaces. In fact, gliders often have a string on the canopy to tell if you’re flying straight.

Given the above, it might not come surprising to you that it’s quite common for RC planes to lack either a rudder or ailerons. Some very light gliders even do without the elevator, relying solely on the rudder and the inherent balance of the plane.

Pepijn de Vos

Where are my Pokebytes saved?

Welcome to another episode of me grepping through Pokered. This time to figure out how the game decides to show the Pokedex menu or not.

You get your Pokedex from Oak after delivering the parcel, so Oaks Lab seems like a good place to look for clues.

After some unfruitful searching through the script for Oaks Lab I decided to approach it from the other way and look at the start menu. A wild guess:

git grep -i startmenu

Bingo! Done? I think not.

We can trace back wd74b to Oaks Lab and our old friend wram.asm, but this time I want to know where it is in the save file.

There are no direct references to our byte in any saving related code, so again, let’s start from the other direction. Another wild guess:

git grep -i save

After a lot of not so interesting files, I see some matches in engine/save.asm. What jumps out are calls to CopyData originating in SaveSAVtoSRAM.

CopyData is easily found, and copies bc bytes from hl to de.

At the start of all of the three saving functions are a few lines about SRAM_ENABLE and MBC1SRamBank. This is probably about switching banks in the cartridge, so that seems like a good point to look at the Game Boy pandoc.

It has a section on bank switching, and I found elsewhere that Pokemon Red used the MBC3 type.

So as you can see, addresses 0xA000-0xBFFF can be mapped to 3 battery backed RAM banks for save data. This corresponds to the de addresses we see in the code.

So starting with SaveSAVtoSRAM0 we can now try to figure out which piece of WRAM is copied to which SRAM location.

Here we see the second RAM bank is mapped to 0xA000, so writing to 0xA000 puts a byte in SRAM at 0x2000.

Next we see that 11 bytes of wPlayerName are written to 0xA598. To get the SRAM address, we subtract 0xA000 and add 0x2000, resulting in 0x2598.

We can verify this with the Bulbapedia article about the save file and it is indeed correct. Sadly, our beloved wd74b is not in the article, so on with our search.

Since the file does not reference our address directly, the only way to figure this out is to check all the copies in wram.asm to see if they include our byte. But we’re in luck.

The next copy copies from wPokedexOwned to W_NUMINBOX with wd74b right in the middle. Now all we need is math to tell the location in SRAM.

wPokedexOwned is copied from 0xD2f7 to 0xA5A3, giving us 0x25A3 as the SRAM starting address. Now we add to that the offset of wd74b, to give us:

0x25A3 + (0xD74B - 0xD2f7) = 0x29f7
Pepijn de Vos