diff --git a/c/ds18b20-ds2480/README.md b/c/ds18b20-ds2480/README.md index 524d2b6..7d60e68 100644 --- a/c/ds18b20-ds2480/README.md +++ b/c/ds18b20-ds2480/README.md @@ -105,8 +105,7 @@ UART-обмен. EEPROM использует выдержку 12 мс, её со ## Подключение и тесты Добавьте `ds2480.c`, `ds18b20_ds2480.c` и путь к заголовкам в сборку прошивки. -Номер UART, GPIO и библиотеку платформы выбирает приложение. Готовый порт -для конкретной платы пока не входит в библиотеку. +Номер UART, GPIO и библиотеку платформы выбирает приложение. Порт [STM32F407 / STM32F4 HAL](ports/stm32f4/README.md) входит в библиотеку. ```cmake set(DS18B20_DS2480_BUILD_TESTS OFF CACHE BOOL "" FORCE) @@ -137,7 +136,8 @@ clang -std=c99 -Wall -Wextra -Wpedantic -Werror -I . ds2480.c ds18b20_ds2480.c t ## Использование Добавлена в сабмодуль `templates` проекта `john103C6T6NewVer`, ветка `ds2480`. -В рабочую прошивку ещё не включена: для этого требуется порт выбранного UART. +Подключена к опросу climate через USART6 PC6/PC7; администратор выбирает GPIO или DS2480. +Ожидание преобразования в climate неблокирующее: `ds2480_power_begin/end`. ## Источники diff --git a/c/ds18b20-ds2480/ds2480.c b/c/ds18b20-ds2480/ds2480.c index 8026083..3082662 100644 --- a/c/ds18b20-ds2480/ds2480.c +++ b/c/ds18b20-ds2480/ds2480.c @@ -10,6 +10,7 @@ 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->power_active && command != 0xF1) return DS2480_BUSY; 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); @@ -29,6 +30,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->port = copy; bus->timeout_ms = timeout_ms; if (copy.prepare(copy.user) || @@ -89,11 +91,11 @@ ds2480_status ds2480_byte(ds2480 *bus, uint8_t value, uint8_t *received) return DS2480_OK; } -ds2480_status ds2480_power_byte(ds2480 *bus, uint8_t value, uint32_t hold_ms) +ds2480_status ds2480_power_begin(ds2480 *bus, uint8_t value) { - uint8_t i, bit, reply, echoed = 0; + uint8_t i, bit, echoed = 0; ds2480_status status; - if (!bus || !hold_ms || hold_ms > 1000) return DS2480_ARGUMENT; + if (!bus) 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) { @@ -103,15 +105,39 @@ ds2480_status ds2480_power_byte(ds2480 *bus, uint8_t value, uint32_t hold_ms) } echoed |= (uint8_t)(bit << i); } - bus->port.delay_ms(bus->port.user, hold_ms); + bus->power_active = 1; + if (echoed != value) { + status = ds2480_power_end(bus); + return status == DS2480_OK ? DS2480_DATA : status; + } + return DS2480_OK; +} + +ds2480_status ds2480_power_end(ds2480 *bus) +{ + uint8_t reply; + ds2480_status status; + if (!bus) return DS2480_ARGUMENT; + if (!bus->ready) return DS2480_NOT_READY; + if (!bus->power_active) return DS2480_OK; 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; + bus->power_active = 0; return status; } +ds2480_status ds2480_power_byte(ds2480 *bus, uint8_t value, uint32_t hold_ms) +{ + ds2480_status status; + if (!hold_ms || hold_ms > 1000) return DS2480_ARGUMENT; + status = ds2480_power_begin(bus, value); + if (status != DS2480_OK) return status; + bus->port.delay_ms(bus->port.user, hold_ms); + return ds2480_power_end(bus); +} + uint8_t ds2480_crc8(const uint8_t *data, uint32_t size) { uint8_t crc = 0, bit; diff --git a/c/ds18b20-ds2480/ds2480.h b/c/ds18b20-ds2480/ds2480.h index f686049..a17d82f 100644 --- a/c/ds18b20-ds2480/ds2480.h +++ b/c/ds18b20-ds2480/ds2480.h @@ -10,7 +10,7 @@ extern "C" { typedef enum { DS2480_OK = 0, DS2480_DONE, DS2480_NO_PRESENCE, DS2480_SHORT, DS2480_IO, DS2480_PROTOCOL, DS2480_CRC, DS2480_ARGUMENT, - DS2480_NOT_READY, DS2480_DATA + DS2480_NOT_READY, DS2480_DATA, DS2480_BUSY } ds2480_status; /** Blocking UART callbacks: 0 = success, nonzero = error/timeout. @@ -33,6 +33,7 @@ typedef struct { ds2480_port port; uint32_t timeout_ms; uint8_t ready; + uint8_t power_active; } ds2480; /** Independent ROM search cursor. Zero-initialize before each enumeration. */ @@ -56,6 +57,14 @@ ds2480_status ds2480_byte(ds2480 *bus, uint8_t value, uint8_t *received); * Blocks for hold_ms (1..1000), then terminates the pulse and consumes reply. */ ds2480_status ds2480_power_byte(ds2480 *bus, uint8_t value, uint32_t hold_ms); +/** Start indefinite strong pullup after the final bit; returns immediately + * after UART exchange. Until power_end, other bus operations return BUSY. + * Application MUST call power_end after the sensor's required hold time, + * or init to cancel/recover. No internal timer/interrupt releases the pulse. + */ +ds2480_status ds2480_power_begin(ds2480 *bus, uint8_t value); +/** Release strong pullup, consume its reply. Idempotent when ready/idle. */ +ds2480_status ds2480_power_end(ds2480 *bus); /** CRC8 Dallas/Maxim. data must address size bytes (NULL allowed for size=0). */ uint8_t ds2480_crc8(const uint8_t *data, uint32_t size); /** Search ALL families. OK yields ROM with valid CRC; DONE ends enumeration. diff --git a/c/ds18b20-ds2480/ports/stm32f4/README.md b/c/ds18b20-ds2480/ports/stm32f4/README.md new file mode 100644 index 0000000..17c97dc --- /dev/null +++ b/c/ds18b20-ds2480/ports/stm32f4/README.md @@ -0,0 +1,52 @@ +# STM32F407 / STM32F4 HAL + +Порт UART для `ds2480.c`. Платформа настраивает тактирование и GPIO, порт +формирует BREAK через TX GPIO, восстанавливает UART 9600 8N1 и проверяет +ошибки приёма. DMA и обработчики UART-прерываний не нужны. HAL tick должен +работать; вызывать из прерывания или при запрещённых прерываниях нельзя. + +```c +ds2480 bridge; +ds2480_port io; +ds2480_stm32f4_hal platform = {&huart6, GPIOC, GPIO_PIN_6, GPIO_AF8_USART6}; +/* Before this point: enable GPIOC/USART6 clocks, configure PC6/PC7 as AF8. */ +if (ds2480_stm32f4_hal_bind(&platform, &io) != DS2480_OK) return; +if (ds2480_init(&bridge, &io, 20) != DS2480_OK) return; +``` + +В сборку добавляются `ds2480_stm32f4_hal.c`, ядро библиотеки, путь к этому +каталогу и STM32F4 HAL/CMSIS. UART выделяется только для моста. Порт принимает +по одному байту за операцию: ответ сохраняется в DR во время завершения TX, +затем читается без сброса RX. FE/NE/ORE/PE означают ошибку обмена даже при RXNE. + +## Подключение в climate F407VET6 + +| STM32 / питание | DS2480B | +|---|---| +| PC6, USART6_TX | TXD, вывод 7 (вход моста) | +| PC7, USART6_RX | RXD, вывод 8 (выход моста) | +| Общая земля | GND, вывод 1 | +| +5 В | VDD, вывод 4; VPP, вывод 5; POL, вывод 6 | +| DQ датчиков DS18B20 | 1-W, вывод 2 | + +DS2480B работает от 5 В. Проверьте согласование логических уровней по +электрическим характеристикам конкретной платы/модуля; не считайте питание +DS2480B от 3,3 В допустимым. Для внешнего питания DS18B20 подключите VDD; +при паразитном питании VDD датчика соединяется с GND. В обоих случаях общий GND. + +PC6/PC7 выбраны в `climate` (AF8). UART1/2 используются Modbus, SDIO использует +4-битную шину PC8..PC12/PD2. Пины для другой платы задаются её приложением. + +## Неблокирующее питание датчиков + +`ds2480_power_begin` посылает команду датчика с strong pullup на последнем +слоте. Приложение возвращается в главный цикл, отсчитывает 750 мс для +преобразования либо минимум 10 мс для EEPROM, затем вызывает +`ds2480_power_end`. До завершения импульса остальной обмен возвращает BUSY. +При отмене вызывайте `power_end`, при потере синхронизации — `ds2480_init`. + +Используется в `climate_control_f407vet6_f4`: `ds2480_app.c` связывает порт +из сабмодуля с существующим каталогом датчиков и диагностикой Modbus. + +Источники: [STM32F407, таблица alternate functions](https://www.st.com/resource/en/datasheet/stm32f407ve.pdf), +[DS2480B, выводы и UART](https://www.analog.com/media/en/technical-documentation/data-sheets/ds2480b.pdf). diff --git a/c/ds18b20-ds2480/ports/stm32f4/ds2480_stm32f4_hal.c b/c/ds18b20-ds2480/ports/stm32f4/ds2480_stm32f4_hal.c new file mode 100644 index 0000000..70019be --- /dev/null +++ b/c/ds18b20-ds2480/ports/stm32f4/ds2480_stm32f4_hal.c @@ -0,0 +1,85 @@ +#include "ds2480_stm32f4_hal.h" + +#define RX_ERRORS (USART_SR_ORE | USART_SR_NE | USART_SR_FE | USART_SR_PE) + +static int prepare(void *user) +{ + ds2480_stm32f4_hal *p = (ds2480_stm32f4_hal *)user; + GPIO_InitTypeDef gpio = {0}; + /* Abort resets HAL states after timeouts and disables UART IRQ/DMA. */ + if (HAL_UART_Abort(p->uart) != HAL_OK) return -1; + __HAL_UART_DISABLE(p->uart); + HAL_GPIO_WritePin(p->tx_port, p->tx_pin, GPIO_PIN_RESET); + gpio.Pin = p->tx_pin; + gpio.Mode = GPIO_MODE_OUTPUT_PP; + gpio.Pull = GPIO_NOPULL; + gpio.Speed = GPIO_SPEED_FREQ_HIGH; + HAL_GPIO_Init(p->tx_port, &gpio); + HAL_Delay(2); + HAL_GPIO_WritePin(p->tx_port, p->tx_pin, GPIO_PIN_SET); + HAL_Delay(2); + gpio.Mode = GPIO_MODE_AF_PP; + gpio.Alternate = p->tx_alternate; + HAL_GPIO_Init(p->tx_port, &gpio); + p->uart->Init.BaudRate = 9600; + p->uart->Init.WordLength = UART_WORDLENGTH_8B; + p->uart->Init.StopBits = UART_STOPBITS_1; + p->uart->Init.Parity = UART_PARITY_NONE; + p->uart->Init.Mode = UART_MODE_TX_RX; + p->uart->Init.HwFlowCtl = UART_HWCONTROL_NONE; + p->uart->Init.OverSampling = UART_OVERSAMPLING_16; + if (HAL_UART_Init(p->uart) != HAL_OK) return -1; + /* SR then DR: discard BREAK echo/stale RX and clear FE/NE/ORE/PE. */ + __HAL_UART_CLEAR_OREFLAG(p->uart); + return 0; +} + +static int write_byte(void *user, const uint8_t *data, uint32_t size, uint32_t timeout) +{ + ds2480_stm32f4_hal *p = (ds2480_stm32f4_hal *)user; + if (!data || size != 1 || !timeout || timeout == HAL_MAX_DELAY) return -1; + /* One reply fits in DR while HAL waits for TX complete. Never flush RX + * here: a reply may already be present when HAL_UART_Transmit returns. */ + if (p->uart->Instance->SR & RX_ERRORS) return -1; + return HAL_UART_Transmit(p->uart, data, 1, timeout) == HAL_OK ? 0 : -1; +} + +static int read_byte(void *user, uint8_t *data, uint32_t size, uint32_t timeout) +{ + ds2480_stm32f4_hal *p = (ds2480_stm32f4_hal *)user; + uint32_t start, flags; + if (!data || size != 1 || !timeout || timeout == HAL_MAX_DELAY) return -1; + start = HAL_GetTick(); + for (;;) { + flags = p->uart->Instance->SR; + /* Test errors BEFORE reading DR, including when RXNE is already set. */ + if (flags & RX_ERRORS) { + __HAL_UART_CLEAR_OREFLAG(p->uart); + return -1; + } + if (flags & USART_SR_RXNE) { + *data = (uint8_t)p->uart->Instance->DR; + return 0; + } + if ((uint32_t)(HAL_GetTick() - start) >= timeout) return -1; + } +} + +static void delay_ms(void *user, uint32_t ms) +{ + (void)user; + HAL_Delay(ms); +} + +ds2480_status ds2480_stm32f4_hal_bind(ds2480_stm32f4_hal *p, ds2480_port *port) +{ + if (!p || !port || !p->uart || !p->uart->Instance || !p->tx_port || + !p->tx_pin || (p->tx_pin & (p->tx_pin - 1U)) || p->tx_alternate > 15U) + return DS2480_ARGUMENT; + port->user = p; + port->prepare = prepare; + port->write = write_byte; + port->read = read_byte; + port->delay_ms = delay_ms; + return DS2480_OK; +} diff --git a/c/ds18b20-ds2480/ports/stm32f4/ds2480_stm32f4_hal.h b/c/ds18b20-ds2480/ports/stm32f4/ds2480_stm32f4_hal.h new file mode 100644 index 0000000..0830cc6 --- /dev/null +++ b/c/ds18b20-ds2480/ports/stm32f4/ds2480_stm32f4_hal.h @@ -0,0 +1,32 @@ +#ifndef DS2480_STM32F4_HAL_H +#define DS2480_STM32F4_HAL_H + +#include "ds2480.h" +#include "stm32f4xx_hal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Dedicated UART initialized by the board (clock, RX/TX AF, no IRQ/DMA). + * tx_port/pin/alternate let prepare generate a real >=2 ms BREAK, including + * recovery from a lost strong-pullup response. No reset pin is required. + * GPIO clock must stay enabled. UART must not be used by any other service. + */ +typedef struct { + UART_HandleTypeDef *uart; + GPIO_TypeDef *tx_port; + uint16_t tx_pin; + uint32_t tx_alternate; +} ds2480_stm32f4_hal; + +/** Populate callbacks only; call ds2480_init afterwards to reset/calibrate. + * Thread/main-loop use only, with running HAL tick and interrupts enabled. + * UART baud rate is fixed at 9600 8N1; all transfers are single-byte. + */ +ds2480_status ds2480_stm32f4_hal_bind(ds2480_stm32f4_hal *context, ds2480_port *port); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/c/ds18b20-ds2480/tests/test_ds18b20_ds2480.c b/c/ds18b20-ds2480/tests/test_ds18b20_ds2480.c index 84b1234..5c1777b 100644 --- a/c/ds18b20-ds2480/tests/test_ds18b20_ds2480.c +++ b/c/ds18b20-ds2480/tests/test_ds18b20_ds2480.c @@ -168,6 +168,16 @@ int main(void) 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(ds2480_reset(&bus) == DS2480_OK); + CHECK(ds2480_byte(&bus, 0xCC, &bit) == DS2480_OK); + CHECK(ds2480_power_begin(&bus, 0x44) == DS2480_OK); + CHECK(f.hold == 0 && f.pulse && bus.power_active); + CHECK(ds2480_reset(&bus) == DS2480_BUSY); + CHECK(ds2480_byte(&bus, 0xFF, &bit) == DS2480_BUSY); + CHECK(ds2480_power_begin(&bus, 0x44) == DS2480_BUSY); + delay(&f, 750); + CHECK(ds2480_power_end(&bus) == DS2480_OK && !bus.power_active); + CHECK(ds2480_power_end(&bus) == DS2480_OK); 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) {