Compare commits
2 Commits
df02070cc4
...
def3eb08f3
| Author | SHA1 | Date | |
|---|---|---|---|
| def3eb08f3 | |||
| 3020de7e0c |
@@ -14,4 +14,9 @@ if(DS18B20_DS2480_BUILD_TESTS)
|
|||||||
add_executable(test_ds18b20_ds2480 tests/test_ds18b20_ds2480.c)
|
add_executable(test_ds18b20_ds2480 tests/test_ds18b20_ds2480.c)
|
||||||
target_link_libraries(test_ds18b20_ds2480 PRIVATE ds18b20_ds2480)
|
target_link_libraries(test_ds18b20_ds2480 PRIVATE ds18b20_ds2480)
|
||||||
add_test(NAME ds18b20_ds2480 COMMAND test_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()
|
endif()
|
||||||
|
|||||||
@@ -94,10 +94,10 @@ UART-обмен. EEPROM использует выдержку 12 мс, её со
|
|||||||
импульс, и остаётся неготовым. При отказе самого порта снятие питания гарантировать
|
импульс, и остаётся неготовым. При отказе самого порта снятие питания гарантировать
|
||||||
невозможно; восстановление UART/моста остаётся задачей приложения.
|
невозможно; восстановление UART/моста остаётся задачей приложения.
|
||||||
|
|
||||||
Используется стандартная скорость 1-Wire и только Command Mode: байт передаётся
|
Используется стандартная скорость 1-Wire, UART 9600 бод. Байты передаются в
|
||||||
восьмью командами Single Bit. Поэтому любые значения, включая `E3`, передаются
|
Data Mode с экранированием E3, reset/поиск/strong pullup — в Command Mode.
|
||||||
без экранирования. Это простая реализация для опроса температуры; повышенные
|
Переключения режима выполняет библиотека. Повышенные скорости UART,
|
||||||
скорости UART, Overdrive и Search Accelerator пока не реализованы. Параметры
|
Overdrive и Search Accelerator пока не реализованы. Параметры
|
||||||
таймингов длинной линии остаются заводскими. Аппаратная проверка обязательна
|
таймингов длинной линии остаются заводскими. Аппаратная проверка обязательна
|
||||||
для выбранной топологии/нагрузки. Протокол проверен по документации **DS2480B**;
|
для выбранной топологии/нагрузки. Протокол проверен по документации **DS2480B**;
|
||||||
старые ревизии DS2480 без суффикса B отдельно не проверялись.
|
старые ревизии DS2480 без суффикса B отдельно не проверялись.
|
||||||
@@ -105,8 +105,7 @@ UART-обмен. EEPROM использует выдержку 12 мс, её со
|
|||||||
## Подключение и тесты
|
## Подключение и тесты
|
||||||
|
|
||||||
Добавьте `ds2480.c`, `ds18b20_ds2480.c` и путь к заголовкам в сборку прошивки.
|
Добавьте `ds2480.c`, `ds18b20_ds2480.c` и путь к заголовкам в сборку прошивки.
|
||||||
Номер UART, GPIO и библиотеку платформы выбирает приложение. Готовый порт
|
Номер UART, GPIO и библиотеку платформы выбирает приложение. Порт [STM32F407 / STM32F4 HAL](ports/stm32f4/README.md) входит в библиотеку.
|
||||||
для конкретной платы пока не входит в библиотеку.
|
|
||||||
|
|
||||||
```cmake
|
```cmake
|
||||||
set(DS18B20_DS2480_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
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`.
|
Добавлена в сабмодуль `templates` проекта `john103C6T6NewVer`, ветка `ds2480`.
|
||||||
В рабочую прошивку ещё не включена: для этого требуется порт выбранного UART.
|
Подключена к опросу climate через USART6 PC6/PC7; администратор выбирает GPIO или DS2480.
|
||||||
|
Ожидание преобразования в climate неблокирующее: `ds2480_power_begin/end`.
|
||||||
|
|
||||||
## Источники
|
## Источники
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,15 @@ static ds2480_status fault(ds2480 *bus, ds2480_status status)
|
|||||||
|
|
||||||
static ds2480_status exchange(ds2480 *bus, uint8_t command, uint8_t *reply)
|
static ds2480_status exchange(ds2480 *bus, uint8_t command, uint8_t *reply)
|
||||||
{
|
{
|
||||||
|
uint8_t mode = 0xE3;
|
||||||
if (!bus || !reply) return DS2480_ARGUMENT;
|
if (!bus || !reply) return DS2480_ARGUMENT;
|
||||||
if (!bus->ready) return DS2480_NOT_READY;
|
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) ||
|
if (bus->port.write(bus->port.user, &command, 1, bus->timeout_ms) ||
|
||||||
bus->port.read(bus->port.user, reply, 1, bus->timeout_ms))
|
bus->port.read(bus->port.user, reply, 1, bus->timeout_ms))
|
||||||
return fault(bus, DS2480_IO);
|
return fault(bus, DS2480_IO);
|
||||||
@@ -29,6 +36,8 @@ ds2480_status ds2480_init(ds2480 *bus, const ds2480_port *port, uint32_t timeout
|
|||||||
}
|
}
|
||||||
copy = *port; /* Also permit reinitialization with &bus->port. */
|
copy = *port; /* Also permit reinitialization with &bus->port. */
|
||||||
bus->ready = 0;
|
bus->ready = 0;
|
||||||
|
bus->power_active = 0;
|
||||||
|
bus->data_mode = 0;
|
||||||
bus->port = copy;
|
bus->port = copy;
|
||||||
bus->timeout_ms = timeout_ms;
|
bus->timeout_ms = timeout_ms;
|
||||||
if (copy.prepare(copy.user) ||
|
if (copy.prepare(copy.user) ||
|
||||||
@@ -77,23 +86,30 @@ ds2480_status ds2480_bit(ds2480 *bus, uint8_t bit, uint8_t *received)
|
|||||||
|
|
||||||
ds2480_status ds2480_byte(ds2480 *bus, uint8_t value, uint8_t *received)
|
ds2480_status ds2480_byte(ds2480 *bus, uint8_t value, uint8_t *received)
|
||||||
{
|
{
|
||||||
uint8_t i, bit, result = 0;
|
uint8_t mode = 0xE1, reply;
|
||||||
ds2480_status status;
|
if (!bus || !received) return DS2480_ARGUMENT;
|
||||||
if (!received) return DS2480_ARGUMENT;
|
if (!bus->ready) return DS2480_NOT_READY;
|
||||||
for (i = 0; i < 8; ++i) {
|
if (bus->power_active) return DS2480_BUSY;
|
||||||
status = ds2480_bit(bus, (uint8_t)((value >> i) & 1), &bit);
|
if (!bus->data_mode) {
|
||||||
if (status != DS2480_OK) return status;
|
if (bus->port.write(bus->port.user, &mode, 1, bus->timeout_ms))
|
||||||
result |= (uint8_t)(bit << i);
|
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;
|
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;
|
ds2480_status status;
|
||||||
if (!bus || !hold_ms || hold_ms > 1000) return DS2480_ARGUMENT;
|
if (!bus) return DS2480_ARGUMENT;
|
||||||
for (i = 0; i < 8; ++i) {
|
for (i = 0; i < 8; ++i) {
|
||||||
status = slot(bus, (uint8_t)((value >> i) & 1), (uint8_t)(i == 7), &bit);
|
status = slot(bus, (uint8_t)((value >> i) & 1), (uint8_t)(i == 7), &bit);
|
||||||
if (status != DS2480_OK) {
|
if (status != DS2480_OK) {
|
||||||
@@ -103,15 +119,39 @@ ds2480_status ds2480_power_byte(ds2480 *bus, uint8_t value, uint32_t hold_ms)
|
|||||||
}
|
}
|
||||||
echoed |= (uint8_t)(bit << i);
|
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);
|
status = exchange(bus, 0xF1, &reply);
|
||||||
if (status == DS2480_OK && reply != 0xEC && reply != 0xEF)
|
if (status == DS2480_OK && reply != 0xEC && reply != 0xEF)
|
||||||
status = fault(bus, DS2480_PROTOCOL);
|
status = fault(bus, DS2480_PROTOCOL);
|
||||||
if (status != DS2480_OK) (void)bus->port.prepare(bus->port.user);
|
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;
|
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 ds2480_crc8(const uint8_t *data, uint32_t size)
|
||||||
{
|
{
|
||||||
uint8_t crc = 0, bit;
|
uint8_t crc = 0, bit;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ extern "C" {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
DS2480_OK = 0, DS2480_DONE, DS2480_NO_PRESENCE, DS2480_SHORT,
|
DS2480_OK = 0, DS2480_DONE, DS2480_NO_PRESENCE, DS2480_SHORT,
|
||||||
DS2480_IO, DS2480_PROTOCOL, DS2480_CRC, DS2480_ARGUMENT,
|
DS2480_IO, DS2480_PROTOCOL, DS2480_CRC, DS2480_ARGUMENT,
|
||||||
DS2480_NOT_READY, DS2480_DATA
|
DS2480_NOT_READY, DS2480_DATA, DS2480_BUSY
|
||||||
} ds2480_status;
|
} ds2480_status;
|
||||||
|
|
||||||
/** Blocking UART callbacks: 0 = success, nonzero = error/timeout.
|
/** Blocking UART callbacks: 0 = success, nonzero = error/timeout.
|
||||||
@@ -33,6 +33,8 @@ typedef struct {
|
|||||||
ds2480_port port;
|
ds2480_port port;
|
||||||
uint32_t timeout_ms;
|
uint32_t timeout_ms;
|
||||||
uint8_t ready;
|
uint8_t ready;
|
||||||
|
uint8_t power_active;
|
||||||
|
uint8_t data_mode;
|
||||||
} ds2480;
|
} ds2480;
|
||||||
|
|
||||||
/** Independent ROM search cursor. Zero-initialize before each enumeration. */
|
/** Independent ROM search cursor. Zero-initialize before each enumeration. */
|
||||||
@@ -50,12 +52,20 @@ ds2480_status ds2480_init(ds2480 *bus, const ds2480_port *port, uint32_t timeout
|
|||||||
ds2480_status ds2480_reset(ds2480 *bus);
|
ds2480_status ds2480_reset(ds2480 *bus);
|
||||||
/** Exchange one slot (write 1 to read); received must be non-NULL. */
|
/** Exchange one slot (write 1 to read); received must be non-NULL. */
|
||||||
ds2480_status ds2480_bit(ds2480 *bus, uint8_t bit, uint8_t *received);
|
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);
|
ds2480_status ds2480_byte(ds2480 *bus, uint8_t value, uint8_t *received);
|
||||||
/** Write a byte and start strong pullup immediately after its last slot.
|
/** 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.
|
* 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);
|
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). */
|
/** CRC8 Dallas/Maxim. data must address size bytes (NULL allowed for size=0). */
|
||||||
uint8_t ds2480_crc8(const uint8_t *data, uint32_t size);
|
uint8_t ds2480_crc8(const uint8_t *data, uint32_t size);
|
||||||
/** Search ALL families. OK yields ROM with valid CRC; DONE ends enumeration.
|
/** Search ALL families. OK yields ROM with valid CRC; DONE ends enumeration.
|
||||||
|
|||||||
52
c/ds18b20-ds2480/ports/stm32f4/README.md
Normal file
52
c/ds18b20-ds2480/ports/stm32f4/README.md
Normal file
@@ -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).
|
||||||
85
c/ds18b20-ds2480/ports/stm32f4/ds2480_stm32f4_hal.c
Normal file
85
c/ds18b20-ds2480/ports/stm32f4/ds2480_stm32f4_hal.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
32
c/ds18b20-ds2480/ports/stm32f4/ds2480_stm32f4_hal.h
Normal file
32
c/ds18b20-ds2480/ports/stm32f4/ds2480_stm32f4_hal.h
Normal file
@@ -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
|
||||||
39
c/ds18b20-ds2480/ports/stm32f4/tests/stm32f4xx_hal.h
Normal file
39
c/ds18b20-ds2480/ports/stm32f4/tests/stm32f4xx_hal.h
Normal file
@@ -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 <stdint.h>
|
||||||
|
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
|
||||||
41
c/ds18b20-ds2480/ports/stm32f4/tests/test_port.c
Normal file
41
c/ds18b20-ds2480/ports/stm32f4/tests/test_port.c
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#include "ds2480_stm32f4_hal.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ typedef struct {
|
|||||||
unsigned prepared, calibrated, pending, reply, pulse, hold, convert, copy;
|
unsigned prepared, calibrated, pending, reply, pulse, hold, convert, copy;
|
||||||
unsigned fail_read, fail_write, fail_prepare, corrupt_reply, reset_reply;
|
unsigned fail_read, fail_write, fail_prepare, corrupt_reply, reset_reply;
|
||||||
unsigned fail_power, fail_stop, reject_config;
|
unsigned fail_power, fail_stop, reject_config;
|
||||||
|
unsigned data_mode, escape;
|
||||||
} fake;
|
} fake;
|
||||||
|
|
||||||
static void crc_scratch(fake *f) { f->scratch[8] = ds2480_crc8(f->scratch, 8); }
|
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;
|
fake *f = user;
|
||||||
++f->prepared;
|
++f->prepared;
|
||||||
f->pending = f->pulse = f->calibrated = 0;
|
f->pending = f->pulse = f->calibrated = 0;
|
||||||
|
f->data_mode = f->escape = 0;
|
||||||
return (int)f->fail_prepare;
|
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);
|
CHECK(size == 1 && timeout == 20 && !f->pending);
|
||||||
if (f->fail_write) return -1;
|
if (f->fail_write) return -1;
|
||||||
if (!f->calibrated) { CHECK(command == 0xC1); f->calibrated = 1; return 0; }
|
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);
|
CHECK(!f->pulse || command == 0xF1);
|
||||||
if (command == 0x3F) f->reply = 0x3E;
|
if (command == 0x3F) f->reply = 0x3E;
|
||||||
else if (command == 0xC1) {
|
else if (command == 0xC1) {
|
||||||
@@ -168,6 +181,16 @@ int main(void)
|
|||||||
CHECK(ds18b20_ds2480_next(&bus, &search) == DS2480_DONE);
|
CHECK(ds18b20_ds2480_next(&bus, &search) == DS2480_DONE);
|
||||||
CHECK(ds18b20_ds2480_convert(&bus, NULL) == DS2480_OK);
|
CHECK(ds18b20_ds2480_convert(&bus, NULL) == DS2480_OK);
|
||||||
CHECK(f.convert == 1 && f.hold == 750 && !f.pulse && !f.pending);
|
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_temperature(&bus, f.rom[0], &raw) == DS2480_OK && raw == 401);
|
||||||
CHECK(ds18b20_ds2480_convert(&bus, f.rom[1]) == DS2480_OK && f.active == 2);
|
CHECK(ds18b20_ds2480_convert(&bus, f.rom[1]) == DS2480_OK && f.active == 2);
|
||||||
for (i = 9; i <= 12; ++i) {
|
for (i = 9; i <= 12; ++i) {
|
||||||
@@ -178,6 +201,8 @@ int main(void)
|
|||||||
CHECK(raw == (i == 9 ? -168 : i == 10 ? -164 : i == 11 ? -162 : -161));
|
CHECK(raw == (i == 9 ? -168 : i == 10 ? -164 : i == 11 ? -162 : -161));
|
||||||
}
|
}
|
||||||
CHECK(f.copy == 4);
|
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;
|
f.reject_config = 1;
|
||||||
CHECK(ds18b20_ds2480_configure(&bus, f.rom[0], 1, 2, 9, 1) == DS2480_DATA && f.copy == 4);
|
CHECK(ds18b20_ds2480_configure(&bus, f.rom[0], 1, 2, 9, 1) == DS2480_DATA && f.copy == 4);
|
||||||
f.scratch[8] ^= 1; raw = 999;
|
f.scratch[8] ^= 1; raw = 999;
|
||||||
|
|||||||
Reference in New Issue
Block a user