Driver for KX134-1211 Accelerometer

Dependents:   KX134-1211 Examples

KX134.cpp

Committer:
Jasper Swallen
Date:
2020-10-25
Revision:
1:c6e2a348da09
Parent:
0:01d5616ba355

File content as of revision 1:c6e2a348da09:

#include "KX134.h"
#include "math.h"

#define SPI_FREQ 100000

static uint8_t defaultBuffer[2] = {0}; // allows calling writeRegisterOneByte
                                       // without buf argument

/* Writes one byte to a register
 */
void KX134::writeRegisterOneByte(Register addr, uint8_t data,
                                 uint8_t *buf = defaultBuffer)
{
    uint8_t _data[1] = {data};
    writeRegister(addr, _data, buf);
}

KX134::KX134(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName int1,
             PinName int2, PinName rst)
    : _spi(mosi, miso, sclk), _int1(int1), _int2(int2), _cs(cs), _rst(rst)
{
    // set default values for settings variables
    resStatus = 1;   // high performance mode
    drdyeStatus = 1; // Data Ready Engine enabled
    gsel1Status = 0; // +-8g bit 1
    gsel0Status = 0; // +-8g bit 0
    tdteStatus = 0;  // Tap/Double-Tap engine disabled
    tpeStatus = 0;   // Tilt Position Engine disabled

    iirBypass = 0; // IIR filter is not bypassed, i.e. filtering is applied
    lpro = 0;      // IIR filter corner frequency set to ODR/9
    fstup = 0;     // Fast Start is disabled
    osa3 = 0;      // Output Data Rate bits
    osa2 = 1;      // default is 50hz
    osa1 = 1;
    osa0 = 0;

    registerWritingEnabled = 0;

    deselect();
}

bool KX134::init()
{
    deselect();

    _spi.frequency(SPI_FREQ);
    _spi.format(8, 1);

    return reset();
}

bool KX134::reset()
{
    // write registers to start reset
    writeRegisterOneByte(Register::INTERNAL_0X7F, 0x00);
    writeRegisterOneByte(Register::CNTL2, 0x00);
    writeRegisterOneByte(Register::CNTL2, 0x80);

    // software reset time
    wait_us(2000);

    // check existence
    return checkExistence();
}

/* Helper Functions
 * ====================================================
 */

void KX134::deselect()
{
    _cs.write(1);
}

void KX134::select()
{
    _cs.write(0);
}

void KX134::readRegister(Register addr, uint8_t *rx_buf, int size)
{
    select();

    rx_buf[0] = _spi.write((uint8_t)addr | (1 << 7));

    for(int i = 1; i < size; ++i)
    {
        rx_buf[i] = _spi.write(0x00);
    }

    deselect();
}

void KX134::writeRegister(Register addr, uint8_t *data, uint8_t *rx_buf,
                          int size)
{
    select();

    _spi.write((uint8_t)addr); // select register
    for(int i = 0; i < size; ++i)
    {
        rx_buf[i] = _spi.write(data[i]);
    }

    deselect();
}

int16_t KX134::read16BitValue(Register lowAddr, Register highAddr)
{
    // get contents of register
    uint8_t lowWord[2], highWord[2];
    readRegister(lowAddr, lowWord);
    readRegister(highAddr, highWord);

    return convertTo16BitValue(lowWord[1], highWord[1]);
}

int16_t KX134::convertTo16BitValue(uint8_t low, uint8_t high)
{
    // combine low & high words
    uint16_t val2sComplement = (static_cast<uint16_t>(high << 8)) | low;
    int16_t value = static_cast<int16_t>(val2sComplement);

    return value;
}

float KX134::convertRawToGravs(int16_t lsbValue)
{
    if(gsel1Status && gsel0Status)
    {
        // +-64g
        return (float)lsbValue * 0.00195f;
    }
    else if(gsel1Status && !gsel0Status)
    {
        // +-32g
        return (float)lsbValue * 0.00098f;
    }
    else if(!gsel1Status && gsel0Status)
    {
        // +-16g
        return (float)lsbValue * 0.00049f;
    }
    else if(!gsel1Status && !gsel0Status)
    {
        // +-8g
        return (float)lsbValue * 0.00024f;
    }
    else
    {
        return 0;
    }
}

void KX134::getAccelerations(int16_t *output)
{
    uint8_t words[7];

    // this was the recommended method by Kionix
    // for some reason, this has *significantly* less noise than reading
    // one-by-one
    readRegister(Register::XOUT_L, words, 7);

    output[0] = convertTo16BitValue(words[1], words[2]);
    output[1] = convertTo16BitValue(words[3], words[4]);
    output[2] = convertTo16BitValue(words[5], words[6]);
}

bool KX134::checkExistence()
{
    // verify WHO_I_AM
    uint8_t whoami[2];
    readRegister(Register::WHO_AM_I, whoami);

    if(whoami[1] != 0x46)
    {
        return false; // WHO_AM_I is incorrect
    }

    // verify COTR
    uint8_t cotr[2];
    readRegister(Register::COTR, cotr);
    if(cotr[1] != 0x55)
    {
        return false; // COTR is incorrect
    }

    return true;
}

void KX134::setAccelRange(Range range)
{
    gsel0Status = (uint8_t)range & 0b01;
    gsel1Status = (uint8_t)range & 0b10;

    uint8_t writeByte = (1 << 7) | (resStatus << 6) | (drdyeStatus << 5) |
                        (gsel1Status << 4) | (gsel0Status << 3) |
                        (tdteStatus << 2) | (tpeStatus);
    // reserved bit 1, PC1 bit must be enabled

    writeRegisterOneByte(Register::CNTL1, writeByte);

    registerWritingEnabled = 0;
}

void KX134::setOutputDataRateBytes(uint8_t byteHz)
{
    if(!registerWritingEnabled)
    {
        return;
    }

    osa0 = byteHz & 0b0001;
    osa1 = byteHz & 0b0010;
    osa2 = byteHz & 0b0100;
    osa3 = byteHz & 0b1000;

    uint8_t writeByte = (iirBypass << 7) | (lpro << 6) | (fstup << 5) |
                        (osa3 << 3) | (osa2 << 2) | (osa1 << 1) | osa0;
    // reserved bit 4

    writeRegisterOneByte(Register::ODCNTL, writeByte);
}

void KX134::setOutputDataRateHz(uint32_t hz)
{
    // calculate byte representation from new polling rate
    // bytes = log2(32*rate/25)

    double new_rate = (double)hz;

    double bytes_double = log2((32.f / 25.f) * new_rate);
    uint8_t bytes_int = (uint8_t)ceil(bytes_double);

    setOutputDataRateBytes(bytes_int);
}

void KX134::enableRegisterWriting()
{
    writeRegisterOneByte(Register::CNTL1, 0x00);
    registerWritingEnabled = 1;
}

void KX134::disableRegisterWriting()
{
    if(!registerWritingEnabled)
    {
        return;
    }

    uint8_t writeByte = (0 << 7) | (resStatus << 6) | (drdyeStatus << 5) |
                        (gsel1Status << 4) | (gsel0Status << 3) |
                        (tdteStatus << 2) | (tpeStatus);
    // reserved bit 1, PC1 bit must be enabled

    writeRegisterOneByte(Register::CNTL1, writeByte);

    registerWritingEnabled = 0;
}

bool KX134::dataReady()
{
    uint8_t buf[2];
    readRegister(Register::INS2, buf);

    return (buf[1] == 0x10);
}