Changeset - 14710b12b30e
[Not reviewed]
tip sgp30
0 0 4
matthewreed - 6 years ago 2018-08-25 12:50:13

Added sgp30 files
4 files changed with 339 insertions and 0 deletions:
0 comments (0 inline, 0 general)
Inc/i2c.h
Show inline comments
 
new file 100644
 
/*
 
 * i2c.h
 
 *
 
 *  Created on: Mar 7, 2017
 
 *      Author: Matthew Reed
 
 */
 

	
 
#ifndef INC_I2C_H_
 
#define INC_I2C_H_
 

	
 
#include "system.h"
 

	
 
extern I2C_HandleTypeDef i2c_handle;
 

	
 
void i2c_init(void);
 
bool i2c_send(uint16_t address, uint8_t* buffer, uint16_t length);
 
bool i2c_receive(uint16_t address, uint8_t* buffer, uint16_t length);
 

	
 
#endif /* INC_I2C_H_ */
Inc/sgp30.h
Show inline comments
 
new file 100644
 
#ifndef INC_SGP30_H_
 
#define INC_SGP30_H_
 

	
 
#include <stdbool.h>
 
#include "stm32f0xx_hal.h"
 
#include "i2c.h"
 

	
 

	
 
// the i2c address
 
#define SGP30_I2CADDR_DEFAULT 0x58     ///< SGP30 has only one I2C address
 

	
 
// commands and constants
 
#define SGP30_FEATURESET       0x0020  ///< The required set for this library
 
#define SGP30_CRC8_POLYNOMIAL  0x31    ///< Seed for SGP30's CRC polynomial
 
#define SGP30_CRC8_INIT        0xFF    ///< Init value for CRC
 
#define SGP30_WORD_LEN         2       ///< 2 bytes per word
 

	
 

	
 
bool sgp30_init(void);
 
bool sgp30_IAQinit(void);
 
bool sgp30_IAQmeasure(void);
 

	
 
bool sgp30_getIAQBaseline(uint16_t *eco2_base, uint16_t *tvoc_base);
 
bool sgp30_setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base);
 
bool sgp30_setHumidity(uint32_t absolute_humidity);
 

	
 
bool sgp30_readWordFromCommand(uint8_t command[], uint8_t commandLength, uint16_t delay, uint16_t *readdata, uint8_t readlen);
 
uint8_t sgp30_generateCRC(uint8_t data[], uint8_t datalen);
 

	
 
#endif /* INC_SGP30_H_ */
Src/i2c.c
Show inline comments
 
new file 100644
 
/*
 
 * i2c.c
 
 *
 
 *  Created on: Mar 7, 2017
 
 *      Author: Matthew Reed
 
 */
 

	
 
#include "i2c.h"
 

	
 
I2C_HandleTypeDef i2c_handle;
 

	
 
void i2c_init(void)
 
{
 

	
 
    i2c_handle.Instance = I2C1;
 
    i2c_handle.Init.Timing = 0x2000090E;
 
    i2c_handle.Init.OwnAddress1 = 0;
 
    i2c_handle.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
 
    i2c_handle.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
 
    i2c_handle.Init.OwnAddress2 = 0;
 
    i2c_handle.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
 
    i2c_handle.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
 
    i2c_handle.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
 
    HAL_I2C_Init(&i2c_handle);
 

	
 
    //Configure Analog filter
 
    HAL_I2CEx_ConfigAnalogFilter(&i2c_handle, I2C_ANALOGFILTER_ENABLE);
 

	
 
}
 

	
 
bool i2c_send(uint16_t address, uint8_t* buffer, uint16_t length)
 
{
 
    bool result = true;
 

	
 
    HAL_I2C_Master_Transmit(&i2c_handle, address, buffer, length, 10000);
 

	
 
//    while(HAL_I2C_Master_Transmit(&i2c_handle, address, buffer, length, 10000) != HAL_OK)
 
//    {
 
//
 
//        if (HAL_I2C_GetError(&i2c_handle) != HAL_I2C_ERROR_AF)
 
//        {
 
//            result = false;
 
//        }
 
//    }
 

	
 
    return result;
 
}
 

	
 
bool i2c_receive(uint16_t address, uint8_t* buffer, uint16_t length)
 
{
 
    bool result = true;
 

	
 
    HAL_I2C_Master_Receive(&i2c_handle, address, buffer, length, 10000);
 

	
 
//    while(HAL_I2C_Master_Receive(&i2c_handle, address, buffer, length, 10000) != HAL_OK)
 
//    {
 
//        if (HAL_I2C_GetError(&i2c_handle) != HAL_I2C_ERROR_AF)
 
//        {
 
//            result = false;
 
//        }
 
//    }
 

	
 
    return result;
 
}
 

	
 
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *I2cHandle)
 
{
 
}
 

	
 
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *I2cHandle)
 
{
 
}
 

	
 
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *I2cHandle)
 
{
 
}
Src/sgp30.c
Show inline comments
 
new file 100644
 
#include "sgp30.h"
 

	
 
  /**
 
   * The last measurement of the IAQ-calculated Total Volatile Organic Compounds in ppb. This value is set when you call {@link IAQmeasure()}
 
   */
 
  uint16_t TVOC;
 

	
 
  /**
 
   * The last measurement of the IAQ-calculated equivalent CO2 in ppm. This value is set when you call {@link IAQmeasure()}
 
   */
 
  uint16_t eCO2;
 

	
 
  /**
 
   * The 48-bit serial number, this value is set when you call {@link begin()}
 
   */
 
  uint16_t serialnumber[3];
 

	
 
  uint8_t _i2caddr;
 

	
 
/**************************************************************************/
 
/*!
 
    @brief  Setups the hardware and detects a valid SGP30. Initializes I2C
 
    then reads the serialnumber and checks that we are talking to an SGP30
 
    @param  theWire Optional pointer to I2C interface, otherwise use Wire
 
    @returns True if SGP30 found on I2C, False if something went wrong!
 
*/
 
/**************************************************************************/
 
bool sgp30_init()
 
{
 
    _i2caddr = SGP30_I2CADDR_DEFAULT;
 

	
 
    i2c_init();
 

	
 
    uint8_t command[2];
 
    command[0] = 0x36;
 
    command[1] = 0x82;
 
    if (! sgp30_readWordFromCommand(command, 2, 10, serialnumber, 3))
 
        return false;
 

	
 
    uint16_t featureset;
 
    command[0] = 0x20;
 
    command[1] = 0x2F;
 
    if (! sgp30_readWordFromCommand(command, 2, 10, &featureset, 1))
 
        return false;
 
    //Serial.print("Featureset 0x"); Serial.println(featureset, HEX);
 
    if (featureset != SGP30_FEATURESET)
 
        return false;
 
    if (! sgp30_IAQinit())
 
        return false;
 

	
 
    return true;
 
}
 

	
 
uint16_t sgp30_get_tvoc(void)
 
{
 
    return TVOC;
 
}
 

	
 
uint16_t sgp30_get_eC02(void)
 
{
 
    return eCO2;
 
}
 

	
 
/**************************************************************************/
 
/*!
 
    @brief  Commands the sensor to begin the IAQ algorithm. Must be called after startup.
 
    @returns True if command completed successfully, false if something went wrong!
 
*/
 
/**************************************************************************/
 
bool sgp30_IAQinit(void)
 
{
 
    uint8_t command[2];
 
    command[0] = 0x20;
 
    command[1] = 0x03;
 
    return sgp30_readWordFromCommand(command, 2, 10, 0, 0);
 
}
 

	
 
/**************************************************************************/
 
/*!
 
    @brief  Commands the sensor to take a single eCO2/VOC measurement. Places results in {@link TVOC} and {@link eCO2}
 
    @returns True if command completed successfully, false if something went wrong!
 
*/
 
/**************************************************************************/
 
bool sgp30_IAQmeasure(void)
 
{
 
    uint8_t command[2];
 
    command[0] = 0x20;
 
    command[1] = 0x08;
 
    uint16_t reply[2];
 
    if (! sgp30_readWordFromCommand(command, 2, 12, reply, 2))
 
        return false;
 
    TVOC = reply[1];
 
    eCO2 = reply[0];
 
    return true;
 
}
 

	
 
/**************************************************************************/
 
/*!
 
    @brief Request baseline calibration values for both CO2 and TVOC IAQ calculations. Places results in parameter memory locaitons.
 
    @param eco2_base A pointer to a uint16_t which we will save the calibration value to
 
    @param tvoc_base A pointer to a uint16_t which we will save the calibration value to
 
    @returns True if command completed successfully, false if something went wrong!
 
*/
 
/**************************************************************************/
 
bool sgp30_getIAQBaseline(uint16_t *eco2_base, uint16_t *tvoc_base)
 
{
 
    uint8_t command[2];
 
    command[0] = 0x20;
 
    command[1] = 0x15;
 
    uint16_t reply[2];
 
    if (! sgp30_readWordFromCommand(command, 2, 10, reply, 2))
 
    return false;
 
    *eco2_base = reply[0];
 
    *tvoc_base = reply[1];
 
    return true;
 
}
 

	
 
/**************************************************************************/
 
/*!
 
    @brief Assign baseline calibration values for both CO2 and TVOC IAQ calculations.
 
    @param eco2_base A uint16_t which we will save the calibration value from
 
    @param tvoc_base A uint16_t which we will save the calibration value from
 
    @returns True if command completed successfully, false if something went wrong!
 
*/
 
/**************************************************************************/
 
bool sgp30_setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base)
 
{
 
    uint8_t command[8];
 
    command[0] = 0x20;
 
    command[1] = 0x1e;
 
    command[2] = tvoc_base >> 8;
 
    command[3] = tvoc_base & 0xFF;
 
    command[4] = sgp30_generateCRC(command+2, 2);
 
    command[5] = eco2_base >> 8;
 
    command[6] = eco2_base & 0xFF;
 
    command[7] = sgp30_generateCRC(command+5, 2);
 

	
 
    return sgp30_readWordFromCommand(command, 8, 10, 0, 0);
 
}
 

	
 
/**************************************************************************/
 
/*!
 
    @brief Set the absolute humidity value [mg/m^3] for compensation to increase precision of TVOC and eCO2.
 
    @param absolute_humidity A uint32_t [mg/m^3] which we will be used for compensation. If the absolute humidity is set to zero, humidity compensation will be disabled.
 
    @returns True if command completed successfully, false if something went wrong!
 
*/
 
/**************************************************************************/
 
bool sgp30_setHumidity(uint32_t absolute_humidity)
 
{
 
    if (absolute_humidity > 256000) {
 
        return false;
 
    }
 

	
 
    uint16_t ah_scaled = (uint16_t)(((uint64_t)absolute_humidity * 256 * 16777) >> 24);
 
    uint8_t command[5];
 
    command[0] = 0x20;
 
    command[1] = 0x61;
 
    command[2] = ah_scaled >> 8;
 
    command[3] = ah_scaled & 0xFF;
 
    command[4] = sgp30_generateCRC(command+2, 2);
 

	
 
    return sgp30_readWordFromCommand(command, 5, 10, 0, 0);
 
}
 

	
 
/**************************************************************************/
 
/*!
 
    @brief  I2C low level interfacing
 
*/
 
/**************************************************************************/
 

	
 

	
 
bool sgp30_readWordFromCommand(uint8_t command[], uint8_t commandLength, uint16_t delayms, uint16_t *readdata, uint8_t readlen)
 
{
 
    i2c_send(_i2caddr, command, commandLength);
 

	
 
    HAL_Delay(delayms);
 

	
 
    if (readlen == 0)
 
    return true;
 

	
 
    uint8_t replylen = readlen * (SGP30_WORD_LEN +1);
 
    uint8_t replybuffer[replylen];
 
    i2c_receive(_i2caddr, replybuffer, replylen);
 

	
 
    for (uint8_t i=0; i<readlen; i++) {
 
    uint8_t crc = sgp30_generateCRC(replybuffer+i*3, 2);
 
    if (crc != replybuffer[i * 3 + 2])
 
      return false;
 
    // success! store it
 
    readdata[i] = replybuffer[i*3];
 
    readdata[i] <<= 8;
 
    readdata[i] |= replybuffer[i*3 + 1];
 
    }
 

	
 
    return true;
 
}
 

	
 
uint8_t sgp30_generateCRC(uint8_t *data, uint8_t datalen)
 
{
 
    // calculates 8-Bit checksum with given polynomial
 
    uint8_t crc = SGP30_CRC8_INIT;
 

	
 
    for (uint8_t i=0; i<datalen; i++) {
 
    crc ^= data[i];
 
    for (uint8_t b=0; b<8; b++) {
 
        if (crc & 0x80)
 
            crc = (crc << 1) ^ SGP30_CRC8_POLYNOMIAL;
 
        else
 
            crc <<= 1;
 
        }
 
    }
 
    return crc;
 
}
 

	
0 comments (0 inline, 0 general)