229 lines
8.8 KiB
C
229 lines
8.8 KiB
C
#include "ds18b20_ds2480.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define CHECK(x) do { if (!(x)) { fprintf(stderr, "line %d: %s\n", __LINE__, #x); exit(1); } } while (0)
|
|
|
|
enum { ROM_COMMAND, MATCH, FUNCTION, SEARCH, READ, CONFIG };
|
|
typedef struct {
|
|
uint8_t rom[3][8], scratch[9];
|
|
unsigned count, active, state, phase, position, bits, value, match_index;
|
|
unsigned prepared, calibrated, pending, reply, pulse, hold, convert, copy;
|
|
unsigned fail_read, fail_write, fail_prepare, corrupt_reply, reset_reply;
|
|
unsigned fail_power, fail_stop, reject_config;
|
|
} fake;
|
|
|
|
static void crc_scratch(fake *f) { f->scratch[8] = ds2480_crc8(f->scratch, 8); }
|
|
|
|
static void setup(fake *f)
|
|
{
|
|
unsigned i;
|
|
static const uint8_t scratch[9] = {0x91, 0x01, 0x4B, 0x46, 0x7F, 0xFF, 0x0C, 0x10, 0x70};
|
|
memset(f, 0, sizeof *f);
|
|
f->count = 3;
|
|
f->reset_reply = 0xCD;
|
|
memcpy(f->scratch, scratch, 9);
|
|
for (i = 0; i < 3; ++i) {
|
|
f->rom[i][0] = i == 2 ? 0x10 : 0x28;
|
|
f->rom[i][1] = (uint8_t)(i + 1);
|
|
f->rom[i][7] = ds2480_crc8(f->rom[i], 7);
|
|
}
|
|
}
|
|
|
|
static void byte_in(fake *f, uint8_t byte)
|
|
{
|
|
unsigned i;
|
|
if (f->state == ROM_COMMAND) {
|
|
if (byte == 0xF0) { f->state = SEARCH; f->position = f->phase = 0; }
|
|
else if (byte == 0x55) { f->state = MATCH; f->match_index = 0; }
|
|
else { CHECK(byte == 0xCC); f->state = FUNCTION; }
|
|
} else if (f->state == MATCH) {
|
|
for (i = 0; i < f->count; ++i)
|
|
if (f->rom[i][f->match_index] != byte) f->active &= ~(1U << i);
|
|
if (++f->match_index == 8) f->state = FUNCTION;
|
|
} else if (f->state == FUNCTION) {
|
|
CHECK(f->active != 0);
|
|
switch (byte) {
|
|
case 0xBE: f->state = READ; f->position = 0; break;
|
|
case 0x4E: f->state = CONFIG; f->position = 2; break;
|
|
case 0x44: ++f->convert; break;
|
|
case 0x48: ++f->copy; break;
|
|
default: CHECK(0);
|
|
}
|
|
} else {
|
|
CHECK(f->state == CONFIG);
|
|
if (!f->reject_config) f->scratch[f->position] = byte;
|
|
if (++f->position == 5) { crc_scratch(f); f->state = FUNCTION; }
|
|
}
|
|
}
|
|
|
|
static uint8_t wire_bit(fake *f, uint8_t bit)
|
|
{
|
|
unsigned i, result = 1;
|
|
if (f->state == SEARCH) {
|
|
for (i = 0; i < f->count; ++i) {
|
|
unsigned v = (f->rom[i][f->position / 8] >> (f->position % 8)) & 1;
|
|
if (!(f->active & (1U << i))) continue;
|
|
if (f->phase < 2) result &= f->phase ? !v : v;
|
|
else if (v != bit) f->active &= ~(1U << i);
|
|
}
|
|
if (++f->phase == 3) { f->phase = 0; ++f->position; }
|
|
return (uint8_t)result;
|
|
}
|
|
if (f->state == READ) {
|
|
CHECK(f->position < 72 && bit == 1);
|
|
result = (f->scratch[f->position / 8] >> (f->position % 8)) & 1;
|
|
++f->position;
|
|
return (uint8_t)result;
|
|
}
|
|
f->value |= (unsigned)bit << f->bits;
|
|
if (++f->bits == 8) {
|
|
uint8_t byte = (uint8_t)f->value;
|
|
f->bits = f->value = 0;
|
|
byte_in(f, byte);
|
|
}
|
|
return bit;
|
|
}
|
|
|
|
static int prepare(void *user)
|
|
{
|
|
fake *f = user;
|
|
++f->prepared;
|
|
f->pending = f->pulse = f->calibrated = 0;
|
|
return (int)f->fail_prepare;
|
|
}
|
|
|
|
static int transmit(void *user, const uint8_t *data, uint32_t size, uint32_t timeout)
|
|
{
|
|
fake *f = user;
|
|
uint8_t command = *data, bit;
|
|
CHECK(size == 1 && timeout == 20 && !f->pending);
|
|
if (f->fail_write) return -1;
|
|
if (!f->calibrated) { CHECK(command == 0xC1); f->calibrated = 1; return 0; }
|
|
CHECK(!f->pulse || command == 0xF1);
|
|
if (command == 0x3F) f->reply = 0x3E;
|
|
else if (command == 0xC1) {
|
|
f->reply = f->count ? f->reset_reply : 0xCF;
|
|
f->state = ROM_COMMAND; f->bits = f->value = 0;
|
|
f->active = (1U << f->count) - 1;
|
|
} else if (command == 0xF1) {
|
|
CHECK(f->pulse && (f->hold == 750 || f->hold == 12));
|
|
f->pulse = 0; f->reply = 0xEC;
|
|
if (f->fail_stop) f->fail_read = 1;
|
|
} else {
|
|
CHECK(command == 0x81 || command == 0x91 || command == 0x83 || command == 0x93);
|
|
bit = wire_bit(f, (uint8_t)((command >> 4) & 1));
|
|
f->reply = (command & 0xFC) | (bit ? 3U : 0U);
|
|
if (command & 2) {
|
|
CHECK(f->bits == 0 && (f->convert || f->copy));
|
|
f->pulse = 1; f->hold = 0;
|
|
if (f->fail_power) f->fail_read = 1;
|
|
}
|
|
}
|
|
f->pending = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int receive(void *user, uint8_t *data, uint32_t size, uint32_t timeout)
|
|
{
|
|
fake *f = user;
|
|
CHECK(size == 1 && timeout == 20 && f->pending);
|
|
if (f->fail_read) return -1;
|
|
*data = (uint8_t)(f->corrupt_reply ? 0 : f->reply);
|
|
f->pending = 0;
|
|
return 0;
|
|
}
|
|
|
|
static void delay(void *user, uint32_t ms)
|
|
{
|
|
fake *f = user;
|
|
if (f->pulse) f->hold += ms;
|
|
else CHECK(ms == 2);
|
|
}
|
|
|
|
static void init(fake *f, ds2480 *bus)
|
|
{
|
|
ds2480_port port = { f, prepare, transmit, receive, delay };
|
|
CHECK(ds2480_init(bus, &port, 20) == DS2480_OK);
|
|
CHECK(!f->pending);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
fake f, other;
|
|
ds2480 bus, second;
|
|
ds2480_search search = {{0}, 0, 0}, saved;
|
|
uint8_t bit, scratch[9], rom[8];
|
|
int16_t raw = 999;
|
|
unsigned found = 0, i;
|
|
setup(&f); init(&f, &bus);
|
|
CHECK(ds2480_crc8(f.scratch, 9) == 0); /* Datasheet vector, not generated. */
|
|
while (ds18b20_ds2480_next(&bus, &search) == DS2480_OK) {
|
|
if (!memcmp(search.rom, f.rom[0], 8)) found |= 1;
|
|
else if (!memcmp(search.rom, f.rom[1], 8)) found |= 2;
|
|
else CHECK(0);
|
|
}
|
|
CHECK(found == 3 && search.done);
|
|
CHECK(ds18b20_ds2480_next(&bus, &search) == DS2480_DONE);
|
|
CHECK(ds18b20_ds2480_convert(&bus, NULL) == DS2480_OK);
|
|
CHECK(f.convert == 1 && f.hold == 750 && !f.pulse && !f.pending);
|
|
CHECK(ds18b20_ds2480_temperature(&bus, f.rom[0], &raw) == DS2480_OK && raw == 401);
|
|
CHECK(ds18b20_ds2480_convert(&bus, f.rom[1]) == DS2480_OK && f.active == 2);
|
|
for (i = 9; i <= 12; ++i) {
|
|
CHECK(ds18b20_ds2480_configure(&bus, f.rom[0], -5, -20, (uint8_t)i, 1) == DS2480_OK);
|
|
CHECK(f.hold == 12 && !f.pulse && !f.pending);
|
|
f.scratch[0] = 0x5F; f.scratch[1] = 0xFF; crc_scratch(&f);
|
|
CHECK(ds18b20_ds2480_temperature(&bus, f.rom[0], &raw) == DS2480_OK);
|
|
CHECK(raw == (i == 9 ? -168 : i == 10 ? -164 : i == 11 ? -162 : -161));
|
|
}
|
|
CHECK(f.copy == 4);
|
|
f.reject_config = 1;
|
|
CHECK(ds18b20_ds2480_configure(&bus, f.rom[0], 1, 2, 9, 1) == DS2480_DATA && f.copy == 4);
|
|
f.scratch[8] ^= 1; raw = 999;
|
|
CHECK(ds18b20_ds2480_temperature(&bus, f.rom[0], &raw) == DS2480_CRC && raw == 999);
|
|
memset(f.scratch, 0, 9);
|
|
CHECK(ds18b20_ds2480_read(&bus, f.rom[0], scratch) == DS2480_DATA);
|
|
memset(f.scratch, 0xFF, 9);
|
|
CHECK(ds18b20_ds2480_temperature(&bus, f.rom[0], &raw) == DS2480_CRC);
|
|
memcpy(rom, f.rom[0], 8); rom[7] ^= 1;
|
|
CHECK(ds18b20_ds2480_convert(&bus, rom) == DS2480_ARGUMENT);
|
|
CHECK(ds18b20_ds2480_read(&bus, NULL, scratch) == DS2480_ARGUMENT);
|
|
CHECK(ds18b20_ds2480_configure(&bus, f.rom[0], 0, 0, 8, 0) == DS2480_ARGUMENT);
|
|
CHECK(ds2480_bit(&bus, 1, NULL) == DS2480_ARGUMENT);
|
|
f.reset_reply = 0xEC; CHECK(ds2480_reset(&bus) == DS2480_SHORT);
|
|
f.reset_reply = 0xEE; CHECK(ds2480_reset(&bus) == DS2480_OK);
|
|
f.count = 0; CHECK(ds2480_reset(&bus) == DS2480_NO_PRESENCE);
|
|
memset(&search, 0, sizeof search);
|
|
CHECK(ds2480_search_next(&bus, &search) == DS2480_NO_PRESENCE);
|
|
setup(&f); init(&f, &bus); f.count = 1; f.rom[0][7] ^= 1;
|
|
saved = search;
|
|
CHECK(ds2480_search_next(&bus, &search) == DS2480_CRC);
|
|
CHECK(!memcmp(&search, &saved, sizeof search));
|
|
f.fail_read = 1; CHECK(ds2480_reset(&bus) == DS2480_IO);
|
|
CHECK(ds2480_reset(&bus) == DS2480_NOT_READY);
|
|
setup(&f); init(&f, &bus); f.corrupt_reply = 1;
|
|
CHECK(ds2480_reset(&bus) == DS2480_PROTOCOL && !bus.ready);
|
|
setup(&f); init(&f, &bus); CHECK(ds2480_reset(&bus) == DS2480_OK); f.corrupt_reply = 1;
|
|
CHECK(ds2480_bit(&bus, 1, &bit) == DS2480_PROTOCOL);
|
|
setup(&f); init(&f, &bus); f.fail_write = 1;
|
|
CHECK(ds2480_reset(&bus) == DS2480_IO && !bus.ready);
|
|
setup(&f); init(&f, &bus); f.fail_power = 1;
|
|
CHECK(ds18b20_ds2480_convert(&bus, NULL) == DS2480_IO);
|
|
CHECK(!f.pulse && !bus.ready && f.prepared == 2);
|
|
setup(&f); init(&f, &bus); f.fail_stop = 1;
|
|
CHECK(ds18b20_ds2480_convert(&bus, NULL) == DS2480_IO);
|
|
CHECK(!f.pulse && !bus.ready && f.prepared == 2);
|
|
setup(&f); init(&f, &bus);
|
|
setup(&other); init(&other, &second);
|
|
CHECK(ds18b20_ds2480_convert(&second, other.rom[1]) == DS2480_OK);
|
|
CHECK(f.convert == 0 && other.convert == 1);
|
|
CHECK(ds18b20_ds2480_temperature(&bus, f.rom[0], &raw) == DS2480_OK && raw == 401);
|
|
CHECK(ds2480_init(&bus, &bus.port, 20) == DS2480_OK);
|
|
f.fail_prepare = 1;
|
|
CHECK(ds2480_init(&bus, &bus.port, 20) == DS2480_IO && !bus.ready);
|
|
CHECK(ds2480_init(&bus, NULL, 20) == DS2480_ARGUMENT);
|
|
puts("DS18B20/DS2480: all host tests passed");
|
|
return 0;
|
|
}
|