Last active
December 26, 2015 22:49
-
-
Save duppypro/7225636 to your computer and use it in GitHub Desktop.
Sample Electric Imp code for reading i2c registers of MMA8452Q accelerometer
See electricimp.com
See https://www.sparkfun.com/products/10955 for MMA8452Q accelerometer breakout board
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Sample code using MMA8452Q accelerometer | |
// Electric Imp Device Squirrel code | |
// License: | |
// This code is provided under the Creative Commons Attribution-ShareAlike 3.0 License | |
// http://creativecommons.org/licenses/by-sa/3.0/us/legalcode | |
// If you find bugs report to duppypro on github or @duppy #MMA8452Q on twitter | |
// If you find this useful, send a good word to @duppy #MMA8452Q | |
// Thanks to @jayrz for finding the first bug. | |
///////////////////////////////////////////////// | |
// global constants and variables | |
const versionString = "MMA8452Q Sample v00.01.2013-10-29a" | |
//const accelChangeThresh = 500 // change in accel per sample to count as movement. Units of milliGs | |
pollMMA8452QBusy <- false // guard against interrupt handler collisions FIXME: Is this necessary? Debugging why I get no EA_BIT set error sometimes | |
/////////////////////////////////////////////// | |
// constants for MMA8452Q i2c registers | |
// the slave address for this device is set in hardware. Creating a variable to save it here is helpful. | |
// The SparkFun breakout board defaults to 0x1D, set to 0x1C if SA0 jumper on the bottom of the board is set | |
const MMA8452Q_ADDR = 0x1D // A '<< 1' is needed. I add the '<< 1' in the helper functions. | |
//const MM8452Q_ADDR = 0x1C // Use this address if SA0 jumper is set. | |
const STATUS = 0x00 | |
const ZYXOW_BIT = 0x7 // name_BIT == BIT position of name | |
const ZYXDR_BIT = 0x3 | |
const OUT_X_MSB = 0x01 | |
const SYSMOD = 0x0B | |
const SYSMOD_STANDBY = 0x00 | |
const SYSMOD_WAKE = 0x01 | |
const SYSMOD_SLEEP = 0x02 | |
const INT_SOURCE = 0x0C | |
const SRC_ASLP_BIT = 0x7 | |
const SRC_FF_MT_BIT = 0x2 | |
const SRC_DRDY_BIT = 0x0 | |
const WHO_AM_I = 0x0D | |
const I_AM_MMA8452Q = 0x2A // read addr WHO_AM_I, expect I_AM_MMA8452Q | |
const XYZ_DATA_CFG = 0x0E | |
const FS_2G = 0x00 | |
const FS_4G = 0x01 | |
const FS_8G = 0x02 | |
const HPF_OUT_BIT = 0x5 | |
const HP_FILTER_CUTOFF = 0x0F | |
const FF_MT_CFG = 0x15 | |
const ELE_BIT = 0x7 | |
const OAE_BIT = 0x6 | |
const XYZEFE_BIT = 0x3 // numBits == 3 (one each for XYZ) | |
const XYZEFE_ALL = 0x07 // enable all 3 bits | |
const FF_MT_SRC = 0x16 | |
const EA_BIT = 0x7 | |
const FF_MT_THS = 0x17 | |
const DBCNTM_BIT = 0x7 | |
const THS_BIT = 0x0 // numBits == 7 | |
const FF_MT_COUNT = 0x18 | |
const ASLP_COUNT = 0x29 | |
const CTRL_REG1 = 0x2A | |
const ASLP_RATE_BIT = 0x6 // numBits == 2 | |
const ASLP_RATE_12p5HZ = 0x1 | |
const ASLP_RATE_1p56HZ = 0x3 | |
const DR_BIT = 0x3 // numBits == 3 | |
const DR_12p5HZ = 0x5 | |
const DR_1p56HZ = 0x7 | |
const LNOISE_BIT = 0x2 | |
const F_READ_BIT = 0x1 | |
const ACTIVE_BIT = 0x0 | |
const CTRL_REG2 = 0x2B | |
const ST_BIT = 0x7 | |
const RST_BIT = 0x6 | |
const SMODS_BIT = 0x3 // numBits == 2 | |
const SLPE_BIT = 0x2 | |
const MODS_BIT = 0x0 // numBits == 2 | |
const MODS_NORMAL = 0x00 | |
const MODS_LOW_POWER = 0x03 | |
const CTRL_REG3 = 0x2C | |
const WAKE_FF_MT_BIT = 0x3 | |
const IPOL_BIT = 0x1 | |
const CTRL_REG4 = 0x2D | |
const INT_EN_ASLP_BIT = 0x7 | |
const INT_EN_LNDPRT_BIT= 0x4 | |
const INT_EN_FF_MT_BIT = 0x2 | |
const INT_EN_DRDY_BIT = 0x0 | |
const CTRL_REG5 = 0x2E | |
// helper variables for MMA8452Q. These are not const because they may have reason to change dynamically. | |
i2cRetryPeriod <- 1.0 // seconds to wait before retrying a failed i2c operation | |
maxG <- FS_4G // what scale to get G readings | |
i2c <- hardware.i2c89 // now can use i2c.read() | |
/////////////////////////////////////////////// | |
//define functions | |
// start with fairly generic i2c helper functions | |
function readBitField(val, bitPosition, numBits){ // works for 8bit registers | |
// bitPosition and numBits are not bounds checked | |
return (val >> bitPosition) & (0x00FF >> (8 - numBits)) | |
} | |
function readBit(val, bitPosition) { return readBitField(val, bitPosition, 1) } | |
function writeBitField(val, bitPosition, numBits, newVal) { // works for 8bit registers | |
// newVal is not bounds checked | |
return (val & (((0x00FF >> (8 - numBits)) << bitPosition) ^ 0x00FF)) | (newVal << bitPosition) | |
} | |
function writeBit(val, bitPosition, newVal) { return writeBitField(val, bitPosition, 1, newVal) } | |
// Read a single byte from addressToRead and return it as a byte. (The '[0]' causes a byte to return) | |
function readReg(addressToRead) { | |
return readSequentialRegs(addressToRead, 1)[0] | |
} | |
// Writes a single byte (dataToWrite) into addressToWrite. Returns error code from i2c.write | |
// Continue retry until success. Caller does not need to check error code | |
function writeReg(addressToWrite, dataToWrite) { | |
local err = null | |
while (err == null) { | |
err = i2c.write(MMA8452Q_ADDR << 1, format("%c%c", addressToWrite, dataToWrite)) | |
// server.log(format("i2c.write addr=0x%02x data=0x%02x", addressToWrite, dataToWrite)) | |
if (err == null) { | |
server.error("i2c.write of value " + format("0x%02x", dataToWrite) + " to " + format("0x%02x", addressToWrite) + " failed.") | |
imp.sleep(i2cRetryPeriod) | |
server.error("retry i2c.write") | |
} | |
} | |
return err | |
} | |
// Read numBytes sequentially, starting at addressToRead | |
// Continue retry until success. Caller does not need to check error code | |
function readSequentialRegs(addressToRead, numBytes) { | |
local data = null | |
while (data == null) { | |
data = i2c.read(MMA8452Q_ADDR << 1, format("%c", addressToRead), numBytes) | |
if (data == null) { | |
server.error("i2c.read from " + format("0x%02x", addressToRead) + " of " + numBytes + " byte" + ((numBytes > 1) ? "s" : "") + " failed.") | |
imp.sleep(i2cRetryPeriod) | |
server.error("retry i2c.read") | |
} | |
} | |
return data | |
} | |
// now functions unique to MMA8452Q | |
function readAccelData() { | |
local rawData = null // x/y/z accel register data stored here, 3 bytes | |
local accelData = array(3) | |
local i | |
local val | |
rawData = readSequentialRegs(OUT_X_MSB, 3) // Read the three raw data registers into data array | |
foreach (i, val in rawData) { | |
accelData[i] = math.floor(1000.0 * ((val < 128 ? val : val - 256) / ((64 >> maxG) + 0.0))) | |
// HACK: in above calc maxG just happens to be (log2(full_scale) - 1) see: const for FS_2G, FS_4G, FS_8G | |
//convert to signed integer milliGs | |
} | |
return accelData | |
} | |
// Reset the MMA8452Q | |
function MMA8452QReset() { | |
local reg | |
do { | |
reg = readReg(WHO_AM_I) // Read WHO_AM_I register | |
if (reg == I_AM_MMA8452Q) { | |
server.log("Found MMA8452Q. Sending RST command...") | |
break | |
} else { | |
server.error("Could not connect to MMA8452Q: WHO_AM_I reg == " + format("0x%02x", reg)) | |
imp.sleep(i2cRetryPeriod) | |
} | |
} while (true) | |
// send reset command | |
writeReg(CTRL_REG2, writeBit(readReg(CTRL_REG2), RST_BIT, 1)) | |
do { | |
reg = readReg(WHO_AM_I) // Read WHO_AM_I register | |
if (reg == I_AM_MMA8452Q) { | |
server.log("MMA8452Q is online!") | |
break | |
} else { | |
server.error("Could not connect to MMA8452Q: WHO_AM_I reg == " + format("0x%02x", reg)) | |
imp.sleep(i2cRetryPeriod) | |
} | |
} while (true) | |
} | |
function MMA8452QSetActive(mode) { | |
// Sets the MMA8452Q active mode. | |
// 0 == STANDBY for changing registers | |
// 1 == ACTIVE for outputting data | |
writeReg(CTRL_REG1, writeBit(readReg(CTRL_REG1), ACTIVE_BIT, mode)) | |
} | |
function initMMA8452Q() { | |
// Initialize the MMA8452Q registers | |
// See the many application notes for more info on setting all of these registers: | |
// http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MMA8452Q | |
local reg | |
MMA8452QReset() // Sometimes imp card resets and MMA8452Q keeps power | |
// Must be in standby to change registers | |
// in STANDBY already after RESET//MMA8452QSetActive(0) | |
// Set up the full scale range to 2, 4, or 8g. | |
// FIXME: assumes HPF_OUT_BIT in this same register always == 0 | |
writeReg(XYZ_DATA_CFG, maxG) | |
server.log(format("XYZ_DATA_CFG == 0x%02x", readReg(XYZ_DATA_CFG))) | |
// setup CTRL_REG1 | |
reg = readReg(CTRL_REG1) | |
reg = writeBitField(reg, ASLP_RATE_BIT, 2, ASLP_RATE_1p56HZ) | |
reg = writeBitField(reg, DR_BIT, 3, DR_12p5HZ) | |
// leave LNOISE_BIT as default off to save power | |
// Set Fast read mode to read 8bits per xyz instead of 12bits | |
reg = writeBit(reg, F_READ_BIT, 1) | |
// set all CTRL_REG1 bit fields in one i2c write | |
writeReg(CTRL_REG1, reg) | |
server.log(format("CTRL_REG1 == 0x%02x", readReg(CTRL_REG1))) | |
// setup CTRL_REG2 | |
reg = readReg(CTRL_REG2) | |
// set Oversample mode in sleep | |
reg = writeBitField(reg, SMODS_BIT, 2, MODS_LOW_POWER) | |
// Enable Auto-SLEEP | |
//reg = writeBit(reg, SLPE_BIT, 1) | |
// Disable Auto-SLEEP | |
reg = writeBit(reg, SLPE_BIT, 0) | |
// set Oversample mode in wake | |
reg = writeBitField(reg, MODS_BIT, 2, MODS_LOW_POWER) | |
// set all CTRL_REG2 bit fields in one i2c write | |
writeReg(CTRL_REG2, reg) | |
server.log(format("CTRL_REG2 == 0x%02x", readReg(CTRL_REG2))) | |
// setup CTRL_REG3 | |
reg = readReg(CTRL_REG3) | |
// allow Motion to wake from SLEEP | |
reg = writeBit(reg, WAKE_FF_MT_BIT, 1) | |
// change Int Polarity | |
reg = writeBit(reg, IPOL_BIT, 1) | |
// set all CTRL_REG3 bit fields in one i2c write | |
writeReg(CTRL_REG3, reg) | |
server.log(format("CTRL_REG3 == 0x%02x", readReg(CTRL_REG3))) | |
// setup FF_MT_CFG | |
reg = readReg(FF_MT_CFG) | |
// enable ELE_BIT to latch FF_MT_SRC events | |
reg = writeBit(reg, ELE_BIT, 1) | |
// enable Motion detection (not Free Fall detection) | |
reg = writeBit(reg, OAE_BIT, 1) | |
// enable on all axis x, y, and z | |
reg = writeBitField(reg, XYZEFE_BIT, 3, XYZEFE_ALL) | |
// set all FF_MT_CFG bit fields in one i2c write | |
writeReg(FF_MT_CFG, reg) | |
server.log(format("FF_MT_CFG == 0x%02x", readReg(FF_MT_CFG))) | |
// setup Motion threshold to n*0.063. (16 * 0.063 == 1G) | |
writeReg(FF_MT_THS, 60) // FIXME: this is a shortcut and assumes DBCNTM_BIT is 0 | |
server.log(format("FF_MT_THS == 0x%02x", readReg(FF_MT_THS))) | |
// setup sleep counter, the time in multiples of 320ms of no activity to enter sleep mode | |
//dont' use ASLP_COUNT for now, use change in prev AccelData reading | |
//writeReg(ASLP_COUNT, 10) // 10 * 320ms = 3.2 seconds | |
//Enable Sleep interrupts | |
// writeReg(CTRL_REG4, writeBit(readReg(CTRL_REG4), INT_EN_ASLP_BIT, 1)) | |
//Enable Motion interrupts | |
writeReg(CTRL_REG4, writeBit(readReg(CTRL_REG4), INT_EN_FF_MT_BIT, 1)) | |
// Enable interrupts on every new data | |
writeReg(CTRL_REG4, writeBit(readReg(CTRL_REG4), INT_EN_DRDY_BIT, 1)) | |
server.log(format("CTRL_REG4 == 0x%02x", readReg(CTRL_REG4))) | |
MMA8452QSetActive(1) // Set to active to start reading | |
} // initMMA8452Q | |
// now application specific functions | |
function pollMMA8452Q() { | |
local xyz | |
local reg | |
while (pollMMA8452QBusy) { | |
server.log("pollMMA8452QBusy collision") | |
// wait herer unitl other instance of int handler is done | |
// FIXME: I never see this error, probably not neessary, just being paranoid. | |
} | |
pollMMA8452QBusy = true // mark as busy | |
if (hardware.pin1.read() == 1) { // only react to low to high edge | |
//FIXME: do we need to check status for data ready in all xyz?//log(format("STATUS == 0x%02x", readReg(STATUS)), 80) | |
reg = readReg(INT_SOURCE) | |
while (reg != 0x00) { | |
// server.log(format("INT_SOURCE == 0x%02x", reg)) | |
if (readBit(reg, SRC_DRDY_BIT) == 0x1) { | |
xyz = readAccelData() // this clears the SRC_DRDY_BIT | |
server.log(format("%4d %4d %4d", xyz[0], xyz[1], xyz[2])) | |
// do something with xyz data here | |
} | |
if (readBit(reg, SRC_FF_MT_BIT) == 0x1) { | |
server.log("Interrupt SRC_FF_MT_BIT") | |
reg = readReg(FF_MT_SRC) // this clears SRC_FF_MT_BIT | |
imp.setpowersave(false) // go to low latency mode because we detected motion | |
} | |
if (readBit(reg, SRC_ASLP_BIT) == 0x1) { | |
reg = readReg(SYSMOD) // this clears SRC_ASLP_BIT | |
// server.log(format("Entering SYSMOD 0x%02x", reg)) | |
} | |
reg = readReg(INT_SOURCE) | |
} // while (reg != 0x00) | |
} else { | |
// server.log("INT2 LOW") | |
} | |
pollMMA8452QBusy = false // clear so other inst of int handler can run | |
} // pollMMA8452Q | |
//////////////////////////////////////////////////////// | |
// first code starts here | |
imp.setpowersave(true) // start in low power mode. | |
// Optimized for case where wakeup was caused by periodic timer, not user activity | |
// Register with the server | |
//imp.configure("MMA8452Q 1D6", [], []) // One 6-sided Die | |
// no in and out []s anymore, using Agent messages | |
// Send status to know we are alive | |
server.log("BOOTING " + versionString + " " + hardware.getimpeeid() + "/" + imp.getmacaddress()) | |
server.log("imp software version : " + imp.getsoftwareversion()) | |
// BUGBUG: below needed until newer firmware!? See http://forums.electricimp.com/discussion/comment/4875#Comment_2714 | |
// imp.enableblinkup(true) | |
// Configure pin1 for wakeup. Connect MMA8452Q INT2 pin to imp pin1. | |
hardware.pin1.configure(DIGITAL_IN_WAKEUP, pollMMA8452Q) | |
// set the I2C clock speed. We can do 10 kHz, 50 kHz, 100 kHz, or 400 kHz | |
// i2c.configure(CLOCK_SPEED_400_KHZ) | |
i2c.configure(CLOCK_SPEED_100_KHZ) // try to fix i2c read errors. May need 4.7K external pull-up to go to 400_KHZ | |
initMMA8452Q() // sets up code to run on interrupts from MMA8452Q | |
pollMMA8452Q() // call first time to get a value on boot. | |
// No more code to execute so we'll sleep until an interrupt from MMA8452Q. | |
// Sample functions for using MMA8452Q accelerometer | |
// Electric Imp Device Squirrel (.nut) code | |
// end of code |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment