From a1d3b42d53495a2dc05e6a8831fab26d91fb9f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=C5=BDaitl=C3=ADk?= Date: Sun, 18 Jul 2021 18:52:42 +0200 Subject: [PATCH] Prepared a library fro the sps30 dust sensor. The readout reads some values but the sensor we have is likely damaged. --- fw/Core/Inc/sps30.h | 78 ++++++++++++++++++ fw/Core/Src/main.c | 23 +++++- fw/Core/Src/sps30.c | 193 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 292 insertions(+), 2 deletions(-) create mode 100644 fw/Core/Inc/sps30.h create mode 100644 fw/Core/Src/sps30.c diff --git a/fw/Core/Inc/sps30.h b/fw/Core/Inc/sps30.h new file mode 100644 index 0000000..140b228 --- /dev/null +++ b/fw/Core/Inc/sps30.h @@ -0,0 +1,78 @@ +/* + * sps30.h + * + * Created on: Jul 18, 2021 + * Author: mrs + */ + +#ifndef INC_SPS30_H_ +#define INC_SPS30_H_ + +#include "stdint.h" +#include "stm32l0xx_ll_i2c.h" +#include "stm32l0xx_ll_utils.h" +#include "i2c.h" +#include "crc8.h" + +/* + * Defines & macros + */ + +#define SPS30_I2C_ADDRESS 0x69 + +/* + * Return values + */ + +#define SPS30_OK 0 +#define SPS30_ERROR -1 // generic error +#define SPS30_CRC8_ERROR -2 // checksum failed + +/* + * Data types + */ + +typedef enum { + SPS30_START_MEASUREMENT = 0x0010, + SPS30_STOP_MEASUREMENT = 0x0104, + SPS30_READ_DATA_READY_FLAG = 0x0202, + SPS30_READ_MEASURED_VALUES = 0x0300, + SPS30_SLEEP = 0x1001, + SPS30_WAKE_UP = 0x1103, + SPS30_START_FAN_CLEANING = 0x5607, + SPS30_READ_AUTO_CLEANING_INTERVAL = 0x8004, + SPS30_WRITE_AUTO_CLEANING_INTERVAL = 0x8004, + SPS30_READ_PRODUCT_TYPE = 0xD002, + SPS30_READ_SERIAL_NUMBER = 0xD033, + SPS30_READ_VERSION = 0xD100, + SPS30_READ_DEVICE_STATUS_REGISTER = 0xD206, + SPS30_CLEAR_DEVICE_STATUS_REGISTER = 0xD210, + SPS30_RESET = 0xD304 + +} sps30_cmd_t; + +typedef enum { + SPS30_FLOAT_FORMAT = 0x03, + SPS30_UINT16_FORMAT = 0x05 +} sps30_data_format; + +int8_t sps30_send_cmd(sps30_cmd_t cmd); + +int8_t sps30_start_measurement( void ); +int8_t sps30_stop_measurement( void ); +int8_t sps30_read_measured_values(uint16_t *measured_values, uint8_t measured_values_len); + +int8_t sps30_sleep( void ); +int8_t sps30_wake_up( void ); + +int8_t sps30_start_fan_cleaning( void ); + +int8_t sps30_reset( void ); + +int8_t sps30_read_status_register ( void ); + +int8_t sps30_read_firmware_version ( uint8_t * fw_ver_hi, uint8_t * fw_ver_lo ); + +static uint8_t calculate_crc(uint8_t data[2]); + +#endif /* INC_SPS30_H_ */ diff --git a/fw/Core/Src/main.c b/fw/Core/Src/main.c index ce8927e..bfcf26d 100644 --- a/fw/Core/Src/main.c +++ b/fw/Core/Src/main.c @@ -111,22 +111,41 @@ int main(void) //scd4x_perform_factory_reset(); //LL_mDelay(2000); scd4x_start_periodic_measurement(); + + uint8_t sps30_fw_v_hi, sps30_fw_v_lo; + + sps30_read_firmware_version(&sps30_fw_v_hi, &sps30_fw_v_lo); + + sps30_reset(); + sps30_start_measurement(); LL_mDelay(2000); int CO2, T_SCD4x, RH_SCD4x; int T_SHT4x, RH_SHT4x; + uint16_t sps30_measured_data[10]; + + /* + sps30_start_fan_cleaning(); + LL_mDelay(15000); + */ while (1) { /* RS485 test */ - LL_LPUART_SetDESignalPolarity(LPUART1, 1); + /*LL_LPUART_SetDESignalPolarity(LPUART1, 1); LL_LPUART_TransmitData8(LPUART1, uart_data_dummy); - uart_data_dummy++; + uart_data_dummy++;*/ /* SHT41 measurement */ sht4x_measure(&T_SHT4x, &RH_SHT4x); + LL_mDelay(10); /* SCD4x measurement */ scd4x_read_measurement(&CO2, &T_SCD4x, &RH_SCD4x); + LL_mDelay(10); + /* SPS30 measurement*/ + sps30_read_measured_values(sps30_measured_data, 10); + + /* SLEEP */ LL_mDelay(1000); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ diff --git a/fw/Core/Src/sps30.c b/fw/Core/Src/sps30.c new file mode 100644 index 0000000..b189a4c --- /dev/null +++ b/fw/Core/Src/sps30.c @@ -0,0 +1,193 @@ +/* + * sps30.c + * + * Created on: Jul 18, 2021 + * Author: david + */ + +#include "sps30.h" + +int8_t sps30_send_cmd(sps30_cmd_t cmd) +{ + uint8_t buffer[32]; + uint8_t result; + + // start measurement + buffer[0] = cmd >> 8; + buffer[1] = cmd & 0x00ff; + result = i2c_transmit(SPS30_I2C_ADDRESS<<1, buffer, 2); + // TODO: Proc to vraci NACK? Vyresit. + if (result != I2C_OK) { + return SPS30_ERROR; + } + + return SPS30_OK; +} + +int8_t sps30_start_measurement( void ) +{ + uint8_t i2c_tx_buffer[5]; + uint8_t data_for_crc = {SPS30_UINT16_FORMAT, 0x00}; + + uint8_t result; + + i2c_tx_buffer[0] = SPS30_START_MEASUREMENT >> 8; + i2c_tx_buffer[1] = SPS30_START_MEASUREMENT & 0x00ff; + i2c_tx_buffer[2] = SPS30_UINT16_FORMAT; + i2c_tx_buffer[3] = 0x00; + i2c_tx_buffer[4] = calculate_crc(data_for_crc); + + result = i2c_transmit(SPS30_I2C_ADDRESS<<1, i2c_tx_buffer, 5); + + // TODO: Proc to vraci NACK? Vyresit. + if (result != I2C_OK) { + return SPS30_ERROR; + } + return SPS30_OK; +} + +int8_t sps30_stop_measurement( void ) +{ + return sps30_send_cmd(SPS30_STOP_MEASUREMENT); +} + +int8_t sps30_read_measured_values(uint16_t *measured_values, uint8_t measured_values_len) +{ + + if (measured_values_len != 10) + { + return -5; + } + + uint8_t i2c_tx_buffer[2]; + uint8_t i2c_rx_buffer[30]; + + uint8_t result; + + // start measurement + i2c_tx_buffer[0] = SPS30_READ_MEASURED_VALUES >> 8; + i2c_tx_buffer[1] = SPS30_READ_MEASURED_VALUES & 0x00ff; + result = i2c_transmit(SPS30_I2C_ADDRESS<<1, i2c_tx_buffer, 2); + + // TODO: Proc to vraci NACK? Vyresit. + /*if (result != I2C_OK) { + return SPS30_ERROR; + } + return SPS30_OK;*/ + + LL_mDelay(1); // 10 ms should be enough + // read out + result = i2c_receive(SPS30_I2C_ADDRESS<<1, i2c_rx_buffer, 30); + if (result != I2C_OK) + { + return SPS30_ERROR; + } + + uint8_t checksums[10]; + + uint8_t j = 0; + for (uint8_t i = 0; i < 10; i++) + { + + measured_values[i] = (i2c_rx_buffer[j++] << 8) + i2c_rx_buffer[j++]; + checksums[i] = i2c_rx_buffer[j++]; + } + + return SPS30_OK; +} + +int8_t sps30_sleep( void ) +{ + return sps30_send_cmd(SPS30_SLEEP); +} + +int8_t sps30_wake_up( void ) +{ + return sps30_send_cmd(SPS30_WAKE_UP); + return sps30_send_cmd(SPS30_WAKE_UP); +} + +int8_t sps30_start_fan_cleaning( void ) +{ + return sps30_send_cmd(SPS30_START_FAN_CLEANING); +} + +int8_t sps30_reset( void ) +{ + return sps30_send_cmd(SPS30_RESET); +} + + +int8_t sps30_read_status_register ( void ) +{ + uint8_t i2c_tx_buffer[2]; + uint8_t i2c_rx_buffer[6]; + + uint8_t result; + + // start measurement + i2c_tx_buffer[0] = SPS30_READ_DEVICE_STATUS_REGISTER >> 8; + i2c_tx_buffer[1] = SPS30_READ_DEVICE_STATUS_REGISTER & 0x00ff; + result = i2c_transmit(SPS30_I2C_ADDRESS<<1, i2c_tx_buffer, 2); + + // TODO: Proc to vraci NACK? Vyresit. + /*if (result != I2C_OK) { + return SPS30_ERROR; + } + return SPS30_OK;*/ + + LL_mDelay(1); // 10 ms should be enough + // read out + result = i2c_receive(SPS30_I2C_ADDRESS<<1, i2c_rx_buffer, 6); + + return 0; +} + +int8_t sps30_read_firmware_version ( uint8_t * fw_ver_hi, uint8_t * fw_ver_lo ) +{ + uint8_t i2c_tx_buffer[2]; + uint8_t i2c_rx_buffer[3]; + + uint8_t result; + + // start measurement + i2c_tx_buffer[0] = SPS30_READ_VERSION >> 8; + i2c_tx_buffer[1] = SPS30_READ_VERSION & 0x00ff; + result = i2c_transmit(SPS30_I2C_ADDRESS<<1, i2c_tx_buffer, 2); + + // TODO: Proc to vraci NACK? Vyresit. + /*if (result != I2C_OK) { + return SPS30_ERROR; + } + return SPS30_OK;*/ + + LL_mDelay(1); // 10 ms should be enough + // read out + result = i2c_receive(SPS30_I2C_ADDRESS<<1, i2c_rx_buffer, 3); + /*if (result != I2C_OK) + { + return SPS30_ERROR; + }*/ + + *fw_ver_hi = i2c_rx_buffer[0]; + *fw_ver_lo = i2c_rx_buffer[1]; + + return SPS30_OK; +} + + +static uint8_t calculate_crc(uint8_t data[2]) +{ + uint8_t crc = 0xFF; + for(uint8_t i = 0; i < 2; i++) { + crc ^= data[i]; + for(uint8_t bit = 8; bit > 0; --bit) { + if(crc & 0x80) { + crc = (crc << 1) ^ 0x31u; + } else { + crc = (crc << 1); + } + } + } + return crc; +}