Files
templates/c/ds18b20/tests/fake_onewire.c
Andrey Kruchinkin 873ac438f3 feat(ds18b20): термометры DS18B20 поверх программной 1-Wire
Перенесён из репозитория ds18b20, который подключался сабмодулем в
KONOR_ds18b20. История библиотеки осталась там; сюда пришло состояние
на момент переезда.

Ядро на C99 без stm32f10x.h и динамической памяти. Порт — пять функций
(Init, DelayUs, Reset, WriteBit, ReadBit); реализация для STM32F1 идёт
в комплекте в ports/stm32f1. Хостовые тесты на модели шины с виртуальными
датчиками проходят: tests/test_ds18b20.c.
2026-08-23 01:15:34 +03:00

306 lines
9.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file fake_onewire.c
* @brief Симулятор шины 1-Wire с виртуальными датчиками DS18B20.
*
* Реализует те же пять функций, что и настоящий порт, но работает не с
* выводом, а с моделью шины. Этого достаточно, чтобы прогнать на хосте
* весь алгоритм SEARCH ROM и обмен с датчиком побитно - именно там
* прячутся ошибки, которых не видно в обзоре кода.
*/
#include "fake_onewire.h"
#include <string.h>
#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;
}