Files
templates/c/ds18b20/ports/stm32f4/onewire_stm32f4.c
Andrey Kruchinkin 033c7ab9e8 feat(ds18b20): порты 1-Wire для STM32G4 и STM32F4
Добавлены реализации низкоуровневой шины для STM32G431/G474 и
STM32F407VET6 с шаблонами onewire_config.h под каждый камень.
Логика повторяет порт STM32F1: открытый сток, задержки по DWT,
запрет прерываний на один тайм-слот.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 13:14:23 +03:00

171 lines
5.4 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 onewire_stm32f4.c
* @brief Порт шины 1-Wire на STM32F4: тайминги, вывод и задержки.
*
* Написан под STM32F407VET6 и подходит любому STM32F4 с тем же блоком GPIO:
* различается только CMSIS-заголовок, который подставляет onewire_config.h.
*
* Вывод постоянно настроен как выход с открытым стоком: чтение уровня
* выполняется через регистр IDR, поэтому переключать режим не требуется.
* Прерывания запрещаются только на длительность одного тайм-слота, чтобы не
* терять байты USART во время обмена.
*
* Привязка вывода приходит из onewire_config.h, который предоставляет
* приложение. Шаблон — onewire_config.f407.template.h рядом с этим файлом.
*/
#include "onewire.h"
#include "onewire_config.h"
#if !defined(ONEWIRE_GPIO) || !defined(ONEWIRE_PIN) || !defined(ONEWIRE_GPIO_CLOCK)
#error "onewire_config.h должен задать ONEWIRE_GPIO, ONEWIRE_PIN и ONEWIRE_GPIO_CLOCK"
#endif
/** Длительности тайм-слотов в микросекундах согласно рекомендациям Maxim. */
#define OW_RESET_LOW_US 480U
#define OW_PRESENCE_WAIT_US 70U
#define OW_RESET_TAIL_US 410U
#define OW_WRITE1_LOW_US 6U
#define OW_WRITE1_TAIL_US 64U
#define OW_WRITE0_LOW_US 60U
#define OW_WRITE0_TAIL_US 10U
#define OW_READ_LOW_US 6U
#define OW_READ_SAMPLE_US 9U
#define OW_READ_TAIL_US 55U
/** Число тактов ядра на микросекунду; заполняется в OneWire_Init. */
static uint32_t g_cycles_per_us = 168U;
/**
* @brief Переводит вывод шины в активный низкий уровень.
*/
static void ow_drive_low(void)
{
ONEWIRE_GPIO->BSRR = (uint32_t)(1UL << (ONEWIRE_PIN + 16U));
}
/**
* @brief Отпускает шину; уровень поднимает внешний резистор подтяжки.
*/
static void ow_release(void)
{
ONEWIRE_GPIO->BSRR = (uint32_t)(1UL << ONEWIRE_PIN);
}
/**
* @brief Читает текущий уровень на выводе шины.
*
* @return 1 для высокого уровня, 0 для низкого.
*/
static uint8_t ow_read_pin(void)
{
return (uint8_t)((ONEWIRE_GPIO->IDR >> ONEWIRE_PIN) & 1U);
}
void OneWire_DelayUs(uint32_t microseconds)
{
const uint32_t start = DWT->CYCCNT;
const uint32_t ticks = microseconds * g_cycles_per_us;
while ((DWT->CYCCNT - start) < ticks) {
__NOP();
}
}
void OneWire_Init(void)
{
uint32_t config;
g_cycles_per_us = SystemCoreClock / 1000000U;
if (g_cycles_per_us == 0U) {
g_cycles_per_us = 1U;
}
/* Счётчик тактов ядра — единственный источник микросекундных задержек. */
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0U;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
RCC->AHB1ENR |= ONEWIRE_GPIO_CLOCK;
/* Открытый сток: OTYPER = 1. */
ONEWIRE_GPIO->OTYPER |= (uint32_t)(1UL << ONEWIRE_PIN);
/* Подтяжка внешняя, внутренняя не нужна: PUPDR = 00. */
config = ONEWIRE_GPIO->PUPDR;
config &= ~(0x3UL << (ONEWIRE_PIN * 2U));
ONEWIRE_GPIO->PUPDR = config;
/* Максимальная скорость фронта: OSPEEDR = 11. */
config = ONEWIRE_GPIO->OSPEEDR;
config |= (0x3UL << (ONEWIRE_PIN * 2U));
ONEWIRE_GPIO->OSPEEDR = config;
/* Уровень отпускаем до перевода в выход, чтобы не дёрнуть шину вниз. */
ow_release();
/* Обычный выход: MODER = 01. */
config = ONEWIRE_GPIO->MODER;
config &= ~(0x3UL << (ONEWIRE_PIN * 2U));
config |= (0x1UL << (ONEWIRE_PIN * 2U));
ONEWIRE_GPIO->MODER = config;
OneWire_DelayUs(OW_RESET_LOW_US);
}
uint8_t OneWire_Reset(void)
{
uint32_t primask;
uint8_t presence;
/* Удлинение импульса сброса безопасно, поэтому прерывания не запрещаем. */
ow_drive_low();
OneWire_DelayUs(OW_RESET_LOW_US);
primask = __get_PRIMASK();
__disable_irq();
ow_release();
OneWire_DelayUs(OW_PRESENCE_WAIT_US);
presence = (uint8_t)(ow_read_pin() == 0U);
__set_PRIMASK(primask);
OneWire_DelayUs(OW_RESET_TAIL_US);
return (uint8_t)(presence && (ow_read_pin() != 0U));
}
void OneWire_WriteBit(uint8_t bit)
{
const uint32_t primask = __get_PRIMASK();
__disable_irq();
ow_drive_low();
if (bit != 0U) {
OneWire_DelayUs(OW_WRITE1_LOW_US);
ow_release();
OneWire_DelayUs(OW_WRITE1_TAIL_US);
} else {
OneWire_DelayUs(OW_WRITE0_LOW_US);
ow_release();
OneWire_DelayUs(OW_WRITE0_TAIL_US);
}
__set_PRIMASK(primask);
}
uint8_t OneWire_ReadBit(void)
{
const uint32_t primask = __get_PRIMASK();
uint8_t bit;
__disable_irq();
ow_drive_low();
OneWire_DelayUs(OW_READ_LOW_US);
ow_release();
OneWire_DelayUs(OW_READ_SAMPLE_US);
bit = ow_read_pin();
__set_PRIMASK(primask);
OneWire_DelayUs(OW_READ_TAIL_US);
return bit;
}