#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_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;
+ // 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_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;
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_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);
+ // Wait until the timer expires
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);
- 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))
;
+ // 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)
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);
+ // 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++;
}
}