153 lines
6.7 KiB
C
153 lines
6.7 KiB
C
/* 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 <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
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;
|
|
}
|