160 lines
4.8 KiB
C
160 lines
4.8 KiB
C
/**
|
|
* @file onewire_stm32f1.c
|
|
* @brief Порт шины 1-Wire на STM32F1: тайминги, вывод и задержки.
|
|
*
|
|
* Вывод постоянно настроен как выход с открытым стоком: чтение уровня
|
|
* выполняется через регистр IDR, поэтому переключать режим не требуется.
|
|
* Прерывания запрещаются только на длительность одного тайм-слота, чтобы не
|
|
* терять байты USART во время обмена.
|
|
*
|
|
* Привязка вывода приходит из onewire_config.h, который предоставляет
|
|
* приложение. Шаблон для STM32F103 —
|
|
* onewire_config.f103.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;
|
|
}
|