# Описание компонента 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` фиксированной длины. #### Формат передаваемого пакета | Поле | Тип | Описание | |------|-----|----------| | `data[0]` | `int16_t` | Идентификатор устройства (`DEVICE_ID = 6`) | | `data[1]` | `int16_t` | Тип устройства (`DEVICE_TYPE = 4`) | | `data[2]` | `int16_t` | Напряжение батареи × 100 (например, 330 = 3.30 В) | | `data[3]`–`data[6]` | `int16_t` | Зарезервированы (равны 0) | ### Конфигурационные макросы | Макрос | Значение | Описание | |--------|----------|----------| | `GATEWAY_CHANNEL` | `120` | Радиоканал шлюза | | `GATEWAY_ADDRESS` | `0xDDEEFF` | 3-байтовый адрес шлюза | | `DEVICE_ID` | `6` | Уникальный идентификатор устройства | | `DEVICE_TYPE` | `4` | Код типа устройства (touch pad) | ### Управление памятью Компонент не использует динамическую память. Все данные хранятся в стеке и регистрах микроконтроллера. Флаг `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 | ### Низкоуровневые 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 В на канале AVcc (MUX[3:1] = 111). - Предделитель частоты: 64 (ADPS[2:0] = 111), что даёт ~125 кГц при 8 МГц. - Выполняются **две последовательные конверсии**: первая стабилизирует выборку, вторая даёт результат. - Формула расчёта: `Vbat = (1024.0 × 1.1) / adc_value`. - При обрыве датчика (`adc == 0`) возвращается 0.0. ### Алгоритм передачи через 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) ```c int main(void) { // Отключаем прерывания на время инициализации cli(); // Настраиваем периферию zh_pad_interrupt_init(); zh_spi_init(); zh_nrf24_init(); // Конфигурируем режим сна Power-down set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_enable(); // Включаем прерывания sei(); for (;;) { if (send_request == 1) { // Собираем и отправляем пакет send_request = 0; int16_t data[] = {DEVICE_ID, DEVICE_TYPE, (int16_t)(zh_get_battery_voltage() * 100.0), 0, 0, 0, 0}; zh_nrf24_send((uint8_t *)data, sizeof(data)); } else { // Спим до следующего прерывания sleep_cpu(); } } } ``` ### Изменение конфигурации под другое устройство ```c // Измените идентификатор и тип для нового устройства #define DEVICE_ID 7 // Уникальный ID в сети #define DEVICE_TYPE 4 // 4 = touch pad #define GATEWAY_CHANNEL 120 #define GATEWAY_ADDRESS 0xDDEEFF ``` ### Подключение по SPI (схема выводов) ```c // Назначение пинов AVR для nRF24L01+: // PB2 (SS) -> CE (через резистор, или напрямую) // PB3 (MOSI) -> MOSI // PB5 (SCK) -> SCK // PB1 -> CE (управление передачей) // PD2 -> IRQ (вход, активный низкий) // PD3 -> INT1 (вход сенсорного датчика) ``` --- ## Ограничения 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) - Программатор USBasp (или совместимый) - Фьюзы: LFUSE=`0xE2`, HFUSE=`0xDF`, EFUSE=`0x07` (8 МГц, без делителя) --- ## Установка ### Сборка и прошивка через PlatformIO ```bash pio run -e ATmega328P -t upload ``` ### Подключение в проекте Компонент является самодостаточной прошивкой — не подключается как библиотека. Для интеграции в другой проект скопируйте `src/main.c` и адаптируйте макросы конфигурации. ```c // Зависимости — только стандартные заголовки AVR-libc: #include "avr/io.h" #include "avr/sleep.h" #include "avr/fuse.h" #include "avr/interrupt.h" #include "util/delay.h" ```