Czujnik SHTC3, STM32+HAL i dość dziwne odczyty

Tu możesz pisać o swoich problemach z pisaniem programów w języku C/C++ dla STM.
Awatar użytkownika
ZbeeGin
User
User
Posty: 399
Rejestracja: sobota 08 lip 2017, 17:16
Lokalizacja: GOP
Kontaktowanie:

Czujnik SHTC3, STM32+HAL i dość dziwne odczyty

Postautor: ZbeeGin » wtorek 03 gru 2019, 18:44

Dziwna sprawa.

Mam w projekcie czujnik MEMS SHTC3 (wersja 3V). Połączony magistralą I²C z STM32F072. Jest to jeden z elementów ściennego panela sterującego.
I²C mam skonfigurowane tak: 7-bitów adresu, SCL 100kHz (Choć czujnik może pracować też z 400kHz), nie wykorzystuję Clock Stretching-u. Szyna ma podciągnięcie 3k3 z 3.3V. Czujnik jest prawie przy procesorze więc ścieżki są krótkie. Ma swój własny kondensator 1µ na VDD.

Przewertowałem DS-kę https://www.mouser.com/datasheet/2/682/ ... 323493.pdf i zaadaptowałem blokującą transmisję z pollingiem wykorzystując to co dostarcza HAL.

Wszystko wskazuje na to, że czujnik poprawnie odpowiada na Moje datagramy. Mogę na przykład wysłać zamiast polecenia pomiaru prośbę o ID i zwraca mi wartość 2119 (1000 0100 0111) co odpowiada prawidłowemu "patternowi" w DS-ce (tabelka 15).

Niestety pomiary są dość dziwne. W warsztacie, gdzie tuż obok specjalnie stoi stacja pogodowa czujnik zwraca wartość np. 23% RH po przeliczeniu z 16-bitów na realną wartość (patrz DS i kod); gdzie stacja wyraźnie wskazuje 35-38% RH. Dzień wcześniej jak wg stacji było 42-43% to czujnik zwracał 33%.
Jeśli prześledzę parę odczytów na debugu - bezpośrednio podglądając bufor hum_buf[] - to widać, że za każdym odczytem wartości RAW się nieco zmieniają w okolicy przeliczonej wartości. Na przykład:

60, 152, 7, 108, 207, 9 -> RH = 23,66%
60, 34, 99, 108, 198, 129 -> RH = 23,48%
61, 10, 168, 108, 103, 76 -> RH = 23,84%


(Dla analizujących: Pierwsze dwa bajty to RH: MSB, LSB; potem CRC tej wartości; następnie Temp: MSB, LSB i też CRC tegoż).

Teraz zastanawiam się, czy:
1. Coś przeoczyłem w kodzie.
2. Coś w dokumentacji jest nie tak.
3. Czujniki są do D.

AD.2. Ciekawostka: Jeśli potraktujemy wartości Temp jako RH to po przeliczeniu jest... w miarę OK. RH na poziomie ok. 42%. Co więcej, też temperatura też jest zbliżona do wartości ze stacji pogodowej... :shock:


Kod. Okrojony tylko do najistotniejszych elementów:

Code: Select all

/* Tylko fragment z czytaniem i przeliczaniem czujników
* Temperatura - NTC10K / ADC - działa prawidłowo
* Wilgotność - SHTC3 / I2C - pokazuje raczej gupoty
*/

/**
* @brief Sensor state
*/
typedef enum {
SENSOR_IDLE, /* No action at this time - sleep mode */
SENSOR_ACTIVE, /* Sensor is wake-up and ready to cmd */
SENSOR_BUSY, /* Sensor during measure */
SENSOR_READY /* Data from sensor is ready */
} SensorStateTypeDef;

/**
* @brief Structure holds environment data
*/
typedef struct {
uint16_t actual; /* Actual value in final units */
uint16_t raw; /* Raw data from sensor */
SensorStateTypeDef state; /* Machine state */
} SensorDataTypeDef;

/**
* @brief Crc calculation
*/
typedef enum {
CRC_OK, /* CRC value is ok */
CRC_INVALID, /* CRC value is wrong */
} CRCResultTypeDef;

/**
* @brief SHTC I2C Address
*/
const uint8_t SHT_ADDR = (0x70 << 1); /* SHTC base address */

/**
* @brief SHTC Commands
* @info !!! Beware of swapped MSB<->LSB (Other endianes in ARM) !!!
* @info Trick to make a pointer to command value in HAL transactions
*/
const uint16_t SHT_ID = 0xc8ef; /* Get sensor ID command */
const uint16_t SHT_SLEEP = 0x98b0; /* Sleep command */
const uint16_t SHT_WAKEUP = 0x1735; /* Wakeup command */
const uint16_t SHT_READ_RH_N = 0xe058; /* RH First Normal No Clk str */
const uint16_t SHT_READ_T_N = 0x6678; /* T First Normal No Clk str */
const uint16_t SHT_READ_RH_NC = 0x245c; /* RH First Normal Clk str */
const uint16_t SHT_READ_T_NC = 0xa27c; /* T First Normal Clk str */
const uint16_t SHT_READ_RH_L = 0x1a40; /* RH First Low pow No Clk Str */
const uint16_t SHT_READ_T_L = 0x9c60; /* T First Low pow No Clk Str */
const uint16_t SHT_READ_RH_LC = 0xde44; /* RH First Low pow Clk stretch */
const uint16_t SHT_READ_T_LC = 0x5864; /* T First Low pow Clk stretch */
const uint16_t SHT_SOFT_RST = 0x5d80; /* Soft reset command */

/**
* @brief SHTC CRC Check Polynominal
*/
const uint16_t SHT_CRC_POLY = 0x131; /* x^8+x^5+x^4+1 = 1 0011 0001 */

/**
* @brief Temperature read in 0,1 degrees
*/
static SensorDataTypeDef temperature = { 210, 0, SENSOR_IDLE };

/**
* @brief Humidity read
*/
static SensorDataTypeDef humidity = { 55, 0, SENSOR_IDLE };
static uint8_t hum_buf[6] = { 0 };

/**
* @brief Calculate CRC of SHTC transaction
* @param data : incoming data buffer
* @param nbytes : number of bytes to calc
* @param checksum : received CRC
* @retval CRC_OK when values are equal, CRC_INVALID in other case
*/
static CRCResultTypeDef SHTC_check_crc(uint8_t* data, uint8_t nbytes, uint8_t checksum) {

uint8_t bit; /* bit mask */
uint8_t crc = 0xFF; /* calculated checksum */
uint8_t bytecnt; /* byte counter */

/* Calculates 8-Bit checksum with given polynomial */
for(bytecnt = 0; bytecnt < nbytes; bytecnt++) {
crc ^= (data[bytecnt]);
for(bit = 8; bit > 0; --bit) {
if(crc & 0x80) {
crc = (crc << 1) ^ SHT_CRC_POLY;
} else {
crc = (crc << 1);
}
}
}

/* Verify checksum with given value */
if(crc != checksum) {
return CRC_INVALID;
} else {
return CRC_OK;
}
}

/**
* @brief Calculate temperature to real values
* @param raw : incoming 16bit data
* @retval Real temperature
*/
static float SHTC_calc_temperature(uint16_t raw) {
/* Calculate temperature [^C] */
/* T = -45 + 175 * rawValue / 2^16 */
return (175 * (float)raw / 65536.0f - 45.0f);
}

/**
* @brief Calculate humidity to real values
* @param raw : incoming 16bit data
* @retval Real humidity
*/
static float SHTC_calc_humidity(uint16_t raw) {
/* Calculate relative humidity [%RH] */
/* RH = rawValue / 2^16 * 100 */
return (100 * (float)raw / 65536.0f);
}

/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* MCU Configuration -------------------------------------------------------*/

/* Infinite loop -----------------------------------------------------------*/
while(1) {

/* === Feed the puppy ===*/
HAL_IWDG_Refresh(&hiwdg);

/* === Measure task === */
/* Temperature machine state */
if(temperature.state == SENSOR_READY) {
/* Conversion done */
calculate_degree();
temperature.state = SENSOR_IDLE;
}

if(temperature.state == SENSOR_BUSY) {
/* Check the finish flag */
if(HAL_ADC_PollForConversion(&hadc, 1) == HAL_OK) {
temperature.raw = HAL_ADC_GetValue(&hadc);
temperature.state = SENSOR_READY;
}
}

/* Humidity machine state */
if(humidity.state == SENSOR_BUSY) {
/* SHTC might be ready to read measurement result, so ask it for */
/* If NACK the result IMO will be HAL_ERROR */
if(HAL_I2C_Master_Receive(&hi2c2, SHT_ADDR, hum_buf, 6, 1) == HAL_OK) {
humidity.state = SENSOR_READY;
/* TODO: Remove this LED debug */
led_data.state[3] = ON;
}
}

if(humidity.state == SENSOR_READY) {
/* Now we can read the data */
humidity.raw = ((uint16_t)(hum_buf[0] << 8) | (uint16_t)hum_buf[1]);
/* And recalculate 16 bit value to real value */
if(SHTC_check_crc(&hum_buf[0], 2, hum_buf[2]) == CRC_OK) {
/* TODO: Remove this LED debug */
led_data.state[4] = ON;
humidity.actual = (uint16_t)(SHTC_calc_humidity(humidity.raw));
/* TODO: Remove: Read ID by PLC control panel
* humidity.actual = humidity.raw);
*/
}

/* SHTC may go to sleep */
if(HAL_I2C_Master_Transmit(&hi2c2, SHT_ADDR, (uint8_t *)&SHT_SLEEP, 2, 1) == HAL_OK) {
humidity.state = SENSOR_IDLE;
}
/* TODO: Remove this LED debug */
led_data.state[5] = ON;
}

if(humidity.state == SENSOR_ACTIVE) {
/* SHTC is active and ready to measure */
/* If the sensor respond, we can assume we can poll it in next state */
if(HAL_I2C_Master_Transmit(&hi2c2, SHT_ADDR, (uint8_t *)&SHT_READ_RH_N, 2, 1) == HAL_OK) {
/* TODO: Remove: Only to check SHTC3 ID - return 2119 wich seems good value
* if(HAL_I2C_Master_Transmit(&hi2c2, SHT_ADDR, (uint8_t *)&SHT_ID, 2, 1) == HAL_OK) {
*/
humidity.state = SENSOR_BUSY;
/* TODO: Remove this LED debug */
led_data.state[2] = ON;
}
}

/* Measure trigger */
if(scheduler_timer == ACQ_SCHEDULE) {

if(temperature.state == SENSOR_IDLE) {
/* ADC is free so start conversion */
temperature.state = SENSOR_BUSY;
HAL_ADC_Start(&hadc);
}

if(humidity.state == SENSOR_IDLE) {
/* TODO: Remove this LED debug */
led_data.state[0] = ON;
/* SHTC is sleep so wake it up with WAKEUP command */
/* If the sensor respond, we can assume it will be active in next state */
if(HAL_I2C_Master_Transmit(&hi2c2, SHT_ADDR, (uint8_t *)&SHT_WAKEUP, 2, 1) == HAL_OK) {
humidity.state = SENSOR_ACTIVE;
led_data.state[1] = ON;
}
}
}
/* === End measure === */

/* === Analyze incoming data if no error === */

/* === Send data if there is a time for it === */
/* Tu kasuje też stan LED do następnego wyzwolenia pomiarów */

/* === Keyboard special events task === */

}
}


Info co do kodu:
- LEDowy Debug Live na linijce 8 ledów wskazuje, że cała moja maszyna stanów przechodzi przez kolejne stany.
- Procedury przeliczenia CRC, temperatury i wilgotności pochodzą z kodów udostępnionych przez Sensirion.
- Nie zejdźcie też z tego padołu jak przeczytacie kod. Wiem, że częściowo jest po Japońsku.

Wróć do „Programowanie STM w C/C++”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 1 gość