ATtiny85 8MHz and ADXL345 via I2C with Arduino code

From ivc wiki
Revision as of 18:49, 14 May 2015 by Ivc (talk | contribs) (→‎Code)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

This is a small setup based on the ATtiny85 to make use of the advanced features of the ADXL345 accelerometer.

Parts

Trinket attiny85.png
Adxl345 gy-291.jpg
  • Adafruit Trinket 3V ATtiny85 board
    • No need for level shifting for ADXL345 I2C
    • Comes with [Trinket Bootloader] to upload binary with USB during the first 10 seconds of power-up/reset
    • Red LED on #1/PB1 is handy for debugging
  • GY-291 ADXL345 Digital 3-Axis accelerometer breakout
    • On-board 3.3V LDO for ADXL345
    • I2C 4.7k pull-ups
    • CS 4.7k pull-up - enable I2C, not SPI
    • SDO 4.7k pull-down - I2C address 0x53
    • Comes with 2.54mm pin header

Wiring

  • ATtiny -> ADXL345
    • USB (point) -> VCC
    • GND -> GND
    • #0/PB0 -> SDA
    • #2/PB2 -> SCL

Libraries

  • TinyWireM - Using the USI on ATtiny to emulate TWI/I2C, improved by Adafruit

Speed

Add this section to the hardware.txt in the attiny folder to enable 1MHz and 4MHz processing speed.

trinket1.name=Adafruit Trinket 1MHz trinket1.bootloader.low_fuses=0xF1 trinket1.bootloader.high_fuses=0xD5 trinket1.bootloader.extended_fuses=0xFE trinket1.upload.maximum_size=5310 trinket1.build.mcu=attiny85 trinket1.build.f_cpu=1000000L trinket1.build.core=arduino:arduino trinket1.build.variant=tiny8

trinket2.name=Adafruit Trinket 4MHz trinket2.bootloader.low_fuses=0xF1 trinket2.bootloader.high_fuses=0xD5 trinket2.bootloader.extended_fuses=0xFE trinket2.upload.maximum_size=5310 trinket2.build.mcu=attiny85 trinket2.build.f_cpu=4000000L trinket2.build.core=arduino:arduino trinket2.build.variant=tiny8

Code

Save both files in the same folder, called arduino_adxl345 and open arduino_adxl345.ino

arduino_adxl345.ino:

/* ATtiny85 and ADXL345 demo code 
   by ivc <ivc at x-pec.com> 2015

  This sensor test will enable the red LED on the Trinket board when the y-axis is positive, e.g. tilted to one side, and turn
  off when tilted the other way.
  
  The TWI/I2C speed is determined by the F_CPU speed selected during compiling (Tools -> Board -> Adafruit Trinket 1/8/16MHz).
  Due to inherent instability of the internal RC oscillator in the ATtiny, 8MHz does not always work - try 1MHz. Also, a pull-up
  higher than 4.7K Ohm will introduce slew on the rising edge, causing the signal fail to be recognized. Try 2K Ohm pull-ups
  on 3.3V setups, and 4.7K on 5V (3.3V/1.5mA = 2K Ohm).

  Beware: ADXL345 sensor can easily be damaged if handled improperly. Always keep a couple extras to swap out and for testing.
*/

#include <TinyWireM.h>
#include <avr/power.h>

float x, y, z;

void setup(void) 
{
  // Dynamically change processing speed - use Tools -> Board -> Adafruit Trinket 1/8/16MHz to adjust
  if (F_CPU == 16000000) { clock_prescale_set(clock_div_1);
  } else if (F_CPU == 8000000) { clock_prescale_set(clock_div_1);
  } else if (F_CPU == 4000000) { clock_prescale_set(clock_div_2);
  } else if (F_CPU == 2000000) { clock_prescale_set(clock_div_4);
  } else if (F_CPU == 1000000) { clock_prescale_set(clock_div_8); }

  // On-board Red LED
  pinMode(PB1, OUTPUT);
  digitalWrite(PB1, LOW); 

  // Setup ADXL345
  adxl345_init();
}

void loop(void) 
{
  // Update accelerometer values
  readAccel(&x, &y, &z);

  // Enable LED of tilted over mid-point of y-axis
  if (y > 0) digitalWrite(PB1, HIGH); 
  else digitalWrite(PB1, LOW); 

  // Wait a small period to update
  delay(100);
}

adxl345_sensor.ino:

/************************************************************************
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU License V2.                            *
* This program is distributed in the hope that it will be useful,      *
* but WITHOUT ANY WARRANTY; without even the implied warranty of       *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
* GNU General Public License, version 2 for more details               *
*                                                                      *
* Bare bones ADXL345 i2c example for Arduino 1.0                       *
* by Jens C Brynildsen <http://www.flashgamer.com>                     *
* This version is not reliant of any external lib                      *
* (Adapted for Arduino 1.0 from http://code.google.com/p/adxl345driver)*
*                                                                      *
* Demonstrates use of ADXL345 (using the Sparkfun ADXL345 breakout)    *
* with i2c communication. Datasheet:                                   *
* http://www.sparkfun.com/datasheets/Sensors/Accelerometer/ADXL345.pdf *
* If you need more advanced features such as freefall and tap          *
* detection, check out:                                                *
* https://github.com/jenschr/Arduino-libraries                         *
***********************************************************************/
#include <TinyWireM.h>

#define DEVICE (0x53) // Device address as specified in datasheet 
#define SENSORS_GRAVITY_STANDARD (9.80665F) // Const float
#define MG2G_MULTIPLIER (0.004) // 1 LSB represents about 3.9 mg

byte _buff[2]; // Buffer array

char POWER_CTL = 0x2D;	//Power Control Register
char DATA_FORMAT = 0x31; // Data format control
char DATAX0 = 0x32;	// X-Axis Data 0
char DATAX1 = 0x33;	// X-Axis Data 1
char DATAY0 = 0x34;	// Y-Axis Data 0
char DATAY1 = 0x35;	// Y-Axis Data 1
char DATAZ0 = 0x36;	// Z-Axis Data 0
char DATAZ1 = 0x37;	// Z-Axis Data 1
char BW_RATE = 0x2C;    // Data rate and power mode control

void adxl345_init()
{
  TinyWireM.begin(); // Join i2c bus (address optional for master)

  // Setup ADXL345 registers, see datahsheet to configure for your liking
  writeTo(DATA_FORMAT, B00001000); // Enable full resolution 10-bit, B3, and most sensitive 2g range, B1B0
  writeTo(BW_RATE, B00001100); // Set 400Hz output update rate - not I2C but data update rate, B4B3B2B1
  writeTo(POWER_CTL, 0); // Always be sure to zero out register, don't asume it's zero
  writeTo(POWER_CTL, B00001000); // Enter measurement mode, B4
}

void readAccel(float *x, float *y, float *z)
{
  // How many bytes to request from ADXL345 - it seems there are problems doing more than 4 bytes, keep it at 2 bytes.
  uint8_t howManyBytesToRead = 2; 
  
  // Each axis reading comes in 10 bit resolution, i.e. 2 bytes, least significat byte first!! 
  // Thus we are converting both bytes in to one int - shift bytes to align like DATAX1DATAX0
  // If the bytes seem switched from the sensor, change [1] to [0] and verse visa
  //readFrom( DATAX0, howManyBytesToRead, _buff); //read the acceleration data from the ADXL345
  //*x = ((((int)_buff[0]) << 8) | _buff[1]) * MG2G_MULTIPLIER * SENSORS_GRAVITY_STANDARD;   
  *x = read16(DATAX0) * MG2G_MULTIPLIER * SENSORS_GRAVITY_STANDARD;   

  //readFrom( DATAY0, howManyBytesToRead, _buff); //read the acceleration data from the ADXL345
  //*y = ((((int)_buff[0]) << 8) | _buff[1]) * MG2G_MULTIPLIER * SENSORS_GRAVITY_STANDARD;   
  *y = read16(DATAY0) * MG2G_MULTIPLIER * SENSORS_GRAVITY_STANDARD;   

  //readFrom( DATAZ0, howManyBytesToRead, _buff); //read the acceleration data from the ADXL345
  //*z = ((((int)_buff[0]) << 8) | _buff[1]) * MG2G_MULTIPLIER * SENSORS_GRAVITY_STANDARD;
  // Does not always seem to work - some units have a defective z-component I've noticed
  *z = read16(DATAZ0) * MG2G_MULTIPLIER * SENSORS_GRAVITY_STANDARD;   

  // Test single bit
  //if ( (_buff[0] & 0B10000000) ) digitalWrite(PB1, HIGH); 
  //else digitalWrite(PB1, LOW);
}

// Write vale to register address
void writeTo(byte address, byte val) {
  TinyWireM.beginTransmission(DEVICE);  // start transmission to device 
  TinyWireM.write(address);             // send register address
  TinyWireM.write(val);                 // send value to write
  TinyWireM.endTransmission();          // end transmission
}

// Reads num bytes starting from address register on device in to _buff array
void readFrom(byte address, int num, byte _buff[]) {
  TinyWireM.beginTransmission(DEVICE); // start transmission to device 
  TinyWireM.write(address);             // sends address to read from
  TinyWireM.endTransmission();         // end transmission

  //TinyWireM.beginTransmission(DEVICE); // start transmission to device
  TinyWireM.requestFrom(DEVICE, num);    // request 6 bytes from device

  int i = 0;
  while(TinyWireM.available())         // device may send less than requested (abnormal)
  { 
    _buff[i] = TinyWireM.read();    // receive a byte
    i++;
  }
 //TinyWireM.endTransmission();         // end transmission
}

// Reads 2 bytes (16bits) from first x, y, or z register
int16_t read16(byte address) {
   TinyWireM.beginTransmission(DEVICE);
   TinyWireM.write(address);             // sends address to read from
   TinyWireM.endTransmission();
   TinyWireM.requestFrom(DEVICE, 2); // request 2 bytes from device
   //return (uint16_t)(TinyWireM.read() << 8) | (TinyWireM.read()); // DATAx0DATAx1
   return (uint16_t)(TinyWireM.read() | (TinyWireM.read() << 8)); // DATAx1DATAx0
  }