style: big renaming

This commit is contained in:
aZholtikov committed 2026-10-31 08:06:35 +03:00
1 parent 696091857e
commit e7c4ade170
2 files changed
+377 -355

No files matched your search

File diff suppressed because it is too large. Load diff
+128 -106
View File
@@ -23,11 +23,11 @@
### Структура данных ### Структура данных
Компонент не использует динамическое выделение памяти. Сырые показания хранятся в статических переменных, калибровочные коэффициенты BME280 — в массиве `bme280_calib[33]`. Перед передачей данные сериализуются функцией `zh_nrf24_sensor_build_payload()` из внешней библиотеки `zh_nrf24_config` в буфер `uint8_t[ZH_NRF24_MAX_PAYLOAD_SIZE]` (32 байта) в формате little-endian. Компонент не использует динамическое выделение памяти. Сырые показания хранятся в статических переменных, калибровочные коэффициенты BME280 — в массиве `_bme280_calib[33]`. Перед передачей данные сериализуются функцией `zh_nrf24_sensor_build_payload()` из внешней библиотеки `zh_nrf24_config` в буфер `uint8_t[ZH_NRF24_MAX_PAYLOAD_SIZE]` (32 байта) в формате little-endian.
#### Формат передаваемого пакета #### Формат передаваемого пакета
Данные формируются в глобальной переменной `transmitted_data` типа `zh_nrf24_sensor_transmitted_data_t` из библиотеки `zh_nrf24_config`, затем сериализуются в полезную нагрузку. Для типа `ZH_NRF24_SENSOR_BME280` полный размер пакета — 12 байт (заголовок id + type + battery и три поля измерений, каждое `int16_t` в little-endian). Данные формируются в глобальной переменной `_transmitted_data` типа `zh_nrf24_sensor_transmitted_data_t` из библиотеки `zh_nrf24_config`, затем сериализуются в полезную нагрузку. Для типа `ZH_NRF24_SENSOR_BME280` полный размер пакета — 12 байт (заголовок id + type + battery и три поля измерений, каждое `int16_t` в little-endian).
| Поле | Тип | Описание | | Поле | Тип | Описание |
| ------ | ----- | ---------- | | ------ | ----- | ---------- |
@@ -67,89 +67,102 @@
### Управление памятью ### Управление памятью
Компонент не использует динамическую память. Все данные хранятся в статической области и регистрах микроконтроллера. Глобальная переменная `transmitted_data` заполняется при старте (тип устройства, затем идентификатор из EEPROM или из сопряжения). В цикле обновляются поля `battery`, `value_1` (влажность), `value_2` (температура), `value_3` (давление). Идентификатор устройства сохраняется в EEPROM адресом `0x00` (`eeprom_update_byte`). Компонент не использует динамическую память. Все данные хранятся в статической области и регистрах микроконтроллера. Глобальная переменная `_transmitted_data` заполняется при старте (тип устройства, затем идентификатор из EEPROM или из сопряжения). В цикле обновляются поля `battery`, `value_1` (влажность), `value_2` (температура), `value_3` (давление). Идентификатор устройства сохраняется в EEPROM адресом `0x00` (`eeprom_update_byte`).
--- ---
## API (интерфейс) ## API (интерфейс)
Все функции являются `static` и доступны только внутри модуля `main.c`. Все функции являются `static` и доступны только внутри модуля `zh_avr_bme280_sensor.c`.
### Инициализация ### Инициализация
| Функция | Описание | | Функция | Описание |
| --------- | ---------- | | --------- | ---------- |
| `zh_wdt_start()` | Запуск watchdog-таймера в режиме прерывания с указанным таймаутом (биты WDP3:0) | | `_zh_wdt_start()` | Запуск watchdog-таймера в режиме прерывания с указанным таймаутом (биты WDP3:0) |
| `zh_wdt_stop()` | Остановка watchdog-таймера | | `_zh_wdt_stop()` | Остановка watchdog-таймера |
| `zh_i2c_init()` | Инициализация аппаратного TWI (I2C) на пинах PC4 (SDA), PC5 (SCL), TWBR = 32 | | `_zh_i2c_init()` | Инициализация аппаратного TWI (I2C) на пинах PC4 (SDA), PC5 (SCL), TWBR = 32 |
| `zh_bme280_init()` | Инициализация датчика BME280: проверка CHIP_ID, сброс, чтение калибровки, настройка режима | | `_zh_bme280_init()` | Инициализация датчика BME280: проверка CHIP_ID, сброс, чтение калибровки, настройка режима |
| `zh_spi_init()` | Инициализация аппаратного SPI в режиме мастера (Fosc/16) | | `_zh_spi_init()` | Инициализация аппаратного SPI в режиме мастера (Fosc/16) |
| `zh_nrf24_init()` | Инициализация трансивера nRF24L01+: адреса TX/RX, канал, ретрансмиты, динамические пакеты, CRC | | `_zh_nrf24_init()` | Инициализация трансивера nRF24L01+: адреса TX/RX, канал, ретрансмиты, динамические пакеты, CRC |
| `zh_nrf24_interrupt_init()` | Настройка внешнего прерывания INT0 (PD2, падающий фронт) для сигнала IRQ трансивера | | `_zh_nrf24_interrupt_init()` | Настройка внешнего прерывания INT0 (PD2, падающий фронт) для сигнала IRQ трансивера |
### Идентификатор устройства и сопряжение ### Идентификатор устройства и сопряжение
| Функция | Описание | | Функция | Описание |
| --------- | ---------- | | --------- | ---------- |
| `zh_load_device_id()` | Чтение ID устройства из EEPROM (возвращает `DEVICE_ID_UNASSIGNED` при 0x00/0xFF) | | `_zh_load_device_id()` | Чтение ID устройства из EEPROM (возвращает `DEVICE_ID_UNASSIGNED` при 0x00/0xFF) |
| `zh_save_device_id()` | Сохранение ID устройства в EEPROM через `eeprom_update_byte` | | `_zh_save_device_id()` | Сохранение ID устройства в EEPROM через `eeprom_update_byte` |
| `zh_get_random_token()` | Генерация 16-битного псевдослучайного токена из MCUSR и шума АЦП (никогда не 0) | | `_zh_get_random_token()` | Генерация 16-битного псевдослучайного токена из MCUSR и шума АЦП (никогда не 0) |
| `zh_do_pairing()` | Неблокирующая процедура сопряжения: запрос → ожидание ACK в RX-режиме → backoff; возвращает назначенный ID | | `_zh_do_pairing()` | Неблокирующая процедура сопряжения: запрос → ожидание ACK в RX-режиме → backoff; возвращает назначенный ID |
| `zh_enter_final_sleep()` | Переход в постоянный сон Power-down до сброса (отключение прерываний, WDT, радио, тактов периферии) | | `_zh_enter_final_sleep()` | Переход в постоянный сон Power-down до сброса (отключение прерываний, WDT, радио, тактов периферии) |
### Измерение ### Измерение
| Функция | Описание | | Функция | Описание |
|---------|----------| | --------- | ---------- |
| `zh_get_battery_voltage()` | Измерение напряжения батареи через внутренний АЦП с опорой 1,1 В (две конверсии, усреднение) | | `_zh_get_battery_voltage()` | Измерение напряжения батареи через внутренний АЦП с опорой 1,1 В (две конверсии, усреднение) |
### Чтение данных BME280 ### Чтение данных BME280
| Функция | Описание | | Функция | Описание |
| --------- | ---------- | | --------- | ---------- |
| `zh_bme280_read()` | Перевод BME280 в forced mode, чтение 8 сырых байт и распаковка ADC значений температуры, давления, влажности | | `_zh_bme280_read()` | Перевод BME280 в forced mode, чтение 8 сырых байт и распаковка ADC значений температуры, давления, влажности |
| `zh_bme280_compensate_temperature()` | Компенсация сырой температуры по калибровочным коэффициентам (результат в °C) | | `_zh_bme280_compensate_temperature()` | Компенсация сырой температуры по калибровочным коэффициентам (результат в °C) |
| `zh_bme280_compensate_pressure()` | Компенсация сырого давления по калибровочным коэффициентам (результат в Па) | | `_zh_bme280_compensate_pressure()` | Компенсация сырого давления по калибровочным коэффициентам (результат в Па) |
| `zh_bme280_compensate_humidity()` | Компенсация сырой влажности по калибровочным коэффициентам (результат в %) | | `_zh_bme280_compensate_humidity()` | Компенсация сырой влажности по калибровочным коэффициентам (результат в %) |
### Передача данных nRF24L01+ ### Передача данных nRF24L01+
| Функция | Описание | | Функция | Описание |
| --------- | ---------- | | --------- | ---------- |
| `zh_nrf24_send()` | Передача буфера: Power Up, `FLUSH_TX`, `W_TX_PAYLOAD`, импульс CE, ожидание IRQ с таймаутом ~100 мс, сброс статуса | | `_zh_nrf24_send()` | Передача буфера: Power Up, `FLUSH_TX`, `W_TX_PAYLOAD`, импульс CE, ожидание IRQ с таймаутом ~100 мс, сброс статуса |
| `zh_nrf24_send_no_wait()` | Запуск передачи без ожидания завершения (импульс CE 15 мкс), завершение сигнализируется IRQ | | `_zh_nrf24_send_no_wait()` | Запуск передачи без ожидания завершения (импульс CE 15 мкс), завершение сигнализируется IRQ |
| `zh_nrf24_enter_rx()` | Переход в режим приёма на pipe 0 (PRIM_RX, очистка FIFO, CE) | | `_zh_nrf24_enter_rx()` | Переход в режим приёма на pipe 0 (PRIM_RX, очистка FIFO, CE) |
| `zh_nrf24_enter_power_down()` | Перевод трансивера в режим Power Down | | `_zh_nrf24_enter_power_down()` | Перевод трансивера в режим Power Down |
### Низкоуровневые SPI-операции ### Низкоуровневые SPI-операции
| Функция | Описание | | Функция | Описание |
| --------- | ---------- | | --------- | ---------- |
| `zh_spi_transfer()` | Обмен одного байта по SPI (полный дуплекс) | | `_zh_spi_transfer()` | Обмен одного байта по SPI (полный дуплекс) |
| `zh_nrf24_write_register()` | Запись одного байта в регистр nRF24L01+ | | `_zh_nrf24_write_register()` | Запись одного байта в регистр nRF24L01+ |
| `zh_nrf24_write_buff_register()` | Запись многобайтового буфера в регистр nRF24L01+ | | `_zh_nrf24_write_buff_register()` | Запись многобайтового буфера в регистр nRF24L01+ |
| `zh_nrf24_write_buffer()` | Запись буфера данных с указанной командой (CSN управляется вручную через PB2) | | `_zh_nrf24_write_buffer()` | Запись буфера данных с указанной командой (CSN управляется вручную через PB2) |
| `zh_nrf24_send_command()` | Отправка однобайтовой команды и чтение статуса | | `_zh_nrf24_send_command()` | Отправка однобайтовой команды и чтение статуса |
| `zh_nrf24_read_payload()` | Чтение принятой полезной нагрузки из RX FIFO (`R_RX_PAYLOAD`) | | `_zh_nrf24_read_payload()` | Чтение принятой полезной нагрузки из RX FIFO (`R_RX_PAYLOAD`) |
| `zh_nrf24_read_payload_width()` | Чтение длины пакета в RX FIFO (`R_RX_PL_WID`) | | `_zh_nrf24_read_payload_width()` | Чтение длины пакета в RX FIFO (`R_RX_PL_WID`) |
### Низкоуровневые TWI-операции (аппаратный I2C) ### Низкоуровневые TWI-операции (аппаратный I2C)
| Функция | Описание | | Функция | Описание |
| --------- | ---------- | | --------- | ---------- |
| `zh_i2c_wait()` | Ожидание флага `TWINT` с таймаутом 65535 итераций (возвращает false при зависании шины) | | `_zh_i2c_wait()` | Ожидание флага `TWINT` с таймаутом 65535 итераций (возвращает false при зависании шины) |
| `zh_i2c_start()` | Генерация стартового (или повторного стартового) условия, проверка статусов `0x08`/`0x10` | | `_zh_i2c_start()` | Генерация стартового (или повторного стартового) условия, проверка статусов `0x08`/`0x10` |
| `zh_i2c_stop()` | Генерация стоп-условия с ограниченным ожиданием сброса бита `TWSTO` | | `_zh_i2c_stop()` | Генерация стоп-условия с ограниченным ожиданием сброса бита `TWSTO` |
| `zh_i2c_write()` | Передача одного байта через `TWDR`, проверка статусов ACK (`0x18`, `0x28`, `0x40`) | | `_zh_i2c_write()` | Передача одного байта через `TWDR`, проверка статусов ACK (`0x18`, `0x28`, `0x40`) |
| `zh_i2c_read_ack()` | Чтение байта из `TWDR` с подтверждением ACK (возвращает `0xFF` при таймауте) | | `_zh_i2c_read_ack()` | Чтение байта из `TWDR` с подтверждением ACK (возвращает `0xFF` при таймауте) |
| `zh_i2c_read_nack()` | Чтение последнего байта из `TWDR` без подтверждения NACK (возвращает `0xFF` при таймауте) | | `_zh_i2c_read_nack()` | Чтение последнего байта из `TWDR` без подтверждения NACK (возвращает `0xFF` при таймауте) |
### Служебные TWI-функции BME280 ### Служебные TWI-функции BME280
| Функция | Описание | | Функция | Описание |
| --------- | ---------- | | --------- | ---------- |
| `zh_bme280_write_reg()` | Запись одного байта в регистр BME280 (start, адрес, регистр, данные, stop, задержка 2 мс) | | `_zh_bme280_write_reg()` | Запись одного байта в регистр BME280 (start, адрес, регистр, данные, stop, задержка 2 мс) |
| `zh_bme280_read_reg()` | Чтение нескольких байт из регистров BME280 (repeated-start, ACK/NACK) | | `_zh_bme280_read_reg()` | Чтение нескольких байт из регистров BME280 (repeated-start, ACK/NACK) |
### Статические переменные модуля
Все переменные объявлены `static` и доступны только внутри модуля.
| Переменная | Объявление | Описание |
| --------- | ---------- | ---------- |
| `_wdt_wake_count` | `static volatile uint8_t` | Счётчик прерываний WDT, инкрементируется в `ISR(WDT_vect)` до предела `WDT_WAKE_COUNT` |
| `_nrf_event` | `static volatile bool` | Флаг события трансивера, выставляется в `ISR(INT0_vect)` и сбрасывается в машине состояний сопряжения |
| `_bme280_calib` | `static uint8_t [33]` | Калибровочные коэффициенты BME280, загружаются при инициализации датчика |
| `_bme280_temp_fine` | `static int32_t` | Промежуточное значение `t_fine`, общее для компенсаторов давления и влажности |
| `_bme280_present` | `static bool` | Признак доступности датчика BME280 (сбрасывается при ошибке чтения, восстанавливается повторной инициализацией) |
| `_transmitted_data` | `zh_nrf24_sensor_transmitted_data_t` | Глобальная (в пределах модуля) структура формируемого пакета |
--- ---
@@ -157,8 +170,8 @@
Компонент работает на bare-metal AVR без RTOS. Точки синхронизации — два ISR: Компонент работает на bare-metal AVR без RTOS. Точки синхронизации — два ISR:
- `ISR(WDT_vect)` — увеличивает `wdt_wake_count` (объявлен как `static volatile uint8_t`, инкремент защищён проверкой верхнего предела), заново включает бит `WDIE`. - `ISR(WDT_vect)` — увеличивает `_wdt_wake_count` (объявлен как `static volatile uint8_t`, инкремент защищён проверкой верхнего предела), заново включает бит `WDIE`.
- `ISR(INT0_vect)` — выставляет флаг `nrf_event` (`static volatile uint8_t`) при фронте на пине IRQ трансивера. - `ISR(INT0_vect)` — выставляет флаг `_nrf_event` (`static volatile bool`) при фронте на пине IRQ трансивера.
Главный цикл и машина состояний сопряжения проверяют эти флаги после выхода из сна. Атомарная запись одного байта на AVR гарантирует отсутствие состояний гонки. Весь остальной обмен с периферией (I2C, SPI, АЦП) выполняется последовательно в главном цикле. Главный цикл и машина состояний сопряжения проверяют эти флаги после выхода из сна. Атомарная запись одного байта на AVR гарантирует отсутствие состояний гонки. Весь остальной обмен с периферией (I2C, SPI, АЦП) выполняется последовательно в главном цикле.
@@ -169,13 +182,13 @@
### Алгоритм загрузки идентификатора и сопряжения ### Алгоритм загрузки идентификатора и сопряжения
- При старте читается ID из EEPROM (`eeprom_read_byte`, адрес `0x00`); значения `0x00` и `0xFF` считаются признаком несопряжённого устройства. - При старте читается ID из EEPROM (`eeprom_read_byte`, адрес `0x00`); значения `0x00` и `0xFF` считаются признаком несопряжённого устройства.
- При отсутствии ID выполняется `zh_do_pairing()` — неблокирующая машина состояний (`ZH_NRF24_PAIR_SEND` → `ZH_NRF24_PAIR_TX_WAIT` → `ZH_NRF24_PAIR_RX_WAIT` → `ZH_NRF24_PAIR_BACKOFF`): - При отсутствии ID выполняется `_zh_do_pairing()` — неблокирующая машина состояний (`ZH_NRF24_PAIR_SEND` → `ZH_NRF24_PAIR_TX_WAIT` → `ZH_NRF24_PAIR_RX_WAIT` → `ZH_NRF24_PAIR_BACKOFF`):
- генерируется токен (`zh_get_random_token()` из MCUSR и шума АЦП); - генерируется токен (`_zh_get_random_token()` из MCUSR и шума АЦП);
- отправляется запрос `zh_nrf24_sensor_pairing_data_t` (magic `0xA5`) без ожидания (`zh_nrf24_send_no_wait`); - отправляется запрос `zh_nrf24_sensor_pairing_data_t` (magic `0xA5`) без ожидания (`_zh_nrf24_send_no_wait`);
- после TX_DS трансивер переводится в RX-режим, ожидание ответа со сном и WDT-таймаутом 250 мс; - после TX_DS трансивер переводится в RX-режим, ожидание ответа со сном и WDT-таймаутом 250 мс;
- ответ валидируется по magic `0x5A`, типу, токену и ненулевому ID; ширина пакета проверяется через `R_RX_PL_WID`; - ответ валидируется по magic `0x5A`, типу, токену и ненулевому ID; ширина пакета проверяется через `R_RX_PL_WID`;
- при неудаче — backoff-пауза ~4 с (WDT), не более `PAIRING_TIMEOUT_ATTEMPTS` (7) попыток. - при неудаче — backoff-пауза ~4 с (WDT), не более `PAIRING_TIMEOUT_ATTEMPTS` (7) попыток.
- Назначенный ID сохраняется в EEPROM и записывается в `transmitted_data.id`. - Назначенный ID сохраняется в EEPROM и записывается в `_transmitted_data.id`.
- После инициализации выполняется пробная сборка пакета `zh_nrf24_sensor_build_payload()`; при нулевом результате (неизвестный тип) — переход в постоянный сон. - После инициализации выполняется пробная сборка пакета `zh_nrf24_sensor_build_payload()`; при нулевом результате (неизвестный тип) — переход в постоянный сон.
### Алгоритм инициализации BME280 ### Алгоритм инициализации BME280
@@ -183,18 +196,18 @@
- Задержка 100 мс после включения. - Задержка 100 мс после включения.
- Проверка `CHIP_ID` (регистр `BME280_REG_ID`, ожидается `0x60`). - Проверка `CHIP_ID` (регистр `BME280_REG_ID`, ожидается `0x60`).
- Сброс записью `0xB6` в регистр `BME280_REG_RESET`, задержка 10 мс. - Сброс записью `0xB6` в регистр `BME280_REG_RESET`, задержка 10 мс.
- Чтение калибровочных коэффициентов: 26 байт из `BME280_REG_CALIB00` и 7 байт из `BME280_REG_CALIB26` (всего 33 байта, массив `bme280_calib`). - Чтение калибровочных коэффициентов: 26 байт из `BME280_REG_CALIB00` и 7 байт из `BME280_REG_CALIB26` (всего 33 байта, массив `_bme280_calib`).
- Конфигурация влажности: `ctrl_hum = 0x01` (oversampling x1). - Конфигурация влажности: `ctrl_hum = 0x01` (oversampling x1).
- Конфигурация режима: `config = 0x00` (фильтр off, standby off). - Конфигурация режима: `config = 0x00` (фильтр off, standby off).
- Запуск измерения: `ctrl_meas = 0x25` (temp/pressure oversampling x16, forced mode) в `zh_bme280_read()` перед каждым чтением. - Запуск измерения: `ctrl_meas = 0x25` (temp/pressure oversampling x16, forced mode) в `_zh_bme280_read()` перед каждым чтением.
### Алгоритм компенсации BME280 ### Алгоритм компенсации BME280
Сырые ADC-значения пересчитываются в физические величины по алгоритмам производителя из datasheet BME280. Сырые ADC-значения пересчитываются в физические величины по алгоритмам производителя из datasheet BME280.
- **Температура**: `zh_bme280_compensate_temperature()` — вычисляет `bme280_t_fine` (используется для давления и влажности), возвращает результат в °C. - **Температура**: `_zh_bme280_compensate_temperature()` — вычисляет `_bme280_temp_fine` (используется для давления и влажности), возвращает результат в °C.
- **Давление**: `zh_bme280_compensate_pressure()` — возвращает результат в Па. При `var1 == 0` (знаменатель ноль) возвращает 0.0. - **Давление**: `_zh_bme280_compensate_pressure()` — возвращает результат в Па. При `var1 == 0` (знаменатель ноль) возвращает 0.0.
- **Влажность**: `zh_bme280_compensate_humidity()` — результат ограничивается диапазоном [0, 419430400] на промежуточном шаге, затем делится на 1024.0 и возвращается в процентах. - **Влажность**: `_zh_bme280_compensate_humidity()` — результат ограничивается диапазоном [0, 419430400] на промежуточном шаге, затем делится на 1024.0 и возвращается в процентах.
### Алгоритм аппаратного TWI ### Алгоритм аппаратного TWI
@@ -203,7 +216,7 @@
- Передача байта: запись в `TWDR`, сброс `TWINT`, ожидание завершения, проверка статусов подтверждения (`0x18`/`0x28` — данные, `0x40` — адрес чтения). - Передача байта: запись в `TWDR`, сброс `TWINT`, ожидание завершения, проверка статусов подтверждения (`0x18`/`0x28` — данные, `0x40` — адрес чтения).
- Приём байта: установка `TWEA` для всех байт кроме последнего, чтение `TWDR` после `TWINT`. - Приём байта: установка `TWEA` для всех байт кроме последнего, чтение `TWDR` после `TWINT`.
- Останов: установка `TWSTO`, ограниченное ожидание (1000 итераций) сброса бита аппаратно. - Останов: установка `TWSTO`, ограниченное ожидание (1000 итераций) сброса бита аппаратно.
- Обработка ошибок: каждая операция возвращает `false` при таймауте ожидания или неверном статусе, вызывающая сторона выполняет `zh_i2c_stop()`. - Обработка ошибок: каждая операция возвращает `false` при таймауте ожидания или неверном статусе, вызывающая сторона выполняет `_zh_i2c_stop()`.
### Алгоритм передачи через nRF24L01+ ### Алгоритм передачи через nRF24L01+
@@ -215,16 +228,16 @@
- Ожидание завершения по пину IRQ (PD2) с таймаутом ~100 мс. - Ожидание завершения по пину IRQ (PD2) с таймаутом ~100 мс.
- Сброс флагов статуса, очистка FIFO, переход в Power Down. - Сброс флагов статуса, очистка FIFO, переход в Power Down.
Для процедуры сопряжения используется вариант без блокировки (`zh_nrf24_send_no_wait`, импульс CE 15 мкс) с завершением по прерыванию INT0. Для процедуры сопряжения используется вариант без блокировки (`_zh_nrf24_send_no_wait`, импульс CE 15 мкс) с завершением по прерыванию INT0.
### Алгоритм сна и пробуждения ### Алгоритм сна и пробуждения
- Инициализация WDT: прерывание по таймауту ~8 секунд (`WDT_T_8S`), без сброса MCU. - Инициализация WDT: прерывание по таймауту ~8 секунд (`WDT_T_8S`), без сброса MCU.
- ISR `WDT_vect`: увеличивает `wdt_wake_count` (до предела `WDT_WAKE_COUNT`), заново включает бит `WDIE`. - ISR `WDT_vect`: увеличивает `_wdt_wake_count` (до предела `WDT_WAKE_COUNT`), заново включает бит `WDIE`.
- Главный цикл: переход в Power-down (`sleep_cpu()`). После пробуждения проверяется `wdt_wake_count >= WDT_WAKE_COUNT` (38 × 8 с ≈ 5 минут). - Главный цикл: переход в Power-down (`sleep_cpu()`). После пробуждения проверяется `_wdt_wake_count >= WDT_WAKE_COUNT` (38 × 8 с ≈ 5 минут).
- При достижении порога: обнуление счётчика, повторная инициализация BME280 (если датчик недоступен), чтение показаний, сериализация пакета и передача через nRF24L01+. - При достижении порога: обнуление счётчика, повторная инициализация BME280 (если датчик недоступен), чтение показаний, сериализация пакета и передача через nRF24L01+.
- Первое измерение выполняется сразу после инициализации и сопряжения, до входа в цикл. - Первое измерение выполняется сразу после инициализации и сопряжения, до входа в цикл.
- `zh_enter_final_sleep()` — аварийный постоянный сон: отключение прерываний, WDT, радио, АЦП, TWI, SPI и тактов всей неиспользуемой периферии (PRR). - `_zh_enter_final_sleep()` — аварийный постоянный сон: отключение прерываний, WDT, радио, АЦП, TWI, SPI и тактов всей неиспользуемой периферии (PRR).
--- ---
@@ -235,45 +248,54 @@
```c ```c
int main(void) int main(void)
{ {
transmitted_data.type = ZH_NRF24_SENSOR_BME280; _transmitted_data.type = ZH_NRF24_SENSOR_BME280;
MCUSR &= ~(1 << WDRF); MCUSR &= ~(1 << WDRF);
wdt_disable(); wdt_disable();
cli(); cli();
zh_i2c_init(); _zh_i2c_init();
bme280_present = zh_bme280_init(); _bme280_present = _zh_bme280_init();
zh_spi_init(); _zh_spi_init();
zh_nrf24_init(); _zh_nrf24_init();
set_sleep_mode(SLEEP_MODE_PWR_DOWN); set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sei(); sei();
uint8_t device_id = zh_load_device_id(); uint8_t device_id = _zh_load_device_id();
if (device_id == DEVICE_ID_UNASSIGNED) { if (device_id == DEVICE_ID_UNASSIGNED) {
device_id = zh_do_pairing(); device_id = _zh_do_pairing();
} }
transmitted_data.id = (int16_t)device_id; _transmitted_data.id = (int16_t)device_id;
zh_wdt_start(WDT_T_8S); // Пробная сборка пакета: неизвестный тип — переход в постоянный сон
{
uint8_t probe[ZH_NRF24_MAX_PAYLOAD_SIZE];
if (zh_nrf24_sensor_build_payload(&_transmitted_data, probe,
sizeof(probe)) == 0) {
_zh_enter_final_sleep();
}
}
_zh_wdt_start(WDT_T_8S);
int32_t adc_t = 0, adc_p = 0, adc_h = 0; int32_t adc_t = 0, adc_p = 0, adc_h = 0;
// Первое измерение сразу после инициализации и сопряжения // Первое измерение сразу после инициализации и сопряжения
if (bme280_present == true) { if (_bme280_present == true) {
if (zh_bme280_read(&adc_t, &adc_p, &adc_h) == true) { if (_zh_bme280_read(&adc_t, &adc_p, &adc_h) == true) {
transmitted_data.battery = _transmitted_data.battery =
(int16_t)lroundf(zh_get_battery_voltage() * 100.0); (int16_t)lroundf(_zh_get_battery_voltage() * 100.0);
transmitted_data.value_2 = _transmitted_data.value_2 =
(int16_t)lroundf(zh_bme280_compensate_temperature(adc_t) * 10.0); (int16_t)lroundf(_zh_bme280_compensate_temperature(adc_t) * 10.0);
transmitted_data.value_3 = _transmitted_data.value_3 =
(int16_t)lroundf(zh_bme280_compensate_pressure(adc_p) * 0.00750062 * 10.0); (int16_t)lroundf(_zh_bme280_compensate_pressure(adc_p) * 0.00750062 * 10.0);
transmitted_data.value_1 = _transmitted_data.value_1 =
(int16_t)lroundf(zh_bme280_compensate_humidity(adc_h) * 10.0); (int16_t)lroundf(_zh_bme280_compensate_humidity(adc_h) * 10.0);
uint8_t payload[ZH_NRF24_MAX_PAYLOAD_SIZE]; uint8_t payload[ZH_NRF24_MAX_PAYLOAD_SIZE];
uint8_t size = zh_nrf24_sensor_build_payload(&transmitted_data, uint8_t size = zh_nrf24_sensor_build_payload(&_transmitted_data,
payload, sizeof(payload)); payload, sizeof(payload));
if (size > 0) { if (size > 0) {
zh_nrf24_send(payload, size); _zh_nrf24_send(payload, size);
} }
} else { } else {
bme280_present = false; _bme280_present = false;
} }
} }
@@ -282,29 +304,29 @@ int main(void)
sleep_enable(); sleep_enable();
sleep_cpu(); sleep_cpu();
sleep_disable(); sleep_disable();
if (wdt_wake_count >= WDT_WAKE_COUNT) { if (_wdt_wake_count >= WDT_WAKE_COUNT) {
wdt_wake_count = 0; _wdt_wake_count = 0;
if (bme280_present == false) { if (_bme280_present == false) {
bme280_present = zh_bme280_init(); _bme280_present = _zh_bme280_init();
} }
if (bme280_present == true) { if (_bme280_present == true) {
if (zh_bme280_read(&adc_t, &adc_p, &adc_h) == true) { if (_zh_bme280_read(&adc_t, &adc_p, &adc_h) == true) {
transmitted_data.battery = _transmitted_data.battery =
(int16_t)lroundf(zh_get_battery_voltage() * 100.0); (int16_t)lroundf(_zh_get_battery_voltage() * 100.0);
transmitted_data.value_2 = _transmitted_data.value_2 =
(int16_t)lroundf(zh_bme280_compensate_temperature(adc_t)); (int16_t)lroundf(_zh_bme280_compensate_temperature(adc_t));
transmitted_data.value_3 = _transmitted_data.value_3 =
(int16_t)lroundf(zh_bme280_compensate_pressure(adc_p) * 0.00750062); (int16_t)lroundf(_zh_bme280_compensate_pressure(adc_p) * 0.00750062);
transmitted_data.value_1 = _transmitted_data.value_1 =
(int16_t)lroundf(zh_bme280_compensate_humidity(adc_h)); (int16_t)lroundf(_zh_bme280_compensate_humidity(adc_h));
uint8_t payload[ZH_NRF24_MAX_PAYLOAD_SIZE]; uint8_t payload[ZH_NRF24_MAX_PAYLOAD_SIZE];
uint8_t size = zh_nrf24_sensor_build_payload(&transmitted_data, uint8_t size = zh_nrf24_sensor_build_payload(&_transmitted_data,
payload, sizeof(payload)); payload, sizeof(payload));
if (size > 0) { if (size > 0) {
zh_nrf24_send(payload, size); _zh_nrf24_send(payload, size);
} }
} else { } else {
bme280_present = false; _bme280_present = false;
} }
} }
} }
@@ -348,17 +370,17 @@ int main(void)
## Ошибки ## Ошибки
Компонент не возвращает коды ошибок. Функции `zh_bme280_init`, `zh_bme280_read`, `zh_bme280_write_reg`, `zh_bme280_read_reg` возвращают `bool` (true при успехе). `zh_load_device_id` возвращает `DEVICE_ID_UNASSIGNED` при отсутствии сохранённого ID. Остальные функции имеют тип `void` или возвращают однобайтовый статус SPI (`uint8_t`). Компонент не возвращает коды ошибок. Функции `_zh_bme280_init`, `_zh_bme280_read`, `_zh_bme280_write_reg`, `_zh_bme280_read_reg` возвращают `bool` (true при успехе). `_zh_load_device_id` возвращает `DEVICE_ID_UNASSIGNED` при отсутствии сохранённого ID. Остальные функции имеют тип `void` или возвращают однобайтовый статус SPI (`uint8_t`).
| Условие | Поведение | | Условие | Поведение |
| --------- | ---------- | | --------- | ---------- |
| Нулевое показание АЦП | `zh_get_battery_voltage()` возвращает 0.0 | | Нулевое показание АЦП | `_zh_get_battery_voltage()` возвращает 0.0 |
| Неверный `CHIP_ID` BME280 | `zh_bme280_init()` возвращает false, флаг `bme280_present = false`, при следующем цикле предпринимается повторная попытка инициализации | | Неверный `CHIP_ID` BME280 | `_zh_bme280_init()` возвращает false, флаг `_bme280_present = false`, при следующем цикле предпринимается повторная попытка инициализации |
| Таймаут ожидания `TWINT` | `zh_i2c_wait()` возвращает false, операция TWI прерывается | | Таймаут ожидания `TWINT` | `_zh_i2c_wait()` возвращает false, операция TWI прерывается |
| Неверный статус TWI (NACK, ошибка шины) | `zh_i2c_start()` / `zh_i2c_write()` возвращают false, вызывающая функция выполняет `zh_i2c_stop()` | | Неверный статус TWI (NACK, ошибка шины) | `_zh_i2c_start()` / `_zh_i2c_write()` возвращают false, вызывающая функция выполняет `_zh_i2c_stop()` |
| Таймаут при чтении TWI | `zh_i2c_read_ack()` / `zh_i2c_read_nack()` возвращают `0xFF` | | Таймаут при чтении TWI | `_zh_i2c_read_ack()` / `_zh_i2c_read_nack()` возвращают `0xFF` |
| Ошибка чтения BME280 | `zh_bme280_read()` возвращает false, пакет не формируется, `bme280_present = false` | | Ошибка чтения BME280 | `_zh_bme280_read()` возвращает false, пакет не формируется, `_bme280_present = false` |
| `var1 == 0` при компенсации давления | `zh_bme280_compensate_pressure()` возвращает 0.0 | | `var1 == 0` при компенсации давления | `_zh_bme280_compensate_pressure()` возвращает 0.0 |
| Превышение таймаута передачи (~100 мс) | Цикл ожидания IRQ прерывается, передача может быть неполной | | Превышение таймаута передачи (~100 мс) | Цикл ожидания IRQ прерывается, передача может быть неполной |
| Нет ответа шлюза при сопряжении | После 7 попыток (`PAIRING_TIMEOUT_ATTEMPTS`) — переход в постоянный сон до сброса | | Нет ответа шлюза при сопряжении | После 7 попыток (`PAIRING_TIMEOUT_ATTEMPTS`) — переход в постоянный сон до сброса |
| Ширина принятого пакета ≠ 5 байт / > 32 байт | FIFO очищается, состояние сопряжения переходит в backoff | | Ширина принятого пакета ≠ 5 байт / > 32 байт | FIFO очищается, состояние сопряжения переходит в backoff |
@@ -375,7 +397,7 @@ int main(void)
5. **Точность измерения батареи** — зависит от стабильности внутреннего опорного напряжения 1,1 В. 5. **Точность измерения батареи** — зависит от стабильности внутреннего опорного напряжения 1,1 В.
6. **Программные задержки в TWI** — обмен с BME280 выполняется на аппаратном TWI, но ожидание `TWINT` реализовано поллингом с таймаутом; прерывания TWI не используются. 6. **Программные задержки в TWI** — обмен с BME280 выполняется на аппаратном TWI, но ожидание `TWINT` реализовано поллингом с таймаутом; прерывания TWI не используются.
7. **Задержка между измерениями ~5 минут** — определяется произведением `WDT_WAKE_COUNT` (38) на интервал WDT (~8 секунд). В течение этого времени пакет не передаётся. 7. **Задержка между измерениями ~5 минут** — определяется произведением `WDT_WAKE_COUNT` (38) на интервал WDT (~8 секунд). В течение этого времени пакет не передаётся.
8. **Давление в мм рт. ст.** — `zh_bme280_compensate_pressure()` возвращает давление в Паскалях, для перевода в пакет умножается на `0.00750062` (коэффициент пересчёта Па → мм рт. ст.). 8. **Давление в мм рт. ст.** — `_zh_bme280_compensate_pressure()` возвращает давление в Паскалях, для перевода в пакет умножается на `0.00750062` (коэффициент пересчёта Па → мм рт. ст.).
9. **Масштабирование значений ×10 только в первом цикле** — при первом измерении температура, влажность и давление умножаются на 10 (одна дробная цифра); в цикле пробуждения значения записываются без масштабирования (см. `main`). 9. **Масштабирование значений ×10 только в первом цикле** — при первом измерении температура, влажность и давление умножаются на 10 (одна дробная цифра); в цикле пробуждения значения записываются без масштабирования (см. `main`).
10. **Сопряжение требует шлюза** — без ответившего шлюза устройство после 7 попыток уходит в постоянный сон до сброса/перепрошивки. 10. **Сопряжение требует шлюза** — без ответившего шлюза устройство после 7 попыток уходит в постоянный сон до сброса/перепрошивки.
@@ -401,7 +423,7 @@ pio run -e ATmega328P -t upload
### Подключение в проекте ### Подключение в проекте
Компонент является самодостаточной прошивкой — не подключается как библиотека. Для интеграции в другой проект скопируйте `src/main.c`, добавьте зависимость `zh_nrf24_config` и адаптируйте макросы конфигурации. Компонент является самодостаточной прошивкой — не подключается как библиотека. Для интеграции в другой проект скопируйте `src/zh_avr_bme280_sensor.c`, добавьте зависимость `zh_nrf24_config` и адаптируйте макросы конфигурации.
```ini ```ini
; platformio.ini ; platformio.ini