#include "ds2480.h" static ds2480_status fault(ds2480 *bus, ds2480_status status) { bus->ready = 0; return status; } static ds2480_status exchange(ds2480 *bus, uint8_t command, uint8_t *reply) { if (!bus || !reply) return DS2480_ARGUMENT; if (!bus->ready) return DS2480_NOT_READY; if (bus->port.write(bus->port.user, &command, 1, bus->timeout_ms) || bus->port.read(bus->port.user, reply, 1, bus->timeout_ms)) return fault(bus, DS2480_IO); return DS2480_OK; } ds2480_status ds2480_init(ds2480 *bus, const ds2480_port *port, uint32_t timeout_ms) { uint8_t calibration = 0xC1, reply; ds2480_status status; ds2480_port copy; if (!bus) return DS2480_ARGUMENT; if (!port || !port->prepare || !port->write || !port->read || !port->delay_ms || !timeout_ms) { bus->ready = 0; return DS2480_ARGUMENT; } copy = *port; /* Also permit reinitialization with &bus->port. */ bus->ready = 0; bus->port = copy; bus->timeout_ms = timeout_ms; if (copy.prepare(copy.user) || copy.write(copy.user, &calibration, 1, timeout_ms)) return DS2480_IO; /* First C1 calibrates only: there is NO response byte. */ copy.delay_ms(copy.user, 2); bus->ready = 1; /* Strong pullup duration = infinite, command-mode operations only. */ status = exchange(bus, 0x3F, &reply); if (status != DS2480_OK) return status; if (reply != 0x3E) return fault(bus, DS2480_PROTOCOL); return DS2480_OK; } ds2480_status ds2480_reset(ds2480 *bus) { uint8_t reply; ds2480_status status = exchange(bus, 0xC1, &reply); if (status != DS2480_OK) return status; /* DS2480B revision pattern from table 2; bit 5 is unspecified. */ if ((reply & 0xDC) != 0xCC) return fault(bus, DS2480_PROTOCOL); switch (reply & 3) { case 0: return DS2480_SHORT; case 3: return DS2480_NO_PRESENCE; default: return DS2480_OK; /* Ordinary or alarming presence. */ } } static ds2480_status slot(ds2480 *bus, uint8_t bit, uint8_t power, uint8_t *received) { uint8_t reply, command = (uint8_t)(0x81 | (bit ? 0x10 : 0) | (power ? 2 : 0)); ds2480_status status = exchange(bus, command, &reply); if (status != DS2480_OK) return status; if ((reply & 0xFC) != (command & 0xFC) || ((reply & 3) != 0 && (reply & 3) != 3)) return fault(bus, DS2480_PROTOCOL); *received = reply & 1; return DS2480_OK; } ds2480_status ds2480_bit(ds2480 *bus, uint8_t bit, uint8_t *received) { if (!received) return DS2480_ARGUMENT; return slot(bus, bit, 0, received); } ds2480_status ds2480_byte(ds2480 *bus, uint8_t value, uint8_t *received) { uint8_t i, bit, result = 0; ds2480_status status; if (!received) return DS2480_ARGUMENT; for (i = 0; i < 8; ++i) { status = ds2480_bit(bus, (uint8_t)((value >> i) & 1), &bit); if (status != DS2480_OK) return status; result |= (uint8_t)(bit << i); } *received = result; return DS2480_OK; } ds2480_status ds2480_power_byte(ds2480 *bus, uint8_t value, uint32_t hold_ms) { uint8_t i, bit, reply, echoed = 0; ds2480_status status; if (!bus || !hold_ms || hold_ms > 1000) return DS2480_ARGUMENT; for (i = 0; i < 8; ++i) { status = slot(bus, (uint8_t)((value >> i) & 1), (uint8_t)(i == 7), &bit); if (status != DS2480_OK) { /* A lost reply may leave an infinite pulse active: reset hardware. */ if (i == 7 && !bus->ready) (void)bus->port.prepare(bus->port.user); return status; } echoed |= (uint8_t)(bit << i); } bus->port.delay_ms(bus->port.user, hold_ms); status = exchange(bus, 0xF1, &reply); if (status == DS2480_OK && reply != 0xEC && reply != 0xEF) status = fault(bus, DS2480_PROTOCOL); if (status != DS2480_OK) (void)bus->port.prepare(bus->port.user); if (status == DS2480_OK && echoed != value) return DS2480_DATA; return status; } uint8_t ds2480_crc8(const uint8_t *data, uint32_t size) { uint8_t crc = 0, bit; uint32_t i; for (i = 0; i < size; ++i) { crc ^= data[i]; for (bit = 0; bit < 8; ++bit) crc = (uint8_t)((crc >> 1) ^ ((crc & 1) ? 0x8C : 0)); } return crc; } ds2480_status ds2480_search_next(ds2480 *bus, ds2480_search *search) { ds2480_search next; ds2480_status status; uint8_t pos, a, b, direction, ignored, last_zero = 0; if (!bus || !search || search->discrepancy > 64) return DS2480_ARGUMENT; if (search->done) return DS2480_DONE; next = *search; status = ds2480_reset(bus); if (status != DS2480_OK) return status; status = ds2480_byte(bus, 0xF0, &ignored); if (status != DS2480_OK) return status; for (pos = 1; pos <= 64; ++pos) { uint8_t index = (uint8_t)((pos - 1) / 8); uint8_t mask = (uint8_t)(1U << ((pos - 1) % 8)); status = ds2480_bit(bus, 1, &a); if (status != DS2480_OK) return status; status = ds2480_bit(bus, 1, &b); if (status != DS2480_OK) return status; if (a && b) return DS2480_DATA; /* Devices disappeared mid-search. */ if (a != b) direction = a; else { direction = pos < search->discrepancy ? (uint8_t)!!(search->rom[index] & mask) : (uint8_t)(pos == search->discrepancy); if (!direction) last_zero = pos; } if (direction) next.rom[index] |= mask; else next.rom[index] &= (uint8_t)~mask; status = ds2480_bit(bus, direction, &ignored); if (status != DS2480_OK) return status; } if (!next.rom[0]) return DS2480_DATA; if (ds2480_crc8(next.rom, 8)) return DS2480_CRC; next.discrepancy = last_zero; next.done = (uint8_t)(last_zero == 0); *search = next; return DS2480_OK; }