diff --git a/c/ds18b20-ds2480/CMakeLists.txt b/c/ds18b20-ds2480/CMakeLists.txt index 40698a2..1f24308 100644 --- a/c/ds18b20-ds2480/CMakeLists.txt +++ b/c/ds18b20-ds2480/CMakeLists.txt @@ -14,4 +14,9 @@ if(DS18B20_DS2480_BUILD_TESTS) add_executable(test_ds18b20_ds2480 tests/test_ds18b20_ds2480.c) target_link_libraries(test_ds18b20_ds2480 PRIVATE ds18b20_ds2480) add_test(NAME ds18b20_ds2480 COMMAND test_ds18b20_ds2480) + add_executable(test_ds2480_stm32f4 + ports/stm32f4/ds2480_stm32f4_hal.c ports/stm32f4/tests/test_port.c) + target_include_directories(test_ds2480_stm32f4 PRIVATE + . ports/stm32f4 ports/stm32f4/tests) + add_test(NAME ds2480_stm32f4 COMMAND test_ds2480_stm32f4) endif() diff --git a/c/ds18b20-ds2480/README.md b/c/ds18b20-ds2480/README.md index 7d60e68..011e4e6 100644 --- a/c/ds18b20-ds2480/README.md +++ b/c/ds18b20-ds2480/README.md @@ -94,10 +94,10 @@ UART-обмен. EEPROM использует выдержку 12 мс, её со импульс, и остаётся неготовым. При отказе самого порта снятие питания гарантировать невозможно; восстановление UART/моста остаётся задачей приложения. -Используется стандартная скорость 1-Wire и только Command Mode: байт передаётся -восьмью командами Single Bit. Поэтому любые значения, включая `E3`, передаются -без экранирования. Это простая реализация для опроса температуры; повышенные -скорости UART, Overdrive и Search Accelerator пока не реализованы. Параметры +Используется стандартная скорость 1-Wire, UART 9600 бод. Байты передаются в +Data Mode с экранированием E3, reset/поиск/strong pullup — в Command Mode. +Переключения режима выполняет библиотека. Повышенные скорости UART, +Overdrive и Search Accelerator пока не реализованы. Параметры таймингов длинной линии остаются заводскими. Аппаратная проверка обязательна для выбранной топологии/нагрузки. Протокол проверен по документации **DS2480B**; старые ревизии DS2480 без суффикса B отдельно не проверялись. diff --git a/c/ds18b20-ds2480/ds2480.c b/c/ds18b20-ds2480/ds2480.c index 3082662..8d1a878 100644 --- a/c/ds18b20-ds2480/ds2480.c +++ b/c/ds18b20-ds2480/ds2480.c @@ -8,9 +8,15 @@ static ds2480_status fault(ds2480 *bus, ds2480_status status) static ds2480_status exchange(ds2480 *bus, uint8_t command, uint8_t *reply) { + uint8_t mode = 0xE3; if (!bus || !reply) return DS2480_ARGUMENT; if (!bus->ready) return DS2480_NOT_READY; if (bus->power_active && command != 0xF1) return DS2480_BUSY; + if (bus->data_mode) { + if (bus->port.write(bus->port.user, &mode, 1, bus->timeout_ms)) + return fault(bus, DS2480_IO); + bus->data_mode = 0; + } 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); @@ -31,6 +37,7 @@ ds2480_status ds2480_init(ds2480 *bus, const ds2480_port *port, uint32_t timeout copy = *port; /* Also permit reinitialization with &bus->port. */ bus->ready = 0; bus->power_active = 0; + bus->data_mode = 0; bus->port = copy; bus->timeout_ms = timeout_ms; if (copy.prepare(copy.user) || @@ -79,15 +86,22 @@ ds2480_status ds2480_bit(ds2480 *bus, uint8_t bit, uint8_t *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); + uint8_t mode = 0xE1, reply; + if (!bus || !received) return DS2480_ARGUMENT; + if (!bus->ready) return DS2480_NOT_READY; + if (bus->power_active) return DS2480_BUSY; + if (!bus->data_mode) { + if (bus->port.write(bus->port.user, &mode, 1, bus->timeout_ms)) + return fault(bus, DS2480_IO); + bus->data_mode = 1; } - *received = result; + if (bus->port.write(bus->port.user, &value, 1, bus->timeout_ms)) + return fault(bus, DS2480_IO); + if (value == 0xE3 && bus->port.write(bus->port.user, &value, 1, bus->timeout_ms)) + return fault(bus, DS2480_IO); + if (bus->port.read(bus->port.user, &reply, 1, bus->timeout_ms)) + return fault(bus, DS2480_IO); + *received = reply; return DS2480_OK; } diff --git a/c/ds18b20-ds2480/ds2480.h b/c/ds18b20-ds2480/ds2480.h index a17d82f..e4c2e97 100644 --- a/c/ds18b20-ds2480/ds2480.h +++ b/c/ds18b20-ds2480/ds2480.h @@ -34,6 +34,7 @@ typedef struct { uint32_t timeout_ms; uint8_t ready; uint8_t power_active; + uint8_t data_mode; } ds2480; /** Independent ROM search cursor. Zero-initialize before each enumeration. */ @@ -51,7 +52,7 @@ ds2480_status ds2480_init(ds2480 *bus, const ds2480_port *port, uint32_t timeout ds2480_status ds2480_reset(ds2480 *bus); /** Exchange one slot (write 1 to read); received must be non-NULL. */ ds2480_status ds2480_bit(ds2480 *bus, uint8_t bit, uint8_t *received); -/** Exchange a byte, LSB first, in command mode (eight UART transactions). */ +/** Exchange a byte in Data Mode; escapes E3 and preserves one reply per byte. */ ds2480_status ds2480_byte(ds2480 *bus, uint8_t value, uint8_t *received); /** Write a byte and start strong pullup immediately after its last slot. * Blocks for hold_ms (1..1000), then terminates the pulse and consumes reply. diff --git a/c/ds18b20-ds2480/ports/stm32f4/tests/stm32f4xx_hal.h b/c/ds18b20-ds2480/ports/stm32f4/tests/stm32f4xx_hal.h new file mode 100644 index 0000000..fa40fc6 --- /dev/null +++ b/c/ds18b20-ds2480/ports/stm32f4/tests/stm32f4xx_hal.h @@ -0,0 +1,39 @@ +/* Host-only HAL model, never add this directory to a firmware include path. */ +#ifndef TEST_STM32F4_HAL_H +#define TEST_STM32F4_HAL_H +#include +typedef struct { uint32_t SR, DR, CR1; } USART_TypeDef; +typedef struct { uint32_t pin, level, mode; } GPIO_TypeDef; +typedef struct { uint32_t BaudRate,WordLength,StopBits,Parity,Mode,HwFlowCtl,OverSampling; } UART_InitTypeDef; +typedef struct { USART_TypeDef *Instance; UART_InitTypeDef Init; } UART_HandleTypeDef; +typedef struct { uint32_t Pin, Mode, Pull, Speed, Alternate; } GPIO_InitTypeDef; +typedef enum { HAL_OK, HAL_ERROR } HAL_StatusTypeDef; +#define USART_SR_ORE 8U +#define USART_SR_NE 4U +#define USART_SR_FE 2U +#define USART_SR_PE 1U +#define USART_SR_RXNE 32U +#define GPIO_PIN_RESET 0U +#define GPIO_PIN_SET 1U +#define GPIO_MODE_OUTPUT_PP 1U +#define GPIO_MODE_AF_PP 2U +#define GPIO_NOPULL 0U +#define GPIO_SPEED_FREQ_HIGH 3U +#define UART_WORDLENGTH_8B 0U +#define UART_STOPBITS_1 0U +#define UART_PARITY_NONE 0U +#define UART_MODE_TX_RX 12U +#define UART_HWCONTROL_NONE 0U +#define UART_OVERSAMPLING_16 0U +#define HAL_MAX_DELAY UINT32_MAX +#define __HAL_UART_DISABLE(u) ((u)->Instance->CR1=0) +void test_clear(UART_HandleTypeDef *u); +#define __HAL_UART_CLEAR_OREFLAG(u) test_clear(u) +HAL_StatusTypeDef HAL_UART_Abort(UART_HandleTypeDef *u); +HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *u); +HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *u,const uint8_t *p,uint16_t n,uint32_t timeout); +void HAL_GPIO_WritePin(GPIO_TypeDef *g,uint16_t pin,uint32_t level); +void HAL_GPIO_Init(GPIO_TypeDef *g,const GPIO_InitTypeDef *i); +void HAL_Delay(uint32_t ms); +uint32_t HAL_GetTick(void); +#endif diff --git a/c/ds18b20-ds2480/ports/stm32f4/tests/test_port.c b/c/ds18b20-ds2480/ports/stm32f4/tests/test_port.c new file mode 100644 index 0000000..b6562b0 --- /dev/null +++ b/c/ds18b20-ds2480/ports/stm32f4/tests/test_port.c @@ -0,0 +1,41 @@ +#include "ds2480_stm32f4_hal.h" +#include +#include + +static uint32_t now, low_ms, high_ms, clears; +static GPIO_TypeDef gpio; +static unsigned fail_init, fail_tx; +void test_clear(UART_HandleTypeDef *u){u->Instance->SR=0;++clears;} +HAL_StatusTypeDef HAL_UART_Abort(UART_HandleTypeDef *u){(void)u;return HAL_OK;} +HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *u) +{assert(gpio.mode==GPIO_MODE_AF_PP && gpio.level==1);assert(u->Init.BaudRate==9600);return fail_init?HAL_ERROR:HAL_OK;} +HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *u,const uint8_t *p,uint16_t n,uint32_t timeout) +{assert(n==1 && timeout==20);u->Instance->DR=*p;u->Instance->SR=USART_SR_RXNE;return fail_tx?HAL_ERROR:HAL_OK;} +void HAL_GPIO_WritePin(GPIO_TypeDef *g,uint16_t pin,uint32_t level){g->pin=pin;g->level=level;} +void HAL_GPIO_Init(GPIO_TypeDef *g,const GPIO_InitTypeDef *i){assert(i->Pin==64);g->mode=i->Mode;} +void HAL_Delay(uint32_t ms){now+=ms;if(gpio.level)high_ms+=ms;else low_ms+=ms;} +uint32_t HAL_GetTick(void){return now++;} + +int main(void) +{ + USART_TypeDef regs={0};UART_HandleTypeDef uart={0}; + ds2480_stm32f4_hal ctx={&uart,&gpio,64,8};ds2480_port port; + uint8_t byte=0xCD,rx=0; + uart.Instance=®s; + assert(ds2480_stm32f4_hal_bind(&ctx,&port)==DS2480_OK); + assert(port.prepare(port.user)==0 && low_ms>=2 && high_ms>=2 && clears==1); + assert(port.write(port.user,&byte,1,20)==0); + assert(regs.SR & USART_SR_RXNE); /* Early reply must survive TX completion. */ + assert(port.read(port.user,&rx,1,20)==0 && rx==byte && clears==1); + regs.SR=USART_SR_RXNE|USART_SR_FE;rx=0xA5; + assert(port.read(port.user,&rx,1,20)!=0 && rx==0xA5 && clears==2); + regs.SR=USART_SR_ORE;assert(port.write(port.user,&byte,1,20)!=0); + regs.SR=0;now=UINT32_MAX-5; + assert(port.read(port.user,&rx,1,20)!=0); /* Wrap-safe bounded timeout. */ + assert(port.write(port.user,&byte,2,20)!=0); + assert(port.read(port.user,&rx,1,HAL_MAX_DELAY)!=0); + fail_tx=1;assert(port.write(port.user,&byte,1,20)!=0); + fail_init=1;assert(port.prepare(port.user)!=0 && gpio.level==1); + ctx.tx_pin=3;assert(ds2480_stm32f4_hal_bind(&ctx,&port)==DS2480_ARGUMENT); + puts("STM32F4 UART port: OK");return 0; +} diff --git a/c/ds18b20-ds2480/tests/test_ds18b20_ds2480.c b/c/ds18b20-ds2480/tests/test_ds18b20_ds2480.c index 5c1777b..efc5eec 100644 --- a/c/ds18b20-ds2480/tests/test_ds18b20_ds2480.c +++ b/c/ds18b20-ds2480/tests/test_ds18b20_ds2480.c @@ -12,6 +12,7 @@ typedef struct { 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; + unsigned data_mode, escape; } fake; static void crc_scratch(fake *f) { f->scratch[8] = ds2480_crc8(f->scratch, 8); } @@ -91,6 +92,7 @@ static int prepare(void *user) fake *f = user; ++f->prepared; f->pending = f->pulse = f->calibrated = 0; + f->data_mode = f->escape = 0; return (int)f->fail_prepare; } @@ -101,6 +103,17 @@ static int transmit(void *user, const uint8_t *data, uint32_t size, uint32_t tim CHECK(size == 1 && timeout == 20 && !f->pending); if (f->fail_write) return -1; if (!f->calibrated) { CHECK(command == 0xC1); f->calibrated = 1; return 0; } + if (f->data_mode) { + unsigned i, reply = 0; + if (command == 0xE3 && !f->escape) { f->escape=1; return 0; } + if (!f->escape || command == 0xE3) { + f->escape=0; + for (i=0; i<8; ++i) reply |= (unsigned)wire_bit(f,(uint8_t)((command>>i)&1)) << i; + f->reply=reply; f->pending=1; return 0; + } + f->escape=0; f->data_mode=0; + } + if (command == 0xE1) { f->data_mode=1; return 0; } CHECK(!f->pulse || command == 0xF1); if (command == 0x3F) f->reply = 0x3E; else if (command == 0xC1) { @@ -188,6 +201,8 @@ int main(void) CHECK(raw == (i == 9 ? -168 : i == 10 ? -164 : i == 11 ? -162 : -161)); } CHECK(f.copy == 4); + CHECK(ds18b20_ds2480_configure(&bus, f.rom[0], (int8_t)-29, 2, 12, 0) == DS2480_OK); + CHECK(f.scratch[2] == 0xE3); /* Escaped byte followed by an ordinary byte. */ 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;