]> mj.ucw.cz Git - home-hw.git/blob - test-opencm3/ds18b20.c
fda6e83ef7580ebd3ad20046ed927e90038081bd
[home-hw.git] / test-opencm3 / ds18b20.c
1 // DS18B20 Temperature Sensor
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
12 static volatile u32 ds_dma_buffer;
13
14 #define DS_TIMER TIM3
15 #define DS_GPIO GPIOA
16 #define DS_PIN GPIO7
17 #define DS_DMA DMA1
18 #define DS_DMA_CH 6
19
20 #define DS_DEBUG
21 #undef DS_DEBUG2
22
23 #ifdef DS_DEBUG
24 #define DEBUG debug_printf
25 #else
26 #define DEBUG(xxx, ...) do { } while (0)
27 #endif
28
29 #ifdef DS_DEBUG2
30 #define DEBUG2 debug_printf
31 #else
32 #define DEBUG2(xxx, ...) do { } while (0)
33 #endif
34
35 // Current temperature
36 int ds_current_temp = DS_TEMP_UNKNOWN;
37
38 // Auxiliary functions which are missing from libopencm3
39 static inline bool timer_is_counter_enabled(u32 timer)
40 {
41         return TIM_CR1(timer) & TIM_CR1_CEN;
42 }
43
44 static inline void timer_enable_dma_cc1(u32 timer)
45 {
46         TIM_DIER(timer) |= TIM_DIER_CC1DE;
47 }
48
49 static inline void timer_disable_dma_cc1(u32 timer)
50 {
51         TIM_DIER(timer) &= ~TIM_DIER_CC1DE;
52 }
53
54 static bool ds_reset(void)
55 {
56         DEBUG2("DS18B20: Reset\n");
57         timer_disable_counter(DS_TIMER);
58         timer_one_shot_mode(DS_TIMER);
59
60         // DMA for reading pin state
61         ds_dma_buffer = 0xdeadbeef;
62         dma_set_memory_address(DS_DMA, DS_DMA_CH, (u32) &ds_dma_buffer);
63         dma_set_peripheral_address(DS_DMA, DS_DMA_CH, (u32) &GPIO_IDR(DS_GPIO));
64         dma_set_number_of_data(DS_DMA, DS_DMA_CH, 1);
65         dma_enable_channel(DS_DMA, DS_DMA_CH);
66
67         // CC1 is used to drive the DMA (read line state at specified time)
68         timer_disable_oc_output(DS_TIMER, TIM_OC1);
69         timer_set_oc_mode(DS_TIMER, TIM_OC1, TIM_OCM_FROZEN);
70         timer_set_oc_value(DS_TIMER, TIM_OC1, 560);
71         timer_set_dma_on_compare_event(DS_TIMER);
72         timer_enable_dma_cc1(DS_TIMER);
73
74         // CC2 is used to generate pulses (return line to idle state at specified time)
75         timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
76         timer_enable_oc_output(DS_TIMER, TIM_OC2);
77         timer_set_oc_value(DS_TIMER, TIM_OC2, 480);
78         timer_set_oc_polarity_low(DS_TIMER, TIM_OC2);
79
80         // Set timer period to the length of the whole transaction (1 ms)
81         timer_set_period(DS_TIMER, 999);
82
83         // Pull line down and start timer
84         cm_disable_interrupts();
85         timer_enable_counter(DS_TIMER);
86         timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
87         cm_enable_interrupts();
88
89         // Wait until the timer expires
90         while (timer_is_counter_enabled(DS_TIMER))
91                 ;
92         // Counter is automatically disabled at the end of cycle
93
94         // Disable DMA
95         timer_disable_dma_cc1(DS_TIMER);
96         dma_disable_channel(DS_DMA, DS_DMA_CH);
97
98         DEBUG2("Init DMA: %08x [%u] (%u remains)\n",
99                ds_dma_buffer,
100                !!(ds_dma_buffer & GPIO7),
101                dma_get_number_of_data(DS_DMA, DS_DMA_CH));
102
103         // Did the device respond?
104         if (ds_dma_buffer & GPIO7) {
105                 DEBUG("DS18B20: Initialization failed\n");
106                 return 0;
107         } else
108                 return 1;
109 }
110
111 static void ds_send_byte(byte b)
112 {
113         DEBUG2("DS write: %02x\n", b);
114         timer_set_period(DS_TIMER, 99); // Each write slot takes 100 μs
115         for (uint m = 1; m < 0x100; m <<= 1) {
116                 timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
117                 timer_set_oc_value(DS_TIMER, TIM_OC2, ((b & m) ? 3 : 89));      // 1: 3μs pulse, 0: 89μs pulse
118                 cm_disable_interrupts();
119                 // XXX: On STM32F1, we must configure the OC channel _after_ we enable the counter,
120                 // otherwise OC triggers immediately. Reasons?
121                 timer_enable_counter(DS_TIMER);
122                 timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
123                 cm_enable_interrupts();
124                 while (timer_is_counter_enabled(DS_TIMER))
125                         ;
126         }
127 }
128
129 static byte ds_recv_byte(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         uint out = 0;
137         for (uint m = 1; m < 0x100; m <<= 1) {
138                 ds_dma_buffer = 0xdeadbeef;
139                 dma_set_number_of_data(DS_DMA, DS_DMA_CH, 1);
140                 dma_enable_channel(DS_DMA, DS_DMA_CH);
141                 timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
142                 cm_disable_interrupts();
143                 timer_enable_counter(DS_TIMER);
144                 timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
145                 cm_enable_interrupts();
146                 while (timer_is_counter_enabled(DS_TIMER))
147                         ;
148                 // DEBUG2("XXX %08x\n", ds_dma_buffer);
149                 if (ds_dma_buffer & GPIO7)
150                         out |= m;
151                 dma_disable_channel(DS_DMA, DS_DMA_CH);
152         }
153
154         timer_disable_dma_cc1(DS_TIMER);
155         DEBUG2("DS read: %02x\n", out);
156         return out;
157 }
158
159 static byte ds_buf[10];
160
161 static bool ds_recv_block(uint n)
162 {
163         uint crc = 0;
164         for (uint i = 0; i < n; i++) {
165                 uint b = ds_recv_byte();
166                 // DEBUG("%02x ", b);
167                 ds_buf[i] = b;
168                 for (uint j = 0; j < 8; j++) {
169                         uint k = (b & 1) ^ (crc >> 7);
170                         crc = (crc << 1) & 0xff;
171                         if (k)
172                                 crc ^= 0x31;
173                         b >>= 1;
174                 }
175         }
176
177         if (crc) {
178                 DEBUG("DS18B20: Invalid CRC %02x\n", crc);
179                 return 0;
180         }
181         return 1;
182 }
183
184 void ds_init(void)
185 {
186         DEBUG("DS18B20: Init\n");
187
188         dma_set_read_from_peripheral(DS_DMA, DS_DMA_CH);
189         dma_set_priority(DS_DMA, DS_DMA_CH, DMA_CCR_PL_VERY_HIGH);
190         dma_disable_peripheral_increment_mode(DS_DMA, DS_DMA_CH);
191         dma_enable_memory_increment_mode(DS_DMA, DS_DMA_CH);
192         dma_set_peripheral_size(DS_DMA, DS_DMA_CH, DMA_CCR_PSIZE_16BIT);
193         dma_set_memory_size(DS_DMA, DS_DMA_CH, DMA_CCR_MSIZE_16BIT);
194
195         timer_set_prescaler(DS_TIMER, 71);              // 1 tick = 1 μs
196         timer_set_mode(DS_TIMER, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
197         timer_disable_preload(DS_TIMER);
198
199         gpio_set_mode(DS_GPIO, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO7);
200
201         // Identify device
202         if (!ds_reset())
203                 return;
204         ds_send_byte(0x33);
205         ds_recv_block(8);
206
207         // FIXME: Configure precision
208 }
209
210 void ds_step(void)
211 {
212         static byte ds_running;
213         static byte ds_timeout;
214
215         if (!ds_running) {
216                 // Start measurement
217                 if (!ds_reset()) {
218                         ds_current_temp = DS_TEMP_UNKNOWN;
219                         return;
220                 }
221                 ds_send_byte(0xcc);
222                 ds_send_byte(0x44);
223                 ds_running = 1;
224                 ds_timeout = 255;
225         } else {
226                 // Still running?
227                 if (ds_recv_byte() != 0xff) {
228                         if (!ds_timeout--) {
229                                 ds_current_temp = DS_TEMP_UNKNOWN;
230                                 ds_running = 0;
231                                 DEBUG("DS18B20: Timeout\n");
232                         }
233                         return;
234                 }
235                 ds_running = 0;
236
237                 // Read scratch pad
238                 if (!ds_reset()) {
239                         ds_current_temp = DS_TEMP_UNKNOWN;
240                         return;
241                 }
242                 ds_send_byte(0xcc);
243                 ds_send_byte(0xbe);
244                 if (!ds_recv_block(9)) {
245                         ds_current_temp = DS_TEMP_UNKNOWN;
246                         return;
247                 }
248                 int t = (int16_t) (ds_buf[0] | (ds_buf[1] << 8));
249                 t = t * 1000 / 16;
250
251                 DEBUG("DS18B20: %d.%03d degC\n", t / 1000, t % 1000);
252                 ds_current_temp = t;
253         }
254 }