doc: added wiki
This commit is contained in:
1 parent
6f79a3c111
commit
58d86a0c71
1 file changed
+266
+266
@@ -0,0 +1,266 @@
|
|||||||
|
# Описание компонента zh_ttp229
|
||||||
|
|
||||||
|
## Общие сведения
|
||||||
|
|
||||||
|
**zh_ttp229** — это компонент ESP-IDF для работы с контроллером ёмкостных сенсорных кнопок TTP229 по интерфейсу SCL/SDO. Чтение состояния кнопок выполняется через периферию RMT вместо программного переключения GPIO (bit-banging).
|
||||||
|
|
||||||
|
Компонент регистрирует прерывание на выводе SDO, по фронтальному фронту генерирует тактовые импульсы SCL через RMT TX, принимает импульс с линии SDO через RMT RX и публикует событие `ZH_TTP229` с номером нажатой кнопки.
|
||||||
|
|
||||||
|
## Основные возможности
|
||||||
|
|
||||||
|
1. **Два режима работы** — конфигурации на 8 и на 16 кнопок.
|
||||||
|
2. **RMT-обмен** — тактирование и приём без bit-banging, с IRAM-safe обработчиками.
|
||||||
|
3. **Многоустройственность** — несколько плат TTP229 с уникальными номерами устройств.
|
||||||
|
4. **Событийный API** — уведомления через фреймворк `esp_event`.
|
||||||
|
5. **Debounce по тикам** — отбрасывание дребезга без задержек в задаче обработки.
|
||||||
|
6. **Восстановление RX-канала** — автоматический сброс RMT RX при ошибках драйвера.
|
||||||
|
7. **Сбор статистики** — счётчики ошибок RMT, постинга событий, переполнения очереди, остаток стека.
|
||||||
|
8. **Самоочистка ресурсов** — общая очередь и задача удаляются при деинициализации последнего устройства.
|
||||||
|
|
||||||
|
## Внутреннее устройство
|
||||||
|
|
||||||
|
### Структура `zh_ttp229_handle_t`
|
||||||
|
|
||||||
|
Непрозрачный дескриптор одного устройства, определяется в `zh_ttp229.c`.
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
|------|-----|----------|
|
||||||
|
| `sdo_gpio` | `gpio_num_t` | Вывод данных SDO (прерывание и приём) |
|
||||||
|
| `scl_gpio` | `gpio_num_t` | Вывод тактирования SCL (RMT TX) |
|
||||||
|
| `device_number` | `uint8_t` | Уникальный номер устройства, заданный при инициализации |
|
||||||
|
| `rmt_tx_start_delay` | `uint8_t` | Задержка (мкс) между импульсом на SDO и стартом генератора RMT |
|
||||||
|
| `scl_symbols_count` | `uint8_t` | Число тактовых символов SCL (8 или 16) |
|
||||||
|
| `debounce_time` | `uint16_t` | Время debounce в миллисекундах |
|
||||||
|
| `last_event_tick` | `TickType_t` | Тик последнего отправленного события, `-1` до первого события |
|
||||||
|
| `rx_needs_reset` | `volatile bool` | Флаг запроса сброса RMT RX канала, устанавливается из ISR |
|
||||||
|
| `scl_symbols` | `rmt_symbol_word_t [16]` | Предвычисленные символы тактового сигнала SCL |
|
||||||
|
| `rx_symbols` | `rmt_symbol_word_t` | Принятый символ с линии SDO |
|
||||||
|
| `tx_channel` | `rmt_channel_handle_t` | Дескриптор RMT TX канала |
|
||||||
|
| `rx_channel` | `rmt_channel_handle_t` | Дескриптор RMT RX канала |
|
||||||
|
| `copy_encoder` | `rmt_encoder_handle_t` | Copy-энкодер для RMT TX |
|
||||||
|
|
||||||
|
Поле `rx_channel` обнуляется при деинициализации — обработчик прерывания проверяет его и завершается, не обращаясь к удалённому каналу.
|
||||||
|
|
||||||
|
### Структура `zh_ttp229_queue_t`
|
||||||
|
|
||||||
|
Элемент очереди, передаваемый из RMT RX callback в задачу обработки.
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
|------|-----|----------|
|
||||||
|
| `handle` | `zh_ttp229_handle_t *` | Указатель на дескриптор устройства-инициатора |
|
||||||
|
| `rx_symbol` | `rmt_symbol_word_t` | Снимок принятого символа SDO |
|
||||||
|
|
||||||
|
### Модульные статические данные
|
||||||
|
|
||||||
|
| Переменная | Тип | Описание |
|
||||||
|
|------------|-----|----------|
|
||||||
|
| `zh_ttp229` | `TaskHandle_t` | Задача обработки прерываний; создаётся для первого устройства |
|
||||||
|
| `_queue_handle` | `QueueHandle_t` | Общая очередь уведомлений от RMT RX callback |
|
||||||
|
| `_stats` | `zh_ttp229_stats_t` | Накопленная статистика ошибок |
|
||||||
|
| `_vector` | `zh_vector_t *` | Вектор номеров всех инициализированных устройств (компонент `zh_vector`) |
|
||||||
|
|
||||||
|
### Управление памятью
|
||||||
|
|
||||||
|
Дескриптор выделяется через `heap_caps_calloc()` с флагом `MALLOC_CAP_8BIT` при инициализации и освобождается в `zh_ttp229_deinit()`. Общая память (очередь, задача, вектор) принадлежит первому устройству: при ошибке инициализации первого устройства выполняется откат (`_zh_ttp229_rollback_task_and_resources()`), при удалении последнего — полное освобождение.
|
||||||
|
|
||||||
|
## API (интерфейс)
|
||||||
|
|
||||||
|
### Инициализация и освобождение
|
||||||
|
|
||||||
|
| Функция | Описание |
|
||||||
|
|---------|----------|
|
||||||
|
| `zh_ttp229_init()` | Инициализация устройства: валидация конфигурации, создание общих ресурсов (для первого устройства), настройка GPIO с прерыванием по фронтальному фронту, создание RMT TX/RX каналов. Требует `*handle == NULL` на входе. |
|
||||||
|
| `zh_ttp229_deinit()` | Деинициализация устройства: удаление из вектора, снятие ISR handler, удаление RMT каналов и энкодера, сброс GPIO. Общие ресурсы удаляются только для последнего устройства. |
|
||||||
|
|
||||||
|
### Получение информации и сброс
|
||||||
|
|
||||||
|
| Функция | Описание |
|
||||||
|
|---------|----------|
|
||||||
|
| `zh_ttp229_get_stats()` | Возвращает указатель на статическую структуру `zh_ttp229_stats_t`, действительную всё время работы приложения. |
|
||||||
|
| `zh_ttp229_reset_stats()` | Сбрасывает все счётчики статистики в ноль. |
|
||||||
|
|
||||||
|
### Типы и константы
|
||||||
|
|
||||||
|
| Элемент | Описание |
|
||||||
|
|---------|----------|
|
||||||
|
| `ZH_TTP229_INIT_CONFIG_DEFAULT()` | Макрос конфигурации по умолчанию (приоритет 1, очередь 5, 8 кнопок, debounce 100 мс, задержка 15 мкс). |
|
||||||
|
| `ZH_TTP229` | База событий `esp_event` для уведомлений о нажатиях. |
|
||||||
|
| `zh_ttp229_work_mode_t` | Режим работы: `ZH_TTP229_8_PAD` (8) или `ZH_TTP229_16_PAD` (16). |
|
||||||
|
| `zh_ttp229_init_config_t` | Конфигурация инициализации (GPIO, режим, debounce, параметры задачи и очереди). |
|
||||||
|
| `zh_ttp229_stats_t` | Статистика ошибок и остатка стека. |
|
||||||
|
| `zh_ttp229_event_on_isr_t` | Полезная нагрузка события: `pad_number` (1-based) и `device_number`. |
|
||||||
|
|
||||||
|
## Потокобезопасность
|
||||||
|
|
||||||
|
1. **Очередь FreeRTOS** — единственной синхронизирующей примитивом является общая очередь `_queue_handle`: ISR handler и RMT RX callback кладут в неё записи через `xQueueSendFromISR()`, задача обработки забирает через `xQueueReceive()`. Взаимные исключения между задачами пользователя не предусмотрены.
|
||||||
|
2. **Флаг `rx_needs_reset`** — объявлен как `volatile bool`, устанавливается из ISR и сбрасывается в задаче обработки; используется для отложенного сброса RMT RX канала вне контекста прерывания.
|
||||||
|
3. **Отключение прерываний GPIO** — на время цикла чтения прерывание SDO отключается (`gpio_intr_disable()`) и включается обратно в задаче обработки после постинга события, что исключает повторный вход в ISR во время обмена.
|
||||||
|
4. **Удаление** — при переполнении очереди или ошибке RMT прерывания GPIO включаются сразу из ISR, чтобы устройство не осталось «зависшим».
|
||||||
|
|
||||||
|
## Алгоритмы
|
||||||
|
|
||||||
|
### Опрос кнопок (протокол SCL/SDO)
|
||||||
|
|
||||||
|
1. Фронтальный фронт на SDO (контроллер готов передать данные) вызывает `_zh_ttp229_isr_handler()`.
|
||||||
|
2. ISR вооружает приём одного символа `rmt_receive()` на RX канале (окно сигнала 100 нс – 2 мкс).
|
||||||
|
3. Кратковременный импульс на SDO (OUTPUT low → high → INPUT) синхронизирует контроллер.
|
||||||
|
4. Через RMT TX передаются `scl_symbols_count` тактовых символов SCL (каждый: 2 мкс low + 2 мкс high, `init_level = 1`).
|
||||||
|
5. По завершении приёма RMT RX callback кладёт измеренный символ в очередь.
|
||||||
|
|
||||||
|
### Декодирование номера кнопки
|
||||||
|
|
||||||
|
Номер кнопки определяется по длительности `duration1` принятого символа (в мкс, разрешение RMT 1 МГц). Обозначим `base = rmt_tx_start_delay`. Для кнопки N (1..16) ожидаемое окно:
|
||||||
|
|
||||||
|
- N = 1: `duration1 ∈ {base-1, base, base+1}`
|
||||||
|
- N > 1: `duration1 ∈ {base + 4·(N-1) - 1, base + 4·(N-1), base + 4·(N-1) + 1}`
|
||||||
|
|
||||||
|
Тolerance ±1 мкс компенсирует джиттер. Распознанный номер больше `scl_symbols_count` отбрасывается.
|
||||||
|
|
||||||
|
### Debounce
|
||||||
|
|
||||||
|
- Событие публикуется, если с предыдущего события прошло не менее `pdMS_TO_TICKS(debounce_time)` тиков, либо событие первое (`last_event_tick == -1`).
|
||||||
|
- Окно сравнивается по арифметике тиков без вычитания переполнения (`now - last_event_tick`), что корректно при переполнении счётчика.
|
||||||
|
- подавленные события не обновляют `last_event_tick`, поэтому кнопка «срабатывает» сразу после окончания окна.
|
||||||
|
|
||||||
|
### Восстановление RX канала
|
||||||
|
|
||||||
|
- При ошибке `rmt_receive()`/`rmt_transmit()` устанавливается `rx_needs_reset` и в очередь ставится пустая запись-уведомление.
|
||||||
|
- Задача обработки выполняет `rmt_disable()` + `rmt_enable()` для RX канала вне ISR.
|
||||||
|
|
||||||
|
## Примеры использования
|
||||||
|
|
||||||
|
### Базовый пример
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_ttp229.h"
|
||||||
|
|
||||||
|
static zh_ttp229_handle_t *ttp229_handle = NULL;
|
||||||
|
|
||||||
|
void touch_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
||||||
|
{
|
||||||
|
zh_ttp229_event_on_isr_t *event = event_data;
|
||||||
|
printf("Устройство %d: нажата кнопка %d\n", event->device_number, event->pad_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
esp_event_loop_create_default();
|
||||||
|
esp_event_handler_instance_register(ZH_TTP229, ESP_EVENT_ANY_ID, &touch_handler, NULL, NULL);
|
||||||
|
|
||||||
|
zh_ttp229_init_config_t config = ZH_TTP229_INIT_CONFIG_DEFAULT();
|
||||||
|
config.scl_gpio = GPIO_NUM_25;
|
||||||
|
config.sdo_gpio = GPIO_NUM_27;
|
||||||
|
config.work_mode = ZH_TTP229_16_PAD;
|
||||||
|
config.rmt_tx_start_delay = 23; // подбирается экспериментально
|
||||||
|
|
||||||
|
if (zh_ttp229_init(&config, &ttp229_handle) != ESP_OK)
|
||||||
|
{
|
||||||
|
return; // обработка ошибки инициализации
|
||||||
|
}
|
||||||
|
// ... работа ...
|
||||||
|
zh_ttp229_deinit(&ttp229_handle); // очистка ресурсов
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Работа с несколькими устройствами
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_ttp229.h"
|
||||||
|
|
||||||
|
#define DEV1 0x01
|
||||||
|
#define DEV2 0x02
|
||||||
|
|
||||||
|
static zh_ttp229_handle_t *dev1 = NULL;
|
||||||
|
static zh_ttp229_handle_t *dev2 = NULL;
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
esp_event_loop_create_default();
|
||||||
|
|
||||||
|
zh_ttp229_init_config_t config1 = ZH_TTP229_INIT_CONFIG_DEFAULT();
|
||||||
|
config1.scl_gpio = GPIO_NUM_25;
|
||||||
|
config1.sdo_gpio = GPIO_NUM_26;
|
||||||
|
config1.device_number = DEV1;
|
||||||
|
|
||||||
|
zh_ttp229_init_config_t config2 = ZH_TTP229_INIT_CONFIG_DEFAULT();
|
||||||
|
config2.scl_gpio = GPIO_NUM_27;
|
||||||
|
config2.sdo_gpio = GPIO_NUM_22;
|
||||||
|
config2.device_number = DEV2;
|
||||||
|
|
||||||
|
zh_ttp229_init(&config1, &dev1);
|
||||||
|
zh_ttp229_init(&config2, &dev2); // общее ресурсы не пересоздаются
|
||||||
|
// ... работа ...
|
||||||
|
zh_ttp229_deinit(&dev2);
|
||||||
|
zh_ttp229_deinit(&dev1); // последний объект удаляет задачу и очередь
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Мониторинг статистики
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_ttp229.h"
|
||||||
|
|
||||||
|
void stats_task(void *arg)
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
const zh_ttp229_stats_t *stats = zh_ttp229_get_stats();
|
||||||
|
if (stats->rmt_driver_error > 0 || stats->queue_overflow_error > 0)
|
||||||
|
{
|
||||||
|
printf("Ошибки RMT: %lu, переполнения очереди: %lu\n",
|
||||||
|
stats->rmt_driver_error, stats->queue_overflow_error);
|
||||||
|
zh_ttp229_reset_stats(); // сброс счётчиков
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(60000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Логирование
|
||||||
|
|
||||||
|
Компонент использует ESP-IDF логирование с тегом `"zh_ttp229"`.
|
||||||
|
|
||||||
|
```c
|
||||||
|
esp_log_level_set("zh_ttp229", ESP_LOG_ERROR);
|
||||||
|
```
|
||||||
|
|
||||||
|
Также компонент зависит от `zh_vector` с тегом `"zh_vector"`, который стоит приглушить отдельно.
|
||||||
|
|
||||||
|
## Ошибки
|
||||||
|
|
||||||
|
| Код | Описание |
|
||||||
|
|-----|----------|
|
||||||
|
| `ESP_OK` | Успех |
|
||||||
|
| `ESP_ERR_INVALID_ARG` | NULL-аргумент, значение вне допустимого диапазона, дублирующийся `device_number` |
|
||||||
|
| `ESP_ERR_INVALID_STATE` | `*handle` не равен `NULL` при вызове `zh_ttp229_init()` |
|
||||||
|
| `ESP_ERR_NO_MEM` | Не удалось выделить память под дескриптор |
|
||||||
|
| `ESP_FAIL` | Ошибка создания очереди/задачи, инициализации GPIO, ISR-сервиса или RMT; устройство не найдено в векторе при деинициализации |
|
||||||
|
|
||||||
|
## Ограничения
|
||||||
|
|
||||||
|
1. **Первое нажатие после старта/сброса не работает** — особенность контроллера TTP229.
|
||||||
|
2. **Мультитач не поддерживается** — за один цикл декодируется только одна кнопка.
|
||||||
|
3. **`device_number` должен быть уникальным и > 0** — используется как ключ в векторе устройств.
|
||||||
|
4. **Общая очередь и задача используют параметры первого устройства** — `queue_size`, `stack_size` и `task_priority` последующих устройств игнорируются.
|
||||||
|
5. **`rmt_tx_start_delay` подбирается индивидуально** — минимум 15 мкс, точность зависит от платы; определяется экспериментально или логическим анализатором.
|
||||||
|
6. **Первое нажатие после инициализации** может быть потеряно из-за синхронизации контроллера.
|
||||||
|
7. **Потокобезопасность ограничена** — функции `init`/`deinit` не защищены от одновременного вызова из нескольких задач.
|
||||||
|
8. **`min_stack_size` обновляется только при событиях** — при отсутствии нажатий значение остаётся равным размеру стека из конфигурации.
|
||||||
|
|
||||||
|
## Требования для сборки
|
||||||
|
|
||||||
|
- ESP-IDF >= 5.0 (в `CMakeLists.txt` учтены различия `driver` и `esp_driver_rmt`/`esp_driver_gpio` для IDF 6.x)
|
||||||
|
- FreeRTOS (входит в состав ESP-IDF)
|
||||||
|
- `esp_event` (входит в состав ESP-IDF)
|
||||||
|
- `zh_vector` — динамический вектор (подтягивается автоматически через `idf_component.yml`)
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ваш_проект/components
|
||||||
|
git clone https://github.com/aZholtikov/zh_ttp229
|
||||||
|
```
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_ttp229.h"
|
||||||
|
```
|
||||||
Reference in new issue
Block a user