/** * @file onewire_stm32g4.c * @brief Порт шины 1-Wire на STM32G4: тайминги, вывод и задержки. * * Подходит и STM32G431, и STM32G474 — периферия GPIO и шина тактирования * у них одинаковы, различается только CMSIS-заголовок, который подставляет * onewire_config.h. * * Вывод постоянно настроен как выход с открытым стоком: чтение уровня * выполняется через регистр IDR, поэтому переключать режим не требуется. * Прерывания запрещаются только на длительность одного тайм-слота, чтобы не * терять байты USART во время обмена. * * Привязка вывода приходит из onewire_config.h, который предоставляет * приложение. Шаблоны — onewire_config.g431.template.h и * onewire_config.g474.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 = 170U; /** * @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->AHB2ENR |= 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; }