Contiki 3.x
temp-sensor.c
1 /*
2  * Copyright (c) 2015, Copyright Per Lindgren <per.o.lindgren@gmail.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  *
32  * Author : Per Lindgren <per.o.lindgren@gmail.com>
33  * Hacked by: Robert Olsson robert@radio-sensors.com
34  * Created : 2015-11-22
35  */
36 
37 #include "contiki.h"
38 #include "dev/temp-sensor.h"
39 #include <util/delay_basic.h>
40 #define delay_us(us) (_delay_loop_2(1 + (us * F_CPU) / 4000000UL))
41 
42 const struct sensors_sensor temp_mcu_sensor;
43 
44 /* probe_for_ds18b20 probes for the sensor. Returns 0 on failure, 1 on success
45  * Assumptions: only one sensor on the "1-wire bus", on port WSN_DS18B20_PORT
46  * BUG: THIS CODE DOES NOT WORK AS INTENDED! IT RETURNS "1" EVEN WHEN THERE
47  * IS NO SENSOR CONNECTED.
48  */
49 
50 uint8_t
51 ds18b20_probe(void)
52 {
53  uint8_t result = 0;
54 
55  /* Reset 1W-bus */
56 
57  /* Pull PIN low for 480 microseconds (us)
58  * Start with setting bit DS18B20_1_PIN to 0 */
59  OW_SET_PIN_LOW();
60  /* then set direction to OUT by setting DS18B20_1_DDR bit to 1 */
61  OW_SET_OUTPUT();
62  /* Delay 480 us */
63  clock_delay_usec(480);
64  /* See if sensor responds. First release the bus and switch to INput mode
65  * by setting DS18B20_1_DDR bit to 0 */
66  OW_SET_INPUT();
67  /* Activate internal pull-up by setting pin to HIGH (when in INput mode)
68  * OW_SET_PIN_HIGH();
69  * Wait for the pin to go HIGH for 64 us */
70  clock_delay_usec(64);
71  /* Now the sensor, if present, pulls the pin LOW for 60-240 us
72  * Detect 0 on PIND bit DS18B20_1_PIN. Invert the result so a presence
73  * (aka * a 0) sets "result" to 1 (for success) */
74  result = !OW_GET_PIN_STATE();
75 
76  /* The sensor releases the pin so it goes HIGH after 240 us, add some
77  for the signal to stabilize, say 300 usecs to be on the safe side? */
78  if(result) {
79  clock_delay_usec(300);
80  /* Now the bus should be HIGH again */
81  result = OW_GET_PIN_STATE();
82  }
83  return result;
84 }
85 /* Write 1 or 0 on the bus */
86 
87 void
88 write_bit(uint8_t bit)
89 {
90  /* Set pin to 0 */
91  OW_SET_OUTPUT();
92  OW_SET_PIN_LOW();
93 
94  /* Pin should be 0 for at least 1 us */
96 
97  /* If we're writing a 1, let interna pull-up pull the bus high
98  * within 15 us of setting the bus to low */
99  if(bit) {
100  /* Internal pull-up is activated by setting direction to IN and the
101  * setting the pin to HIGH */
102  OW_SET_INPUT();
103  OW_SET_PIN_HIGH();
104  }
105  /* OK, now the bus is either LOW, or pulled HIGH by the internal pull-up
106  * Let this state remain for 60 us, then release the bus */
107  clock_delay_usec(60);
108 
109  /* Release the bus */
110  OW_SET_PIN_HIGH();
111  OW_SET_INPUT();
112 
113  /* Allow > 1 us between read/write operations */
114  clock_delay_usec(2);
115 }
116 /* Read one bit of information from the bus, and return it as 1 or 0 */
117 
118 uint8_t
119 read_bit(void)
120 {
121  uint8_t bit = 0;
122 
123  /* Set pin to 0 */
124  OW_SET_OUTPUT();
125  OW_SET_PIN_LOW();
126 
127  /* Pin should be 0 for at least 1 us */
128  clock_delay_usec(2);
129 
130  /* Now read the bus, start by setting in/out direction and activating
131  * internal pull-up resistor */
132  OW_SET_INPUT();
133  OW_SET_PIN_HIGH();
134 
135  /* ds18b20 either keeps the pin down or releases the bus and the
136  * bus then goes high because of the interna pull-up resistor
137  * Check whichever happens before 15 us has passed */
138  clock_delay_usec(15 - 2 - 1);
139  bit = OW_GET_PIN_STATE();
140 
141  /* The complete read cycle must last at least 60 us. We have now spent
142  * about 14-15 us in delays, so add another delay to reach >= 60 us */
143  clock_delay_usec(50);
144 
145  /* Release bus */
146  OW_SET_PIN_HIGH();
147  OW_SET_INPUT();
148 
149  /* Allow > 1 us between read/write operations */
150  clock_delay_usec(2);
151 
152  return bit ? 1 : 0;
153 }
154 /* Read one byte of information. A byte is read least significant bit first */
155 
156 uint8_t
157 read_byte(void)
158 {
159  uint8_t result = 0;
160  uint8_t bit;
161  int i;
162 
163  for(i = 0; i < 8; i++) {
164  bit = read_bit();
165  result += (bit << i);
166  }
167  return result;
168 }
169 /* Write one byte of information. A byte is written least significant bit first */
170 
171 void
172 write_byte(uint8_t byte)
173 {
174  int i;
175 
176  for(i = 0; i < 8; i++) {
177  write_bit((byte >> i) & 1);
178  }
179 }
180 /* ds18b20_get_temp returns the temperature in "temp" (in degrees celsius)
181  * Returns 0 on failure (and then "temp" is left unchanged
182  * Returns 1 on success, and sets temp */
183 
184 uint8_t
185 ds18b20_get_temp(double *temp)
186 {
187  uint8_t result = 0;
188 
189  /* Reset bus by probing. Probe returns 1 on success/presence of sensor */
190  if(ds18b20_probe()) {
191  /* write command "skip rom" since we only have one sensor on the wire! */
192  write_byte(DS18B20_COMMAND_SKIP_ROM);
193 
194  /* write command to start measurement */
195  write_byte(DS18B20_COMMAND_START_CONVERSION);
196 
197  /* Wait for conversion to complete. Conversion is 12-bit by default.
198  * Since we have external power to the sensor (ie not in "parasitic power"
199  * mode) the bus is held LOW by the sensor while the conversion is going
200  * on, and then HIGH when conversion is finished. */
201  OW_SET_INPUT();
202  int count = 0;
203  while(!OW_GET_PIN_STATE()) {
204  clock_delay_msec(10);
205  count++;
206  /* Longest conversion time is 750 ms (12-bit resolution)
207  * So if count > 80 (for a little margin!), we return -274.0
208  * which indicates failure to read the temperature. */
209  if(count > 80) {
210  return 0;
211  }
212  }
213 
214  /* The result is stored in the "scratch pad", a 9 byte memory block.
215  * The first two bytes are the conversion result. Reading the scratch pad
216  * can be terminated by sending a reset signal (but we read all 9 bytes) */
217  (void)ds18b20_probe();
218  write_byte(DS18B20_COMMAND_SKIP_ROM);
219  write_byte(DS18B20_COMMAND_READ_SCRATCH_PAD);
220  uint8_t i, sp_arr[9];
221  for(i = 0; i < 9; i++) {
222  sp_arr[i] = read_byte();
223  }
224 
225  /* Check CRC, if mismatch, return 0 (failure to read temperature) */
226  uint8_t crc_cal = crc8_ds18b20(sp_arr, 8);
227 
228  if(crc_cal != sp_arr[8]) {
229  return 0;
230  }
231 
232  /* OK, now decode what the temperature reading is. This code assumes
233  * 12-bit resolution, so this must be modified if the code is modified
234  * to use any other resolution! */
235  int16_t temp_res;
236  uint8_t temp_lsb = sp_arr[0];
237  uint8_t temp_msb = sp_arr[1];
238 
239  temp_res = (int16_t)temp_msb << 8 | temp_lsb;
240  *temp = (double)temp_res * 0.0625;
241 
242  result = 1;
243  }
244  return result;
245 }
246 /* crc8 algorithm for ds18b20 */
247 /* http://www.miscel.dk/MiscEl/CRCcalculations.html */
248 
249 uint8_t
250 crc8_ds18b20(uint8_t *buf, uint8_t buf_len)
251 {
252  uint8_t result = 0;
253  uint8_t i, b;
254 
255  for(i = 0; i < buf_len; i++) {
256  result = result ^ buf[i];
257  for(b = 1; b < 9; b++) {
258  if(result & 0x1) {
259  result = (result >> 1) ^ 0x8C;
260  } else {
261  result = result >> 1;
262  }
263  }
264  }
265  return result;
266 }
267 static int
268 value(int type)
269 {
270  double t;
271  int ret;
272  ret = ds18b20_get_temp(&t);
273 
274  /* Return temp multiplied by 100 for two decimals */
275  if(ret)
276  return (int) (t * 100);
277 
278  /* Error return largest negative value */
279  return 0x8000;
280 }
281 static int
282 configure(int type, int c)
283 {
284  ds18b20_probe();
285  return 0;
286 }
287 static int
288 status(int type)
289 {
290  return 1;
291 }
292 SENSORS_SENSOR(temp_sensor, TEMP_SENSOR, value, configure, status);
void clock_delay_msec(uint16_t howlong)
Delay up to 65535 milliseconds.
Definition: clock.c:260
static volatile clock_time_t count
These routines define the AVR-specific calls declared in /core/sys/clock.h CLOCK_SECOND is the number...
Definition: clock.c:80
void clock_delay_usec(uint16_t dt)
Delay a given number of microseconds.
Definition: clock.c:94