Firmware SDK
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
bc_adc.c
1 #include <bc_adc.h>
2 #include <bc_scheduler.h>
3 #include <bc_irq.h>
4 #include <stm32l083xx.h>
5 
6 #include <bc_system.h>
7 
8 #define VREFINT_CAL_ADDR 0x1ff80078
9 
10 #define BC_ADC_CHANNEL_INTERNAL_REFERENCE 6
11 #define BC_ADC_CHANNEL_NONE ((bc_adc_channel_t) (-1))
12 #define BC_ADC_CHANNEL_COUNT ((bc_adc_channel_t) 7)
13 
14 typedef enum
15 {
16  BC_ADC_STATE_CALIBRATION_BY_INTERNAL_REFERENCE_BEGIN,
17  BC_ADC_STATE_CALIBRATION_BY_INTERNAL_REFERENCE_END,
18  BC_ADC_STATE_MEASURE_INPUT
19 
20 } bc_adc_state_t;
21 
22 typedef struct
23 {
24  void (*event_handler)(bc_adc_channel_t, bc_adc_event_t, void *);
25  void *event_param;
26  bool pending;
27  bc_adc_resolution_t resolution;
28  bc_adc_oversampling_t oversampling;
29  uint16_t value;
30  uint32_t chselr;
31 
33 
34 static struct
35 {
36  bool initialized;
37  bc_adc_channel_t channel_in_progress;
38  uint16_t vrefint;
39  float real_vdda_voltage;
40  bc_adc_state_t state;
41  bc_scheduler_task_id_t task_id;
42  bc_adc_channel_config_t channel_table[7];
43 }
44 _bc_adc =
45 {
46  .initialized = false,
47  .channel_in_progress = BC_ADC_CHANNEL_NONE,
48  .channel_table =
49  {
50  [BC_ADC_CHANNEL_A0].chselr = ADC_CHSELR_CHSEL0,
51  [BC_ADC_CHANNEL_A1].chselr = ADC_CHSELR_CHSEL1,
52  [BC_ADC_CHANNEL_A2].chselr = ADC_CHSELR_CHSEL2,
53  [BC_ADC_CHANNEL_A3].chselr = ADC_CHSELR_CHSEL3,
54  [BC_ADC_CHANNEL_A4].chselr = ADC_CHSELR_CHSEL4,
55  [BC_ADC_CHANNEL_A5].chselr = ADC_CHSELR_CHSEL5,
56  [BC_ADC_CHANNEL_INTERNAL_REFERENCE] = { NULL, NULL, false, BC_ADC_RESOLUTION_12_BIT, BC_ADC_OVERSAMPLING_256, 0, ADC_CHSELR_CHSEL17 }
57  }
58 };
59 
60 static void _bc_adc_task(void *param);
61 
62 static inline bool _bc_adc_get_pending(bc_adc_channel_t *next ,bc_adc_channel_t start);
63 
65 {
66  if (_bc_adc.initialized != true)
67  {
68  // Enable ADC clock
69  RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
70 
71  // Errata workaround
72  RCC->APB2ENR;
73 
74  // Set auto-off mode, left align
75  ADC1->CFGR1 |= ADC_CFGR1_AUTOFF;
76 
77  // Set PCLK as a clock source
78  ADC1->CFGR2 = ADC_CFGR2_CKMODE_0 | ADC_CFGR2_CKMODE_1;
79 
80  // Sampling time selection (12.5 cycles)
81  ADC1->SMPR |= ADC_SMPR_SMP_1 | ADC_SMPR_SMP_0;
82 
83  // Enable ADC voltage regulator
84  ADC1->CR |= ADC_CR_ADVREGEN;
85 
86  // Load Vrefint constant from ROM
87  _bc_adc.vrefint = (*(uint16_t *) VREFINT_CAL_ADDR);// << 4;
88 
89  NVIC_EnableIRQ(ADC1_COMP_IRQn);
90 
91  _bc_adc.initialized = true;
92 
93  _bc_adc.task_id = bc_scheduler_register(_bc_adc_task, NULL, BC_TICK_INFINITY);
94 
96  }
97 }
98 
100 {
101  _bc_adc.channel_table[channel].oversampling = oversampling;
102 }
103 
105 {
106  _bc_adc.channel_table[channel].resolution = resolution;
107 }
108 
109 static void _bc_adc_configure_resolution(bc_adc_resolution_t resolution)
110 {
111  ADC1->CFGR1 &= ~ADC_CFGR1_RES_Msk;
112  ADC1->CFGR1 |= resolution & ADC_CFGR1_RES_Msk;
113 }
114 
115 static void _bc_adc_configure_oversampling(bc_adc_oversampling_t oversampling)
116 {
117  // Clear oversampling enable, oversampling register and oversampling shift register
118  ADC1->CFGR2 &= ~(ADC_CFGR2_OVSE_Msk | ADC_CFGR2_OVSR_Msk | ADC_CFGR2_OVSS_Msk);
119 
120  static const uint16_t oversampling_register_lut[9] =
121  {
122  0, // no oversampling
123  ADC_CFGR2_OVSE | ADC_CFGR2_OVSS_0, // 2x
124  ADC_CFGR2_OVSE | ADC_CFGR2_OVSR_0 | ADC_CFGR2_OVSS_1, // 4x
125  ADC_CFGR2_OVSE | ADC_CFGR2_OVSR_1 | ADC_CFGR2_OVSS_1 | ADC_CFGR2_OVSS_0, // 8x
126  ADC_CFGR2_OVSE | ADC_CFGR2_OVSR_1 | ADC_CFGR2_OVSR_0 | ADC_CFGR2_OVSS_2, // 16x
127  ADC_CFGR2_OVSE | ADC_CFGR2_OVSR_2 | ADC_CFGR2_OVSS_2 | ADC_CFGR2_OVSS_0, // 32x
128  ADC_CFGR2_OVSE | ADC_CFGR2_OVSR_2 | ADC_CFGR2_OVSR_0 | ADC_CFGR2_OVSS_2 | ADC_CFGR2_OVSS_1, // 64x
129  ADC_CFGR2_OVSE | ADC_CFGR2_OVSR_2 | ADC_CFGR2_OVSR_1 | ADC_CFGR2_OVSS_2 | ADC_CFGR2_OVSS_1 | ADC_CFGR2_OVSS_0, // 128x
130  ADC_CFGR2_OVSE | ADC_CFGR2_OVSR_2 | ADC_CFGR2_OVSR_1 | ADC_CFGR2_OVSR_0 | ADC_CFGR2_OVSS_3, // 256x
131  };
132 
133  ADC1->CFGR2 |= oversampling_register_lut[oversampling];
134 }
135 
136 static uint16_t _bc_adc_get_measured_value(bc_adc_channel_t channel)
137 {
138  uint16_t value = ADC1->DR;
139 
140  switch (_bc_adc.channel_table[channel].resolution)
141  {
143  {
144  value <<= 10;
145  break;
146  }
148  {
149  value <<= 8;
150  break;
151  }
153  {
154  value <<= 6;
155  break;
156  }
158  {
159  value <<= 4;
160  break;
161  }
162  default:
163  {
164  break;
165  }
166  }
167 
168  return value;
169 }
170 
172 {
173  return _bc_adc.channel_in_progress == BC_ADC_CHANNEL_NONE;
174 }
175 
176 bool bc_adc_get_value(bc_adc_channel_t channel, uint16_t *result)
177 {
178  // If ongoing conversion...
179  if (_bc_adc.channel_in_progress != BC_ADC_CHANNEL_NONE)
180  {
181  return false;
182  }
183 
184  // Set ADC channel
185  ADC1->CHSELR = _bc_adc.channel_table[channel].chselr;
186 
187  // Disable all ADC interrupts
188  ADC1->IER = 0;
189 
190  // Clear EOS flag (it is cleared by software writing 1 to it)
191  ADC1->ISR = ADC_ISR_EOS;
192 
193  // Clear ADRDY (it is cleared by software writing 1 to it)
194  ADC1->ISR |= ADC_ISR_ADRDY;
195 
196  _bc_adc_configure_oversampling(_bc_adc.channel_table[channel].oversampling);
197  _bc_adc_configure_resolution(_bc_adc.channel_table[channel].resolution);
198 
199  // Start the AD measurement
200  ADC1->CR |= ADC_CR_ADSTART;
201 
202  // wait for end of measurement
203  while ((ADC1->ISR & ADC_ISR_EOS) == 0)
204  {
205  continue;
206  }
207 
208  if (result != NULL)
209  {
210  *result = _bc_adc_get_measured_value(channel);
211  }
212 
213  return true;
214 }
215 
216 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)
217 {
218  // Check ongoing on edited channel
219  if (_bc_adc.channel_in_progress == channel)
220  {
221  return false;
222  }
223 
224  bc_adc_channel_config_t *adc = &_bc_adc.channel_table[channel];
225 
226  adc->event_handler = event_handler;
227  adc->event_param = event_param;
228 
229  return true;
230 }
231 
233 {
234  // If another conversion is ongoing...
235  if (_bc_adc.channel_in_progress != BC_ADC_CHANNEL_NONE)
236  {
237  _bc_adc.channel_table[channel].pending = true;
238 
239  return true;
240  }
241 
242  _bc_adc.channel_in_progress = channel;
243  _bc_adc.channel_table[channel].pending = false;
244 
245  // Skip cal and measure VREF + channel
246  //------------------------------------
247  _bc_adc.state = BC_ADC_STATE_CALIBRATION_BY_INTERNAL_REFERENCE_END;
248 
249  // Enable internal reference to ADC peripheral
250  ADC->CCR |= ADC_CCR_VREFEN;
251 
252  _bc_adc_configure_oversampling(_bc_adc.channel_table[BC_ADC_CHANNEL_INTERNAL_REFERENCE].oversampling);
253  _bc_adc_configure_resolution(_bc_adc.channel_table[BC_ADC_CHANNEL_INTERNAL_REFERENCE].resolution);
254 
255  // Set ADC channel
256  ADC1->CHSELR = _bc_adc.channel_table[BC_ADC_CHANNEL_INTERNAL_REFERENCE].chselr;
257 
258  // Clear end of calibration interrupt
259  ADC1->ISR = ADC_ISR_EOCAL;
260 
261  // Enable end of conversion interrupt
262  ADC1->IER = ADC_IER_EOCIE;
263 
264  // Begin internal reference reading
265  ADC1->CR |= ADC_CR_ADSTART;
266 
268 
269  return true;
270 }
271 
272 bool bc_adc_async_get_value(bc_adc_channel_t channel, uint16_t *result)
273 {
274  *result = _bc_adc.channel_table[channel].value;
275  return true;
276 }
277 
278 bool bc_adc_async_get_voltage(bc_adc_channel_t channel, float *result)
279 {
280  *result = (_bc_adc.channel_table[channel].value * _bc_adc.real_vdda_voltage) / 65536.f;
281  return true;
282 }
283 
284 bool bc_adc_get_vdda_voltage(float *vdda_voltage)
285 {
286  if (_bc_adc.real_vdda_voltage == 0.f)
287  {
288  return false;
289  }
290  else
291  {
292  *vdda_voltage = _bc_adc.real_vdda_voltage;
293 
294  return true;
295  }
296 }
297 
298 void ADC1_COMP_IRQHandler(void)
299 {
300  // Get real VDDA and begin analog channel measurement
301  if (_bc_adc.state == BC_ADC_STATE_CALIBRATION_BY_INTERNAL_REFERENCE_END)
302  {
303  // Compute actual VDDA
304  _bc_adc.real_vdda_voltage = 3.f * ((float) _bc_adc.vrefint / (float) ADC1->DR);
305 
306  _bc_adc_configure_oversampling(_bc_adc.channel_table[_bc_adc.channel_in_progress].oversampling);
307  _bc_adc_configure_resolution(_bc_adc.channel_table[_bc_adc.channel_in_progress].resolution);
308 
309  // Set ADC channel
310  ADC1->CHSELR = _bc_adc.channel_table[_bc_adc.channel_in_progress].chselr;
311 
312  _bc_adc.state = BC_ADC_STATE_MEASURE_INPUT;
313 
314  // Clear end of conversion interrupt
315  ADC1->ISR = ADC_ISR_EOC;
316 
317  // Begin adc input channel measurement
318  ADC1->CR |= ADC_CR_ADSTART;
319  }
320 
321  // Measurement is done, plan calling callback
322  else if (_bc_adc.state == BC_ADC_STATE_MEASURE_INPUT)
323  {
324  // Disable internal reference
325  ADC->CCR &= ~ADC_CCR_VREFEN;
326 
327  _bc_adc.channel_table[_bc_adc.channel_in_progress].value = _bc_adc_get_measured_value(_bc_adc.channel_in_progress);
328 
329  // Plan ADC task
330  bc_scheduler_plan_now(_bc_adc.task_id);
331 
332  // Clear all interrupts
333  ADC1->ISR = 0xffff;
334 
335  // Disable all ADC interrupts
336  ADC1->IER = 0;
337 
339  }
340 }
341 
343 {
344  if (_bc_adc.channel_in_progress != BC_ADC_CHANNEL_NONE)
345  {
346  return false;
347  }
348 
349  if (ADC1->CR & ADC_CR_ADEN)
350  {
351  return false;
352  }
353 
354  // Perform ADC calibration
355  ADC1->CR |= ADC_CR_ADCAL;
356  while ((ADC1->ISR & ADC_ISR_EOCAL) == 0)
357  {
358  continue;
359  }
360 
361  // Enable internal reference
362  ADC->CCR |= ADC_CCR_VREFEN;
363 
364  // Set ADC channel
365  ADC1->CHSELR = _bc_adc.channel_table[BC_ADC_CHANNEL_INTERNAL_REFERENCE].chselr;
366 
367  // Clear EOS flag (it is cleared by software writing 1 to it)
368  ADC1->ISR = ADC_ISR_EOS;
369 
370  // Perform measurement on internal reference
371  ADC1->CR |= ADC_CR_ADSTART;
372 
373  while ((ADC1->ISR & ADC_ISR_EOS) == 0)
374  {
375  continue;
376  }
377 
378  // Compute actual VDDA
379  _bc_adc.real_vdda_voltage = 3.f * ((float) _bc_adc.vrefint / (float) ADC1->DR);
380 
381  // Disable internal reference
382  ADC->CCR &= ~ADC_CCR_VREFEN;
383 
384  return true;
385 }
386 
387 static void _bc_adc_task(void *param)
388 {
389  (void) param;
390 
391  bc_adc_channel_config_t *adc = &_bc_adc.channel_table[_bc_adc.channel_in_progress];
392  bc_adc_channel_t pending_result_channel;
393  bc_adc_channel_t next;
394 
395  // Update pending channel result
396  pending_result_channel = _bc_adc.channel_in_progress;
397 
398  // Release ADC for further conversion
399  _bc_adc.channel_in_progress = BC_ADC_CHANNEL_NONE;
400 
401  // Disable interrupts
402  bc_irq_disable();
403 
404  // Get pending
405  if (_bc_adc_get_pending(&next, pending_result_channel) == true)
406  {
407  bc_adc_async_measure(next);
408  }
409 
410  // Enable interrupts
411  bc_irq_enable();
412 
413  // Perform event call-back
414  if (adc->event_handler != NULL)
415  {
416  adc->event_handler(pending_result_channel, BC_ADC_EVENT_DONE, adc->event_param);
417  }
418 }
419 
420 static inline bool _bc_adc_get_pending(bc_adc_channel_t *next ,bc_adc_channel_t start)
421 {
422  for (int i = start + 1; i != start; i++)
423  {
424  if (i == BC_ADC_CHANNEL_COUNT)
425  {
426  if (start == BC_ADC_CHANNEL_A0)
427  {
428  break;
429  }
430  else
431  {
432  i = BC_ADC_CHANNEL_A0;
433  }
434  }
435 
436  if (_bc_adc.channel_table[i].pending == true)
437  {
438  *next = i;
439 
440  return true;
441  }
442  }
443 
444  return false;
445 }
void bc_irq_enable(void)
Enable interrupt requests globally (call can be nested)
Definition: bc_irq.c:21
void bc_adc_resolution_set(bc_adc_channel_t channel, bc_adc_resolution_t resolution)
Set ADC resolution for specific channel.
Definition: bc_adc.c:104
ADC 256x oversampling.
Definition: bc_adc.h:64
bool bc_adc_calibration(void)
Calibration.
Definition: bc_adc.c:342
ADC 10 bit resolution.
Definition: bc_adc.h:76
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_scheduler_disable_sleep(void)
Disable sleep mode, implemented as semaphore.
Definition: bc_scheduler.c:109
bc_adc_oversampling_t
ADC oversampling.
Definition: bc_adc.h:37
ADC 6 bit resolution.
Definition: bc_adc.h:82
ADC channel A2.
Definition: bc_adc.h:22
ADC channel A4.
Definition: bc_adc.h:28
void bc_scheduler_enable_sleep(void)
Enable sleep mode, implemented as semaphore.
Definition: bc_scheduler.c:114
ADC 8 bit resolution.
Definition: bc_adc.h:79
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
void bc_adc_init()
Initialize ADC converter.
Definition: bc_adc.c:64
size_t bc_scheduler_task_id_t
Task ID assigned by scheduler.
Definition: bc_scheduler.h:18
ADC channel A5.
Definition: bc_adc.h:31
bool bc_adc_async_get_voltage(bc_adc_channel_t channel, float *result)
Get asynchronous measurement result in volts.
Definition: bc_adc.c:278
bool bc_adc_async_get_value(bc_adc_channel_t channel, uint16_t *result)
Get asynchronous measurement result.
Definition: bc_adc.c:272
ADC channel A0.
Definition: bc_adc.h:16
ADC channel A1.
Definition: bc_adc.h:19
#define BC_TICK_INFINITY
Maximum timestamp value.
Definition: bc_tick.h:12
bc_adc_resolution_t
ADC resolution.
Definition: bc_adc.h:70
bool bc_adc_get_vdda_voltage(float *vdda_voltage)
Get voltage on VDDA pin.
Definition: bc_adc.c:284
ADC 12 bit resolution.
Definition: bc_adc.h:73
void bc_irq_disable(void)
Disable interrupt requests globally (call can be nested)
Definition: bc_irq.c:7
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
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
ADC channel A3.
Definition: bc_adc.h:25
bc_adc_channel_t
ADC channel.
Definition: bc_adc.h:13
ADC event.
Definition: bc_adc.h:91
bool bc_adc_get_value(bc_adc_channel_t channel, uint16_t *result)
Reads the ADC channel value.
Definition: bc_adc.c:176
bool bc_adc_is_ready()
Check if ADC is ready for reading.
Definition: bc_adc.c:171