Difference between revisions of "ATtiny85 8MHz and ADXL345 via I2C with Arduino code"

From ivc wiki
Jump to navigationJump to search
 
(5 intermediate revisions by the same user not shown)
Line 2: Line 2:


== Parts ==
== Parts ==
[[Image:trinket_attiny85.png|right|300px]] [[Image:adxl345_gy-291.jpg|right|400px]]
[[Image:trinket_attiny85.png|right|200px]] [[Image:adxl345_gy-291.jpg|right|250px]]
* Adafruit Trinket 3V ATtiny85 board
* Adafruit Trinket 3V ATtiny85 board
** No need for level shifting for ADXL345 I2C
** No need for level shifting for ADXL345 I2C
Line 25: Line 25:


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


<code>
<code>
      trinket1.name=Adafruit Trinket 1MHz
trinket1.name=Adafruit Trinket 1MHz
      trinket1.bootloader.low_fuses=0xF1
trinket1.bootloader.low_fuses=0xF1
      trinket1.bootloader.high_fuses=0xD5
trinket1.bootloader.high_fuses=0xD5
      trinket1.bootloader.extended_fuses=0xFE
trinket1.bootloader.extended_fuses=0xFE
      trinket1.upload.maximum_size=5310
trinket1.upload.maximum_size=5310
      trinket1.build.mcu=attiny85
trinket1.build.mcu=attiny85
      trinket1.build.f_cpu=1000000L
trinket1.build.f_cpu=1000000L
      trinket1.build.core=arduino:arduino
trinket1.build.core=arduino:arduino
      trinket1.build.variant=tiny8
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>
</code>


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


arduino_adxl345.ino:
arduino_adxl345.ino:
<pre>
<pre>
<nowiki>
/* ATtiny85 and ADXL345 demo code  
/* ATtiny85 and ADXL345 demo code  
   by ivc <ivc at x-pec.com> 2015
   by ivc <ivc at x-pec.com> 2015
Line 86: Line 96:
   readAccel(&x, &y, &z);
   readAccel(&x, &y, &z);


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


Line 93: Line 103:
   delay(100);
   delay(100);
}
}
<nowiki />
 
</pre>
</pre>


Line 99: Line 109:


<pre>
<pre>
<nowiki>
/************************************************************************
/************************************************************************
* This program is free software; you can redistribute it and/or modify *
* This program is free software; you can redistribute it and/or modify *
Line 124: Line 133:
#define DEVICE (0x53) // Device address as specified in datasheet  
#define DEVICE (0x53) // Device address as specified in datasheet  
#define SENSORS_GRAVITY_STANDARD (9.80665F) // Const float
#define SENSORS_GRAVITY_STANDARD (9.80665F) // Const float
#define MG2G_MULTIPLIER (0.004) // 4mg/LSB
#define MG2G_MULTIPLIER (0.004) // 1 LSB represents about 3.9 mg


byte _buff[2]; // Buffer array
byte _buff[2]; // Buffer array
Line 143: Line 152:


   // Setup ADXL345 registers, see datahsheet to configure for your liking
   // Setup ADXL345 registers, see datahsheet to configure for your liking
   writeTo(DATA_FORMAT, B00000000); // Enable most sensitive 2g range, B1B0
   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 output, B4B3B2B1
   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, 0); // Always be sure to zero out register, don't asume it's zero
   writeTo(POWER_CTL, B00001000); // Enter measurement mode, B4
   writeTo(POWER_CTL, B00001000); // Enter measurement mode, B4
Line 152: Line 161:
{
{
   // How many bytes to request from ADXL345 - it seems there are problems doing more than 4 bytes, keep it at 2 bytes.
   // How many bytes to request from ADXL345 - it seems there are problems doing more than 4 bytes, keep it at 2 bytes.
  // Z-axis DATAZ1 register is sometimes is bad, only first DATAZ1 register return valid data
   uint8_t howManyBytesToRead = 2;  
   uint8_t howManyBytesToRead = 2;  
  // Zero buffer array
  memset(_buff, 0, 6);
    
    
   // Each axis reading comes in 10 bit resolution, i.e. 2 bytes, least significat byte first!!  
   // 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
   // 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
   // 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
   //readFrom( DATAX0, howManyBytesToRead, _buff); //read the acceleration data from the ADXL345
   *x = ((((int)_buff[0]) << 8) | _buff[1]) * MG2G_MULTIPLIER * SENSORS_GRAVITY_STANDARD;   
   //*x = ((((int)_buff[0]) << 8) | _buff[1]) * MG2G_MULTIPLIER * SENSORS_GRAVITY_STANDARD; 
  *x = read16(DATAX0) * MG2G_MULTIPLIER * SENSORS_GRAVITY_STANDARD;   


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


   readFrom( DATAY0, howManyBytesToRead, _buff); //read the acceleration data from the ADXL345
   //readFrom( DATAZ0, howManyBytesToRead, _buff); //read the acceleration data from the ADXL345
   *y = ((((int)_buff[0]) << 8) | _buff[1]) * MG2G_MULTIPLIER * SENSORS_GRAVITY_STANDARD;  
   //*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
   // Zero buffer array
   *z = read16(DATAZ0) * MG2G_MULTIPLIER * SENSORS_GRAVITY_STANDARD;  
  memset(_buff, 0, 6);
 
  readFrom( DATAZ0, howManyBytesToRead, _buff); //read the acceleration data from the ADXL345
   *z = ((((int)_buff[1]) << 8) | _buff[0]) * MG2G_MULTIPLIER * SENSORS_GRAVITY_STANDARD;


   // Test single bit
   // Test single bit
Line 206: Line 209:
  //TinyWireM.endTransmission();        // end transmission
  //TinyWireM.endTransmission();        // end transmission
}
}
<nowiki />
 
// 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
  }
</pre>
</pre>

Latest revision as of 19:49, 14 May 2015

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
  }