]> mj.ucw.cz Git - home-hw.git/commitdiff
DS18B20: Works
authorMartin Mares <mj@ucw.cz>
Sat, 21 Jul 2018 22:20:43 +0000 (00:20 +0200)
committerMartin Mares <mj@ucw.cz>
Sat, 21 Jul 2018 22:20:43 +0000 (00:20 +0200)
nucleo-test/Src/test.c

index 717771dbdee783a314a8686df1901fc1e5b763f5..f49107fadd7cdb0f55ea42e064cf99bf178c916b 100644 (file)
@@ -421,29 +421,34 @@ void run_test(void)
 
 #else
 
 
 #else
 
-static volatile uint32_t dma_buffer;
+// DS18B20 Temperature Sensor
 
 
-static void ds18b20_reset(void)
+static volatile uint32_t ds_dma_buffer;
+
+static void ds_reset(void)
 {
 {
-  debug_puts("DS18B20: Reset\r\n");
+  // debug_puts("DS18B20: Reset\r\n");
   LL_TIM_DisableCounter(TIM3);
   LL_TIM_SetOnePulseMode(TIM3, LL_TIM_ONEPULSEMODE_SINGLE);
   LL_TIM_DisableCounter(TIM3);
   LL_TIM_SetOnePulseMode(TIM3, LL_TIM_ONEPULSEMODE_SINGLE);
-  LL_TIM_CC_SetDMAReqTrigger(TIM3, LL_TIM_CCDMAREQUEST_CC);
 
 
-  dma_buffer = 0xdeadbeef;
-  LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_4, (uint32_t) &dma_buffer);
+  // DMA for reading pin state
+  ds_dma_buffer = 0xdeadbeef;
+  LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_4, (uint32_t) &ds_dma_buffer);
   LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_4, (uint32_t) &THERMO_GPIO_Port->IDR);
   LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_4, 1);
   LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_4);
 
   LL_TIM_OC_InitTypeDef oc;
 
   LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_4, (uint32_t) &THERMO_GPIO_Port->IDR);
   LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_4, 1);
   LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_4);
 
   LL_TIM_OC_InitTypeDef oc;
 
+  // CC1 is used to drive the DMA (read line state at specified time)
   LL_TIM_OC_StructInit(&oc);
   oc.OCMode = LL_TIM_OCMODE_FROZEN;
   oc.CompareValue = 560;
   LL_TIM_OC_Init(TIM3, LL_TIM_CHANNEL_CH1, &oc);
   LL_TIM_EnableDMAReq_CC1(TIM3);
   LL_TIM_OC_StructInit(&oc);
   oc.OCMode = LL_TIM_OCMODE_FROZEN;
   oc.CompareValue = 560;
   LL_TIM_OC_Init(TIM3, LL_TIM_CHANNEL_CH1, &oc);
   LL_TIM_EnableDMAReq_CC1(TIM3);
+  LL_TIM_CC_SetDMAReqTrigger(TIM3, LL_TIM_CCDMAREQUEST_CC);
 
 
+  // CC2 is used to generate pulses (return line to idle state at specified time)
   LL_TIM_OC_StructInit(&oc);
   oc.OCMode = LL_TIM_OCMODE_FORCED_ACTIVE;
   oc.OCState = LL_TIM_OCSTATE_ENABLE;
   LL_TIM_OC_StructInit(&oc);
   oc.OCMode = LL_TIM_OCMODE_FORCED_ACTIVE;
   oc.OCState = LL_TIM_OCSTATE_ENABLE;
@@ -451,33 +456,95 @@ static void ds18b20_reset(void)
   oc.OCPolarity = LL_TIM_OCPOLARITY_LOW;
   LL_TIM_OC_Init(TIM3, LL_TIM_CHANNEL_CH2, &oc);
 
   oc.OCPolarity = LL_TIM_OCPOLARITY_LOW;
   LL_TIM_OC_Init(TIM3, LL_TIM_CHANNEL_CH2, &oc);
 
+  // Set timer period to the length of the whole transaction
   LL_TIM_SetAutoReload(TIM3, 999);
   LL_TIM_SetAutoReload(TIM3, 999);
-  LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_INACTIVE);
 
 
+  // Pull line down and start timer
+  LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_INACTIVE);
   LL_TIM_EnableCounter(TIM3);
 
   LL_TIM_EnableCounter(TIM3);
 
+  // Wait until the timer expires
   while (LL_TIM_IsEnabledCounter(TIM3))
     ;
   // Counter is automatically disabled at the end of cycle
 
   while (LL_TIM_IsEnabledCounter(TIM3))
     ;
   // Counter is automatically disabled at the end of cycle
 
+  // Disable DMA
   LL_TIM_DisableDMAReq_CC1(TIM3);
   LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_4);
 
   LL_TIM_DisableDMAReq_CC1(TIM3);
   LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_4);
 
-  debug_printf("DMA = %08x [%u] (%u remains)\r\n", dma_buffer, !!(dma_buffer & THERMO_Pin), LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_4));
+  // debug_printf("Init DMA: %08x [%u] (%u remains)\r\n", ds_dma_buffer, !!(ds_dma_buffer & THERMO_Pin), LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_4));
+}
+
+static void ds_send_byte(byte b)
+{
+  // debug_printf("DS write: %02x\r\n", b);
+  LL_TIM_SetAutoReload(TIM3, 99);      // Each write slot takes 100μs
+  for (uint m=1; m < 0x100; m <<= 1)
+    {
+      LL_TIM_OC_SetCompareCH2(TIM3, ((b & m) ? 1 : 89));       // 1: 1μs pulse, 0: 89μs pulse
+      LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_FORCED_ACTIVE);
+      LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_INACTIVE);
+      LL_TIM_EnableCounter(TIM3);
+      while (LL_TIM_IsEnabledCounter(TIM3))
+       ;
+    }
 }
 
 }
 
-static void ds18b20_send_byte(byte b)
+static byte ds_recv_byte(void)
 {
 {
-  LL_TIM_SetAutoReload(TIM3, 99);
-  for (uint m=0x80; m; m >>= 1)
+  LL_TIM_SetAutoReload(TIM3, 79);      // Each read slot takes 80μs
+  LL_TIM_OC_SetCompareCH2(TIM3, 1);    // Generate 1μs pulse to start read slot
+  LL_TIM_OC_SetCompareCH1(TIM3, 8);    // Sample data 8μs after start of slot
+  LL_TIM_EnableDMAReq_CC1(TIM3);
+
+  uint out = 0;
+  for (uint m=1; m < 0x100; m <<= 1)
     {
     {
-      LL_TIM_OC_SetCompareCH2(TIM3, ((b & m) ? 1 : 89));
+  ds_dma_buffer = 0xdeadbeef;
+  LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_4, 1);
+  LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_4);
       LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_FORCED_ACTIVE);
       LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_INACTIVE);
       LL_TIM_EnableCounter(TIM3);
       while (LL_TIM_IsEnabledCounter(TIM3))
        ;
       LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_FORCED_ACTIVE);
       LL_TIM_OC_SetMode(TIM3, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_INACTIVE);
       LL_TIM_EnableCounter(TIM3);
       while (LL_TIM_IsEnabledCounter(TIM3))
        ;
+      // FIXME: Using the Pin constant directly is fragile!
+      // debug_printf("XXX %08x\r\n", ds_dma_buffer);
+      if (ds_dma_buffer & THERMO_Pin)
+       out |= m;
+  LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_4);
     }
     }
+
+  LL_TIM_DisableDMAReq_CC1(TIM3);
+  // debug_printf("DS read: %02x\r\n", out);
+  return out;
+}
+
+static byte ds_buf[10];
+
+static int ds_recv_block(uint n)
+{
+  uint crc = 0;
+  for (uint i=0; i<n; i++)
+    {
+      uint b = ds_recv_byte();
+      ds_buf[i] = b;
+      for (uint j=0; j<8; j++)
+       {
+         uint k = (b & 1) ^ (crc >> 7);
+         crc = (crc << 1) & 0xff;
+         if (k)
+           crc ^= 0x31;
+         b >>= 1;
+       }
+    }
+
+  if (crc)
+    {
+      debug_printf("WARNING: Invalid CRC %02x\r\n", crc);
+      return 0;
+    }
+  return 1;
 }
 
 void run_test(void)
 }
 
 void run_test(void)
@@ -485,32 +552,37 @@ void run_test(void)
   uint cnt = 0;
 
   debug_puts("Init\r\n");
   uint cnt = 0;
 
   debug_puts("Init\r\n");
-  NVIC_DisableIRQ(TIM3_IRQn);
+  NVIC_DisableIRQ(TIM3_IRQn);  // One day, we will handle everything from interrupts...
 
 
-#if 0
-  LL_GPIO_InitTypeDef GPIO_InitStruct;
-  GPIO_InitStruct.Pin = THERMO_Pin;
-  GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
-  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
-  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
-  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
-  LL_GPIO_Init(THERMO_GPIO_Port, &GPIO_InitStruct);
-  LL_GPIO_SetOutputPin(THERMO_GPIO_Port, THERMO_Pin);
-#endif
+  // Identify device
+  ds_reset();
+  ds_send_byte(0x33);
+  ds_recv_block(8);
 
 
-  ds18b20_reset();
+  // FIXME: Configure precision
 
   for (;;)
     {
       LL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
 
   for (;;)
     {
       LL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
+      // debug_printf("Tick tock: %d\r\n", cnt);
+
+      // Start measurement
+      ds_reset();
+      ds_send_byte(0xcc);
+      ds_send_byte(0x44);
+      while (ds_recv_byte() != 0xff)
+       LL_mDelay(10);
+
+      // Read scratch pad
+      ds_reset();
+      ds_send_byte(0xcc);
+      ds_send_byte(0xbe);
+      ds_recv_block(9);
+      int t = (int16_t)(ds_buf[0] | (ds_buf[1] << 8));
+      t = t*1000/16;
+      debug_printf("Temp: %d.%03d degC\r\n", t/1000, t%1000);
 
 
-      debug_printf("Tick tock: %d\r\n", cnt);
-      // ds18b20_send_byte(0xcc);
-      ds18b20_reset();
-
-      LL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
-
-      LL_mDelay(100);
+      LL_mDelay(1000);
       cnt++;
     }
 }
       cnt++;
     }
 }