feat(ds18b20): порты 1-Wire для STM32G4 и STM32F4
Добавлены реализации низкоуровневой шины для STM32G431/G474 и STM32F407VET6 с шаблонами onewire_config.h под каждый камень. Логика повторяет порт STM32F1: открытый сток, задержки по DWT, запрет прерываний на один тайм-слот. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,8 @@
|
||||
| `include/ds18b20.h`, `src/ds18b20.c` | перечень датчиков, измерения, запись TH/TL/разрешения | `onewire.h`, `stdint.h` |
|
||||
| `include/onewire.h`, `src/onewire.c` | байты и блоки, CRC8 Dallas/Maxim, обход SEARCH ROM | `stdint.h` |
|
||||
| `ports/stm32f1/` | тайминги Maxim, вывод с открытым стоком, задержки по DWT | CMSIS `stm32f10x.h` |
|
||||
| `ports/stm32g4/` | то же для STM32G431 и STM32G474 | CMSIS `stm32g4xx.h` |
|
||||
| `ports/stm32f4/` | то же для STM32F407VET6 и родственных F4 | CMSIS `stm32f4xx.h` |
|
||||
| `tests/` | симулятор шины с виртуальными датчиками и host-тесты | — |
|
||||
|
||||
## Порт: что нужно от платформы
|
||||
@@ -60,6 +62,33 @@ uint8_t OneWire_ReadBit(void);
|
||||
|
||||
Требуется внешняя подтяжка 4.7 кОм к питанию.
|
||||
|
||||
### Готовые порты STM32G4 и STM32F4
|
||||
|
||||
Логика та же, что и в порте STM32F1: открытый сток, чтение через `IDR`,
|
||||
задержки по `DWT->CYCCNT`, запрет прерываний только на один тайм-слот.
|
||||
Отличается лишь настройка вывода — у G4 и F4 это `MODER`/`OTYPER`/
|
||||
`OSPEEDR`/`PUPDR` вместо `CRL`/`CRH`, а низкий уровень выставляется
|
||||
через верхнюю половину `BSRR`.
|
||||
|
||||
STM32G431 и STM32G474 обслуживает один порт `ports/stm32g4/`: блок GPIO и
|
||||
шина тактирования у них совпадают, различается только CMSIS-заголовок в
|
||||
`onewire_config.h`. Шаблоны лежат рядом — по одному на каждый камень:
|
||||
|
||||
```c
|
||||
/* ports/stm32g4/onewire_config.g474.template.h */
|
||||
#include "stm32g4xx.h"
|
||||
|
||||
#define ONEWIRE_GPIO GPIOA
|
||||
#define ONEWIRE_PIN 1U
|
||||
#define ONEWIRE_GPIO_CLOCK RCC_AHB2ENR_GPIOAEN /* у F4 — RCC_AHB1ENR_GPIOAEN */
|
||||
```
|
||||
|
||||
Порт `ports/stm32f4/` написан под STM32F407VET6 и подходит любому F4 с тем
|
||||
же блоком GPIO; тактирование включается в `RCC->AHB1ENR`.
|
||||
|
||||
Подтяжка 4.7 кОм — внешняя и здесь: внутренняя `PUPDR` намеренно
|
||||
оставлена выключенной, её сопротивления для шины не хватает.
|
||||
|
||||
## Использование
|
||||
|
||||
```c
|
||||
|
||||
26
c/ds18b20/ports/stm32f4/onewire_config.f407.template.h
Normal file
26
c/ds18b20/ports/stm32f4/onewire_config.f407.template.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @file onewire_config.f407.template.h
|
||||
* @brief Шаблон привязки шины 1-Wire для STM32F407VET6 (порт stm32f4).
|
||||
*
|
||||
* Скопируйте в своё приложение под именем onewire_config.h и положите на
|
||||
* путь поиска заголовков. Библиотека сама этот файл не подставляет:
|
||||
* привязка выводов принадлежит плате, а не драйверу.
|
||||
*
|
||||
* Требуется внешняя подтяжка 4.7 кОм к питанию.
|
||||
*/
|
||||
|
||||
#ifndef ONEWIRE_CONFIG_H
|
||||
#define ONEWIRE_CONFIG_H
|
||||
|
||||
#include "stm32f4xx.h"
|
||||
|
||||
/** Порт вывода шины. */
|
||||
#define ONEWIRE_GPIO GPIOA
|
||||
|
||||
/** Номер вывода в порту, 0..15. */
|
||||
#define ONEWIRE_PIN 1U
|
||||
|
||||
/** Бит включения тактирования порта в RCC->AHB1ENR. */
|
||||
#define ONEWIRE_GPIO_CLOCK RCC_AHB1ENR_GPIOAEN
|
||||
|
||||
#endif /* ONEWIRE_CONFIG_H */
|
||||
170
c/ds18b20/ports/stm32f4/onewire_stm32f4.c
Normal file
170
c/ds18b20/ports/stm32f4/onewire_stm32f4.c
Normal file
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
26
c/ds18b20/ports/stm32g4/onewire_config.g431.template.h
Normal file
26
c/ds18b20/ports/stm32g4/onewire_config.g431.template.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @file onewire_config.g431.template.h
|
||||
* @brief Шаблон привязки шины 1-Wire для STM32G431 (порт stm32g4).
|
||||
*
|
||||
* Скопируйте в своё приложение под именем onewire_config.h и положите на
|
||||
* путь поиска заголовков. Библиотека сама этот файл не подставляет:
|
||||
* привязка выводов принадлежит плате, а не драйверу.
|
||||
*
|
||||
* Требуется внешняя подтяжка 4.7 кОм к питанию.
|
||||
*/
|
||||
|
||||
#ifndef ONEWIRE_CONFIG_H
|
||||
#define ONEWIRE_CONFIG_H
|
||||
|
||||
#include "stm32g4xx.h"
|
||||
|
||||
/** Порт вывода шины. */
|
||||
#define ONEWIRE_GPIO GPIOA
|
||||
|
||||
/** Номер вывода в порту, 0..15. */
|
||||
#define ONEWIRE_PIN 1U
|
||||
|
||||
/** Бит включения тактирования порта в RCC->AHB2ENR. */
|
||||
#define ONEWIRE_GPIO_CLOCK RCC_AHB2ENR_GPIOAEN
|
||||
|
||||
#endif /* ONEWIRE_CONFIG_H */
|
||||
26
c/ds18b20/ports/stm32g4/onewire_config.g474.template.h
Normal file
26
c/ds18b20/ports/stm32g4/onewire_config.g474.template.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @file onewire_config.g474.template.h
|
||||
* @brief Шаблон привязки шины 1-Wire для STM32G474 (порт stm32g4).
|
||||
*
|
||||
* Скопируйте в своё приложение под именем onewire_config.h и положите на
|
||||
* путь поиска заголовков. Библиотека сама этот файл не подставляет:
|
||||
* привязка выводов принадлежит плате, а не драйверу.
|
||||
*
|
||||
* Требуется внешняя подтяжка 4.7 кОм к питанию.
|
||||
*/
|
||||
|
||||
#ifndef ONEWIRE_CONFIG_H
|
||||
#define ONEWIRE_CONFIG_H
|
||||
|
||||
#include "stm32g4xx.h"
|
||||
|
||||
/** Порт вывода шины. */
|
||||
#define ONEWIRE_GPIO GPIOA
|
||||
|
||||
/** Номер вывода в порту, 0..15. */
|
||||
#define ONEWIRE_PIN 1U
|
||||
|
||||
/** Бит включения тактирования порта в RCC->AHB2ENR. */
|
||||
#define ONEWIRE_GPIO_CLOCK RCC_AHB2ENR_GPIOAEN
|
||||
|
||||
#endif /* ONEWIRE_CONFIG_H */
|
||||
172
c/ds18b20/ports/stm32g4/onewire_stm32g4.c
Normal file
172
c/ds18b20/ports/stm32g4/onewire_stm32g4.c
Normal file
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
Reference in New Issue
Block a user