]> mj.ucw.cz Git - home-hw.git/blob - ssr/Src/ds18b20.c
5fa25a129f0ab4249676ea06473694dbffb7b295
[home-hw.git] / ssr / Src / ds18b20.c
1 // DS18B20 Temperature Sensor
2
3 #include "main.h"
4 #include "util.h"
5 #include "app.h"
6
7 static volatile u32 ds_dma_buffer;
8
9 #define DS_PORT THERMO_GPIO_Port
10 #define DS_PIN THERMO_GPIO_Pin
11 #define DS_PIN_MASK (1U << 7)           // Unfortunately, this cannot be inferred from the Pin constant
12 #define DS_DMA DMA1
13 #define DS_DMA_CHANNEL LL_DMA_CHANNEL_6
14 #undef DS_DEBUG
15 #undef DS_DEBUG2
16
17 #ifdef DS_DEBUG
18 #define DEBUG debug_printf
19 #else
20 #define DEBUG(xxx, ...) do { } while (0)
21 #endif
22
23 #ifdef DS_DEBUG2
24 #define DEBUG2 debug_printf
25 #else
26 #define DEBUG2(xxx, ...) do { } while (0)
27 #endif
28
29 // Current temperature
30 int ds_current_temp = DS_TEMP_UNKNOWN;
31
32 static bool ds_reset(void)
33 {
34   DEBUG2("DS18B20: Reset\n");
35   LL_TIM_DisableCounter(TIM3);
36   LL_TIM_SetOnePulseMode(TIM3, LL_TIM_ONEPULSEMODE_SINGLE);
37
38   // DMA for reading pin state
39   ds_dma_buffer = 0xdeadbeef;
40   LL_DMA_SetMemoryAddress(DS_DMA, DS_DMA_CHANNEL, (u32) &ds_dma_buffer);
41   LL_DMA_SetPeriphAddress(DS_DMA, DS_DMA_CHANNEL, (u32) &DS_PORT->IDR);
42   LL_DMA_SetDataLength(DS_DMA, DS_DMA_CHANNEL, 1);
43   LL_DMA_EnableChannel(DS_DMA, DS_DMA_CHANNEL);
44
45   LL_TIM_OC_InitTypeDef oc;
46
47   // CC1 is used to drive the DMA (read line state at specified time)
48   LL_TIM_OC_StructInit(&oc);
49   oc.OCMode = LL_TIM_OCMODE_FROZEN;
50   oc.CompareValue = 560;
51   LL_TIM_OC_Init(TIM3, LL_TIM_CHANNEL_CH1, &oc);
52   LL_TIM_EnableDMAReq_CC1(TIM3);
53   LL_TIM_CC_SetDMAReqTrigger(TIM3, LL_TIM_CCDMAREQUEST_CC);
54
55   // CC2 is used to generate pulses (return line to idle state at specified time)
56   LL_TIM_OC_StructInit(&oc);
57   oc.OCMode = LL_TIM_OCMODE_FORCED_ACTIVE;
58   oc.OCState = LL_TIM_OCSTATE_ENABLE;
59   oc.CompareValue = 480;
60   oc.OCPolarity = LL_TIM_OCPOLARITY_LOW;
61   LL_TIM_OC_Init(TIM3, LL_TIM_CHANNEL_CH2, &oc);
62
63   // Set timer period to the length of the whole transaction
64   LL_TIM_SetAutoReload(TIM3, 999);
65
66   // Pull line down and start timer
67   LL_TIM_EnableCounter(TIM3);
68   LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_INACTIVE);
69
70   // Wait until the timer expires
71   while (LL_TIM_IsEnabledCounter(TIM3))
72     ;
73   // Counter is automatically disabled at the end of cycle
74
75   // Disable DMA
76   LL_TIM_DisableDMAReq_CC1(TIM3);
77   LL_DMA_DisableChannel(DS_DMA, DS_DMA_CHANNEL);
78
79   DEBUG2("Init DMA: %08x [%u] (%u remains)\n", ds_dma_buffer, !!(ds_dma_buffer & DS_PIN_MASK), LL_DMA_GetDataLength(DS_DMA, DS_DMA_CHANNEL));
80
81   // Did the device respond?
82   if (ds_dma_buffer & DS_PIN_MASK)
83     {
84       DEBUG("DS18B20: Initialization failed\n");
85       return 0;
86     }
87   else
88     return 1;
89 }
90
91 static void ds_send_byte(byte b)
92 {
93   DEBUG2("DS write: %02x\n", b);
94   LL_TIM_SetAutoReload(TIM3, 99);       // Each write slot takes 100μs
95   for (uint m=1; m < 0x100; m <<= 1)
96     {
97       LL_TIM_OC_SetCompareCH2(TIM3, ((b & m) ? 1 : 89));        // 1: 1μs pulse, 0: 89μs pulse
98       LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_FORCED_ACTIVE);
99       __disable_irq();
100       // XXX: On STM32F1, we must configure the OC channel _after_ we enable the counter,
101       // otherwise OC triggers immediately. Reasons?
102       LL_TIM_EnableCounter(TIM3);
103       LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_INACTIVE);
104       __enable_irq();
105       while (LL_TIM_IsEnabledCounter(TIM3))
106         ;
107     }
108 }
109
110 static byte ds_recv_byte(void)
111 {
112   LL_TIM_SetAutoReload(TIM3, 79);       // Each read slot takes 80μs
113   LL_TIM_OC_SetCompareCH2(TIM3, 1);     // Generate 1μs pulse to start read slot
114   LL_TIM_OC_SetCompareCH1(TIM3, 8);     // Sample data 8μs after start of slot
115   LL_TIM_EnableDMAReq_CC1(TIM3);
116
117   uint out = 0;
118   for (uint m=1; m < 0x100; m <<= 1)
119     {
120       ds_dma_buffer = 0xdeadbeef;
121       LL_DMA_SetDataLength(DS_DMA, DS_DMA_CHANNEL, 1);
122       LL_DMA_EnableChannel(DS_DMA, DS_DMA_CHANNEL);
123       LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_FORCED_ACTIVE);
124       __disable_irq();
125       LL_TIM_EnableCounter(TIM3);
126       LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_INACTIVE);
127       __enable_irq();
128       while (LL_TIM_IsEnabledCounter(TIM3))
129         ;
130       DEBUG2("XXX %08x\n", ds_dma_buffer);
131       if (ds_dma_buffer & DS_PIN_MASK)
132         out |= m;
133       LL_DMA_DisableChannel(DS_DMA, DS_DMA_CHANNEL);
134     }
135
136   LL_TIM_DisableDMAReq_CC1(TIM3);
137   DEBUG2("DS read: %02x\n", out);
138   return out;
139 }
140
141 static byte ds_buf[10];
142
143 static bool ds_recv_block(uint n)
144 {
145   uint crc = 0;
146   for (uint i=0; i<n; i++)
147     {
148       uint b = ds_recv_byte();
149       // DEBUG("%02x ", b);
150       ds_buf[i] = b;
151       for (uint j=0; j<8; j++)
152         {
153           uint k = (b & 1) ^ (crc >> 7);
154           crc = (crc << 1) & 0xff;
155           if (k)
156             crc ^= 0x31;
157           b >>= 1;
158         }
159     }
160
161   if (crc)
162     {
163       DEBUG("DS18B20: Invalid CRC %02x\n", crc);
164       return 0;
165     }
166   return 1;
167 }
168
169 void ds_init(void)
170 {
171   DEBUG("DS18B20: Init\n");
172   NVIC_DisableIRQ(TIM3_IRQn);   // One day, we will handle everything from interrupts...
173
174   // Identify device
175   if (!ds_reset())
176     return;
177   ds_send_byte(0x33);
178   ds_recv_block(8);
179
180   // FIXME: Configure precision
181 }
182
183 void ds_step(void)
184 {
185   static byte ds_running;
186   static byte ds_timeout;
187
188   if (!ds_running)
189     {
190       // Start measurement
191       if (!ds_reset())
192         {
193           ds_current_temp = DS_TEMP_UNKNOWN;
194           return;
195         }
196       ds_send_byte(0xcc);
197       ds_send_byte(0x44);
198       ds_running = 1;
199       ds_timeout = 255;
200     }
201   else
202     {
203       // Still running?
204       if (ds_recv_byte() != 0xff)
205         {
206           if (!ds_timeout--)
207             {
208               ds_current_temp = DS_TEMP_UNKNOWN;
209               ds_running = 0;
210               DEBUG("DS18B20: Timeout\n");
211             }
212           return;
213         }
214       ds_running = 0;
215
216       // Read scratch pad
217       if (!ds_reset())
218         {
219           ds_current_temp = DS_TEMP_UNKNOWN;
220           return;
221         }
222       ds_send_byte(0xcc);
223       ds_send_byte(0xbe);
224       if (!ds_recv_block(9))
225         {
226           ds_current_temp = DS_TEMP_UNKNOWN;
227           return;
228         }
229       int t = (int16_t)(ds_buf[0] | (ds_buf[1] << 8));
230       t = t*1000/16;
231
232       DEBUG("DS18B20: %d.%03d degC\n", t/1000, t%1000);
233       ds_current_temp = t;
234     }
235 }