15 KiB
Описание компонента zh_avr_touch_pad
Общие сведения
zh_avr_touch_pad — прошивка для AVR-микроконтроллеров ATMega168/328, реализующая беспроводную сенсорную кнопку на базе трансивера nRF24L01+.
Компонент работает в режиме глубокого сна (Power-down), пробуждается по внешнему прерыванию от сенсорного датчика и передаёт на шлюз пакет с идентификатором устройства и уровнем заряда батареи.
Основные возможности
- Сверхнизкое энергопотребление — около 2 мкА в режиме сна.
- Передача уровня заряда батареи — измерение напряжения через внутренний АЦП с опорным напряжением 1,1 В.
- Работа по протоколу nRF24L01+ — передача через SPI, адресная доставка на шлюз.
- Поддержка нескольких МК — ATMega168/168P/168PB/328/328P/328PB.
- Минимальная обвязка — установка в стандартный корпус сенсорного выключателя.
- Пробуждение по внешнему прерыванию INT1 — rising edge на пине PD3.
- Автономная работа — не требует внешних подтягивающих резисторов.
Внутреннее устройство
Структура данных
Компонент не использует динамическое выделение памяти и сложные структуры данных. Все данные передаются как массив 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. Синхронизация между прерыванием и главным циклом реализуется следующим образом:
volatileфлаг — переменнаяsend_requestобъявлена какvolatile, что гарантирует чтение актуального значения из памяти при каждой проверке в главном цикле.- Атомарная запись из ISR — обработка INT1 устанавливает флаг в единицу. Запись одного байта на AVR является атомарной операцией.
- Отключение прерываний при инициализации —
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, аргументы не валидируются |
Ограничения
- Только передача (TX-only) — компонент не принимает данные, шлюз не может отправить подтверждение на уровне приложения.
- Отсутствие шифрования — CRC включён, но AES-шифрование не используется. Пакеты могут быть перехвачены.
- Жёстко зашитый адрес шлюза — смена адреса требует перепрошивки.
- Таймаут передачи 100 мс — если шлюз недоступен, пакет теряется без повторной попытки на уровне приложения (аппаратные ретрансмиты настроены через
SETUP_RETR = 0xFF: 15 попыток с максимальным интервалом). - Одно сенсорное событие за цикл — несколько касаний во время передачи будут потеряны (флаг — один бит).
- Точность измерения батареи — зависит от стабильности внутреннего опорного напряжения 1,1 В и может отличаться от реального напряжения на ±3%.
- Нет механизма глубокого сна для периферии — АЦП полностью отключается после измерения (
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"