/* Model real SEARCH ROM participation: each branch filters the active slaves. * Fault injection tests transport recovery, not analog cable characteristics. */ #include "ds18b20.h" #include "ds18b20_config.h" #include #include #include typedef struct { ds18b20_t *bus; uint8_t roms[3][8]; uint8_t masks[3]; unsigned active, command_bits, bit, pair, presence, attempts; unsigned low_time, elapsed, low, critical, recovery; unsigned fault_attempt, fault_bit, persistent_fault, crc_fault; } port_t; static void low(void *ctx) { port_t *p = ctx; p->low = 1U; p->low_time = 0U; p->elapsed = 0U; } static unsigned rom_bit(port_t *p, unsigned i) { return (p->roms[i][p->bit / 8U] >> (p->bit % 8U)) & 1U; } static unsigned fault(port_t *p) { return p->attempts == p->fault_attempt || (p->persistent_fault && p->attempts >= p->fault_attempt); } static void release_line(void *ctx) { port_t *p = ctx; unsigned i, direction; if (!p->low) return; p->low = 0U; if (p->low_time == 480U) { ++p->attempts; p->active = p->masks[p->bus->search_diagnostics.passes_finished]; p->presence = 1U; p->command_bits = 0U; p->bit = 0U; p->pair = 0U; } else if (p->low_time != 3U) { assert(p->low_time == 6U || p->low_time == 60U); if (p->command_bits < 8U) { ++p->command_bits; return; } direction = p->low_time == 6U; for (i = 0U; i < 3U; ++i) if (rom_bit(p, i) != direction) p->active &= ~(1U << i); ++p->bit; p->pair = 0U; } } static uint8_t read_line(void *ctx) { port_t *p = ctx; unsigned i, zeros = 0U, ones = 0U, value; assert(p->critical); if (p->presence) { assert(p->elapsed == 550U); p->presence = 0U; return p->active ? 0U : 1U; } assert(p->elapsed == 13U); /* The read sample did not move. */ assert(p->bit < 64U); for (i = 0U; i < 3U; ++i) if (p->active & (1U << i)) { if (rom_bit(p, i)) ++ones; else ++zeros; } value = p->pair++ == 0U ? !zeros : !ones; if (fault(p) && p->bit + 1U == p->fault_bit) value = p->crc_fault ? !value : 1U; return (uint8_t)value; } static void delay(void *ctx, uint32_t us) { port_t *p = ctx; p->elapsed += us; if (p->low) p->low_time += us; if (us == DS18B20_SLOT_RECOVERY_EXTRA_US) { assert(!p->critical && !p->low); ++p->recovery; } } static void enter(void *ctx) { port_t *p = ctx; assert(!p->critical); p->critical = 1U; } static void leave(void *ctx) { port_t *p = ctx; assert(p->critical); p->critical = 0U; } static const ds18b20_onewire_ops_t ops = {low, release_line, read_line, delay, NULL, enter, leave, NULL}; static void setup(ds18b20_t *bus, port_t *p, uint8_t storage[][8], size_t cap) { unsigned i; memset(p, 0, sizeof(*p)); p->bus = bus; for (i = 0U; i < 3U; ++i) { p->roms[i][0] = 0x28U; p->roms[i][1] = (uint8_t)(i + 1U); p->roms[i][7] = ds18b20_crc8(p->roms[i], 7U); p->masks[i] = 7U; } assert(ds18b20_init(bus, &ops, p, storage, cap) == DS18B20_OK); } static ds18b20_status_t finish(ds18b20_t *bus, port_t *p) { ds18b20_status_t s; unsigned calls = 0U, before; do { before = p->attempts; s = ds18b20_search_step(bus); assert(p->attempts <= before + 1U); /* Never retries in a tight loop. */ assert(++calls <= DS18B20_SEARCH_MAX_ATTEMPTS + 1U); } while (s == DS18B20_E_BUSY); before = p->attempts; assert(ds18b20_search_step(bus) == s && p->attempts == before); return s; } int main(void) { ds18b20_t bus; port_t p; uint8_t storage[3][8], saved[8], discrepancy, family; setup(&bus, &p, storage, 3U); assert(ds18b20_search(&bus) == DS18B20_OK); assert(bus.rom_count == 3U && p.attempts == 9U); assert(bus.search_diagnostics.complete_passes == 3U && p.recovery != 0U); assert(bus.search_diagnostics.last_error == DS18B20_OK); /* A fault on the second branch must restore the PREVIOUS tree path. */ setup(&bus, &p, storage, 3U); p.fault_attempt = 2U; p.fault_bit = 17U; ds18b20_search_begin(&bus); assert(ds18b20_search_step(&bus) == DS18B20_E_BUSY); memcpy(saved, bus.search_rom, 8U); discrepancy = bus.last_discrepancy; family = bus.last_family_discrepancy; assert(ds18b20_search_step(&bus) == DS18B20_E_BUSY); assert(memcmp(saved, bus.search_rom, 8U) == 0); assert(bus.last_discrepancy == discrepancy && bus.last_family_discrepancy == family); assert(finish(&bus, &p) == DS18B20_OK && bus.rom_count == 3U); assert(bus.search_diagnostics.retries == 1U); assert(bus.search_diagnostics.last_error_bit == 17U && bus.search_diagnostics.last_error_pair == 3U); setup(&bus, &p, storage, 3U); p.fault_attempt = 2U; p.fault_bit = 64U; p.crc_fault = 1U; assert(ds18b20_search(&bus) == DS18B20_OK && bus.rom_count == 3U); assert(bus.search_diagnostics.crc_errors == 1U && bus.search_diagnostics.retries == 1U); assert(bus.search_diagnostics.last_error_bit == 64U); /* Each pass sees a different device; the validated union retains all three. */ setup(&bus, &p, storage, 3U); p.masks[0] = 1U; p.masks[1] = 2U; p.masks[2] = 4U; assert(ds18b20_search(&bus) == DS18B20_OK && bus.rom_count == 3U); assert(p.attempts == 3U); setup(&bus, &p, storage, 3U); memset(p.masks, 0, sizeof(p.masks)); assert(ds18b20_search(&bus) == DS18B20_E_NO_DEVICE); assert(p.attempts == 15U && bus.search_diagnostics.retries == 12U); assert(bus.search_diagnostics.last_error_bit == 0U); assert(ds18b20_add_known_rom(&bus, p.roms[0]) == DS18B20_OK); assert(ds18b20_add_known_rom(&bus, p.roms[0]) == DS18B20_OK && bus.rom_count == 1U); assert(ds18b20_add_known_rom(&bus, NULL) == DS18B20_E_ARGUMENT); p.roms[1][7] ^= 1U; assert(ds18b20_add_known_rom(&bus, p.roms[1]) == DS18B20_E_CRC); setup(&bus, &p, storage, 3U); p.fault_attempt = 2U; p.fault_bit = 17U; p.persistent_fault = 1U; assert(ds18b20_search(&bus) == DS18B20_E_IO && bus.rom_count == 1U); assert(p.attempts == 16U && bus.search_diagnostics.retries == 12U); assert(bus.search_diagnostics.complete_passes == 0U); setup(&bus, &p, storage, 3U); p.fault_attempt = 1U; p.fault_bit = 64U; p.persistent_fault = 1U; p.crc_fault = 1U; assert(ds18b20_search(&bus) == DS18B20_E_CRC && bus.rom_count == 0U); assert(bus.search_diagnostics.crc_errors == 15U); setup(&bus, &p, storage, 1U); assert(ds18b20_search(&bus) == DS18B20_E_CAPACITY && bus.rom_count == 1U); setup(&bus, &p, storage, 3U); ds18b20_search_begin(&bus); bus.search_diagnostics.attempts = DS18B20_SEARCH_MAX_ATTEMPTS; assert(finish(&bus, &p) == DS18B20_E_TIMEOUT && p.attempts == 0U); puts("DS18B20 robust search, retries, CRC, union, limits and timing: OK"); return 0; }