Firmware SDK
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
bc_lis2dh12.c
1 #include <bc_lis2dh12.h>
2 #include <bc_scheduler.h>
3 #include <bc_exti.h>
4 #include <stm32l0xx.h>
5 
6 #define _BC_LIS2DH12_DELAY_RUN 10
7 #define _BC_LIS2DH12_DELAY_READ 10
8 #define _BC_LIS2DH12_AUTOINCREMENT_ADR 0x80
9 
10 static void _bc_lis2dh12_task_interval(void *param);
11 static void _bc_lis2dh12_task_measure(void *param);
12 static bool _bc_lis2dh12_power_down(bc_lis2dh12_t *self);
13 static bool _bc_lis2dh12_continuous_conversion(bc_lis2dh12_t *self);
14 static bool _bc_lis2dh12_read_result(bc_lis2dh12_t *self);
15 static void _bc_lis2dh12_interrupt(bc_exti_line_t line, void *param);
16 
17 static const float _bc_lis2dh12_fs_lut[] =
18 {
19  [BC_LIS2DH12_SCALE_2G] = (1/1000.f),
20  [BC_LIS2DH12_SCALE_4G] = (2/1000.f),
21  [BC_LIS2DH12_SCALE_8G] = (4/1000.f),
22  [BC_LIS2DH12_SCALE_16G] = (12/1000.f)
23 };
24 
25 bool bc_lis2dh12_init(bc_lis2dh12_t *self, bc_i2c_channel_t i2c_channel, uint8_t i2c_address)
26 {
27  memset(self, 0, sizeof(*self));
28 
29  self->_i2c_channel = i2c_channel;
30  self->_i2c_address = i2c_address;
31 
32  bc_i2c_init(self->_i2c_channel, BC_I2C_SPEED_400_KHZ);
33 
34  // Enable GPIOB clock
35  RCC->IOPENR |= RCC_IOPENR_GPIOBEN;
36 
37  // Errata workaround
38  RCC->IOPENR;
39 
40  // Set input mode
41  GPIOB->MODER &= ~GPIO_MODER_MODE6_Msk;
42 
43  self->_task_id_interval = bc_scheduler_register(_bc_lis2dh12_task_interval, self, BC_TICK_INFINITY);
44  self->_task_id_measure = bc_scheduler_register(_bc_lis2dh12_task_measure, self, _BC_LIS2DH12_DELAY_RUN);
45 
46  return true;
47 }
48 
49 void bc_lis2dh12_set_event_handler(bc_lis2dh12_t *self, void (*event_handler)(bc_lis2dh12_t *, bc_lis2dh12_event_t, void *), void *event_param)
50 {
51  self->_event_handler = event_handler;
52  self->_event_param = event_param;
53 }
54 
56 {
57  self->_update_interval = interval;
58 
59  if (self->_update_interval == BC_TICK_INFINITY)
60  {
61  bc_scheduler_plan_absolute(self->_task_id_interval, BC_TICK_INFINITY);
62  }
63  else
64  {
65  bc_scheduler_plan_relative(self->_task_id_interval, self->_update_interval);
66 
67  bc_lis2dh12_measure(self);
68  }
69 }
70 
72 {
73  if (self->_measurement_active)
74  {
75  return false;
76  }
77 
78  self->_measurement_active = true;
79 
80  bc_scheduler_plan_now(self->_task_id_measure);
81 
82  return true;
83 }
84 
86 {
87  *result_raw = self->_raw;
88 
89  return self->_accelerometer_valid;
90 }
91 
93 {
94  bc_lis2dh12_result_raw_t result_raw;
95 
96  if (!bc_lis2dh12_get_result_raw(self, &result_raw))
97  {
98  return false;
99  }
100 
101  float sensitivity = _bc_lis2dh12_fs_lut[self->_scale];
102 
103  result_g->x_axis = (result_raw.x_axis >> 4) * sensitivity;
104  result_g->y_axis = (result_raw.y_axis >> 4) * sensitivity;
105  result_g->z_axis = (result_raw.z_axis >> 4) * sensitivity;
106 
107  return true;
108 }
109 
110 static void _bc_lis2dh12_task_interval(void *param)
111 {
112  bc_lis2dh12_t *self = param;
113 
114  bc_lis2dh12_measure(self);
115 
116  bc_scheduler_plan_current_relative(self->_update_interval);
117 }
118 
119 static void _bc_lis2dh12_task_measure(void *param)
120 {
121  bc_lis2dh12_t *self = param;
122 
123  while (true)
124  {
125  switch (self->_state)
126  {
127  case BC_LIS2DH12_STATE_ERROR:
128  {
129  self->_accelerometer_valid = false;
130 
131  self->_measurement_active = false;
132 
133  if (self->_event_handler != NULL)
134  {
135  self->_event_handler(self, BC_LIS2DH12_EVENT_ERROR, self->_event_param);
136  }
137 
138  self->_state = BC_LIS2DH12_STATE_INITIALIZE;
139 
140  return;
141  }
142  case BC_LIS2DH12_STATE_INITIALIZE:
143  {
144  self->_state = BC_LIS2DH12_STATE_ERROR;
145 
146  // Read and check WHO_AM_I register
147  uint8_t who_am_i;
148  if (!bc_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, 0x0f, &who_am_i))
149  {
150  continue;
151  }
152 
153  if (who_am_i != 0x33)
154  {
155  continue;
156  }
157 
158  uint8_t cfg_reg4 = 0x80 | ((uint8_t) self->_scale << 4) | (((uint8_t) self->_resolution & 0x01) << 3);
159 
160  if (!bc_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, 0x23, cfg_reg4))
161  {
162  continue;
163  }
164 
165  if (!_bc_lis2dh12_power_down(self))
166  {
167  continue;
168  }
169 
170  self->_state = BC_LIS2DH12_STATE_MEASURE;
171 
172  if (self->_measurement_active)
173  {
175  }
176 
177  return;
178  }
179  case BC_LIS2DH12_STATE_MEASURE:
180  {
181  self->_state = BC_LIS2DH12_STATE_ERROR;
182 
183  if (!_bc_lis2dh12_continuous_conversion(self))
184  {
185  continue;
186  }
187 
188  self->_state = BC_LIS2DH12_STATE_READ;
189 
190  bc_scheduler_plan_current_from_now(_BC_LIS2DH12_DELAY_READ);
191 
192  return;
193  }
194  case BC_LIS2DH12_STATE_READ:
195  {
196  self->_state = BC_LIS2DH12_STATE_ERROR;
197 
198  if (!_bc_lis2dh12_read_result(self))
199  {
200  continue;
201  }
202 
203  // Power down only when no alarm is set
204  if(!self->_alarm_active)
205  {
206  if (!_bc_lis2dh12_power_down(self))
207  {
208  continue;
209  }
210  }
211 
212  self->_accelerometer_valid = true;
213 
214  self->_state = BC_LIS2DH12_STATE_UPDATE;
215 
216  continue;
217  }
218  case BC_LIS2DH12_STATE_UPDATE:
219  {
220  self->_state = BC_LIS2DH12_STATE_ERROR;
221 
222  self->_measurement_active = false;
223 
224  if (self->_event_handler != NULL)
225  {
226  self->_event_handler(self, BC_LIS2DH12_EVENT_UPDATE, self->_event_param);
227  }
228 
229  // When the interrupt alarm is active
230  if(self->_alarm_active)
231  {
232  uint8_t int1_src;
233 
234  if(!bc_i2c_memory_read_8b(self->_i2c_channel, self->_i2c_address, 0x31, &int1_src))
235  {
236  continue;
237  }
238 
239  if(self->_irq_flag)
240  {
241  self->_irq_flag = 0;
242 
243  if (self->_event_handler != NULL)
244  {
245  self->_event_handler(self, BC_LIS2DH12_EVENT_ALARM, self->_event_param);
246  }
247  }
248  }
249 
250  self->_state = BC_LIS2DH12_STATE_MEASURE;
251 
252  return;
253  }
254  default:
255  {
256  self->_state = BC_LIS2DH12_STATE_ERROR;
257 
258  continue;
259  }
260  }
261  }
262 }
263 
264 static bool _bc_lis2dh12_power_down(bc_lis2dh12_t *self)
265 {
266  if (!bc_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, 0x20, 0x07))
267  {
268  return false;
269  }
270 
271  return true;
272 }
273 
274 static bool _bc_lis2dh12_continuous_conversion(bc_lis2dh12_t *self)
275 {
276 
277  uint8_t cfg_reg1 = 0x57 | ((self->_resolution & 0x02) << 2);
278 
279  // ODR = 0x5 => 100Hz
280  if (!bc_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, 0x20, cfg_reg1))
281  {
282  return false;
283  }
284 
285  return true;
286 }
287 
288 static bool _bc_lis2dh12_read_result(bc_lis2dh12_t *self)
289 {
290  bc_i2c_memory_transfer_t transfer;
291 
292  transfer.device_address = self->_i2c_address;
293  transfer.memory_address = _BC_LIS2DH12_AUTOINCREMENT_ADR | 0x28;
294  transfer.buffer = &self->_raw;
295  transfer.length = 6;
296 
297  return bc_i2c_memory_read(self->_i2c_channel, &transfer);
298 }
299 
301 {
302  if (alarm != NULL)
303  {
304  // Enable alarm
305  self->_alarm_active = true;
306 
307  // Disable IRQ first to change the registers
308  if (!bc_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, 0x30, 0x00))
309  {
310  return false;
311  }
312 
313  // Recalculate threshold to the 4g full-scale setting
314  uint8_t int1_ths = (uint8_t)((alarm->threshold) / 0.031f);
315 
316  // Ensure minimum threshold level
317  if (int1_ths == 0)
318  {
319  int1_ths = 1;
320  }
321 
322  if (!bc_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, 0x32, int1_ths))
323  {
324  return false;
325  }
326 
327  uint8_t int1_duration = (uint8_t)(alarm->duration / 1); // /10
328  if (!bc_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, 0x33, int1_duration))
329  {
330  return false;
331  }
332 
333  // CTRL_REG3
334  uint8_t ctrl_reg3 = (1 << 6);
335  if (!bc_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, 0x22, ctrl_reg3))
336  {
337  return false;
338  }
339 
340  // CTRL_REG6 - invert interrupt
341  uint8_t ctrl_reg6 = (1 << 1);
342  if (!bc_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, 0x25, ctrl_reg6))
343  {
344  return false;
345  }
346 
347  // ctr_reg5
348  uint8_t ctrl_reg5 = (0 << 3); // latch interrupt request
349  if (!bc_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, 0x24, ctrl_reg5))
350  {
351  return false;
352  }
353 
354  // INT_CFG1
355  uint8_t int_cfg1;
356 
357  int_cfg1 = 0; //(1 << 7) | (1 << 6); // AOI = 0, 6D = 1
358 
359  int_cfg1 |= (alarm->z_high) ? (1 << 5) : 0;
360  int_cfg1 |= (alarm->z_low) ? (1 << 4) : 0;
361 
362  int_cfg1 |= (alarm->y_high) ? (1 << 3) : 0;
363  int_cfg1 |= (alarm->y_low) ? (1 << 2) : 0;
364 
365  int_cfg1 |= (alarm->x_high) ? (1 << 1) : 0;
366  int_cfg1 |= (alarm->x_low) ? (1 << 0) : 0;
367 
368  if (!bc_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, 0x30, int_cfg1))
369  {
370  return false;
371  }
372 
373  bc_exti_register(BC_EXTI_LINE_PB6, BC_EXTI_EDGE_FALLING, _bc_lis2dh12_interrupt, self);
374  }
375  else
376  {
377  // Disable alarm
378  self->_alarm_active = false;
379 
380  if (!bc_i2c_memory_write_8b(self->_i2c_channel, self->_i2c_address, 0x30, 0x00))
381  {
382  return false;
383  }
384 
386  }
387 
388  bc_lis2dh12_measure(self);
389 
390  return true;
391 }
392 
394 {
395  self->_resolution = resolution;
396 
397  self->_state = BC_LIS2DH12_STATE_INITIALIZE;
398 
399  bc_scheduler_plan_now(self->_task_id_measure);
400 
401  return true;
402 }
403 
405 {
406  self->_scale = scale;
407 
408  self->_state = BC_LIS2DH12_STATE_INITIALIZE;
409 
410  bc_scheduler_plan_now(self->_task_id_measure);
411 
412  return true;
413 }
414 
415 static void _bc_lis2dh12_interrupt(bc_exti_line_t line, void *param)
416 {
417  (void) line;
418 
419  bc_lis2dh12_t *self = param;
420 
421  self->_irq_flag = true;
422 
423  bc_lis2dh12_measure(self);
424 }
void * buffer
Pointer to buffer which is being written or read.
Definition: bc_i2c.h:66
bc_lis2dh12_resolution_t
Resolution and mode.
Definition: bc_lis2dh12.h:29
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
float z_axis
Z-axis.
Definition: bc_lis2dh12.h:84
bool bc_i2c_memory_read(bc_i2c_channel_t channel, const bc_i2c_memory_transfer_t *transfer)
Memory read from I2C channel.
Definition: bc_i2c.c:365
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
float y_axis
Y-axis.
Definition: bc_lis2dh12.h:81
bool bc_lis2dh12_measure(bc_lis2dh12_t *self)
Start measurement manually.
Definition: bc_lis2dh12.c:71
void bc_scheduler_plan_now(bc_scheduler_task_id_t task_id)
Schedule specified task for immediate execution.
Definition: bc_scheduler.c:119
bool bc_lis2dh12_init(bc_lis2dh12_t *self, bc_i2c_channel_t i2c_channel, uint8_t i2c_address)
Initialize LIS2DH12.
Definition: bc_lis2dh12.c:25
void bc_i2c_init(bc_i2c_channel_t channel, bc_i2c_speed_t speed)
Initialize I2C channel.
Definition: bc_i2c.c:54
bc_lis2dh12_event_t
Callback events.
Definition: bc_lis2dh12.h:14
bool bc_lis2dh12_get_result_raw(bc_lis2dh12_t *self, bc_lis2dh12_result_raw_t *result_raw)
Get measured acceleration as raw value.
Definition: bc_lis2dh12.c:85
±2 g (Default)
Definition: bc_lis2dh12.h:45
float threshold
Alarm threshold in g.
Definition: bc_lis2dh12.h:93
void bc_scheduler_plan_current_relative(bc_tick_t tick)
Schedule current task to tick relative from current spin.
Definition: bc_scheduler.c:149
int16_t x_axis
X-axis.
Definition: bc_lis2dh12.h:63
struct bc_lis2dh12_t bc_lis2dh12_t
LIS2DH12 instance.
Definition: bc_lis2dh12.h:106
int16_t y_axis
Y-axis.
Definition: bc_lis2dh12.h:66
uint32_t memory_address
8-bit I2C memory address (it can be extended to 16-bit format if OR-ed with BC_I2C_MEMORY_ADDRESS_16_...
Definition: bc_i2c.h:63
uint8_t device_address
7-bit I2C device address
Definition: bc_i2c.h:60
bool bc_lis2dh12_set_scale(bc_lis2dh12_t *self, bc_lis2dh12_scale_t scale)
Set scale.
Definition: bc_lis2dh12.c:404
LIS2DH12 alarm set structure.
Definition: bc_lis2dh12.h:90
I2C memory transfer parameters.
Definition: bc_i2c.h:57
void bc_exti_register(bc_exti_line_t line, bc_exti_edge_t edge, void(*callback)(bc_exti_line_t, void *), void *param)
Enable EXTI line interrupt and register callback function.
Definition: bc_exti.c:17
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
void bc_exti_unregister(bc_exti_line_t line)
Disable EXTI line interrupt.
Definition: bc_exti.c:90
LIS2DH12 result in raw values.
Definition: bc_lis2dh12.h:60
bool bc_lis2dh12_set_alarm(bc_lis2dh12_t *self, bc_lis2dh12_alarm_t *alarm)
Enable or disable accelerometer threshold alarm.
Definition: bc_lis2dh12.c:300
void bc_lis2dh12_set_update_interval(bc_lis2dh12_t *self, bc_tick_t interval)
Set measurement interval.
Definition: bc_lis2dh12.c:55
int16_t z_axis
Z-axis.
Definition: bc_lis2dh12.h:69
bc_i2c_channel_t
I2C channels.
Definition: bc_i2c.h:15
bc_exti_line_t
EXTI lines.
Definition: bc_exti.h:21
bc_lis2dh12_scale_t
Definition: bc_lis2dh12.h:42
void bc_scheduler_plan_current_from_now(bc_tick_t tick)
Schedule current task to tick relative from now.
Definition: bc_scheduler.c:154
EXTI line PB6.
Definition: bc_exti.h:90
#define BC_TICK_INFINITY
Maximum timestamp value.
Definition: bc_tick.h:12
LIS2DH12 result in g.
Definition: bc_lis2dh12.h:75
bool bc_lis2dh12_set_resolution(bc_lis2dh12_t *self, bc_lis2dh12_resolution_t resolution)
Set resolution.
Definition: bc_lis2dh12.c:393
size_t length
Length of buffer which is being written or read.
Definition: bc_i2c.h:69
bool bc_lis2dh12_get_result_g(bc_lis2dh12_t *self, bc_lis2dh12_result_g_t *result_g)
Get measured acceleration in g.
Definition: bc_lis2dh12.c:92
void bc_lis2dh12_set_event_handler(bc_lis2dh12_t *self, void(*event_handler)(bc_lis2dh12_t *, bc_lis2dh12_event_t, void *), void *event_param)
Set callback function.
Definition: bc_lis2dh12.c:49
EXTI line is configured to falling edge sensitivity.
Definition: bc_exti.h:201
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
float x_axis
X-axis.
Definition: bc_lis2dh12.h:78