Firmware SDK
twr_hts221.c
1 #include <twr_hts221.h>
2 
3 #define HTS221_WHO_AM_I 0x0F
4 #define HTS221_WHO_AM_I_RESULT 0xBC
5 #define HTS221_AV_CONF 0x10
6 #define HTS221_CTRL_REG1 0x20
7 #define HTS221_CTRL_REG2 0x21
8 #define HTS221_CTRL_REG3 0x22
9 #define HTS221_STATUS_REG 0x27
10 #define HTS221_HUMIDITY_OUT_L 0x28
11 #define HTS221_HUMIDITY_OUT_H 0x29
12 #define HTS221_TEMP_OUT_L 0x2A
13 #define HTS221_TEMP_OUT_H 0x2B
14 #define HTS221_CALIB_OFFSET 0x30
15 #define HTS221_CALIB_0 0x30
16 #define HTS221_CALIB_1 0x31
17 #define HTS221_CALIB_2 0x32
18 #define HTS221_CALIB_3 0x33
19 #define HTS221_CALIB_4 0x34
20 #define HTS221_CALIB_5 0x35
21 #define HTS221_CALIB_6 0x36
22 #define HTS221_CALIB_7 0x37
23 #define HTS221_CALIB_8 0x38
24 #define HTS221_CALIB_9 0x39
25 #define HTS221_CALIB_A 0x3A
26 #define HTS221_CALIB_B 0x3B
27 #define HTS221_CALIB_C 0x3C
28 #define HTS221_CALIB_D 0x3D
29 #define HTS221_CALIB_E 0x3E
30 #define HTS221_CALIB_F 0x3F
31 #define HTS221_BIT_PD 0x80
32 #define HTS221_BIT_BDU 0x04
33 #define HTS221_BIT_ONE_SHOT 0x01
34 #define HTS221_BIT_T_DA 0x01
35 #define HTS221_BIT_H_DA 0x02
36 #define HTS221_MASK_ODR 0x03
37 #define HTS221_ODR_ONE_SHOT 0x00
38 #define HTS221_ODR_1_HZ 0x01
39 #define HTS221_ODR_7_HZ 0x02
40 #define HTS221_ODR_12_HZ 0x03
41 
42 // TODO Clarify timing with ST
43 #define _TWR_HTS221_DELAY_RUN 50
44 #define _TWR_HTS221_DELAY_INITIALIZATION 50
45 #define _TWR_HTS221_DELAY_MEASUREMENT 50
46 
47 static void _twr_hts221_task_interval(void *param);
48 
49 static void _twr_hts221_task_measure(void *param);
50 
51 static bool _twr_hts221_load_calibration(twr_hts221_t *self);
52 
53 void twr_hts221_init(twr_hts221_t *self, twr_i2c_channel_t i2c_channel, uint8_t i2c_address)
54 {
55  memset(self, 0, sizeof(*self));
56 
57  self->_i2c_channel = i2c_channel;
58  self->_i2c_address = i2c_address;
59 
60  self->_task_id_interval = twr_scheduler_register(_twr_hts221_task_interval, self, TWR_TICK_INFINITY);
61  self->_task_id_measure = twr_scheduler_register(_twr_hts221_task_measure, self, _TWR_HTS221_DELAY_RUN);
62 
63  self->_tick_ready = _TWR_HTS221_DELAY_RUN;
64 
65  twr_i2c_init(self->_i2c_channel, TWR_I2C_SPEED_400_KHZ);
66 
67  // TODO This delays initialization, should be part of state machine
68  _twr_hts221_load_calibration(self);
69 }
70 
72 {
73  uint8_t ctrl_reg1;
74  if (!twr_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG1, &ctrl_reg1))
75  {
76  ctrl_reg1 &= ~HTS221_BIT_PD;
77  twr_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG1, ctrl_reg1);
78  }
79 
80  twr_scheduler_unregister(self->_task_id_interval);
81  twr_scheduler_unregister(self->_task_id_measure);
82 }
83 
84 void twr_hts221_set_event_handler(twr_hts221_t *self, void (*event_handler)(twr_hts221_t *, twr_hts221_event_t, void *), void *event_param)
85 {
86  self->_event_handler = event_handler;
87  self->_event_param = event_param;
88 }
89 
91 {
92  self->_update_interval = interval;
93 
94  if (self->_update_interval == TWR_TICK_INFINITY)
95  {
96  twr_scheduler_plan_absolute(self->_task_id_interval, TWR_TICK_INFINITY);
97  }
98  else
99  {
100  twr_scheduler_plan_relative(self->_task_id_interval, self->_update_interval);
101 
102  twr_hts221_measure(self);
103  }
104 }
105 
107 {
108  if (self->_measurement_active)
109  {
110  return false;
111  }
112 
113  self->_measurement_active = true;
114 
115  twr_scheduler_plan_absolute(self->_task_id_measure, self->_tick_ready);
116 
117  return true;
118 }
119 
120 bool twr_hts221_get_humidity_percentage(twr_hts221_t *self, float *percentage)
121 {
122  if (!self->_humidity_valid)
123  {
124  return false;
125  }
126 
127  *percentage = self->_h0_rh + ((self->_reg_humidity - self->_h0_t0_out) * self->_h_grad);
128 
129  if (*percentage >= 100.f)
130  {
131  *percentage = 100.f;
132  }
133 
134  return true;
135 }
136 
137 static void _twr_hts221_task_interval(void *param)
138 {
139  twr_hts221_t *self = param;
140 
141  twr_hts221_measure(self);
142 
143  twr_scheduler_plan_current_relative(self->_update_interval);
144 }
145 
146 static void _twr_hts221_task_measure(void *param)
147 {
148  twr_hts221_t *self = param;
149 
150 start:
151 
152  switch (self->_state)
153  {
154  case TWR_HTS221_STATE_ERROR:
155  {
156  self->_humidity_valid = false;
157 
158  self->_measurement_active = false;
159 
160  if (self->_event_handler != NULL)
161  {
162  self->_event_handler(self, TWR_HTS221_EVENT_ERROR, self->_event_param);
163  }
164 
165  self->_state = TWR_HTS221_STATE_INITIALIZE;
166 
167  return;
168  }
169  case TWR_HTS221_STATE_INITIALIZE:
170  {
171  self->_state = TWR_HTS221_STATE_ERROR;
172 
173  uint8_t ctrl_reg1;
174 
175  if (!twr_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG1, &ctrl_reg1))
176  {
177  goto start;
178  }
179 
180  ctrl_reg1 &= ~HTS221_BIT_PD;
181 
182  if (!twr_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG1, ctrl_reg1))
183  {
184  goto start;
185  }
186 
187  self->_state = TWR_HTS221_STATE_MEASURE;
188 
189  self->_tick_ready = twr_tick_get() + _TWR_HTS221_DELAY_INITIALIZATION;
190 
191  if (self->_measurement_active)
192  {
193  twr_scheduler_plan_current_absolute(self->_tick_ready);
194  }
195 
196  return;
197  }
198  case TWR_HTS221_STATE_MEASURE:
199  {
200  self->_state = TWR_HTS221_STATE_ERROR;
201 
202  uint8_t ctrl_reg1;
203 
204  if (!twr_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG1, &ctrl_reg1))
205  {
206  goto start;
207  }
208 
209  ctrl_reg1 |= HTS221_BIT_PD;
210 
211  if (!twr_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG1, ctrl_reg1))
212  {
213  goto start;
214  }
215 
216  if (!twr_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG1, HTS221_BIT_PD | HTS221_BIT_BDU))
217  {
218  goto start;
219  }
220 
221  if (!twr_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG2, HTS221_BIT_ONE_SHOT))
222  {
223  goto start;
224  }
225 
226  self->_state = TWR_HTS221_STATE_READ;
227 
228  twr_scheduler_plan_current_from_now(_TWR_HTS221_DELAY_MEASUREMENT);
229 
230  return;
231  }
232  case TWR_HTS221_STATE_READ:
233  {
234  self->_state = TWR_HTS221_STATE_ERROR;
235 
236  uint8_t reg_status;
237  uint8_t retval[2];
238  uint8_t ctrl_reg1;
239 
240  if (!twr_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, HTS221_STATUS_REG, &reg_status))
241  {
242  goto start;
243  }
244 
245  if ((reg_status & HTS221_BIT_H_DA) == 0)
246  {
247  goto start;
248  }
249 
250  if (!twr_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, HTS221_HUMIDITY_OUT_H, &retval[1]))
251  {
252  goto start;
253  }
254 
255  if (!twr_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, HTS221_HUMIDITY_OUT_L, &retval[0]))
256  {
257  goto start;
258  }
259 
260  self->_reg_humidity = ((uint16_t) retval[1] << 8) | retval[0];
261 
262  if (!twr_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG1, &ctrl_reg1))
263  {
264  goto start;
265  }
266 
267  ctrl_reg1 &= ~HTS221_BIT_PD;
268 
269  if (!twr_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG1, ctrl_reg1))
270  {
271  goto start;
272  }
273 
274  self->_humidity_valid = true;
275 
276  self->_state = TWR_HTS221_STATE_UPDATE;
277 
278  goto start;
279  }
280  case TWR_HTS221_STATE_UPDATE:
281  {
282  self->_measurement_active = false;
283 
284  if (self->_event_handler != NULL)
285  {
286  self->_event_handler(self, TWR_HTS221_EVENT_UPDATE, self->_event_param);
287  }
288 
289  self->_state = TWR_HTS221_STATE_MEASURE;
290 
291  return;
292  }
293  default:
294  {
295  self->_state = TWR_HTS221_STATE_ERROR;
296 
297  goto start;
298  }
299  }
300 }
301 
302 static bool _twr_hts221_load_calibration(twr_hts221_t *self)
303 {
304  uint8_t i;
305  uint8_t calibration[16];
306  int16_t h1_rh;
307  int16_t h1_t0_out;
308 
309  for (i = 0; i < 16; i++)
310  {
311  if (!twr_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, HTS221_CALIB_OFFSET + i, &calibration[i]))
312  {
313  return false;
314  }
315  }
316 
317  self->_h0_rh = (int16_t) calibration[0];
318  self->_h0_rh >>= 1;
319  h1_rh = (int16_t) calibration[1];
320  h1_rh >>= 1;
321 
322  self->_h0_t0_out = (int16_t) calibration[6];
323  self->_h0_t0_out |= ((int16_t) calibration[7]) << 8;
324 
325  h1_t0_out = (int16_t) calibration[10];
326  h1_t0_out |= ((int16_t) calibration[11]) << 8;
327 
328  if ((h1_t0_out - self->_h0_t0_out) == 0)
329  {
330  return false;
331  }
332 
333  self->_h_grad = (float) (h1_rh - self->_h0_rh) / (float) (h1_t0_out - self->_h0_t0_out);
334 
335  uint16_t t0_degC = (int16_t) calibration[2];
336  t0_degC |= (int16_t) (0x03 & calibration[5]) << 8;
337  t0_degC >>= 3; // /= 8.0
338 
339  uint16_t t1_degC = (int16_t) calibration[3];
340  t1_degC |= (int16_t) (0x0C & calibration[5]) << 6;
341  t1_degC >>= 3;
342 
343  return true;
344 }
void twr_scheduler_plan_relative(twr_scheduler_task_id_t task_id, twr_tick_t tick)
Schedule specified task to tick relative from current spin.
void twr_hts221_set_update_interval(twr_hts221_t *self, twr_tick_t interval)
Set measurement interval.
Definition: twr_hts221.c:90
void twr_scheduler_plan_current_relative(twr_tick_t tick)
Schedule current task to tick relative from current spin.
twr_scheduler_task_id_t twr_scheduler_register(void(*task)(void *), void *param, twr_tick_t tick)
Register task in scheduler.
Definition: twr_scheduler.c:53
void twr_hts221_deinit(twr_hts221_t *self)
Deinitialize HTS221.
Definition: twr_hts221.c:71
void twr_scheduler_plan_current_absolute(twr_tick_t tick)
Schedule current task to absolute tick.
void twr_hts221_set_event_handler(twr_hts221_t *self, void(*event_handler)(twr_hts221_t *, twr_hts221_event_t, void *), void *event_param)
Set callback function.
Definition: twr_hts221.c:84
Update event.
Definition: twr_hts221.h:19
void twr_scheduler_plan_current_from_now(twr_tick_t tick)
Schedule current task to tick relative from now.
void twr_i2c_init(twr_i2c_channel_t channel, twr_i2c_speed_t speed)
Initialize I2C channel.
Definition: twr_i2c.c:57
Error event.
Definition: twr_hts221.h:16
uint64_t twr_tick_t
Timestamp data type.
Definition: twr_tick.h:16
twr_hts221_event_t
Callback events.
Definition: twr_hts221.h:13
bool twr_i2c_memory_write_8b(twr_i2c_channel_t channel, uint8_t device_address, uint32_t memory_address, uint8_t data)
Memory write 1 byte to I2C channel.
Definition: twr_i2c.c:408
twr_tick_t twr_tick_get(void)
Get absolute timestamp since start of program.
Definition: twr_tick.c:7
twr_i2c_channel_t
I2C channels.
Definition: twr_i2c.h:15
void twr_scheduler_unregister(twr_scheduler_task_id_t task_id)
Unregister specified task.
Definition: twr_scheduler.c:77
bool twr_hts221_measure(twr_hts221_t *self)
Start measurement manually.
Definition: twr_hts221.c:106
struct twr_hts221_t twr_hts221_t
HTS221 instance.
Definition: twr_hts221.h:25
bool twr_i2c_memory_read_8b(twr_i2c_channel_t channel, uint8_t device_address, uint32_t memory_address, uint8_t *data)
Memory read 1 byte from I2C channel.
Definition: twr_i2c.c:437
void twr_hts221_init(twr_hts221_t *self, twr_i2c_channel_t i2c_channel, uint8_t i2c_address)
Initialize HTS221.
Definition: twr_hts221.c:53
#define TWR_TICK_INFINITY
Maximum timestamp value.
Definition: twr_tick.h:12
void twr_scheduler_plan_absolute(twr_scheduler_task_id_t task_id, twr_tick_t tick)
Schedule specified task to absolute tick.
I2C communication speed is 400 kHz.
Definition: twr_i2c.h:36
bool twr_hts221_get_humidity_percentage(twr_hts221_t *self, float *percentage)
Get measured humidity as percentage.
Definition: twr_hts221.c:120