-// DS18B20 Temperature Sensor
+// DS18B20 Temperature Sensors
#include "util.h"
#include "ds18b20.h"
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/timer.h>
+#include <string.h>
static volatile u32 ds_dma_buffer;
return 1;
}
+static void ds_send_bit(bool bit)
+{
+ timer_set_period(DS_TIMER, 99); // Each write slot takes 100 μs
+ timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
+ timer_set_oc_value(DS_TIMER, TIM_OC2, (bit ? 3 : 89)); // 1: 3μs pulse, 0: 89μs pulse
+ cm_disable_interrupts();
+ // XXX: On STM32F1, we must configure the OC channel _after_ we enable the counter,
+ // otherwise OC triggers immediately. Reasons?
+ timer_enable_counter(DS_TIMER);
+ timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
+ cm_enable_interrupts();
+ while (timer_is_counter_enabled(DS_TIMER))
+ ;
+}
+
static void ds_send_byte(byte b)
{
DEBUG2("DS write: %02x\n", b);
- timer_set_period(DS_TIMER, 99); // Each write slot takes 100 μs
- for (uint m = 1; m < 0x100; m <<= 1) {
- timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
- timer_set_oc_value(DS_TIMER, TIM_OC2, ((b & m) ? 3 : 89)); // 1: 3μs pulse, 0: 89μs pulse
- cm_disable_interrupts();
- // XXX: On STM32F1, we must configure the OC channel _after_ we enable the counter,
- // otherwise OC triggers immediately. Reasons?
- timer_enable_counter(DS_TIMER);
- timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
- cm_enable_interrupts();
- while (timer_is_counter_enabled(DS_TIMER))
- ;
- }
+ for (uint m = 1; m < 0x100; m <<= 1)
+ ds_send_bit(b & m);
}
-static byte ds_recv_byte(void)
+static bool ds_recv_bit(void)
{
- timer_set_period(DS_TIMER, 79); // Each read slot takes 80μs
+ timer_set_period(DS_TIMER, 79); // Each read slot takes 80μs
timer_set_oc_value(DS_TIMER, TIM_OC2, 2); // Generate 2μs pulse to start read slot
timer_set_oc_value(DS_TIMER, TIM_OC1, 8); // Sample data 8μs after start of slot
timer_enable_dma_cc1(DS_TIMER);
+ ds_dma_buffer = 0xdeadbeef;
+ dma_set_number_of_data(DS_DMA, DS_DMA_CH, 1);
+ dma_enable_channel(DS_DMA, DS_DMA_CH);
+ timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
+ cm_disable_interrupts();
+ timer_enable_counter(DS_TIMER);
+ timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
+ cm_enable_interrupts();
+ while (timer_is_counter_enabled(DS_TIMER))
+ ;
+ // DEBUG2("XXX %08x\n", ds_dma_buffer);
+ bool out = ds_dma_buffer & GPIO7;
+ dma_disable_channel(DS_DMA, DS_DMA_CH);
+
+ timer_disable_dma_cc1(DS_TIMER);
+
+ return out;
+}
+
+static byte ds_recv_byte(void)
+{
uint out = 0;
for (uint m = 1; m < 0x100; m <<= 1) {
- ds_dma_buffer = 0xdeadbeef;
- dma_set_number_of_data(DS_DMA, DS_DMA_CH, 1);
- dma_enable_channel(DS_DMA, DS_DMA_CH);
- timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
- cm_disable_interrupts();
- timer_enable_counter(DS_TIMER);
- timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
- cm_enable_interrupts();
- while (timer_is_counter_enabled(DS_TIMER))
- ;
- // DEBUG2("XXX %08x\n", ds_dma_buffer);
- if (ds_dma_buffer & GPIO7)
+ if (ds_recv_bit())
out |= m;
- dma_disable_channel(DS_DMA, DS_DMA_CH);
}
- timer_disable_dma_cc1(DS_TIMER);
DEBUG2("DS read: %02x\n", out);
return out;
}
static byte ds_buf[10];
-static bool ds_recv_block(uint n)
+static byte ds_crc_block(uint n)
{
+ /// XXX: This might be worth optimizing
uint crc = 0;
+
for (uint i = 0; i < n; i++) {
- uint b = ds_recv_byte();
- // DEBUG("%02x ", b);
- ds_buf[i] = b;
+ byte b = ds_buf[i];
for (uint j = 0; j < 8; j++) {
uint k = (b & 1) ^ (crc >> 7);
crc = (crc << 1) & 0xff;
}
}
+ return crc;
+}
+
+static bool ds_recv_block(uint n)
+{
+ for (uint i = 0; i < n; i++)
+ ds_buf[i] = ds_recv_byte();
+
+ byte crc = ds_crc_block(n);
if (crc) {
DEBUG("DS18B20: Invalid CRC %02x\n", crc);
return 0;
return 1;
}
+struct ds_sensor ds_sensors[DS_NUM_SENSORS];
+
+#if DS_NUM_SENSORS == 1
+
+static void ds_enumerate(void)
+{
+ if (!ds_reset())
+ return;
+
+ ds_send_byte(0x33); // READ_ROM
+ if (!ds_recv_block(8))
+ return;
+
+ DEBUG("DS18B20: Found sensor ");
+ for (uint i = 0; i < 8; i++) {
+ DEBUG("%02x", ds_buf[i]);
+ ds_sensors[0].address[i] = ds_buf[i];
+ }
+ DEBUG("\n");
+}
+
+#else
+
+static void ds_enumerate(void)
+{
+ /*
+ * The enumeration algorithm roughly follows the one described in the
+ * Book of iButton Standards (Maxim Integrated Application Note 937).
+ *
+ * It simulates depth-first search on the trie of all device IDs.
+ * In each pass, it walks the trie from the root and recognizes branching nodes.
+ *
+ * The old_choice variable remembers the deepest left branch taken in the
+ * previous pass, new_choice is the same for the current pass.
+ */
+
+ DEBUG("DS18B20: Enumerate\n");
+
+ uint num_sensors = 0;
+ byte *addr = ds_buf;
+ byte old_choice = 0;
+
+ for (;;) {
+ if (!ds_reset()) {
+ DEBUG("DS18B20: Enumeration found no sensor\n");
+ return;
+ }
+
+ ds_send_byte(0xf0); // SEARCH_ROM
+ byte new_choice = 0;
+ for (byte i=0; i<64; i++) {
+ bool have_one = ds_recv_bit();
+ bool have_zero = ds_recv_bit();
+ bool old_bit = addr[i/8] & (1U << (i%8));
+ bool new_bit;
+ switch (2*have_one + have_zero) {
+ case 3:
+ // This should not happen
+ DEBUG("DS18B20: Enumeration failed\n");
+ return;
+ case 1:
+ // Only 0
+ new_bit = 0;
+ break;
+ case 2:
+ // Only 1
+ new_bit = 1;
+ break;
+ default:
+ // Both
+ if (i == old_choice)
+ new_bit = 1;
+ else if (i > old_choice) {
+ new_bit = 0;
+ new_choice = i;
+ } else {
+ new_bit = old_bit;
+ if (!new_bit)
+ new_choice = i;
+ }
+ }
+ if (new_bit)
+ addr[i/8] |= 1U << (i%8);
+ else
+ addr[i/8] &= ~(1U << (i%8));
+ ds_send_bit(new_bit);
+ }
+
+ if (num_sensors >= DS_NUM_SENSORS) {
+ DEBUG("DS18B20: Too many sensors\n");
+ return;
+ }
+
+ DEBUG("DS18B20: Found sensor #%u: ", num_sensors);
+ for (byte i=0; i<8; i++)
+ DEBUG("%02x", addr[i]);
+ if (ds_crc_block(8)) {
+ DEBUG(" - invalid CRC!\n");
+ } else if (ds_buf[0] == 0x28) {
+ DEBUG("\n");
+ memcpy(ds_sensors[num_sensors].address, ds_buf, 8);
+ num_sensors++;
+ } else {
+ DEBUG(" - wrong type\n");
+ }
+
+ old_choice = new_choice;
+ if (!old_choice)
+ break;
+ }
+}
+
+#endif
+
void ds_init(void)
{
DEBUG("DS18B20: Init\n");
+ for (uint i = 0; i < DS_NUM_SENSORS; i++) {
+ memset(ds_sensors[i].address, 0, 8);
+ ds_sensors[i].current_temp = DS_TEMP_UNKNOWN;
+ }
+
dma_set_read_from_peripheral(DS_DMA, DS_DMA_CH);
dma_set_priority(DS_DMA, DS_DMA_CH, DMA_CCR_PL_VERY_HIGH);
dma_disable_peripheral_increment_mode(DS_DMA, DS_DMA_CH);
gpio_set_mode(DS_GPIO, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO7);
- // Identify device
- if (!ds_reset())
- return;
- ds_send_byte(0x33);
- ds_recv_block(8);
+ ds_enumerate();
+
+ // FIXME: Configure precision?
+}
- // FIXME: Configure precision
+#if DS_NUM_SENSORS == 1
+#define ds_current_id 0
+#else
+ static byte ds_current_id;
+#endif
+
+static bool ds_activate(void)
+{
+ if (!ds_reset()) {
+ DEBUG("DS18B20: Reset failed\n");
+ return false;
+ }
+#if DS_NUM_SENSORS == 1
+ ds_send_byte(0xcc); // SKIP_ROM
+#else
+ ds_send_byte(0x55); // MATCH_ROM
+ for (uint i = 0; i < 8; i++)
+ ds_send_byte(ds_sensors[ds_current_id].address[i]);
+#endif
+ return true;
}
void ds_step(void)
if (!ds_running) {
// Start measurement
- if (!ds_reset()) {
- ds_current_temp = DS_TEMP_UNKNOWN;
+#if DS_NUM_SENSORS != 1
+ uint maxn = DS_NUM_SENSORS;
+ do {
+ if (!maxn--)
+ return;
+ ds_current_id++;
+ if (ds_current_id >= DS_NUM_SENSORS)
+ ds_current_id = 0;
+ } while (!ds_sensors[ds_current_id].address[0]);
+#endif
+ if (!ds_activate()) {
+ ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
return;
}
- ds_send_byte(0xcc);
- ds_send_byte(0x44);
+ ds_send_byte(0x44); // CONVERT_T
ds_running = 1;
ds_timeout = 255;
} else {
// Still running?
- if (ds_recv_byte() != 0xff) {
+ if (!ds_recv_bit()) {
if (!ds_timeout--) {
- ds_current_temp = DS_TEMP_UNKNOWN;
+ DEBUG("DS18B20 #%u: Timeout\n", ds_current_id);
+ ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
ds_running = 0;
- DEBUG("DS18B20: Timeout\n");
}
return;
}
ds_running = 0;
// Read scratch pad
- if (!ds_reset()) {
- ds_current_temp = DS_TEMP_UNKNOWN;
+ if (!ds_activate())
return;
- }
- ds_send_byte(0xcc);
- ds_send_byte(0xbe);
+ ds_send_byte(0xbe); // READ_SCRATCHPAD
if (!ds_recv_block(9)) {
- ds_current_temp = DS_TEMP_UNKNOWN;
+ ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
return;
}
int t = (int16_t) (ds_buf[0] | (ds_buf[1] << 8));
t = t * 1000 / 16;
- DEBUG("DS18B20: %d.%03d degC\n", t / 1000, t % 1000);
- ds_current_temp = t;
+ DEBUG("DS18B20 #%u: %d.%03d degC\n", ds_current_id, t / 1000, t % 1000);
+ ds_sensors[ds_current_id].current_temp = t;
}
}