LIS302DL. A 3 Axis Accelerometer

Tuesday July 14, 13.12.21

Ooooh. Those code jockeys in the Laboratory have been mucking about in the ol’ locker room, giving each other rat-tails, chucking firecrackers in the halls and having a good horsin’ around. Smoking cigarettes and drinking cheap booze. Everything. Stink bombs in the girl’s room. Whatever. It’s a regular Lord of the Flies fest on the electronics wing of the Near Future Laboratory. And, look what we found! Some pole dancin’ hardware porn! Step right up! Don’t crowd..

Wednesday July 15, 13.04.33

In reverent honor of my friends and chums who are holding forth with Sketching in Hardware 09 in London and for the solemn sadness I have for not being able to participate this year, I hereby drop some code and hardware science up on this piece of blog with a dozen or so lines of Arduinoness meant to articulate and instrumentalize the wonderful ST Micro LIS302DL 3 axis accelerometer, delivered here via the Sparkfun breakout board. Without further ado, but with plenty of firmware nakedness, here’s the sketch..*slug* this one’s for you, Sketchers..*sob*

#include

// TWI (I2C) sketch to communicate with the LIS302DL accelerometer
// Using the Wire library (created by Nicholas Zambetti)
// http://wiring.org.co/reference/libraries/Wire/index.html
// On the Arduino board, Analog In 4 is SDA, Analog In 5 is SCL
// These correspond to pin 27 (PC4/ADC4/SDA) and pin 28 (PC5/ADC5/SCL) on the Atmega8 and Atmega168
// The Wire class handles the TWI transactions, abstracting the nitty-gritty to make
// prototyping easy.

// We've got two accelerometers connected. You configure the address of each one
// by some wiring
int address_1 = 0x1C; // SDO on the LIS302DL connected to GND makes it at I2C address 0x1C
int address_2 = 0x1D; // SDO/MISO on the LIS302DL connected to VCC makes it at I2C address 0x1D
void setup()
{
  // we'll use the serial port to spit out our data
  Serial.begin(9600);

  byte tmp;

  Wire.begin(); // join i2c bus (address optional for master)


  // Read from the "WHO_AM_I" register of the LIS302DL and see if it is at
  // the expected address.
  // If it is not, spit out a useful message on the serial port. We'll get
  // erroneous data from querying this address, though.
  Wire.beginTransmission(address_1);
  Wire.send(0x0F);
  Wire.endTransmission();
  Wire.requestFrom(address_1,1);
  while(Wire.available()) {
   tmp = Wire.receive();
   if(tmp != 0x3B) {
     Serial.print("Problem! Can't find device at address "); Serial.println(address_1, HEX);
     delay(1000);
   } else {
  // configure the device's CTRL_REG1 register to initialize it
  Wire.beginTransmission(address_1);
  Wire.send(0x20); // CTRL_REG1 (20h)
  Wire.send(B01000111); // Nominal data rate, Device in active mode, +/- 2g scale, self-tests disabled, all axis's enabled
  Wire.endTransmission();
   }
  }


  // Read from the "WHO_AM_I" register of the second LIS302DL and see if it is at
  // the expected address.
  // If it is not, spit out a useful message on the serial port. We'll get
  // erroneous data from querying this address, though.
  Wire.beginTransmission(address_2);
  Wire.send(0x0F);
  Wire.endTransmission();
  Wire.requestFrom(address_2,1);
  while(Wire.available()) {
   tmp = Wire.receive();
   if(tmp != 0x3B) {
     Serial.print("Problem! Can't find device at address "); Serial.println(address_2, HEX);
     delay(1000);
   } else {

  // configure the device's CTRL_REG1 register to initialize it
  Wire.beginTransmission(address_2);
  Wire.send(0x20); // CTRL_REG1 (20h)
  Wire.send(B01000111); // Nominal data rate, Device in active mode, +/- 2g scale, self-tests disabled, all axis's enabled
  Wire.endTransmission();
   }
  }
}

void loop()
{
  Serial.print("1:"); read_acceleration(address_1);
  Serial.print("2:"); read_acceleration(address_2);
}

void read_acceleration(int address) {
  byte z_val_l, z_val_h, x_val_l, x_val_h, y_val_l, y_val_h;
  int z_val, x_val, y_val;
  Wire.beginTransmission(address);
 // Now do a transfer reading one byte from the LIS302DL
 // This data will be the contents of register 0x29, which is OUT_X
  Wire.send(0x29);
  Wire.endTransmission();
 Wire.requestFrom(address, 1);
  while(Wire.available())
 {
   x_val = Wire.receive();
 }
 // This data will be the contents of register 0x2B which is OUT_Y
 Wire.beginTransmission(address);
 Wire.send(0x2B);
 Wire.endTransmission();
 Wire.requestFrom(address, 1);
 while(Wire.available())
 {
    y_val = Wire.receive();
 }

 // This data will be the contents of register 0x2D, which is OUT_Z
 Wire.beginTransmission(address);
 Wire.send(0x2D);
 Wire.endTransmission();
 Wire.requestFrom(address, 1);
 while(Wire.available())
 {
    z_val = Wire.receive();
 }

 // I want values that run from {-X to 0} and {0 to +X}, so a little bit math goes on here...
 if(bit_is_set(x_val, 7)) {
   x_val &= B01111111;
   x_val = -1*x_val + 128;
   x_val *= -1;
 }

if(bit_is_set(y_val, 7)) {
   y_val &= B01111111;
   y_val = 128 - y_val;
   y_val *= -1;
 }

 if(bit_is_set(z_val, 7)) {
   z_val &= B01111111;
   z_val = 128 - z_val;
   z_val *= -1;
 }

 Serial.print(x_val); Serial.print(":");Serial.print(y_val); Serial.print(":"); Serial.println(z_val);
}

Tuesday July 14, 13.09.59

Wednesday July 15, 14.37.44

What’s going on here? Well, straightforward silliness and hardware gafflin’. Two accelerometers on the I2C bus just cause. It looks like this is as many as you can have on there, unless you do some shenanigans or create a second bus with some cleverness.

The hardware spec on the device (Which. You. Should. Read.) explains that, if we’re going to talk to these things using the I2C bus we need to wire CS to the high end of the logic rail, so VCC. This tells the device that we’ll be using I2C protocol. Then we need to address the device. If we connect the MISO (master in, slave out) pin to GND, then the address will be 0x1C. If we connect the MISO pin to VCC, then the address will be 0x1D. So, MISO, in the I2C configuration, controls the least significant bit of the address. This way, without further mucking about, we can have two accelerometers on the bus, which is probably one more than most situations demand, but just in case.

If I were to connect more than two, I would probably go ahead and use the three-wire protocol and have one microcontroller pin per accelerometer dedicated for chip-select (CS). Fortunately, this device supports three-wire protocols, or the SPI protocol.

Tuesday July 14, 13.17.11

The Arduino code example above does some simple preambling — initializing the two devices after making sure they are there. Then it just loops forever, reading accelerometer data from each of the three axes of each one, doing a little simple bitwise arithmetic to make the data from a negative value for negative g (upside down in most situations) to positive g (right side up, in most situations). The initialization stage sets the accelerometer range — that is, the max/min values it will read — to +/- 2g. (The device will support +/- 8g according to the specifications.)

There are some cool additional features that I don’t play with, including some interrupts that can be triggered if the device falls suddenly, or if it is “clicked/tapped” or “double-clicked/double-tapped”, which is kinda cool, I guess. If you can come up with a non-gratuitous scenario. Which is probably harder than it sounds. But, even in your gratuitous-I-double-click-my-glass-of-Porto-to-signal-the-waiter-I-need-more-Porto the device will save you the hassle of trying to do this sort of interaction semantics in firmware and get you back to finishing what you were doing in the first place.

Why do I blog this? Notes on the integration of hardware to firmware to ideas. This time with a “new” accelerometer that has some pretty neat features. After this, we’ll be going to paper-pulp and line drawings for a bit folks.

Laboratories, Accelerometers And Kitchen Crockery

The Memsic 6202 Accelerometer, fresh out o’the skillet.

We’ve been out of the Laboratory proper — the place where things are constructed and soldered and heated up — for a bit now, not because we don’t go in there, or we haven’t been doing things that will go in there. No, rather, or because of a variety of curiosities that have attracted our attention and, in that way, have slung us around to be more in the mode of reading and observing so as to launch into the next, “next vector of interests.” We believe strongly here allowing things to stew and simmer in the reading-writing-intellect as much as in the making-things section of the young noggin. There is much good that comes from the process of tinkering as well as, err..think-ering, both simultaneously, perhaps. Doing things to help think through what one finds curious or helpful, tinkering our way to new, more habitable world. Making things is a great way to learn. It is also a great way to think-things-through. Rather than the still idle of pondering, as an adjunct to the quiet sit in the cafe — making something that helps add some weight to the thought. Perhaps closer to The Craftsman by Richard Sennett.

The interests today swing around the ways of framing things, explaining and demonstrating and communicating, especially future fictions, perhaps designed fictions, as a way of describing the sometimes peculiar imaginings that get brought upstairs from the boys in the basement in the Bureau of Coming Attractions. We have our Bureau of Urban Scouting, evolving its own practice of observation and, perhaps of more interest, a variety of instruments and procedures for seeing the world sideways. Nicolas and I are in the final stages of this short essay for the Situated Technologies project that will include this topic. Literally in the final stages. As in, right now I should be editing a Google Doc rather than writing this, but I let the morning coffee lead me.

So, this is all to say that this blog swerves constructively, probably baffling all eighteen readers into wondering — what the heck is going on here? Where is the editorial consistency? Where’s my code/diagrams/schematics?

To this I only ask that you look for such things even here, as they are always in-the-making, becoming the hardware in the midst of the thinking-through, in the ideas.

As the curiosity-seeker Jack Schulze recently intoned in a wonderfully Talmudic way"No one cares about what you think, unless you do what you think. No one cares what you do, unless you think about what you do. No one ever really cares what you say."

This may explain Jack’s characteristically quiet demeanor. We’re quite verbally agitated here, so we’ll let the last one slip by us. But, the yammering is the thinking-making-doing, sometimes in words, sometimes in thoughts, sometimes in the materialization of our imagination.

With this said, in response to Reader Number Fourteen’s request this morning for “some code”, I share with you this little thing: Code to Test the Memsic 6202 Accelerometer. You may remember this little guy. We used it in our early foray into fabricating surface-mount electronics. With kitchen skillets. We learned this bit of tinker-y hacker-y from our friends over at Spark Fun. It works great. And it’s cheap as rocks.

I can’t say we were super excited about this particular accelerometer, but that’s okay. It’s just an accelerometer after all. If you follow us down toward the foxhole of these things, you’ll find a whole world of accelerometer fanatics with plenty of material to fuss with — smart accelerometers, accelerometers that let you know when they’re falling, accelerometers that tell you the time as well as whether they’re falling, etc. You’ll find some material buried here in the blog archives, to be exhumed with the right incantation of search and category selection parameters.

This code was written for the Atmel 8-bit microcontrollers that live on the Arduino board. It also makes use of the Wire library for the Arduino, and, as well, the two-wire interface (TWI) hardware support on those microcontrollers, making the whole thing pretty easy-peasy.

#include
#include


// TWI (I2C) sketch to communicate with the Memsic MXC6202J accelerometer
// Using the Wire library (created by Nicholas Zambetti)
// http://wiring.org.co/reference/libraries/Wire/index.html
//
// On the Arduino board, Analog In 4 is SDA, Analog In 5 is SCL
// These correspond to pin 27 (PC4/ADC4/SDA) and pin 28 (PC5/ADC5/SCL) on the Atmega8
// Know You're Microcontroller. Check Which One Is On Your Board/Arduino.
// Newer (as of May '09) Arduinos Use The Atmega168. Can You See It?
// The Wire class handles the TWI transactions, abstracting the nitty-gritty to make
// prototyping easy.

void setup()
{
  Serial.begin(9600);
  Serial.println("Naah??");
  Wire.begin(); // join i2c bus (address optional for master)
}

void loop()
{

  byte x_val_l, x_val_h, y_val_l, y_val_h;
  int x_val, y_val;
// transmit to device with address 0x1D
// according to the MXC6202xMP datasheet, the TWI/I2C address of is fixed
// at the factory depending on what the "x" in the part name is
// Read The Data Sheet section "I2C Bus Data Transfer" for descriptions
// of what the part expects to receive as its address. If you do not understand
// how this works, you are guaranteed to get lost/confusted/frustrated.
// 00010000b is the address of the chip I have, or 0x10

  // a little fuzzy in my recollection here. I think this initializes the chip
  // by setting some bits in the Memsic device's 8-bit internal register
  // Not 100% sure why I initialize two bytes.
  Wire.beginTransmission(0x10);
  Wire.send(0x00);
  Wire.send(0xF0);
  Wire.endTransmission();

  delay(100);

  Wire.beginTransmission(0x10);
  Wire.send(0x00);
  Wire.endTransmission();

  // request five bytes of data
  // first one is the internal register, we can ignore that, but lets look at it for giggles
  Wire.requestFrom(0x10,5);
  while(Wire.available()) {
    Serial.print(Wire.receive(), HEX); // drop the internal register on the floor..
  }
  Serial.println("****");

  // next byte is the MSB of the X axis acceleration
  if(Wire.available()) {
    x_val_h = Wire.receive();
  }
  // next byte is the LSB of the X axis acceleration
  if(Wire.available()) {
    x_val_l = Wire.receive();
  }
  // next byte is the MSB of the Y axis acceleration
  if(Wire.available()) {
    y_val_h = Wire.receive();
  }
  // next byte is the LSB of the Y axis acceleration
  if(Wire.available()) {
    y_val_l = Wire.receive();
  }
  // cobble all of this together into 16 bit acceleration values
  // doing the usual bit-shift polka
  x_val = (x_val_h << 8); x_val |= x_val_l;
  y_val = (y_val_h << 8); y_val |= y_val_l;
  // spit it all out across the serial port so we can see it
  Serial.print(x_val_h, HEX); Serial.print(" "); Serial.print(x_val_l, HEX); Serial.print("n");
  Serial.print(y_val_h, HEX); Serial.print(" "); Serial.print(y_val_l, HEX); Serial.print("n");
  Serial.println("===");

 Wire.endTransmission();

 // loop and do it again.
  delay(200);
}

Continue reading Laboratories, Accelerometers And Kitchen Crockery

Finding The Way – HMC6352 Digital Compass

Friday March 13, 23:53:41

Since I was asked, and asked again, I’ll toss this technotopia solutions nugget to demonstrate that, despite the evidence demonstrated by recent public appearances and weblog postings, we here at the Laboratory are in fact still fully engaged in connecting wires amongst components. Components take a variety of forms, as do the wires. Sometimes bits of silicon strongly encased in hermetically sealed plastic. Other times, cultural components made of different materials. Observations and their conclusions take a variety of paths and sources of inspiration. We enjoy looking at curious people-practices, inspecting them for insights and stories to be told with a variety of resources: objects, images, words.

And so on. More on this point later.

In the meantime, an eager follower has asked for some simple advice that could have been dispatched in an electronic mailing. Instead, The Near Future Laboratory resources room has decided to fold that information into a weblog “posting.”

Wayfinding, of the digital variety has become exceptionally straightforward for those who are okay with writing a dozen or so lines of code for the handy-dandy Arduino and this HMC6352 digital compass which is available as a breakout board off the rack from our friends at Sparkfun.

The compass will give you a perpetual stream of degrees-heading from magnetic north and has interesting adjustments to let you make offset corrections and that sort of thing. It works simply, as all such things should be. It’s a TWI/I2C device, so you get it on the I2C bus, and send commands to its address (42h). Easy-peasy. If you’re doing anything like setting a RAM or EEPROM register, you send the approrpriate command and address. But, for the most part, you’ll just want to tell it to send heading data, right? That’s simple — send the letter ‘A’ (41h) and back comes the heading data. That’s it. Send an ‘A’, get a heading. Send an ‘A’..get a heading. Over and over, or as often as necessary.

Done.

Here’s some Arduino code to get you going!


#include

// http://wiring.org.co/reference/libraries/Wire/index.html
// On the Arduino board, Analog In 4 is SDA, Analog In 5 is SCL
// These correspond to pin 27 (PC4/ADC4/SDA) and pin 28 (PC5/ADC5/SCL) on the Atmega8
//
// These correspond to pin 27 (PC4/SDA/PCINT12/Analog Input 4) and pin 28 (PC5/ADC5/SCL/PCINT13/Analog Input 5) on the Atemga168
// The Wire class handles the TWI transactions, abstracting the nitty-gritty to make
// prototyping easy.

int address = 0x42 >> 1;
 int reading;
void setup()
{
  // this for debugging the data
  Serial.begin(9600);
  // set up
  CLKPR = (1<<clkpce);
  CLKPR = 0;
  // initialize the HMC6352
  Wire.begin(); // join i2c bus (address optional for master)
}

void loop() {

  Wire.beginTransmission(address);
  Wire.send('A');
  Wire.endTransmission();
  //delay(10);
  Wire.requestFrom(address, 2);

   if(2 <= Wire.available())    // if two bytes were received
  {
    reading = Wire.receive();  // receive high byte (overwrites previous reading)
    reading = reading << 8;    // shift high byte to be high 8 bits
    reading += Wire.receive(); // receive low byte as lower 8 bits
    reading /= 10;
    Serial.println(reading);   // print the reading
  }

//  delay(50);
}

Continue reading Finding The Way – HMC6352 Digital Compass

Autonomous Game Controllers

JCB_01012009__142528_4160_DRV001

Continuing on my strange pursuit of designing weird interfaces that disrupt conventional game interaction rituals, I put together a bit more of my “PSX” project. You probably don’t recall, but this is the project where I’ve created a little “dongle” that can fool a PS2 (or Playstation 3, as it turns out..) into thinking that the dongle is actually a Playstation controller. You can also hook up a real Playstation controller to it and it can “read” the controller buttons and joystick and pass that data through, or do whatever you want with it. It appears as an TWI/I2C device to anything that talks to TWI/I2C devices..like my favorite TWI/I2C talking thing — the Arduino.

You can see here that the dongle — the white box with those annoying, huge Playstation connectors on either end, one male, one female — has four wires going into it. Those are for power, ground and the TWI signals, SDA and SCL. The power and ground are necessary because the Arduino (at least this one here) and the dongle run at different voltages. The Arduino is a 5V device, while the dongle is a 3.3V device, so there’s some level shifting going on inside there.

JCB_03012009__195744_4176_DRV001

190808_200037

So, that’s all fine. It works, etc. But then the question that’s actually more intriguing than building the thing..what do you do with it?

Well, I have some ideas, including hooking up a bike to it and attaching a giant human form kick-boxing dummy to play old school fighting games. For some early fun and nonsense, I connected a Wii Nunchuck up to it to control Playstation games with Wii-y gestures and stuff. Which could be cool if I find the right game, or spend a little more time thinking things through rather than just writing and revising microcontroller firmware to make a TWI controllable device, and doing Catia to CAD-up and print plastic enclosures.

But, in the meantime, I decided to do an absolutely crucial bit of game science. Something that I am entirely sure is mulled over constantly, but never properly investigated. The question is best stated thusly: how long would it take the Little Prince to roll up an entire room based on a random path algorithm?

How long indeed. Well, short answer is a long time. I let it go for about 70 minutes, after which he had just 10 things to go in a particularly tricky location that required rolling across a narrow bridge. At this point, I took over..but check out the 8x speed video anyway!

Katamari Autonomy from Julian Bleecker on Vimeo

I wrote a quick little Arduino code for my PSX dongle to have the Little Prince roll forward and then, after a few moments, make a random directional change. (Or stop, take a load off and look around the world.)

This was all done by sending TWI commands to the appropriate registers in my little DIY Playstation 2 controller emulator. All the buttons and the joysticks can be emulated as to their state through a series of write-to registers. If there’s a controller stuck in the other side of the dongle, there are a complement of read-from registers so you can see if any of the buttons are pressed and how much the joysticks are displaced. (I set up an “escape” buttons sequence — pressing both L2 and R2 — to bring control back to the normal joystick so I could navigate menus or take control over, which I had to do after I realized, with four items left, the completion of cleaning the room would probably not happen before the universe ran out of steam.)

Here’s the Arduino code. Pretty straight forward Wire / TWI library stuff.


#include
#include "nunchuck_funcs.h"

#define is_bit_clear(a, n) (0 == (a & (1<<n)))
#define is_bit_set(a, n)  (1 == (a & (1<<n))

#define is_button_pressed(a, button) is_bit_clear(a, button)
#define is_button_released(a, button) is_bit_set(a, button)

#define BTN_SELECT 0
#define BTN_L3 1
#define BTN_R3 2
#define BTN_START 3
#define BTN_UP 4
#define BTN_RIGHT 5
#define BTN_DOWN 6
#define BTN_LEFT 7
#define BTN_L2 8
#define BTN_R2 9
#define BTN_L1 10
#define BTN_R1 11
#define BTN_TRIANGLE 12
#define BTN_CIRCLE 13
#define BTN_X 14
#define BTN_SQUARE 15

// register addresses in the PSX I2C device
#define W_BUTTONS_0 0x00 // (, ^, start, R3, L3, select
#define W_BUTTONS_1 0x01 // (square, x, o, triangle, R1, L1, R2, L2)
#define W_RIGHT_X 0x02
#define W_RIGHT_Y 0x03
#define W_LEFT_X 0x04
#define W_LEFT_Y 0x05

#define R_BUTTONS_0 0x12 // (, ^, start, R3, L3, select)
#define R_BUTTONS_1 0x13 // (square, x, o, triangle, R1, L1, R2, L2)
#define R_RIGHT_X 0x14 // a value from 0x00 - 0xFF
#define R_RIGHT_Y 0x15 // a value from 0x00 - 0xFF
#define R_LEFT_X 0x16
#define R_LEFT_Y 0x17

// I2C address of the PSX dongle
int psx_dongle_addr = 0x72;
int j;
void setup()
{
  Serial.begin(9600);
  randomSeed(analogRead(0));
  Wire.begin(); // join i2c bus (address optional for master)


// this is the control register. setting it to 1 means that
// we have to tell the PSX device what data to send to the
// Playstation2. Setting it to 0 means that it simply passes
// through the data from the controller to the PS2. We can
// read the state of the contorller at any time.

writeToAddress(psx_dongle_addr, 0x24, 1);
}

// we'll use count to figure out when to change direction
int count = 0;
int buttons;

// mode is used to indicate either "pass thru" where we can use the
// actually real human controller to control the PS2, or to generate
// data via the PSX dongle.
// pressing both L2 and R2 simultaneously toggles the mode
int mode = 1;
byte randomNumber;

void loop()
{
  byte val;
  count++;
  //Serial.println(count, DEC);
 // 0x70 shows up as either ID $20 or ID $E0 on Propeller

/*******************************************/
/*
  BTN_SELECT = $0001
  BTN_L3 = $0002
  BTN_R3 = $0004
  BTN_START = $0008
  BTN_UP = $0010
  BTN_RIGHT = $0020
  BTN_DOWN = $0040
  BTN_LEFT = $0080
  BTN_L2 = $0100
  BTN_R2 = $0200
  BTN_L1 = $0400
  BTN_R1 = $0800
  BTN_TRIANGLE = $1000
  BTN_CIRCLE = $2000
  BTN_X = $4000
  BTN_SQUARE = $8000
**/


// 0x00 write to BUTTONS_0, 0x12 read from BUTTONS_0
// 0x01 write to BUTTONS_1, 0x13 read from BUTTONS_1
// 0x02 write to RIGHT_X, 0x14 read from RIGHT_X
// 0x03 write to RIGHT_Y, 0x15 read from RIGHT_Y
// 0x04 write to LEFT_X, 0x16 read from LEFT_X
// 0x05 write to LEFT_Y, 0x17 read from LEFT_Y
//Serial.println(getButtons(), HEX);
//int buttons = getButtons();
//Serial.print(buttons, BIN);
//  passThruButtons();

if(count > 512) {
  count = 0;
}
//Serial.println(mode, HEX);

// get the buttons
buttons = getButtons();

// mode is used to indicate either "pass thru" where we can use the
// actually real human controller to control the PS2, or to generate
// data via the PSX dongle.
// pressing both L2 and R2 simultaneously toggles the mode
  if(mode == 1 &&
     is_button_pressed(buttons, BTN_L2) && is_button_pressed(buttons, BTN_R2)) {
    mode = 0;
    delay(1000);
  } else
  if(mode == 0 &&
     is_button_pressed(buttons, BTN_L2) && is_button_pressed(buttons, BTN_R2)) {
     mode = 1;
     delay(1000);
     }
 if(mode == 1) {
passThruAllAndShow();
 }
 if(mode == 0)
  {

  writeToAddress(psx_dongle_addr, W_LEFT_Y, 0x00);
  writeToAddress(psx_dongle_addr, W_RIGHT_Y, 0x00);
  passThruButtons();

  if(count == 512) {
    count = 0;

    randomNumber = random(1,5);
        Serial.print("FLIP! ");
        Serial.println(randomNumber, HEX);
    switch(randomNumber) {
      case 1: case 2: case 6:
        writeToAddress(psx_dongle_addr, W_LEFT_Y, 0x00);
        writeToAddress(psx_dongle_addr, W_RIGHT_Y, 0xAF);
        delay(500);
        break;
      case 3: case 4: case 5:
        writeToAddress(psx_dongle_addr, W_LEFT_Y, 0xAF);
        writeToAddress(psx_dongle_addr, W_RIGHT_Y, 0x00);
        delay(500);
        break;
          default:
         delay(500);
         break;
    }
  }

  /*
writeToAddress(psx_dongle_addr, W_LEFT_X, (float)map(nunchuck_accelx(), (float)0x48, (float)0xB0,
                (float)0x00, (float)0xFF));
writeToAddress(pssx_dongle_addr, W_LEFT_Y, (float)map(nunchuck_accely(), (float)0x48, (float)0xB0,
                (float)0x00, (float)0xFF));

writeToAddress(psx_dongle_addr, W_RIGHT_Y, (float)map(nunchuck_joyy(), (float)0x1D, (float)0xDF,
                (float)0x00, (float)0xFF));
writeToAddress(psx_dongle_addr, W_RIGHT_X, (float)map(nunchuck_joyx(), (float)0x1D, (float)0xDF,
                (float)0x00, (float)0xFF));
*/
}

delay(10);
}


//
int getButtons()
{
int result = 0x00;

result = readFromAddress(psx_dongle_addr, 0x13, 1);
//  Serial.print(result, HEX); Serial.print(" ");
result <> 8)); // MSB
  Wire.send((int)(addr & 0xFF)); // LSB
  Wire.send(data);
  Wire.endTransmission();
}

byte readFromAddress(int twi_addr, int addr, int bytes_to_read)
{
  byte rdata;
 Wire.beginTransmission(twi_addr);
  Wire.send((int)(addr >> 8)); // MSB
  Wire.send((int)(addr & 0xFF)); // LSB
  Wire.endTransmission();
  Wire.requestFrom(twi_addr,bytes_to_read);
  while (Wire.available()) rdata = Wire.receive();
  return rdata;
}

Continue reading Autonomous Game Controllers

PCA9306 Level Shifter

Persistent nagging problem — shifting logic levels between devices that are fabbed with different technologies so their voltages end up being different. Digital circuits “trigger” based on voltage levels — a logic 1 or high signal is relative to the electrical specifications of the device and the fabrication process of the silicon. (A techie diagram is available here.) It’s not a new problem, but one I seem to be coming across more and more as I use the Arduino to do quick sketches of project ideas, or to stand-in for a more embedded solution that might use another of the Atmel 8-bit devices later on when the design and details are more refined.

I’ve tried a bunch of things, including the kind of canonical reference circuit from Philips that uses the BSN20 MOSFET. Sparkfun has a neat little breakout board using a MOSFET, the BSS138. Same principle, built out for you by the fine folk at Sparkfun.

While poking around some more what cause of this PSX project where I have an Arduino trying to talk to a Parallax Propeller, I found reference to a single-chip solution from TI that’s called a PCA9306, which is that small guy up in the photos above. (Just a hair smaller than an 8-pin SSOP package, which means that it was too small to fit on this SSOP break-out — the pins don’t reach the copper, so I had Igor do some hand work to get that to take hold.
Continue reading PCA9306 Level Shifter

Propeller and Arduino

Not the most exciting thing, but an interesting challenge here. I’m trying to get a Parallax Propeller chip to behave nicely as an TWI/I2C slave with the idea that I’d like to create a pretty much black-box interface that’ll allow a TWI/I2C master to control it for stuff. Ultimately I’d like to use the Propeller in the PSX project. It’ll sit in between a Playstation 2 and a normal Playstation controller, and be available as a TWI/I2C slave, so that another device, that doesn’t really care to figure out how to talk to a Playstation 2 or its controllers can make the PSX box with the Propeller in it emulate, for the Playstation, certain button presses, etc. Or, it could “read” over TWI/I2C from the PSX box and find out what the real controller is doing. Or, it could just read or write from a special TWI register address to make the PSX box (with the Propeller in it) just do a “pass-thru” so that the signals just go straight through as if there were nothing in between.

That’s the theory. In practice, setting up this test jig took more time that I would’ve thought.
Continue reading Propeller and Arduino

The Ersatz Cylon Detector: A Hardware Sketch To Illuminate The Inner Workings of the MAX6953 Integrated Circuit

Cylon Detector In Operation

An on-going project that requires the display of alphabetic and numeric characters using a 5×7 LED matrix turned me towards a chip by Maxim IC — the MAX6953. The chip, while expensive (~$8), has lots of built-in features that mitigate the damage to my purse, trading such in kind for time-saved. It will directly drive up to 4 5×7 LED matrices with direct control from a microcontroller over a two-wire interface (TWI/I2C), can manage varying intensities (16 or 32 degree scales), has a built-in Arial-like font set with a reasonably full-range of characters, diacritics and symbols, and allows me to create 24 characters of my own design.

Here’s my Eagle PCB library of the MAX6951 and MAX6953; feel free to use it.

SlowMessenger_Max6953_Breakout

SlowMessenger_Max6953_Breakout_Board

My first attempt at mucking with the chip used the DIP version, which is ginormous and requires such patience and diligence with wiring the thing up — all those lines going to the pins of the LED matrix — that I pretty much gave up after it didn’t work the first time. I decided that I’d just go ahead and design a PCB for the thing and continue to hone my skills with Eagle, while also learning how to work with the MAX6953.

Some Samples

I sent a design off, it came back, I stared at it for a few minutes, looked for the MAX6953’s in my bin of samples and realized I didn’t have any. I managed to coax a bunch from Maxim’s sample guy, which is great. (Thanks whoever you are, wherever you are.) A couple of weeks later, when my plate was relatively clear, I assembled one of the boards, using a yellowish/orange 5×7 matrix LED I purchased from Digikey and manufactured by LiteOn. I wanted small, but the closest I got to small was 0.7″, which still seems big to me. (The story of it’s assembly and problems therein is instructive if you’re curious about surface-mount PCB work..and the problems therein.)

I powered the thing up and, naturally enough, it didn’t illuminate at all. I poked through the spec sheet and figured out that there’s a whole start-up ritual you’ll want to go through, such as the test mode and negating the shutdown bit, which defaults to “shutdown enabled.” After an hour or so of puzzling, I managed to get it displaying characters while hooked up to my usual Arduino test harness thing.

Lower "T"

While I was assembling the board, I realized I wasn’t sure what the proper orientation of the LED device should be. I looked for markings to indicate pin 1 and found nothing definitive. I looked at the product data sheet and they had an arrow pointing at pin 1 but nothing that clearly told me how to determine which of the pins was the first. Now, the holes I put in for the LED device in Eagle were a bit snug — too snug, really. It required a bit of jiggering to get the LED device to fit. (I went back and made the holes slightly bigger in the design.) But, I realized this could be an advantage given my current pin alignment peril — the snugness would allow the LED device’s pins to make contact with the through-holes’ platings! In other words, I could fit the device one way and not solder the pins to see if I had the thing right-way-around.

Evil Rabbit Character

Well, that worked. A consequence of it working was that some of the columns and rows didn’t illuminate quite consistently — they’d flicker and such — and a mysterious character appeared — an evil rabbit was the first thing I saw.

I ran through a few code gyrations and got characters to show, right-way-around, so I went ahead and soldered the LED device permanently to the board, easy-peasy.

Thinking of a few possible ways to make this learning project fun to share for the show-and-tell session of the weekly luncheon I have with some friends, I decided that I’d make a combination Magic 8 Ball / Cylon Detector, the idea being that one could reasonably ask the Magic 8 Ball if one were a Cylon — I mean..why not? — which would be a lot easier than what Baltar was trying to construct, and wouldn’t require blood samples or anything. You could just ask yourself, in the privacy of your own home or a public restroom, and then choose whether or not you wanted to share your result.

So, I hooked it up to an accelerometer that detects the normative shaking motion one inflicts on the Magic 8 Ball..the evil rabbit then divines the response.

Anyway, it definitely works.

The Ersatz Cylon Detector is meant for entertainment purposes only. It does not purport to factually report whether or not individuals are Cylons, except for Don Milvio. (He’s definitely a weird hybrid Franco-Italian Cylon thing.) The results are for home amusement and cannot be used for discovery or detection of actual Cylons, their friends or family. The results cannot be used for legal purposes, nor as a sanction for physical violence. The Ersatz Cylon Detector and its results are not endorsed by the producers of Battlestar Galactica, The SciFi Channel, or its affiliates and their station managers.


Arduino Code

#include <Wire.h>
#include <math.h>

// http://wiring.org.co/reference/libraries/Wire/index.html
// On the Arduino board, Analog In 4 is SDA, Analog In 5 is SCL
// These correspond to pin 27 (PC4/ADC4/SDA) and pin 28 (PC5/ADC5/SCL) on the Atmega8
// The Wire class handles the TWI transactions, abstracting the nittygritty to make
// prototyping easy.
// This sketch has two TWI devices connected to it — a LIS3LV02DQ triaxis accelerometer
// and the MAX6953 5x7 matrix LED driver. The MAX6953 is at address 0x50, and the LIS3LV02DQ is
// at address 0x1D.

char mAnswer_1[19] = {‘S’,‘i’,‘g’,‘n’,‘s’,‘ ‘,‘p’,‘o’,‘i’,‘n’,‘t’,‘ ‘,‘t’,‘o’,‘ ‘,‘y’,‘e’,‘s’,0x00};
char mAnswer_2[4] = {‘Y’,‘e’,‘s’,0x00};
char mAnswer_3[12] = {‘M’,‘o’,‘s’,‘t’,‘ ‘,‘l’,‘i’,‘k’,‘e’,‘l’,‘y’,0x00};
char mAnswer_4[16] = {‘W’,‘i’,‘t’,‘h’,‘o’,‘u’,‘t’,‘ ‘,‘a’,‘ ‘,‘d’,‘o’,‘u’,‘b’,‘t’,0x00};
char mAnswer_5{16] = {‘Y’,‘e’,‘s’,‘,’,‘ ‘,‘d’,‘e’,‘f’,‘i’,‘n’,‘i’,‘t’,‘e’,‘l’,‘y’,0x00};
char mAnswer_6[17] = {‘A’,‘s’,‘ ‘,‘I’,‘ ‘,‘s’,‘e’,‘e’,‘ ‘,‘i’,‘t’,‘,’,‘ ‘,‘y’,‘e’,‘s’,0x00};
char mAnswer_7[19] = {‘Y’,‘o’,‘u’,‘ ‘,‘m’,‘a’,‘y’,‘ ‘,‘r’,‘e’,‘l’,‘y’,‘ ‘,‘o’,‘n’,‘ ‘,‘i’,‘t’,0x00};
char mAnswer_8[13] = {‘O’,‘u’,‘t’,‘l’,‘o’,‘o’,‘k’,‘ ‘,‘g’,‘o’,‘o’,‘d’,0x00};
char mAnswer_9[14] = {‘I’,‘t’,‘ ‘,‘i’,‘s’,‘ ‘,‘c’,‘e’,‘r’,‘t’,‘a’,‘i’,‘n’,0x00};
char mAnswer_10[19] = {‘I’,‘t’,‘ ‘,‘i’,‘s’,‘ ‘,‘d’,‘e’,‘c’,‘i’,‘d’,‘e’,‘d’,‘l’,‘y’,‘ ‘,‘s’,‘o’,0x00};
char mAnswer_11[22] = {‘R’,‘e’,‘p’,‘l’,‘y’,‘ ‘,‘h’,‘a’,‘z’,‘y’,‘,’,‘ ‘,‘t’,‘r’,‘y’,‘ ‘,‘a’,‘g’,‘a’,‘i’,‘n’,0x00};
char mAnswer_12[24] = {‘B’,‘e’,‘t’,‘t’,‘e’,‘r’,‘ ‘,‘n’,‘o’,‘t’,‘ ‘,‘t’,‘e’,‘l’,‘l’,‘ ‘,‘y’,‘o’,‘u’,‘ ‘,‘n’,‘o’,‘w’,0x00};
char mAnswer_13[16] = {‘A’,‘s’,‘k’,‘ ‘,‘a’,‘g’,‘a’,‘i’,‘n’,‘ ‘,‘l’,‘a’,‘t’,‘e’,‘r’,0x00};
char mAnswer_14[26] = {‘C’,‘o’,‘n’,‘c’,‘e’,‘n’,‘t’,‘r’,‘a’,‘t’,‘e’,‘ ‘,‘a’,‘n’,‘d’,‘ ‘,‘a’,‘s’,‘k’,‘ ‘,‘a’,‘g’,‘a’,‘i’,‘n’,0x00};
char mAnswer_15[19] = {‘C’,‘a’,‘n’,‘n’,‘o’,‘t’,‘ ‘,‘p’,‘r’,‘e’,‘d’,‘i’,‘c’,‘t’,‘ ‘,‘n’,‘o’,‘w’,0x00};
char mAnswer_16[18] = {‘M’,‘y’,‘ ‘,‘s’,‘o’,‘u’,‘r’,‘c’,‘e’,‘s’,‘ ‘,‘s’,‘a’,‘y’,‘ ‘,‘n’,‘o’,0x00};
char mAnswer_17[14] = {‘V’,‘e’,‘r’,‘y’,‘ ‘,‘d’,‘o’,‘u’,‘b’,‘t’,‘f’,‘u’,‘l’,0x00};
char mAnswer_18[20] = {‘O’,‘u’,‘t’,‘l’,‘o’,‘o’,‘k’,‘ ‘,‘n’,‘o’,‘t’,‘ ‘,‘s’,‘o’,‘ ‘,‘g’,‘o’,‘o’,‘d’,0x00};
char mAnswer_19[18] = {‘D’,‘o’,‘n’,,‘t’,‘ ‘,‘c’,‘o’,‘u’,‘n’,‘t’,‘ ‘,‘o’,‘n’,‘ ‘,‘i’,‘t’,0x00};

void setup()
{

Serial.begin(9600);

CLKPR = (1<<CLKPCE);
CLKPR = 0;

// initialize the LIS3LV02DQ
Wire.begin(); // join i2c bus (address optional for master)
Wire.beginTransmission(0x1D);
Wire.send(0x20); // CTRL_REG1 (20h)
Wire.send(0x87); // Device on, 40hz, normal mode, all axiss enabled
Wire.endTransmission();

// initialize the MAX6953
Wire.beginTransmission(0x50);
Wire.send(0x07); // Display Test
Wire.send(0x00); // Normal Operation
Wire.endTransmission();

Wire.beginTransmission(0x50);
Wire.send(0x01); // Intensity16
Wire.send(0x0F); // Full
Wire.endTransmission();

Wire.beginTransmission(0x50);
Wire.send(0x04); // configuration register
Wire.send(0x01); // disable shutdown mode
Wire.endTransmission();
initializeSpecialCharacters();
}

void loop() {
int z_val, x_val, y_val;
int sum_sq, sum_sq_2, diff, mShake, message;
int max_accel;
byte i;

message = 0;

setCharacter(0x00);
delay(random(500,1000));
setCharacter(0x01);
delay(random(500,1500));

x_val = getX();
y_val = getY();
z_val = getZ();

sum_sq = sqrt(pow(x_val, 2) + pow(y_val, 2) + pow(z_val, 2));

delay(100);
x_val = getX();
y_val = getY();
z_val = getZ();
mShake = 0;
sum_sq_2 = sqrt(pow(x_val, 2) + pow(y_val, 2) + pow(z_val, 2));
diff = abs(sum_sqsum_sq_2);
if(diff > 300) { mShake = 1; }
if(diff > 1000) { mShake = 2; }
if(diff > 1200) { mShake = 3; }

//Serial.print(diff); Serial.print(" "); Serial.print(max_accel); Serial.print(" "); Serial.println(mShake);

if(diff > max_accel) max_accel = diff;

//delay(500);

if(mShake > 0) {
message = random(0,18);
for(int i=0; i<3; i++) {
setCharacter(‘*’); delay(500); setCharacter(‘ ‘); delay(500);
}
} else {
message = 1;
}
switch(message) {
case 0:
i = 0;
while(mAnswer_1[i] != 0x00) {
setCharacter(mAnswer_1[i]); delay(800);
i++;
}
break;
case 1:
i = 0;
while(mAnswer_2[i] != 0x00) {
setCharacter(mAnswer_2[i]); delay(800);
i++;
}
case 2:
i = 0;
while(mAnswer_3[i] != 0x00) {
setCharacter(mAnswer_3[i]); delay(800);
i++;
}
break;
case 3:
i = 0;
while(mAnswer_4[i] != 0x00) {
setCharacter(mAnswer_4[i]); delay(800);
i++;
}
break;
case 4:
i = 0;
while(mAnswer_5[i] != 0x00) {
setCharacter(mAnswer_5[i]); delay(800);
i++;
}
break;
case 5:
i = 0;
while(mAnswer_5[i] != 0x00) {
setCharacter(mAnswer_5[i]); delay(800);
i++;
}
break;
case 6:
i = 0;
while(mAnswer_6[i] != 0x00) {
setCharacter(mAnswer_6[i]); delay(800);
i++;
}
break;
case 7:
i = 0;
while(mAnswer_7[i] != 0x00) {
setCharacter(mAnswer_7[i]); delay(800);
i++;
}
break;
case 8:
i = 0;
while(mAnswer_8[i] != 0x00) {
setCharacter(mAnswer_8[i]); delay(800);
i++;
}
break;
case 9:
i = 0;
while(mAnswer_9[i] != 0x00) {
setCharacter(mAnswer_9[i]); delay(800);
i++;
}
break;
case 10:
i = 0;
while(mAnswer_10[i] != 0x00) {
setCharacter(mAnswer_10[i]); delay(800);
i++;
}
break;
case 11:
i = 0;
while(mAnswer_11[i] != 0x00) {
setCharacter(mAnswer_11[i]); delay(800);
i++;
}
break;
case 12:
i = 0;
while(mAnswer_12[i] != 0x00) {
setCharacter(mAnswer_12[i]); delay(800);
i++;
}
break;
case 13:
i = 0;
while(mAnswer_13[i] != 0x00) {
setCharacter(mAnswer_13[i]); delay(800);
i++;
}
break;
case 14:
i = 0;
while(mAnswer_14[i] != 0x00) {
setCharacter(mAnswer_14[i]); delay(800);
i++;
}
break;
case 15:
i = 0;
while(mAnswer_15[i] != 0x00) {
setCharacter(mAnswer_15[i]); delay(800);
i++;
}
break;
case 16:
i = 0;
while(mAnswer_16[i] != 0x00) {
setCharacter(mAnswer_16[i]); delay(800);
i++;
}
break;
case 17
i = 0;
while(mAnswer_17[i] != 0x00) {
setCharacter(mAnswer_17[i]); delay(800);
i++;
}
break;
case 18:
i = 0;
while(mAnswer_18[i] != 0x00) {
setCharacter(mAnswer_18[i]); delay(800);
i++;
}
break;
case 19:
i = 0;
while(mAnswer_19[i] != 0x00) {
setCharacter(mAnswer_19[i]); delay(800);
i++;
}
break;
default:
break;
}
message = 0;
}

void flickerOutIntensity()
{
for(int i=16; i>=0; i) {
Wire.beginTransmission(0x50);
Wire.send(0x01); // Intensity16
Wire.send(i);
Wire.endTransmission();
delay(random(400));
if(i % 4 == 0) {
Wire.beginTransmission(0x50);
Wire.send(0x01); // Intensity16
Wire.send(0x02);
Wire.endTransmission();
delay(random(500));
}
}
Wire.beginTransmission(0x50);
Wire.send(0x01); // Intensity16
Wire.send(0x0f); // Full
Wire.endTransmission();
}

// create two custom characters that look like an evil cylon detecting magic 8 ball rabbit
// this writes the two characters by writing first to register 0x05 the value 0x80, which is
// the address of the first 7 bits of he first usercreated font character. Each character
// has 5 rows of 7 bits, you write to each row in sequence. The MAX6953 will autoincrement
// the address written during the I2C/TWI transaction, so each of these Wire.send instructions
// is writing to the next row.
void initializeSpecialCharacters
{
Wire.beginTransmission(0x50);
Wire.send(0x05);
Wire.send(0x80);

// character 0x00
Wire.send(0x7f);
Wire.send(0x14);
Wire.send(0x44);
Wire.send(0x14);
Wire.send(0x7f);

// character 0x01
Wire.send(0x7f);
Wire.send(0x04);
Wire.send(0x44);
Wire.send(0x04);
Wire.send(0x7f);

Wire.endTransmission();

}

void setCharacter(char c)
{
// if(c >= 0x20 && c <= 0x7F) {
Wire.beginTransmission(0x50);
Wire.send(0x20); // Digit 0 Plane 0
Wire.send(c); // N
Wire.endTransmission();
// }
}

int getX()
{
byte x_val_l, x_val_h;
int x_val, y_val;
//Serial.println("hello?");
//byte in_byte;
// transmit to device with address 0x1D
// according to the LIS3L* datasheet, the i2c address of is fixed
// at the factory at 0011101b (0x1D)
Wire.beginTransmission(0x1D);
// send the sub address for the register we want to read
// this is for the OUTZ_H register
// n.b. supposedly masking the register address with 0x80,
// you can do multiple reads, with the register address autoincremented
Wire.send(0x28);
// stop transmitting
Wire.endTransmission();
// Now do a transfer reading one byte from the LIS3L*
// This data will be the contents of register 0x28
Wire.requestFrom(0x1D, 1);
while(Wire.available())
{
x_val_l = Wire.receive();
}
Wire.beginTransmission(0x1D); Wire.send(0x29); Wire.endTransmission();
Wire.requestFrom(0x1D, 1);
while(Wire.available())
{
x_val_h = Wire.receive();
}
x_val = x_val_h;
x_val <<= 8;
x_val += x_val_l;

return x_val;
}

int getY()
{
byte y_val_l, y_val_h;
int y_val;
// Y Axis
Wire.beginTransmission(0x1D); Wire.send(0x2A); Wire.endTransmission();
Wire.requestFrom(0x1D, 1);
while(Wire.available())
{
y_val_l = Wire.receive();
}
Wire.beginTransmission(0x1D); Wire.send(0x2B); Wire.endTransmission();
Wire.requestFrom(0x1D, 1);
while(Wire.available())
{
y_val_h = Wire.receive();
}

y_val = y_val_h;
y_val <<= 8;
y_val += y_val_l;

return y_val;
}

int getZ()
{
byte z_val_l, z_val_h;
int z_val;
// Z Axis
Wire.beginTransmission(0x1D); Wire.send(0x2C); Wire.endTransmission();
Wire.requestFrom(0x1D, 1);
while(Wire.available())
{
z_val_l = Wire.receive();
}
Wire.beginTransmission(0x1D); Wire.send(0x2D); Wire.endTransmission();
Wire.requestFrom(0x1D, 1);
while(Wire.available())
{
z_val_h = Wire.receive();
}

z_val = z_val_h;
z_val <<= 8;
z_val += z_val_l;

return z_val;
}

Update — here are a schematic and a board layout for the little board in the picture that contains the MAX6953 and the Matrix LED. I used my own breakout board like this, rather than run a whole bunch of wires from the 6953 to the Matrix LED.

SlowMail_Schematic

SlowMail_Board


Creative Commons License


This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License.

Technorati Tags: ,

Arduino and the Two-Wire Interface (TWI/I2C) Including A Short Didactic Parenthetical On Making TWI Work On An Arduino Mini

I have been using the Arduino and Atmel microcontroller’s generally using the SPI (serial-peripheral interface), but decided to look at the two-wire (a.k.a. I2C) interface as well. I’m doing this partially because it would be good to know how it works, but also because it’s electrically more compact. It only uses two-wires, rather than the four required for SPI, so schematic designs and board layouts become a bit more manageable. The trade-off is a little bit more complicated protocols semantics, but nothing out of control. (I’m also looking at using a two-axis accelerometer that’s much less expensive than the three-axis one I’ve been using – $4 versus $15. For some experiments, two-axes may be perfectly fine, and I’m happy to save the $11.)

The first step was making sure the Arduino would handle the TWI – there’s pretty much no reason it shouldn’t, because the Atmega8 certainly handles it. So, the next step was finding out how best to handle TWI transactions.

To do this, I consulted the Atmega8 specification sheet, which has a pretty thorough explanation of the protocol and the implementation details on the Atmega8. (There are also a couple of useful application notes available here and here.) It’s so thorough that I had to print it out. I got a pretty good understanding of how it works but before I started coding, I noticed that there were some TWI libraries both in avrlibc and in the “Wire” library in Wiring.org happens to be packaged as a “sanctioned” external library for Arduino, so that was pretty much that.

Nicholas Zambetti, who wrote the Wire library, pretty much told me it should work with no problems, and he was pretty much right. No problems. His library abstracts the TWI innards really nicely, so I don’t have to muck with any Atmega registers or anything of that sort.

I hooked up my Arduino to a handy accelerometer for testing. (I’m still using the expensive LIS3LV02DQ.) Analog In 4 goes to SDA, and Analog In 5 goes to SCL. I have pull-up resistors on those lines, but Nicolas explains that his library enables the internal pull-ups on the Atmega, so I can probably pull those from my breadboard. Either way, it’s working just fine, even with the external pull-up resistors. (I’m using 4.7k resistors.)

Arduino Mini TWI (I2C)

(I also found in one weird situation that I had to explicitly clear the clock prescaler to get the UART functioning properly. This was while getting TWI working on an AT90USB1287 – TWI worked fine, but the UART was spitting out garbage. On chips where the clock prescaler can be set through the fuses, it’s important to verify that the prescaler isn’t hard wired to divide the clock. This’ll cause anything that is dependent on timing to potentially be off kilter. In my case, the UART was expecting an 8MHz clock to determine timing and baud rate, but the clock was being divided in hardware – not even in the firmware.)

Wire/TWI (I2C) on the Arduino Mini

Getting TWI working on the Arduino Mini (Although the Arduino and Arduino Mini share a name, they don’t share the same processor. The Arduino Mini uses an ATmega168, while the Arduino..normal.. uses an ATmega8. You’ll need to recompile/re-“verify” the Wire library files in order to get TWI to work on the ATmega168/Arduino Mini. There’s a thing or two you’ll have to do by hand to get this to work, and you’ll need to do it each time you move code that’s using the Wire library to a different processor. In other words, when you decide to port the code to the ATmega8, or you put an ATmega16 in your Arduino, or whatever — you’ll need to recompile these libraries. Here’s the drill:

Navigate in a file browser or command prompt to the root of your Arduino file hierarchy. Then go into:

lib/targets/libraries/Wire/
delete Wire.o

lib/targets/libraries/Wire/utility
delete twi.o

Once you’ve done this, make one modification to the header file twi.h in lib/targets/libraries/Wire/utility. Look around line 27 for a commented out line like this:

//#define ATMEGA8

Un-comment it (take out the “//” in the front.) This’ll ensure that the internal pull-up resistors on the ATmega8 are enabled when you’re developing for the normal Arduino. You’ll only need to make this change to twi.h once and for all. Future builds of Arduino should have this fixed.

For those who are curious — pull-up resistors are required on the TWI lines — SCL and SDA. You may use your own external pull-ups, but enabling the internal ones saves you the hassle. But, I don’t think you’ll do much harm if you have the internal one’s enabled and use external ones. But, generally you’ll want to avoid having both internal and external pull-ups.)

Anyway. These modifications described above should get TWI working on the Arduino Mini.)

Here’s the code I wrote and ran:

#include

// TWI (I2C) sketch to communicate with the LIS3LV02DQ accelerometer
// Using the Wire library (created by Nicholas Zambetti)
// http://wiring.org.co/reference/libraries/Wire/index.html
// On the Arduino board, Analog In 4 is SDA, Analog In 5 is SCL
// These correspond to pin 27 (PC4/ADC4/SDA) and pin 28 (PC5/ADC5/SCL) on the Atmega8
// The Wire class handles the TWI transactions, abstracting the nitty-gritty to make
// prototyping easy.

void setup()
{
  pinMode(9, OUTPUT);
  digitalWrite(9, HIGH);
  Serial.begin(9600);

  CLKPR = (1<<clkpce);
  CLKPR = 0;

  Wire.begin(); // join i2c bus (address optional for master)
  Wire.beginTransmission(0x1D);
  Wire.send(0x20); // CTRL_REG1 (20h)
  Wire.send(0x87); // Device on, 40hz, normal mode, all axis's enabled
  Wire.endTransmission();

}

void loop()
{

  byte z_val_l, z_val_h, x_val_l, x_val_h, y_val_l, y_val_h;
  int z_val, x_val, y_val;
  //Serial.println("hello?");
  //byte in_byte;
  // transmit to device with address 0x1D
  // according to the LIS3L* datasheet, the i2c address of is fixed
  // at the factory at 0011101b (0x1D)
  Wire.beginTransmission(0x1D);
  // send the sub address for the register we want to read
  // this is for the OUTZ_H register
  // n.b. supposedly masking the register address with 0x80,
  // you can do multiple reads, with the register address auto-incremented
  Wire.send(0x28);
  // stop transmitting
  Wire.endTransmission();
 // Now do a transfer reading one byte from the LIS3L*
 // This data will be the contents of register 0x28
 Wire.requestFrom(0x1D, 1);
  while(Wire.available())
 {
   x_val_l = Wire.receive();
 }
 Wire.beginTransmission(0x1D); Wire.send(0x29); Wire.endTransmission();
 Wire.requestFrom(0x1D, 1);
 while(Wire.available())
 {
    x_val_h = Wire.receive();
 }
 x_val = x_val_h;
 x_val <<= 8;
 x_val += x_val_l;

 // Y Axis
 Wire.beginTransmission(0x1D); Wire.send(0x2A); Wire.endTransmission();
 Wire.requestFrom(0x1D, 1);
 while(Wire.available())
 {
    y_val_l = Wire.receive();
 }
 Wire.beginTransmission(0x1D); Wire.send(0x2B); Wire.endTransmission();
 Wire.requestFrom(0x1D, 1);
 while(Wire.available())
 {
    y_val_h = Wire.receive();
 }

 y_val = y_val_h;
 y_val <<= 8;
 y_val += y_val_l;

// Z Axis
 Wire.beginTransmission(0x1D); Wire.send(0x2C); Wire.endTransmission();
 Wire.requestFrom(0x1D, 1);
 while(Wire.available())
 {
    z_val_l = Wire.receive();
 }
 Wire.beginTransmission(0x1D); Wire.send(0x2D); Wire.endTransmission();
 Wire.requestFrom(0x1D, 1);
 while(Wire.available())
 {
    z_val_h = Wire.receive();
 }

 z_val = z_val_h;
 z_val <<= 8;
 z_val += z_val_l;
  // Set up to read the next register
  // Supposedly, if you set the most significant bit of
  // the register address to 1, you can do a multiple read,
  // with the register address auto-incrementing. This way, you
  // don't have to do another write — specifying the next register —
  // before reading the next register value. This would be very good, but
  // with a cursory attempt, I have yet to make this work.
/*
    Wire.beginTransmission(0x1D); // transmit to device #4
  Wire.send(0x2C);        // read outx_h
  // stop transmitting
  Wire.endTransmission();
  Wire.requestFrom(0x1D, 1);
 while(Wire.available())
 {
   z_val_l = Wire.receive();
 }

 z_val = z_val_h;
 z_val <<= 8;
 z_val += z_val_l;

  // z axis acceleration, all set.
  // Perfectly still and with the z-axis parallel to the ground, it should be about 1024-ish
  // (I find in practice it is around 1019.)
  // When the z-axis is orthogonal to the ground, it should be zero.
  // 1024 is +1g acceleration, or normal gravity
  // 2048 is +2g
  // 0 is 0g
  Serial.println(z_val, DEC);
*/
Serial.print(x_val, HEX); Serial.print(" ");Serial.print(y_val, HEX); Serial.print(" "); Serial.println(z_val, HEX);
delay(100);
}

Technorati Tags: ,