Skip to content

File dps310.h

File List > dev > dps310.h

Go to the documentation of this file

Source Code

#pragma once
#ifndef DSY_DPS310_H
#define DSY_DPS310_H

#define DPS310_I2CADDR_DEFAULT (0x77) 

#define DPS310_PRSB2 0x00       
#define DPS310_TMPB2 0x03       
#define DPS310_PRSCFG 0x06      
#define DPS310_TMPCFG 0x07      
#define DPS310_MEASCFG 0x08     
#define DPS310_CFGREG 0x09      
#define DPS310_RESET 0x0C       
#define DPS310_PRODREVID 0x0D   
#define DPS310_TMPCOEFSRCE 0x28 

namespace daisy
{
class Dps310I2CTransport
{
  public:
    Dps310I2CTransport() {}
    ~Dps310I2CTransport() {}

    struct Config
    {
        I2CHandle::Config::Peripheral periph;
        I2CHandle::Config::Speed      speed;
        Pin                           scl;
        Pin                           sda;

        uint8_t address;

        Config()
        {
            address = DPS310_I2CADDR_DEFAULT;

            periph = I2CHandle::Config::Peripheral::I2C_1;
            speed  = I2CHandle::Config::Speed::I2C_400KHZ;

            scl = Pin(PORTB, 8);
            sda = Pin(PORTB, 9);
        }
    };

    inline void Init(Config config)
    {
        config_ = config;

        I2CHandle::Config i2c_config;
        i2c_config.mode   = I2CHandle::Config::Mode::I2C_MASTER;
        i2c_config.periph = config.periph;
        i2c_config.speed  = config.speed;

        i2c_config.pin_config.scl = config.scl;
        i2c_config.pin_config.sda = config.sda;

        i2c_.Init(i2c_config);
    }

    void Write(uint8_t *data, uint16_t size)
    {
        error_ |= I2CHandle::Result::OK
                  != i2c_.TransmitBlocking(config_.address, data, size, 10);
    }

    void Read(uint8_t *data, uint16_t size)
    {
        error_ |= I2CHandle::Result::OK
                  != i2c_.ReceiveBlocking(config_.address, data, size, 10);
    }

    void Write8(uint8_t reg, uint8_t value)
    {
        uint8_t buffer[2];

        buffer[0] = reg;
        buffer[1] = value;

        Write(buffer, 2);
    }

    void ReadReg(uint8_t reg, uint8_t *buff, uint8_t size)
    {
        Write(&reg, 1);
        Read(buff, size);
    }

    uint8_t Read8(uint8_t reg)
    {
        uint8_t buffer;
        ReadReg(reg, &buffer, 1);
        return buffer;
    }

    uint16_t Read16(uint8_t reg)
    {
        uint8_t buffer[2];
        ReadReg(reg, buffer, 2);

        return uint16_t(buffer[0]) << 8 | uint16_t(buffer[1]);
    }

    uint32_t Read24(uint8_t reg)
    {
        uint8_t buffer[3];

        ReadReg(reg, buffer, 3);

        return uint32_t(buffer[0]) << 16 | uint32_t(buffer[1]) << 8
               | uint32_t(buffer[2]);
    }

    bool GetError()
    {
        bool tmp = error_;
        error_   = false;
        return tmp;
    }

  private:
    I2CHandle i2c_;
    Config    config_;

    // true if error has occured since last check
    bool error_;
};

class Dps310SpiTransport
{
  public:
    Dps310SpiTransport() {}
    ~Dps310SpiTransport() {}

    struct Config
    {
        SpiHandle::Config::Peripheral periph;
        Pin                           sclk;
        Pin                           miso;
        Pin                           mosi;
        Pin                           nss;

        Config()
        {
            periph = SpiHandle::Config::Peripheral::SPI_1;
            sclk   = Pin(PORTG, 11);
            miso   = Pin(PORTB, 4);
            mosi   = Pin(PORTB, 5);
            nss    = Pin(PORTG, 10);
        }
    };

    inline void Init(Config config)
    {
        SpiHandle::Config spi_conf;
        spi_conf.mode           = SpiHandle::Config::Mode::MASTER;
        spi_conf.direction      = SpiHandle::Config::Direction::TWO_LINES;
        spi_conf.clock_polarity = SpiHandle::Config::ClockPolarity::LOW;
        spi_conf.clock_phase    = SpiHandle::Config::ClockPhase::ONE_EDGE;
        spi_conf.baud_prescaler = SpiHandle::Config::BaudPrescaler::PS_2;
        spi_conf.nss            = SpiHandle::Config::NSS::SOFT;

        spi_conf.periph          = config.periph;
        spi_conf.pin_config.sclk = config.sclk;
        spi_conf.pin_config.miso = config.miso;
        spi_conf.pin_config.mosi = config.mosi;
        spi_conf.pin_config.nss  = config.nss;

        spi_.Init(spi_conf);
    }

    void Write(uint8_t *data, uint16_t size)
    {
        error_ |= SpiHandle::Result::OK != spi_.BlockingTransmit(data, size);
    }

    void Read(uint8_t *data, uint16_t size)
    {
        error_ |= SpiHandle::Result::OK != spi_.BlockingReceive(data, size, 10);
    }

    void Write8(uint8_t reg, uint8_t value)
    {
        uint8_t buffer[2];

        buffer[0] = reg & ~0x80;
        buffer[1] = value;

        Write(buffer, 2);
    }

    void ReadReg(uint8_t reg, uint8_t *buff, uint8_t size)
    {
        reg = uint8_t(reg | 0x80);
        Write(&reg, 1);
        Read(buff, size);
    }

    uint8_t Read8(uint8_t reg)
    {
        uint8_t buffer;
        ReadReg(reg, &buffer, 1);
        return buffer;
    }

    uint16_t Read16(uint8_t reg)
    {
        uint8_t buffer[2];
        ReadReg(reg, buffer, 2);

        return uint16_t(buffer[0]) << 8 | uint16_t(buffer[1]);
    }

    uint32_t Read24(uint8_t reg)
    {
        uint8_t buffer[3];

        ReadReg(reg, buffer, 3);

        return uint32_t(buffer[0]) << 16 | uint32_t(buffer[1]) << 8
               | uint32_t(buffer[2]);
    }

    bool GetError()
    {
        bool tmp = error_;
        error_   = false;
        return tmp;
    }

  private:
    SpiHandle spi_;
    bool      error_;
};

template <typename Transport>
class Dps310
{
  public:
    Dps310() {}
    ~Dps310() {}

    int32_t oversample_scalefactor[8]
        = {524288, 1572864, 3670016, 7864320, 253952, 516096, 1040384, 2088960};

    enum dps310_rate_t
    {
        DPS310_1HZ,   
        DPS310_2HZ,   
        DPS310_4HZ,   
        DPS310_8HZ,   
        DPS310_16HZ,  
        DPS310_32HZ,  
        DPS310_64HZ,  
        DPS310_128HZ, 
    };

    enum dps310_oversample_t
    {
        DPS310_1SAMPLE,    
        DPS310_2SAMPLES,   
        DPS310_4SAMPLES,   
        DPS310_8SAMPLES,   
        DPS310_16SAMPLES,  
        DPS310_32SAMPLES,  
        DPS310_64SAMPLES,  
        DPS310_128SAMPLES, 
    };

    enum dps310_mode_t
    {
        DPS310_IDLE            = 0b000, 
        DPS310_ONE_PRESSURE    = 0b001, 
        DPS310_ONE_TEMPERATURE = 0b010, 
        DPS310_CONT_PRESSURE   = 0b101, 
        DPS310_CONT_TEMP       = 0b110, 
        DPS310_CONT_PRESTEMP = 0b111, 
    };

    struct Config
    {
        typename Transport::Config transport_config;

        Config() {}
    };

    enum Result
    {
        OK = 0,
        ERR
    };

    Result Init(Config config)
    {
        config_ = config;

        transport_.Init(config_.transport_config);

        // make sure we're talking to the right chip
        if(Read8(DPS310_PRODREVID) != 0x10)
        {
            // No DPS310 detected ... return false
            return ERR;
        }

        reset();
        _readCalibration();
        // default to high precision
        configurePressure(DPS310_64HZ, DPS310_64SAMPLES);
        configureTemperature(DPS310_64HZ, DPS310_64SAMPLES);
        // continuous
        setMode(DPS310_CONT_PRESTEMP);
        // wait until we have at least one good measurement
        while(!temperatureAvailable() || !pressureAvailable())
        {
            System::Delay(10);
        }

        return GetTransportError();
    }


    void reset(void)
    {
        Write8(DPS310_RESET, 0x89);

        // Wait for a bit till its out of hardware reset
        System::Delay(10);

        while(!ReadBits(DPS310_MEASCFG, 1, 6))
        {
            System::Delay(1);
        }
    }

    static int32_t twosComplement(int32_t val, uint8_t bits)
    {
        if(val & ((uint32_t)1 << (bits - 1)))
        {
            val -= (uint32_t)1 << bits;
        }
        return val;
    }

    void _readCalibration(void)
    {
        // Wait till we're ready to read calibration
        while(!ReadBits(DPS310_MEASCFG, 1, 7))
        {
            System::Delay(1);
        }

        uint8_t coeffs[18];
        for(uint8_t addr = 0; addr < 18; addr++)
        {
            coeffs[addr] = Read8(0x10 + addr);
        }
        _c0 = ((uint16_t)coeffs[0] << 4) | (((uint16_t)coeffs[1] >> 4) & 0x0F);
        _c0 = twosComplement(_c0, 12);

        _c1 = twosComplement((((uint16_t)coeffs[1] & 0x0F) << 8) | coeffs[2],
                             12);

        _c00 = ((uint32_t)coeffs[3] << 12) | ((uint32_t)coeffs[4] << 4)
               | (((uint32_t)coeffs[5] >> 4) & 0x0F);
        _c00 = twosComplement(_c00, 20);

        _c10 = (((uint32_t)coeffs[5] & 0x0F) << 16) | ((uint32_t)coeffs[6] << 8)
               | (uint32_t)coeffs[7];
        _c10 = twosComplement(_c10, 20);

        _c01 = twosComplement(((uint16_t)coeffs[8] << 8) | (uint16_t)coeffs[9],
                              16);
        _c11 = twosComplement(
            ((uint16_t)coeffs[10] << 8) | (uint16_t)coeffs[11], 16);
        _c20 = twosComplement(
            ((uint16_t)coeffs[12] << 8) | (uint16_t)coeffs[13], 16);
        _c21 = twosComplement(
            ((uint16_t)coeffs[14] << 8) | (uint16_t)coeffs[15], 16);
        _c30 = twosComplement(
            ((uint16_t)coeffs[16] << 8) | (uint16_t)coeffs[17], 16);
    }


    bool temperatureAvailable(void) { return ReadBits(DPS310_MEASCFG, 1, 5); }


    bool pressureAvailable(void) { return ReadBits(DPS310_MEASCFG, 1, 4); }


    float GetAltitude(float seaLevelhPa)
    {
        float altitude;

        Process();

        altitude = 44330 * (1.0 - pow((_pressure / 100) / seaLevelhPa, 0.1903));

        return altitude;
    }


    void setMode(dps310_mode_t mode) { WriteBits(DPS310_MEASCFG, mode, 3, 0); }


    void configurePressure(dps310_rate_t rate, dps310_oversample_t os)
    {
        WriteBits(DPS310_PRSCFG, rate, 3, 4);
        WriteBits(DPS310_PRSCFG, os, 4, 0);

        if(os > DPS310_8SAMPLES)
        {
            WriteBits(DPS310_CFGREG, 1, 1, 2);
        }
        else
        {
            WriteBits(DPS310_CFGREG, 0, 1, 2);
        }

        pressure_scale = oversample_scalefactor[os];
    }


    void configureTemperature(dps310_rate_t rate, dps310_oversample_t os)
    {
        WriteBits(DPS310_TMPCFG, rate, 3, 4);
        WriteBits(DPS310_TMPCFG, os, 4, 0);
        temp_scale = oversample_scalefactor[os];

        // Set shift bit if necessary
        if(os > DPS310_8SAMPLES)
        {
            WriteBits(DPS310_CFGREG, 1, 1, 3);
        }
        else
        {
            WriteBits(DPS310_CFGREG, 0, 1, 3);
        }

        // Find out what our calibration source is
        uint8_t read = ReadBits(DPS310_TMPCOEFSRCE, 1, 7);
        WriteBits(DPS310_TMPCFG, read, 1, 7);
    }


    void Process(void)
    {
        raw_temperature = twosComplement(Read24(DPS310_TMPB2), 24);
        raw_pressure    = twosComplement(Read24(DPS310_PRSB2), 24);

        _scaled_rawtemp = (float)raw_temperature / temp_scale;
        _temperature    = _scaled_rawtemp * _c1 + _c0 / 2.0;

        _pressure = (float)raw_pressure / pressure_scale;

        _pressure
            = (int32_t)_c00
              + _pressure
                    * ((int32_t)_c10
                       + _pressure
                             * ((int32_t)_c20 + _pressure * (int32_t)_c30))
              + _scaled_rawtemp
                    * ((int32_t)_c01
                       + _pressure
                             * ((int32_t)_c11 + _pressure * (int32_t)_c21));
    }

    float GetTemperature() { return _temperature; }

    float GetPressure() { return _pressure / 100; }

    void Write8(uint8_t reg, uint8_t value)
    {
        return transport_.Write8(reg, value);
    }

    void ReadReg(uint8_t reg, uint8_t *buff, uint8_t size)
    {
        return transport_.ReadReg(reg, buff, size);
    }

    uint8_t Read8(uint8_t reg) { return transport_.Read8(reg); }

    uint16_t Read16(uint8_t reg) { return transport_.Read16(reg); }

    uint32_t Read24(uint8_t reg) { return transport_.Read24(reg); }

    uint16_t Read16_LE(uint8_t reg)
    {
        uint16_t temp = Read16(reg);
        return (temp >> 8) | (temp << 8);
    }

    int16_t ReadS16(uint8_t reg) { return (int16_t)Read16(reg); }

    int16_t ReadS16_LE(uint8_t reg) { return (int16_t)Read16_LE(reg); }


    uint8_t ReadBits(uint8_t reg, uint8_t bits, uint8_t shift)
    {
        uint8_t val = Read8(reg);
        val >>= shift;
        return val & ((1 << (bits)) - 1);
    }

    void WriteBits(uint8_t reg, uint8_t data, uint8_t bits, uint8_t shift)
    {
        uint8_t val = Read8(reg);

        // mask off the data before writing
        uint8_t mask = (1 << (bits)) - 1;
        data &= mask;

        mask <<= shift;
        val &= ~mask;         // remove the current data at that spot
        val |= data << shift; // and add in the new data

        Write8(reg, val);
    }

    Result GetTransportError() { return transport_.GetError() ? ERR : OK; }

  private:
    Config    config_;
    Transport transport_;

    int16_t _c0, _c1, _c01, _c11, _c20, _c21, _c30;
    int32_t _c00, _c10;

    int32_t raw_pressure, raw_temperature;
    float   _temperature, _scaled_rawtemp, _pressure;
    int32_t temp_scale, pressure_scale;
};

using Dps310I2C = Dps310<Dps310I2CTransport>;
using Dps310Spi = Dps310<Dps310SpiTransport>;
} // namespace daisy
#endif