Hello all,
I am a newcomer to the microcontroller scene and I am very impressed with the Teensy 3.2. This forum has been very helpful as well. I have encountered a frustrating problem: Initializing the SD-Card ruins my Accelerometer data.
Goal: Log Acceleration Data onto SD-Card
I want to record the outputs of a MEMS accelerometer (ADXL-362) of 100 readings/sec onto a microSD card using the Teensy adapter sold on PJRC.
My Teensy 3.2 can properly communicate with either device in isolation. I can Serial.print the correct values with Teensy+ADXL (X=10,Y=-10,Z=1280); and I can successfully Read/Write to the SD card with Teensy+SD adapter. Both devices share the same MISO,MOSI,CLK lines, and have a different CS pin.
However, once I initialize the SD-Card using SD.begin(), my ADXL readings become garbage. If I initialize the SD-card before my accelerometer, I get (X=255,Y=0,Z=0). If I initialize the SD card after the accelerometer, I get (X=-6,000, Y =-7000, Z = -168000). Both very wrong values.
What could be the issue?
It must be a software problem because both SPI-devices behave correctly in isolation. Both the ADXL-362 and the SD-adapter run on SPI_MODE=0. I suspect it's some SPI setting that I am not familiar with.
This link points to a similar problem, where the solution was to separate out each 4 SPI lines between devices because the temperature sensor was using soft_SPI instead of hard-coded SPI pins. But I am using the Teensy's dedicated SPI pins, and don't want to have multiple lines because I will be making a PCB with an Othermill CNC. [Off topic: that thing is great!]
My guess is that I am beginning the SPI twice and thus messing up the settings, seeing how the "order" of initializing SD or ADXL matters. I tried digging through the SdFatBase.cpp and SdSpiTeensy3.cpp but my head started spinning trying to keep track of all the definitions. I don't see where SDfat.begin() initializes the SPI. I am also using a 3rd party arduino library for the ADXL-362 with some custom modifications (below)
Details:
I have the setup as follows:
Teensy Pin 10 <==> SD-Slave Select
Teensy Pin 11 <==> SD-MOSI // ADXL-MOSI
Teensy Pin 12 <==> SD-MISO // ADXL-MISO
Teensy Pin 14 <==> SD-CLK // ADXL-CLK
Teensy Pin 15 <==> ADXL-Slave Select
I have used the alternate Teensy CLK pin of 14 instead of the default 13 because I want 13's LED function. Elsewhere in the forum I see that Paul suggested these CORE_PIN commands, which I put in both my ADXL library and SdSpiTeensy3.cpp in the SD Fat library :
Help Request
Has anybody here gotten a MEMS sensor's data correctly saved onto an SD card? I'm a newbie to SPI and I thought it was conceptually simple, but and it's very confusing to me with this talk of "soft" vs "hard" SPI pins, vs bitbanging
TEENSY CODE
Teensy SdSpiTeensy3.cpp
ADXL362.CPP
ADXL362.H
I am a newcomer to the microcontroller scene and I am very impressed with the Teensy 3.2. This forum has been very helpful as well. I have encountered a frustrating problem: Initializing the SD-Card ruins my Accelerometer data.
Goal: Log Acceleration Data onto SD-Card
I want to record the outputs of a MEMS accelerometer (ADXL-362) of 100 readings/sec onto a microSD card using the Teensy adapter sold on PJRC.
My Teensy 3.2 can properly communicate with either device in isolation. I can Serial.print the correct values with Teensy+ADXL (X=10,Y=-10,Z=1280); and I can successfully Read/Write to the SD card with Teensy+SD adapter. Both devices share the same MISO,MOSI,CLK lines, and have a different CS pin.
However, once I initialize the SD-Card using SD.begin(), my ADXL readings become garbage. If I initialize the SD-card before my accelerometer, I get (X=255,Y=0,Z=0). If I initialize the SD card after the accelerometer, I get (X=-6,000, Y =-7000, Z = -168000). Both very wrong values.
What could be the issue?
It must be a software problem because both SPI-devices behave correctly in isolation. Both the ADXL-362 and the SD-adapter run on SPI_MODE=0. I suspect it's some SPI setting that I am not familiar with.
This link points to a similar problem, where the solution was to separate out each 4 SPI lines between devices because the temperature sensor was using soft_SPI instead of hard-coded SPI pins. But I am using the Teensy's dedicated SPI pins, and don't want to have multiple lines because I will be making a PCB with an Othermill CNC. [Off topic: that thing is great!]
My guess is that I am beginning the SPI twice and thus messing up the settings, seeing how the "order" of initializing SD or ADXL matters. I tried digging through the SdFatBase.cpp and SdSpiTeensy3.cpp but my head started spinning trying to keep track of all the definitions. I don't see where SDfat.begin() initializes the SPI. I am also using a 3rd party arduino library for the ADXL-362 with some custom modifications (below)
Details:
I have the setup as follows:
Teensy Pin 10 <==> SD-Slave Select
Teensy Pin 11 <==> SD-MOSI // ADXL-MOSI
Teensy Pin 12 <==> SD-MISO // ADXL-MISO
Teensy Pin 14 <==> SD-CLK // ADXL-CLK
Teensy Pin 15 <==> ADXL-Slave Select
I have used the alternate Teensy CLK pin of 14 instead of the default 13 because I want 13's LED function. Elsewhere in the forum I see that Paul suggested these CORE_PIN commands, which I put in both my ADXL library and SdSpiTeensy3.cpp in the SD Fat library :
Code:
CORE_PIN13_CONFIG = PORT_PCR_MUX(1); //Make SCK =14
CORE_PIN14_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);//Make SCK =14
Help Request
Has anybody here gotten a MEMS sensor's data correctly saved onto an SD card? I'm a newbie to SPI and I thought it was conceptually simple, but and it's very confusing to me with this talk of "soft" vs "hard" SPI pins, vs bitbanging
TEENSY CODE
Code:
#include <SPI.h>
#include <ADXL362.h>
#include <SdFat.h>
#define VIOLATION_LED 13 //Violation Pin
#define STATUS_LED 6 //Calibration Indicator
#define INIT_SIZE 500
ADXL362 xl;
SdFat SD;
File myFile;
int16_t XValue, YValue, ZValue, Temperature;
unsigned long starttime;
unsigned long runtime;
#define SD_SS 10 //SD Card Slave Select
#define XL_SS 15 //ADXL Slave Select
void setup(){
//Initialize Pins
pinMode(XL_SS,OUTPUT);
pinMode(SD_SS,OUTPUT);
digitalWrite(XL_SS,HIGH);
digitalWrite(SD_SS,HIGH);
//Start Serial, get signal to start
Serial.begin(57600);
delay(1000);
Serial.println(F("Send any character to start sketch.\n"));
while (Serial.available() && Serial.read()); // empty buffer
while (!Serial.available()){
delay(1500);
}
while (Serial.available() && Serial.read()); // empty buffer again
// //Start SPI of SD Card (starting SD Card before ADXL = 255, 0, 0)
////----------------------------------------------------------------------------------------
// digitalWrite(XL_SS,HIGH);
// digitalWrite(SD_SS,LOW);
//
// if (!SD.begin(SD_SS)) {
// Serial.println("SD Card initialization failed!");
// return;
// }
// Serial.println("SD Card initialization done.");
//
////----------------------------------------------------------------------------------------
//Start SPI for ADXL
//----------------------------------------------------------------------------------------
digitalWrite(XL_SS,LOW);
digitalWrite(SD_SS,HIGH);
xl.begin(XL_SS); // Setup SPI protocol, issue device soft reset
// xl.setNoise(ADXL362_NOISE);
// xl.setNoise(ADXL362_LOWNOISE);
xl.setNoise(ADXL362_ULTRALOWNOISE);
xl.setODR(ADXL362_RATE_100);
xl.beginMeasure(); // Switch ADXL362 to measure mode
//----------------------------------------------------------------------------------------
// Start SPI of SD Card (starting SD Card AFTER ADXL = really high numbers)
//----------------------------------------------------------------------------------------
digitalWrite(XL_SS,HIGH);
digitalWrite(SD_SS,LOW);
if (!SD.begin(SD_SS)) {
Serial.println("SD Card initialization failed!");
return;
}
Serial.println("SD Card initialization done.");
//----------------------------------------------------------------------------------------
starttime = millis();
}
void loop(){
// read all three axis in burst to ensure all measurements correspond to same sample time
xl.readXYZTData(XValue, YValue, ZValue, Temperature);
runtime = millis()-starttime;
Serial.print(runtime);
Serial.print(",");
Serial.print(XValue);
Serial.print(",");
Serial.print(YValue);
Serial.print(",");
Serial.print(ZValue);
Serial.print(",");
Serial.println(Temperature);
delay(10);
}
Teensy SdSpiTeensy3.cpp
Code:
/* Arduino SdSpi Library
* Copyright (C) 2013 by William Greiman
*
* This file is part of the Arduino SdSpi Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino SdSpi Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "SdSpi.h"
#if defined(__arm__) && defined(CORE_TEENSY)
// SPI definitions
#include "kinetis.h"
#ifdef KINETISK
// use 16-bit frame if SPI_USE_8BIT_FRAME is zero
#define SPI_USE_8BIT_FRAME 0
// Limit initial fifo to three entries to avoid fifo overrun
#define SPI_INITIAL_FIFO_DEPTH 3
// define some symbols that are not in mk20dx128.h
#ifndef SPI_SR_RXCTR
#define SPI_SR_RXCTR 0XF0
#endif // SPI_SR_RXCTR
#ifndef SPI_PUSHR_CONT
#define SPI_PUSHR_CONT 0X80000000
#endif // SPI_PUSHR_CONT
#ifndef SPI_PUSHR_CTAS
#define SPI_PUSHR_CTAS(n) (((n) & 7) << 28)
#endif // SPI_PUSHR_CTAS
//------------------------------------------------------------------------------
/**
* initialize SPI pins
*/
void SdSpi::begin() {
SIM_SCGC6 |= SIM_SCGC6_SPI0;
}
//------------------------------------------------------------------------------
/**
* Initialize hardware SPI
*
*/
void SdSpi::init(uint8_t sckDivisor) {
uint32_t ctar, ctar0, ctar1;
if (sckDivisor <= 2) {
// 1/2 speed
ctar = SPI_CTAR_DBR | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0);
} else if (sckDivisor <= 4) {
// 1/4 speed
ctar = SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0);
} else if (sckDivisor <= 8) {
// 1/8 speed
ctar = SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1);
} else if (sckDivisor <= 12) {
// 1/12 speed
ctar = SPI_CTAR_BR(2) | SPI_CTAR_CSSCK(2);
} else if (sckDivisor <= 16) {
// 1/16 speed
ctar = SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(3);
} else if (sckDivisor <= 32) {
// 1/32 speed
ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(4) | SPI_CTAR_CSSCK(4);
} else if (sckDivisor <= 64) {
// 1/64 speed
ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(5);
} else {
// 1/128 speed
ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(6);
}
// CTAR0 - 8 bit transfer
ctar0 = ctar | SPI_CTAR_FMSZ(7);
// CTAR1 - 16 bit transfer
ctar1 = ctar | SPI_CTAR_FMSZ(15);
if (SPI0_CTAR0 != ctar0 || SPI0_CTAR1 != ctar1) {
SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F);
SPI0_CTAR0 = ctar0;
SPI0_CTAR1 = ctar1;
}
SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F);
CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
CORE_PIN12_CONFIG = PORT_PCR_MUX(2);
// CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); //Original SCK = 13
CORE_PIN13_CONFIG = PORT_PCR_MUX(1); //Make SCK =14
CORE_PIN14_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);//Make SCK =14
}
//------------------------------------------------------------------------------
/** SPI receive a byte */
uint8_t SdSpi::receive() {
SPI0_MCR |= SPI_MCR_CLR_RXF;
SPI0_SR = SPI_SR_TCF;
SPI0_PUSHR = 0xFF;
while (!(SPI0_SR & SPI_SR_TCF)) {}
return SPI0_POPR;
}
//------------------------------------------------------------------------------
/** SPI receive multiple bytes */
uint8_t SdSpi::receive(uint8_t* buf, size_t n) {
// clear any data in RX FIFO
SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F);
#if SPI_USE_8BIT_FRAME
// initial number of bytes to push into TX FIFO
int nf = n < SPI_INITIAL_FIFO_DEPTH ? n : SPI_INITIAL_FIFO_DEPTH;
for (int i = 0; i < nf; i++) {
SPI0_PUSHR = 0XFF;
}
// limit for pushing dummy data into TX FIFO
uint8_t* limit = buf + n - nf;
while (buf < limit) {
while (!(SPI0_SR & SPI_SR_RXCTR)) {}
SPI0_PUSHR = 0XFF;
*buf++ = SPI0_POPR;
}
// limit for rest of RX data
limit += nf;
while (buf < limit) {
while (!(SPI0_SR & SPI_SR_RXCTR)) {}
*buf++ = SPI0_POPR;
}
#else // SPI_USE_8BIT_FRAME
// use 16 bit frame to avoid TD delay between frames
// get one byte if n is odd
if (n & 1) {
*buf++ = receive();
n--;
}
// initial number of words to push into TX FIFO
int nf = n/2 < SPI_INITIAL_FIFO_DEPTH ? n/2 : SPI_INITIAL_FIFO_DEPTH;
for (int i = 0; i < nf; i++) {
SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | 0XFFFF;
}
uint8_t* limit = buf + n - 2*nf;
while (buf < limit) {
while (!(SPI0_SR & SPI_SR_RXCTR)) {}
SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | 0XFFFF;
uint16_t w = SPI0_POPR;
*buf++ = w >> 8;
*buf++ = w & 0XFF;
}
// limit for rest of RX data
limit += 2*nf;
while (buf < limit) {
while (!(SPI0_SR & SPI_SR_RXCTR)) {}
uint16_t w = SPI0_POPR;
*buf++ = w >> 8;
*buf++ = w & 0XFF;
}
#endif // SPI_USE_8BIT_FRAME
return 0;
}
//------------------------------------------------------------------------------
/** SPI send a byte */
void SdSpi::send(uint8_t b) {
SPI0_MCR |= SPI_MCR_CLR_RXF;
SPI0_SR = SPI_SR_TCF;
SPI0_PUSHR = b;
while (!(SPI0_SR & SPI_SR_TCF)) {}
}
//------------------------------------------------------------------------------
/** SPI send multiple bytes */
void SdSpi::send(const uint8_t* buf , size_t n) {
// clear any data in RX FIFO
SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F);
#if SPI_USE_8BIT_FRAME
// initial number of bytes to push into TX FIFO
int nf = n < SPI_INITIAL_FIFO_DEPTH ? n : SPI_INITIAL_FIFO_DEPTH;
// limit for pushing data into TX fifo
const uint8_t* limit = buf + n;
for (int i = 0; i < nf; i++) {
SPI0_PUSHR = *buf++;
}
// write data to TX FIFO
while (buf < limit) {
while (!(SPI0_SR & SPI_SR_RXCTR)) {}
SPI0_PUSHR = *buf++;
SPI0_POPR;
}
// wait for data to be sent
while (nf) {
while (!(SPI0_SR & SPI_SR_RXCTR)) {}
SPI0_POPR;
nf--;
}
#else // SPI_USE_8BIT_FRAME
// use 16 bit frame to avoid TD delay between frames
// send one byte if n is odd
if (n & 1) {
send(*buf++);
n--;
}
// initial number of words to push into TX FIFO
int nf = n/2 < SPI_INITIAL_FIFO_DEPTH ? n/2 : SPI_INITIAL_FIFO_DEPTH;
// limit for pushing data into TX fifo
const uint8_t* limit = buf + n;
for (int i = 0; i < nf; i++) {
uint16_t w = (*buf++) << 8;
w |= *buf++;
SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | w;
}
// write data to TX FIFO
while (buf < limit) {
uint16_t w = *buf++ << 8;
w |= *buf++;
while (!(SPI0_SR & SPI_SR_RXCTR)) {}
SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | w;
SPI0_POPR;
}
// wait for data to be sent
while (nf) {
while (!(SPI0_SR & SPI_SR_RXCTR)) {}
SPI0_POPR;
nf--;
}
#endif // SPI_USE_8BIT_FRAME
}
#else // KINETISK
//==============================================================================
// Use standard SPI library if not KINETISK
#include "SPI.h"
/**
* Initialize SPI pins.
*/
void SdSpi::begin() {
SPI.begin();
}
/** Set SPI options for access to SD/SDHC cards.
*
* \param[in] divisor SCK clock divider relative to the system clock.
*/
void SdSpi::init(uint8_t divisor) {
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0);
#ifndef SPI_CLOCK_DIV128
SPI.setClockDivider(divisor);
#else // SPI_CLOCK_DIV128
int v;
if (divisor <= 2) {
v = SPI_CLOCK_DIV2;
} else if (divisor <= 4) {
v = SPI_CLOCK_DIV4;
} else if (divisor <= 8) {
v = SPI_CLOCK_DIV8;
} else if (divisor <= 16) {
v = SPI_CLOCK_DIV16;
} else if (divisor <= 32) {
v = SPI_CLOCK_DIV32;
} else if (divisor <= 64) {
v = SPI_CLOCK_DIV64;
} else {
v = SPI_CLOCK_DIV128;
}
SPI.setClockDivider(v);
#endif // SPI_CLOCK_DIV128
}
/** Receive a byte.
*
* \return The byte.
*/
uint8_t SdSpi::receive() {
return SPI.transfer(0XFF);
}
/** Receive multiple bytes.
*
* \param[out] buf Buffer to receive the data.
* \param[in] n Number of bytes to receive.
*
* \return Zero for no error or nonzero error code.
*/
uint8_t SdSpi::receive(uint8_t* buf, size_t n) {
for (size_t i = 0; i < n; i++) {
buf[i] = SPI.transfer(0XFF);
}
return 0;
}
/** Send a byte.
*
* \param[in] b Byte to send
*/
void SdSpi::send(uint8_t b) {
SPI.transfer(b);
}
/** Send multiple bytes.
*
* \param[in] buf Buffer for data to be sent.
* \param[in] n Number of bytes to send.
*/
void SdSpi::send(const uint8_t* buf , size_t n) {
for (size_t i = 0; i < n; i++) {
SPI.transfer(buf[i]);
}
}
#endif // KINETISK
#endif // defined(__arm__) && defined(CORE_TEENSY)
ADXL362.CPP
Code:
#include <Arduino.h>
#include <ADXL362.h>
#include <SPI.h>
//#define ADXL362_DEBUG
int16_t slaveSelectPin = 15;
ADXL362::ADXL362() {
}
//
// begin()
// Initial SPI setup, soft reset of device
//
void ADXL362::begin(int16_t chipSelectPin) {
slaveSelectPin = chipSelectPin;
pinMode(slaveSelectPin, OUTPUT);
SPI.begin();
SPI.setDataMode(SPI_MODE0); //CPHA = CPOL = 0 MODE = 0
// First reassign pin 13 to Alt1 so that it is not SCK but the LED still works and then reassign pin 14 to SCK
CORE_PIN13_CONFIG = PORT_PCR_MUX(1);
CORE_PIN14_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
delay(1000);
// soft reset
SPIwriteOneRegister(0x1F, 0x52); // Write to SOFT RESET, "R"
delay(10);
#ifdef ADXL362_DEBUG
Serial.println("Soft Reset\n");
#endif
}
//
// beginMeasure()
// turn on Measurement mode - required after reset
//
void ADXL362::beginMeasure() {
byte temp = SPIreadOneRegister(0x2D); // read Reg 2D before modifying for measure mode
#ifdef ADXL362_DEBUG
Serial.print( "Setting Measurement Mode - Reg 2D before = ");
Serial.print(temp);
#endif
// turn on measurement mode
byte tempwrite = temp | 0x02; // turn on measurement bit in Reg 2D
SPIwriteOneRegister(0x2D, tempwrite); // Write to POWER_CTL_REG, Measurement Mode
delay(10);
#ifdef ADXL362_DEBUG
temp = SPIreadOneRegister(0x2D);
Serial.print( ", Reg 2D after = ");
Serial.println(temp);
Serial.println();
#endif
}
//
// setRange(int sense)
// set the G sensitivity, either ADXL362_2G, ADXL362_4G or ADXL362_8G
// returns true if the register set was successful
//
void ADXL362::setRange(int sense)
{
byte reg = SPIreadOneRegister(FILTER_CTL);
reg &= 0x3F; // mask out the sense bits;
reg |= (sense << RANGE);
SPIwriteOneRegister(FILTER_CTL, reg);
}
//
// setMeasureRate(int rate);
// sets the sample rate of the adxl362, pg. 33 of datasheet.
void ADXL362::setSELFTEST(int Val)
{
// byte reg = SPIreadOneRegister(SELF_TEST);
// reg &= 0xFC;
// reg |= (rate << ODR);
SPIwriteOneRegister(SELF_TEST, Val);
}
//
// setSelfTest(int rate);
// sets the sample rate of the adxl362, pg. 33 of datasheet.
void ADXL362::setODR(int rate)
{
byte reg = SPIreadOneRegister(FILTER_CTL);
reg &= 0xFC;
reg |= (rate << ODR);
SPIwriteOneRegister(FILTER_CTL, reg);
}
//
// setNoise(int rate);
// sets the sample rate of the adxl362, pg. 33 of datasheet.
void ADXL362::setNoise(int noise)
{
byte reg = SPIreadOneRegister(POWER_CTL);
reg &= 0xCF; // mask out the sense bits;
reg |= (noise << LOW_NOISE);
SPIwriteOneRegister(POWER_CTL, reg);
}
//
// readXData(), readYData(), readZData(), readTemp()
// Read X, Y, Z, and Temp registers
//
int16_t ADXL362::readXData(){
int16_t XDATA = SPIreadTwoRegisters(0x0E);
#ifdef ADXL362_DEBUG
Serial.print("XDATA = ");
Serial.println(XDATA);
#endif
return XDATA;
}
int16_t ADXL362::readYData(){
int16_t YDATA = SPIreadTwoRegisters(0x10);
#ifdef ADXL362_DEBUG
Serial.print("\tYDATA = ");
Serial.println(YDATA);
#endif
return YDATA;
}
int16_t ADXL362::readZData(){
int16_t ZDATA = SPIreadTwoRegisters(0x12);
#ifdef ADXL362_DEBUG
Serial.print("\tZDATA = ");
Serial.println(ZDATA);
#endif
return ZDATA;
}
int16_t ADXL362::readTemp(){
int16_t TEMP = SPIreadTwoRegisters(0x14);
#ifdef ADXL362_DEBUG
Serial.print("\tTEMP = ");
Serial.println(TEMP);
#endif
return TEMP;
}
void ADXL362::readXYZTData(int16_t &XData, int16_t &YData, int16_t &ZData, int16_t &Temperature){
// burst SPI read
// A burst read of all three axis is required to guarantee all measurements correspond to same sample time
digitalWrite(slaveSelectPin, LOW);
SPI.transfer(0x0B); // read instruction
SPI.transfer(0x0E); // Start at XData Reg
XData = SPI.transfer(0x00);
XData = XData + (SPI.transfer(0x00) << 8);
YData = SPI.transfer(0x00);
YData = YData + (SPI.transfer(0x00) << 8);
ZData = SPI.transfer(0x00);
ZData = ZData + (SPI.transfer(0x00) << 8);
Temperature = SPI.transfer(0x00);
Temperature = Temperature + (SPI.transfer(0x00) << 8);
digitalWrite(slaveSelectPin, HIGH);
#ifdef ADXL362_DEBUG
Serial.print("XDATA = "); Serial.print(XData);
Serial.print("\tYDATA = "); Serial.print(YData);
Serial.print("\tZDATA = "); Serial.print(ZData);
Serial.print("\tTemperature = "); Serial.println(Temperature);
#endif
}
void ADXL362::setupDCActivityInterrupt(int16_t threshold, byte time){
// Setup motion and time thresholds
SPIwriteTwoRegisters(0x20, threshold);
SPIwriteOneRegister(0x22, time);
// turn on activity interrupt
byte ACT_INACT_CTL_Reg = SPIreadOneRegister(0x27); // Read current reg value
ACT_INACT_CTL_Reg = ACT_INACT_CTL_Reg | (0x01); // turn on bit 1, ACT_EN
SPIwriteOneRegister(0x27, ACT_INACT_CTL_Reg); // Write new reg value
ACT_INACT_CTL_Reg = SPIreadOneRegister(0x27); // Verify properly written
#ifdef ADXL362_DEBUG
Serial.print("DC Activity Threshold set to "); Serial.print(SPIreadTwoRegisters(0x20));
Serial.print(", Time threshold set to "); Serial.print(SPIreadOneRegister(0x22));
Serial.print(", ACT_INACT_CTL Register is "); Serial.println(ACT_INACT_CTL_Reg, HEX);
#endif
}
void ADXL362::setupACActivityInterrupt(int16_t threshold, byte time){
// Setup motion and time thresholds
SPIwriteTwoRegisters(0x20, threshold);
SPIwriteOneRegister(0x22, time);
// turn on activity interrupt
byte ACT_INACT_CTL_Reg = SPIreadOneRegister(0x27); // Read current reg value
ACT_INACT_CTL_Reg = ACT_INACT_CTL_Reg | (0x03); // turn on bit 2 and 1, ACT_AC_DCB, ACT_EN
SPIwriteOneRegister(0x27, ACT_INACT_CTL_Reg); // Write new reg value
ACT_INACT_CTL_Reg = SPIreadOneRegister(0x27); // Verify properly written
#ifdef ADXL362_DEBUG
Serial.print("AC Activity Threshold set to "); Serial.print(SPIreadTwoRegisters(0x20));
Serial.print(", Time Activity set to "); Serial.print(SPIreadOneRegister(0x22));
Serial.print(", ACT_INACT_CTL Register is "); Serial.println(ACT_INACT_CTL_Reg, HEX);
#endif
}
void ADXL362::setupDCInactivityInterrupt(int16_t threshold, int16_t time){
// Setup motion and time thresholds
SPIwriteTwoRegisters(0x23, threshold);
SPIwriteTwoRegisters(0x25, time);
// turn on inactivity interrupt
byte ACT_INACT_CTL_Reg = SPIreadOneRegister(0x27); // Read current reg value
ACT_INACT_CTL_Reg = ACT_INACT_CTL_Reg | (0x04); // turn on bit 3, INACT_EN
SPIwriteOneRegister(0x27, ACT_INACT_CTL_Reg); // Write new reg value
ACT_INACT_CTL_Reg = SPIreadOneRegister(0x27); // Verify properly written
#ifdef ADXL362_DEBUG
Serial.print("DC Inactivity Threshold set to "); Serial.print(SPIreadTwoRegisters(0x23));
Serial.print(", Time Inactivity set to "); Serial.print(SPIreadTwoRegisters(0x25));
Serial.print(", ACT_INACT_CTL Register is "); Serial.println(ACT_INACT_CTL_Reg, HEX);
#endif
}
void ADXL362::setupACInactivityInterrupt(int16_t threshold, int16_t time){
// Setup motion and time thresholds
SPIwriteTwoRegisters(0x23, threshold);
SPIwriteTwoRegisters(0x25, time);
// turn on inactivity interrupt
byte ACT_INACT_CTL_Reg = SPIreadOneRegister(0x27); // Read current reg value
ACT_INACT_CTL_Reg = ACT_INACT_CTL_Reg | (0x0C); // turn on bit 3 and 4, INACT_AC_DCB, INACT_EN
SPIwriteOneRegister(0x27, ACT_INACT_CTL_Reg); // Write new reg value
ACT_INACT_CTL_Reg = SPIreadOneRegister(0x27); // Verify properly written
#ifdef ADXL362_DEBUG
Serial.print("AC Inactivity Threshold set to "); Serial.print(SPIreadTwoRegisters(0x23));
Serial.print(", Time Inactivity set to "); Serial.print(SPIreadTwoRegisters(0x25));
Serial.print(", ACT_INACT_CTL Register is "); Serial.println(ACT_INACT_CTL_Reg, HEX);
#endif
}
void ADXL362::checkAllControlRegs(){
//byte filterCntlReg = SPIreadOneRegister(0x2C);
//byte ODR = filterCntlReg & 0x07; Serial.print("ODR = "); Serial.println(ODR, HEX);
//byte ACT_INACT_CTL_Reg = SPIreadOneRegister(0x27); Serial.print("ACT_INACT_CTL_Reg = "); Serial.println(ACT_INACT_CTL_Reg, HEX);
digitalWrite(slaveSelectPin, LOW);
SPI.transfer(0x0B); // read instruction
SPI.transfer(0x20); // Start burst read at Reg 20
#ifdef ADXL362_DEBUG
Serial.println("Start Burst Read of all Control Regs - Library version 6-5-2014:");
Serial.print("Reg 20 = "); Serial.println(SPI.transfer(0x00), HEX);
Serial.print("Reg 21 = "); Serial.println(SPI.transfer(0x00), HEX);
Serial.print("Reg 22 = "); Serial.println(SPI.transfer(0x00), HEX);
Serial.print("Reg 23 = "); Serial.println(SPI.transfer(0x00), HEX);
Serial.print("Reg 24 = "); Serial.println(SPI.transfer(0x00), HEX);
Serial.print("Reg 25 = "); Serial.println(SPI.transfer(0x00), HEX);
Serial.print("Reg 26 = "); Serial.println(SPI.transfer(0x00), HEX);
Serial.print("Reg 27 = "); Serial.println(SPI.transfer(0x00), HEX);
Serial.print("Reg 28 = "); Serial.println(SPI.transfer(0x00), HEX);
Serial.print("Reg 29 = "); Serial.println(SPI.transfer(0x00), HEX);
Serial.print("Reg 2A = "); Serial.println(SPI.transfer(0x00), HEX);
Serial.print("Reg 2B = "); Serial.println(SPI.transfer(0x00), HEX);
Serial.print("Reg 2C = "); Serial.println(SPI.transfer(0x00), HEX);
Serial.print("Reg 2D = "); Serial.println(SPI.transfer(0x00), HEX);
Serial.print("Reg 2E = "); Serial.println(SPI.transfer(0x00), HEX);
#endif
digitalWrite(slaveSelectPin, HIGH);
}
// Basic SPI routines to simplify code
// read and write one register
byte ADXL362::SPIreadOneRegister(byte regAddress){
byte regValue = 0;
digitalWrite(slaveSelectPin, LOW);
SPI.transfer(0x0B); // read instruction
SPI.transfer(regAddress);
regValue = SPI.transfer(0x00);
digitalWrite(slaveSelectPin, HIGH);
return regValue;
}
void ADXL362::SPIwriteOneRegister(byte regAddress, byte regValue){
digitalWrite(slaveSelectPin, LOW);
SPI.transfer(0x0A); // write instruction
SPI.transfer(regAddress);
SPI.transfer(regValue);
digitalWrite(slaveSelectPin, HIGH);
}
int16_t ADXL362::SPIreadTwoRegisters(byte regAddress){
int16_t twoRegValue = 0;
digitalWrite(slaveSelectPin, LOW);
SPI.transfer(0x0B); // read instruction
SPI.transfer(regAddress);
twoRegValue = SPI.transfer(0x00);
twoRegValue = twoRegValue + (SPI.transfer(0x00) << 8);
digitalWrite(slaveSelectPin, HIGH);
return twoRegValue;
}
void ADXL362::SPIwriteTwoRegisters(byte regAddress, int16_t twoRegValue){
byte twoRegValueH = twoRegValue >> 8;
byte twoRegValueL = twoRegValue;
digitalWrite(slaveSelectPin, LOW);
SPI.transfer(0x0A); // write instruction
SPI.transfer(regAddress);
SPI.transfer(twoRegValueL);
SPI.transfer(twoRegValueH);
digitalWrite(slaveSelectPin, HIGH);
}
Code:
#include "Arduino.h"
#ifndef ADXL362_h
#define ADXL362_h
#define FILTER_CTL 0x2C
#define POWER_CTL 0x2D
#define SELF_TEST 0x2E
#define RANGE 6
#define ODR 0
#define LOW_NOISE 4
#define ADXL362_2G 0
#define ADXL362_4G 1
#define ADXL362_8G 2
#define ADXL362_NOISE 0
#define ADXL362_LOWNOISE 1
#define ADXL362_ULTRALOWNOISE 2
#define ADXL362_RATE_12_5 0 // 12.5 Hz
#define ADXL362_RATE_25 1 // 25 Hz
#define ADXL362_RATE_50 2 // 50 Hz
#define ADXL362_RATE_100 3 // 100 Hz (reset default)
#define ADXL362_RATE_200 4 // 200 Hz
#define ADXL362_RATE_400 7 // 400 Hz
class ADXL362
{
public:
ADXL362();
//
// Basic Device control and readback functions
//
void begin(int16_t chipSelectPin = 10);
void beginMeasure();
int16_t readXData();
int16_t readYData();
int16_t readZData();
void readXYZTData(int16_t &XData, int16_t &YData, int16_t &ZData, int16_t &Temperature);
int16_t readTemp();
void setRange(int sense);
void setODR(int rate);
void setNoise(int rate);
void setSELFTEST(int val);
//
// Activity/Inactivity interrupt functions
//
void setupDCActivityInterrupt(int16_t threshold, byte time);
void setupDCInactivityInterrupt(int16_t threshold, int16_t time);
void setupACActivityInterrupt(int16_t threshold, byte time);
void setupACInactivityInterrupt(int16_t threshold, int16_t time);
// TODO:
// void mapINT1
// void mapINT2
// void autoSleep
// void activityInterruptControl
// - Activity, Inactivity, Both
// - Referenced, Absolute
// - Free Fall, Linked Mode, Loop Mode
void checkAllControlRegs();
//remove:
byte SPIreadOneRegister(byte regAddress);
private:
// Low-level SPI control, to simplify overall coding
//byte SPIreadOneRegister(byte regAddress);
void SPIwriteOneRegister(byte regAddress, byte regValue);
int16_t SPIreadTwoRegisters(byte regAddress);
void SPIwriteTwoRegisters(byte regAddress, int16_t twoRegValue);
};
#endif