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