230 lines
12 KiB
Markdown
230 lines
12 KiB
Markdown
# Описание компонента 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"
|
||
```
|