IoT-MBBR (masterthesis)
Masterthesis upgrading a Moving Bed BioReactor with extra sensors and cloud connectivity.
adc.c File Reference

ADC functionality for reading the (battery) voltage, internal temperature and CH4/5/6/7 voltages. More...

#include <stdint.h>
#include <stdbool.h>
#include "em_device.h"
#include "em_cmu.h"
#include "em_adc.h"
#include "adc.h"
#include "debug_dbprint.h"
#include "util.h"

Go to the source code of this file.

Macros

#define DBPRINT_TIMEOUT   0
 
#define TIMEOUT_CONVERSION   70
 

Functions

static float32_t convertToCelsius (int32_t adcSample)
 Method to convert an ADC value to a temperature value. More...
 
void initADC (ADC_Measurement_t peripheral)
 Method to initialize the ADC to later check the battery voltage or internal temperature. More...
 
int32_t readADC (ADC_Measurement_t peripheral)
 Method to read the battery voltage or internal temperature. More...
 
void ADC0_IRQHandler (void)
 Interrupt Service Routine for ADC0. More...
 

Variables

volatile bool adcConversionComplete = false
 
ADC_Init_TypeDef init = ADC_INIT_DEFAULT
 
ADC_InitSingle_TypeDef initSingle = ADC_INITSINGLE_DEFAULT
 

Detailed Description

ADC functionality for reading the (battery) voltage, internal temperature and CH4/5/6/7 voltages.

Version
3.0
Author
Brecht Van Eeckhoudt

Versions

  • v1.0: Moved ADC functionality from other.c to this file.
  • v1.1: Removed re-initialization dbprint messages.
  • v1.2: Started using custom enum type and cleaned up some unnecessary statements after testing.
  • v1.3: Changed error numbering.
  • v1.4: Added timeout to while loop and changed types to int32_t.
  • v1.5: Refined timout functionality.
  • v2.0: Disabled peripheral clock before entering an error function, added functionality to exit methods after error call and updated version number.
  • v2.1: Removed static before the local variables (not necessary).
  • v3.0: Added CH4/5/6/7 measurement capabilities.

License

Copyright (C) 2020 - Brecht Van Eeckhoudt

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

A copy of the GNU General Public License can be found in the LICENSE file along with this source code.


Some methods use code obtained from examples from Silicon Labs' GitHub. These sections are licensed under the Silabs License Agreement. See the file "Silabs_License_Agreement.txt" for details. Before using this software for any purpose, you must agree to the terms of that agreement.

Definition in file adc.c.

Macro Definition Documentation

◆ DBPRINT_TIMEOUT

#define DBPRINT_TIMEOUT   0

Enable (1) or disable (0) printing the timeout counter value using DBPRINT

Definition at line 64 of file adc.c.

◆ TIMEOUT_CONVERSION

#define TIMEOUT_CONVERSION   70

Maximum value for the counter before exiting a while loop

Definition at line 67 of file adc.c.

Function Documentation

◆ ADC0_IRQHandler()

void ADC0_IRQHandler ( void  )

Interrupt Service Routine for ADC0.

Definition at line 376 of file adc.c.

377 {
378  /* Read interrupt flags */
379  uint32_t flags = ADC_IntGet(ADC0);
380 
381  /* Clear the ADC0 interrupt flags */
382  ADC_IntClear(ADC0, flags);
383 
384  /* Indicate that an ADC conversion has been completed */
385  adcConversionComplete = true;
386 }
volatile bool adcConversionComplete
Definition: adc.c:71
unsigned int uint32_t
Typedef defining 32 bit unsigned int type. The developer should modify this to suit the platform bein...
Definition: vl53l1_types.h:78

◆ convertToCelsius()

static float32_t convertToCelsius ( int32_t  adcSample)
static

Method to convert an ADC value to a temperature value.

Note
This is a static method because it's only internally used in this file and called by other methods if necessary.
Parameters
[in]adcSampleThe ADC sample to convert to a temperature value.
Returns
The converted temperature value.

Definition at line 351 of file adc.c.

352 {
353  float32_t temp;
354 
355  /* Factory calibration temperature from device information page. */
356  int32_t cal_temp_0 = ((DEVINFO->CAL & _DEVINFO_CAL_TEMP_MASK)
357  >> _DEVINFO_CAL_TEMP_SHIFT);
358 
359  /* Factory calibration value from device information page. */
360  int32_t cal_value_0 = ((DEVINFO->ADC0CAL2 & _DEVINFO_ADC0CAL2_TEMP1V25_MASK)
361  >> _DEVINFO_ADC0CAL2_TEMP1V25_SHIFT);
362 
363  /* Temperature gradient (from datasheet) */
364  float32_t t_grad = -6.27;
365 
366  temp = (cal_temp_0 - ((cal_value_0 - adcSample) / t_grad));
367 
368  return (temp);
369 }
int int32_t
Typedef defining 32 bit int type. The developer should modify this to suit the platform being deploye...
Definition: vl53l1_types.h:83

◆ initADC()

void initADC ( ADC_Measurement_t  peripheral)

Method to initialize the ADC to later check the battery voltage or internal temperature.

Parameters
[in]peripheralSelect the ADC peripheral to initialize.

Definition at line 87 of file adc.c.

88 {
89  /* Enable necessary clocks (just in case) */
90  CMU_ClockEnable(cmuClock_HFPER, true); /* ADC0 is a High Frequency Peripheral */
91  CMU_ClockEnable(cmuClock_ADC0, true);
92 
93  /* Set a timebase providing at least 1 us.
94  * If the argument is "0" the currently defined HFPER clock setting is used. */
95  init.timebase = ADC_TimebaseCalc(0);
96 
97  /* Set a prescale value according to the ADC frequency (400 000 Hz) wanted.
98  * If the last argument is "0" the currently defined HFPER clock setting is for the calculation used. */
99  init.prescale = ADC_PrescaleCalc(400000, 0);
100 
101  /* Initialize ADC peripheral */
102  ADC_Init(ADC0, &init);
103 
104  /* Setup single conversions */
105 
106  /* initSingle.acqTime = adcAcqTime16;
107  * The statement above was found in a SiLabs example but DRAMCO disabled it.
108  * After testing this seemed to have no real effect so it was disabled.
109  * This is probably not necessary since a prescale value other than 0 (default) has been defined. */
110  if (peripheral == INTERNAL_TEMPERATURE) initSingle.input = adcSingleInpTemp; /* Internal temperature */
111  else if (peripheral == BATTERY_VOLTAGE) initSingle.input = adcSingleInpVDDDiv3; /* Internal VDD/3 */
112  else if (peripheral == CH1)
113  {
114  initSingle.input = adcSingleInputCh4; /* Channel 4 */
115  initSingle.reference = adcRef2V5; /* Internal 2V5 reference (1V25 default) */
116  }
117  else if (peripheral == CH2)
118  {
119  initSingle.input = adcSingleInputCh5; /* Channel 5 */
120  initSingle.reference = adcRef2V5; /* Internal 2V5 reference (1V25 default) */
121  }
122  else if (peripheral == CH3)
123  {
124  initSingle.input = adcSingleInputCh6; /* Channel 6 */
125  initSingle.reference = adcRef2V5; /* Internal 2V5 reference (1V25 default) */
126  }
127  else if (peripheral == CH4)
128  {
129  initSingle.input = adcSingleInputCh7; /* Channel 7 */
130  initSingle.reference = adcRef2V5; /* Internal 2V5 reference (1V25 default) */
131  }
132  else
133  {
134 
135 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
136  dbcrit("Unknown ADC peripheral selected!");
137 #endif /* DEBUG_DBPRINT */
138 
139  /* Disable used clock */
140  CMU_ClockEnable(cmuClock_ADC0, false);
141 
142  error(11);
143 
144  /* Exit function */
145  return;
146  }
147 
148  ADC_InitSingle(ADC0, &initSingle);
149 
150  /* Manually set some calibration values
151  * ADC0->CAL = (0x7C << _ADC_CAL_SINGLEOFFSET_SHIFT) | (0x1F << _ADC_CAL_SINGLEGAIN_SHIFT);
152  * The statement above was found in a SiLabs example but DRAMCO disabled it.
153  * After testing this seemed to throw off the first measurement so it was disabled. */
154 
155  /* Enable interrupt on completed conversion */
156  ADC_IntEnable(ADC0, ADC_IEN_SINGLE);
157  NVIC_ClearPendingIRQ(ADC0_IRQn);
158  NVIC_EnableIRQ(ADC0_IRQn);
159 
160  /* Disable used clock */
161  CMU_ClockEnable(cmuClock_ADC0, false);
162 
163 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
164  if (peripheral == INTERNAL_TEMPERATURE) dbinfo("ADC0 initialized for internal temperature");
165  else if (peripheral == BATTERY_VOLTAGE) dbinfo("ADC0 initialized for VBAT");
166  else if (peripheral == CH1) dbinfo("ADC0 initialized for CH1");
167  else if (peripheral == CH2) dbinfo("ADC0 initialized for CH2");
168  else if (peripheral == CH3) dbinfo("ADC0 initialized for CH3");
169  else if (peripheral == CH4) dbinfo("ADC0 initialized for CH4");
170 #endif /* DEBUG_DBPRINT */
171 
172 }
ADC_InitSingle_TypeDef initSingle
Definition: adc.c:73
Definition: adc.h:53
Definition: adc.h:50
void dbinfo(char *message)
Print an info string (char array) to USARTx and go to the next line.
Definition: dbprint.c:505
ADC_Init_TypeDef init
Definition: adc.c:72
void error(uint8_t number)
Error method.
Definition: util.c:122
Definition: adc.h:51
Definition: adc.h:52
void dbcrit(char *message)
Print a critical error string (char array) in red to USARTx and go to the next line.
Definition: dbprint.c:541

◆ readADC()

int32_t readADC ( ADC_Measurement_t  peripheral)

Method to read the battery voltage or internal temperature.

This method re-initializes the ADC settings if necessary.
Negative internal temperatures work fine.

Parameters
[in]peripheralSelect the ADC peripheral to read from.
Returns
The measured battery voltage or internal temperature.

Definition at line 189 of file adc.c.

190 {
191  uint16_t counter = 0; /* Timeout counter */
192  int32_t value = 0; /* Value to eventually return */
193 
194  /* Enable necessary clock */
195  CMU_ClockEnable(cmuClock_ADC0, true);
196 
197  /* Change ADC settings if necessary */
198  if (peripheral == INTERNAL_TEMPERATURE)
199  {
200  if (initSingle.input != adcSingleInpTemp)
201  {
202  initSingle.input = adcSingleInpTemp; /* Internal temperature */
203  initSingle.reference = adcRef1V25; /* Internal 1V25 reference (default) */
204  ADC_InitSingle(ADC0, &initSingle);
205  }
206  }
207  else if (peripheral == BATTERY_VOLTAGE)
208  {
209  if (initSingle.input != adcSingleInpVDDDiv3)
210  {
211  initSingle.input = adcSingleInpVDDDiv3; /* Internal VDD/3 */
212  initSingle.reference = adcRef1V25; /* Internal 1V25 reference (default) */
213  ADC_InitSingle(ADC0, &initSingle);
214  }
215  }
216  else if (peripheral == CH1)
217  {
218  if (initSingle.input != adcSingleInputCh4)
219  {
220  initSingle.input = adcSingleInputCh4; /* Channel 4 */
221  initSingle.reference = adcRef2V5; /* Internal 2V5 reference (1V25 default) */
222  ADC_InitSingle(ADC0, &initSingle);
223  }
224  }
225  else if (peripheral == CH2)
226  {
227  if (initSingle.input != adcSingleInputCh5)
228  {
229  initSingle.input = adcSingleInputCh5; /* Channel 5 */
230  initSingle.reference = adcRef2V5; /* Internal 2V5 reference (1V25 default) */
231  ADC_InitSingle(ADC0, &initSingle);
232  }
233  }
234  else if (peripheral == CH3)
235  {
236  if (initSingle.input != adcSingleInputCh6)
237  {
238  initSingle.input = adcSingleInputCh6; /* Channel 6 */
239  initSingle.reference = adcRef2V5; /* Internal 2V5 reference (1V25 default) */
240  ADC_InitSingle(ADC0, &initSingle);
241  }
242  }
243  else if (peripheral == CH4)
244  {
245  if (initSingle.input != adcSingleInputCh7)
246  {
247  initSingle.input = adcSingleInputCh7; /* Channel 7 */
248  initSingle.reference = adcRef2V5; /* Internal 2V5 reference (1V25 default) */
249  ADC_InitSingle(ADC0, &initSingle);
250  }
251  }
252  else
253  {
254 
255 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
256  dbcrit("Unknown ADC peripheral selected!");
257 #endif /* DEBUG_DBPRINT */
258 
259  /* Disable used clock */
260  CMU_ClockEnable(cmuClock_ADC0, false);
261 
262  error(12);
263 
264  /* Exit function */
265  return (0);
266  }
267 
268  /* Set variable false just in case */
269  adcConversionComplete = false;
270 
271  /* Start single ADC conversion */
272  ADC_Start(ADC0, adcStartSingle);
273 
274  /* Wait until the conversion is completed */
275  while ((counter < TIMEOUT_CONVERSION) && !adcConversionComplete) counter++;
276 
277  /* Exit the function if the maximum waiting time was reached */
278  if (counter == TIMEOUT_CONVERSION)
279  {
280 
281 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
282  dbcrit("Waiting time for ADC conversion reached!");
283 #endif /* DEBUG_DBPRINT */
284 
285  /* Disable used clock */
286  CMU_ClockEnable(cmuClock_ADC0, false);
287 
288  error(13);
289 
290  /* Exit function */
291  return (0);
292  }
293 #if DBPRINT_TIMEOUT == 1 /* DBPRINT_TIMEOUT */
294  else
295  {
296 
297 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
298  dbwarnInt("ADC conversion (", counter, ")");
299 #endif /* DEBUG_DBPRINT */
300 
301  }
302 #endif /* DBPRINT_TIMEOUT */
303 
304  /* Get the ADC value */
305  value = ADC_DataSingleGet(ADC0);
306 
307  dbwarnInt("ADC value = ", value, ""); // Debugging
308 
309  /* Disable used clock */
310  CMU_ClockEnable(cmuClock_ADC0, false);
311 
312  /* Calculate final value according to parameter */
313  if (peripheral == INTERNAL_TEMPERATURE)
314  {
315  float32_t ft = convertToCelsius(value)*1000;
316  value = (int32_t) ft;
317  }
318  else if (peripheral == BATTERY_VOLTAGE)
319  {
320  /* 12 bit: 2^12 = 4096 */
321  float32_t fv = (value * 3.75) / 4.096;
322  value = (int32_t) fv;
323  }
324  else if (peripheral == CH1 || peripheral == CH2 || peripheral == CH3 || peripheral == CH4)
325  {
326  /* 2.5 = reference voltage
327  * 12 bit: 2^12 = 4096 (comma placement is for int32_t conversion...) */
328  dbprintln("Here!");
329  float32_t fv = (value * 2.5) / 409.6;
330  value = (int32_t) fv;
331  }
332 
333  return (value);
334 }
void dbprintln(char *message)
Print a string (char array) to USARTx and go to the next line.
Definition: dbprint.c:397
ADC_InitSingle_TypeDef initSingle
Definition: adc.c:73
Definition: adc.h:53
void dbwarnInt(char *message1, int32_t value, char *message2)
Print a warning value surrounded by two strings (char array) to USARTx.
Definition: dbprint.c:600
Definition: adc.h:50
#define TIMEOUT_CONVERSION
Definition: adc.c:67
static float32_t convertToCelsius(int32_t adcSample)
Method to convert an ADC value to a temperature value.
Definition: adc.c:351
void error(uint8_t number)
Error method.
Definition: util.c:122
volatile bool adcConversionComplete
Definition: adc.c:71
int int32_t
Typedef defining 32 bit int type. The developer should modify this to suit the platform being deploye...
Definition: vl53l1_types.h:83
Definition: adc.h:51
Definition: adc.h:52
void dbcrit(char *message)
Print a critical error string (char array) in red to USARTx and go to the next line.
Definition: dbprint.c:541
unsigned short uint16_t
Typedef defining 16 bit unsigned short type. The developer should modify this to suit the platform be...
Definition: vl53l1_types.h:88

Variable Documentation

◆ adcConversionComplete

volatile bool adcConversionComplete = false

Definition at line 71 of file adc.c.

◆ init

ADC_Init_TypeDef init = ADC_INIT_DEFAULT

Definition at line 72 of file adc.c.

◆ initSingle

ADC_InitSingle_TypeDef initSingle = ADC_INITSINGLE_DEFAULT

Definition at line 73 of file adc.c.