/** * @file onewire_stm32f1.c * @brief Порт шины 1-Wire на STM32F1: тайминги, вывод и задержки. * * Вывод постоянно настроен как выход с открытым стоком: чтение уровня * выполняется через регистр IDR, поэтому переключать режим не требуется. * Прерывания запрещаются только на длительность одного тайм-слота, чтобы не * терять байты USART во время обмена. * * Привязка вывода приходит из onewire_config.h, который предоставляет * приложение. Шаблон - onewire_config.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 = 72U; /** * @brief Переводит вывод шины в активный низкий уровень. */ static void ow_drive_low(void) { ONEWIRE_GPIO->BRR = (uint32_t)(1UL << ONEWIRE_PIN); } /** * @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->APB2ENR |= ONEWIRE_GPIO_CLOCK; /* Выход с открытым стоком, 50 МГц: CNF = 01, MODE = 11. */ if (ONEWIRE_PIN < 8U) { config = ONEWIRE_GPIO->CRL; config &= ~(0xFUL << (ONEWIRE_PIN * 4U)); config |= (0x7UL << (ONEWIRE_PIN * 4U)); ONEWIRE_GPIO->CRL = config; } else { config = ONEWIRE_GPIO->CRH; config &= ~(0xFUL << ((ONEWIRE_PIN - 8U) * 4U)); config |= (0x7UL << ((ONEWIRE_PIN - 8U) * 4U)); ONEWIRE_GPIO->CRH = config; } ow_release(); 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; }