Files
zh_avr_touch_pad/wiki_ru.md
T
2026-10-21 20:13:58 +03:00

15 KiB
Raw Blame History

Описание компонента zh_avr_touch_pad

Общие сведения

zh_avr_touch_pad — прошивка для AVR-микроконтроллеров ATMega168/328, реализующая беспроводную сенсорную кнопку на базе трансивера nRF24L01+.

Компонент работает в режиме глубокого сна (Power-down), пробуждается по внешнему прерыванию от сенсорного датчика и передаёт на шлюз пакет с идентификатором устройства и уровнем заряда батареи.

Основные возможности

  1. Сверхнизкое энергопотребление — около 2 мкА в режиме сна.
  2. Передача уровня заряда батареи — измерение напряжения через внутренний АЦП с опорным напряжением 1,1 В.
  3. Работа по протоколу nRF24L01+ — передача через SPI, адресная доставка на шлюз.
  4. Поддержка нескольких МК — ATMega168/168P/168PB/328/328P/328PB.
  5. Минимальная обвязка — установка в стандартный корпус сенсорного выключателя.
  6. Пробуждение по внешнему прерыванию INT1 — rising edge на пине PD3.
  7. Автономная работа — не требует внешних подтягивающих резисторов.

Внутреннее устройство

Структура данных

Компонент не использует динамическое выделение памяти и сложные структуры данных. Все данные передаются как массив int16_t фиксированной длины.

Формат передаваемого пакета

Пакет формируется в глобальной переменной transmitted_data типа zh_rf_sensor_transmitted_data_t из внешней библиотеки zh_rf_config и передаётся побайтово как массив байтов (sizeof структуры — 14 байт).

Поле Тип Описание
id int16_t Идентификатор устройства (DEVICE_ID = 6)
type int16_t Тип устройства (ZH_RF_SENSOR_TOUCH_SWITCH)
battery_voltage int16_t Напряжение батареи × 100 (например, 330 = 3.30 В)
value_1–value_4 int16_t Зарезервированы (равны 0)

Конфигурационные макросы

Макрос Значение Описание
GATEWAY_CHANNEL 120 Радиоканал шлюза
GATEWAY_ADDRESS 0xDDEEFF 3-байтовый адрес шлюза
DEVICE_ID 6 Уникальный идентификатор устройства

Тип устройства задаётся не макросом, а значением перечисления ZH_RF_SENSOR_TOUCH_SWITCH из библиотеки zh_rf_config.

Управление памятью

Компонент не использует динамическую память. Все данные хранятся в статической области и регистрах микроконтроллера. Глобальная переменная transmitted_data заполняется один раз при старте (идентификатор и тип), в рабочем цикле обновляется только поле battery_voltage. Флаг send_request объявлен как static volatile uint8_t для корректного доступа из ISR и главного цикла.


API (интерфейс)

Все функции являются static и доступны только внутри модуля main.c.

Инициализация

Функция Описание
zh_pad_interrupt_init() Настройка внешнего прерывания INT1 (PD3, rising edge) для пробуждения по касанию
zh_spi_init() Инициализация аппаратного SPI в режиме мастера (Fosc/16)
zh_nrf24_init() Инициализация трансивера nRF24L01+ в режим передачи

Измерение

Функция Описание
zh_get_battery_voltage() Измерение напряжения батареи через внутренний АЦП с опорой 1,1 В

Передача данных

Функция Описание
zh_nrf24_send() Передача буфера через nRF24L01+ с полным циклом TX: Power Up, FLUSH_TX, загрузка W_TX_PAYLOAD, импульс CE, ожидание флага TX_DS/MAX_RT по пину IRQ, сброс статуса и переход в Power Down

Низкоуровневые SPI-операции

Функция Описание
zh_spi_transfer() Обмен одного байта по SPI (полный дуплекс)
zh_nrf24_write_register() Запись одного байта в регистр nRF24L01+
zh_nrf24_write_buff_register() Запись многобайтового буфера в регистр nRF24L01+
zh_nrf24_write_buffer() Запись буфера данных с указанной командой
zh_nrf24_send_command() Отправка однобайтовой команды и чтение статуса

Потокобезопасность

Компонент работает на bare-metal AVR без RTOS. Синхронизация между прерыванием и главным циклом реализуется следующим образом:

  1. volatile флаг — переменная send_request объявлена как volatile, что гарантирует чтение актуального значения из памяти при каждой проверке в главном цикле.
  2. Атомарная запись из ISR — обработка INT1 устанавливает флаг в единицу. Запись одного байта на AVR является атомарной операцией.
  3. Отключение прерываний при инициализации — cli() перед настройкой периферии исключает гонки при старте.

Алгоритмы

Алгоритм измерения напряжения батареи

  • ADC конфигурируется с внутренним опорным напряжением 1,1 В на канале внутреннего источника 1,1 В (REFS0=1, MUX[3:1]=111), вход AVcc сравнивается с опорой.
  • Предделитель частоты: 64 (ADPS[2:0] = 111), что даёт ~125 кГц при 8 МГц.
  • Выполняются две последовательные конверсии: первая стабилизирует выборку, вторая даёт результат.
  • Формула расчёта: Vbat = (1024.0 × 1.1) / adc_value.
  • При нулевой выборке (adc == 0) возвращается 0.0.
  • После измерения АЦП отключается сбросом бита ADEN.

Алгоритм передачи через nRF24L01+

  • Переход в Power Up (бит PWR_UP в CONFIG).
  • Ожидание 5 мс (время стартования осциллятора).
  • Очистка TX FIFO командой FLUSH_TX.
  • Загрузка полезной нагрузки командой W_TX_PAYLOAD.
  • Импульс CE (установка в 1) для запуска передачи.
  • Ожидание завершения по пину IRQ (PD2) с таймаутом 100 мс.
  • Сброс флагов статуса, очистка FIFO, переход в Power Down.

Алгоритм сна и пробуждения

  • Главный цикл проверяет флаг send_request.
  • Если флаг не установлен — вызывается sleep_cpu() (режим Power-down).
  • Пробуждение происходит по аппаратному прерыванию INT1 (rising edge на PD3).
  • ISR устанавливает send_request = 1, цикл просыпается и выполняет передачу.

Примеры использования

Типовой цикл работы (main)

int main(void)
{
    // Отключаем прерывания на время инициализации
    cli();
    // Настраиваем периферию
    zh_pad_interrupt_init();
    zh_spi_init();
    zh_nrf24_init();
    // Конфигурируем режим сна Power-down
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sei();

    // Глобальная структура из zh_rf_config
    zh_rf_sensor_transmitted_data_t transmitted_data =
        {DEVICE_ID, ZH_RF_SENSOR_TOUCH_SWITCH, 0, 0, 0, 0, 0};

    for (;;)
    {
        if (send_request == 1)
        {
            // Собираем и отправляем пакет
            cli();
            send_request = 0;
            sei();
            transmitted_data.battery_voltage =
                (int16_t)(zh_get_battery_voltage() * 100.0);
            zh_nrf24_send((uint8_t *)&transmitted_data,
                          sizeof(transmitted_data));
        }
        else
        {
            // Спим до следующего прерывания
            cli();
            if (send_request == 0)
            {
                sleep_enable();
                sei();
                sleep_cpu();
            }
            sei();
            sleep_disable();
        }
    }
}

Изменение конфигурации под другое устройство

// Измените идентификатор для нового устройства
#define DEVICE_ID 7       // Уникальный ID в сети
// Тип устройства задаётся перечислением ZH_RF_SENSOR_TOUCH_SWITCH
#define GATEWAY_CHANNEL 120
#define GATEWAY_ADDRESS 0xDDEEFF

Подключение по SPI (схема выводов)

// Назначение пинов AVR для nRF24L01+:
// PB2 (SS)   -> CSN (управление SPI-селектом)
// PB3 (MOSI) -> MOSI
// PB5 (SCK)  -> SCK
// PB1        -> CE  (управление передачей)
// PD2        -> IRQ (вход, активный низкий, с подтяжкой к VCC)
// PD3        -> INT1 (вход сенсорного датчика, rising edge)

Логирование

Компонент не использует ESP-IDF логирование (прошивка для AVR, а не ESP32). Логирование отсутствует.


Ошибки

Компонент не возвращает коды ошибок. Все функции имеют тип void или возвращают однобайтовый статус SPI (uint8_t) для низкоуровневых операций.

Условие Поведение
Нулевое показание АЦП zh_get_battery_voltage() возвращает 0.0
Превышение таймаута передачи Передача прерывается, пакет теряется
Неверный аргумент Не применимо — все функции static, аргументы не валидируются

Ограничения

  1. Только передача (TX-only) — компонент не принимает данные, шлюз не может отправить подтверждение на уровне приложения.
  2. Отсутствие шифрования — CRC включён, но AES-шифрование не используется. Пакеты могут быть перехвачены.
  3. Жёстко зашитый адрес шлюза — смена адреса требует перепрошивки.
  4. Таймаут передачи 100 мс — если шлюз недоступен, пакет теряется без повторной попытки на уровне приложения (аппаратные ретрансмиты настроены через SETUP_RETR = 0xFF: 15 попыток с максимальным интервалом).
  5. Одно сенсорное событие за цикл — несколько касаний во время передачи будут потеряны (флаг — один бит).
  6. Точность измерения батареи — зависит от стабильности внутреннего опорного напряжения 1,1 В и может отличаться от реального напряжения на ±3%.
  7. Нет механизма глубокого сна для периферии — АЦП полностью отключается после измерения (ADEN = 0), но SPI остаётся включённым в режиме сна.

Требования для сборки

  • PlatformIO с плагином platform-atmelavr
  • AVR-GCC (входит в состав PlatformIO)
  • Библиотека zh_rf_config (подключается через lib_deps в platformio.ini)
  • Программатор USBasp (или совместимый)
  • Фьюзы: LFUSE=0xE2, HFUSE=0xDF, EFUSE=0x07 (8 МГц, без делителя)

Установка

Сборка и прошивка через PlatformIO

pio run -e ATmega328P -t upload

Подключение в проекте

Компонент является самодостаточной прошивкой — не подключается как библиотека. Для интеграции в другой проект скопируйте src/main.c, добавьте зависимость zh_rf_config и адаптируйте макросы конфигурации.

; platformio.ini
lib_deps =
    https://github.com/aZholtikov/zh_rf_config
// Зависимости: стандартные заголовки AVR-libc и конфигурация протокола
#include "avr/io.h"
#include "avr/sleep.h"
#include "avr/fuse.h"
#include "avr/interrupt.h"
#include "util/delay.h"
#include "zh_rf_config.h"