Firmware SDK
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
bc_hts221.c
1 #include <bc_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 _BC_HTS221_DELAY_RUN 50
44 #define _BC_HTS221_DELAY_INITIALIZATION 50
45 #define _BC_HTS221_DELAY_MEASUREMENT 50
46 
47 static void _bc_hts221_task_interval(void *param);
48 
49 static void _bc_hts221_task_measure(void *param);
50 
51 static bool _bc_hts221_load_calibration(bc_hts221_t *self);
52 
53 void bc_hts221_init(bc_hts221_t *self, bc_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 = bc_scheduler_register(_bc_hts221_task_interval, self, BC_TICK_INFINITY);
61  self->_task_id_measure = bc_scheduler_register(_bc_hts221_task_measure, self, _BC_HTS221_DELAY_RUN);
62 
63  self->_tick_ready = _BC_HTS221_DELAY_RUN;
64 
65  bc_i2c_init(self->_i2c_channel, BC_I2C_SPEED_400_KHZ);
66 
67  // TODO This delays initialization, should be part of state machine
68  _bc_hts221_load_calibration(self);
69 }
70 
71 void bc_hts221_set_event_handler(bc_hts221_t *self, void (*event_handler)(bc_hts221_t *, bc_hts221_event_t, void *), void *event_param)
72 {
73  self->_event_handler = event_handler;
74  self->_event_param = event_param;
75 }
76 
78 {
79  self->_update_interval = interval;
80 
81  if (self->_update_interval == BC_TICK_INFINITY)
82  {
83  bc_scheduler_plan_absolute(self->_task_id_interval, BC_TICK_INFINITY);
84  }
85  else
86  {
87  bc_scheduler_plan_relative(self->_task_id_interval, self->_update_interval);
88 
89  bc_hts221_measure(self);
90  }
91 }
92 
94 {
95  if (self->_measurement_active)
96  {
97  return false;
98  }
99 
100  self->_measurement_active = true;
101 
102  bc_scheduler_plan_absolute(self->_task_id_measure, self->_tick_ready);
103 
104  return true;
105 }
106 
107 bool bc_hts221_get_humidity_percentage(bc_hts221_t *self, float *percentage)
108 {
109  if (!self->_humidity_valid)
110  {
111  return false;
112  }
113 
114  *percentage = self->_h0_rh + ((self->_reg_humidity - self->_h0_t0_out) * self->_h_grad);
115 
116  if (*percentage >= 100.f)
117  {
118  *percentage = 100.f;
119  }
120 
121  return true;
122 }
123 
124 static void _bc_hts221_task_interval(void *param)
125 {
126  bc_hts221_t *self = param;
127 
128  bc_hts221_measure(self);
129 
130  bc_scheduler_plan_current_relative(self->_update_interval);
131 }
132 
133 static void _bc_hts221_task_measure(void *param)
134 {
135  bc_hts221_t *self = param;
136 
137 start:
138 
139  switch (self->_state)
140  {
141  case BC_HTS221_STATE_ERROR:
142  {
143  self->_humidity_valid = false;
144 
145  self->_measurement_active = false;
146 
147  if (self->_event_handler != NULL)
148  {
149  self->_event_handler(self, BC_HTS221_EVENT_ERROR, self->_event_param);
150  }
151 
152  self->_state = BC_HTS221_STATE_INITIALIZE;
153 
154  return;
155  }
156  case BC_HTS221_STATE_INITIALIZE:
157  {
158  self->_state = BC_HTS221_STATE_ERROR;
159 
160  uint8_t ctrl_reg1;
161 
162  if (!bc_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG1, &ctrl_reg1))
163  {
164  goto start;
165  }
166 
167  ctrl_reg1 &= ~HTS221_BIT_PD;
168 
169  if (!bc_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG1, ctrl_reg1))
170  {
171  goto start;
172  }
173 
174  self->_state = BC_HTS221_STATE_MEASURE;
175 
176  self->_tick_ready = bc_tick_get() + _BC_HTS221_DELAY_INITIALIZATION;
177 
178  if (self->_measurement_active)
179  {
180  bc_scheduler_plan_current_absolute(self->_tick_ready);
181  }
182 
183  return;
184  }
185  case BC_HTS221_STATE_MEASURE:
186  {
187  self->_state = BC_HTS221_STATE_ERROR;
188 
189  uint8_t ctrl_reg1;
190 
191  if (!bc_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG1, &ctrl_reg1))
192  {
193  goto start;
194  }
195 
196  ctrl_reg1 |= HTS221_BIT_PD;
197 
198  if (!bc_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG1, ctrl_reg1))
199  {
200  goto start;
201  }
202 
203  if (!bc_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG1, HTS221_BIT_PD | HTS221_BIT_BDU))
204  {
205  goto start;
206  }
207 
208  if (!bc_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG2, HTS221_BIT_ONE_SHOT))
209  {
210  goto start;
211  }
212 
213  self->_state = BC_HTS221_STATE_READ;
214 
215  bc_scheduler_plan_current_from_now(_BC_HTS221_DELAY_MEASUREMENT);
216 
217  return;
218  }
219  case BC_HTS221_STATE_READ:
220  {
221  self->_state = BC_HTS221_STATE_ERROR;
222 
223  uint8_t reg_status;
224  uint8_t retval[2];
225  uint8_t ctrl_reg1;
226 
227  if (!bc_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, HTS221_STATUS_REG, &reg_status))
228  {
229  goto start;
230  }
231 
232  if ((reg_status & HTS221_BIT_H_DA) == 0)
233  {
234  goto start;
235  }
236 
237  if (!bc_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, HTS221_HUMIDITY_OUT_H, &retval[1]))
238  {
239  goto start;
240  }
241 
242  if (!bc_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, HTS221_HUMIDITY_OUT_L, &retval[0]))
243  {
244  goto start;
245  }
246 
247  self->_reg_humidity = ((uint16_t) retval[1] << 8) | retval[0];
248 
249  if (!bc_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG1, &ctrl_reg1))
250  {
251  goto start;
252  }
253 
254  ctrl_reg1 &= ~HTS221_BIT_PD;
255 
256  if (!bc_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, HTS221_CTRL_REG1, ctrl_reg1))
257  {
258  goto start;
259  }
260 
261  self->_humidity_valid = true;
262 
263  self->_state = BC_HTS221_STATE_UPDATE;
264 
265  goto start;
266  }
267  case BC_HTS221_STATE_UPDATE:
268  {
269  self->_measurement_active = false;
270 
271  if (self->_event_handler != NULL)
272  {
273  self->_event_handler(self, BC_HTS221_EVENT_UPDATE, self->_event_param);
274  }
275 
276  self->_state = BC_HTS221_STATE_MEASURE;
277 
278  return;
279  }
280  default:
281  {
282  self->_state = BC_HTS221_STATE_ERROR;
283 
284  goto start;
285  }
286  }
287 }
288 
289 static bool _bc_hts221_load_calibration(bc_hts221_t *self)
290 {
291  uint8_t i;
292  uint8_t calibration[16];
293  int16_t h1_rh;
294  int16_t h1_t0_out;
295 
296  for (i = 0; i < 16; i++)
297  {
298  if (!bc_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, HTS221_CALIB_OFFSET + i, &calibration[i]))
299  {
300  return false;
301  }
302  }
303 
304  self->_h0_rh = (int16_t) calibration[0];
305  self->_h0_rh >>= 1;
306  h1_rh = (int16_t) calibration[1];
307  h1_rh >>= 1;
308 
309  self->_h0_t0_out = (int16_t) calibration[6];
310  self->_h0_t0_out |= ((int16_t) calibration[7]) << 8;
311 
312  h1_t0_out = (int16_t) calibration[10];
313  h1_t0_out |= ((int16_t) calibration[11]) << 8;
314 
315  if ((h1_t0_out - self->_h0_t0_out) == 0)
316  {
317  return false;
318  }
319 
320  self->_h_grad = (float) (h1_rh - self->_h0_rh) / (float) (h1_t0_out - self->_h0_t0_out);
321 
322  uint16_t t0_degC = (int16_t) calibration[2];
323  t0_degC |= (int16_t) (0x03 & calibration[5]) << 8;
324  t0_degC >>= 3; // /= 8.0
325 
326  uint16_t t1_degC = (int16_t) calibration[3];
327  t1_degC |= (int16_t) (0x0C & calibration[5]) << 6;
328  t1_degC >>= 3;
329 
330  return true;
331 }
uint64_t bc_tick_t
Timestamp data type.
Definition: bc_tick.h:16
I2C communication speed is 400 kHz.
Definition: bc_i2c.h:36
void bc_scheduler_plan_absolute(bc_scheduler_task_id_t task_id, bc_tick_t tick)
Schedule specified task to absolute tick.
Definition: bc_scheduler.c:124
bc_hts221_event_t
Callback events.
Definition: bc_hts221.h:13
bc_scheduler_task_id_t bc_scheduler_register(void(*task)(void *), void *param, bc_tick_t tick)
Register task in scheduler.
Definition: bc_scheduler.c:56
bool bc_i2c_memory_read_8b(bc_i2c_channel_t channel, uint8_t device_address, uint32_t memory_address, uint8_t *data)
Memory read 1 byte from I2C channel.
Definition: bc_i2c.c:431
void bc_scheduler_plan_current_absolute(bc_tick_t tick)
Schedule current task to absolute tick.
Definition: bc_scheduler.c:144
void bc_i2c_init(bc_i2c_channel_t channel, bc_i2c_speed_t speed)
Initialize I2C channel.
Definition: bc_i2c.c:54
Update event.
Definition: bc_hts221.h:19
bc_tick_t bc_tick_get(void)
Get absolute timestamp since start of program.
Definition: bc_tick.c:7
void bc_scheduler_plan_current_relative(bc_tick_t tick)
Schedule current task to tick relative from current spin.
Definition: bc_scheduler.c:149
void bc_hts221_set_update_interval(bc_hts221_t *self, bc_tick_t interval)
Set measurement interval.
Definition: bc_hts221.c:77
bool bc_hts221_measure(bc_hts221_t *self)
Start measurement manually.
Definition: bc_hts221.c:93
void bc_hts221_init(bc_hts221_t *self, bc_i2c_channel_t i2c_channel, uint8_t i2c_address)
Initialize HTS221.
Definition: bc_hts221.c:53
bool bc_hts221_get_humidity_percentage(bc_hts221_t *self, float *percentage)
Get measured humidity as percentage.
Definition: bc_hts221.c:107
void bc_scheduler_plan_relative(bc_scheduler_task_id_t task_id, bc_tick_t tick)
Schedule specified task to tick relative from current spin.
Definition: bc_scheduler.c:129
Error event.
Definition: bc_hts221.h:16
struct bc_hts221_t bc_hts221_t
HTS221 instance.
Definition: bc_hts221.h:25
bc_i2c_channel_t
I2C channels.
Definition: bc_i2c.h:15
void bc_scheduler_plan_current_from_now(bc_tick_t tick)
Schedule current task to tick relative from now.
Definition: bc_scheduler.c:154
#define BC_TICK_INFINITY
Maximum timestamp value.
Definition: bc_tick.h:12
void bc_hts221_set_event_handler(bc_hts221_t *self, void(*event_handler)(bc_hts221_t *, bc_hts221_event_t, void *), void *event_param)
Set callback function.
Definition: bc_hts221.c:71
bool bc_i2c_memory_write_8b(bc_i2c_channel_t channel, uint8_t device_address, uint32_t memory_address, uint8_t data)
Memory write 1 byte to I2C channel.
Definition: bc_i2c.c:402