]> mj.ucw.cz Git - home-hw.git/blob - lib/ds18b20.c
8f6b550e1b7f665ab4f01cd644fba21f3e91df53
[home-hw.git] / lib / ds18b20.c
1 /*
2  *      Interface to DS18B20 Temperature Sensors
3  *
4  *      (c) 2019 Martin Mareš <mj@ucw.cz>
5  */
6
7 #include "util.h"
8 #include "ds18b20.h"
9 #include "ext-timer.h"
10
11 #include <libopencm3/cm3/cortex.h>
12 #include <libopencm3/stm32/dma.h>
13 #include <libopencm3/stm32/gpio.h>
14 #include <libopencm3/stm32/rcc.h>
15 #include <string.h>
16
17 /*** Configuration ***/
18
19 // You should set the following parameters in config.h
20
21 // #define DS_TIMER TIM3
22 // #define DS_GPIO GPIOA
23 // #define DS_PIN GPIO7
24 // #define DS_DMA DMA1
25 // #define DS_DMA_CH 6
26
27 // #undef DS_DEBUG
28 // #undef DS_DEBUG2
29
30 // Maximum number of supported sensors
31 // #define DS_NUM_SENSORS 8
32
33 #ifdef DS_DEBUG
34 #define DEBUG debug_printf
35 #else
36 #define DEBUG(xxx, ...) do { } while (0)
37 #endif
38
39 #ifdef DS_DEBUG2
40 #define DEBUG2 debug_printf
41 #else
42 #define DEBUG2(xxx, ...) do { } while (0)
43 #endif
44
45 static volatile u32 ds_dma_buffer;
46
47 static bool ds_reset(void)
48 {
49         DEBUG2("DS18B20: Reset\n");
50         timer_disable_counter(DS_TIMER);
51         timer_one_shot_mode(DS_TIMER);
52
53         // DMA for reading pin state
54         ds_dma_buffer = 0xdeadbeef;
55         dma_set_memory_address(DS_DMA, DS_DMA_CH, (u32) &ds_dma_buffer);
56         dma_set_peripheral_address(DS_DMA, DS_DMA_CH, (u32) &GPIO_IDR(DS_GPIO));
57         dma_set_number_of_data(DS_DMA, DS_DMA_CH, 1);
58         dma_enable_channel(DS_DMA, DS_DMA_CH);
59
60         // CC1 is used to drive the DMA (read line state at specified time)
61         timer_disable_oc_output(DS_TIMER, TIM_OC1);
62         timer_set_oc_mode(DS_TIMER, TIM_OC1, TIM_OCM_FROZEN);
63         timer_set_oc_value(DS_TIMER, TIM_OC1, 560);
64         timer_set_dma_on_compare_event(DS_TIMER);
65         timer_enable_dma_cc1(DS_TIMER);
66
67         // CC2 is used to generate pulses (return line to idle state at specified time)
68         timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
69         timer_enable_oc_output(DS_TIMER, TIM_OC2);
70         timer_set_oc_value(DS_TIMER, TIM_OC2, 480);
71         timer_set_oc_polarity_low(DS_TIMER, TIM_OC2);
72
73         // Set timer period to the length of the whole transaction (1 ms)
74         timer_set_period(DS_TIMER, 999);
75
76         // Pull line down and start timer
77         cm_disable_interrupts();
78         timer_enable_counter(DS_TIMER);
79         timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
80         cm_enable_interrupts();
81
82         // Wait until the timer expires
83         while (timer_is_counter_enabled(DS_TIMER))
84                 ;
85         // Counter is automatically disabled at the end of cycle
86
87         // Disable DMA
88         timer_disable_dma_cc1(DS_TIMER);
89         dma_disable_channel(DS_DMA, DS_DMA_CH);
90
91         DEBUG2("Init DMA: %08x [%u] (%u remains)\n",
92                ds_dma_buffer,
93                !!(ds_dma_buffer & DS_PIN),
94                dma_get_number_of_data(DS_DMA, DS_DMA_CH));
95
96         // Did the device respond?
97         if (ds_dma_buffer & DS_PIN) {
98                 DEBUG("DS18B20: Initialization failed\n");
99                 return 0;
100         } else
101                 return 1;
102 }
103
104 static void ds_send_bit(bool bit)
105 {
106         timer_set_period(DS_TIMER, 99);                         // Each write slot takes 100 μs
107         timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
108         timer_set_oc_value(DS_TIMER, TIM_OC2, (bit ? 3 : 89));  // 1: 3μs pulse, 0: 89μs pulse
109         cm_disable_interrupts();
110         // XXX: On STM32F1, we must configure the OC channel _after_ we enable the counter,
111         // otherwise OC triggers immediately. Reasons?
112         timer_enable_counter(DS_TIMER);
113         timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
114         cm_enable_interrupts();
115         while (timer_is_counter_enabled(DS_TIMER))
116                 ;
117 }
118
119 static void ds_send_byte(byte b)
120 {
121         DEBUG2("DS write: %02x\n", b);
122         for (uint m = 1; m < 0x100; m <<= 1)
123                 ds_send_bit(b & m);
124 }
125
126 static bool ds_recv_bit(void)
127 {
128         timer_set_period(DS_TIMER, 79);                 // Each read slot takes 80μs
129         timer_set_oc_value(DS_TIMER, TIM_OC2, 2);       // Generate 2μs pulse to start read slot
130         timer_set_oc_value(DS_TIMER, TIM_OC1, 8);       // Sample data 8μs after start of slot
131         timer_enable_dma_cc1(DS_TIMER);
132
133         ds_dma_buffer = 0xdeadbeef;
134         dma_set_number_of_data(DS_DMA, DS_DMA_CH, 1);
135         dma_enable_channel(DS_DMA, DS_DMA_CH);
136         timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
137         cm_disable_interrupts();
138         timer_enable_counter(DS_TIMER);
139         timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
140         cm_enable_interrupts();
141         while (timer_is_counter_enabled(DS_TIMER))
142                 ;
143         // DEBUG2("XXX %08x\n", ds_dma_buffer);
144         bool out = ds_dma_buffer & DS_PIN;
145         dma_disable_channel(DS_DMA, DS_DMA_CH);
146
147         timer_disable_dma_cc1(DS_TIMER);
148
149         return out;
150 }
151
152 static byte ds_recv_byte(void)
153 {
154         uint out = 0;
155         for (uint m = 1; m < 0x100; m <<= 1) {
156                 if (ds_recv_bit())
157                         out |= m;
158         }
159
160         DEBUG2("DS read: %02x\n", out);
161         return out;
162 }
163
164 static byte ds_buf[10];
165
166 static byte ds_crc_block(uint n)
167 {
168         /// XXX: This might be worth optimizing
169         uint crc = 0;
170
171         for (uint i = 0; i < n; i++) {
172                 byte b = ds_buf[i];
173                 for (uint j = 0; j < 8; j++) {
174                         uint k = (b & 1) ^ (crc >> 7);
175                         crc = (crc << 1) & 0xff;
176                         if (k)
177                                 crc ^= 0x31;
178                         b >>= 1;
179                 }
180         }
181
182         return crc;
183 }
184
185 static bool ds_recv_block(uint n)
186 {
187         for (uint i = 0; i < n; i++)
188                 ds_buf[i] = ds_recv_byte();
189
190         byte crc = ds_crc_block(n);
191         if (crc) {
192                 DEBUG("DS18B20: Invalid CRC %02x\n", crc);
193                 return 0;
194         }
195         return 1;
196 }
197
198 struct ds_sensor ds_sensors[DS_NUM_SENSORS];
199
200 #if DS_NUM_SENSORS == 1
201
202 static void ds_enumerate(void)
203 {
204         if (!ds_reset())
205                 return;
206
207         ds_send_byte(0x33);     // READ_ROM
208         if (!ds_recv_block(8))
209                 return;
210
211         DEBUG("DS18B20: Found sensor ");
212         for (uint i = 0; i < 8; i++) {
213                 DEBUG("%02x", ds_buf[i]);
214                 ds_sensors[0].address[i] = ds_buf[i];
215         }
216         DEBUG("\n");
217 }
218
219 #else
220
221 static void ds_enumerate(void)
222 {
223         /*
224          *  The enumeration algorithm roughly follows the one described in the
225          *  Book of iButton Standards (Maxim Integrated Application Note 937).
226          *
227          *  It simulates depth-first search on the trie of all device IDs.
228          *  In each pass, it walks the trie from the root and recognizes branching nodes.
229          *
230          *  The old_choice variable remembers the deepest left branch taken in the
231          *  previous pass, new_choice is the same for the current pass.
232          */
233
234         DEBUG("DS18B20: Enumerate\n");
235
236         uint num_sensors = 0;
237         byte *addr = ds_buf;
238         byte old_choice = 0;
239
240         for (;;) {
241                 if (!ds_reset()) {
242                         DEBUG("DS18B20: Enumeration found no sensor\n");
243                         return;
244                 }
245
246                 ds_send_byte(0xf0);     // SEARCH_ROM
247                 byte new_choice = 0;
248                 for (byte i=0; i<64; i++) {
249                         bool have_one = ds_recv_bit();
250                         bool have_zero = ds_recv_bit();
251                         bool old_bit = addr[i/8] & (1U << (i%8));
252                         bool new_bit;
253                         switch (2*have_one + have_zero) {
254                                 case 3:
255                                         // This should not happen
256                                         DEBUG("DS18B20: Enumeration failed\n");
257                                         return;
258                                 case 1:
259                                         // Only 0
260                                         new_bit = 0;
261                                         break;
262                                 case 2:
263                                         // Only 1
264                                         new_bit = 1;
265                                         break;
266                                 default:
267                                         // Both
268                                         if (i == old_choice)
269                                                 new_bit = 1;
270                                         else if (i > old_choice) {
271                                                 new_bit = 0;
272                                                 new_choice = i;
273                                         } else {
274                                                 new_bit = old_bit;
275                                                 if (!new_bit)
276                                                         new_choice = i;
277                                         }
278                         }
279                         if (new_bit)
280                                 addr[i/8] |= 1U << (i%8);
281                         else
282                                 addr[i/8] &= ~(1U << (i%8));
283                         ds_send_bit(new_bit);
284                 }
285
286                 if (num_sensors >= DS_NUM_SENSORS) {
287                         DEBUG("DS18B20: Too many sensors\n");
288                         return;
289                 }
290
291                 DEBUG("DS18B20: Found sensor #%u: ", num_sensors);
292                 for (byte i=0; i<8; i++)
293                         DEBUG("%02x", addr[i]);
294                 if (ds_crc_block(8)) {
295                         DEBUG(" - invalid CRC!\n");
296                 } else if (ds_buf[0] == 0x28) {
297                         DEBUG("\n");
298                         memcpy(ds_sensors[num_sensors].address, ds_buf, 8);
299                         num_sensors++;
300                 } else {
301                         DEBUG(" - wrong type\n");
302                 }
303
304                 old_choice = new_choice;
305                 if (!old_choice)
306                         break;
307         }
308 }
309
310 #endif
311
312 void ds_init(void)
313 {
314         DEBUG("DS18B20: Init\n");
315
316         for (uint i = 0; i < DS_NUM_SENSORS; i++) {
317                 memset(ds_sensors[i].address, 0, 8);
318                 ds_sensors[i].current_temp = DS_TEMP_UNKNOWN;
319         }
320
321         dma_set_read_from_peripheral(DS_DMA, DS_DMA_CH);
322         dma_set_priority(DS_DMA, DS_DMA_CH, DMA_CCR_PL_VERY_HIGH);
323         dma_disable_peripheral_increment_mode(DS_DMA, DS_DMA_CH);
324         dma_enable_memory_increment_mode(DS_DMA, DS_DMA_CH);
325         dma_set_peripheral_size(DS_DMA, DS_DMA_CH, DMA_CCR_PSIZE_16BIT);
326         dma_set_memory_size(DS_DMA, DS_DMA_CH, DMA_CCR_MSIZE_16BIT);
327
328         timer_set_prescaler(DS_TIMER, CPU_CLOCK_MHZ - 1);       // 1 tick = 1 μs
329         timer_set_mode(DS_TIMER, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
330         timer_disable_preload(DS_TIMER);
331
332         gpio_set_mode(DS_GPIO, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, DS_PIN);
333
334         ds_enumerate();
335
336         // FIXME: Configure precision?
337 }
338
339 #if DS_NUM_SENSORS == 1
340 #define ds_current_id 0
341 #else
342         static byte ds_current_id;
343 #endif
344
345 static bool ds_activate(void)
346 {
347         if (!ds_reset()) {
348                 DEBUG("DS18B20: Reset failed\n");
349                 return false;
350         }
351 #if DS_NUM_SENSORS == 1
352         ds_send_byte(0xcc);     // SKIP_ROM
353 #else
354         ds_send_byte(0x55);     // MATCH_ROM
355         for (uint i = 0; i < 8; i++)
356                 ds_send_byte(ds_sensors[ds_current_id].address[i]);
357 #endif
358         return true;
359 }
360
361 void ds_step(void)
362 {
363         static byte ds_running;
364         static byte ds_timeout;
365
366         if (!ds_running) {
367                 // Start measurement
368 #if DS_NUM_SENSORS != 1
369                 uint maxn = DS_NUM_SENSORS;
370                 do {
371                         if (!maxn--)
372                                 return;
373                         ds_current_id++;
374                         if (ds_current_id >= DS_NUM_SENSORS) {
375                                 ds_current_id = 0;
376                         }
377                 } while (!ds_sensors[ds_current_id].address[0]);
378 #endif
379                 if (!ds_activate()) {
380                         ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
381                         return;
382                 }
383                 ds_send_byte(0x44);     // CONVERT_T
384                 ds_running = 1;
385                 ds_timeout = 255;
386         } else {
387                 // Still running?
388                 if (!ds_recv_bit()) {
389                         if (!ds_timeout--) {
390                                 DEBUG("DS18B20 #%u: Timeout\n", ds_current_id);
391                                 ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
392                                 ds_running = 0;
393                         }
394                         return;
395                 }
396                 ds_running = 0;
397
398                 // Read scratch pad
399                 if (!ds_activate())
400                         return;
401                 ds_send_byte(0xbe);     // READ_SCRATCHPAD
402                 if (!ds_recv_block(9)) {
403                         ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
404                         return;
405                 }
406                 int t = (int16_t) (ds_buf[0] | (ds_buf[1] << 8));
407                 t = t * 1000 / 16;
408
409                 DEBUG("DS18B20 #%u: %d.%03d degC\n", ds_current_id, t / 1000, t % 1000);
410                 ds_sensors[ds_current_id].current_temp = t;
411         }
412 }