Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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
// 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