Embedded System Design 2 - Project
DS18B20.c
Go to the documentation of this file.
1 /***************************************************************************//**
2  * @file DS18B20.c
3  * @brief All code for the DS18B20 temperature sensor.
4  * @version 3.1
5  * @author
6  * Alec Vanderhaegen & Sarah Goossens@n
7  * Modified by Brecht Van Eeckhoudt
8  *
9  * ******************************************************************************
10  *
11  * @section Versions
12  *
13  * @li v1.0: Reformatted existing methods to use pin_mapping.h, changed unsigned char to
14  * uint8_t values, added comments and cleaned up includes.
15  * @li v1.1: Added documentation, removed unnecessary GPIO statements regarding
16  * DOUT values of VDD pin.
17  * @li v1.2: Removed some unnecessary GPIO lines and added comments about `out` (DOUT) argument.
18  * @li v1.3: Changed some methods to be static (~hidden).
19  * @li v1.4: Cleaned up includes.
20  * @li v1.5: Made more methods static.
21  * @li v1.6: Updated documentation.
22  * @li v1.7: Started using new delay functionality.
23  * @li v1.8: Added line to disable DATA pin after a measurement, this breaks the code but fixes the sleep current.
24  * @li v1.9: Enabled and disabled timer each time a measurement is taken.
25  * @li v2.0: Updated documentation.
26  * @li v2.1: Changed method to return `uint32_t` instead of float.
27  * @li v2.2: Changed error numbering, moved definition from header to source file and updated header file include.
28  * @li v2.3: Changed *timeout* variable and changed types to `int32_t`.
29  * @li v2.4: Fixed temperature measurement and refined timeout functionality.
30  * @li v2.5: Updated documentation.
31  * @li v2.6: Updated code to don't execute code further if the first initialization failed.
32  * @li v3.0: Disabled initialized functionality before entering an `error` function, added
33  * functionality to exit methods after `error` call and updated version number.
34  * @li v3.1: Removed `static` before the local variable (not necessary).
35  *
36  * ******************************************************************************
37  *
38  * @section License
39  *
40  * **Copyright (C) 2019 - Brecht Van Eeckhoudt**
41  *
42  * This program is free software: you can redistribute it and/or modify
43  * it under the terms of the **GNU General Public License** as published by
44  * the Free Software Foundation, either **version 3** of the License, or
45  * (at your option) any later version.
46  *
47  * This program is distributed in the hope that it will be useful,
48  * but WITHOUT ANY WARRANTY; without even the implied warranty of
49  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
50  * GNU General Public License for more details.
51  *
52  * *A copy of the GNU General Public License can be found in the `LICENSE`
53  * file along with this source code.*
54  *
55  * @n
56  *
57  * Some methods also use code obtained from examples from [Silicon Labs' GitHub](https://github.com/SiliconLabs/peripheral_examples).
58  * These sections are licensed under the Silabs License Agreement. See the file
59  * "Silabs_License_Agreement.txt" for details. Before using this software for
60  * any purpose, you must agree to the terms of that agreement.
61  *
62  ******************************************************************************/
63 
64 
65 #include <stdint.h> /* (u)intXX_t */
66 #include <stdbool.h> /* "bool", "true", "false" */
67 #include "em_cmu.h" /* Clock Management Unit */
68 #include "em_gpio.h" /* General Purpose IO (GPIO) peripheral API */
69 
70 #include "DS18B20.h" /* Corresponding header file */
71 #include "pin_mapping.h" /* PORT and PIN definitions */
72 #include "debug_dbprint.h" /* Enable or disable printing to UART */
73 #include "delay.h" /* Delay functionality */
74 #include "util.h" /* Utility functionality */
75 #include "ustimer.h" /* Timer functionality */
76 
77 
78 /* Local definitions */
79 /** Enable (1) or disable (0) printing the timeout counter value using DBPRINT */
80 #define DBPRINT_TIMEOUT 0
81 
82 /* Maximum values for the counters before exiting a `while` loop */
83 #define TIMEOUT_INIT 20
84 #define TIMEOUT_CONVERSION 500 /* 12 bit resolution (reset default) = 750 ms max resolving time */
85 
86 
87 /* Local variable */
89 
90 
91 /* Local prototypes */
92 static void powerDS18B20 (bool enabled);
93 static bool init_DS18B20 (void);
94 static void writeByteToDS18B20 (uint8_t data);
95 static uint8_t readByteFromDS18B20 (void);
96 static int32_t convertTempData (uint8_t tempLS, uint8_t tempMS);
97 
98 
99 /**************************************************************************//**
100  * @brief
101  * Get a temperature value from the DS18B20.
102  *
103  * @details
104  * USTimer gets initialized, the sensor gets powered, the data-transmission
105  * takes place, the timer gets de-initialized to disable the clocks and interrupts,
106  * the data and power pin get disabled and finally the read values are converted
107  * to an `int32_t` value.@n
108  * **Negative temperatures work fine.**
109  *
110  * @return
111  * The read temperature data.
112  *****************************************************************************/
113 int32_t readTempDS18B20 (void)
114 {
115  /* Timeout counter */
116  uint16_t counter = 0;
117 
118  /* Variable to indicate if a conversion has been completed */
119  bool conversionCompleted = false;
120 
121  /* Variable to hold raw data bytes */
122  uint8_t rawDataFromDS18B20Arr[9] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
123 
124  /* Initialize timer
125  * Initializing and disabling the timer again adds about 40 µs active time but should conserve sleep energy... */
126  USTIMER_Init();
127 
128  /* Initialize and power VDD pin */
129  powerDS18B20(true);
130 
131  /* Power-up delay of 5 ms */
132  delay(5);
133 
134  /* Initialize communication and only continue if successful */
135  if (init_DS18B20())
136  {
137  writeByteToDS18B20(0xCC); /* 0xCC = "Skip Rom" (address all devices on the bus simultaneously without sending out any ROM code information) */
138  writeByteToDS18B20(0x44); /* 0x44 = "Convert T" */
139 
140  /* MASTER now generates "read time slots", the DS18B20 will write HIGH to the bus if the conversion is completed
141  * The datasheet gives the following directions for time slots, but reading bytes also seems to work...
142  * - Read time slots have a 60 µs duration and 1 µs recovery between slots
143  * - After the master pulls the line low for 1 µs, the data is valid for up to 15 µs */
144  while ((counter < TIMEOUT_CONVERSION) && !conversionCompleted)
145  {
146  uint8_t testByte = readByteFromDS18B20();
147  if (testByte > 0) conversionCompleted = true;
148 
149  counter++;
150  }
151 
152  /* Exit the function if the maximum waiting time was reached */
153  if (counter == TIMEOUT_CONVERSION)
154  {
155 
156 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
157  dbcrit("Waiting time for DS18B20 conversion reached!");
158 #endif /* DEBUG_DBPRINT */
159 
160  /* Disable interrupts and turn off the clock to the underlying hardware timer. */
161  USTIMER_DeInit();
162 
163  /* Disable data pin (otherwise we got a "sleep" current of about 330 µA due to the on-board 10k pull-up) */
164  GPIO_PinModeSet(TEMP_DATA_PORT, TEMP_DATA_PIN, gpioModeDisabled, 0);
165 
166  /* Disable the VDD pin */
167  powerDS18B20(false);
168 
169  error(29);
170 
171  /* Exit function */
172  return (0);
173 
174  }
175 #if DBPRINT_TIMEOUT == 1 /* DBPRINT_TIMEOUT */
176  else
177  {
178 
179 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
180  dbwarnInt("DS18B20 conversion (", counter, ")");
181 #endif /* DEBUG_DBPRINT */
182 
183  }
184 #endif /* DBPRINT_TIMEOUT */
185 
186  init_DS18B20(); /* Initialize communication */
187  writeByteToDS18B20(0xCC); /* 0xCC = "Skip Rom" */
188  writeByteToDS18B20(0xBE); /* 0xCC = "Read Scratchpad" */
189 
190  /* Read the bytes */
191  for (uint8_t i = 0; i < 9; i++) rawDataFromDS18B20Arr[i] = readByteFromDS18B20();
192 
193  /* Disable interrupts and turn off the clock to the underlying hardware timer. */
194  USTIMER_DeInit();
195 
196  /* Disable data pin (otherwise we got a "sleep" current of about 330 µA due to the on-board 10k pull-up) */
197  GPIO_PinModeSet(TEMP_DATA_PORT, TEMP_DATA_PIN, gpioModeDisabled, 0);
198 
199  /* Disable the VDD pin */
200  powerDS18B20(false);
201 
202  /* Return the converted byte */
203  return (convertTempData(rawDataFromDS18B20Arr[0], rawDataFromDS18B20Arr[1]));
204  }
205  else
206  {
207  /* Disable interrupts and turn off the clock to the underlying hardware timer. */
208  USTIMER_DeInit();
209 
210  /* Disable data pin (otherwise we got a "sleep" current of about 330 µA due to the on-board 10k pull-up) */
211  GPIO_PinModeSet(TEMP_DATA_PORT, TEMP_DATA_PIN, gpioModeDisabled, 0);
212 
213  /* Disable the VDD pin */
214  powerDS18B20(false);
215 
216  /* Exit function */
217  return (0);
218  }
219 }
220 
221 
222 /**************************************************************************//**
223  * @brief
224  * Enable or disable the power to the temperature sensor.
225  *
226  * @details
227  * This method also initializes the pin-mode if necessary.
228  *
229  * @note
230  * This is a static method because it's only internally used in this file
231  * and called by other methods if necessary.
232  *
233  * @param[in] enabled
234  * @li `true` - Enable the GPIO pin connected to the VDD pin of the temperature sensor.
235  * @li `talse` - Disable the GPIO pin connected to the VDD pin of the temperature sensor.
236  *****************************************************************************/
237 static void powerDS18B20 (bool enabled)
238 {
239  /* Enable necessary clocks (just in case) */
240  CMU_ClockEnable(cmuClock_HFPER, true); /* GPIO is a High Frequency Peripheral */
241  CMU_ClockEnable(cmuClock_GPIO, true);
242 
243  /* Initialize VDD pin if not already the case */
245  {
246  /* In the case of gpioModePushPull", the last argument directly sets the pin state */
247  GPIO_PinModeSet(TEMP_VDD_PORT, TEMP_VDD_PIN, gpioModePushPull, enabled);
248 
250  }
251  else
252  {
253  if (enabled) GPIO_PinOutSet(TEMP_VDD_PORT, TEMP_VDD_PIN); /* Enable VDD pin */
254  else GPIO_PinOutClear(TEMP_VDD_PORT, TEMP_VDD_PIN); /* Disable VDD pin */
255  }
256 }
257 
258 
259 /**************************************************************************//**
260  * @brief
261  * Initialize communication to the DS18B20.
262  *
263  * @note
264  * This is a static method because it's only internally used in this file
265  * and called by other methods if necessary.
266  *
267  * @return
268  * @li `true` - *Presence* pulse detected in time.
269  * @li `false` - No *presence* pulse detected.
270  *****************************************************************************/
271 static bool init_DS18B20 (void)
272 {
273  /* Timeout counter */
274  uint32_t counter = 0;
275 
276  /* MASTER RESET: Pull data line LOW for at least 480 µs (Master TX) */
277  GPIO_PinModeSet(TEMP_DATA_PORT, TEMP_DATA_PIN, gpioModePushPull, 0); /* gpioModePushPull: Last argument directly sets the pin state */
278  USTIMER_DelayIntSafe(480);
279 
280  /* Change pin-mode to input - External pull-up resistor pulls data line back HIGH */
281  GPIO_PinModeSet(TEMP_DATA_PORT, TEMP_DATA_PIN, gpioModeInput, 0);
282 
283  /* Check if the line becomes LOW (~ wait while it stays high) during the maximum waiting time
284  * The DS18B20 should detect the data line rising due to the pull-up resistor, waits 15 - 50 µs
285  * and then pulls the line back LOW (for 60 - 240 µs) to indicate it's PRESENCE */
286  while ((counter < TIMEOUT_INIT) && (GPIO_PinInGet(TEMP_DATA_PORT, TEMP_DATA_PIN) == 1)) counter++;
287 
288  /* Exit the function if the maximum waiting time was reached */
289  if (counter == TIMEOUT_INIT)
290  {
291 
292 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
293  dbcrit("No DS18B20 presence pulse detected in time!");
294 #endif /* DEBUG_DBPRINT */
295 
296  error(28);
297 
298  return (false);
299  }
300 #if DBPRINT_TIMEOUT == 1 /* DBPRINT_TIMEOUT */
301  else
302  {
303 
304 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
305  dbwarnInt("DS18B20 INIT (", counter, ")");
306 #endif /* DEBUG_DBPRINT */
307 
308  }
309 #endif /* DBPRINT_TIMEOUT */
310 
311  /* Master RX should be at least 480 µs */
312  USTIMER_DelayIntSafe(480);
313 
314  return (true);
315 }
316 
317 
318 /**************************************************************************//**
319  * @brief
320  * Write a byte (`uint8_t`) to the DS18B20.
321  *
322  * @note
323  * This is a static method because it's only internally used in this file
324  * and called by other methods if necessary.
325  *
326  * @param[in] data
327  * The data to write to the DS18B20.
328  *****************************************************************************/
329 static void writeByteToDS18B20 (uint8_t data)
330 {
331  /* In the case of gpioModePushPull", the last argument directly sets the pin state */
332  GPIO_PinModeSet(TEMP_DATA_PORT, TEMP_DATA_PIN, gpioModePushPull, 0);
333 
334  /* Write the byte, bit by bit */
335  for (uint8_t i = 0; i < 8; i++)
336  {
337  /* Check if we need to write a "1" */
338  if (data & 0x01)
339  {
340  GPIO_PinOutClear(TEMP_DATA_PORT, TEMP_DATA_PIN);
341 
342  /* 5 µs delay should be called here but this loop works fine too... */
343  for (uint8_t i=0; i<5; i++);
344 
345  GPIO_PinOutSet(TEMP_DATA_PORT, TEMP_DATA_PIN);
346  USTIMER_DelayIntSafe(60);
347  }
348  /* If not, write a "0" */
349  else
350  {
351  GPIO_PinOutClear(TEMP_DATA_PORT, TEMP_DATA_PIN);
352  USTIMER_DelayIntSafe(60);
353  GPIO_PinOutSet(TEMP_DATA_PORT, TEMP_DATA_PIN);
354 
355  /* 5 µs delay should be called here but this loop works fine too... */
356  for (uint8_t i=0; i<5; i++);
357  }
358  /* Right shift bits once */
359  data >>= 1;
360  }
361 
362  /* Set data line high */
363  GPIO_PinOutSet(TEMP_DATA_PORT, TEMP_DATA_PIN);
364 }
365 
366 
367 /**************************************************************************//**
368  * @brief
369  * Read a byte (`uint8_t`) from the DS18B20.
370  *
371  * @note
372  * This is a static method because it's only internally used in this file
373  * and called by other methods if necessary.
374  *
375  * @return
376  * The byte read from the DS18B20.
377  *****************************************************************************/
378 static uint8_t readByteFromDS18B20 (void)
379 {
380  /* Data to eventually return */
381  uint8_t data = 0x0;
382 
383  /* Read the byte, bit by bit */
384  for (uint8_t i = 0; i < 8; i++)
385  {
386  /* Change pin-mode to input */
387  GPIO_PinModeSet(TEMP_DATA_PORT, TEMP_DATA_PIN, gpioModeInput, 0);
388 
389  /* Right shift bits once */
390  data >>= 1;
391 
392  /* If the line is high, OR the first bit of the data:
393  * 0x80 = 1000 0000 */
394  if (GPIO_PinInGet(TEMP_DATA_PORT, TEMP_DATA_PIN)) data |= 0x80;
395 
396  /* In the case of gpioModePushPull", the last argument directly sets the pin state */
397  GPIO_PinModeSet(TEMP_DATA_PORT, TEMP_DATA_PIN, gpioModePushPull, 1);
398 
399  /* Wait some time before going into next loop */
400  USTIMER_DelayIntSafe(70);
401  }
402  return (data);
403 }
404 
405 
406 /**************************************************************************//**
407  * @brief
408  * Convert temperature data.
409  *
410  * @note
411  * This is a static method because it's only internally used in this file
412  * and called by other methods if necessary.
413  *
414  * @param[in] tempLS
415  * Least significant byte.
416  *
417  * @param[in] tempMS
418  * Most significant byte.
419  *
420  * @return
421  * The converted temperature data.
422  *****************************************************************************/
423 static int32_t convertTempData (uint8_t tempLS, uint8_t tempMS)
424 {
425  uint16_t rawDataMerge;
426  uint16_t reverseRawDataMerge;
427 
428  int32_t finalTemperature;
429 
430  /* Check if it is a negative temperature value
431  * 0xF8 = 0b1111 1000 */
432  if (tempMS & 0xF8)
433  {
434  rawDataMerge = tempMS;
435 
436  /* Left shift 8 times */
437  rawDataMerge <<= 8;
438 
439  /* Add the second part */
440  rawDataMerge += tempLS;
441 
442  /* Invert the value since we have a negative temperature */
443  reverseRawDataMerge = ~rawDataMerge;
444 
445  /* Calculate the final temperature */
446  finalTemperature = -(reverseRawDataMerge + 1) * 62.5;
447  }
448  /* We're dealing with a positive temperature */
449  else
450  {
451  rawDataMerge = tempMS;
452 
453  /* Left shift 8 times */
454  rawDataMerge <<= 8;
455 
456  /* Add the second part */
457  rawDataMerge += tempLS;
458 
459  /* Calculate the final temperature */
460  finalTemperature = rawDataMerge * 62.5;
461  }
462 
463  return (finalTemperature);
464 }
static void writeByteToDS18B20(uint8_t data)
Write a byte (uint8_t) to the DS18B20.
Definition: DS18B20.c:329
MeasurementData_t data
Definition: main.c:189
void dbwarnInt(char *message1, int32_t value, char *message2)
Print a warning value surrounded by two strings (char array) to USARTx.
Definition: dbprint.c:598
static void powerDS18B20(bool enabled)
Enable or disable the power to the temperature sensor.
Definition: DS18B20.c:237
#define TIMEOUT_CONVERSION
Definition: DS18B20.c:84
void error(uint8_t number)
Error method.
Definition: util.c:131
static uint8_t readByteFromDS18B20(void)
Read a byte (uint8_t) from the DS18B20.
Definition: DS18B20.c:378
Utility functionality.
static int32_t convertTempData(uint8_t tempLS, uint8_t tempMS)
Convert temperature data.
Definition: DS18B20.c:423
The pin definitions for the regular and custom Happy Gecko board.
bool DS18B20_VDD_initialized
Definition: DS18B20.c:88
Delay functionality.
Enable or disable printing to UART with dbprint.
void delay(uint32_t msDelay)
Wait for a certain amount of milliseconds in EM2/3.
Definition: delay.c:124
#define TEMP_DATA_PIN
Definition: pin_mapping.h:104
#define TEMP_DATA_PORT
Definition: pin_mapping.h:103
int32_t readTempDS18B20(void)
Get a temperature value from the DS18B20.
Definition: DS18B20.c:113
#define TEMP_VDD_PIN
Definition: pin_mapping.h:106
#define TIMEOUT_INIT
Definition: DS18B20.c:83
#define TEMP_VDD_PORT
Definition: pin_mapping.h:105
static bool init_DS18B20(void)
Initialize communication to the DS18B20.
Definition: DS18B20.c:271
void dbcrit(char *message)
Print a critical error string (char array) in red to USARTx and go to the next line.
Definition: dbprint.c:539
All code for the DS18B20 temperature sensor.