]> mj.ucw.cz Git - home-hw.git/blob - lib/ds18b20.c
Merge branch 'master' of ssh://git.ucw.cz/home/mj/GIT/home-hw
[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         // XXX: We do not know why this is needed...
77         static bool once;
78         if (!once) {
79                 for (int i=0; i<10000; i++) __asm__ volatile ("nop");
80                 once = 1;
81         }
82
83         // Pull line down and start timer
84         timer_generate_event(DS_TIMER, TIM_EGR_UG);
85         timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
86         timer_enable_counter(DS_TIMER);
87
88         // Wait until the timer expires
89         while (timer_is_counter_enabled(DS_TIMER))
90                 ;
91         // Counter is automatically disabled at the end of cycle
92
93         // Disable DMA
94         timer_disable_dma_cc1(DS_TIMER);
95         dma_disable_channel(DS_DMA, DS_DMA_CH);
96
97         DEBUG2("Init DMA: %08x [%u] (%u remains)\n",
98                ds_dma_buffer,
99                !!(ds_dma_buffer & DS_PIN),
100                dma_get_number_of_data(DS_DMA, DS_DMA_CH));
101
102         // Did the device respond?
103         if (ds_dma_buffer & DS_PIN) {
104                 DEBUG("DS18B20: Initialization failed\n");
105                 return 0;
106         } else
107                 return 1;
108 }
109
110 static void ds_send_bit(bool bit)
111 {
112         timer_set_period(DS_TIMER, 99);                         // Each write slot takes 100 μs
113         timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
114         timer_set_oc_value(DS_TIMER, TIM_OC2, (bit ? 3 : 89));  // 1: 3μs pulse, 0: 89μs pulse
115         timer_generate_event(DS_TIMER, TIM_EGR_UG);
116         timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
117         timer_enable_counter(DS_TIMER);
118         while (timer_is_counter_enabled(DS_TIMER))
119                 ;
120 }
121
122 static void ds_send_byte(byte b)
123 {
124         DEBUG2("DS write: %02x\n", b);
125         for (uint m = 1; m < 0x100; m <<= 1)
126                 ds_send_bit(b & m);
127 }
128
129 static bool ds_recv_bit(void)
130 {
131         timer_set_period(DS_TIMER, 79);                 // Each read slot takes 80μs
132         timer_set_oc_value(DS_TIMER, TIM_OC2, 2);       // Generate 2μs pulse to start read slot
133         timer_set_oc_value(DS_TIMER, TIM_OC1, 8);       // Sample data 8μs after start of slot
134         timer_enable_dma_cc1(DS_TIMER);
135
136         ds_dma_buffer = 0xdeadbeef;
137         dma_set_number_of_data(DS_DMA, DS_DMA_CH, 1);
138         dma_enable_channel(DS_DMA, DS_DMA_CH);
139         timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
140         timer_generate_event(DS_TIMER, TIM_EGR_UG);
141         timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
142         timer_enable_counter(DS_TIMER);
143         while (timer_is_counter_enabled(DS_TIMER))
144                 ;
145         // DEBUG2("XXX %08x\n", ds_dma_buffer);
146         bool out = ds_dma_buffer & DS_PIN;
147         dma_disable_channel(DS_DMA, DS_DMA_CH);
148
149         timer_disable_dma_cc1(DS_TIMER);
150
151         return out;
152 }
153
154 static byte ds_recv_byte(void)
155 {
156         uint out = 0;
157         for (uint m = 1; m < 0x100; m <<= 1) {
158                 if (ds_recv_bit())
159                         out |= m;
160         }
161
162         DEBUG2("DS read: %02x\n", out);
163         return out;
164 }
165
166 static byte ds_buf[10];
167
168 static byte ds_crc_block(uint n)
169 {
170         /// XXX: This might be worth optimizing
171         uint crc = 0;
172
173         for (uint i = 0; i < n; i++) {
174                 byte b = ds_buf[i];
175                 for (uint j = 0; j < 8; j++) {
176                         uint k = (b & 1) ^ (crc >> 7);
177                         crc = (crc << 1) & 0xff;
178                         if (k)
179                                 crc ^= 0x31;
180                         b >>= 1;
181                 }
182         }
183
184         return crc;
185 }
186
187 static bool ds_recv_block(uint n)
188 {
189         for (uint i = 0; i < n; i++)
190                 ds_buf[i] = ds_recv_byte();
191
192         byte crc = ds_crc_block(n);
193         if (crc) {
194                 DEBUG("DS18B20: Invalid CRC %02x\n", crc);
195                 return 0;
196         }
197         return 1;
198 }
199
200 struct ds_sensor ds_sensors[DS_NUM_SENSORS];
201
202 #if DS_NUM_SENSORS == 1
203
204 static void ds_enumerate(void)
205 {
206         if (!ds_reset())
207                 return;
208
209         ds_send_byte(0x33);     // READ_ROM
210         if (!ds_recv_block(8))
211                 return;
212
213         DEBUG("DS18B20: Found sensor ");
214         for (uint i = 0; i < 8; i++) {
215                 DEBUG("%02x", ds_buf[i]);
216                 ds_sensors[0].address[i] = ds_buf[i];
217         }
218         DEBUG("\n");
219 }
220
221 #else
222
223 static void ds_enumerate(void)
224 {
225         /*
226          *  The enumeration algorithm roughly follows the one described in the
227          *  Book of iButton Standards (Maxim Integrated Application Note 937).
228          *
229          *  It simulates depth-first search on the trie of all device IDs.
230          *  In each pass, it walks the trie from the root and recognizes branching nodes.
231          *
232          *  The old_choice variable remembers the deepest left branch taken in the
233          *  previous pass, new_choice is the same for the current pass.
234          */
235
236         DEBUG("DS18B20: Enumerate\n");
237
238         uint num_sensors = 0;
239         byte *addr = ds_buf;
240         byte old_choice = 0;
241
242         for (;;) {
243                 if (!ds_reset()) {
244                         DEBUG("DS18B20: Enumeration found no sensor\n");
245                         return;
246                 }
247
248                 ds_send_byte(0xf0);     // SEARCH_ROM
249                 byte new_choice = 0;
250                 for (byte i=0; i<64; i++) {
251                         bool have_one = ds_recv_bit();
252                         bool have_zero = ds_recv_bit();
253                         bool old_bit = addr[i/8] & (1U << (i%8));
254                         bool new_bit;
255                         switch (2*have_one + have_zero) {
256                                 case 3:
257                                         // This should not happen
258                                         DEBUG("DS18B20: Enumeration failed\n");
259                                         return;
260                                 case 1:
261                                         // Only 0
262                                         new_bit = 0;
263                                         break;
264                                 case 2:
265                                         // Only 1
266                                         new_bit = 1;
267                                         break;
268                                 default:
269                                         // Both
270                                         if (i == old_choice)
271                                                 new_bit = 1;
272                                         else if (i > old_choice) {
273                                                 new_bit = 0;
274                                                 new_choice = i;
275                                         } else {
276                                                 new_bit = old_bit;
277                                                 if (!new_bit)
278                                                         new_choice = i;
279                                         }
280                         }
281                         if (new_bit)
282                                 addr[i/8] |= 1U << (i%8);
283                         else
284                                 addr[i/8] &= ~(1U << (i%8));
285                         ds_send_bit(new_bit);
286                 }
287
288                 if (num_sensors >= DS_NUM_SENSORS) {
289                         DEBUG("DS18B20: Too many sensors\n");
290                         return;
291                 }
292
293                 DEBUG("DS18B20: Found sensor #%u: ", num_sensors);
294                 for (byte i=0; i<8; i++)
295                         DEBUG("%02x", addr[i]);
296                 if (ds_crc_block(8)) {
297                         DEBUG(" - invalid CRC!\n");
298                 } else if (ds_buf[0] == 0x28) {
299                         DEBUG("\n");
300                         memcpy(ds_sensors[num_sensors].address, ds_buf, 8);
301                         num_sensors++;
302                 } else {
303                         DEBUG(" - wrong type\n");
304                 }
305
306                 old_choice = new_choice;
307                 if (!old_choice)
308                         break;
309         }
310 }
311
312 #endif
313
314 void ds_init(void)
315 {
316         DEBUG("DS18B20: Init\n");
317
318         for (uint i = 0; i < DS_NUM_SENSORS; i++) {
319                 memset(ds_sensors[i].address, 0, 8);
320                 ds_sensors[i].current_temp = DS_TEMP_UNKNOWN;
321         }
322
323         dma_set_read_from_peripheral(DS_DMA, DS_DMA_CH);
324         dma_set_priority(DS_DMA, DS_DMA_CH, DMA_CCR_PL_VERY_HIGH);
325         dma_disable_peripheral_increment_mode(DS_DMA, DS_DMA_CH);
326         dma_enable_memory_increment_mode(DS_DMA, DS_DMA_CH);
327         dma_set_peripheral_size(DS_DMA, DS_DMA_CH, DMA_CCR_PSIZE_16BIT);
328         dma_set_memory_size(DS_DMA, DS_DMA_CH, DMA_CCR_MSIZE_16BIT);
329
330         timer_set_prescaler(DS_TIMER, CPU_CLOCK_MHZ - 1);       // 1 tick = 1 μs
331         timer_set_mode(DS_TIMER, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
332         timer_disable_preload(DS_TIMER);
333
334         gpio_set_mode(DS_GPIO, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, DS_PIN);
335
336         ds_enumerate();
337
338         // FIXME: Configure precision?
339 }
340
341 #if DS_NUM_SENSORS == 1
342 #define ds_current_id 0
343 #else
344         static byte ds_current_id;
345 #endif
346
347 static bool ds_activate(void)
348 {
349         if (!ds_reset()) {
350                 DEBUG("DS18B20: Reset failed\n");
351                 return false;
352         }
353 #if DS_NUM_SENSORS == 1
354         ds_send_byte(0xcc);     // SKIP_ROM
355 #else
356         ds_send_byte(0x55);     // MATCH_ROM
357         for (uint i = 0; i < 8; i++)
358                 ds_send_byte(ds_sensors[ds_current_id].address[i]);
359 #endif
360         return true;
361 }
362
363 void ds_step(void)
364 {
365         static byte ds_running;
366         static byte ds_timeout;
367
368         if (!ds_running) {
369                 // Start measurement
370 #if DS_NUM_SENSORS != 1
371                 uint maxn = DS_NUM_SENSORS;
372                 do {
373                         if (!maxn--)
374                                 return;
375                         ds_current_id++;
376                         if (ds_current_id >= DS_NUM_SENSORS) {
377                                 ds_current_id = 0;
378                         }
379                 } while (!ds_sensors[ds_current_id].address[0]);
380 #endif
381                 if (!ds_activate()) {
382                         ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
383                         return;
384                 }
385                 ds_send_byte(0x44);     // CONVERT_T
386                 ds_running = 1;
387                 ds_timeout = 255;
388         } else {
389                 // Still running?
390                 if (!ds_recv_bit()) {
391                         if (!ds_timeout--) {
392                                 DEBUG("DS18B20 #%u: Timeout\n", ds_current_id);
393                                 ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
394                                 ds_running = 0;
395                         }
396                         return;
397                 }
398                 ds_running = 0;
399
400                 // Read scratch pad
401                 if (!ds_activate())
402                         return;
403                 ds_send_byte(0xbe);     // READ_SCRATCHPAD
404                 if (!ds_recv_block(9)) {
405                         ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
406                         return;
407                 }
408                 int t = (int16_t) (ds_buf[0] | (ds_buf[1] << 8));
409                 t = t * 1000 / 16;
410
411                 DEBUG("DS18B20 #%u: %d.%03d degC\n", ds_current_id, t / 1000, t % 1000);
412                 ds_sensors[ds_current_id].current_temp = t;
413         }
414 }