Skip to content

Commit 27a163e

Browse files
author
Alexander Wachter
committed
drivers: sensor: ens210: Implement AMS ens210 Sensor
Implementation of AMS (Austria Micro Systems) ENS210 temperature and relative humidity sensor. Signed-off-by: Alexander Wachter <[email protected]>
1 parent 8610b7e commit 27a163e

File tree

7 files changed

+413
-0
lines changed

7 files changed

+413
-0
lines changed

drivers/sensor/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ add_subdirectory_ifdef(CONFIG_BMI160 bmi160)
1515
add_subdirectory_ifdef(CONFIG_BMM150 bmm150)
1616
add_subdirectory_ifdef(CONFIG_CCS811 ccs811)
1717
add_subdirectory_ifdef(CONFIG_DHT dht)
18+
add_subdirectory_ifdef(CONFIG_ENS210 ens210)
1819
add_subdirectory_ifdef(CONFIG_FXAS21002 fxas21002)
1920
add_subdirectory_ifdef(CONFIG_FXOS8700 fxos8700)
2021
add_subdirectory(grove)

drivers/sensor/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ source "drivers/sensor/ccs811/Kconfig"
5555

5656
source "drivers/sensor/dht/Kconfig"
5757

58+
source "drivers/sensor/ens210/Kconfig"
59+
5860
source "drivers/sensor/fxas21002/Kconfig"
5961

6062
source "drivers/sensor/fxos8700/Kconfig"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
zephyr_library()
4+
5+
zephyr_library_sources_ifdef(CONFIG_ENS210 ens210.c)

drivers/sensor/ens210/Kconfig

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Kconfig - ENS210 Digital Temperature and Humidity sensor configuration options
2+
3+
#
4+
# Copyright (c) 2018 Alexander Wachter.
5+
#
6+
# SPDX-License-Identifier: Apache-2.0
7+
#
8+
9+
menuconfig ENS210
10+
bool "ENS210 Digital Temperature and Humidity sensor"
11+
depends on I2C && HAS_DTS_I2C
12+
help
13+
Enable driver for ENS210 Digital Temperature and Humidity sensor.
14+
if ENS210
15+
16+
config ENS210_CRC_CHECK
17+
bool "Enable CRC Check"
18+
default y
19+
help
20+
Check the crc value after data reading.
21+
22+
config ENS210_MAX_STAT_RETRIES
23+
int "Number of status read retries"
24+
default 4
25+
help
26+
Number of retries when status reading failed or device not ready.
27+
28+
config ENS210_MAX_READ_RETRIES
29+
int "Number of value reading retries"
30+
default 4
31+
help
32+
Number of retries when value reading failed, value not valid
33+
or crc not ok.
34+
35+
endif # ENS210

drivers/sensor/ens210/ens210.c

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
/*
2+
* Copyright (c) 2018 Alexander Wachter.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <device.h>
8+
#include <i2c.h>
9+
#include <kernel.h>
10+
#include <misc/byteorder.h>
11+
#include <misc/util.h>
12+
#include <sensor.h>
13+
#include <misc/__assert.h>
14+
#include <logging/log.h>
15+
#include "ens210.h"
16+
17+
#define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL
18+
LOG_MODULE_REGISTER(ENS210);
19+
20+
#ifdef ENS210_CRC_CHECK
21+
u32_t ens210_crc7(u32_t bitstream)
22+
{
23+
u32_t polynomial = (ENS210_CRC7_POLY << (ENS210_CRC7_DATA_WIDTH - 1));
24+
u32_t bit = ENS210_CRC7_DATA_MSB << ENS210_CRC7_WIDTH;
25+
u32_t val = (bitstream << ENS210_CRC7_WIDTH) | ENS210_CRC7_IVEC;
26+
27+
while (bit & (ENS210_CRC7_DATA_MASK << ENS210_CRC7_WIDTH)) {
28+
if (bit & val) {
29+
val ^= polynomial;
30+
}
31+
32+
bit >>= 1;
33+
polynomial >>= 1;
34+
}
35+
36+
return val;
37+
}
38+
#endif /*ENS210_CRC_CHECK*/
39+
40+
static int ens210_sample_fetch(struct device *dev, enum sensor_channel chan)
41+
{
42+
struct ens210_data *drv_data = dev->driver_data;
43+
struct ens210_value_data data[2];
44+
int ret, cnt;
45+
46+
#ifdef ENS210_CRC_CHECK
47+
u32_t temp_valid, humidity_valid;
48+
#endif /*ENS210_CRC_CHECK*/
49+
50+
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
51+
52+
for (cnt = 0; cnt <= CONFIG_ENS210_MAX_READ_RETRIES; cnt++) {
53+
ret = i2c_burst_read(drv_data->i2c, DT_AMS_ENS210_0_BASE_ADDRESS,
54+
ENS210_REG_T_VAL, (u8_t *)&data, sizeof(data));
55+
if (ret < 0) {
56+
LOG_ERR("Failed to read data");
57+
continue;
58+
}
59+
60+
if (!data[0].valid) {
61+
LOG_WRN("Temperature not valid");
62+
continue;
63+
}
64+
65+
if (!data[1].valid) {
66+
LOG_WRN("Humidity not valid");
67+
continue;
68+
}
69+
70+
#ifdef ENS210_CRC_CHECK
71+
temp_valid = data[0].val |
72+
(data[0].valid << (sizeof(data[0].val) * 8));
73+
humidity_valid = data[1].val |
74+
(data[1].valid << (sizeof(data[1].val) * 8));
75+
76+
if (ens210_crc7(temp_valid) != data[0].crc7) {
77+
LOG_WRN("Temperature CRC error");
78+
continue;
79+
}
80+
81+
if (ens210_crc7(humidity_valid) != data[1].crc7) {
82+
LOG_WRN("Humidity CRC error");
83+
continue;
84+
}
85+
#endif /*ENS210_CRC_CHECK*/
86+
87+
drv_data->temp = data[0];
88+
drv_data->humidity = data[1];
89+
90+
return 0;
91+
}
92+
93+
return -EIO;
94+
}
95+
96+
static int ens210_channel_get(struct device *dev,
97+
enum sensor_channel chan,
98+
struct sensor_value *val)
99+
{
100+
struct ens210_data *drv_data = dev->driver_data;
101+
s32_t temp_frac;
102+
s32_t humidity_frac;
103+
104+
switch (chan) {
105+
case SENSOR_CHAN_AMBIENT_TEMP:
106+
/* Temperature is in 1/64 Kelvin. Subtract 273.15 for Celsius */
107+
temp_frac = sys_le16_to_cpu(drv_data->temp.val) * (1000000 / 64);
108+
temp_frac -= 273150000;
109+
110+
val->val1 = temp_frac / 1000000;
111+
val->val2 = temp_frac - val->val1;
112+
break;
113+
case SENSOR_CHAN_HUMIDITY:
114+
humidity_frac = sys_le16_to_cpu(drv_data->humidity.val) *
115+
(1000000 / 512);
116+
val->val1 = humidity_frac / 1000000;
117+
val->val2 = humidity_frac - val->val1;
118+
119+
break;
120+
default:
121+
return -ENOTSUP;
122+
}
123+
124+
return 0;
125+
}
126+
127+
static int ens210_sys_reset(struct device *i2c_dev)
128+
{
129+
const struct ens210_sys_ctrl sys_ctrl = {.low_power = 0, .reset = 1};
130+
int ret;
131+
132+
ret = i2c_reg_write_byte(i2c_dev, DT_AMS_ENS210_0_BASE_ADDRESS,
133+
ENS210_REG_SYS_CTRL, *(u8_t *)&sys_ctrl);
134+
if (ret < 0) {
135+
LOG_ERR("Failed to set SYS_CTRL to 0x%x", *(u8_t *)&sys_ctrl);
136+
}
137+
return ret;
138+
}
139+
140+
static int ens210_sys_enable(struct device *i2c_dev)
141+
{
142+
const struct ens210_sys_ctrl sys_ctrl = {.low_power = 0, .reset = 0};
143+
int ret;
144+
145+
ret = i2c_reg_write_byte(i2c_dev, DT_AMS_ENS210_0_BASE_ADDRESS,
146+
ENS210_REG_SYS_CTRL, *(u8_t *)&sys_ctrl);
147+
if (ret < 0) {
148+
LOG_ERR("Failed to set SYS_CTRL to 0x%x", *(u8_t *)&sys_ctrl);
149+
}
150+
return ret;
151+
}
152+
153+
static int ens210_wait_boot(struct device *i2c_dev)
154+
{
155+
int cnt;
156+
int ret;
157+
struct ens210_sys_stat sys_stat;
158+
159+
for (cnt = 0; cnt <= CONFIG_ENS210_MAX_STAT_RETRIES; cnt++) {
160+
ret = i2c_reg_read_byte(i2c_dev, DT_AMS_ENS210_0_BASE_ADDRESS,
161+
ENS210_REG_SYS_STAT,
162+
(u8_t *)&sys_stat);
163+
164+
if (ret < 0) {
165+
k_sleep(1);
166+
continue;
167+
}
168+
169+
if (sys_stat.sys_active) {
170+
return 0;
171+
}
172+
173+
if (cnt == 0) {
174+
ens210_sys_reset(i2c_dev);
175+
}
176+
177+
ens210_sys_enable(i2c_dev);
178+
179+
k_sleep(2);
180+
}
181+
182+
if (ret < 0) {
183+
LOG_ERR("Failed to read SYS_STATE");
184+
}
185+
186+
187+
LOG_ERR("Sensor is not in active state");
188+
return -EIO;
189+
}
190+
191+
static const struct sensor_driver_api en210_driver_api = {
192+
.sample_fetch = ens210_sample_fetch,
193+
.channel_get = ens210_channel_get,
194+
};
195+
196+
static int ens210_init(struct device *dev)
197+
{
198+
struct ens210_data *drv_data = dev->driver_data;
199+
const struct ens210_sens_run sense_run = {
200+
.t_run = 1,
201+
.h_run = 1
202+
};
203+
const struct ens210_sens_start sense_start = {
204+
.t_start = 1,
205+
.h_start = 1
206+
};
207+
int ret;
208+
u16_t part_id;
209+
210+
drv_data->i2c = device_get_binding(DT_AMS_ENS210_0_BUS_NAME);
211+
if (drv_data->i2c == NULL) {
212+
LOG_ERR("Failed to get pointer to %s device!",
213+
DT_AMS_ENS210_0_BUS_NAME);
214+
return -EINVAL;
215+
}
216+
217+
/* Wait until the device is ready. */
218+
ret = ens210_wait_boot(drv_data->i2c);
219+
if (ret < 0) {
220+
return -EIO;
221+
}
222+
223+
/* Check Hardware ID. This is only possible after device is ready */
224+
ret = i2c_burst_read(drv_data->i2c, DT_AMS_ENS210_0_BASE_ADDRESS,
225+
ENS210_REG_PART_ID, (u8_t *)&part_id,
226+
sizeof(part_id));
227+
if (ret < 0) {
228+
LOG_ERR("Failed to read Part ID register");
229+
return -EIO;
230+
}
231+
232+
if (part_id != ENS210_PART_ID) {
233+
LOG_ERR("Part ID does not match. Want 0x%x, got 0x%x",
234+
ENS210_PART_ID, part_id);
235+
return -EIO;
236+
}
237+
238+
/* Set continuous measurement */
239+
ret = i2c_reg_write_byte(drv_data->i2c, DT_AMS_ENS210_0_BASE_ADDRESS,
240+
ENS210_REG_SENS_RUN, *(u8_t *)&sense_run);
241+
if (ret < 0) {
242+
LOG_ERR("Failed to set SENS_RUN to 0x%x",
243+
*(u8_t *)&sense_run);
244+
return -EIO;
245+
}
246+
247+
/* Start measuring */
248+
ret = i2c_reg_write_byte(drv_data->i2c, DT_AMS_ENS210_0_BASE_ADDRESS,
249+
ENS210_REG_SENS_START, *(u8_t *)&sense_start);
250+
if (ret < 0) {
251+
LOG_ERR("Failed to set SENS_START to 0x%x",
252+
*(u8_t *)&sense_start);
253+
return -EIO;
254+
}
255+
return 0;
256+
}
257+
258+
static struct ens210_data ens210_driver;
259+
260+
DEVICE_AND_API_INIT(ens210, DT_AMS_ENS210_0_LABEL, ens210_init, &ens210_driver,
261+
NULL, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,
262+
&en210_driver_api);

0 commit comments

Comments
 (0)