X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=test-opencm3%2Fds18b20.c;h=434af43f9ac8ce7965b6e3cd7b65dbedc694e6bf;hb=71d8e81533dc24d06cae545edac06c0fe393b374;hp=fda6e83ef7580ebd3ad20046ed927e90038081bd;hpb=5f30fc3f24fb95a4ff745d3a8fadd0b4e4affaa3;p=home-hw.git diff --git a/test-opencm3/ds18b20.c b/test-opencm3/ds18b20.c index fda6e83..434af43 100644 --- a/test-opencm3/ds18b20.c +++ b/test-opencm3/ds18b20.c @@ -1,4 +1,4 @@ -// DS18B20 Temperature Sensor +// DS18B20 Temperature Sensors #include "util.h" #include "ds18b20.h" @@ -8,6 +8,7 @@ #include #include #include +#include static volatile u32 ds_dma_buffer; @@ -17,7 +18,7 @@ static volatile u32 ds_dma_buffer; #define DS_DMA DMA1 #define DS_DMA_CH 6 -#define DS_DEBUG +#undef DS_DEBUG #undef DS_DEBUG2 #ifdef DS_DEBUG @@ -108,63 +109,75 @@ static bool ds_reset(void) 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; @@ -174,6 +187,15 @@ static bool ds_recv_block(uint n) } } + 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; @@ -181,10 +203,129 @@ static bool ds_recv_block(uint n) 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); @@ -198,13 +339,31 @@ void ds_init(void) 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? +} + +#if DS_NUM_SENSORS == 1 +#define ds_current_id 0 +#else + static byte ds_current_id; +#endif - // FIXME: Configure precision +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) @@ -214,41 +373,48 @@ 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; } }