]> mj.ucw.cz Git - home-hw.git/blob - ssr/Src/ds18b20.c
SSR: DS18B20
[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 #define DS_DEBUG
15
16 #ifdef DS_DEBUG
17 #define DEBUG debug_printf
18 #else
19 #define DEBUG(xxx, ...) do { } while (0)
20 #endif
21
22 static void ds_reset(void)
23 {
24   DEBUG("DS18B20: Reset\n");
25   LL_TIM_DisableCounter(TIM3);
26   LL_TIM_SetOnePulseMode(TIM3, LL_TIM_ONEPULSEMODE_SINGLE);
27
28   // DMA for reading pin state
29   ds_dma_buffer = 0xdeadbeef;
30   LL_DMA_SetMemoryAddress(DS_DMA, DS_DMA_CHANNEL, (u32) &ds_dma_buffer);
31   LL_DMA_SetPeriphAddress(DS_DMA, DS_DMA_CHANNEL, (u32) &DS_PORT->IDR);
32   LL_DMA_SetDataLength(DS_DMA, DS_DMA_CHANNEL, 1);
33   LL_DMA_EnableChannel(DS_DMA, DS_DMA_CHANNEL);
34
35   LL_TIM_OC_InitTypeDef oc;
36
37   // CC1 is used to drive the DMA (read line state at specified time)
38   LL_TIM_OC_StructInit(&oc);
39   oc.OCMode = LL_TIM_OCMODE_FROZEN;
40   oc.CompareValue = 560;
41   LL_TIM_OC_Init(TIM3, LL_TIM_CHANNEL_CH1, &oc);
42   LL_TIM_EnableDMAReq_CC1(TIM3);
43   LL_TIM_CC_SetDMAReqTrigger(TIM3, LL_TIM_CCDMAREQUEST_CC);
44
45   // CC2 is used to generate pulses (return line to idle state at specified time)
46   LL_TIM_OC_StructInit(&oc);
47   oc.OCMode = LL_TIM_OCMODE_FORCED_ACTIVE;
48   oc.OCState = LL_TIM_OCSTATE_ENABLE;
49   oc.CompareValue = 480;
50   oc.OCPolarity = LL_TIM_OCPOLARITY_LOW;
51   LL_TIM_OC_Init(TIM3, LL_TIM_CHANNEL_CH2, &oc);
52
53   // Set timer period to the length of the whole transaction
54   LL_TIM_SetAutoReload(TIM3, 999);
55
56   // Pull line down and start timer
57   LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_INACTIVE);
58   LL_TIM_EnableCounter(TIM3);
59
60   // Wait until the timer expires
61   while (LL_TIM_IsEnabledCounter(TIM3))
62     ;
63   // Counter is automatically disabled at the end of cycle
64
65   // Disable DMA
66   LL_TIM_DisableDMAReq_CC1(TIM3);
67   LL_DMA_DisableChannel(DS_DMA, DS_DMA_CHANNEL);
68
69   DEBUG("Init DMA: %08x [%u] (%u remains)\n", ds_dma_buffer, !!(ds_dma_buffer & DS_PIN_MASK), LL_DMA_GetDataLength(DS_DMA, DS_DMA_CHANNEL));
70 }
71
72 static void ds_send_byte(byte b)
73 {
74   DEBUG("DS write: %02x\n", b);
75   LL_TIM_SetAutoReload(TIM3, 99);       // Each write slot takes 100μs
76   for (uint m=1; m < 0x100; m <<= 1)
77     {
78       LL_TIM_OC_SetCompareCH2(TIM3, ((b & m) ? 1 : 89));        // 1: 1μs pulse, 0: 89μs pulse
79       LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_FORCED_ACTIVE);
80       LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_INACTIVE);
81       LL_TIM_EnableCounter(TIM3);
82       while (LL_TIM_IsEnabledCounter(TIM3))
83         ;
84     }
85 }
86
87 static byte ds_recv_byte(void)
88 {
89   LL_TIM_SetAutoReload(TIM3, 79);       // Each read slot takes 80μs
90   LL_TIM_OC_SetCompareCH2(TIM3, 1);     // Generate 1μs pulse to start read slot
91   LL_TIM_OC_SetCompareCH1(TIM3, 8);     // Sample data 8μs after start of slot
92   LL_TIM_EnableDMAReq_CC1(TIM3);
93
94   uint out = 0;
95   for (uint m=1; m < 0x100; m <<= 1)
96     {
97       ds_dma_buffer = 0xdeadbeef;
98       LL_DMA_SetDataLength(DS_DMA, DS_DMA_CHANNEL, 1);
99       LL_DMA_EnableChannel(DS_DMA, DS_DMA_CHANNEL);
100       LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_FORCED_ACTIVE);
101       LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_INACTIVE);
102       LL_TIM_EnableCounter(TIM3);
103       while (LL_TIM_IsEnabledCounter(TIM3))
104         ;
105       debug_printf("XXX %08x\n", ds_dma_buffer);
106       if (ds_dma_buffer & DS_PIN_MASK)
107         out |= m;
108       LL_DMA_DisableChannel(DS_DMA, DS_DMA_CHANNEL);
109     }
110
111   LL_TIM_DisableDMAReq_CC1(TIM3);
112   DEBUG("DS read: %02x\n", out);
113   return out;
114 }
115
116 static byte ds_buf[10];
117
118 static int ds_recv_block(uint n)
119 {
120   uint crc = 0;
121   for (uint i=0; i<n; i++)
122     {
123       uint b = ds_recv_byte();
124       ds_buf[i] = b;
125       for (uint j=0; j<8; j++)
126         {
127           uint k = (b & 1) ^ (crc >> 7);
128           crc = (crc << 1) & 0xff;
129           if (k)
130             crc ^= 0x31;
131           b >>= 1;
132         }
133     }
134
135   if (crc)
136     {
137       debug_printf("WARNING: Invalid CRC %02x\n", crc);
138       return 0;
139     }
140   return 1;
141 }
142
143 void ds_test(void)
144 {
145   uint cnt = 0;
146
147   debug_puts("Init\n");
148   NVIC_DisableIRQ(TIM3_IRQn);   // One day, we will handle everything from interrupts...
149
150   // Identify device
151   ds_reset();
152   ds_send_byte(0x33);
153   ds_recv_block(8);
154
155   for(;;);
156
157   // FIXME: Configure precision
158
159   for (;;)
160     {
161       LL_GPIO_ResetOutputPin(LED_GPIO_Port, LED_Pin);
162       // debug_printf("Tick tock: %d\n", cnt);
163
164       // Start measurement
165       ds_reset();
166       ds_send_byte(0xcc);
167       ds_send_byte(0x44);
168       while (ds_recv_byte() != 0xff)
169         LL_mDelay(10);
170
171       // Read scratch pad
172       ds_reset();
173       ds_send_byte(0xcc);
174       ds_send_byte(0xbe);
175       ds_recv_block(9);
176       int t = (int16_t)(ds_buf[0] | (ds_buf[1] << 8));
177       t = t*1000/16;
178
179       LL_GPIO_SetOutputPin(LED_GPIO_Port, LED_Pin);
180       debug_printf("Temp: %d.%03d degC\n", t/1000, t%1000);
181
182       LL_mDelay(1000);
183       cnt++;
184     }
185 }