Firmware SDK
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
bc_module_battery.c
1 #include <stm32l0xx.h>
2 #include <bc_module_battery.h>
3 #include <bc_gpio.h>
4 #include <bc_adc.h>
5 #include <bc_scheduler.h>
6 #include <bc_timer.h>
7 
8 #define _BC_MODULE_BATTERY_CELL_VOLTAGE 1.5f
9 
10 #define _BC_MODULE_BATTERY_STANDATD_DEFAULT_LEVEL_LOW (1.2 * 4)
11 #define _BC_MODULE_BATTERY_DEFAULT_DEFAULT_LEVEL_CRITICAL (1.0 * 4)
12 
13 #define _BC_MODULE_BATTERY_MINI_DEFAULT_LEVEL_LOW (1.2 * 2)
14 #define _BC_MODULE_BATTERY_MINI_DEFAULT_LEVEL_CRITICAL (1.0 * 2)
15 
16 #define _BC_MODULE_BATTERY_MINI_VOLTAGE_ON_BATTERY_TO_PERCENTAGE(__VOLTAGE__) ((100. * ((__VOLTAGE__) - _BC_MODULE_BATTERY_MINI_DEFAULT_LEVEL_CRITICAL)) / ((_BC_MODULE_BATTERY_CELL_VOLTAGE * 2) - _BC_MODULE_BATTERY_MINI_DEFAULT_LEVEL_CRITICAL))
17 #define _BC_MODULE_BATTERY_STANDARD_VOLTAGE_ON_BATTERY_TO_PERCENTAGE(__VOLTAGE__) ((100. * ((__VOLTAGE__) - _BC_MODULE_BATTERY_DEFAULT_DEFAULT_LEVEL_CRITICAL)) / ((_BC_MODULE_BATTERY_CELL_VOLTAGE * 4) - _BC_MODULE_BATTERY_DEFAULT_DEFAULT_LEVEL_CRITICAL))
18 
19 #define _BC_MODULE_BATTERY_MINI_CALIBRATION(__VOLTAGE__) ((__VOLTAGE__) * 1.095f + 0.0069f)
20 #define _BC_MODULE_BATTERY_STANDARD_CALIBRATION(__VOLTAGE__) ((__VOLTAGE__) * 1.1068f + 0.0212f)
21 
22 #define _BC_MODULE_BATTERY_MINI_RESULT_TO_VOLTAGE(__RESULT__) ((__RESULT__) * (1 / (5.0 / (5.0 + 10.0))))
23 #define _BC_MODULE_BATTERY_STANDARD_RESULT_TO_VOLTAGE(__RESULT__) ((__RESULT__) * (1 / 0.13))
24 
25 typedef enum
26 {
27  BC_MODULE_STATE_DETECT_PRESENT = 0,
28  BC_MODULE_STATE_DETECT_FORMAT = 1,
29  BC_MODULE_STATE_MEASURE = 2,
30  BC_MODULE_STATE_READ = 3,
31  BC_MODULE_STATE_UPDATE = 4
32 
33 } _bc_module_battery_state_t;
34 
35 static struct
36 {
37  float voltage;
38  float valid_min;
39  float valid_max;
41  void (*event_handler)(bc_module_battery_event_t, void *);
42  void *event_param;
43  bool measurement_active;
44  float level_low_threshold;
45  float level_critical_threshold;
46  bc_tick_t update_interval;
47  bc_tick_t next_update_start;
48  bc_scheduler_task_id_t task_id;
49  float adc_value;
50  _bc_module_battery_state_t state;
51 
52 } _bc_module_battery;
53 
54 static bool _bc_module_battery_present_test(void);
55 static void _bc_module_battery_task(void *param);
56 static void _bc_module_battery_adc_event_handler(bc_adc_channel_t channel, bc_adc_event_t event, void *param);
57 static void _bc_module_battery_measurement(int state);
58 
60 {
61  memset(&_bc_module_battery, 0, sizeof(_bc_module_battery));
62 
63  _bc_module_battery.voltage = NAN;
64  _bc_module_battery.adc_value = NAN;
65  _bc_module_battery.update_interval = BC_TICK_INFINITY;
66  _bc_module_battery.task_id = bc_scheduler_register(_bc_module_battery_task, NULL, BC_TICK_INFINITY);
67 
70 
71  bc_timer_init();
72 }
73 
74 void bc_module_battery_set_event_handler(void (*event_handler)(bc_module_battery_event_t, void *), void *event_param)
75 {
76  _bc_module_battery.event_handler = event_handler;
77  _bc_module_battery.event_param = event_param;
78 }
79 
81 {
82  _bc_module_battery.update_interval = interval;
83 
84  if (_bc_module_battery.update_interval == BC_TICK_INFINITY)
85  {
86  if (!_bc_module_battery.measurement_active)
87  {
88  bc_scheduler_plan_absolute(_bc_module_battery.task_id, BC_TICK_INFINITY);
89  }
90  }
91  else
92  {
94  }
95 }
96 
97 void bc_module_battery_set_threshold_levels(float level_low_threshold, float level_critical_threshold)
98 {
99  _bc_module_battery.level_low_threshold = level_low_threshold;
100  _bc_module_battery.level_critical_threshold = level_critical_threshold;
101 }
102 
104 {
105  return _bc_module_battery.format;
106 }
107 
109 {
110  if (_bc_module_battery.measurement_active)
111  {
112  return false;
113  }
114 
115  _bc_module_battery.measurement_active = true;
116 
117  bc_scheduler_plan_now(_bc_module_battery.task_id);
118 
119  return true;
120 }
121 
122 bool bc_module_battery_get_voltage(float *voltage)
123 {
124  *voltage = _bc_module_battery.voltage;
125 
126  return !isnan(_bc_module_battery.voltage);
127 }
128 
130 {
131  float voltage;
132 
133  if (bc_module_battery_get_voltage(&voltage))
134  {
135  // Calculate the percentage of charge
136  if (_bc_module_battery.format == BC_MODULE_BATTERY_FORMAT_MINI)
137  {
138  *percentage = _BC_MODULE_BATTERY_MINI_VOLTAGE_ON_BATTERY_TO_PERCENTAGE(voltage);
139  }
140  else
141  {
142  *percentage = _BC_MODULE_BATTERY_STANDARD_VOLTAGE_ON_BATTERY_TO_PERCENTAGE(voltage);
143  }
144 
145  if (*percentage > 100)
146  {
147  *percentage = 100;
148  }
149  else if (*percentage < 0)
150  {
151  *percentage = 0;
152  }
153 
154  return true;
155  }
156 
157  return false;
158 }
159 
161 {
162  if (_bc_module_battery.state != BC_MODULE_STATE_DETECT_PRESENT)
163  {
164  return true;
165  }
166 
167  return _bc_module_battery_present_test();
168 }
169 
170 static bool _bc_module_battery_present_test(void)
171 {
172  bc_system_pll_enable();
173 
175 
177 
179 
181 
183 
185 
186  __NOP();
187 
188  int value = bc_gpio_get_input(BC_GPIO_P0);
189 
190  bc_system_pll_disable();
191 
192  return value != 0;
193 }
194 
195 static void _bc_module_battery_task(void *param)
196 {
197  (void) param;
198 
199 start:
200 
201  switch (_bc_module_battery.state)
202  {
203 
204  case BC_MODULE_STATE_DETECT_PRESENT:
205  {
206  if (_bc_module_battery.update_interval == BC_TICK_INFINITY)
207  {
208  _bc_module_battery.next_update_start = BC_TICK_INFINITY;
209  }
210  else
211  {
212  _bc_module_battery.next_update_start = bc_tick_get() + _bc_module_battery.update_interval;
213  }
214 
215  _bc_module_battery.format = BC_MODULE_BATTERY_FORMAT_UNKNOWN;
216 
217  if (!_bc_module_battery_present_test())
218  {
219  bc_scheduler_plan_current_absolute(_bc_module_battery.next_update_start);
220 
221  if (_bc_module_battery.next_update_start == BC_TICK_INFINITY)
222  {
223  _bc_module_battery.measurement_active = false;
224  }
225 
226  if (_bc_module_battery.event_handler != NULL)
227  {
228  _bc_module_battery.event_handler(BC_MODULE_BATTERY_EVENT_ERROR, _bc_module_battery.event_param);
229  }
230 
231  return;
232  }
233 
234  _bc_module_battery_measurement(ENABLE);
235 
236  bc_adc_init();
238  bc_adc_set_event_handler(BC_ADC_CHANNEL_A0, _bc_module_battery_adc_event_handler, NULL);
239 
241 
242  _bc_module_battery.state = BC_MODULE_STATE_DETECT_FORMAT;
243 
244  break;
245  }
246  case BC_MODULE_STATE_DETECT_FORMAT:
247  {
248  float voltage = _BC_MODULE_BATTERY_STANDARD_CALIBRATION(_BC_MODULE_BATTERY_STANDARD_RESULT_TO_VOLTAGE(_bc_module_battery.adc_value));
249 
250  if ((voltage > 3.8) && (voltage < 7.0))
251  {
252  _bc_module_battery.format = BC_MODULE_BATTERY_FORMAT_STANDARD;
253  _bc_module_battery.level_low_threshold = _BC_MODULE_BATTERY_STANDATD_DEFAULT_LEVEL_LOW;
254  _bc_module_battery.level_critical_threshold = _BC_MODULE_BATTERY_DEFAULT_DEFAULT_LEVEL_CRITICAL;
255  _bc_module_battery.valid_min = 3.8;
256  _bc_module_battery.valid_max = 7.0;
257  }
258  else
259  {
260  _bc_module_battery.format = BC_MODULE_BATTERY_FORMAT_MINI;
261  _bc_module_battery.level_low_threshold = _BC_MODULE_BATTERY_MINI_DEFAULT_LEVEL_LOW;
262  _bc_module_battery.level_critical_threshold = _BC_MODULE_BATTERY_MINI_DEFAULT_LEVEL_CRITICAL;
263  _bc_module_battery.valid_min = 1.8;
264  _bc_module_battery.valid_max = 3.8;
265  }
266 
267  _bc_module_battery.state = BC_MODULE_STATE_MEASURE;
268 
269  if (_bc_module_battery.measurement_active)
270  {
272  }
273  else
274  {
275  bc_scheduler_plan_current_absolute(_bc_module_battery.next_update_start);
276  }
277 
278  break;
279  }
280  case BC_MODULE_STATE_MEASURE:
281  {
282  if (_bc_module_battery.update_interval == BC_TICK_INFINITY)
283  {
284  _bc_module_battery.next_update_start = BC_TICK_INFINITY;
285  }
286  else
287  {
288  _bc_module_battery.next_update_start = bc_tick_get() + _bc_module_battery.update_interval;
289  }
290 
291  _bc_module_battery_measurement(ENABLE);
292 
293  bc_adc_set_event_handler(BC_ADC_CHANNEL_A0, _bc_module_battery_adc_event_handler, NULL);
294 
296 
297  _bc_module_battery.state = BC_MODULE_STATE_READ;
298 
299  break;
300  }
301  case BC_MODULE_STATE_READ:
302  {
303  if (_bc_module_battery.format == BC_MODULE_BATTERY_FORMAT_MINI)
304  {
305  _bc_module_battery.voltage = _BC_MODULE_BATTERY_MINI_CALIBRATION(_BC_MODULE_BATTERY_MINI_RESULT_TO_VOLTAGE(_bc_module_battery.adc_value));
306  }
307  else
308  {
309  _bc_module_battery.voltage = _BC_MODULE_BATTERY_STANDARD_CALIBRATION(_BC_MODULE_BATTERY_STANDARD_RESULT_TO_VOLTAGE(_bc_module_battery.adc_value));
310  }
311 
312  _bc_module_battery.measurement_active = false;
313 
314  if ((_bc_module_battery.voltage < _bc_module_battery.valid_min) || (_bc_module_battery.voltage > _bc_module_battery.valid_max))
315  {
316  _bc_module_battery.voltage = NAN;
317 
318  _bc_module_battery.state = BC_MODULE_STATE_DETECT_PRESENT;
319 
320  bc_scheduler_plan_current_absolute(_bc_module_battery.next_update_start);
321 
322  if (_bc_module_battery.event_handler != NULL)
323  {
324  _bc_module_battery.event_handler(BC_MODULE_BATTERY_EVENT_ERROR, _bc_module_battery.event_param);
325  }
326 
327  return;
328  }
329 
330  _bc_module_battery.state = BC_MODULE_STATE_UPDATE;
331 
332  goto start;
333  }
334  case BC_MODULE_STATE_UPDATE:
335  {
336  if (_bc_module_battery.event_handler != NULL)
337  {
338  // Notify event based on calculated percentage
339  if (_bc_module_battery.voltage <= _bc_module_battery.level_critical_threshold)
340  {
341  _bc_module_battery.event_handler(BC_MODULE_BATTERY_EVENT_LEVEL_CRITICAL, _bc_module_battery.event_param);
342  }
343  else if (_bc_module_battery.voltage <= _bc_module_battery.level_low_threshold)
344  {
345  _bc_module_battery.event_handler(BC_MODULE_BATTERY_EVENT_LEVEL_LOW, _bc_module_battery.event_param);
346  }
347 
348  _bc_module_battery.event_handler(BC_MODULE_BATTERY_EVENT_UPDATE, _bc_module_battery.event_param);
349  }
350 
351  _bc_module_battery.state = BC_MODULE_STATE_MEASURE;
352 
353  bc_scheduler_plan_current_absolute(_bc_module_battery.next_update_start);
354 
355  break;
356  }
357  default:
358  {
359  return;
360  }
361  }
362 }
363 
364 static void _bc_module_battery_adc_event_handler(bc_adc_channel_t channel, bc_adc_event_t event, void *param)
365 {
366  (void) channel;
367  (void) param;
368 
369  if (event == BC_ADC_EVENT_DONE)
370  {
371 
372  if (!bc_adc_async_get_voltage(BC_ADC_CHANNEL_A0, &_bc_module_battery.adc_value))
373  {
374  _bc_module_battery.adc_value = NAN;
375  }
376 
377  _bc_module_battery_measurement(DISABLE);
378 
379  bc_scheduler_plan_now(_bc_module_battery.task_id);
380  }
381 }
382 
383 static void _bc_module_battery_measurement(int state)
384 {
385  if (_bc_module_battery.format == BC_MODULE_BATTERY_FORMAT_MINI)
386  {
388  }
389  else
390  {
392  }
393 
394  if (state == ENABLE)
395  {
396  bc_timer_start();
397 
398  bc_timer_delay(100);
399 
400  bc_timer_stop();
401  }
402 }
403 
uint64_t bc_tick_t
Timestamp data type.
Definition: bc_tick.h:16
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
ADC 256x oversampling.
Definition: bc_adc.h:64
bool bc_module_battery_measure(void)
Start mesurement.
void bc_timer_start(void)
Start timer.
Definition: bc_timer.c:24
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
void bc_scheduler_plan_now(bc_scheduler_task_id_t task_id)
Schedule specified task for immediate execution.
Definition: bc_scheduler.c:119
void bc_timer_delay(uint16_t microseconds)
Relative delay.
Definition: bc_timer.c:40
void bc_scheduler_plan_current_absolute(bc_tick_t tick)
Schedule current task to absolute tick.
Definition: bc_scheduler.c:144
void bc_gpio_set_output(bc_gpio_channel_t channel, int state)
Set output state for GPIO channel.
Definition: bc_gpio.c:442
Format is standard 4xAAA.
bc_module_battery_format_t
Battery Module format.
void bc_gpio_set_mode(bc_gpio_channel_t channel, bc_gpio_mode_t mode)
Set mode of operation for GPIO channel.
Definition: bc_gpio.c:340
void bc_gpio_set_pull(bc_gpio_channel_t channel, bc_gpio_pull_t pull)
Set pull-up/pull-down configuration for GPIO channel.
Definition: bc_gpio.c:313
void bc_module_battery_init(void)
Initialize Battery Module.
bc_tick_t bc_tick_get(void)
Get absolute timestamp since start of program.
Definition: bc_tick.c:7
void bc_timer_stop(void)
Stop timer.
Definition: bc_timer.c:55
void bc_adc_oversampling_set(bc_adc_channel_t channel, bc_adc_oversampling_t oversampling)
Set ADC oversampling for specific channel.
Definition: bc_adc.c:99
bool bc_module_battery_get_charge_level(int *percentage)
Get Battery Module charge in percents.
void bc_adc_init()
Initialize ADC converter.
Definition: bc_adc.c:64
void bc_module_battery_set_event_handler(void(*event_handler)(bc_module_battery_event_t, void *), void *event_param)
Set callback function.
size_t bc_scheduler_task_id_t
Task ID assigned by scheduler.
Definition: bc_scheduler.h:18
int bc_gpio_get_input(bc_gpio_channel_t channel)
Get input state for GPIO channel.
Definition: bc_gpio.c:436
GPIO channel operates as input.
Definition: bc_gpio.h:99
bool bc_adc_async_get_voltage(bc_adc_channel_t channel, float *result)
Get asynchronous measurement result in volts.
Definition: bc_adc.c:278
void bc_module_battery_set_update_interval(bc_tick_t interval)
Set update interval.
ADC channel A0.
Definition: bc_adc.h:16
GPIO channel has pull-down.
Definition: bc_gpio.h:90
GPIO channel P1.
Definition: bc_gpio.h:18
bool bc_module_battery_get_voltage(float *voltage)
Get Battery Module voltage.
#define BC_TICK_INFINITY
Maximum timestamp value.
Definition: bc_tick.h:12
bool bc_module_battery_is_present(void)
Get Battery Module is pressent, can use without bc_module_battery_init.
bc_module_battery_event_t
Battery Module event.
bc_module_battery_format_t bc_module_battery_get_format()
Get Battery Module format.
GPIO channel P0.
Definition: bc_gpio.h:15
bc_adc_event_t
ADC event.
Definition: bc_adc.h:88
bool bc_adc_async_measure(bc_adc_channel_t channel)
Begins reading the ADC channel voltage in asynchronous mode.
Definition: bc_adc.c:232
void bc_timer_init(void)
Initialize timer.
Definition: bc_timer.c:18
GPIO channel operates in analog mode.
Definition: bc_gpio.h:108
bool bc_adc_set_event_handler(bc_adc_channel_t channel, void(*event_handler)(bc_adc_channel_t, bc_adc_event_t, void *), void *event_param)
Set callback function.
Definition: bc_adc.c:216
void bc_scheduler_plan_current_now(void)
Schedule current task for immediate execution.
Definition: bc_scheduler.c:139
void bc_gpio_init(bc_gpio_channel_t channel)
Initialize GPIO channel.
Definition: bc_gpio.c:301
Format is mini 2xAAA.
bc_adc_channel_t
ADC channel.
Definition: bc_adc.h:13
ADC event.
Definition: bc_adc.h:91
void bc_module_battery_set_threshold_levels(float level_low_threshold, float level_critical_threshold)
Set voltage levels.
GPIO channel operates as output.
Definition: bc_gpio.h:102