From f94dc549e71aa347f695ac736d7cbc9ee92a38d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=C5=BDaitl=C3=ADk?= Date: Sun, 9 Jan 2022 16:01:48 +0100 Subject: [PATCH] Created library for SGP4x. --- fw/Core/Inc/main.h | 1 + fw/Core/Inc/sgp4x.h | 50 ++++++++++ fw/Core/Src/sgp4x.c | 155 +++++++++++++++++++++++++++++++ fw/iaq_wired_sensor Debug.launch | 2 +- 4 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 fw/Core/Inc/sgp4x.h create mode 100644 fw/Core/Src/sgp4x.c diff --git a/fw/Core/Inc/main.h b/fw/Core/Inc/main.h index b2bec4a..24af6bc 100644 --- a/fw/Core/Inc/main.h +++ b/fw/Core/Inc/main.h @@ -53,6 +53,7 @@ extern "C" { #include "scd4x.h" #include "sht4x.h" #include "sps30.h" +#include "sgp4x.h" #include "modbus.h" #include "config.h" #include "rgbled.h" diff --git a/fw/Core/Inc/sgp4x.h b/fw/Core/Inc/sgp4x.h new file mode 100644 index 0000000..b90b9de --- /dev/null +++ b/fw/Core/Inc/sgp4x.h @@ -0,0 +1,50 @@ +/* + * sgp4x.h + * + * Created on: Jan 9, 2022 + * Author: david + */ + +#ifndef INC_SGP4X_H_ +#define INC_SGP4X_H_ + + +#include "stdint.h" +#include "stm32l0xx_ll_i2c.h" +#include "stm32l0xx_ll_utils.h" +#include "i2c.h" +#include "crc8.h" + +/* + * Defines & macros + */ + +#define SGP4X_I2C_ADDRESS 0x59 +#define SGP4X_MAX_MEAS_DURATION_MS 30 + +/* + * Return values + */ + +#define SGP4X_OK 0 +#define SGP4X_ERROR -1 // generic error +#define SGP4X_CRC8_ERROR -2 // checksum failed + +typedef enum { + SGP4X_MEASURE_RAW_SIGNAL = 0x260F, + SGP4X_EXECUTE_SELF_TEST = 0x280E, + SGP4X_TURN_HEATER_OFF = 0x3615, + SGP4X_GET_SERIAL_NUMBER = 0x3682, + SGP4X_SOFT_RESET = 0x0006 +} sgp4x_cmd_t; + +int8_t sgp4x_send_cmd(sgp4x_cmd_t cmd); + +int8_t sgp4x_measure_raw_signal (uint16_t * voc_ticks); +int8_t sgp4x_measure_raw_signal_compensated (uint16_t * voc_ticks, uint16_t relative_humidity, int16_t temperature); +int8_t sgp4x_execute_self_test ( uint8_t * test_result); +int8_t sgp4x_get_serial_number ( uint8_t serial[6]); +int8_t sgp4x_turn_heater_off ( void ); +int8_t sgp4x_soft_reset ( void ); + +#endif /* INC_SGP4X_H_ */ diff --git a/fw/Core/Src/sgp4x.c b/fw/Core/Src/sgp4x.c new file mode 100644 index 0000000..422dc18 --- /dev/null +++ b/fw/Core/Src/sgp4x.c @@ -0,0 +1,155 @@ +/* + * sgp4x.c + * + * Created on: Jan 9, 2022 + * Author: david + */ + +#include "sgp4x.h" + +int8_t sgp4x_send_cmd(sgp4x_cmd_t cmd) +{ + uint8_t buffer[32]; + int result; + + // start measurement + buffer[0] = cmd >> 8; + buffer[1] = cmd & 0x00ff; + result = i2c_transmit(SGP4X_I2C_ADDRESS<<1, buffer, 2); + if (result == I2C_ERROR_TX_INCOMPLETE) { + return SGP4X_ERROR; + } + + /* Sensirion sensors return NACK after last byte (so NACK at the end is ok) */ + return SGP4X_OK; +} + +int8_t sgp4x_measure_raw_signal (uint16_t * voc_ticks) +{ + uint8_t buffer[32]; + int result; + + // start measurement + buffer[0] = SGP4X_MEASURE_RAW_SIGNAL >> 8; + buffer[1] = SGP4X_MEASURE_RAW_SIGNAL & 0x00ff; + buffer[2] = 0x80; + buffer[3] = 0x00; + buffer[4] = 0xA2; + buffer[5] = 0x66; + buffer[6] = 0x66; + buffer[7] = 0x93; + + result = i2c_transmit(SGP4X_I2C_ADDRESS<<1, buffer, 8); + if (result != I2C_OK) { + return SGP4X_ERROR; + } + + LL_mDelay(SGP4X_MAX_MEAS_DURATION_MS); // 30ms + + result = i2c_receive(SGP4X_I2C_ADDRESS<<1, buffer, 3); + if (result != I2C_OK) + { + return SGP4X_ERROR; + } + + *voc_ticks = (buffer[0] << 8) + buffer[1]; + uint8_t voc_ticks_crc = buffer[2]; + uint8_t crc_correct = crc8_calculate(buffer, 2) == voc_ticks_crc; + if (!crc_correct) { + return SGP4X_CRC8_ERROR; + } + return SGP4X_OK; + +} + +int8_t sgp4x_measure_raw_signal_compensated (uint16_t * voc_ticks, uint16_t relative_humidity, int16_t temperature) +{ + uint8_t buffer[32]; + int result; + + uint16_t rh_ticks = (uint16_t) ((uint32_t)relative_humidity * (uint32_t)(65535/100)); + uint16_t t_ticks = (uint16_t) ((uint32_t)(temperature + 45) * (uint32_t)(65535/175)); + + buffer[0] = SGP4X_MEASURE_RAW_SIGNAL >> 8; + buffer[1] = SGP4X_MEASURE_RAW_SIGNAL & 0x00ff; + buffer[2] = 0x80; + buffer[3] = 0x00; + buffer[4] = crc8_calculate(buffer+2, 2); + buffer[5] = 0x66; + buffer[6] = 0x66; + buffer[7] = crc8_calculate(buffer+5, 2); + + + result = i2c_transmit(SGP4X_I2C_ADDRESS<<1, buffer, 8); + if (result != I2C_OK) { + return SGP4X_ERROR; + } + + LL_mDelay(SGP4X_MAX_MEAS_DURATION_MS); // 30ms + + result = i2c_receive(SGP4X_I2C_ADDRESS<<1, buffer, 3); + if (result != I2C_OK) + { + return SGP4X_ERROR; + } + + *voc_ticks = (buffer[0] << 8) + buffer[1]; + uint8_t voc_ticks_crc = buffer[2]; + uint8_t crc_correct = crc8_calculate(buffer, 2) == voc_ticks_crc; + if (!crc_correct) { + return SGP4X_CRC8_ERROR; + } + return SGP4X_OK; +} + +int8_t sgp4x_execute_self_test ( uint8_t * test_result) +{ + uint8_t buffer[16]; + + scd4x_send_cmd(SGP4X_EXECUTE_SELF_TEST); + i2c_receive(SGP4X_I2C_ADDRESS << 1, buffer, 3); + + test_result = buffer[0]; + uint8_t test_result_crc = buffer[2]; + + uint8_t crc_correct = crc8_calculate(buffer, 2) == test_result_crc; + if (!crc_correct) { + return SGP4X_CRC8_ERROR; + } + return SGP4X_OK; +} + +int8_t sgp4x_get_serial_number ( uint8_t serial[6]) +{ + uint8_t buffer[16]; + + scd4x_send_cmd(SGP4X_GET_SERIAL_NUMBER); + i2c_receive(SGP4X_I2C_ADDRESS << 1, buffer, 9); + + serial[0] = buffer[0]; + serial[1] = buffer[1]; + uint8_t crc_ser01 = buffer[3]; + serial[2] = buffer[4]; + serial[3] = buffer[5]; + uint8_t crc_ser23 = buffer[6]; + serial[4] = buffer[7]; + serial[5] = buffer[8]; + uint8_t crc_ser45 = buffer[9]; + + uint8_t crc_correct = crc8_calculate(buffer, 2) == crc_ser01; + crc_correct &= crc8_calculate(buffer + 3, 2) == crc_ser23; + crc_correct &= crc8_calculate(buffer + 6, 2) == crc_ser45; + if (!crc_correct) { + return SGP4X_CRC8_ERROR; + } + return SGP4X_OK; +} + +int8_t sgp4x_turn_heater_off ( void ) +{ + return sgp4x_send_cmd(SGP4X_TURN_HEATER_OFF); +} +int8_t sgp4x_soft_reset ( void ) +{ + return sgp4x_send_cmd(SGP4X_SOFT_RESET); +} diff --git a/fw/iaq_wired_sensor Debug.launch b/fw/iaq_wired_sensor Debug.launch index 8aa4a98..191079c 100644 --- a/fw/iaq_wired_sensor Debug.launch +++ b/fw/iaq_wired_sensor Debug.launch @@ -58,7 +58,7 @@ - +