165 lines
5.7 KiB
C
165 lines
5.7 KiB
C
#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;
|
|
}
|