1
0
Fork 0
forked from nuttx/nuttx-update

drivers/sensors/sgp30.c: Reset I2C in case init msg takes too long

We have experimentally found out that this change helps with somewhat quirky CO2/VOC-sensor.
This commit is contained in:
Juha Niskanen 2020-03-05 08:01:43 -06:00 committed by Gregory Nutt
parent b4d85d6498
commit 83b6953624
2 changed files with 94 additions and 6 deletions

View file

@ -72,6 +72,8 @@
#endif
#define SGP30_I2C_RETRIES 3
#define SGP30_INIT_RETRIES 5
#define SGP30_INIT_LIMIT_MS 10
/****************************************************************************
* Private
@ -374,6 +376,29 @@ static uint8_t sgp30_crc_word(uint16_t word)
return crc;
}
/****************************************************************************
* Name: sgp30_soft_reset
****************************************************************************/
static int sgp30_soft_reset(FAR struct sgp30_dev_s *priv)
{
struct i2c_msg_s msg[1];
uint8_t buf[1];
int ret = 0;
buf[0] = CONFIG_SGP30_RESET_SECOND_BYTE;
msg[0].frequency = CONFIG_SGP30_I2C_FREQUENCY;
msg[0].addr = CONFIG_SGP30_RESET_ADDR;
msg[0].flags = 0;
msg[0].buffer = buf;
msg[0].length = 1;
ret = sgp30_do_transfer(priv->i2c, msg, 1);
return (ret >= 0) ? OK : ret;
}
/****************************************************************************
* Name: sgp30_set_command_param
****************************************************************************/
@ -424,16 +449,39 @@ static int sgp30_check_data_crc(FAR const struct sgp30_word_s *words,
*
****************************************************************************/
static bool has_time_passed(struct timespec curr,
struct timespec start,
static bool has_time_passed(FAR struct timespec *curr,
FAR struct timespec *start,
unsigned int secs_since_start)
{
if ((long)((start.tv_sec + secs_since_start) - curr.tv_sec) == 0)
if ((long)((start->tv_sec + secs_since_start) - curr->tv_sec) == 0)
{
return start.tv_nsec <= curr.tv_nsec;
return start->tv_nsec <= curr->tv_nsec;
}
return (long)((start.tv_sec + secs_since_start) - curr.tv_sec) <= 0;
return (long)((start->tv_sec + secs_since_start) - curr->tv_sec) <= 0;
}
/****************************************************************************
* Name: time_has_passed_ms
*
* Description:
* Return true if curr >= start + msecs_since_start
*
****************************************************************************/
static bool time_has_passed_ms(FAR struct timespec *curr,
FAR struct timespec *start,
unsigned int msecs_since_start)
{
uint32_t start_msec = start->tv_nsec / (1000 * 1000);
uint32_t curr_msec = curr->tv_nsec / (1000 * 1000);
if (start->tv_sec < curr->tv_sec)
{
curr_msec += 1000 * ((long)(curr->tv_sec - start->tv_sec));
}
return (start_msec + msecs_since_start) <= curr_msec;
}
/****************************************************************************
@ -536,6 +584,7 @@ static int sgp30_open(FAR struct file *filep)
sgp30_read_cmd(priv, SGP30_CMD_GET_FEATURE_SET_VERSION, buf, 1) >= 0
&& sgp30_check_data_crc(buf, 1) >= 0)
{
struct timespec start, curr;
sgp30_dbg("serial id: %04x-%04x-%04x\n",
sgp30_data_word_to_uint16(serial + 0),
sgp30_data_word_to_uint16(serial + 1),
@ -546,12 +595,46 @@ static int sgp30_open(FAR struct file *filep)
add_sensor_randomness((buf[0].crc << 24) ^ (serial[0].crc << 16) ^
(serial[1].crc << 8) ^ (serial[2].crc << 0));
clock_gettime(CLOCK_REALTIME, &start);
ret = sgp30_write_cmd(priv, SGP30_CMD_INIT_AIR_QUALITY, NULL, 0);
if (ret < 0)
{
sgp30_dbg("sgp30_write_cmd(SGP30_CMD_INIT_AIR_QUALITY) failed, %d\n",
ret);
}
else
{
uint32_t repeat = SGP30_INIT_RETRIES;
clock_gettime(CLOCK_REALTIME, &curr);
sgp30_dbg("sgp30_write_cmd(SGP30_CMD_INIT_AIR_QUALITY)\n");
while (repeat-- && time_has_passed_ms(&curr, &start, SGP30_INIT_LIMIT_MS))
{
/* Infrequently the SGP30_CMD_INIT_AIR_QUALITY message delivery
* takes suspiciously long time (SGP30_INIT_LIMIT_MS or more) and
* in these cases the TVOC values will never reach the correct
* level (not even after 24 hours).
* If this delay is detected, the sensor is given a "General Call"
* soft reset as described in the SGP30 datasheet and initialization
* is tried again after CONFIG_SGP30_RESET_DELAY_US.
*/
ret = sgp30_soft_reset(priv);
if (ret < 0)
{
sgp30_dbg("sgp30_soft_reset failed, %d\n", ret);
return ret;
}
nxsig_usleep(CONFIG_SGP30_RESET_DELAY_US);
clock_gettime(CLOCK_REALTIME, &start);
ret = sgp30_write_cmd(priv, SGP30_CMD_INIT_AIR_QUALITY, NULL, 0);
clock_gettime(CLOCK_REALTIME, &curr);
if (ret < 0)
{
sgp30_dbg("sgp30_write_cmd(SGP30_CMD_INIT_AIR_QUALITY) failed, %d\n", ret);
}
}
}
}
else
{
@ -646,7 +729,7 @@ static ssize_t sgp30_read(FAR struct file *filep, FAR char *buffer,
clock_gettime(CLOCK_REALTIME, &ts);
while (!has_time_passed(ts, priv->last_update, 1))
while (!has_time_passed(&ts, &priv->last_update, 1))
{
if (filep->f_oflags & O_NONBLOCK)
{

View file

@ -47,6 +47,11 @@
#define CONFIG_SGP30_ADDR 0x58
#define CONFIG_SGP30_RESET_ADDR 0x00
#define CONFIG_SGP30_RESET_SECOND_BYTE 0x06
#define CONFIG_SGP30_RESET_DELAY_US 300000
/****************************************************************************
* Public Types
****************************************************************************/