arduino / Arduino

Arduino IDE 1.x
https://www.arduino.cc/en/software
Other
14.11k stars 7k forks source link

No support for I2C restart condition at DUE; parameter sendStop in endTransmission not considered #2428

Closed AmLimit closed 9 years ago

AmLimit commented 9 years ago

With a DUE I try to connect a MMA8452 but get wrong answers. On scope I can see that the parameter "sendStop" in "uint8_t TwoWire::endTransmission(uint8_t sendStop)" has no effect - instead of a restart condition a stop condition followed by a new start condition is perfomed. A look into source code offers that the parameter "sendStop" is not used. The header comments are ready but the code doesn't reflect the description. As I understand the MMA845x manual a restart condition is necessary to handle queries, so the MMA8452 is currently not controllable by the wire library. May be there are additional issues in twi.c to handle that issue.

sngl commented 9 years ago

The sendStop variable is a global variable and its value is used inside the ISR that manages the communication.

https://github.com/arduino/Arduino/blob/ide-1.5.x/hardware/arduino/avr/libraries/Wire/utility/twi.c#L383

AmLimit commented 9 years ago

I think the static variable "twi_sendStop" https://github.com/arduino/Arduino/blob/ide-1.5.x/hardware/arduino/avr/libraries/Wire/utility/twi.c#L43

and the mentioned function parameter "sendStop" https://github.com/arduino/Arduino/blob/ide-1.5.x/hardware/arduino/avr/libraries/Wire/Wire.cpp#L142

is not the same.

mal333 commented 9 years ago

Can say same about requestFrom. As a parameter uint8_t sendStop exist, but not using in function.

AmLimit commented 9 years ago

If the item will not be fixed in nearer future: is there any work around to support a MMA845x accelerometer on a DUE?

AmLimit commented 9 years ago

Some good news: I found a way to use my MMA8452 on a DUE :-)

To perform the needed repeated start condition to handle the MMA8452 you have to use the internal address functionality of the SAM controller (Chapter TWI / Master Mode / Internal Address). Due to the MMA8452 uses 7-bit addressing this feature offers up to 3 automatically sent request bytes - you don't have to handle it manually (quite a good feature). If I didn't miss any information within the documentation the MMA8452 is always controlled by 1 byte register requests. To use this feature with my DUE I added another overloaded method of TwoWire::requestFrom to support the needed parameter iaddress (and optionally isize) to TWI_StartRead:

\arduino-1.5.8\hardware\arduino\sam\libraries\Wire\Wire.cpp

uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize) {
  if (quantity > BUFFER_LENGTH)
  quantity = BUFFER_LENGTH;

  // perform blocking read into buffer
  int readed = 0;
  TWI_StartRead(twi, address, iaddress, isize);
  do {
    // Stop condition must be set during the reception of last byte
    if (readed + 1 == quantity)
    TWI_SendSTOPCondition( twi);

    TWI_WaitByteReceived(twi, RECV_TIMEOUT);
    rxBuffer[readed++] = TWI_ReadByte(twi);
  } while (readed < quantity);
  TWI_WaitTransferComplete(twi, RECV_TIMEOUT);

  // set rx buffer iterator vars
  rxBufferIndex = 0;
  rxBufferLength = readed;

  return readed;
}

\arduino-1.5.8\hardware\arduino\sam\libraries\Wire\Wire.h

public:
  uint8_t requestFrom(uint8_t, uint8_t, uint32_t, uint8_t);

To use this new method call the used MMA8452Q.cpp needs some changes in registerRead and registersRead. To keep the opportunity to apply e.g. a NANO I added compiler directives to choose the right code depending on applied board. You can see that performing a request on a DUE is much more simple:

\MMA8452Q.cpp

static inline uint8_t registerRead(uint8_t addr) {
#ifdef _VARIANT_ARDUINO_DUE_X_
    Wire.requestFrom((uint8_t) MMA8452Q_ADDRESS, (uint8_t) 1, (uint32_t) addr, (uint8_t) 1);
#else
    Wire.beginTransmission(MMA8452Q_ADDRESS);
    Wire.write(addr);
    Wire.endTransmission(false);

    Wire.requestFrom(MMA8452Q_ADDRESS, 1);
#endif

    while (!Wire.available());
    return Wire.read();
}

static inline void registersRead(uint8_t addr, uint8_t data[], size_t count) {
#ifdef _VARIANT_ARDUINO_DUE_X_
    Wire.requestFrom((uint8_t) MMA8452Q_ADDRESS, (uint8_t) count, (uint32_t) addr, (uint8_t) 1);
#else
    Wire.beginTransmission(MMA8452Q_ADDRESS);
    Wire.write(addr);
    Wire.endTransmission(false);

    Wire.requestFrom(MMA8452Q_ADDRESS, count);
#endif

    while (Wire.available() < count);

    for (size_t i = 0; i < count; i++)
        data[i] = Wire.read();
}

I checked this out at DUE and NANO and both works fine.

I hope this will be helpful for some other users being held up by this problem. This is the first time for me to give support to others and it makes me happy done this small milestone to be having achieved in my life :-)

May be it's not the most structured solution, but for me it's currently ok. I would appreciate if one specialist on the arduino software would give me feedback to this. Some questions from me to this are:

Your feedback is welcome.

Reslof commented 9 years ago

I'm working with the same hardware but I cannot seem to get the MMA8452 working. The read for the WHO_AM_I register is always 0. I am also working with the Wire1 interface as opposed to Wire but I've made the necessary adjustments and still nothing. Could you please post the files you modified above so I can see where I went wrong?

AmLimit commented 9 years ago

In my last post all additions to the existing files are listed. But maybe I changed the MMA address - in my application it is set to

define MMA8452Q_ADDRESS 0x1D

Do you control the SA0 pin? In my case it has to be set to HIGH to 'select' the MMA. If SA0 is LOW the MMA would not answer. Changing the address to 0x1C inverts the behaviour. Please post a feedback if the hint solves your problem. I would have posted the files within this comment, but pasting source files is not supported. I think I would have to check it into the repository but don't know how to do this correctly right now.

mal333 commented 9 years ago

AmLimit, thank a lot, it's work! Tested on MLX90614 &DUE, everything fine.

Reslof commented 9 years ago

After making the necessary adjustments and fixing the data types for my setup, I can confirm that the fix AmLimit provided for the Wire library works. I tested it with my Due and MMA8452Q breakout with the Wire1 bus. I deleted the non Due directives though. Thanks!

davidrojas commented 9 years ago

This is issue is still open in Arduino 1.6.3 But your fix works like a charm, AmLimit, thank you so much, I've been banging my head against this for almost a week (different accelerometer, but same problem with the Wire library). Just one thing, in the Arduno 1.6.x now the libraries for the due are in C:\Users\YourUser\AppData\Roaming\Arduino15\packages\arduino\hardware\sam\1.6.3\libraries\

SeanTaba commented 9 years ago

Good job AmLimit, thank you very much.

facchinm commented 9 years ago

Fixed with 39c3f8b

Kaveh14934 commented 8 years ago

Can you you tell me where I can find the complete code for MMA8452Q you have mentioned AmLimit, Thanks

AmLimit commented 8 years ago

Hi Kaveh14934,

Currently I don't remember, where I found that, so to have it complete:

Attached are the 3 changed files (and 1 unchanged to have it complete) I used under Arduino 1.5.8. I am currently not working on this project, so I did not change to a newer arduino version till now.

directory for Wire.h and Wire.cpp: .........\arduino-1.5.8\hardware\arduino\sam\libraries\Wire

directory for MMA8452Q.h and MMA8452Q.cpp: project directory

Hope this helps.

Regards

Am 23.03.2016 um 20:32 schrieb Kaveh14934:

Can you you tell me where I can find the complete code for MMA8452Q you have mentioned AmLimit, Thanks

— You are receiving this because you authored the thread. Reply to this email directly or view it on GitHub https://github.com/arduino/Arduino/issues/2428#issuecomment-200508677

/*

extern "C" {

include

}

include "Wire.h"

static inline bool TWI_FailedAcknowledge(Twi *pTwi) { return pTwi->TWI_SR & TWI_SR_NACK; }

static inline bool TWI_WaitTransferComplete(Twi *_twi, uint32_t _timeout) { uint32_t _status_reg = 0; while ((_status_reg & TWI_SR_TXCOMP) != TWI_SR_TXCOMP) { _status_reg = TWI_GetStatus(_twi);

    if (_status_reg & TWI_SR_NACK)
        return false;

    if (--_timeout == 0)
        return false;
}
return true;

}

static inline bool TWI_WaitByteSent(Twi *_twi, uint32_t _timeout) { uint32_t _status_reg = 0; while ((_status_reg & TWI_SR_TXRDY) != TWI_SR_TXRDY) { _status_reg = TWI_GetStatus(_twi);

    if (_status_reg & TWI_SR_NACK)
        return false;

    if (--_timeout == 0)
        return false;
}

return true;

}

static inline bool TWI_WaitByteReceived(Twi *_twi, uint32_t _timeout) { uint32_t _status_reg = 0; while ((_status_reg & TWI_SR_RXRDY) != TWI_SR_RXRDY) { _status_reg = TWI_GetStatus(_twi);

    if (_status_reg & TWI_SR_NACK)
        return false;

    if (--_timeout == 0)
        return false;
}

return true;

}

static inline bool TWI_STATUS_SVREAD(uint32_t status) { return (status & TWI_SR_SVREAD) == TWI_SR_SVREAD; }

static inline bool TWI_STATUS_SVACC(uint32_t status) { return (status & TWI_SR_SVACC) == TWI_SR_SVACC; }

static inline bool TWI_STATUS_GACC(uint32_t status) { return (status & TWI_SR_GACC) == TWI_SR_GACC; }

static inline bool TWI_STATUS_EOSACC(uint32_t status) { return (status & TWI_SR_EOSACC) == TWI_SR_EOSACC; }

static inline bool TWI_STATUS_NACK(uint32_t status) { return (status & TWI_SR_NACK) == TWI_SR_NACK; }

TwoWire::TwoWire(Twi twi, void(beginCb)(void)) : twi(_twi), rxBufferIndex(0), rxBufferLength(0), txAddress(0), txBufferLength(0), srvBufferIndex(0), srvBufferLength(0), status( UNINITIALIZED), onBeginCallback(_beginCb), twiClock(TWI_CLOCK) { }

void TwoWire::begin(void) { if (onBeginCallback) onBeginCallback();

// Disable PDC channel
twi->TWI_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;

TWI_ConfigureMaster(twi, twiClock, VARIANT_MCK);
status = MASTER_IDLE;

}

void TwoWire::begin(uint8_t address) { if (onBeginCallback) onBeginCallback();

// Disable PDC channel
twi->TWI_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;

TWI_ConfigureSlave(twi, address);
status = SLAVE_IDLE;
TWI_EnableIt(twi, TWI_IER_SVACC);
//| TWI_IER_RXRDY | TWI_IER_TXRDY   | TWI_IER_TXCOMP);

}

void TwoWire::begin(int address) { begin((uint8_t) address); }

void TwoWire::setClock(uint32_t frequency) { twiClock = frequency; TWI_SetClock(twi, twiClock, VARIANT_MCK); }

uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize) { if (quantity > BUFFER_LENGTH) quantity = BUFFER_LENGTH;

// perform blocking read into buffer int readed = 0; TWI_StartRead(twi, address, iaddress, isize); do { // Stop condition must be set during the reception of last byte if (readed + 1 == quantity) TWI_SendSTOPCondition( twi);

TWI_WaitByteReceived(twi, RECV_TIMEOUT);
rxBuffer[readed++] = TWI_ReadByte(twi);

} while (readed < quantity); TWI_WaitTransferComplete(twi, RECV_TIMEOUT);

// set rx buffer iterator vars rxBufferIndex = 0; rxBufferLength = readed;

return readed; }

uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) { if (quantity > BUFFER_LENGTH) quantity = BUFFER_LENGTH;

// perform blocking read into buffer
int readed = 0;
TWI_StartRead(twi, address, 0, 0);
do {
    // Stop condition must be set during the reception of last byte
    if (readed + 1 == quantity)
        TWI_SendSTOPCondition( twi);

    TWI_WaitByteReceived(twi, RECV_TIMEOUT);
    rxBuffer[readed++] = TWI_ReadByte(twi);
} while (readed < quantity);
TWI_WaitTransferComplete(twi, RECV_TIMEOUT);

// set rx buffer iterator vars
rxBufferIndex = 0;
rxBufferLength = readed;

return readed;

}

uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity) { return requestFrom((uint8_t) address, (uint8_t) quantity, (uint8_t) true); }

uint8_t TwoWire::requestFrom(int address, int quantity) { return requestFrom((uint8_t) address, (uint8_t) quantity, (uint8_t) true); }

uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop) { return requestFrom((uint8_t) address, (uint8_t) quantity, (uint8_t) sendStop); }

void TwoWire::beginTransmission(uint8_t address) { status = MASTER_SEND;

// save address of target and empty buffer
txAddress = address;
txBufferLength = 0;

}

void TwoWire::beginTransmission(int address) { beginTransmission((uint8_t) address); }

// // Originally, 'endTransmission' was an f(void) function. // It has been modified to take one parameter indicating // whether or not a STOP should be performed on the bus. // Calling endTransmission(false) allows a sketch to // perform a repeated start. // // WARNING: Nothing in the library keeps track of whether // the bus tenure has been properly ended with a STOP. It // is very possible to leave the bus in a hung state if // no call to endTransmission(true) is made. Some I2C // devices will behave oddly if they do not see a STOP. // uint8_t TwoWire::endTransmission(uint8_t sendStop) { uint8_t error = 0; // transmit buffer (blocking) TWI_StartWrite(twi, txAddress, 0, 0, txBuffer[0]); if (!TWI_WaitByteSent(twi, XMIT_TIMEOUT)) error = 2; // error, got NACK on address transmit

if (error == 0) {
    uint16_t sent = 1;
    while (sent < txBufferLength) {
        TWI_WriteByte(twi, txBuffer[sent++]);
        if (!TWI_WaitByteSent(twi, XMIT_TIMEOUT))
            error = 3;  // error, got NACK during data transmmit
    }
}

if (error == 0) {
    TWI_Stop(twi);
    if (!TWI_WaitTransferComplete(twi, XMIT_TIMEOUT))
        error = 4;  // error, finishing up
}

txBufferLength = 0;     // empty buffer
status = MASTER_IDLE;
return error;

}

// This provides backwards compatibility with the original // definition, and expected behaviour, of endTransmission // uint8_t TwoWire::endTransmission(void) { return endTransmission(true); }

size_t TwoWire::write(uint8_t data) { if (status == MASTER_SEND) { if (txBufferLength >= BUFFER_LENGTH) return 0; txBuffer[txBufferLength++] = data; return 1; } else { if (srvBufferLength >= BUFFER_LENGTH) return 0; srvBuffer[srvBufferLength++] = data; return 1; } }

size_t TwoWire::write(const uint8_t *data, size_t quantity) { if (status == MASTER_SEND) { for (size_t i = 0; i < quantity; ++i) { if (txBufferLength >= BUFFER_LENGTH) return i; txBuffer[txBufferLength++] = data[i]; } } else { for (size_t i = 0; i < quantity; ++i) { if (srvBufferLength >= BUFFER_LENGTH) return i; srvBuffer[srvBufferLength++] = data[i]; } } return quantity; }

int TwoWire::available(void) { return rxBufferLength - rxBufferIndex; }

int TwoWire::read(void) { if (rxBufferIndex < rxBufferLength) return rxBuffer[rxBufferIndex++]; return -1; }

int TwoWire::peek(void) { if (rxBufferIndex < rxBufferLength) return rxBuffer[rxBufferIndex]; return -1; }

void TwoWire::flush(void) { // Do nothing, use endTransmission(..) to force // data transfer. }

void TwoWire::onReceive(void(*function)(int)) { onReceiveCallback = function; }

void TwoWire::onRequest(void(*function)(void)) { onRequestCallback = function; }

void TwoWire::onService(void) { // Retrieve interrupt status uint32_t sr = TWI_GetStatus(twi);

if (status == SLAVE_IDLE && TWI_STATUS_SVACC(sr)) {
    TWI_DisableIt(twi, TWI_IDR_SVACC);
    TWI_EnableIt(twi, TWI_IER_RXRDY | TWI_IER_GACC | TWI_IER_NACK
            | TWI_IER_EOSACC | TWI_IER_SCL_WS | TWI_IER_TXCOMP);

    srvBufferLength = 0;
    srvBufferIndex = 0;

    // Detect if we should go into RECV or SEND status
    // SVREAD==1 means *master* reading -> SLAVE_SEND
    if (!TWI_STATUS_SVREAD(sr)) {
        status = SLAVE_RECV;
    } else {
        status = SLAVE_SEND;

        // Alert calling program to generate a response ASAP
        if (onRequestCallback)
            onRequestCallback();
        else
            // create a default 1-byte response
            write((uint8_t) 0);
    }
}

if (status != SLAVE_IDLE) {
    if (TWI_STATUS_TXCOMP(sr) && TWI_STATUS_EOSACC(sr)) {
        if (status == SLAVE_RECV && onReceiveCallback) {
            // Copy data into rxBuffer
            // (allows to receive another packet while the
            // user program reads actual data)
            for (uint8_t i = 0; i < srvBufferLength; ++i)
                rxBuffer[i] = srvBuffer[i];
            rxBufferIndex = 0;
            rxBufferLength = srvBufferLength;

            // Alert calling program
            onReceiveCallback( rxBufferLength);
        }

        // Transfer completed
        TWI_EnableIt(twi, TWI_SR_SVACC);
        TWI_DisableIt(twi, TWI_IDR_RXRDY | TWI_IDR_GACC | TWI_IDR_NACK
                | TWI_IDR_EOSACC | TWI_IDR_SCL_WS | TWI_IER_TXCOMP);
        status = SLAVE_IDLE;
    }
}

if (status == SLAVE_RECV) {
    if (TWI_STATUS_RXRDY(sr)) {
        if (srvBufferLength < BUFFER_LENGTH)
            srvBuffer[srvBufferLength++] = TWI_ReadByte(twi);
    }
}

if (status == SLAVE_SEND) {
    if (TWI_STATUS_TXRDY(sr) && !TWI_STATUS_NACK(sr)) {
        uint8_t c = 'x';
        if (srvBufferIndex < srvBufferLength)
            c = srvBuffer[srvBufferIndex++];
        TWI_WriteByte(twi, c);
    }
}

}

if WIRE_INTERFACES_COUNT > 0

static void Wire_Init(void) { pmc_enable_periph_clk(WIRE_INTERFACE_ID); PIO_Configure( g_APinDescription[PIN_WIRE_SDA].pPort, g_APinDescription[PIN_WIRE_SDA].ulPinType, g_APinDescription[PIN_WIRE_SDA].ulPin, g_APinDescription[PIN_WIRE_SDA].ulPinConfiguration); PIO_Configure( g_APinDescription[PIN_WIRE_SCL].pPort, g_APinDescription[PIN_WIRE_SCL].ulPinType, g_APinDescription[PIN_WIRE_SCL].ulPin, g_APinDescription[PIN_WIRE_SCL].ulPinConfiguration);

NVIC_DisableIRQ(WIRE_ISR_ID);
NVIC_ClearPendingIRQ(WIRE_ISR_ID);
NVIC_SetPriority(WIRE_ISR_ID, 0);
NVIC_EnableIRQ(WIRE_ISR_ID);

}

TwoWire Wire = TwoWire(WIRE_INTERFACE, Wire_Init);

void WIRE_ISR_HANDLER(void) { Wire.onService(); }

endif

if WIRE_INTERFACES_COUNT > 1

static void Wire1_Init(void) { pmc_enable_periph_clk(WIRE1_INTERFACE_ID); PIO_Configure( g_APinDescription[PIN_WIRE1_SDA].pPort, g_APinDescription[PIN_WIRE1_SDA].ulPinType, g_APinDescription[PIN_WIRE1_SDA].ulPin, g_APinDescription[PIN_WIRE1_SDA].ulPinConfiguration); PIO_Configure( g_APinDescription[PIN_WIRE1_SCL].pPort, g_APinDescription[PIN_WIRE1_SCL].ulPinType, g_APinDescription[PIN_WIRE1_SCL].ulPin, g_APinDescription[PIN_WIRE1_SCL].ulPinConfiguration);

NVIC_DisableIRQ(WIRE1_ISR_ID);
NVIC_ClearPendingIRQ(WIRE1_ISR_ID);
NVIC_SetPriority(WIRE1_ISR_ID, 0);
NVIC_EnableIRQ(WIRE1_ISR_ID);

}

TwoWire Wire1 = TwoWire(WIRE1_INTERFACE, Wire1_Init);

void WIRE1_ISR_HANDLER(void) { Wire1.onService(); }

endif

/*

ifndef TwoWire_h

define TwoWire_h

// Include Atmel CMSIS driver

include <include/twi.h>

include "Stream.h"

include "variant.h"

define BUFFER_LENGTH 32

class TwoWire : public Stream { public: TwoWire(Twi _twi, void(_begin_cb)(void)); void begin(); void begin(uint8_t); void begin(int); void setClock(uint32_t); void beginTransmission(uint8_t); void beginTransmission(int); uint8_t endTransmission(void); uint8_t endTransmission(uint8_t); uint8_t requestFrom(uint8_t, uint8_t, uint32_t, uint8_t); uint8_t requestFrom(uint8_t, uint8_t); uint8_t requestFrom(uint8_t, uint8_t, uint8_t); uint8_t requestFrom(int, int); uint8_t requestFrom(int, int, int); virtual size_t write(uint8_t); virtual size_t write(const uint8t , sizet); virtual int available(void); virtual int read(void); virtual int peek(void); virtual void flush(void); void onReceive(void()(int)); void onRequest(void(*)(void));

inline size_t write(unsigned long n) { return write((uint8_t)n); }
inline size_t write(long n) { return write((uint8_t)n); }
inline size_t write(unsigned int n) { return write((uint8_t)n); }
inline size_t write(int n) { return write((uint8_t)n); }
using Print::write;

void onService(void);

private: // RX Buffer uint8_t rxBuffer[BUFFER_LENGTH]; uint8_t rxBufferIndex; uint8_t rxBufferLength;

// TX Buffer
uint8_t txAddress;
uint8_t txBuffer[BUFFER_LENGTH];
uint8_t txBufferLength;

// Service buffer
uint8_t srvBuffer[BUFFER_LENGTH];
uint8_t srvBufferIndex;
uint8_t srvBufferLength;

// Callback user functions
void (*onRequestCallback)(void);
void (*onReceiveCallback)(int);

// Called before initialization
void (*onBeginCallback)(void);

// TWI instance
Twi *twi;

// TWI state
enum TwoWireStatus {
    UNINITIALIZED,
    MASTER_IDLE,
    MASTER_SEND,
    MASTER_RECV,
    SLAVE_IDLE,
    SLAVE_RECV,
    SLAVE_SEND
};
TwoWireStatus status;

// TWI clock frequency
static const uint32_t TWI_CLOCK = 100000;
uint32_t twiClock;

// Timeouts (
static const uint32_t RECV_TIMEOUT = 100000;
static const uint32_t XMIT_TIMEOUT = 100000;

};

if WIRE_INTERFACES_COUNT > 0

extern TwoWire Wire;

endif

if WIRE_INTERFACES_COUNT > 1

extern TwoWire Wire1;

endif

endif

/*

include

include

include "MMA8452Q.h"

define MMA8452Q_ADDRESS 0x1D

enum MMA8452Q_REGISTERS { STATUS = 0x00,

OUT_X_MSB    = 0x01,
OUT_X_LSB    = 0x02,

OUT_Y_MSB    = 0x03,
OUT_Y_LSB    = 0x04,

OUT_Z_MSB    = 0x05,
OUT_Z_LSB    = 0x06,

SYSMOD       = 0x0B,
INT_SOURCE   = 0x0C,
WHO_AM_I     = 0x0D,
XYZ_DATA_CFG = 0x0E,

PL_STATUS    = 0x10,
PL_CFG       = 0x11,
PL_COUNT     = 0x12,

CTRL_REG1    = 0x2A,
CTRL_REG2    = 0x2B,
CTRL_REG3    = 0x2C,
CTRL_REG4    = 0x2D,
CTRL_REG5    = 0x2E,

OFF_X        = 0x2F,
OFF_Y        = 0x30,
OFF_Z        = 0x31

};

enum MMA8452Q_STATUS { ZYX_OW = 0b10000000, Z_OW = 0b01000000, Y_OW = 0b00100000, X_OW = 0b00010000, ZYX_DR = 0b00001000, Z_DR = 0b00000100, Y_DR = 0b00000010, X_DR = 0b00000001 };

enum MMA8452Q_CTRL_REG1 { ACTIVE = 0, F_READ = 1, LNOISE = 2, DR0 = 3, DR1 = 4, DR2 = 5 };

enum MMA8452Q_CTRL_REG2 { MODS0 = 0, MODS1 = 1, SLPE = 2, SMODS0 = 3, SMODS1 = 4, RST = 6, ST = 7 };

enum MMA8452Q_CTRL_REG3 { PP_OD = 0, IPOL = 1, WAKE_FF_MT = 3, WAKE_PULSE = 4, WAKE_LNDPRT = 5, WAKE_TRANS = 6 };

enum MMA8452Q_CTRL_REG4 { INT_EN_DRDY = 0, INT_EN_FF_MT = 2, INT_EN_PULSE = 3, INT_EN_LNDPRT = 4, INT_EN_TRANS = 5, INT_EN_ASLP = 7 };

enum MMA8452Q_CTRL_REG5 { INT_CFG_DRDY = 0, INT_CFG_FF_MT = 2, INT_CFG_PULSE = 3, INT_CFG_LNDPRT = 4, INT_CFG_TRANS = 5, INT_CFG_ASLP = 7 };

enum MMA8452Q_PL_STATUS { BAFRO = 0, LAPO0 = 1, LAPO1 = 2, LO = 6, NEWLP = 7 };

enum MMA8452Q_PL_CFG { PL_EN = 6, DBCNTM = 7 };

static inline uint8_t registerRead(uint8_t addr) {

ifdef _VARIANT_ARDUINO_DUEX

Wire.requestFrom((uint8_t) MMA8452Q_ADDRESS, (uint8_t) 1, (uint32_t) addr, (uint8_t) 1);

else

Wire.beginTransmission(MMA8452Q_ADDRESS);
Wire.write(addr);
Wire.endTransmission(false);
Wire.requestFrom(MMA8452Q_ADDRESS, 1);

endif

while (!Wire.available());
return Wire.read();

}

static inline void registersRead(uint8_t addr, uint8_t data[], int count) {

ifdef _VARIANT_ARDUINO_DUEX

Wire.requestFrom((uint8_t) MMA8452Q_ADDRESS, (uint8_t) count, (uint32_t) addr, (uint8_t) 1);

else

Wire.beginTransmission(MMA8452Q_ADDRESS);
Wire.write(addr);
Wire.endTransmission(false);
Wire.requestFrom(MMA8452Q_ADDRESS, count);

endif

while (Wire.available() < count);

for (int i = 0; i < count; i++)
    data[i] = Wire.read();

}

static inline void registerWrite(uint8_t addr, uint8_t value) { Wire.beginTransmission(MMA8452Q_ADDRESS); Wire.write(addr); Wire.write(value); Wire.endTransmission(); }

static inline void registersWrite(uint8_t addr, uint8_t data[], int count) { Wire.beginTransmission(MMA8452Q_ADDRESS); Wire.write(addr);

for (int i = 0; i < count; i++)
    Wire.write(data[i]);

Wire.endTransmission();

}

static inline void registerSetBit(uint8_t addr, uint8_t bit, bool value) { uint8_t val = registerRead(addr); bitWrite(val, bit, value); registerWrite(addr, val); }

MMA8452Q::MMA8452Q() { }

int MMA8452Q::begin(void) { uint8_t whoami;

Wire.begin();

whoami = registerRead(WHO_AM_I);

if (whoami != 0x2A)
    return -1;

this -> active(true);

return 0;

}

uint8_t MMA8452Q::status(void) { return registerRead(STATUS); }

uint8_t MMA8452Q::sysmod(void) { return registerRead(SYSMOD); }

uint8_t MMA8452Q::intSource(void) { return registerRead(INT_SOURCE); }

void MMA8452Q::scale(uint8_t scale) { uint8_t value = registerRead(XYZ_DATA_CFG);

switch (scale) {
    case 2: bitWrite(value, 0, 0); bitWrite(value, 1, 0); break;
    case 4: bitWrite(value, 0, 1); bitWrite(value, 1, 0); break;
    case 8: bitWrite(value, 0, 0); bitWrite(value, 1, 1); break;
}

}

void MMA8452Q::offset(int8_t off_x, int8_t off_y, int8_t off_z) { registerWrite(OFF_X, off_x); registerWrite(OFF_Y, off_y); registerWrite(OFF_Z, off_z); }

void MMA8452Q::active(bool enable) { registerSetBit(CTRL_REG1, ACTIVE, enable); }

void MMA8452Q::fastRead(bool enable) { registerSetBit(CTRL_REG1, F_READ, enable); }

void MMA8452Q::lowNoise(bool enable) { registerSetBit(CTRL_REG1, LNOISE, enable); }

void MMA8452Q::reset(void) { registerSetBit(CTRL_REG2, RST, 1); }

void MMA8452Q::selfTest(bool enable) { registerSetBit(CTRL_REG2, ST, enable); }

void MMA8452Q::autoSleep(bool enable) { registerSetBit(CTRL_REG2, SLPE, enable); }

void MMA8452Q::detectOrientation(bool enable) { registerSetBit(PL_CFG, PL_EN, enable); }

void MMA8452Q::wakeOn(bool enable, uint8_t events) { if (events & FREEFALL_MOTION) registerSetBit(CTRL_REG3, WAKE_FF_MT, enable);

if (events & PULSE)
    registerSetBit(CTRL_REG3, WAKE_PULSE, enable);

if (events & ORIENTATION)
    registerSetBit(CTRL_REG3, WAKE_LNDPRT, enable);

}

/* void MMA8452Q::intDataRdy(bool enable, uint8t pin) { / /_ registerSetBit(CTRL_REG4, INT_ENDRDY, enable); / /_ registerSetBit(CTRL_REG5, INT_CFGDRDY, enable); / /_ } */

/* void MMA8452Q::intFreefallMotion(bool enable, uint8t pin) { / /_ registerSetBit(CTRL_REG4, INT_EN_FFMT, enable); / /_ registerSetBit(CTRL_REG5, INT_CFG_FFMT, enable); / /_ } */

/* void MMA8452Q::intPulse(bool enable, uint8t pin) { / /_ registerSetBit(CTRL_REG4, INT_ENPULSE, enable); / /_ registerSetBit(CTRL_REG5, INT_CFGPULSE, enable); / /_ } */

/* void MMA8452Q::intOrientation(bool enable, uint8t pin) { / /_ registerSetBit(CTRL_REG4, INT_ENLNDPRT, enable); / /_ registerSetBit(CTRL_REG5, INT_CFGLNDPRT, enable); / /_ } */

/* void MMA8452Q::intAutoSlp(bool enable, uint8t pin) { / /_ registerSetBit(CTRL_REG4, INT_ENASLP, enable); / /_ registerSetBit(CTRL_REG5, INT_CFGASLP, enable); / /_ } */

void MMA8452Q::axes(int16_t axes[]) { uint8_t *data; uint8_t read_count = 0; uint8_t val = registerRead(CTRL_REG1);

if (bitRead(val, F_READ) == 0)
    read_count = 6;
else
    read_count = 3;

data = new uint8_t[read_count];

registersRead(OUT_X_MSB, data, read_count);

for (uint8_t i = 0; i < 3; i++) {
    axes[i]  = data[i * (read_count / 3)] << 8;

    if (bitRead(val, F_READ) == 0)
        axes[i] |= data[(i * 2) + 1];

    axes[i] >>= 4;
}

delete[] data;

}

bool MMA8452Q::orientation(uint8_t _value) { *value = registerRead(PL_STATUS); return bitRead(_value, NEWLP); }

int MMA8452Q::portrait(uint8_t orient) { if ((bitRead(orient, LAPO1) == 0) && (bitRead(orient, LAPO0) == 0)) return HIGH; else if ((bitRead(orient, LAPO1) == 0) && (bitRead(orient, LAPO0) == 1)) return LOW; else return -1; }

int MMA8452Q::landscape(uint8_t orient) { if ((bitRead(orient, LAPO1) == 1) && (bitRead(orient, LAPO0) == 0)) return HIGH; else if ((bitRead(orient, LAPO1) == 1) && (bitRead(orient, LAPO0) == 1)) return LOW; else return -1; }

int MMA8452Q::backFront(uint8_t orient) { return bitRead(orient, BAFRO); }

/*

ifndef MMA8452Q_H

define MMA8452Q_H

include

class MMA8452Q { public: /* enum MMA8452QINTERRUPTS { / /_ MMA8452QINT1 = 1, / /_ MMA8452QINT2 = 0 / /_ }; */

enum MMA8452QSYSMOD { STANDBY, /*< Stand-by system mode / WAKE, /**< Wake system mode / SLEEP /_< Sleep system mode / };

enum MMA8452Q_EVENTS { FREEFALLMOTION = 0x01, /*< Freefall/motion event / PULSE = 0x02, /**< Pulse (tap) event / ORIENTATION = 0x04 /_< Orientation change event / };

MMA8452Q();

/! Initialize the MMA8452Q. / int begin(void);

/! Read the status of the MMA8452Q. / uint8_t status(void);

/*! Read the system mode of the MMA8452Q.

\return One of the MMA8452Q::MMA8452Q_SYSMOD values.

Example: \verbatim embed:rst .. code-block:: c++

switch (accel.sysmod()) { case MMA8452Q::STANDBY: Serial.println("StandBy"); break;

case MMA8452Q::SLEEP:
  Serial.println("Sleep");
  break;

case MMA8452Q::WAKE:
  Serial.println("Wake");
  break;

} \endverbatim */ uint8_t sysmod(void);

/! Read the source pin of the last interrupt. / uint8_t intSource(void);

/*! Set the scale of the MMA8452Q.

Before calling this method, the MMA8452Q must be disabled using the active() method.

\param scale scale can be 2, 4, or 8.

Example: \verbatim embed:rst .. code-block:: c++

accel.active(false); accel.scale(2); accel.active(true); \endverbatim */ void scale(uint8_t scale);

/*! Calibrate the MMA8452Q.

Before calling this method, the MMA8452Q must be disabled using the active() method.

\param off_x X axis calibration. \param off_y Y axis calibration. \param off_z Z axis calibration.

Example: \verbatim embed:rst .. code-block:: c++

accel.active(false); accel.offset(5, -6, 10); accel.active(true); \endverbatim */

void offset(int8_t off_x, int8_t off_y, int8_t off_z);

/*! Read the raw values of the axes.

\param axes output array of size 3 (must be allocated by the user).

Example: \verbatim embed:rst .. code-block:: c++

int axes[3]; int x, y, z;

accel.axes(axes);

x = axes[0]; y = axes[1]; x = axes[2]; \endverbatim */

void axes(int16_t *axes);

/*! Read the orientation value.

Before calling this method, the orientation detection must be activated using the detectOrientation() method.

\param value output value. \return Whether the orientation has changed since the last read.

Example: \verbatim embed:rst .. code-block:: c++

uint8_t orientation;

if (accel.orientation(&orientation)) { ... } \endverbatim / bool orientation(uint8_t value);

/*! Read the landscape orientation status.

\param orientation The orientation set by the orientation() method. \return Whether the landscape orientation is right or left.

Example: \verbatim embed:rst .. code-block:: c++

uint8_t orientation;

accel.orientation(&orientation));

switch (accel.landscape(orientation)) { case HIGH: Serial.println("Landscape Right"); break;

case LOW:
  Serial.println("Landscape Left");
  break;

default:
  Serial.println("No Orientation");
  break;

} \endverbatim */ int landscape(uint8_t orientation);

/*! Read the portrait orientation status.

\param orientation The orientation set by the orientation() method. \return Whether the portrait orientation is up or down.

Example: \verbatim embed:rst .. code-block:: c++

uint8_t orientation;

accel.orientation(&orientation));

switch (accel.portrait(orientation)) { case HIGH: Serial.println("Portrait Up"); break;

case LOW:
  Serial.println("Portrait Down");
  break;

default:
  Serial.println("No Portrait");
  break;

} \endverbatim */ int portrait(uint8_t orientation);

/*! Read the back/front orientation status.

\param orientation The orientation set by the orientation() method. \return Whether the orientation is back or front.

Example: \verbatim embed:rst .. code-block:: c++

uint8_t orientation;

accel.orientation(&orientation));

if (accel.backFront(orientation)) Serial.println("Back"); else Serial.println("Front"); \endverbatim */ int backFront(uint8_t orientation);

/*! Enable/disable the MMA8452Q.

\param enable Whether to enable or disable the MMA8452Q. */ void active(bool enable);

/*! Enable/disable the fast read mode.

Before calling this method, the MMA8452Q must be disabled using the active() method.

\param enable Whether to enable or disable the fast read mode.

Example: \verbatim embed:rst .. code-block:: c++

accel.active(false); accel.fastRead(true); accel.active(true); \endverbatim */ void fastRead(bool enable);

/*! Enable/disable the low noise mode.

Before calling this method, the MMA8452Q must be disabled using the active() method.

\param enable Whether to enable or disable the low noise mode.

Example: \verbatim embed:rst .. code-block:: c++

accel.active(false); accel.lowNoise(true); accel.active(true); \endverbatim */ void lowNoise(bool enable);

/! Reset the MMA8452Q. / void reset(void);

/*! Enable/disable the self-test mode.

Before calling this method, the MMA8452Q must be disabled using the active() method.

\param enable Whether to enable or disable the self-test mode.

Example: \verbatim embed:rst .. code-block:: c++

accel.active(false); accel.selfTest(true); accel.active(true); \endverbatim */ void selfTest(bool enable);

/*! Enable/disable the auto-sleep mode.

Before calling this method, the MMA8452Q must be disabled using the active() method.

\param enable Whether to enable or disable the auto-sleep mode.

Example: \verbatim embed:rst .. code-block:: c++

accel.active(false); accel.autoSleep(true); accel.active(true); \endverbatim */ void autoSleep(bool enable);

/*! Enable/disable the orientation detection.

Before calling this method, the MMA8452Q must be disabled using the active() method.

\param enable Whether to enable or disable the orientation detection.

Example: \verbatim embed:rst .. code-block:: c++

accel.active(false); accel.detectOrientation(true); accel.active(true); \endverbatim */ void detectOrientation(bool enable);

/*! Enable/disable auto-wake on specific events.

Before calling this method, the MMA8452Q must be disabled using the active() method.

\param enable Whether to enable or disable the auto-wake on the selected events. \param events Bit mask listing the desired events. See MMA8452Q::MMA8452Q_EVENTS.

Example: \verbatim embed:rst .. code-block:: c++

accel.active(false); accel.wakeOn(true, FREEFALL_MOTION | ORIENTATION); accel.active(true); \endverbatim */ void wakeOn(bool enable, uint8_t events);

/* void intDataRdy(bool enable, uint8t pin); / /_ void intFreefallMotion(bool enable, uint8t pin); / /_ void intPulse(bool enable, uint8t pin); / /_ void intOrientation(bool enable, uint8t pin); / /_ void intAutoSlp(bool enable, uint8_t pin); */ };

endif

AmLimit commented 8 years ago

Hi Kaveh14934, I sent you an email with the complete code.

May be this was the source: https://gist.github.com/ghedo/6750945

Kaveh14934 commented 8 years ago

Nice work, I could get it up and running.

Darthpbal commented 7 years ago

Testing the DUE with the Adafruit NXP 9DOF breakout and the fix worked like a charm. I was surprised I had to do the edits though because this seems like a problem solved a while ago for SAM chips, but the overloaded requestFrom function wasn't in my SAM wire library.