2 * Interface to DS18B20 Temperature Sensors
4 * (c) 2019 Martin Mareš <mj@ucw.cz>
11 #include <libopencm3/cm3/cortex.h>
12 #include <libopencm3/stm32/dma.h>
13 #include <libopencm3/stm32/gpio.h>
14 #include <libopencm3/stm32/rcc.h>
17 /*** Configuration ***/
19 // You should set the following parameters in config.h
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
30 // Maximum number of supported sensors
31 // #define DS_NUM_SENSORS 8
34 #define DEBUG debug_printf
36 #define DEBUG(xxx, ...) do { } while (0)
40 #define DEBUG2 debug_printf
42 #define DEBUG2(xxx, ...) do { } while (0)
45 static volatile u32 ds_dma_buffer;
47 static bool ds_reset(void)
49 DEBUG2("DS18B20: Reset\n");
50 timer_disable_counter(DS_TIMER);
51 timer_one_shot_mode(DS_TIMER);
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);
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);
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);
73 // Set timer period to the length of the whole transaction (1 ms)
74 timer_set_period(DS_TIMER, 999);
76 // XXX: We do not know why this is needed...
79 for (int i=0; i<10000; i++) __asm__ volatile ("nop");
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);
88 // Wait until the timer expires
89 while (timer_is_counter_enabled(DS_TIMER))
91 // Counter is automatically disabled at the end of cycle
94 timer_disable_dma_cc1(DS_TIMER);
95 dma_disable_channel(DS_DMA, DS_DMA_CH);
97 DEBUG2("Init DMA: %08x [%u] (%u remains)\n",
99 !!(ds_dma_buffer & DS_PIN),
100 dma_get_number_of_data(DS_DMA, DS_DMA_CH));
102 // Did the device respond?
103 if (ds_dma_buffer & DS_PIN) {
104 DEBUG("DS18B20: Initialization failed\n");
110 static void ds_send_bit(bool bit)
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))
122 static void ds_send_byte(byte b)
124 DEBUG2("DS write: %02x\n", b);
125 for (uint m = 1; m < 0x100; m <<= 1)
129 static bool ds_recv_bit(void)
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);
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))
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);
149 timer_disable_dma_cc1(DS_TIMER);
154 static byte ds_recv_byte(void)
157 for (uint m = 1; m < 0x100; m <<= 1) {
162 DEBUG2("DS read: %02x\n", out);
166 static byte ds_buf[10];
168 static byte ds_crc_block(uint n)
170 /// XXX: This might be worth optimizing
173 for (uint i = 0; i < n; i++) {
175 for (uint j = 0; j < 8; j++) {
176 uint k = (b & 1) ^ (crc >> 7);
177 crc = (crc << 1) & 0xff;
187 static bool ds_recv_block(uint n)
189 for (uint i = 0; i < n; i++)
190 ds_buf[i] = ds_recv_byte();
192 byte crc = ds_crc_block(n);
194 DEBUG("DS18B20: Invalid CRC %02x\n", crc);
200 struct ds_sensor ds_sensors[DS_NUM_SENSORS];
202 #if DS_NUM_SENSORS == 1
204 static void ds_enumerate(void)
209 ds_send_byte(0x33); // READ_ROM
210 if (!ds_recv_block(8))
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];
223 static void ds_enumerate(void)
226 * The enumeration algorithm roughly follows the one described in the
227 * Book of iButton Standards (Maxim Integrated Application Note 937).
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.
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.
236 DEBUG("DS18B20: Enumerate\n");
238 uint num_sensors = 0;
244 DEBUG("DS18B20: Enumeration found no sensor\n");
248 ds_send_byte(0xf0); // SEARCH_ROM
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));
255 switch (2*have_one + have_zero) {
257 // This should not happen
258 DEBUG("DS18B20: Enumeration failed\n");
272 else if (i > old_choice) {
282 addr[i/8] |= 1U << (i%8);
284 addr[i/8] &= ~(1U << (i%8));
285 ds_send_bit(new_bit);
288 if (num_sensors >= DS_NUM_SENSORS) {
289 DEBUG("DS18B20: Too many sensors\n");
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) {
300 memcpy(ds_sensors[num_sensors].address, ds_buf, 8);
303 DEBUG(" - wrong type\n");
306 old_choice = new_choice;
316 DEBUG("DS18B20: Init\n");
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;
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);
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);
334 gpio_set_mode(DS_GPIO, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, DS_PIN);
338 // FIXME: Configure precision?
341 #if DS_NUM_SENSORS == 1
342 #define ds_current_id 0
344 static byte ds_current_id;
347 static bool ds_activate(void)
350 DEBUG("DS18B20: Reset failed\n");
353 #if DS_NUM_SENSORS == 1
354 ds_send_byte(0xcc); // SKIP_ROM
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]);
365 static byte ds_running;
366 static byte ds_timeout;
370 #if DS_NUM_SENSORS != 1
371 uint maxn = DS_NUM_SENSORS;
376 if (ds_current_id >= DS_NUM_SENSORS) {
379 } while (!ds_sensors[ds_current_id].address[0]);
381 if (!ds_activate()) {
382 ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
385 ds_send_byte(0x44); // CONVERT_T
390 if (!ds_recv_bit()) {
392 DEBUG("DS18B20 #%u: Timeout\n", ds_current_id);
393 ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
403 ds_send_byte(0xbe); // READ_SCRATCHPAD
404 if (!ds_recv_block(9)) {
405 ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
408 int t = (int16_t) (ds_buf[0] | (ds_buf[1] << 8));
411 DEBUG("DS18B20 #%u: %d.%03d degC\n", ds_current_id, t / 1000, t % 1000);
412 ds_sensors[ds_current_id].current_temp = t;