commit a0ed61200d27028b93251ef5f8d4d04f96860c4f Author: Jan Mrna Date: Sun Jun 12 22:53:17 2022 +0200 Added files (not yet modified) diff --git a/sgp40.c b/sgp40.c new file mode 100644 index 0000000..d5c65d0 --- /dev/null +++ b/sgp40.c @@ -0,0 +1,186 @@ +/* + * SGP40.c + * + * Created on: Jan 9, 2022 + * Author: david + */ + +#include +#include "main.h" /* for uart_disable_interrupts() */ + +int8_t sgp40_send_cmd(sgp40_cmd_t cmd) +{ + uint8_t buffer[32]; + int result; + + // start measurement + buffer[0] = cmd >> 8; + buffer[1] = cmd & 0x00ff; + uart_disable_interrupts(); + result = i2c_transmit(SGP40_I2C_ADDRESS<<1, buffer, 2); + uart_enable_interrupts(); + if (result == I2C_ERROR_TX_INCOMPLETE) { + return SGP40_ERROR; + } + + /* Sensirion sensors return NACK after last byte (so NACK at the end is ok) */ + return SGP40_OK; +} + +int8_t sgp40_measure_raw_signal(uint16_t * voc_ticks) +{ + uint8_t buffer[32]; + int result; + + /* Start measurement */ + buffer[0] = 0x26; + buffer[1] = 0x0F; + buffer[2] = 0x80; + buffer[3] = 0x00; + buffer[4] = 0xA2; + buffer[5] = 0x66; + buffer[6] = 0x66; + buffer[7] = 0x93; + + /* Returns NACK if CRC is wrong */ + uart_disable_interrupts(); + result = i2c_transmit(SGP40_I2C_ADDRESS<<1, buffer, 8); + uart_enable_interrupts(); + if (result != I2C_OK) { + return SGP40_ERROR; + } + + LL_mDelay(SGP40_MAX_MEAS_DURATION_MS); // 30ms + + uart_disable_interrupts(); + result = i2c_receive(SGP40_I2C_ADDRESS<<1, buffer, 3); + uart_enable_interrupts(); + if (result != I2C_OK) + { + return SGP40_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 SGP40_CRC8_ERROR; + } + return SGP40_OK; + +} + +int8_t sgp40_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 * 65535 / 100); + uint16_t t_ticks = (uint16_t)(((uint32_t)temperature/10 + 45) * 65535 / 175); + + buffer[0] = SGP40_MEASURE_RAW_SIGNAL >> 8; + buffer[1] = SGP40_MEASURE_RAW_SIGNAL & 0x00ff; + buffer[2] = (uint8_t)(rh_ticks >> 8); + buffer[3] = (uint8_t)rh_ticks; + buffer[4] = crc8_calculate(buffer+2, 2); + buffer[5] = (uint8_t)(t_ticks >> 8); + buffer[6] = (uint8_t)t_ticks; + buffer[7] = crc8_calculate(buffer+5, 2); + + /* Returns NACK if CRC is wrong */ + uart_disable_interrupts(); + result = i2c_transmit(SGP40_I2C_ADDRESS<<1, buffer, 8); + uart_enable_interrupts(); + if (result != I2C_OK) { + return SGP40_ERROR; + } + + LL_mDelay(SGP40_MAX_MEAS_DURATION_MS); // 30ms + + uart_disable_interrupts(); + result = i2c_receive(SGP40_I2C_ADDRESS<<1, buffer, 3); + uart_enable_interrupts(); + if (result != I2C_OK) + { + return SGP40_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 SGP40_CRC8_ERROR; + } + return SGP40_OK; +} + +int8_t SGP40_execute_self_test ( uint8_t * test_result) +{ + uint8_t buffer[16]; + int8_t result; + + result = sgp40_send_cmd(SGP40_EXECUTE_SELF_TEST); + if (result != I2C_OK) { + return SGP40_ERROR; + } + + LL_mDelay(350); + + uart_disable_interrupts(); + result = i2c_receive(SGP40_I2C_ADDRESS << 1, buffer, 3); + uart_enable_interrupts(); + if (result != I2C_OK) { + return SGP40_ERROR; + } + + 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 SGP40_CRC8_ERROR; + } + return SGP40_OK; +} + +int8_t SGP40_get_serial_number(uint8_t serial[6]) +{ + uint8_t buffer[16]; + + uart_disable_interrupts(); + sgp40_send_cmd(SGP40_GET_SERIAL_NUMBER); + uart_enable_interrupts(); + + LL_mDelay(5); + + uart_disable_interrupts(); + i2c_receive(SGP40_I2C_ADDRESS << 1, buffer, 9); + uart_enable_interrupts(); + + 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 SGP40_CRC8_ERROR; + } + return SGP40_OK; +} + +int8_t SGP40_turn_heater_off(void) +{ + return sgp40_send_cmd(SGP40_TURN_HEATER_OFF); +} +int8_t SGP40_soft_reset(void) +{ + return sgp40_send_cmd(SGP40_SOFT_RESET); +} diff --git a/sgp40.h b/sgp40.h new file mode 100644 index 0000000..e4faf01 --- /dev/null +++ b/sgp40.h @@ -0,0 +1,50 @@ +/* + * sgp4x.h + * + * Created on: Jan 9, 2022 + * Author: david + */ + +#ifndef INC_SGP40_H_ +#define INC_SGP40_H_ + + +#include "stdint.h" +#include "stm32l0xx_ll_i2c.h" +#include "stm32l0xx_ll_utils.h" +#include "i2c.h" +#include "crc8.h" + +/* + * Defines & macros + */ + +#define SGP40_I2C_ADDRESS 0x59 +#define SGP40_MAX_MEAS_DURATION_MS 50 + +/* + * Return values + */ + +#define SGP40_OK 0 +#define SGP40_ERROR -1 // generic error +#define SGP40_CRC8_ERROR -2 // checksum failed + +typedef enum { + SGP40_MEASURE_RAW_SIGNAL = 0x260F, + SGP40_EXECUTE_SELF_TEST = 0x280E, + SGP40_TURN_HEATER_OFF = 0x3615, + SGP40_GET_SERIAL_NUMBER = 0x3682, + SGP40_SOFT_RESET = 0x0006 +} sgp40_cmd_t; + +int8_t sgp40_send_cmd(sgp40_cmd_t cmd); + +int8_t sgp40_measure_raw_signal (uint16_t * voc_ticks); +int8_t sgp40_measure_raw_signal_compensated (uint16_t * voc_ticks, uint16_t relative_humidity, int16_t temperature); +int8_t sgp40_execute_self_test ( uint8_t * test_result); +int8_t sgp40_get_serial_number ( uint8_t serial[6]); +int8_t sgp40_turn_heater_off ( void ); +int8_t sgp40_soft_reset ( void ); + +#endif /* INC_SGP40_H_ */