/** * @file fake_onewire.c * @brief Симулятор шины 1-Wire с виртуальными датчиками DS18B20. * * Реализует те же пять функций, что и настоящий порт, но работает не с * выводом, а с моделью шины. Этого достаточно, чтобы прогнать на хосте * весь алгоритм SEARCH ROM и обмен с датчиком побитно - именно там * прячутся ошибки, которых не видно в обзоре кода. */ #include "fake_onewire.h" #include #include "onewire.h" /** Состояние разбора команд шины. */ typedef enum { FAKE_IDLE = 0, /**< После сброса, ждём код команды ROM. */ FAKE_SEARCH, /**< Идёт SEARCH ROM. */ FAKE_MATCH, /**< Принимаем 8 байт адреса. */ FAKE_FUNCTION, /**< ROM-команда отработала, ждём функцию. */ FAKE_READ_SCRATCH, /**< Отдаём scratchpad. */ FAKE_WRITE_SCRATCH, /**< Принимаем TH, TL, config. */ FAKE_READ_POWER /**< Отдаём один бит типа питания. */ } fake_state_t; static fake_bus_t *g_bus; static fake_state_t g_state; /* Сборка входящих и исходящих байтов по битам. */ static uint8_t g_in_byte; static uint8_t g_in_bits; static uint8_t g_out_byte; static uint8_t g_out_bits; static uint8_t g_out_index; /* Состояние SEARCH ROM. */ static uint8_t g_search_bit; /**< Номер бита ROM, 0..63. */ static uint8_t g_search_phase; /**< 0 - прямой, 1 - обратный, 2 - выбор. */ static uint8_t g_match_index; static uint8_t g_write_index; /** Активен ли датчик в текущей транзакции. */ static uint8_t g_selected[FAKE_MAX_DEVICES]; void fake_bus_attach(fake_bus_t *bus) { g_bus = bus; g_state = FAKE_IDLE; g_in_byte = 0U; g_in_bits = 0U; g_out_bits = 0U; g_out_index = 0U; bus->reset_count = 0U; bus->convert_count = 0U; bus->copy_count = 0U; bus->delay_us_total = 0U; } uint8_t fake_bus_crc8(const uint8_t *data, uint32_t size) { uint8_t crc = 0U; for (uint32_t i = 0U; i < size; i++) { crc ^= data[i]; for (uint8_t bit = 0U; bit < 8U; bit++) { crc = (uint8_t)((crc & 1U) ? ((crc >> 1) ^ 0x8CU) : (crc >> 1)); } } return crc; } void fake_device_init(fake_device_t *device, const uint8_t rom7[7], int16_t raw, uint8_t parasite) { memcpy(device->rom, rom7, 7U); device->rom[7] = fake_bus_crc8(device->rom, 7U); device->present = 1U; device->parasite = parasite; device->accept_writes = 1U; device->corrupt_crc = 0U; device->scratchpad[0] = (uint8_t)((uint16_t)raw & 0xFFU); device->scratchpad[1] = (uint8_t)(((uint16_t)raw >> 8) & 0xFFU); device->scratchpad[2] = 0x4BU; /* TH по умолчанию */ device->scratchpad[3] = 0x46U; /* TL по умолчанию */ device->scratchpad[4] = 0x7FU; /* 12 бит */ device->scratchpad[5] = 0xFFU; device->scratchpad[6] = 0x0CU; device->scratchpad[7] = 0x10U; device->scratchpad[8] = fake_bus_crc8(device->scratchpad, 8U); } static void refresh_crc(fake_device_t *device) { device->scratchpad[8] = fake_bus_crc8(device->scratchpad, 8U); if (device->corrupt_crc != 0U) { device->scratchpad[8] ^= 0xFFU; } } /** Единственный выбранный датчик либо NULL, если их несколько или ноль. */ static fake_device_t *sole_selected(void) { fake_device_t *found = NULL; for (uint8_t i = 0U; i < g_bus->count; i++) { if ((g_selected[i] != 0U) && (g_bus->devices[i].present != 0U)) { if (found != NULL) { return NULL; } found = &g_bus->devices[i]; } } return found; } /* --- Порт: пять функций, которых ждёт переносимая часть ------------------- */ void OneWire_Init(void) { g_state = FAKE_IDLE; } void OneWire_DelayUs(uint32_t microseconds) { if (g_bus != NULL) { g_bus->delay_us_total += microseconds; } } uint8_t OneWire_Reset(void) { uint8_t presence = 0U; g_state = FAKE_IDLE; g_in_byte = 0U; g_in_bits = 0U; g_out_bits = 0U; g_out_index = 0U; g_search_bit = 0U; g_search_phase = 0U; for (uint8_t i = 0U; i < g_bus->count; i++) { g_selected[i] = g_bus->devices[i].present; if (g_bus->devices[i].present != 0U) { presence = 1U; } } g_bus->reset_count++; return presence; } /** Обрабатывает собранный байт команды. */ static void dispatch_byte(uint8_t value) { switch (g_state) { case FAKE_IDLE: if (value == 0xF0U) { /* SEARCH ROM */ g_state = FAKE_SEARCH; g_search_bit = 0U; g_search_phase = 0U; } else if (value == 0x55U) { /* MATCH ROM */ g_state = FAKE_MATCH; g_match_index = 0U; } else if (value == 0xCCU) { /* SKIP ROM */ g_state = FAKE_FUNCTION; } break; case FAKE_MATCH: for (uint8_t i = 0U; i < g_bus->count; i++) { if (g_bus->devices[i].rom[g_match_index] != value) { g_selected[i] = 0U; } } g_match_index++; if (g_match_index >= 8U) { g_state = FAKE_FUNCTION; } break; case FAKE_FUNCTION: { fake_device_t *device = sole_selected(); if (value == 0x44U) { /* CONVERT T */ g_bus->convert_count++; } else if (value == 0xBEU) { /* READ SCRATCHPAD */ if (device != NULL) { refresh_crc(device); g_state = FAKE_READ_SCRATCH; g_out_index = 0U; g_out_bits = 0U; } } else if (value == 0x4EU) { /* WRITE SCRATCHPAD */ g_state = FAKE_WRITE_SCRATCH; g_write_index = 0U; } else if (value == 0x48U) { /* COPY SCRATCHPAD */ g_bus->copy_count++; } else if (value == 0xB4U) { /* READ POWER SUPPLY */ g_state = FAKE_READ_POWER; } break; } case FAKE_WRITE_SCRATCH: { fake_device_t *device = sole_selected(); if ((device != NULL) && (device->accept_writes != 0U)) { device->scratchpad[2U + g_write_index] = value; } g_write_index++; if (g_write_index >= 3U) { if (device != NULL) { refresh_crc(device); } g_state = FAKE_FUNCTION; } break; } default: break; } } void OneWire_WriteBit(uint8_t bit) { if (g_state == FAKE_SEARCH) { /* Третья фаза SEARCH ROM: мастер сообщает выбранную ветвь. */ for (uint8_t i = 0U; i < g_bus->count; i++) { if (g_selected[i] == 0U) { continue; } uint8_t rom_bit = (uint8_t)((g_bus->devices[i].rom[g_search_bit / 8U] >> (g_search_bit % 8U)) & 1U); if (rom_bit != (bit != 0U)) { g_selected[i] = 0U; } } g_search_bit++; g_search_phase = 0U; if (g_search_bit >= 64U) { g_state = FAKE_FUNCTION; } return; } g_in_byte = (uint8_t)((g_in_byte >> 1) | ((bit != 0U) ? 0x80U : 0U)); g_in_bits++; if (g_in_bits >= 8U) { uint8_t value = g_in_byte; g_in_byte = 0U; g_in_bits = 0U; dispatch_byte(value); } } uint8_t OneWire_ReadBit(void) { if (g_state == FAKE_SEARCH) { uint8_t ones = 0U; uint8_t zeros = 0U; for (uint8_t i = 0U; i < g_bus->count; i++) { if (g_selected[i] == 0U) { continue; } uint8_t rom_bit = (uint8_t)((g_bus->devices[i].rom[g_search_bit / 8U] >> (g_search_bit % 8U)) & 1U); if (rom_bit != 0U) { ones = 1U; } else { zeros = 1U; } } if ((ones == 0U) && (zeros == 0U)) { return 1U; /* никто не отвечает - обе фазы единичны */ } uint8_t result = (g_search_phase == 0U) ? (uint8_t)(zeros == 0U) : (uint8_t)(ones == 0U); g_search_phase = (uint8_t)(g_search_phase + 1U); return result; } if (g_state == FAKE_READ_POWER) { fake_device_t *device = sole_selected(); /* Паразитное питание прижимает шину к нулю. */ return (uint8_t)((device != NULL) && (device->parasite != 0U) ? 0U : 1U); } if (g_state == FAKE_READ_SCRATCH) { fake_device_t *device = sole_selected(); if (device == NULL) { return 1U; } if (g_out_bits == 0U) { g_out_byte = (g_out_index < 9U) ? device->scratchpad[g_out_index] : 0xFFU; } uint8_t bit = (uint8_t)((g_out_byte >> g_out_bits) & 1U); g_out_bits++; if (g_out_bits >= 8U) { g_out_bits = 0U; g_out_index++; } return bit; } return 1U; }