Skip to content

Commit

Permalink
drivers: sensor: ens210: Implement AMS ens210 Sensor
Browse files Browse the repository at this point in the history
Implementation of AMS (Austria Micro Systems) ENS210 temperature and
relative humidity sensor.

Signed-off-by: Alexander Wachter <alexander.wachter@student.tugraz.at>
  • Loading branch information
Alexander Wachter authored and MaureenHelm committed Apr 19, 2019
1 parent f989f2d commit f29ec12
Show file tree
Hide file tree
Showing 7 changed files with 413 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/sensor/CMakeLists.txt
Expand Up @@ -15,6 +15,7 @@ add_subdirectory_ifdef(CONFIG_BMI160 bmi160)
add_subdirectory_ifdef(CONFIG_BMM150 bmm150)
add_subdirectory_ifdef(CONFIG_CCS811 ccs811)
add_subdirectory_ifdef(CONFIG_DHT dht)
add_subdirectory_ifdef(CONFIG_ENS210 ens210)
add_subdirectory_ifdef(CONFIG_FXAS21002 fxas21002)
add_subdirectory_ifdef(CONFIG_FXOS8700 fxos8700)
add_subdirectory(grove)
Expand Down
2 changes: 2 additions & 0 deletions drivers/sensor/Kconfig
Expand Up @@ -55,6 +55,8 @@ source "drivers/sensor/ccs811/Kconfig"

source "drivers/sensor/dht/Kconfig"

source "drivers/sensor/ens210/Kconfig"

source "drivers/sensor/fxas21002/Kconfig"

source "drivers/sensor/fxos8700/Kconfig"
Expand Down
5 changes: 5 additions & 0 deletions drivers/sensor/ens210/CMakeLists.txt
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: Apache-2.0

zephyr_library()

zephyr_library_sources_ifdef(CONFIG_ENS210 ens210.c)
35 changes: 35 additions & 0 deletions drivers/sensor/ens210/Kconfig
@@ -0,0 +1,35 @@
# Kconfig - ENS210 Digital Temperature and Humidity sensor configuration options

#
# Copyright (c) 2018 Alexander Wachter.
#
# SPDX-License-Identifier: Apache-2.0
#

menuconfig ENS210
bool "ENS210 Digital Temperature and Humidity sensor"
depends on I2C && HAS_DTS_I2C
help
Enable driver for ENS210 Digital Temperature and Humidity sensor.
if ENS210

config ENS210_CRC_CHECK
bool "Enable CRC Check"
default y
help
Check the crc value after data reading.

config ENS210_MAX_STAT_RETRIES
int "Number of status read retries"
default 4
help
Number of retries when status reading failed or device not ready.

config ENS210_MAX_READ_RETRIES
int "Number of value reading retries"
default 4
help
Number of retries when value reading failed, value not valid
or crc not ok.

endif # ENS210
262 changes: 262 additions & 0 deletions drivers/sensor/ens210/ens210.c
@@ -0,0 +1,262 @@
/*
* Copyright (c) 2018 Alexander Wachter.
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <device.h>
#include <i2c.h>
#include <kernel.h>
#include <misc/byteorder.h>
#include <misc/util.h>
#include <sensor.h>
#include <misc/__assert.h>
#include <logging/log.h>
#include "ens210.h"

#define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL
LOG_MODULE_REGISTER(ENS210);

#ifdef ENS210_CRC_CHECK
u32_t ens210_crc7(u32_t bitstream)
{
u32_t polynomial = (ENS210_CRC7_POLY << (ENS210_CRC7_DATA_WIDTH - 1));
u32_t bit = ENS210_CRC7_DATA_MSB << ENS210_CRC7_WIDTH;
u32_t val = (bitstream << ENS210_CRC7_WIDTH) | ENS210_CRC7_IVEC;

while (bit & (ENS210_CRC7_DATA_MASK << ENS210_CRC7_WIDTH)) {
if (bit & val) {
val ^= polynomial;
}

bit >>= 1;
polynomial >>= 1;
}

return val;
}
#endif /*ENS210_CRC_CHECK*/

static int ens210_sample_fetch(struct device *dev, enum sensor_channel chan)
{
struct ens210_data *drv_data = dev->driver_data;
struct ens210_value_data data[2];
int ret, cnt;

#ifdef ENS210_CRC_CHECK
u32_t temp_valid, humidity_valid;
#endif /*ENS210_CRC_CHECK*/

__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);

for (cnt = 0; cnt <= CONFIG_ENS210_MAX_READ_RETRIES; cnt++) {
ret = i2c_burst_read(drv_data->i2c, DT_AMS_ENS210_0_BASE_ADDRESS,
ENS210_REG_T_VAL, (u8_t *)&data, sizeof(data));
if (ret < 0) {
LOG_ERR("Failed to read data");
continue;
}

if (!data[0].valid) {
LOG_WRN("Temperature not valid");
continue;
}

if (!data[1].valid) {
LOG_WRN("Humidity not valid");
continue;
}

#ifdef ENS210_CRC_CHECK
temp_valid = data[0].val |
(data[0].valid << (sizeof(data[0].val) * 8));
humidity_valid = data[1].val |
(data[1].valid << (sizeof(data[1].val) * 8));

if (ens210_crc7(temp_valid) != data[0].crc7) {
LOG_WRN("Temperature CRC error");
continue;
}

if (ens210_crc7(humidity_valid) != data[1].crc7) {
LOG_WRN("Humidity CRC error");
continue;
}
#endif /*ENS210_CRC_CHECK*/

drv_data->temp = data[0];
drv_data->humidity = data[1];

return 0;
}

return -EIO;
}

static int ens210_channel_get(struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
struct ens210_data *drv_data = dev->driver_data;
s32_t temp_frac;
s32_t humidity_frac;

switch (chan) {
case SENSOR_CHAN_AMBIENT_TEMP:
/* Temperature is in 1/64 Kelvin. Subtract 273.15 for Celsius */
temp_frac = sys_le16_to_cpu(drv_data->temp.val) * (1000000 / 64);
temp_frac -= 273150000;

val->val1 = temp_frac / 1000000;
val->val2 = temp_frac - val->val1;
break;
case SENSOR_CHAN_HUMIDITY:
humidity_frac = sys_le16_to_cpu(drv_data->humidity.val) *
(1000000 / 512);
val->val1 = humidity_frac / 1000000;
val->val2 = humidity_frac - val->val1;

break;
default:
return -ENOTSUP;
}

return 0;
}

static int ens210_sys_reset(struct device *i2c_dev)
{
const struct ens210_sys_ctrl sys_ctrl = {.low_power = 0, .reset = 1};
int ret;

ret = i2c_reg_write_byte(i2c_dev, DT_AMS_ENS210_0_BASE_ADDRESS,
ENS210_REG_SYS_CTRL, *(u8_t *)&sys_ctrl);
if (ret < 0) {
LOG_ERR("Failed to set SYS_CTRL to 0x%x", *(u8_t *)&sys_ctrl);
}
return ret;
}

static int ens210_sys_enable(struct device *i2c_dev)
{
const struct ens210_sys_ctrl sys_ctrl = {.low_power = 0, .reset = 0};
int ret;

ret = i2c_reg_write_byte(i2c_dev, DT_AMS_ENS210_0_BASE_ADDRESS,
ENS210_REG_SYS_CTRL, *(u8_t *)&sys_ctrl);
if (ret < 0) {
LOG_ERR("Failed to set SYS_CTRL to 0x%x", *(u8_t *)&sys_ctrl);
}
return ret;
}

static int ens210_wait_boot(struct device *i2c_dev)
{
int cnt;
int ret;
struct ens210_sys_stat sys_stat;

for (cnt = 0; cnt <= CONFIG_ENS210_MAX_STAT_RETRIES; cnt++) {
ret = i2c_reg_read_byte(i2c_dev, DT_AMS_ENS210_0_BASE_ADDRESS,
ENS210_REG_SYS_STAT,
(u8_t *)&sys_stat);

if (ret < 0) {
k_sleep(1);
continue;
}

if (sys_stat.sys_active) {
return 0;
}

if (cnt == 0) {
ens210_sys_reset(i2c_dev);
}

ens210_sys_enable(i2c_dev);

k_sleep(2);
}

if (ret < 0) {
LOG_ERR("Failed to read SYS_STATE");
}


LOG_ERR("Sensor is not in active state");
return -EIO;
}

static const struct sensor_driver_api en210_driver_api = {
.sample_fetch = ens210_sample_fetch,
.channel_get = ens210_channel_get,
};

static int ens210_init(struct device *dev)
{
struct ens210_data *drv_data = dev->driver_data;
const struct ens210_sens_run sense_run = {
.t_run = 1,
.h_run = 1
};
const struct ens210_sens_start sense_start = {
.t_start = 1,
.h_start = 1
};
int ret;
u16_t part_id;

drv_data->i2c = device_get_binding(DT_AMS_ENS210_0_BUS_NAME);
if (drv_data->i2c == NULL) {
LOG_ERR("Failed to get pointer to %s device!",
DT_AMS_ENS210_0_BUS_NAME);
return -EINVAL;
}

/* Wait until the device is ready. */
ret = ens210_wait_boot(drv_data->i2c);
if (ret < 0) {
return -EIO;
}

/* Check Hardware ID. This is only possible after device is ready */
ret = i2c_burst_read(drv_data->i2c, DT_AMS_ENS210_0_BASE_ADDRESS,
ENS210_REG_PART_ID, (u8_t *)&part_id,
sizeof(part_id));
if (ret < 0) {
LOG_ERR("Failed to read Part ID register");
return -EIO;
}

if (part_id != ENS210_PART_ID) {
LOG_ERR("Part ID does not match. Want 0x%x, got 0x%x",
ENS210_PART_ID, part_id);
return -EIO;
}

/* Set continuous measurement */
ret = i2c_reg_write_byte(drv_data->i2c, DT_AMS_ENS210_0_BASE_ADDRESS,
ENS210_REG_SENS_RUN, *(u8_t *)&sense_run);
if (ret < 0) {
LOG_ERR("Failed to set SENS_RUN to 0x%x",
*(u8_t *)&sense_run);
return -EIO;
}

/* Start measuring */
ret = i2c_reg_write_byte(drv_data->i2c, DT_AMS_ENS210_0_BASE_ADDRESS,
ENS210_REG_SENS_START, *(u8_t *)&sense_start);
if (ret < 0) {
LOG_ERR("Failed to set SENS_START to 0x%x",
*(u8_t *)&sense_start);
return -EIO;
}
return 0;
}

static struct ens210_data ens210_driver;

DEVICE_AND_API_INIT(ens210, DT_AMS_ENS210_0_LABEL, ens210_init, &ens210_driver,
NULL, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,
&en210_driver_api);

0 comments on commit f29ec12

Please sign in to comment.