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