#include "ds18b20.h" #include "ds18b20_config.h" #include #define OW_SEARCH_ROM 0xF0U #define OW_MATCH_ROM 0x55U #define OW_SKIP_ROM 0xCCU #define DS_CONVERT 0x44U #define DS_READ_SCRATCHPAD 0xBEU #define DS_WRITE_SCRATCHPAD 0x4EU #define DS_COPY_SCRATCHPAD 0x48U #define DS_RECALL_E2 0xB8U static int valid_instance(const ds18b20_t *d) { return d && d->ops && d->ops->drive_low && d->ops->release && d->ops->read && d->ops->delay_us && d->roms && d->rom_capacity; } static void critical(ds18b20_t *d, int enter) { if (enter && d->ops->critical_enter) d->ops->critical_enter(d->platform_context); if (!enter && d->ops->critical_exit) d->ops->critical_exit(d->platform_context); } static void write_bit(ds18b20_t *d, uint8_t bit) { critical(d, 1); d->ops->drive_low(d->platform_context); d->ops->delay_us(d->platform_context, bit ? 6U : 60U); d->ops->release(d->platform_context); d->ops->delay_us(d->platform_context, bit ? 64U : 10U); critical(d, 0); if (d->search_active) d->ops->delay_us(d->platform_context, DS18B20_SLOT_RECOVERY_EXTRA_US); } static uint8_t read_bit(ds18b20_t *d) { uint8_t bit; critical(d, 1); d->ops->drive_low(d->platform_context); d->ops->delay_us(d->platform_context, 3U); d->ops->release(d->platform_context); d->ops->delay_us(d->platform_context, 10U); bit = d->ops->read(d->platform_context) ? 1U : 0U; d->ops->delay_us(d->platform_context, 57U); critical(d, 0); if (d->search_active) d->ops->delay_us(d->platform_context, DS18B20_SLOT_RECOVERY_EXTRA_US); return bit; } static void write_byte(ds18b20_t *d, uint8_t value) { uint8_t i; for (i = 0; i < 8U; ++i) { write_bit(d, value & 1U); value >>= 1U; } } static uint8_t read_byte(ds18b20_t *d) { uint8_t i, value = 0U; for (i = 0; i < 8U; ++i) value |= (uint8_t)(read_bit(d) << i); return value; } static ds18b20_status_t reset(ds18b20_t *d) { uint8_t level; d->ops->drive_low(d->platform_context); d->ops->delay_us(d->platform_context, 480U); /* Protect release-to-presence sampling from interrupt latency. */ critical(d, 1); d->ops->release(d->platform_context); d->ops->delay_us(d->platform_context, 70U); level = d->ops->read(d->platform_context); critical(d, 0); d->ops->delay_us(d->platform_context, 410U); return level ? DS18B20_E_NO_DEVICE : DS18B20_OK; } static void match(ds18b20_t *d, const uint8_t *rom) { uint8_t i; write_byte(d, OW_MATCH_ROM); for (i = 0U; i < 8U; ++i) write_byte(d, rom[i]); } uint8_t ds18b20_crc8(const void *data, size_t length) { const uint8_t *p = (const uint8_t *)data; uint8_t crc = 0U; while (length--) { uint8_t in = *p++, i; for (i = 0U; i < 8U; ++i) { uint8_t mix = (uint8_t)((crc ^ in) & 1U); crc >>= 1U; if (mix) crc ^= 0x8CU; in >>= 1U; } } return crc; } ds18b20_status_t ds18b20_validate_rom(const uint8_t rom[8]) { if (!rom) return DS18B20_E_ARGUMENT; if (rom[0] != DS18B20_FAMILY_CODE) return DS18B20_E_ROM; return ds18b20_crc8(rom, 7U) == rom[7] ? DS18B20_OK : DS18B20_E_CRC; } ds18b20_status_t ds18b20_init(ds18b20_t *d, const ds18b20_onewire_ops_t *ops, void *ctx, uint8_t (*roms)[8], size_t capacity) { if (!d || !ops || !roms || !capacity || !ops->drive_low || !ops->release || !ops->read || !ops->delay_us) return DS18B20_E_ARGUMENT; memset(d, 0, sizeof(*d)); d->ops = ops; d->platform_context = ctx; d->roms = roms; d->rom_capacity = capacity; ops->release(ctx); return DS18B20_OK; } static ds18b20_status_t search_next(ds18b20_t *d) { uint8_t bit_no=1U,last_zero=0U,byte_no=0U,mask=1U; ds18b20_status_t reset_status; if (d->last_device) return DS18B20_OK; d->search_diagnostics.last_error_bit = 0U; d->search_diagnostics.last_error_pair = 0xFFU; reset_status=reset(d); if (reset_status != DS18B20_OK) return reset_status; write_byte(d, OW_SEARCH_ROM); while (byte_no < 8U) { uint8_t id=read_bit(d), cmp=read_bit(d), dir; d->search_diagnostics.last_error_bit = bit_no; d->search_diagnostics.last_error_pair = (uint8_t)((id << 1U) | cmp); if (id && cmp) break; if (id != cmp) dir=id; else { dir=(bit_nolast_discrepancy) ? ((d->search_rom[byte_no]&mask)!=0U) : (bit_no==d->last_discrepancy); if (!dir) { last_zero=bit_no; if (last_zero<9U) d->last_family_discrepancy=last_zero; } } if (dir) d->search_rom[byte_no]|=mask; else d->search_rom[byte_no]&=(uint8_t)~mask; write_bit(d,dir); ++bit_no; mask<<=1U; if (!mask) { ++byte_no; mask=1U; } } if (bit_no < 65U || !d->search_rom[0]) { d->last_discrepancy=0; d->last_device=0; return DS18B20_E_IO; } d->last_discrepancy=last_zero; if (!last_zero) d->last_device=1U; return DS18B20_E_BUSY; } /* Reset only traversal state: the union of validated ROMs survives passes. */ static void search_tree_reset(ds18b20_t *d) { d->last_discrepancy = 0U; d->last_family_discrepancy = 0U; d->last_device = 0U; d->search_retries = 0U; memset(d->search_rom, 0, sizeof(d->search_rom)); } static ds18b20_status_t search_stop(ds18b20_t *d, ds18b20_status_t result) { d->search_active = 0U; d->search_result = result; return result; } static ds18b20_status_t search_pass_finish(ds18b20_t *d, uint8_t complete) { ds18b20_search_diagnostics_t *diag = &d->search_diagnostics; ++diag->passes_finished; if (complete) ++diag->complete_passes; if (diag->passes_finished < DS18B20_SEARCH_PASSES) { search_tree_reset(d); return DS18B20_E_BUSY; } /* A completed traversal can recover earlier faults. Without one, retain * the partial catalog but report the failure instead of claiming success. */ if (diag->complete_passes != 0U) return search_stop(d, d->rom_count ? DS18B20_OK : DS18B20_E_NO_DEVICE); return search_stop(d, diag->last_error); } ds18b20_status_t ds18b20_search_begin(ds18b20_t *d) { if (!valid_instance(d)) return DS18B20_E_ARGUMENT; if (d->strong_pullup_active) return DS18B20_E_BUSY; d->rom_count = 0U; search_tree_reset(d); memset(&d->search_diagnostics, 0, sizeof(d->search_diagnostics)); d->search_diagnostics.last_error_pair = 0xFFU; d->search_active = 1U; d->search_result = DS18B20_E_BUSY; return DS18B20_E_BUSY; } ds18b20_status_t ds18b20_search_step(ds18b20_t *d) { uint8_t previous_rom[8], previous_discrepancy, previous_family; ds18b20_search_diagnostics_t previous_diag; ds18b20_status_t status; if (!valid_instance(d)) return DS18B20_E_ARGUMENT; if (!d->search_active) return d->search_result; if (d->search_diagnostics.attempts >= DS18B20_SEARCH_MAX_ATTEMPTS) { d->search_diagnostics.last_error = DS18B20_E_TIMEOUT; d->search_diagnostics.last_error_bit = 0U; d->search_diagnostics.last_error_pair = 0xFFU; return search_stop(d, DS18B20_E_TIMEOUT); } /* Snapshot the preceding validated tree path BEFORE touching the wire. * A corrupt ROM must never become the path for the following attempt. */ memcpy(previous_rom, d->search_rom, 8U); previous_discrepancy = d->last_discrepancy; previous_family = d->last_family_discrepancy; previous_diag = d->search_diagnostics; ++d->search_diagnostics.attempts; status = search_next(d); if (status == DS18B20_E_BUSY) { /* CRC first: a valid non-DS18B20 family is skipped without corrupting * the traversal; a bad family caused by noise still gets retried. */ status = ds18b20_crc8(d->search_rom, 7U) == d->search_rom[7] ? DS18B20_OK : DS18B20_E_CRC; if (status == DS18B20_OK && d->search_rom[0] == DS18B20_FAMILY_CODE) status = ds18b20_add_known_rom(d, d->search_rom); } if (status == DS18B20_OK) { /* Keep the last FAILURE location, even when a later retry succeeds. */ d->search_diagnostics.last_error_bit = previous_diag.last_error_bit; d->search_diagnostics.last_error_pair = previous_diag.last_error_pair; d->search_retries = 0U; return d->last_device ? search_pass_finish(d, 1U) : DS18B20_E_BUSY; } d->search_diagnostics.last_error = status; if (status == DS18B20_E_CAPACITY) return search_stop(d, status); if (status == DS18B20_E_CRC) ++d->search_diagnostics.crc_errors; else ++d->search_diagnostics.io_errors; memcpy(d->search_rom, previous_rom, 8U); d->last_discrepancy = previous_discrepancy; d->last_family_discrepancy = previous_family; d->last_device = 0U; if (d->search_retries < DS18B20_SEARCH_RETRIES) { ++d->search_retries; ++d->search_diagnostics.retries; return DS18B20_E_BUSY; } return search_pass_finish(d, 0U); } ds18b20_status_t ds18b20_search(ds18b20_t *d) { ds18b20_status_t status; if (!valid_instance(d)) return DS18B20_E_ARGUMENT; if (d->strong_pullup_active) return DS18B20_E_BUSY; status = ds18b20_search_begin(d); while (status == DS18B20_E_BUSY) status = ds18b20_search_step(d); return status; } ds18b20_status_t ds18b20_add_known_rom(ds18b20_t *d, const uint8_t rom[8]) { size_t i; ds18b20_status_t status; if (!valid_instance(d)) return DS18B20_E_ARGUMENT; status = ds18b20_validate_rom(rom); if (status != DS18B20_OK) return status; for (i = 0U; i < d->rom_count; ++i) if (memcmp(d->roms[i], rom, 8U) == 0) return DS18B20_OK; if (d->rom_count >= d->rom_capacity) return DS18B20_E_CAPACITY; memcpy(d->roms[d->rom_count++], rom, 8U); return DS18B20_OK; } size_t ds18b20_count(const ds18b20_t *d) { return valid_instance(d) ? d->rom_count : 0U; } const uint8_t *ds18b20_rom(const ds18b20_t *d,size_t i) { return valid_instance(d)&&irom_count?d->roms[i]:NULL; } ds18b20_status_t ds18b20_start_all(ds18b20_t *d) { if(!valid_instance(d))return DS18B20_E_ARGUMENT; if(reset(d))return DS18B20_E_NO_DEVICE; write_byte(d,OW_SKIP_ROM);write_byte(d,DS_CONVERT);return DS18B20_OK; } ds18b20_status_t ds18b20_start(ds18b20_t *d,const uint8_t *rom) { ds18b20_status_t s;if(!valid_instance(d)||!rom)return DS18B20_E_ARGUMENT;if(ds18b20_validate_rom(rom))return DS18B20_E_ROM;s=reset(d);if(s)return s;match(d,rom);write_byte(d,DS_CONVERT);return DS18B20_OK; } ds18b20_status_t ds18b20_conversion_ready(ds18b20_t *d) { if(!valid_instance(d))return DS18B20_E_ARGUMENT;return read_bit(d)?DS18B20_OK:DS18B20_E_BUSY; } ds18b20_status_t ds18b20_wait(ds18b20_t *d,uint32_t timeout) { uint32_t start;if(!valid_instance(d)||!d->ops->tick_ms)return DS18B20_E_ARGUMENT;start=d->ops->tick_ms(d->platform_context);while(!read_bit(d))if((uint32_t)(d->ops->tick_ms(d->platform_context)-start)>timeout)return DS18B20_E_TIMEOUT;return DS18B20_OK; } ds18b20_status_t ds18b20_read_scratchpad(ds18b20_t *d,const uint8_t *rom,uint8_t *sp) { uint8_t i;ds18b20_status_t s;if(!valid_instance(d)||!rom||!sp)return DS18B20_E_ARGUMENT;if(ds18b20_validate_rom(rom))return DS18B20_E_ROM;s=reset(d);if(s)return s;match(d,rom);write_byte(d,DS_READ_SCRATCHPAD);for(i=0;i<9U;++i)sp[i]=read_byte(d);return ds18b20_crc8(sp,8U)==sp[8]?DS18B20_OK:DS18B20_E_CRC; } ds18b20_status_t ds18b20_decode_temperature(const uint8_t *sp,float *out) { int16_t raw;uint8_t cfg;if(!sp||!out)return DS18B20_E_ARGUMENT;if(ds18b20_crc8(sp,8U)!=sp[8])return DS18B20_E_CRC;cfg=sp[4]&0x60U;raw=(int16_t)((uint16_t)sp[0]|((uint16_t)sp[1]<<8));if(cfg==0)raw&=(int16_t)~7;else if(cfg==0x20)raw&=(int16_t)~3;else if(cfg==0x40)raw&=(int16_t)~1;else if(cfg!=0x60)return DS18B20_E_IO;*out=(float)raw/16.0f;return DS18B20_OK; } static ds18b20_status_t write_config(ds18b20_t *d,const uint8_t *rom,uint8_t th,uint8_t tl,uint8_t cfg) { uint8_t chunk; ds18b20_status_t s=ds18b20_write_scratchpad(d,rom,th,tl,cfg); if(s)return s;s=ds18b20_copy_scratchpad_start(d,rom,0U);if(s)return s; /* TIM1 counter is 16-bit: at 72 ticks/us a single 10 ms wait can never * satisfy the port comparison. Short chunks preserve wrap-safe timing. */ for(chunk=0U;chunk<100U;++chunk)d->ops->delay_us(d->platform_context,100U); return ds18b20_copy_scratchpad_finish(d,0U); } ds18b20_status_t ds18b20_set_resolution(ds18b20_t *d,const uint8_t *rom,uint8_t bits) { uint8_t sp[9],cfg;ds18b20_status_t s;if(bits<9U||bits>12U)return DS18B20_E_ARGUMENT;s=ds18b20_read_scratchpad(d,rom,sp);if(s)return s;cfg=(uint8_t)(0x1FU|((bits-9U)<<5));return write_config(d,rom,sp[2],sp[3],cfg); } ds18b20_status_t ds18b20_write_user_bytes(ds18b20_t *d,const uint8_t *rom,int16_t b12,int16_t b34,uint8_t mask) { uint8_t sp[9];ds18b20_status_t s=ds18b20_read_scratchpad(d,rom,sp);(void)b34;if(s)return s;if(mask&1U)sp[2]=(uint8_t)b12;if(mask&2U)sp[3]=(uint8_t)(b12>>8);/* DS18B20 физически позволяет записать только TH/TL/config; байты 6/7 read-only. */return write_config(d,rom,sp[2],sp[3],sp[4]); } ds18b20_status_t ds18b20_write_scratchpad(ds18b20_t *d,const uint8_t *rom, uint8_t th,uint8_t tl,uint8_t cfg) { ds18b20_status_t s; if(!valid_instance(d)||!rom)return DS18B20_E_ARGUMENT; s=ds18b20_validate_rom(rom);if(s)return s; s=reset(d);if(s)return s;match(d,rom);write_byte(d,DS_WRITE_SCRATCHPAD); write_byte(d,th);write_byte(d,tl);write_byte(d,cfg);return DS18B20_OK; } ds18b20_status_t ds18b20_copy_scratchpad_start(ds18b20_t *d,const uint8_t *rom, uint8_t parasite) { ds18b20_status_t s; if(!valid_instance(d)||!rom)return DS18B20_E_ARGUMENT; if(parasite && !d->ops->strong_pullup)return DS18B20_E_POWER; s=ds18b20_validate_rom(rom);if(s)return s; s=reset(d);if(s)return s;match(d,rom);write_byte(d,DS_COPY_SCRATCHPAD); /* The pull-up must be asserted immediately after the command slot. */ if(parasite){d->ops->strong_pullup(d->platform_context,1U); d->strong_pullup_active=1U;} return DS18B20_OK; } ds18b20_status_t ds18b20_copy_scratchpad_finish(ds18b20_t *d,uint8_t parasite) { if(!valid_instance(d))return DS18B20_E_ARGUMENT; if(parasite){if(!d->ops->strong_pullup)return DS18B20_E_POWER; if(d->strong_pullup_active){d->ops->strong_pullup(d->platform_context,0U); d->strong_pullup_active=0U;}} d->ops->release(d->platform_context);return DS18B20_OK; } ds18b20_status_t ds18b20_recall_e2(ds18b20_t *d,const uint8_t *rom) { ds18b20_status_t s;if(!valid_instance(d)||!rom)return DS18B20_E_ARGUMENT; s=ds18b20_validate_rom(rom);if(s)return s;s=reset(d);if(s)return s; match(d,rom);write_byte(d,DS_RECALL_E2);return DS18B20_OK; } ds18b20_status_t ds18b20_recall_ready(ds18b20_t *d) {if(!valid_instance(d))return DS18B20_E_ARGUMENT;return read_bit(d)?DS18B20_OK:DS18B20_E_BUSY;} void ds18b20_recover_bus(ds18b20_t *d) { if(!valid_instance(d))return; if(d->strong_pullup_active&&d->ops->strong_pullup){ d->ops->strong_pullup(d->platform_context,0U);d->strong_pullup_active=0U;} d->ops->release(d->platform_context); } uint8_t ds18b20_search_retry_due(const ds18b20_t *d,uint8_t conversion_active, uint32_t now,uint32_t last,uint32_t period) { if(!valid_instance(d)||d->rom_count!=0U||conversion_active||period==0U)return 0U; /* Unsigned subtraction keeps the retry correct across HAL tick rollover. */ return ((uint32_t)(now-last)>=period)?1U:0U; }