Добавить документацию Solana PDA и ESP32-подпроект
This commit is contained in:
parent
56cd90a197
commit
74df7e2645
15
.gitignore
vendored
15
.gitignore
vendored
@ -73,3 +73,18 @@ shine-solana/shine/scripts/**/keypairs/
|
||||
shine-solana/shine/scripts/**/runs/
|
||||
shine-solana/shine/scripts/**/*.env
|
||||
shine-solana/shine/scripts/**/TEMP_*.md
|
||||
|
||||
# Локальные артефакты и внешние материалы ESP32-подпроекта
|
||||
ESP32/**/.git/
|
||||
ESP32/**/.idea/
|
||||
ESP32/**/.arduino-build/
|
||||
ESP32/**/official-demo/
|
||||
ESP32/**/original-firmware/*.bin
|
||||
ESP32/**/original-firmware/*.bin.sha256
|
||||
ESP32/**/*.elf
|
||||
ESP32/**/*.map
|
||||
ESP32/**/*.merged.bin
|
||||
ESP32/**/*.uf2
|
||||
ESP32/**/*.o
|
||||
ESP32/**/*.d
|
||||
ESP32/**/*.a
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
# Python-обвязка Telegram → Codex (упрощённый сервис)
|
||||
|
||||
- краткое описание фичи:
|
||||
- добавлен новый упрощённый сервис `SHiNE-agent-bot-coder/py_bot_service.py`;
|
||||
- сервис работает через long-polling Telegram, принимает только текст, ведёт историю в `JSONL`;
|
||||
- добавлены команды `/status`, `/queue`, `/stop`, `/cancel`, `/new`, `/help`;
|
||||
- systemd unit переключён на запуск Python-сервиса.
|
||||
|
||||
- что проверять:
|
||||
- `systemctl --user status shine-agent-bot-coder` показывает `active (running)`;
|
||||
- бот отвечает на `/status` одним сообщением без дублей;
|
||||
- тестовый текстовый запрос получает финальный ответ от Codex;
|
||||
- `/stop` корректно останавливает текущую задачу;
|
||||
- `/new` переносит текущую историю в `data/history/archive`.
|
||||
|
||||
- ожидаемый результат:
|
||||
- стабильная обработка текстовых задач без зависаний и двойной обработки;
|
||||
- только один инстанс сервиса (через lock `data/py_app.lock`).
|
||||
|
||||
- статус:
|
||||
- pending
|
||||
@ -1,26 +0,0 @@
|
||||
# Heartbeat и перезапуск агента-кодера
|
||||
|
||||
## Краткое описание
|
||||
|
||||
Изменена логика Telegram-сервиса агента-кодера:
|
||||
- аварийное сообщение о долгой работе отправляется только после 2 минут молчания Codex;
|
||||
- при дальнейшем молчании статус повторяется каждые 2 минуты;
|
||||
- добавлена команда `/restart_service` с алиасом `/restart` для перезапуска сервиса через systemd.
|
||||
|
||||
## Что проверить
|
||||
|
||||
1. Запустить долгую задачу, в которой Codex регулярно отправляет промежуточные сообщения.
|
||||
2. Убедиться, что дополнительное сообщение `всё ещё выполняется` не появляется без 2 минут молчания.
|
||||
3. Запустить или смоделировать задачу, где Codex молчит больше 2 минут.
|
||||
4. Проверить, что бот присылает статус с общим временем работы задачи и повторяет его каждые 2 минуты молчания.
|
||||
5. Отправить `/restart_service` из разрешённого Telegram-аккаунта.
|
||||
6. Проверить, что сервис завершился и был поднят systemd заново.
|
||||
7. Проверить, что история JSONL сохранилась и не была очищена без команды `/new`.
|
||||
|
||||
## Ожидаемый результат
|
||||
|
||||
Сервис не шумит регулярными статусами при нормальной работе Codex, но сообщает о подозрительном молчании. Команда `/restart_service` перезапускает сервис без ручного входа в консоль.
|
||||
|
||||
## Статус
|
||||
|
||||
pending
|
||||
@ -1,21 +0,0 @@
|
||||
# Канальный режим агента-кодера
|
||||
|
||||
## Краткое описание
|
||||
Сервис `SHiNE-agent-bot-coder` теперь принимает сообщения из Telegram-канала `@shine_writing`.
|
||||
|
||||
Сообщения Айдара (`@AidarKC` / `@aidarkc`) ставятся в очередь как задачи Codex, а ответы отправляются обратно в тот же канал. Сообщения других авторов в канале сохраняются в историю как дополнительный контекст и не выполняются как команды.
|
||||
|
||||
## Что проверить
|
||||
- Отправить текстовое сообщение от Айдара в канал `@shine_writing`.
|
||||
- Убедиться, что бот принял задачу, обработал её и ответил в этот же канал.
|
||||
- Отправить сообщение от другого автора в этот канал.
|
||||
- Убедиться, что бот не запускает задачу по сообщению другого автора.
|
||||
- Проверить, что сообщение другого автора появилось в JSONL-истории как контекст.
|
||||
|
||||
## Ожидаемый результат
|
||||
- Команды Айдара из канала выполняются так же, как личные сообщения.
|
||||
- Ответы бота публикуются в канал.
|
||||
- Сообщения других авторов сохраняются в историю, но не исполняются.
|
||||
|
||||
## Статус
|
||||
pending
|
||||
@ -1,16 +0,0 @@
|
||||
# Удаление старой Java-реализации агента-кодера
|
||||
|
||||
- краткое описание фичи:
|
||||
Старая Java-реализация `SHiNE-agent-bot-coder` удалена, потому что рабочим вариантом сервиса является Python-скрипт `py_bot_service.py`.
|
||||
|
||||
- что именно проверять:
|
||||
1. Gradle-проект больше не содержит подпроект `SHiNE-agent-bot-coder`.
|
||||
2. Локальный systemd-сервис `shine-agent-bot-coder` запускает `py_bot_service.py`.
|
||||
3. Telegram-бот принимает сообщение от Айдара и отвечает через Python-сервис.
|
||||
4. Команды `/status`, `/queue`, `/new` и `/restart_service` работают как раньше.
|
||||
|
||||
- ожидаемый результат:
|
||||
Удаление Java-кода не влияет на текущую работу Python-сервиса агента-кодера.
|
||||
|
||||
- статус:
|
||||
`pending`
|
||||
@ -1,23 +0,0 @@
|
||||
# Групповой чат агента-кодера
|
||||
|
||||
## Что сделано
|
||||
- Сервис `SHiNE-agent-bot-coder` теперь сохраняет сообщения участников обычной Telegram-группы и supergroup как контекст.
|
||||
- На сообщения других участников группы сервис отвечает в тот же чат коротким подтверждением `Получил сообщение.`, но не создаёт задачу Codex.
|
||||
- При миграции обычной группы в supergroup сервис запоминает новый `chat_id` и перенаправляет ответы туда.
|
||||
- Команды и задачи по-прежнему выполняются только от Айдара (`@aidarkc` / `@AidarKC`).
|
||||
|
||||
## Что проверять
|
||||
1. Написать сообщение от другого участника в группе `@shine_writing`.
|
||||
2. Убедиться, что бот ответил на него в группе коротким подтверждением получения.
|
||||
3. Написать задачу от Айдара в этой же группе.
|
||||
4. Убедиться, что ответ приходит в группу, а не в личные сообщения.
|
||||
5. Убедиться, что после миграции group → supergroup ответы не теряются.
|
||||
|
||||
## Ожидаемый результат
|
||||
- Чужие сообщения попадают в историю как контекст.
|
||||
- Чужие сообщения получают ACK в группе, но не попадают в очередь задач.
|
||||
- Сообщение Айдара создаёт задачу и получает ответ в актуальном supergroup-чате.
|
||||
- Ошибка Telegram `group chat was upgraded to a supergroup chat` больше не ломает отправку ответа.
|
||||
|
||||
## Статус
|
||||
pending
|
||||
365
Dev_Docs/Solana/user_pda/README.md
Normal file
365
Dev_Docs/Solana/user_pda/README.md
Normal file
@ -0,0 +1,365 @@
|
||||
# Solana user_pda: итоговый целевой формат пользовательской записи
|
||||
|
||||
Документ описывает целевой формат пользовательской PDA-записи `user_pda` для Solana-программы `shine_users`.
|
||||
|
||||
Это не формат основного блокчейна SHiNE и не документация по `AddBlock`. Основной блокчейн SHiNE описан отдельно в `Dev_Docs/Blockchain/`.
|
||||
|
||||
Статус документа: итоговый согласованный целевой формат. Текущий код Solana-модуля пока использует старый линейный формат записи; этот документ должен стать основой для изменения кода, тестов и интеграции с сервером.
|
||||
|
||||
## 1. Назначение user_pda
|
||||
|
||||
`user_pda` хранит публичное состояние пользователя в Solana:
|
||||
|
||||
- логин пользователя;
|
||||
- неизменяемые параметры создания записи;
|
||||
- корневой публичный ключ пользователя;
|
||||
- ключ устройства;
|
||||
- данные одного или нескольких пользовательских блокчейнов SHiNE;
|
||||
- серверные данные пользователя, если пользователь выступает сервером;
|
||||
- серверы доступа пользователя;
|
||||
- счетчики/лимиты;
|
||||
- подпись записи.
|
||||
|
||||
На первом этапе поддерживается один пользовательский блокчейн SHiNE, но формат блока блокчейна сразу допускает повторение таких блоков в будущем.
|
||||
|
||||
## 2. Адрес PDA
|
||||
|
||||
Адрес пользовательской PDA вычисляется по логину:
|
||||
|
||||
- seed prefix: `login=`;
|
||||
- второй seed: нормализованный логин в нижнем регистре;
|
||||
- program id: программа `shine_users`.
|
||||
|
||||
Один логин соответствует одной `user_pda`.
|
||||
|
||||
## 3. Общие правила кодирования
|
||||
|
||||
- Числа кодируются в Little Endian.
|
||||
- `u8`, `u16`, `u32`, `u64` имеют обычный фиксированный размер.
|
||||
- Публичный ключ Solana/Ed25519: 32 байта.
|
||||
- Ed25519-подпись: 64 байта.
|
||||
- SHA-256/Solana hash: 32 байта.
|
||||
- Строка переменной длины: `len: u8` + `bytes[len]` в UTF-8.
|
||||
- Arweave `tx_id`: строка переменной длины. Ожидаемая практическая длина base64url tx id - 43 байта, но формат хранит длину явно.
|
||||
- Все типизированные блоки после фиксированного заголовка начинаются с `block_type: u8` и `block_version: u8`.
|
||||
- Отдельный `block_len` у типизированных блоков не хранится: блоки парсятся по известным полям, счетчикам и строкам с `len: u8`.
|
||||
|
||||
## 4. Верхний формат записи
|
||||
|
||||
Первые 9 полей фиксированы и идут строго в указанном порядке. Это общий заголовок записи.
|
||||
|
||||
| N | Поле | Тип | Размер | Правило |
|
||||
|---|------|-----|--------|---------|
|
||||
| 1 | `magic` | bytes | 5 | Всегда `SHiNE`. |
|
||||
| 2 | `format_major` | `u8` | 1 | Для нового формата: `2`. |
|
||||
| 3 | `format_minor` | `u8` | 1 | Для первой версии нового формата: `0`. |
|
||||
| 4 | `record_len` | `u16` | 2 | Длина полезной записи от `magic` до `signature` включительно, без padding. |
|
||||
| 5 | `created_at_ms` | `u64` | 8 | Время создания записи, Unix time в миллисекундах. Не меняется. |
|
||||
| 6 | `updated_at_ms` | `u64` | 8 | Время последнего обновления записи. |
|
||||
| 7 | `record_number` | `u32` | 4 | Номер версии записи пользователя. При создании `0`, при обновлении +1. |
|
||||
| 8 | `prev_record_hash` | bytes | 32 | Хэш unsigned-части предыдущей записи. При создании 32 нулевых байта. |
|
||||
| 9 | `login` | string | `1 + len` | Логин пользователя. Не меняется. |
|
||||
|
||||
После первых 9 полей идет набор типизированных блоков:
|
||||
|
||||
```text
|
||||
UserPdaRecordV2
|
||||
- fixed_header: поля 1..9
|
||||
- blocks_count: u8
|
||||
- blocks: TypedBlock[blocks_count]
|
||||
- signature: [u8; 64]
|
||||
- padding: bytes до размера PDA, если нужен
|
||||
```
|
||||
|
||||
`blocks_count` входит в unsigned-часть записи и подписывается.
|
||||
|
||||
## 5. Типы блоков
|
||||
|
||||
Зарезервированные значения `block_type`:
|
||||
|
||||
| block_type | Блок | Назначение |
|
||||
|------------|------|------------|
|
||||
| `1` | `RootKeyBlock` | Корневой ключ пользователя. |
|
||||
| `2` | `DeviceKeyBlock` | Ключ устройства пользователя. |
|
||||
| `3` | `BlockchainRegistryBlock` | Один или несколько блокчейнов пользователя. |
|
||||
| `30` | `ServerProfileBlock` | Серверные данные пользователя. |
|
||||
| `40` | `AccessServersBlock` | Серверы доступа/relay. |
|
||||
| `50` | `TrustedStateBlock` | Счетчик trusted-связей. |
|
||||
| `255` | `ReservedBlock` | Зарезервировано, пока не используется. |
|
||||
|
||||
Правила:
|
||||
|
||||
- неизвестный `block_type` в `format_major = 2` считается ошибкой;
|
||||
- обязательные блоки: `RootKeyBlock`, `DeviceKeyBlock`, `BlockchainRegistryBlock`;
|
||||
- необязательные блоки: `ServerProfileBlock`, `AccessServersBlock`, `TrustedStateBlock`;
|
||||
- каждый обязательный блок должен встречаться ровно один раз;
|
||||
- порядок блоков в записи фиксируется для простоты проверки:
|
||||
`RootKey`, `DeviceKey`, `BlockchainRegistry`, `ServerProfile`, `AccessServers`, `TrustedState`.
|
||||
|
||||
## 6. RootKeyBlock
|
||||
|
||||
Смена `root_key` пока не проектируется и не реализуется. Блок фиксирует только стадию `0`.
|
||||
|
||||
```text
|
||||
RootKeyBlock
|
||||
- block_type: u8 = 1
|
||||
- block_version: u8 = 0
|
||||
- root_key: [u8; 32]
|
||||
```
|
||||
|
||||
Правила:
|
||||
|
||||
- при создании задается корневой публичный ключ пользователя;
|
||||
- при обновлении `root_key` должен совпадать с предыдущей записью;
|
||||
- ротация root-key будет отдельным форматом/сценарием в будущем.
|
||||
|
||||
## 7. DeviceKeyBlock
|
||||
|
||||
Смена `device_key` пока также не проектируется как отдельная ротация. В версии `0` хранится один ключ устройства.
|
||||
|
||||
```text
|
||||
DeviceKeyBlock
|
||||
- block_type: u8 = 2
|
||||
- block_version: u8 = 0
|
||||
- device_key: [u8; 32]
|
||||
```
|
||||
|
||||
Правила:
|
||||
|
||||
- при создании задается текущий публичный ключ устройства;
|
||||
- при обновлении ключ устройства может быть обновлен только если это отдельно разрешено бизнес-логикой инструкции;
|
||||
- история устройств и несколько устройств в этом формате не хранятся.
|
||||
|
||||
## 8. BlockchainRegistryBlock
|
||||
|
||||
Блок хранит данные пользовательских блокчейнов SHiNE. Сейчас используется один блокчейн, но структура сразу сделана как список.
|
||||
|
||||
```text
|
||||
BlockchainRegistryBlock
|
||||
- block_type: u8 = 3
|
||||
- block_version: u8 = 0
|
||||
- blockchain_count: u8
|
||||
- blockchain_records: BlockchainRecord[blockchain_count]
|
||||
```
|
||||
|
||||
Правила:
|
||||
|
||||
- на первом этапе `blockchain_count = 1`;
|
||||
- в будущем можно увеличить количество записей без изменения смысла `BlockchainRecord`;
|
||||
- каждый `BlockchainRecord` описывает один пользовательский SHiNE-блокчейн.
|
||||
|
||||
## 9. BlockchainRecord
|
||||
|
||||
```text
|
||||
BlockchainRecord
|
||||
- blockchain_type: u8
|
||||
- blockchain_name: string
|
||||
- blockchain_public_key: [u8; 32]
|
||||
- paid_limit_bytes: u64
|
||||
- used_bytes: u64
|
||||
- last_block_number: u32
|
||||
- last_block_hash: [u8; 32]
|
||||
- last_block_signature: [u8; 64]
|
||||
- arweave_present: u8
|
||||
- arweave_tx_id: string, только если arweave_present = 1
|
||||
```
|
||||
|
||||
`blockchain_type`:
|
||||
|
||||
| Значение | Смысл |
|
||||
|----------|-------|
|
||||
| `1` | Основной пользовательский SHiNE-блокчейн. |
|
||||
|
||||
Поля:
|
||||
|
||||
- `blockchain_name` - строковое имя пользовательского блокчейна, например `login-001`. На первом этапе для основного блокчейна пользователя используется имя вида `<login>-001`, потому что это первый блокчейн этого пользователя.
|
||||
- `blockchain_public_key` - публичный ключ блокчейна пользователя.
|
||||
- `paid_limit_bytes` - оплаченный лимит хранения/записей в байтах.
|
||||
- `used_bytes` - сколько байт уже занято в пользовательском SHiNE-блокчейне.
|
||||
- `last_block_number` - номер последнего известного блока пользовательского блокчейна.
|
||||
- `last_block_hash` - хэш последнего известного блока.
|
||||
- `last_block_signature` - подпись хэша специального сообщения о вершине блокчейна ключом `blockchain_public_key`.
|
||||
- `arweave_present` - `0`, если ссылки нет; `1`, если ссылка есть.
|
||||
- `arweave_tx_id` - Arweave transaction id, где лежит выгруженный пользовательский канал/состояние.
|
||||
|
||||
Arweave `tx_id` - обычное поле внутри записи конкретного блокчейна. Solana-программа не проверяет, что такой Arweave transaction действительно существует и содержит корректные данные; это ответственность клиента/сервера/пользователя.
|
||||
|
||||
## 10. Правила обновления BlockchainRecord
|
||||
|
||||
При обновлении записи:
|
||||
|
||||
- `blockchain_type` для существующей записи не меняется;
|
||||
- `blockchain_public_key` пока не ротируется автоматически; смена ключа требует отдельного согласованного сценария;
|
||||
- `paid_limit_bytes` может только увеличиваться или оставаться прежним;
|
||||
- при увеличении `paid_limit_bytes` пользователь платит комиссию в Solana по тарифам программы;
|
||||
- `used_bytes` может только увеличиваться или оставаться прежним;
|
||||
- `last_block_number` может только увеличиваться или оставаться прежним;
|
||||
- `used_bytes <= paid_limit_bytes`;
|
||||
- если `last_block_number` увеличился, то должны быть переданы новый `last_block_hash` и новая `last_block_signature`;
|
||||
- `last_block_signature` проверяется через Ed25519-инструкцию Solana: подпись должна соответствовать хэшу сообщения `LastBlockState` и `blockchain_public_key`;
|
||||
- `arweave_tx_id` можно добавить или заменить на новый, если пользователь выгрузил более актуальное состояние в Arweave;
|
||||
- уменьшать лимит, число блоков или занятый размер нельзя.
|
||||
|
||||
Сообщение `LastBlockState`, которое хэшируется и подписывается ключом `blockchain_public_key`:
|
||||
|
||||
```text
|
||||
LastBlockState
|
||||
- constant: bytes = "SHiNE_LAST_BLOCK"
|
||||
- login: string
|
||||
- blockchain_name: string
|
||||
- last_block_number: u32
|
||||
- last_block_hash: [u8; 32]
|
||||
- used_bytes: u64
|
||||
```
|
||||
|
||||
Алгоритм:
|
||||
|
||||
```text
|
||||
message = SHA-256(LastBlockState bytes)
|
||||
last_block_signature = Ed25519(blockchain_public_key, message)
|
||||
```
|
||||
|
||||
Причина проверки подписи `LastBlockState`: `root_key` управляет Solana-записью пользователя, а `blockchain_public_key` подтверждает состояние конкретного пользовательского блокчейна. Подписывается не голый хэш, а связка логина, имени блокчейна, номера последнего блока, хэша последнего блока и занятого размера.
|
||||
|
||||
## 11. ServerProfileBlock
|
||||
|
||||
Блок присутствует, если пользователь выступает сервером.
|
||||
|
||||
```text
|
||||
ServerProfileBlock
|
||||
- block_type: u8 = 30
|
||||
- block_version: u8 = 0
|
||||
- is_server: u8
|
||||
- server_key: [u8; 32], только если is_server = 1
|
||||
- server_address: string, только если is_server = 1
|
||||
- sync_servers_count: u8, только если is_server = 1
|
||||
- sync_servers: string[sync_servers_count], только если is_server = 1
|
||||
```
|
||||
|
||||
Правила:
|
||||
|
||||
- `is_server = 0` означает, что серверных данных нет;
|
||||
- `is_server = 1` означает, что пользователь публикует серверный профиль;
|
||||
- `sync_servers_count` максимум `32`;
|
||||
- `server_address` - строковый адрес сервера в формате, который будет отдельно закреплен на уровне приложения;
|
||||
- `sync_servers` - логины пользователей системы, через которых этот сервер пытается синхронизироваться. Solana-программа не обязана проверять, что эти логины действительно зарегистрированы как серверы.
|
||||
|
||||
## 12. AccessServersBlock
|
||||
|
||||
Блок хранит серверы доступа/relay для пользователя.
|
||||
|
||||
```text
|
||||
AccessServersBlock
|
||||
- block_type: u8 = 40
|
||||
- block_version: u8 = 0
|
||||
- access_servers_count: u8
|
||||
- access_servers: string[access_servers_count]
|
||||
```
|
||||
|
||||
Правила:
|
||||
|
||||
- блок может отсутствовать, если серверы доступа не заданы;
|
||||
- список может обновляться при изменении маршрутизации пользователя;
|
||||
- `access_servers` - логины пользователей системы, используемых как серверы доступа/relay. Solana-программа не обязана проверять, что эти логины действительно зарегистрированы как серверы;
|
||||
- точная семантика выбора сервера доступа определяется клиентской/серверной логикой SHiNE.
|
||||
|
||||
## 13. TrustedStateBlock
|
||||
|
||||
Пока trusted-логика не реализована полностью, поэтому блок хранит только счетчик.
|
||||
|
||||
```text
|
||||
TrustedStateBlock
|
||||
- block_type: u8 = 50
|
||||
- block_version: u8 = 0
|
||||
- trusted_count: u8 = 0
|
||||
```
|
||||
|
||||
Пока блок с доверенными лицами не реализуется, потому что полный формат trusted-логики еще не составлен. В будущем trusted-связи, очереди, таймеры и подтверждения должны быть вынесены в отдельный формат.
|
||||
|
||||
## 14. Подпись user_pda
|
||||
|
||||
Подписывается не вся PDA целиком, а unsigned-часть записи:
|
||||
|
||||
- от `magic` до последнего байта последнего типизированного блока включительно;
|
||||
- включая `record_len`, `blocks_count`, все заголовки блоков и тела блоков;
|
||||
- без поля `signature`;
|
||||
- без padding.
|
||||
|
||||
Алгоритм:
|
||||
|
||||
```text
|
||||
message = hash(unsigned_record_bytes)
|
||||
signature = Ed25519(root_key, message)
|
||||
```
|
||||
|
||||
Solana-программа проверяет подпись через встроенную Ed25519-инструкцию. Подписантом должен быть `root_key` из `RootKeyBlock`.
|
||||
|
||||
Смену формата подписи сейчас не трогаем.
|
||||
|
||||
## 15. Регистрация пользователя
|
||||
|
||||
При регистрации:
|
||||
|
||||
- PDA еще не должна существовать;
|
||||
- логин проходит проверку формата и login guard;
|
||||
- `record_number = 0`;
|
||||
- `prev_record_hash = 0x00...00`;
|
||||
- `created_at_ms = updated_at_ms`;
|
||||
- обязательные блоки присутствуют;
|
||||
- создается минимум один `BlockchainRecord`;
|
||||
- стартовый `paid_limit_bytes` равен стартовому бонусу плюс оплаченный дополнительный лимит;
|
||||
- `used_bytes <= paid_limit_bytes`;
|
||||
- пользователь платит регистрационную комиссию;
|
||||
- если покупается дополнительный лимит, пользователь платит комиссию за этот лимит;
|
||||
- вся unsigned-часть записи подписана `root_key`.
|
||||
|
||||
## 16. Обновление пользователя
|
||||
|
||||
При обновлении:
|
||||
|
||||
- PDA должна существовать;
|
||||
- `login`, `created_at_ms`, `root_key` не меняются;
|
||||
- `record_number = previous_record_number + 1`;
|
||||
- `prev_record_hash` равен хэшу unsigned-части предыдущей записи;
|
||||
- `updated_at_ms` обновляется;
|
||||
- unsigned-часть новой записи подписана `root_key`;
|
||||
- лимиты блокчейнов могут только увеличиваться;
|
||||
- занятый размер и номер последнего блока не могут уменьшаться;
|
||||
- при увеличении оплаченного лимита пользователь доплачивает комиссию;
|
||||
- Arweave `tx_id` может быть пустым или обновленным, но его содержимое Solana не валидирует.
|
||||
|
||||
## 17. Отличия от старого линейного формата
|
||||
|
||||
Старый формат после `login` хранил поля линейно:
|
||||
|
||||
- `root_key_status`;
|
||||
- `root_key`;
|
||||
- `blockchain_key_status`;
|
||||
- `blockchain_key`;
|
||||
- `device_key_status`;
|
||||
- `device_key`;
|
||||
- `chain_number`;
|
||||
- `balance`;
|
||||
- серверные поля;
|
||||
- access-серверы;
|
||||
- `trusted_count`;
|
||||
- `reserved`;
|
||||
- `signature`.
|
||||
|
||||
Новый целевой формат сохраняет первые 9 фиксированных полей как заголовок, но дальше переходит на типизированные блоки:
|
||||
|
||||
- ключи становятся отдельными блоками;
|
||||
- данные блокчейна становятся расширенным блоком со своим публичным ключом, лимитом, занятым размером, вершиной цепочки и Arweave `tx_id`;
|
||||
- серверные данные и access-серверы отделяются от данных блокчейна;
|
||||
- расширение формата делается добавлением новых версий блоков или новых `block_type`, а не вставкой полей в середину линейной записи.
|
||||
|
||||
## 18. Что пока не входит в формат
|
||||
|
||||
Пока не проектируем:
|
||||
|
||||
- ротацию `root_key`;
|
||||
- сложную ротацию `device_key`;
|
||||
- ротацию `blockchain_public_key`;
|
||||
- проверку содержимого Arweave transaction;
|
||||
- хранение полной истории пользовательского блокчейна внутри Solana;
|
||||
- подключение Solana-модуля к сборке/деплою основного сервера SHiNE.
|
||||
119
ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/CODEX_PORTING_GUIDE.md
Normal file
119
ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/CODEX_PORTING_GUIDE.md
Normal file
@ -0,0 +1,119 @@
|
||||
# ESP32-S3-Touch-AMOLED-2.16 Codex Guide
|
||||
|
||||
Этот файл переносится в другие проекты как готовая инструкция для Codex по этой плате.
|
||||
|
||||
## 1) Что это за плата
|
||||
|
||||
- Модель: `Waveshare ESP32-S3-Touch-AMOLED-2.16`
|
||||
- MCU: `ESP32-S3` (flash 16MB, PSRAM 8MB)
|
||||
- Экран: AMOLED, физически 480x480, углы скруглены (часть крайних пикселей может быть невидима)
|
||||
- Touch: CST92xx
|
||||
- IMU: QMI8658
|
||||
- Аудио:
|
||||
- DAC/вывод (динамик): ES8311
|
||||
- ADC/вход (микрофоны): ES7210
|
||||
|
||||
## 2) Что уже установлено в этой среде
|
||||
|
||||
- Ubuntu
|
||||
- `arduino-cli 1.4.0`
|
||||
- `esp32:esp32` core `3.3.5`
|
||||
- `esptool` из `~/.arduino15/packages/esp32/tools/esptool_py/5.1.0/esptool`
|
||||
- USB порт платы: обычно `/dev/ttyACM0`
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
arduino-cli version
|
||||
arduino-cli core list
|
||||
arduino-cli board list
|
||||
ls -l /dev/ttyACM0
|
||||
```
|
||||
|
||||
## 3) Структура подпроекта (эталон)
|
||||
|
||||
- `official-demo/` — официальный repo Waveshare (примеры+библиотеки)
|
||||
- `original-firmware/` — backup/restore заводской прошивки
|
||||
- `test-device/` — прошивки и `burn.sh`
|
||||
- `reference/` — заметки и ссылки
|
||||
|
||||
## 4) Бэкап перед любыми экспериментами
|
||||
|
||||
```bash
|
||||
cd ESP32-S3-Touch-AMOLED-2.16/original-firmware
|
||||
./backup_factory.sh
|
||||
```
|
||||
|
||||
Ожидаемый результат:
|
||||
- `factory-full-16mb.bin`
|
||||
- `factory-full-16mb.bin.sha256`
|
||||
|
||||
Восстановление:
|
||||
|
||||
```bash
|
||||
./restore_factory_backup.sh
|
||||
```
|
||||
|
||||
## 5) Деплой (прошивка) — стандарт
|
||||
|
||||
Главный скрипт:
|
||||
|
||||
```bash
|
||||
cd ESP32-S3-Touch-AMOLED-2.16/test-device
|
||||
./burn.sh <mode>
|
||||
```
|
||||
|
||||
Режимы:
|
||||
- `hello` — базовый экран
|
||||
- `widgets` — экран+touch+IMU (официальный пример)
|
||||
- `audio` — тест аудио тракта
|
||||
- `simple` — кастомный интеграционный тест (экран, touch, запись/воспроизведение, VU, tilt)
|
||||
|
||||
## 6) Как писать код под эту плату (важно)
|
||||
|
||||
1. **Экран**
|
||||
- Рабочее разрешение использовать `480x480`.
|
||||
- Не рисовать критичный текст/кнопки впритык к краю; держать safe margin (`~20px+`) из-за скругленных углов.
|
||||
- Не делать полный `fillScreen` в каждом loop: только частичные обновления (`fillRect`/локальные перерисовки), иначе мерцание.
|
||||
|
||||
2. **Touch**
|
||||
- Настройка CST:
|
||||
- `setMaxCoordinates(480, 480)`
|
||||
- `setSwapXY(true)`
|
||||
- `setMirrorXY(true, false)`
|
||||
- Обрабатывать touch по IRQ + `getPoint`.
|
||||
- После смещения UI обязательно пересчитывать hitbox кнопок.
|
||||
|
||||
3. **Аудио**
|
||||
- Для динамика инициализировать `ES8311`.
|
||||
- Для микрофона обязательно инициализировать `ES7210`; без этого запись может быть пустой.
|
||||
- Для отладки записи показывать VU/peak на экране во время `RECORD`.
|
||||
- Для быстрой проверки тракта всегда держать кнопку `BEEP` (тон), чтобы отделить проблему динамика от проблемы микрофона.
|
||||
|
||||
4. **IMU**
|
||||
- QMI8658 обновлять с ограниченной частотой (например 80–150 мс для UI-строки), чтобы не шуметь перерисовками.
|
||||
|
||||
5. **Стабильность UI**
|
||||
- Статика: рисуется один раз в setup.
|
||||
- Динамика: отдельная зона, перерисовывать только по изменению данных.
|
||||
|
||||
## 7) Рекомендуемый workflow для Codex
|
||||
|
||||
1. Проверить порт и инструменты.
|
||||
2. Если новая плата/первый запуск — сделать backup flash.
|
||||
3. Собрать и залить `simple`.
|
||||
4. Пройти ручной чек:
|
||||
- экран отображает текст без обрезки,
|
||||
- touch срабатывает по кнопкам,
|
||||
- `BEEP` слышно,
|
||||
- VU двигается во время записи,
|
||||
- `PLAY` воспроизводит записанное,
|
||||
- `Tilt` меняется при повороте.
|
||||
5. Только после этого усложнять приложение.
|
||||
|
||||
## 8) Ссылки
|
||||
|
||||
- Product page: https://www.waveshare.com/product/arduino/boards-kits/esp32-s3/esp32-s3-touch-amoled-2.16.htm
|
||||
- Docs: https://docs.waveshare.com/ESP32-S3-Touch-AMOLED-2.16
|
||||
- Arduino setup: https://docs.waveshare.com/ESP32-S3-Touch-AMOLED-2.16/Development-Environment-Setup-Arduino
|
||||
- Official examples: https://github.com/waveshareteam/ESP32-S3-Touch-AMOLED-2.16
|
||||
25
ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/README.md
Normal file
25
ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# ESP32-S3-Touch-AMOLED-2.16
|
||||
|
||||
Подпроект для Waveshare `ESP32-S3-Touch-AMOLED-2.16`.
|
||||
|
||||
Структура:
|
||||
|
||||
- `official-demo/` — официальный репозиторий примеров Waveshare
|
||||
- `original-firmware/` — резервная копия заводской прошивки
|
||||
- `test-device/` — скрипты быстрой проверки устройства
|
||||
- `reference/` — локальные заметки по документации и железу
|
||||
|
||||
Примечание по git:
|
||||
|
||||
- `official-demo/` держать как локальный внешний checkout из `https://github.com/waveshareteam/ESP32-S3-Touch-AMOLED-2.16`, в основной git его не добавлять.
|
||||
- `original-firmware/*.bin` — локальный дамп конкретной платы, в git не добавлять.
|
||||
- `.arduino-build/` и готовые `.bin/.elf/.map` — сборочные артефакты, в git не добавлять.
|
||||
|
||||
Быстрый старт:
|
||||
|
||||
1. Сделать backup текущей прошивки:
|
||||
- `cd original-firmware && ./backup_factory.sh`
|
||||
2. Залить тест экрана/тача:
|
||||
- `cd ../test-device && ./burn.sh widgets`
|
||||
3. Залить тест динамика:
|
||||
- `cd ../test-device && ./burn.sh audio`
|
||||
@ -0,0 +1,11 @@
|
||||
# Factory Firmware Backup
|
||||
|
||||
Здесь хранится полный дамп флеша текущего состояния платы.
|
||||
|
||||
Файлы:
|
||||
- `factory-full-16mb.bin` — полный дамп флеша `0x000000..0xFFFFFF`
|
||||
- `factory-full-16mb.bin.sha256` — контрольная сумма
|
||||
|
||||
Скрипты:
|
||||
- `backup_factory.sh` — снять резервную копию
|
||||
- `restore_factory_backup.sh` — восстановить резервную копию на плату
|
||||
19
ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/original-firmware/backup_factory.sh
Executable file
19
ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/original-firmware/backup_factory.sh
Executable file
@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PORT="${PORT:-/dev/ttyACM0}"
|
||||
ESPTOOL="${ESPTOOL:-$HOME/.arduino15/packages/esp32/tools/esptool_py/5.1.0/esptool}"
|
||||
BAUD="${BAUD:-921600}"
|
||||
OUT_BIN="${ROOT_DIR}/factory-full-16mb.bin"
|
||||
|
||||
echo "== Port: ${PORT}"
|
||||
echo "== Output: ${OUT_BIN}"
|
||||
echo "== Esptool: ${ESPTOOL}"
|
||||
echo "== Baud: ${BAUD}"
|
||||
|
||||
"${ESPTOOL}" --port "${PORT}" --baud "${BAUD}" read-flash 0 0x1000000 "${OUT_BIN}"
|
||||
sha256sum "${OUT_BIN}" | tee "${OUT_BIN}.sha256"
|
||||
|
||||
echo
|
||||
echo "== Backup done."
|
||||
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PORT="${PORT:-/dev/ttyACM0}"
|
||||
ESPTOOL="${ESPTOOL:-$HOME/.arduino15/packages/esp32/tools/esptool_py/5.1.0/esptool}"
|
||||
IN_BIN="${IN_BIN:-${ROOT_DIR}/factory-full-16mb.bin}"
|
||||
|
||||
if [[ ! -f "${IN_BIN}" ]]; then
|
||||
echo "Backup file not found: ${IN_BIN}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "== Port: ${PORT}"
|
||||
echo "== Input: ${IN_BIN}"
|
||||
echo "== Esptool: ${ESPTOOL}"
|
||||
|
||||
"${ESPTOOL}" --port "${PORT}" --baud 921600 write-flash 0 "${IN_BIN}"
|
||||
|
||||
echo
|
||||
echo "== Restore done."
|
||||
17
ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/reference/README.md
Normal file
17
ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/reference/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Reference Notes
|
||||
|
||||
Модель: Waveshare `ESP32-S3-Touch-AMOLED-2.16` (SKU 33969/33970)
|
||||
|
||||
Полезные страницы:
|
||||
- Product: https://www.waveshare.com/product/arduino/boards-kits/esp32-s3/esp32-s3-touch-amoled-2.16.htm
|
||||
- Docs home: https://docs.waveshare.com/ESP32-S3-Touch-AMOLED-2.16
|
||||
- Arduino setup: https://docs.waveshare.com/ESP32-S3-Touch-AMOLED-2.16/Development-Environment-Setup-Arduino
|
||||
- Official examples: https://github.com/waveshareteam/ESP32-S3-Touch-AMOLED-2.16
|
||||
|
||||
Ключевое по железу (по документации):
|
||||
- ESP32-S3R8, 16MB flash, 8MB PSRAM
|
||||
- AMOLED 2.16" 480x480
|
||||
- Capacitive touch (CST9220)
|
||||
- IMU QMI8658
|
||||
- Audio codec ES8311 (playback) и ES7210 (microphones)
|
||||
- TF slot (SD), сейчас без карты
|
||||
18
ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/README.md
Normal file
18
ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/README.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Test Device
|
||||
|
||||
Скрипт заливает официальные Arduino-примеры для быстрой проверки платы.
|
||||
|
||||
Для режимов `widgets`, `audio` и `hello` рядом должен лежать локальный checkout `official-demo/` из официального репозитория Waveshare. В основной git он не добавляется, потому что это большой внешний набор примеров, библиотек, прошивок и артефактов.
|
||||
|
||||
Режимы:
|
||||
- `widgets` — экран + touch + IMU (пример `05_LVGL_Widgets`)
|
||||
- `audio` — динамик/аудио-кодек (пример `07_ES8311`)
|
||||
- `hello` — базовый тест экрана (пример `01_HelloWorld`)
|
||||
- `simple` — простой кастомный тест: экран + touch + запись/проигрывание + наклон (IMU)
|
||||
|
||||
Запуск:
|
||||
|
||||
- `./burn.sh widgets`
|
||||
- `./burn.sh audio`
|
||||
- `./burn.sh hello`
|
||||
- `./burn.sh simple`
|
||||
51
ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/burn.sh
Executable file
51
ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/burn.sh
Executable file
@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BOARD_DIR="$(cd "${ROOT_DIR}/.." && pwd)"
|
||||
DEMO_BASE="${BOARD_DIR}/official-demo/examples/Arduino-v3.3.5"
|
||||
MODE="${1:-widgets}"
|
||||
PORT="${PORT:-/dev/ttyACM0}"
|
||||
FQBN="${FQBN:-esp32:esp32:esp32s3:USBMode=hwcdc,CDCOnBoot=cdc,UploadSpeed=921600,CPUFreq=240,FlashMode=dio,FlashSize=16M,PartitionScheme=app3M_fat9M_16MB,PSRAM=opi}"
|
||||
BUILD_DIR="${BUILD_DIR:-${ROOT_DIR}/.arduino-build/build-${MODE}}"
|
||||
OUT_DIR="${OUT_DIR:-${ROOT_DIR}/.arduino-build/out-${MODE}}"
|
||||
|
||||
case "${MODE}" in
|
||||
hello) SKETCH_DIR="${DEMO_BASE}/examples/01_HelloWorld" ;;
|
||||
widgets) SKETCH_DIR="${DEMO_BASE}/examples/05_LVGL_Widgets" ;;
|
||||
audio) SKETCH_DIR="${DEMO_BASE}/examples/07_ES8311" ;;
|
||||
simple) SKETCH_DIR="${ROOT_DIR}/simple_av_test" ;;
|
||||
*)
|
||||
echo "Unknown mode: ${MODE}" >&2
|
||||
echo "Use one of: hello, widgets, audio, simple" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "== Mode: ${MODE}"
|
||||
echo "== Sketch: ${SKETCH_DIR}"
|
||||
echo "== Port: ${PORT}"
|
||||
echo "== FQBN: ${FQBN}"
|
||||
|
||||
mkdir -p "${BUILD_DIR}" "${OUT_DIR}"
|
||||
|
||||
arduino-cli compile \
|
||||
--clean \
|
||||
--fqbn "${FQBN}" \
|
||||
--build-path "${BUILD_DIR}" \
|
||||
--output-dir "${OUT_DIR}" \
|
||||
--library "${DEMO_BASE}/libraries/GFX_Library_for_Arduino" \
|
||||
--library "${DEMO_BASE}/libraries/SensorLib" \
|
||||
--library "${DEMO_BASE}/libraries/XPowersLib" \
|
||||
--library "${DEMO_BASE}/libraries/lvgl" \
|
||||
--library "${DEMO_BASE}/libraries/Mylibrary" \
|
||||
"${SKETCH_DIR}"
|
||||
|
||||
arduino-cli upload \
|
||||
-p "${PORT}" \
|
||||
--fqbn "${FQBN}" \
|
||||
--input-dir "${OUT_DIR}" \
|
||||
"${SKETCH_DIR}"
|
||||
|
||||
echo
|
||||
echo "== Done."
|
||||
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _AUDIO_HAL_H_
|
||||
#define _AUDIO_HAL_H_
|
||||
|
||||
#define AUDIO_HAL_VOL_DEFAULT 60
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Select media hal codec mode
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_HAL_CODEC_MODE_ENCODE = 1, /*!< select adc */
|
||||
AUDIO_HAL_CODEC_MODE_DECODE, /*!< select dac */
|
||||
AUDIO_HAL_CODEC_MODE_BOTH, /*!< select both adc and dac */
|
||||
AUDIO_HAL_CODEC_MODE_LINE_IN, /*!< set adc channel */
|
||||
} audio_hal_codec_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Select adc channel for input mic signal
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_HAL_ADC_INPUT_LINE1 = 0x00, /*!< mic input to adc channel 1 */
|
||||
AUDIO_HAL_ADC_INPUT_LINE2, /*!< mic input to adc channel 2 */
|
||||
AUDIO_HAL_ADC_INPUT_ALL, /*!< mic input to both channels of adc */
|
||||
AUDIO_HAL_ADC_INPUT_DIFFERENCE, /*!< mic input to adc difference channel */
|
||||
} audio_hal_adc_input_t;
|
||||
|
||||
/**
|
||||
* @brief Select channel for dac output
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_HAL_DAC_OUTPUT_LINE1 = 0x00, /*!< dac output signal to channel 1 */
|
||||
AUDIO_HAL_DAC_OUTPUT_LINE2, /*!< dac output signal to channel 2 */
|
||||
AUDIO_HAL_DAC_OUTPUT_ALL, /*!< dac output signal to both channels */
|
||||
} audio_hal_dac_output_t;
|
||||
|
||||
/**
|
||||
* @brief Select operating mode i.e. start or stop for audio codec chip
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_HAL_CTRL_STOP = 0x00, /*!< set stop mode */
|
||||
AUDIO_HAL_CTRL_START = 0x01, /*!< set start mode */
|
||||
} audio_hal_ctrl_t;
|
||||
|
||||
/**
|
||||
* @brief Select I2S interface operating mode i.e. master or slave for audio codec chip
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_HAL_MODE_SLAVE = 0x00, /*!< set slave mode */
|
||||
AUDIO_HAL_MODE_MASTER = 0x01, /*!< set master mode */
|
||||
} audio_hal_iface_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Select I2S interface samples per second
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_HAL_08K_SAMPLES, /*!< set to 8k samples per second */
|
||||
AUDIO_HAL_11K_SAMPLES, /*!< set to 11.025k samples per second */
|
||||
AUDIO_HAL_16K_SAMPLES, /*!< set to 16k samples in per second */
|
||||
AUDIO_HAL_22K_SAMPLES, /*!< set to 22.050k samples per second */
|
||||
AUDIO_HAL_24K_SAMPLES, /*!< set to 24k samples in per second */
|
||||
AUDIO_HAL_32K_SAMPLES, /*!< set to 32k samples in per second */
|
||||
AUDIO_HAL_44K_SAMPLES, /*!< set to 44.1k samples per second */
|
||||
AUDIO_HAL_48K_SAMPLES, /*!< set to 48k samples per second */
|
||||
} audio_hal_iface_samples_t;
|
||||
|
||||
/**
|
||||
* @brief Select I2S interface number of bits per sample
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_HAL_BIT_LENGTH_16BITS = 1, /*!< set 16 bits per sample */
|
||||
AUDIO_HAL_BIT_LENGTH_24BITS, /*!< set 24 bits per sample */
|
||||
AUDIO_HAL_BIT_LENGTH_32BITS, /*!< set 32 bits per sample */
|
||||
} audio_hal_iface_bits_t;
|
||||
|
||||
/**
|
||||
* @brief Select I2S interface format for audio codec chip
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_HAL_I2S_NORMAL = 0, /*!< set normal I2S format */
|
||||
AUDIO_HAL_I2S_LEFT, /*!< set all left format */
|
||||
AUDIO_HAL_I2S_RIGHT, /*!< set all right format */
|
||||
AUDIO_HAL_I2S_DSP, /*!< set dsp/pcm format */
|
||||
} audio_hal_iface_format_t;
|
||||
|
||||
/**
|
||||
* @brief I2s interface configuration for audio codec chip
|
||||
*/
|
||||
typedef struct {
|
||||
audio_hal_iface_mode_t mode; /*!< audio codec chip mode */
|
||||
audio_hal_iface_format_t fmt; /*!< I2S interface format */
|
||||
audio_hal_iface_samples_t samples; /*!< I2S interface samples per second */
|
||||
audio_hal_iface_bits_t bits; /*!< i2s interface number of bits per sample */
|
||||
} audio_hal_codec_i2s_iface_t;
|
||||
|
||||
/**
|
||||
* @brief Configure media hal for initialization of audio codec chip
|
||||
*/
|
||||
typedef struct {
|
||||
audio_hal_adc_input_t adc_input; /*!< set adc channel */
|
||||
audio_hal_dac_output_t dac_output; /*!< set dac channel */
|
||||
audio_hal_codec_mode_t codec_mode; /*!< select codec mode: adc, dac or both */
|
||||
audio_hal_codec_i2s_iface_t i2s_iface; /*!< set I2S interface configuration */
|
||||
} audio_hal_codec_config_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //__AUDIO_HAL_H__
|
||||
@ -0,0 +1,549 @@
|
||||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2021 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
#include <Wire.h>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "es7210.h"
|
||||
|
||||
|
||||
#define I2S_DSP_MODE_A 0
|
||||
#define MCLK_DIV_FRE 256
|
||||
|
||||
|
||||
#define ES7210_MCLK_SOURCE FROM_CLOCK_DOUBLE_PIN /* In master mode, 0 : MCLK from pad 1 : MCLK from clock doubler */
|
||||
#define FROM_PAD_PIN 0
|
||||
#define FROM_CLOCK_DOUBLE_PIN 1
|
||||
|
||||
|
||||
static TwoWire *es7210wire;
|
||||
static es7210_gain_value_t gain;
|
||||
|
||||
/*
|
||||
* Clock coefficient structer
|
||||
*/
|
||||
struct _coeff_div_es7210 {
|
||||
uint32_t mclk; /* mclk frequency */
|
||||
uint32_t lrck; /* lrck */
|
||||
uint8_t ss_ds;
|
||||
uint8_t adc_div; /* adcclk divider */
|
||||
uint8_t dll; /* dll_bypass */
|
||||
uint8_t doubler; /* doubler enable */
|
||||
uint8_t osr; /* adc osr */
|
||||
uint8_t mclk_src; /* select mclk source */
|
||||
uint32_t lrck_h; /* The high 4 bits of lrck */
|
||||
uint32_t lrck_l; /* The low 8 bits of lrck */
|
||||
};
|
||||
|
||||
static const char *TAG = "ES7210";
|
||||
|
||||
static es7210_input_mics_t mic_select = (es7210_input_mics_t)(ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2 | ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4);
|
||||
|
||||
/* Codec hifi mclk clock divider coefficients
|
||||
* MEMBER REG
|
||||
* mclk: 0x03
|
||||
* lrck: standard
|
||||
* ss_ds: --
|
||||
* adc_div: 0x02
|
||||
* dll: 0x06
|
||||
* doubler: 0x02
|
||||
* osr: 0x07
|
||||
* mclk_src: 0x03
|
||||
* lrckh: 0x04
|
||||
* lrckl: 0x05
|
||||
*/
|
||||
static const struct _coeff_div_es7210 coeff_div[] = {
|
||||
//mclk lrck ss_ds adc_div dll doubler osr mclk_src lrckh lrckl
|
||||
/* 8k */
|
||||
{12288000, 8000 , 0x00, 0x03, 0x01, 0x00, 0x20, 0x00, 0x06, 0x00},
|
||||
{16384000, 8000 , 0x00, 0x04, 0x01, 0x00, 0x20, 0x00, 0x08, 0x00},
|
||||
{19200000, 8000 , 0x00, 0x1e, 0x00, 0x01, 0x28, 0x00, 0x09, 0x60},
|
||||
{4096000, 8000 , 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
|
||||
|
||||
/* 11.025k */
|
||||
{11289600, 11025, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00},
|
||||
|
||||
/* 12k */
|
||||
{12288000, 12000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00},
|
||||
{19200000, 12000, 0x00, 0x14, 0x00, 0x01, 0x28, 0x00, 0x06, 0x40},
|
||||
|
||||
/* 16k */
|
||||
{4096000, 16000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
|
||||
{19200000, 16000, 0x00, 0x0a, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x80},
|
||||
{16384000, 16000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00},
|
||||
{12288000, 16000, 0x00, 0x03, 0x01, 0x01, 0x20, 0x00, 0x03, 0x00},
|
||||
|
||||
/* 22.05k */
|
||||
{11289600, 22050, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
|
||||
|
||||
/* 24k */
|
||||
{12288000, 24000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
|
||||
{19200000, 24000, 0x00, 0x0a, 0x00, 0x01, 0x28, 0x00, 0x03, 0x20},
|
||||
|
||||
/* 32k */
|
||||
{12288000, 32000, 0x00, 0x03, 0x00, 0x00, 0x20, 0x00, 0x01, 0x80},
|
||||
{16384000, 32000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
|
||||
{19200000, 32000, 0x00, 0x05, 0x00, 0x00, 0x1e, 0x00, 0x02, 0x58},
|
||||
|
||||
/* 44.1k */
|
||||
{11289600, 44100, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
|
||||
|
||||
/* 48k */
|
||||
{12288000, 48000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
|
||||
{19200000, 48000, 0x00, 0x05, 0x00, 0x01, 0x28, 0x00, 0x01, 0x90},
|
||||
|
||||
/* 64k */
|
||||
{16384000, 64000, 0x01, 0x01, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00},
|
||||
{19200000, 64000, 0x00, 0x05, 0x00, 0x01, 0x1e, 0x00, 0x01, 0x2c},
|
||||
|
||||
/* 88.2k */
|
||||
{11289600, 88200, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80},
|
||||
|
||||
/* 96k */
|
||||
{12288000, 96000, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80},
|
||||
{19200000, 96000, 0x01, 0x05, 0x00, 0x01, 0x28, 0x00, 0x00, 0xc8},
|
||||
};
|
||||
|
||||
static esp_err_t es7210_write_reg(uint8_t reg_addr, uint8_t data)
|
||||
{
|
||||
|
||||
es7210wire->beginTransmission(ES7210_ADDR);
|
||||
es7210wire->write(reg_addr);
|
||||
es7210wire->write(data);
|
||||
return es7210wire->endTransmission();
|
||||
|
||||
}
|
||||
|
||||
static esp_err_t es7210_update_reg_bit(uint8_t reg_addr, uint8_t update_bits, uint8_t data)
|
||||
{
|
||||
uint8_t regv;
|
||||
regv = es7210_read_reg(reg_addr);
|
||||
regv = (regv & (~update_bits)) | (update_bits & data);
|
||||
return es7210_write_reg(reg_addr, regv);
|
||||
}
|
||||
|
||||
static int get_coeff(uint32_t mclk, uint32_t lrck)
|
||||
{
|
||||
for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) {
|
||||
if (coeff_div[i].lrck == lrck && coeff_div[i].mclk == mclk)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int8_t get_es7210_mclk_src(void)
|
||||
{
|
||||
return ES7210_MCLK_SOURCE;
|
||||
}
|
||||
|
||||
int es7210_read_reg(uint8_t reg_addr)
|
||||
{
|
||||
uint8_t data;
|
||||
es7210wire->beginTransmission(ES7210_ADDR);
|
||||
es7210wire->write(reg_addr);
|
||||
es7210wire->endTransmission(false);
|
||||
es7210wire->requestFrom(ES7210_ADDR, (size_t)1);
|
||||
data = es7210wire->read();
|
||||
return (int)data;
|
||||
}
|
||||
|
||||
esp_err_t es7210_config_sample(audio_hal_iface_samples_t sample)
|
||||
{
|
||||
uint8_t regv;
|
||||
int coeff;
|
||||
int sample_fre = 0;
|
||||
int mclk_fre = 0;
|
||||
esp_err_t ret = ESP_OK;
|
||||
switch (sample) {
|
||||
case AUDIO_HAL_08K_SAMPLES:
|
||||
sample_fre = 8000;
|
||||
break;
|
||||
case AUDIO_HAL_11K_SAMPLES:
|
||||
sample_fre = 11025;
|
||||
break;
|
||||
case AUDIO_HAL_16K_SAMPLES:
|
||||
sample_fre = 16000;
|
||||
break;
|
||||
case AUDIO_HAL_22K_SAMPLES:
|
||||
sample_fre = 22050;
|
||||
break;
|
||||
case AUDIO_HAL_24K_SAMPLES:
|
||||
sample_fre = 24000;
|
||||
break;
|
||||
case AUDIO_HAL_32K_SAMPLES:
|
||||
sample_fre = 32000;
|
||||
break;
|
||||
case AUDIO_HAL_44K_SAMPLES:
|
||||
sample_fre = 44100;
|
||||
break;
|
||||
case AUDIO_HAL_48K_SAMPLES:
|
||||
sample_fre = 48000;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Unable to configure sample rate %dHz", sample_fre);
|
||||
break;
|
||||
}
|
||||
mclk_fre = sample_fre * MCLK_DIV_FRE;
|
||||
coeff = get_coeff(mclk_fre, sample_fre);
|
||||
if (coeff < 0) {
|
||||
ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_fre, mclk_fre);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* Set clock parammeters */
|
||||
if (coeff >= 0) {
|
||||
/* Set adc_div & doubler & dll */
|
||||
regv = es7210_read_reg(ES7210_MAINCLK_REG02) & 0x00;
|
||||
regv |= coeff_div[coeff].adc_div;
|
||||
regv |= coeff_div[coeff].doubler << 6;
|
||||
regv |= coeff_div[coeff].dll << 7;
|
||||
ret |= es7210_write_reg(ES7210_MAINCLK_REG02, regv);
|
||||
/* Set osr */
|
||||
regv = coeff_div[coeff].osr;
|
||||
ret |= es7210_write_reg(ES7210_OSR_REG07, regv);
|
||||
/* Set lrck */
|
||||
regv = coeff_div[coeff].lrck_h;
|
||||
ret |= es7210_write_reg(ES7210_LRCK_DIVH_REG04, regv);
|
||||
regv = coeff_div[coeff].lrck_l;
|
||||
ret |= es7210_write_reg(ES7210_LRCK_DIVL_REG05, regv);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_mic_select(es7210_input_mics_t mic)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
mic_select = mic;
|
||||
if (mic_select & (ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2 | ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4)) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43 + i, 0x10, 0x00);
|
||||
}
|
||||
ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B, 0xff);
|
||||
ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0xff);
|
||||
if (mic_select & ES7210_INPUT_MIC1) {
|
||||
ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC1");
|
||||
ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00);
|
||||
ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B, 0x00);
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43, 0x10, 0x10);
|
||||
}
|
||||
if (mic_select & ES7210_INPUT_MIC2) {
|
||||
ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC2");
|
||||
ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00);
|
||||
ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B, 0x00);
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC2_GAIN_REG44, 0x10, 0x10);
|
||||
}
|
||||
if (mic_select & ES7210_INPUT_MIC3) {
|
||||
ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC3");
|
||||
ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x15, 0x00);
|
||||
ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0x00);
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC3_GAIN_REG45, 0x10, 0x10);
|
||||
}
|
||||
if (mic_select & ES7210_INPUT_MIC4) {
|
||||
ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC4");
|
||||
ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x15, 0x00);
|
||||
ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0x00);
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC4_GAIN_REG46, 0x10, 0x10);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Microphone selection error");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_adc_init(TwoWire *tw, audio_hal_codec_config_t *codec_cfg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
es7210wire = tw;
|
||||
|
||||
ret |= es7210_write_reg(ES7210_RESET_REG00, 0xff);
|
||||
ret |= es7210_write_reg(ES7210_RESET_REG00, 0x41);
|
||||
ret |= es7210_write_reg(ES7210_CLOCK_OFF_REG01, 0x1f);
|
||||
ret |= es7210_write_reg(ES7210_TIME_CONTROL0_REG09, 0x30); /* Set chip state cycle */
|
||||
ret |= es7210_write_reg(ES7210_TIME_CONTROL1_REG0A, 0x30); /* Set power on state cycle */
|
||||
// ret |= es7210_write_reg(ES7210_ADC12_HPF2_REG23, 0x2a); /* Quick setup */
|
||||
// ret |= es7210_write_reg(ES7210_ADC12_HPF1_REG22, 0x0a);
|
||||
// ret |= es7210_write_reg(ES7210_ADC34_HPF2_REG20, 0x0a);
|
||||
// ret |= es7210_write_reg(ES7210_ADC34_HPF1_REG21, 0x2a);
|
||||
/* Set master/slave audio interface */
|
||||
audio_hal_codec_i2s_iface_t *i2s_cfg = & (codec_cfg->i2s_iface);
|
||||
switch (i2s_cfg->mode) {
|
||||
case AUDIO_HAL_MODE_MASTER: /* MASTER MODE */
|
||||
ESP_LOGI(TAG, "ES7210 in Master mode");
|
||||
// ret |= es7210_update_reg_bit(ES7210_MODE_CONFIG_REG08, 0x01, 0x01);
|
||||
ret |= es7210_write_reg(ES7210_MODE_CONFIG_REG08, 0x20);
|
||||
/* Select clock source for internal mclk */
|
||||
switch (get_es7210_mclk_src()) {
|
||||
case FROM_PAD_PIN:
|
||||
ret |= es7210_update_reg_bit(ES7210_MASTER_CLK_REG03, 0x80, 0x00);
|
||||
break;
|
||||
case FROM_CLOCK_DOUBLE_PIN:
|
||||
ret |= es7210_update_reg_bit(ES7210_MASTER_CLK_REG03, 0x80, 0x80);
|
||||
break;
|
||||
default:
|
||||
ret |= es7210_update_reg_bit(ES7210_MASTER_CLK_REG03, 0x80, 0x00);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AUDIO_HAL_MODE_SLAVE: /* SLAVE MODE */
|
||||
ESP_LOGI(TAG, "ES7210 in Slave mode");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ret |= es7210_write_reg(ES7210_ANALOG_REG40, 0xC3); /* Select power off analog, vdda = 3.3V, close vx20ff, VMID select 5KΩ start */
|
||||
ret |= es7210_write_reg(ES7210_MIC12_BIAS_REG41, 0x70); /* Select 2.87v */
|
||||
ret |= es7210_write_reg(ES7210_MIC34_BIAS_REG42, 0x70); /* Select 2.87v */
|
||||
ret |= es7210_write_reg(ES7210_OSR_REG07, 0x20);
|
||||
ret |= es7210_write_reg(ES7210_MAINCLK_REG02, 0xc1); /* Set the frequency division coefficient and use dll except clock doubler, and need to set 0xc1 to clear the state */
|
||||
ret |= es7210_config_sample(i2s_cfg->samples);
|
||||
ret |= es7210_mic_select(mic_select);
|
||||
ret |= es7210_adc_set_gain_all(GAIN_0DB);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t es7210_adc_deinit()
|
||||
{
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t es7210_config_fmt(audio_hal_iface_format_t fmt)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint8_t adc_iface = 0;
|
||||
adc_iface = es7210_read_reg(ES7210_SDP_INTERFACE1_REG11);
|
||||
adc_iface &= 0xfc;
|
||||
switch (fmt) {
|
||||
case AUDIO_HAL_I2S_NORMAL:
|
||||
ESP_LOGD(TAG, "ES7210 in I2S Format");
|
||||
adc_iface |= 0x00;
|
||||
break;
|
||||
case AUDIO_HAL_I2S_LEFT:
|
||||
case AUDIO_HAL_I2S_RIGHT:
|
||||
ESP_LOGD(TAG, "ES7210 in LJ Format");
|
||||
adc_iface |= 0x01;
|
||||
break;
|
||||
case AUDIO_HAL_I2S_DSP:
|
||||
if (I2S_DSP_MODE_A) {
|
||||
ESP_LOGD(TAG, "ES7210 in DSP-A Format");
|
||||
adc_iface |= 0x03;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "ES7210 in DSP-B Format");
|
||||
adc_iface |= 0x13;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
adc_iface &= 0xfc;
|
||||
break;
|
||||
}
|
||||
ret |= es7210_write_reg(ES7210_SDP_INTERFACE1_REG11, adc_iface);
|
||||
/* Force ADC1/2 output to SDOUT1 and ADC3/4 output to SDOUT2 */
|
||||
ret |= es7210_write_reg(ES7210_SDP_INTERFACE2_REG12, 0x00);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_set_bits(audio_hal_iface_bits_t bits)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint8_t adc_iface = 0;
|
||||
adc_iface = es7210_read_reg(ES7210_SDP_INTERFACE1_REG11);
|
||||
adc_iface &= 0x1f;
|
||||
switch (bits) {
|
||||
case AUDIO_HAL_BIT_LENGTH_16BITS:
|
||||
adc_iface |= 0x60;
|
||||
break;
|
||||
case AUDIO_HAL_BIT_LENGTH_24BITS:
|
||||
adc_iface |= 0x00;
|
||||
break;
|
||||
case AUDIO_HAL_BIT_LENGTH_32BITS:
|
||||
adc_iface |= 0x80;
|
||||
break;
|
||||
default:
|
||||
adc_iface |= 0x60;
|
||||
break;
|
||||
}
|
||||
ret |= es7210_write_reg(ES7210_SDP_INTERFACE1_REG11, adc_iface);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_adc_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret |= es7210_set_bits(iface->bits);
|
||||
ret |= es7210_config_fmt(iface->fmt);
|
||||
ret |= es7210_config_sample(iface->samples);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_start(uint8_t clock_reg_value)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret |= es7210_write_reg(ES7210_CLOCK_OFF_REG01, clock_reg_value);
|
||||
ret |= es7210_write_reg(ES7210_POWER_DOWN_REG06, 0x00);
|
||||
// ret |= es7210_write_reg(ES7210_ANALOG_REG40, 0x40);
|
||||
ret |= es7210_write_reg(ES7210_MIC1_POWER_REG47, 0x00);
|
||||
ret |= es7210_write_reg(ES7210_MIC2_POWER_REG48, 0x00);
|
||||
ret |= es7210_write_reg(ES7210_MIC3_POWER_REG49, 0x00);
|
||||
ret |= es7210_write_reg(ES7210_MIC4_POWER_REG4A, 0x00);
|
||||
ret |= es7210_mic_select(mic_select);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_stop(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret |= es7210_write_reg(ES7210_MIC1_POWER_REG47, 0xff);
|
||||
ret |= es7210_write_reg(ES7210_MIC2_POWER_REG48, 0xff);
|
||||
ret |= es7210_write_reg(ES7210_MIC3_POWER_REG49, 0xff);
|
||||
ret |= es7210_write_reg(ES7210_MIC4_POWER_REG4A, 0xff);
|
||||
ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B,0xff);
|
||||
ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0xff);
|
||||
// ret |= es7210_write_reg(ES7210_ANALOG_REG40, 0xc0);
|
||||
ret |= es7210_write_reg(ES7210_CLOCK_OFF_REG01, 0x7f);
|
||||
ret |= es7210_write_reg(ES7210_POWER_DOWN_REG06, 0x07);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_adc_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state)
|
||||
{
|
||||
static uint8_t regv;
|
||||
esp_err_t ret = ESP_OK;
|
||||
// ESP_LOGW(TAG, "ES7210 only supports ADC mode");
|
||||
ret = es7210_read_reg(ES7210_CLOCK_OFF_REG01);
|
||||
if ((ret != 0x7f) && (ret != 0xff)) {
|
||||
regv = es7210_read_reg(ES7210_CLOCK_OFF_REG01);
|
||||
}
|
||||
if (ctrl_state == AUDIO_HAL_CTRL_START) {
|
||||
ESP_LOGI(TAG, "The ES7210_CLOCK_OFF_REG01 value before stop is %x",regv);
|
||||
ret |= es7210_start(regv);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "The codec is about to stop");
|
||||
regv = es7210_read_reg(ES7210_CLOCK_OFF_REG01);
|
||||
ret |= es7210_stop();
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t es7210_adc_set_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t gain)
|
||||
{
|
||||
esp_err_t ret_val = ESP_OK;
|
||||
|
||||
if (gain < GAIN_0DB) {
|
||||
gain = GAIN_0DB;
|
||||
}
|
||||
|
||||
if (gain > GAIN_37_5DB) {
|
||||
gain = GAIN_37_5DB;
|
||||
}
|
||||
|
||||
if (mic_mask & ES7210_INPUT_MIC1) {
|
||||
ret_val |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43, 0x0f, gain);
|
||||
}
|
||||
if (mic_mask & ES7210_INPUT_MIC2) {
|
||||
ret_val |= es7210_update_reg_bit(ES7210_MIC2_GAIN_REG44, 0x0f, gain);
|
||||
}
|
||||
if (mic_mask & ES7210_INPUT_MIC3) {
|
||||
ret_val |= es7210_update_reg_bit(ES7210_MIC3_GAIN_REG45, 0x0f, gain);
|
||||
}
|
||||
if (mic_mask & ES7210_INPUT_MIC4) {
|
||||
ret_val |= es7210_update_reg_bit(ES7210_MIC4_GAIN_REG46, 0x0f, gain);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
esp_err_t es7210_adc_set_gain_all(es7210_gain_value_t gain)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint32_t max_gain_vaule = 14;
|
||||
if (gain < 0) {
|
||||
gain = (es7210_gain_value_t) 0;
|
||||
} else if (gain > max_gain_vaule) {
|
||||
gain = (es7210_gain_value_t) max_gain_vaule;
|
||||
}
|
||||
ESP_LOGD(TAG, "SET: gain:%d", gain);
|
||||
if (mic_select & ES7210_INPUT_MIC1) {
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43, 0x0f, gain);
|
||||
}
|
||||
if (mic_select & ES7210_INPUT_MIC2) {
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC2_GAIN_REG44, 0x0f, gain);
|
||||
}
|
||||
if (mic_select & ES7210_INPUT_MIC3) {
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC3_GAIN_REG45, 0x0f, gain);
|
||||
}
|
||||
if (mic_select & ES7210_INPUT_MIC4) {
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC4_GAIN_REG46, 0x0f, gain);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_adc_get_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t *gain)
|
||||
{
|
||||
int regv = 0;
|
||||
uint8_t gain_value;
|
||||
if (mic_mask & ES7210_INPUT_MIC1) {
|
||||
regv = es7210_read_reg(ES7210_MIC1_GAIN_REG43);
|
||||
} else if (mic_mask & ES7210_INPUT_MIC2) {
|
||||
regv = es7210_read_reg(ES7210_MIC2_GAIN_REG44);
|
||||
} else if (mic_mask & ES7210_INPUT_MIC3) {
|
||||
regv = es7210_read_reg(ES7210_MIC3_GAIN_REG45);
|
||||
} else if (mic_mask & ES7210_INPUT_MIC4) {
|
||||
regv = es7210_read_reg(ES7210_MIC4_GAIN_REG46);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No MIC selected");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (regv == ESP_FAIL) {
|
||||
return regv;
|
||||
}
|
||||
gain_value = (regv & 0x0f); /* Retain the last four bits for gain */
|
||||
*gain = (es7210_gain_value_t) gain_value;
|
||||
ESP_LOGI(TAG, "GET: gain_value:%d", gain_value);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t es7210_adc_set_volume(int volume)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_LOGD(TAG, "ADC can adjust gain");
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_set_mute(bool enable)
|
||||
{
|
||||
ESP_LOGD(TAG, "ES7210 SetMute :%d", enable);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void es7210_read_all(void)
|
||||
{
|
||||
for (int i = 0; i <= 0x4E; i++) {
|
||||
uint8_t reg = es7210_read_reg(i);
|
||||
ets_printf("REG:%02x, %02x\n", reg, i);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,260 @@
|
||||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2021 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _ES7210_H
|
||||
#define _ES7210_H
|
||||
|
||||
#include "audio_hal.h"
|
||||
#include <Wire.h>
|
||||
|
||||
typedef enum {
|
||||
ES7210_AD1_AD0_00 = 0x40,
|
||||
ES7210_AD1_AD0_01 = 0x41,
|
||||
ES7210_AD1_AD0_10 = 0x42,
|
||||
ES7210_AD1_AD0_11 = 0x43,
|
||||
} es7210_address_t;
|
||||
|
||||
/* ES7210 address*/
|
||||
#define ES7210_ADDR ES7210_AD1_AD0_00
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ES7210_RESET_REG00 0x00 /* Reset control */
|
||||
#define ES7210_CLOCK_OFF_REG01 0x01 /* Used to turn off the ADC clock */
|
||||
#define ES7210_MAINCLK_REG02 0x02 /* Set ADC clock frequency division */
|
||||
#define ES7210_MASTER_CLK_REG03 0x03 /* MCLK source $ SCLK division */
|
||||
#define ES7210_LRCK_DIVH_REG04 0x04 /* lrck_divh */
|
||||
#define ES7210_LRCK_DIVL_REG05 0x05 /* lrck_divl */
|
||||
#define ES7210_POWER_DOWN_REG06 0x06 /* power down */
|
||||
#define ES7210_OSR_REG07 0x07
|
||||
#define ES7210_MODE_CONFIG_REG08 0x08 /* Set master/slave & channels */
|
||||
#define ES7210_TIME_CONTROL0_REG09 0x09 /* Set Chip intial state period*/
|
||||
#define ES7210_TIME_CONTROL1_REG0A 0x0A /* Set Power up state period */
|
||||
#define ES7210_SDP_INTERFACE1_REG11 0x11 /* Set sample & fmt */
|
||||
#define ES7210_SDP_INTERFACE2_REG12 0x12 /* Pins state */
|
||||
#define ES7210_ADC_AUTOMUTE_REG13 0x13 /* Set mute */
|
||||
#define ES7210_ADC34_MUTERANGE_REG14 0x14 /* Set mute range */
|
||||
#define ES7210_ADC34_HPF2_REG20 0x20 /* HPF */
|
||||
#define ES7210_ADC34_HPF1_REG21 0x21
|
||||
#define ES7210_ADC12_HPF1_REG22 0x22
|
||||
#define ES7210_ADC12_HPF2_REG23 0x23
|
||||
#define ES7210_ANALOG_REG40 0x40 /* ANALOG Power */
|
||||
#define ES7210_MIC12_BIAS_REG41 0x41
|
||||
#define ES7210_MIC34_BIAS_REG42 0x42
|
||||
#define ES7210_MIC1_GAIN_REG43 0x43
|
||||
#define ES7210_MIC2_GAIN_REG44 0x44
|
||||
#define ES7210_MIC3_GAIN_REG45 0x45
|
||||
#define ES7210_MIC4_GAIN_REG46 0x46
|
||||
#define ES7210_MIC1_POWER_REG47 0x47
|
||||
#define ES7210_MIC2_POWER_REG48 0x48
|
||||
#define ES7210_MIC3_POWER_REG49 0x49
|
||||
#define ES7210_MIC4_POWER_REG4A 0x4A
|
||||
#define ES7210_MIC12_POWER_REG4B 0x4B /* MICBias & ADC & PGA Power */
|
||||
#define ES7210_MIC34_POWER_REG4C 0x4C
|
||||
|
||||
|
||||
|
||||
typedef enum {
|
||||
ES7210_INPUT_MIC1 = 0x01,
|
||||
ES7210_INPUT_MIC2 = 0x02,
|
||||
ES7210_INPUT_MIC3 = 0x04,
|
||||
ES7210_INPUT_MIC4 = 0x08
|
||||
} es7210_input_mics_t;
|
||||
|
||||
typedef enum gain_value{
|
||||
GAIN_0DB = 0,
|
||||
GAIN_3DB,
|
||||
GAIN_6DB,
|
||||
GAIN_9DB,
|
||||
GAIN_12DB,
|
||||
GAIN_15DB,
|
||||
GAIN_18DB,
|
||||
GAIN_21DB,
|
||||
GAIN_24DB,
|
||||
GAIN_27DB,
|
||||
GAIN_30DB,
|
||||
GAIN_33DB,
|
||||
GAIN_34_5DB,
|
||||
GAIN_36DB,
|
||||
GAIN_37_5DB,
|
||||
} es7210_gain_value_t;
|
||||
|
||||
/*
|
||||
* @brief Initialize ES7210 ADC chip
|
||||
*
|
||||
* @param[in] codec_cfg: configuration of ES7210
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*/
|
||||
esp_err_t es7210_adc_init(TwoWire *tw, audio_hal_codec_config_t *codec_cfg);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize ES7210 ADC chip
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*/
|
||||
esp_err_t es7210_adc_deinit();
|
||||
|
||||
/**
|
||||
* @brief Configure ES7210 ADC mode and I2S interface
|
||||
*
|
||||
* @param[in] mode: codec mode
|
||||
* @param[in] iface: I2S config
|
||||
*
|
||||
* @return
|
||||
* - ESP_FAIL Parameter error
|
||||
* - ESP_OK Success
|
||||
*/
|
||||
esp_err_t es7210_adc_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface);
|
||||
|
||||
/**
|
||||
* @brief Control ES7210 ADC chip
|
||||
*
|
||||
* @param[in] mode: codec mode
|
||||
* @param[in] ctrl_state: start or stop progress
|
||||
*
|
||||
* @return
|
||||
* - ESP_FAIL Parameter error
|
||||
* - ESP_OK Success
|
||||
*/
|
||||
esp_err_t es7210_adc_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state);
|
||||
|
||||
/**
|
||||
* @brief Set gain of given mask
|
||||
*
|
||||
* @param[in] mic_mask Mask of MIC channel
|
||||
*
|
||||
* @param[in] gain: gain
|
||||
*
|
||||
* gain : value
|
||||
* GAIN_0DB : 1
|
||||
* GAIN_3DB : 2
|
||||
* GAIN_6DB : 3
|
||||
* ·
|
||||
* ·
|
||||
* ·
|
||||
* GAIN_30DB : 10
|
||||
* GAIN_33DB : 11
|
||||
* GAIN_34_5DB : 12
|
||||
* GAIN_36DB : 13
|
||||
* GAIN_37_5DB : 14
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*/
|
||||
esp_err_t es7210_adc_set_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t gain);
|
||||
|
||||
/**
|
||||
* @brief Set gain (Note: the enabled microphone sets the same gain)
|
||||
*
|
||||
* @param[in] gain: gain
|
||||
*
|
||||
* gain : value
|
||||
* GAIN_0DB : 1
|
||||
* GAIN_3DB : 2
|
||||
* GAIN_6DB : 3
|
||||
* ·
|
||||
* ·
|
||||
* ·
|
||||
* GAIN_30DB : 10
|
||||
* GAIN_33DB : 11
|
||||
* GAIN_34_5DB : 12
|
||||
* GAIN_36DB : 13
|
||||
* GAIN_37_5DB : 14
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*/
|
||||
esp_err_t es7210_adc_set_gain_all(es7210_gain_value_t gain);
|
||||
|
||||
/**
|
||||
* @brief Get MIC gain
|
||||
*
|
||||
* @param mic_mask Selected MIC
|
||||
* @param gain Pointer to `es7210_gain_value_t`
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*/
|
||||
esp_err_t es7210_adc_get_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t *gain);
|
||||
|
||||
/**
|
||||
* @brief Set volume
|
||||
*
|
||||
* @param[in] volume: volume
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
*/
|
||||
esp_err_t es7210_adc_set_volume(int volume);
|
||||
|
||||
/**
|
||||
* @brief Set ES7210 ADC mute status
|
||||
*
|
||||
* @return
|
||||
* - ESP_FAIL
|
||||
* - ESP_OK
|
||||
*/
|
||||
esp_err_t es7210_set_mute(bool enable);
|
||||
|
||||
/**
|
||||
* @brief Select ES7210 mic
|
||||
*
|
||||
* @param[in] mic: mics
|
||||
*
|
||||
* @return
|
||||
* - ESP_FAIL
|
||||
* - ESP_OK
|
||||
*/
|
||||
esp_err_t es7210_mic_select(es7210_input_mics_t mic);
|
||||
|
||||
/**
|
||||
* @brief Read regs of ES7210
|
||||
*
|
||||
* @param[in] reg_addr: reg_addr
|
||||
*
|
||||
* @return
|
||||
* - ESP_FAIL
|
||||
* - ESP_OK
|
||||
*/
|
||||
int es7210_read_reg(uint8_t reg_addr);
|
||||
|
||||
/**
|
||||
* @brief Read all regs of ES7210
|
||||
*/
|
||||
void es7210_read_all(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ES7210_H_ */
|
||||
@ -0,0 +1,443 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "es8311.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "es8311_reg.h"
|
||||
#include "esp32-hal-i2c.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
unsigned int port;
|
||||
uint16_t dev_addr;
|
||||
} es8311_dev_t;
|
||||
|
||||
/*
|
||||
* Clock coefficient structure
|
||||
*/
|
||||
struct _coeff_div {
|
||||
uint32_t mclk; /* mclk frequency */
|
||||
uint32_t rate; /* sample rate */
|
||||
uint8_t pre_div; /* the pre divider with range from 1 to 8 */
|
||||
uint8_t pre_multi; /* the pre multiplier with 0: 1x, 1: 2x, 2: 4x, 3: 8x selection */
|
||||
uint8_t adc_div; /* adcclk divider */
|
||||
uint8_t dac_div; /* dacclk divider */
|
||||
uint8_t fs_mode; /* double speed or single speed, =0, ss, =1, ds */
|
||||
uint8_t lrck_h; /* adclrck divider and daclrck divider */
|
||||
uint8_t lrck_l;
|
||||
uint8_t bclk_div; /* sclk divider */
|
||||
uint8_t adc_osr; /* adc osr */
|
||||
uint8_t dac_osr; /* dac osr */
|
||||
};
|
||||
|
||||
/* codec hifi mclk clock divider coefficients */
|
||||
static const struct _coeff_div coeff_div[] = {
|
||||
/*!<mclk rate pre_div mult adc_div dac_div fs_mode lrch lrcl bckdiv osr */
|
||||
/* 8k */
|
||||
{12288000, 8000, 0x06, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 8000, 0x03, 0x01, 0x03, 0x03, 0x00, 0x05, 0xff, 0x18, 0x10, 0x10},
|
||||
{16384000, 8000, 0x08, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{8192000, 8000, 0x04, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 8000, 0x03, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{4096000, 8000, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 8000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2048000, 8000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 8000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1024000, 8000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 11.025k */
|
||||
{11289600, 11025, 0x04, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{5644800, 11025, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2822400, 11025, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1411200, 11025, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 12k */
|
||||
{12288000, 12000, 0x04, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 12000, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 12000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 12000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 16k */
|
||||
{12288000, 16000, 0x03, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 16000, 0x03, 0x01, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10},
|
||||
{16384000, 16000, 0x04, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{8192000, 16000, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 16000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{4096000, 16000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 16000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2048000, 16000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 16000, 0x03, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1024000, 16000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 22.05k */
|
||||
{11289600, 22050, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{5644800, 22050, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2822400, 22050, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1411200, 22050, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{705600, 22050, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 24k */
|
||||
{12288000, 24000, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 24000, 0x03, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 24000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 24000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 24000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 32k */
|
||||
{12288000, 32000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 32000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10},
|
||||
{16384000, 32000, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{8192000, 32000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 32000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{4096000, 32000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 32000, 0x03, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2048000, 32000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 32000, 0x03, 0x03, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
||||
{1024000, 32000, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 44.1k */
|
||||
{11289600, 44100, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{5644800, 44100, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2822400, 44100, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1411200, 44100, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 48k */
|
||||
{12288000, 48000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 48000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 48000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 48000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 48000, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 64k */
|
||||
{12288000, 64000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 64000, 0x03, 0x02, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
|
||||
{16384000, 64000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{8192000, 64000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 64000, 0x01, 0x02, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
|
||||
{4096000, 64000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 64000, 0x01, 0x03, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
|
||||
{2048000, 64000, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 64000, 0x01, 0x03, 0x01, 0x01, 0x01, 0x00, 0xbf, 0x03, 0x18, 0x18},
|
||||
{1024000, 64000, 0x01, 0x03, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
||||
|
||||
/* 88.2k */
|
||||
{11289600, 88200, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{5644800, 88200, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2822400, 88200, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1411200, 88200, 0x01, 0x03, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
||||
|
||||
/* 96k */
|
||||
{12288000, 96000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 96000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 96000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 96000, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 96000, 0x01, 0x03, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
||||
};
|
||||
|
||||
static const char *TAG = "ES8311";
|
||||
|
||||
static inline esp_err_t es8311_write_reg(es8311_handle_t dev, uint8_t reg_addr, uint8_t data)
|
||||
{
|
||||
es8311_dev_t *es = (es8311_dev_t *) dev;
|
||||
const uint8_t write_buf[2] = {reg_addr, data};
|
||||
return i2cWrite(es->port, es->dev_addr, write_buf, sizeof(write_buf), 1000);
|
||||
}
|
||||
|
||||
static inline esp_err_t es8311_read_reg(es8311_handle_t dev, uint8_t reg_addr, uint8_t *reg_value)
|
||||
{
|
||||
es8311_dev_t *es = (es8311_dev_t *) dev;
|
||||
size_t readCount = 0;
|
||||
return i2cWriteReadNonStop(es->port, es->dev_addr, ®_addr, 1, reg_value, 1, 1000, &readCount);
|
||||
}
|
||||
|
||||
/*
|
||||
* look for the coefficient in coeff_div[] table
|
||||
*/
|
||||
static int get_coeff(uint32_t mclk, uint32_t rate)
|
||||
{
|
||||
for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) {
|
||||
if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
esp_err_t es8311_sample_frequency_config(es8311_handle_t dev, int mclk_frequency, int sample_frequency)
|
||||
{
|
||||
uint8_t regv;
|
||||
|
||||
/* Get clock coefficients from coefficient table */
|
||||
int coeff = get_coeff(mclk_frequency, sample_frequency);
|
||||
|
||||
if (coeff < 0) {
|
||||
ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_frequency, mclk_frequency);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
const struct _coeff_div *const selected_coeff = &coeff_div[coeff];
|
||||
|
||||
/* register 0x02 */
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_CLK_MANAGER_REG02, ®v), TAG, "I2C read/write error");
|
||||
regv &= 0x07;
|
||||
regv |= (selected_coeff->pre_div - 1) << 5;
|
||||
regv |= selected_coeff->pre_multi << 3;
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG02, regv), TAG, "I2C read/write error");
|
||||
|
||||
/* register 0x03 */
|
||||
const uint8_t reg03 = (selected_coeff->fs_mode << 6) | selected_coeff->adc_osr;
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG03, reg03), TAG, "I2C read/write error");
|
||||
|
||||
/* register 0x04 */
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG04, selected_coeff->dac_osr), TAG, "I2C read/write error");
|
||||
|
||||
/* register 0x05 */
|
||||
const uint8_t reg05 = ((selected_coeff->adc_div - 1) << 4) | (selected_coeff->dac_div - 1);
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG05, reg05), TAG, "I2C read/write error");
|
||||
|
||||
/* register 0x06 */
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_CLK_MANAGER_REG06, ®v), TAG, "I2C read/write error");
|
||||
regv &= 0xE0;
|
||||
|
||||
if (selected_coeff->bclk_div < 19) {
|
||||
regv |= (selected_coeff->bclk_div - 1) << 0;
|
||||
} else {
|
||||
regv |= (selected_coeff->bclk_div) << 0;
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG06, regv), TAG, "I2C read/write error");
|
||||
|
||||
/* register 0x07 */
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_CLK_MANAGER_REG07, ®v), TAG, "I2C read/write error");
|
||||
regv &= 0xC0;
|
||||
regv |= selected_coeff->lrck_h << 0;
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG07, regv), TAG, "I2C read/write error");
|
||||
|
||||
/* register 0x08 */
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG08, selected_coeff->lrck_l), TAG, "I2C read/write error");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t es8311_clock_config(es8311_handle_t dev, const es8311_clock_config_t *const clk_cfg, es8311_resolution_t res)
|
||||
{
|
||||
uint8_t reg06;
|
||||
uint8_t reg01 = 0x3F; // Enable all clocks
|
||||
int mclk_hz;
|
||||
|
||||
/* Select clock source for internal MCLK and determine its frequency */
|
||||
if (clk_cfg->mclk_from_mclk_pin) {
|
||||
mclk_hz = clk_cfg->mclk_frequency;
|
||||
} else {
|
||||
mclk_hz = clk_cfg->sample_frequency * (int)res * 2;
|
||||
reg01 |= BIT(7); // Select BCLK (a.k.a. SCK) pin
|
||||
}
|
||||
|
||||
if (clk_cfg->mclk_inverted) {
|
||||
reg01 |= BIT(6); // Invert MCLK pin
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG01, reg01), TAG, "I2C read/write error");
|
||||
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_CLK_MANAGER_REG06, ®06), TAG, "I2C read/write error");
|
||||
if (clk_cfg->sclk_inverted) {
|
||||
reg06 |= BIT(5);
|
||||
} else {
|
||||
reg06 &= ~BIT(5);
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG06, reg06), TAG, "I2C read/write error");
|
||||
|
||||
/* Configure clock dividers */
|
||||
return es8311_sample_frequency_config(dev, mclk_hz, clk_cfg->sample_frequency);
|
||||
}
|
||||
|
||||
static esp_err_t es8311_resolution_config(const es8311_resolution_t res, uint8_t *reg)
|
||||
{
|
||||
switch (res) {
|
||||
case ES8311_RESOLUTION_16:
|
||||
*reg |= (3 << 2);
|
||||
break;
|
||||
case ES8311_RESOLUTION_18:
|
||||
*reg |= (2 << 2);
|
||||
break;
|
||||
case ES8311_RESOLUTION_20:
|
||||
*reg |= (1 << 2);
|
||||
break;
|
||||
case ES8311_RESOLUTION_24:
|
||||
*reg |= (0 << 2);
|
||||
break;
|
||||
case ES8311_RESOLUTION_32:
|
||||
*reg |= (4 << 2);
|
||||
break;
|
||||
default:
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t es8311_fmt_config(es8311_handle_t dev, const es8311_resolution_t res_in, const es8311_resolution_t res_out)
|
||||
{
|
||||
uint8_t reg09 = 0; // SDP In
|
||||
uint8_t reg0a = 0; // SDP Out
|
||||
|
||||
ESP_LOGI(TAG, "ES8311 in Slave mode and I2S format");
|
||||
uint8_t reg00;
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_RESET_REG00, ®00), TAG, "I2C read/write error");
|
||||
reg00 &= 0xBF;
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_RESET_REG00, reg00), TAG, "I2C read/write error"); // Slave serial port - default
|
||||
|
||||
/* Setup SDP In and Out resolution */
|
||||
es8311_resolution_config(res_in, ®09);
|
||||
es8311_resolution_config(res_out, ®0a);
|
||||
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SDPIN_REG09, reg09), TAG, "I2C read/write error");
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SDPOUT_REG0A, reg0a), TAG, "I2C read/write error");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t es8311_microphone_config(es8311_handle_t dev, bool digital_mic)
|
||||
{
|
||||
uint8_t reg14 = 0x1A; // enable analog MIC and max PGA gain
|
||||
|
||||
/* PDM digital microphone enable or disable */
|
||||
if (digital_mic) {
|
||||
reg14 |= BIT(6);
|
||||
}
|
||||
es8311_write_reg(dev, ES8311_ADC_REG17, 0xC8); // Set ADC gain @todo move this to ADC config section
|
||||
|
||||
return es8311_write_reg(dev, ES8311_SYSTEM_REG14, reg14);
|
||||
}
|
||||
|
||||
esp_err_t es8311_init(es8311_handle_t dev, const es8311_clock_config_t *const clk_cfg, const es8311_resolution_t res_in, const es8311_resolution_t res_out)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(
|
||||
(clk_cfg->sample_frequency >= 8000) && (clk_cfg->sample_frequency <= 96000),
|
||||
ESP_ERR_INVALID_ARG, TAG, "ES8311 init needs frequency in interval [8000; 96000] Hz"
|
||||
);
|
||||
if (!clk_cfg->mclk_from_mclk_pin) {
|
||||
ESP_RETURN_ON_FALSE(res_out == res_in, ESP_ERR_INVALID_ARG, TAG, "Resolution IN/OUT must be equal if MCLK is taken from SCK pin");
|
||||
}
|
||||
|
||||
|
||||
/* Reset ES8311 to its default */
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_RESET_REG00, 0x1F), TAG, "I2C read/write error");
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_RESET_REG00, 0x00), TAG, "I2C read/write error");
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_RESET_REG00, 0x80), TAG, "I2C read/write error"); // Power-on command
|
||||
|
||||
/* Setup clock: source, polarity and clock dividers */
|
||||
ESP_RETURN_ON_ERROR(es8311_clock_config(dev, clk_cfg, res_out), TAG, "");
|
||||
|
||||
/* Setup audio format (fmt): master/slave, resolution, I2S */
|
||||
ESP_RETURN_ON_ERROR(es8311_fmt_config(dev, res_in, res_out), TAG, "");
|
||||
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SYSTEM_REG0D, 0x01), TAG, "I2C read/write error"); // Power up analog circuitry - NOT default
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SYSTEM_REG0E, 0x02), TAG, "I2C read/write error"); // Enable analog PGA, enable ADC modulator - NOT default
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SYSTEM_REG12, 0x00), TAG, "I2C read/write error"); // power-up DAC - NOT default
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SYSTEM_REG13, 0x10), TAG, "I2C read/write error"); // Enable output to HP drive - NOT default
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_ADC_REG1C, 0x6A), TAG, "I2C read/write error"); // ADC Equalizer bypass, cancel DC offset in digital domain
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_DAC_REG37, 0x08), TAG, "I2C read/write error"); // Bypass DAC equalizer - NOT default
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void es8311_delete(es8311_handle_t dev)
|
||||
{
|
||||
free(dev);
|
||||
}
|
||||
|
||||
esp_err_t es8311_voice_volume_set(es8311_handle_t dev, int volume, int *volume_set)
|
||||
{
|
||||
if (volume < 0) {
|
||||
volume = 0;
|
||||
} else if (volume > 100) {
|
||||
volume = 100;
|
||||
}
|
||||
|
||||
int reg32;
|
||||
if (volume == 0) {
|
||||
reg32 = 0;
|
||||
} else {
|
||||
reg32 = ((volume) * 256 / 100) - 1;
|
||||
}
|
||||
|
||||
// provide user with real volume set
|
||||
if (volume_set != NULL) {
|
||||
*volume_set = volume;
|
||||
}
|
||||
return es8311_write_reg(dev, ES8311_DAC_REG32, reg32);
|
||||
}
|
||||
|
||||
esp_err_t es8311_voice_volume_get(es8311_handle_t dev, int *volume)
|
||||
{
|
||||
uint8_t reg32;
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_DAC_REG32, ®32), TAG, "I2C read/write error");
|
||||
|
||||
if (reg32 == 0) {
|
||||
*volume = 0;
|
||||
} else {
|
||||
*volume = ((reg32 * 100) / 256) + 1;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t es8311_voice_mute(es8311_handle_t dev, bool mute)
|
||||
{
|
||||
uint8_t reg31;
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_DAC_REG31, ®31), TAG, "I2C read/write error");
|
||||
|
||||
if (mute) {
|
||||
reg31 |= BIT(6) | BIT(5);
|
||||
} else {
|
||||
reg31 &= ~(BIT(6) | BIT(5));
|
||||
}
|
||||
|
||||
return es8311_write_reg(dev, ES8311_DAC_REG31, reg31);
|
||||
}
|
||||
|
||||
esp_err_t es8311_microphone_gain_set(es8311_handle_t dev, es8311_mic_gain_t gain_db)
|
||||
{
|
||||
return es8311_write_reg(dev, ES8311_ADC_REG16, gain_db); // ADC gain scale up
|
||||
}
|
||||
|
||||
esp_err_t es8311_voice_fade(es8311_handle_t dev, const es8311_fade_t fade)
|
||||
{
|
||||
uint8_t reg37;
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_DAC_REG37, ®37), TAG, "I2C read/write error");
|
||||
reg37 &= 0x0F;
|
||||
reg37 |= (fade << 4);
|
||||
return es8311_write_reg(dev, ES8311_DAC_REG37, reg37);
|
||||
}
|
||||
|
||||
esp_err_t es8311_microphone_fade(es8311_handle_t dev, const es8311_fade_t fade)
|
||||
{
|
||||
uint8_t reg15;
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_ADC_REG15, ®15), TAG, "I2C read/write error");
|
||||
reg15 &= 0x0F;
|
||||
reg15 |= (fade << 4);
|
||||
return es8311_write_reg(dev, ES8311_ADC_REG15, reg15);
|
||||
}
|
||||
|
||||
void es8311_register_dump(es8311_handle_t dev)
|
||||
{
|
||||
for (int reg = 0; reg < 0x4A; reg++) {
|
||||
uint8_t value;
|
||||
ESP_ERROR_CHECK(es8311_read_reg(dev, reg, &value));
|
||||
printf("REG:%02x: %02x", reg, value);
|
||||
}
|
||||
}
|
||||
|
||||
es8311_handle_t es8311_create(const unsigned int port, const uint16_t dev_addr)
|
||||
{
|
||||
es8311_dev_t *sensor = (es8311_dev_t *) calloc(1, sizeof(es8311_dev_t));
|
||||
sensor->port = port;
|
||||
sensor->dev_addr = dev_addr;
|
||||
return (es8311_handle_t) sensor;
|
||||
}
|
||||
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ES8311 driver
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_types.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
/* ES8311 address: CE pin low - 0x18, CE pin high - 0x19 */
|
||||
#define ES8311_ADDRRES_0 0x18u // Leaving this here for backward compatibility
|
||||
#define ES8311_ADDRESS_0 0x18u
|
||||
#define ES8311_ADDRESS_1 0x19u
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void *es8311_handle_t;
|
||||
|
||||
typedef enum {
|
||||
ES8311_MIC_GAIN_MIN = -1,
|
||||
ES8311_MIC_GAIN_0DB,
|
||||
ES8311_MIC_GAIN_6DB,
|
||||
ES8311_MIC_GAIN_12DB,
|
||||
ES8311_MIC_GAIN_18DB,
|
||||
ES8311_MIC_GAIN_24DB,
|
||||
ES8311_MIC_GAIN_30DB,
|
||||
ES8311_MIC_GAIN_36DB,
|
||||
ES8311_MIC_GAIN_42DB,
|
||||
ES8311_MIC_GAIN_MAX
|
||||
} es8311_mic_gain_t;
|
||||
|
||||
typedef enum {
|
||||
ES8311_FADE_OFF = 0,
|
||||
ES8311_FADE_4LRCK, // 4LRCK means ramp 0.25dB/4LRCK
|
||||
ES8311_FADE_8LRCK,
|
||||
ES8311_FADE_16LRCK,
|
||||
ES8311_FADE_32LRCK,
|
||||
ES8311_FADE_64LRCK,
|
||||
ES8311_FADE_128LRCK,
|
||||
ES8311_FADE_256LRCK,
|
||||
ES8311_FADE_512LRCK,
|
||||
ES8311_FADE_1024LRCK,
|
||||
ES8311_FADE_2048LRCK,
|
||||
ES8311_FADE_4096LRCK,
|
||||
ES8311_FADE_8192LRCK,
|
||||
ES8311_FADE_16384LRCK,
|
||||
ES8311_FADE_32768LRCK,
|
||||
ES8311_FADE_65536LRCK
|
||||
} es8311_fade_t;
|
||||
|
||||
typedef enum es8311_resolution_t {
|
||||
ES8311_RESOLUTION_16 = 16,
|
||||
ES8311_RESOLUTION_18 = 18,
|
||||
ES8311_RESOLUTION_20 = 20,
|
||||
ES8311_RESOLUTION_24 = 24,
|
||||
ES8311_RESOLUTION_32 = 32
|
||||
} es8311_resolution_t;
|
||||
|
||||
typedef struct es8311_clock_config_t {
|
||||
bool mclk_inverted;
|
||||
bool sclk_inverted;
|
||||
bool mclk_from_mclk_pin; // true: from MCLK pin (pin no. 2), false: from SCLK pin (pin no. 6)
|
||||
int mclk_frequency; // This parameter is ignored if MCLK is taken from SCLK pin
|
||||
int sample_frequency; // in Hz
|
||||
} es8311_clock_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize ES8311
|
||||
*
|
||||
* There are two ways of providing Master Clock (MCLK) signal to ES8311 in Slave Mode:
|
||||
* 1. From MCLK pin:
|
||||
* For flexible scenarios. A clock signal from I2S master is routed to MCLK pin.
|
||||
* Its frequency must be defined in clk_cfg->mclk_frequency parameter.
|
||||
* 2. From SCLK pin:
|
||||
* For simpler scenarios. ES8311 takes its clock from SCK pin. MCLK pin does not have to be connected.
|
||||
* In this case, res_in must equal res_out; clk_cfg->mclk_frequency parameter is ignored
|
||||
* and MCLK is calculated as MCLK = clk_cfg->sample_frequency * res_out * 2.
|
||||
* Not all sampling frequencies are supported in this mode.
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
* @param[in] clk_cfg Clock configuration
|
||||
* @param[in] res_in Input serial port resolution
|
||||
* @param[in] res_out Output serial port resolution
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - ESP_ERR_INVALID_ARG Sample frequency or resolution invalid
|
||||
* - Else fail
|
||||
*/
|
||||
esp_err_t es8311_init(es8311_handle_t dev, const es8311_clock_config_t *const clk_cfg, const es8311_resolution_t res_in,
|
||||
const es8311_resolution_t res_out);
|
||||
|
||||
/**
|
||||
* @brief Set output volume
|
||||
*
|
||||
* Volume paramter out of <0, 100> interval will be truncated.
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
* @param[in] volume Set volume (0 ~ 100)
|
||||
* @param[out] volume_set Volume that was set. Same as volume, unless volume is outside of <0, 100> interval.
|
||||
* This parameter can be set to NULL, if user does not need this information.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - Else fail
|
||||
*/
|
||||
esp_err_t es8311_voice_volume_set(es8311_handle_t dev, int volume, int *volume_set);
|
||||
|
||||
/**
|
||||
* @brief Get output volume
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
* @param[out] volume get volume (0 ~ 100)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - Else fail
|
||||
*/
|
||||
esp_err_t es8311_voice_volume_get(es8311_handle_t dev, int *volume);
|
||||
|
||||
/**
|
||||
* @brief Print out ES8311 register content
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
*/
|
||||
void es8311_register_dump(es8311_handle_t dev);
|
||||
|
||||
/**
|
||||
* @brief Mute ES8311 output
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
* @param[in] enable true: mute, false: don't mute
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - Else fail
|
||||
*/
|
||||
esp_err_t es8311_voice_mute(es8311_handle_t dev, bool enable);
|
||||
|
||||
/**
|
||||
* @brief Set Microphone gain
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
* @param[in] gain_db Microphone gain
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - Else fail
|
||||
*/
|
||||
esp_err_t es8311_microphone_gain_set(es8311_handle_t dev, es8311_mic_gain_t gain_db);
|
||||
|
||||
/**
|
||||
* @brief Configure microphone
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
* @param[in] digital_mic Set to true for digital microphone
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - Else fail
|
||||
*/
|
||||
esp_err_t es8311_microphone_config(es8311_handle_t dev, bool digital_mic);
|
||||
|
||||
/**
|
||||
* @brief Configure sampling frequency
|
||||
*
|
||||
* @note This function is called by es8311_init().
|
||||
* Call this function explicitly only if you want to change sample frequency during runtime.
|
||||
* @param dev ES8311 handle
|
||||
* @param[in] mclk_frequency MCLK frequency in [Hz] (MCLK or SCLK pin, depending on bit register01[7])
|
||||
* @param[in] sample_frequency Required sample frequency in [Hz], e.g. 44100, 22050...
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - ESP_ERR_INVALID_ARG cannot set clock dividers for given MCLK and sampling frequency
|
||||
* - Else I2C read/write error
|
||||
*/
|
||||
esp_err_t es8311_sample_frequency_config(es8311_handle_t dev, int mclk_frequency, int sample_frequency);
|
||||
|
||||
/**
|
||||
* @brief Configure fade in/out for ADC: voice
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
* @param[in] fade Fade ramp rate
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - Else I2C read/write error
|
||||
*/
|
||||
esp_err_t es8311_voice_fade(es8311_handle_t dev, const es8311_fade_t fade);
|
||||
|
||||
/**
|
||||
* @brief Configure fade in/out for DAC: microphone
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
* @param[in] fade Fade ramp rate
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - Else I2C read/write error
|
||||
*/
|
||||
esp_err_t es8311_microphone_fade(es8311_handle_t dev, const es8311_fade_t fade);
|
||||
|
||||
/**
|
||||
* @brief Create ES8311 object and return its handle
|
||||
*
|
||||
* @param[in] port I2C port number
|
||||
* @param[in] dev_addr I2C device address of ES8311
|
||||
*
|
||||
* @return
|
||||
* - NULL Fail
|
||||
* - Others Success
|
||||
*/
|
||||
es8311_handle_t es8311_create(const unsigned int port, const uint16_t dev_addr);
|
||||
|
||||
/**
|
||||
* @brief Delete ES8311 object
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
*/
|
||||
void es8311_delete(es8311_handle_t dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* ES8311_REGISTER NAME_REG_REGISTER ADDRESS
|
||||
*/
|
||||
#define ES8311_RESET_REG00 0x00 /*reset digital,csm,clock manager etc.*/
|
||||
|
||||
/*
|
||||
* Clock Scheme Register definition
|
||||
*/
|
||||
#define ES8311_CLK_MANAGER_REG01 0x01 /* select clk src for mclk, enable clock for codec */
|
||||
#define ES8311_CLK_MANAGER_REG02 0x02 /* clk divider and clk multiplier */
|
||||
#define ES8311_CLK_MANAGER_REG03 0x03 /* adc fsmode and osr */
|
||||
#define ES8311_CLK_MANAGER_REG04 0x04 /* dac osr */
|
||||
#define ES8311_CLK_MANAGER_REG05 0x05 /* clk divier for adc and dac */
|
||||
#define ES8311_CLK_MANAGER_REG06 0x06 /* bclk inverter and divider */
|
||||
#define ES8311_CLK_MANAGER_REG07 0x07 /* tri-state, lrck divider */
|
||||
#define ES8311_CLK_MANAGER_REG08 0x08 /* lrck divider */
|
||||
/*
|
||||
* SDP
|
||||
*/
|
||||
#define ES8311_SDPIN_REG09 0x09 /* dac serial digital port */
|
||||
#define ES8311_SDPOUT_REG0A 0x0A /* adc serial digital port */
|
||||
/*
|
||||
* SYSTEM
|
||||
*/
|
||||
#define ES8311_SYSTEM_REG0B 0x0B /* system */
|
||||
#define ES8311_SYSTEM_REG0C 0x0C /* system */
|
||||
#define ES8311_SYSTEM_REG0D 0x0D /* system, power up/down */
|
||||
#define ES8311_SYSTEM_REG0E 0x0E /* system, power up/down */
|
||||
#define ES8311_SYSTEM_REG0F 0x0F /* system, low power */
|
||||
#define ES8311_SYSTEM_REG10 0x10 /* system */
|
||||
#define ES8311_SYSTEM_REG11 0x11 /* system */
|
||||
#define ES8311_SYSTEM_REG12 0x12 /* system, Enable DAC */
|
||||
#define ES8311_SYSTEM_REG13 0x13 /* system */
|
||||
#define ES8311_SYSTEM_REG14 0x14 /* system, select DMIC, select analog pga gain */
|
||||
/*
|
||||
* ADC
|
||||
*/
|
||||
#define ES8311_ADC_REG15 0x15 /* ADC, adc ramp rate, dmic sense */
|
||||
#define ES8311_ADC_REG16 0x16 /* ADC */
|
||||
#define ES8311_ADC_REG17 0x17 /* ADC, volume */
|
||||
#define ES8311_ADC_REG18 0x18 /* ADC, alc enable and winsize */
|
||||
#define ES8311_ADC_REG19 0x19 /* ADC, alc maxlevel */
|
||||
#define ES8311_ADC_REG1A 0x1A /* ADC, alc automute */
|
||||
#define ES8311_ADC_REG1B 0x1B /* ADC, alc automute, adc hpf s1 */
|
||||
#define ES8311_ADC_REG1C 0x1C /* ADC, equalizer, hpf s2 */
|
||||
/*
|
||||
* DAC
|
||||
*/
|
||||
#define ES8311_DAC_REG31 0x31 /* DAC, mute */
|
||||
#define ES8311_DAC_REG32 0x32 /* DAC, volume */
|
||||
#define ES8311_DAC_REG33 0x33 /* DAC, offset */
|
||||
#define ES8311_DAC_REG34 0x34 /* DAC, drc enable, drc winsize */
|
||||
#define ES8311_DAC_REG35 0x35 /* DAC, drc maxlevel, minilevel */
|
||||
#define ES8311_DAC_REG37 0x37 /* DAC, ramprate */
|
||||
/*
|
||||
*GPIO
|
||||
*/
|
||||
#define ES8311_GPIO_REG44 0x44 /* GPIO, dac2adc for test */
|
||||
#define ES8311_GP_REG45 0x45 /* GP CONTROL */
|
||||
/*
|
||||
* CHIP
|
||||
*/
|
||||
#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */
|
||||
#define ES8311_CHD2_REGFE 0xFE /* CHIP ID2 */
|
||||
#define ES8311_CHVER_REGFF 0xFF /* VERSION */
|
||||
#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */
|
||||
|
||||
#define ES8311_MAX_REGISTER 0xFF
|
||||
@ -0,0 +1,272 @@
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <math.h>
|
||||
#include "ESP_I2S.h"
|
||||
#include "esp_check.h"
|
||||
#include "Arduino_GFX_Library.h"
|
||||
#include "pin_config.h"
|
||||
#include "TouchDrvCSTXXX.hpp"
|
||||
#include "SensorQMI8658.hpp"
|
||||
#include "es8311.h"
|
||||
#include "es7210.h"
|
||||
|
||||
static const int DISP_W = 480;
|
||||
static const int DISP_H = 480;
|
||||
static const int SAFE = 20;
|
||||
static const uint32_t SAMPLE_RATE = 16000;
|
||||
static const size_t REC_SECONDS = 2;
|
||||
static const size_t AUDIO_BYTES = SAMPLE_RATE * REC_SECONDS * 2 * 2;
|
||||
static const int I2C_NUM = 0;
|
||||
|
||||
I2SClass i2s;
|
||||
TouchDrvCST92xx touch;
|
||||
SensorQMI8658 qmi;
|
||||
IMUdata acc;
|
||||
|
||||
Arduino_DataBus *bus = new Arduino_ESP32QSPI(LCD_CS, LCD_SCLK, LCD_SDIO0, LCD_SDIO1, LCD_SDIO2, LCD_SDIO3);
|
||||
Arduino_CO5300 *gfx = new Arduino_CO5300(bus, LCD_RESET, 0, DISP_W, DISP_H, 0, 0, 0, 0);
|
||||
|
||||
uint8_t *audioBuf = nullptr;
|
||||
size_t audioLen = 0;
|
||||
bool touchIrq = false;
|
||||
String statusLine = "Ready";
|
||||
String tiltLine = "Tilt: FLAT";
|
||||
String audioLine = "Audio: empty";
|
||||
uint32_t lastTiltMs = 0;
|
||||
bool micReady = false;
|
||||
|
||||
void IRAM_ATTR onTouchInt() { touchIrq = true; }
|
||||
|
||||
esp_err_t codecInit() {
|
||||
es8311_handle_t esHandle = es8311_create(I2C_NUM, ES8311_ADDRRES_0);
|
||||
ESP_RETURN_ON_FALSE(esHandle, ESP_FAIL, "AV", "es8311 create failed");
|
||||
const es8311_clock_config_t clk = {
|
||||
.mclk_inverted = false,
|
||||
.sclk_inverted = false,
|
||||
.mclk_from_mclk_pin = true,
|
||||
.mclk_frequency = SAMPLE_RATE * 256,
|
||||
.sample_frequency = SAMPLE_RATE,
|
||||
};
|
||||
ESP_ERROR_CHECK(es8311_init(esHandle, &clk, ES8311_RESOLUTION_16, ES8311_RESOLUTION_16));
|
||||
ESP_RETURN_ON_ERROR(es8311_sample_frequency_config(esHandle, clk.mclk_frequency, clk.sample_frequency), "AV", "clock cfg failed");
|
||||
ESP_RETURN_ON_ERROR(es8311_microphone_config(esHandle, false), "AV", "mic cfg failed");
|
||||
ESP_RETURN_ON_ERROR(es8311_voice_volume_set(esHandle, 90, NULL), "AV", "volume failed");
|
||||
ESP_RETURN_ON_ERROR(es8311_microphone_gain_set(esHandle, (es8311_mic_gain_t)6), "AV", "mic gain failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t micInit() {
|
||||
audio_hal_codec_config_t cfg = {
|
||||
.adc_input = AUDIO_HAL_ADC_INPUT_ALL,
|
||||
.codec_mode = AUDIO_HAL_CODEC_MODE_ENCODE,
|
||||
.i2s_iface = {
|
||||
.mode = AUDIO_HAL_MODE_SLAVE,
|
||||
.fmt = AUDIO_HAL_I2S_NORMAL,
|
||||
.samples = AUDIO_HAL_16K_SAMPLES,
|
||||
.bits = AUDIO_HAL_BIT_LENGTH_16BITS,
|
||||
},
|
||||
};
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret |= es7210_adc_init(&Wire, &cfg);
|
||||
ret |= es7210_adc_config_i2s(cfg.codec_mode, &cfg.i2s_iface);
|
||||
ret |= es7210_adc_set_gain(
|
||||
(es7210_input_mics_t)(ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2),
|
||||
(es7210_gain_value_t)GAIN_24DB);
|
||||
ret |= es7210_adc_set_gain(
|
||||
(es7210_input_mics_t)(ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4),
|
||||
(es7210_gain_value_t)GAIN_24DB);
|
||||
ret |= es7210_adc_ctrl_state(cfg.codec_mode, AUDIO_HAL_CTRL_START);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void drawStaticUi() {
|
||||
gfx->fillScreen(RGB565_BLACK);
|
||||
gfx->setTextSize(2);
|
||||
gfx->setTextColor(RGB565_WHITE);
|
||||
gfx->setCursor(SAFE + 16, SAFE + 10);
|
||||
gfx->println("Simple AV + Tilt Test");
|
||||
|
||||
gfx->drawRect(24, 76, 132, 64, RGB565_GREEN);
|
||||
gfx->setCursor(44, 100);
|
||||
gfx->println("RECORD");
|
||||
|
||||
gfx->drawRect(174, 76, 132, 64, RGB565_YELLOW);
|
||||
gfx->setCursor(212, 100);
|
||||
gfx->println("PLAY");
|
||||
|
||||
gfx->drawRect(324, 76, 132, 64, RGB565_CYAN);
|
||||
gfx->setCursor(360, 100);
|
||||
gfx->println("BEEP");
|
||||
}
|
||||
|
||||
void drawDynamic() {
|
||||
gfx->fillRect(20, 166, 440, 140, RGB565_BLACK);
|
||||
gfx->setTextSize(2);
|
||||
gfx->setTextColor(RGB565_WHITE);
|
||||
gfx->setCursor(28, 178);
|
||||
gfx->println(statusLine);
|
||||
gfx->setTextColor(RGB565_CYAN);
|
||||
gfx->setCursor(28, 212);
|
||||
gfx->println(tiltLine);
|
||||
gfx->setTextColor(RGB565_ORANGE);
|
||||
gfx->setCursor(28, 246);
|
||||
gfx->println(audioLine);
|
||||
gfx->drawRect(28, 278, 300, 20, RGB565_DARKGREY);
|
||||
}
|
||||
|
||||
void updateTilt() {
|
||||
if (!qmi.getDataReady()) return;
|
||||
if (!qmi.getAccelerometer(acc.x, acc.y, acc.z)) return;
|
||||
String next = "Tilt: FLAT";
|
||||
if (acc.x > 0.8f) next = "Tilt: RIGHT";
|
||||
else if (acc.x < -0.8f) next = "Tilt: LEFT";
|
||||
else if (acc.y > 0.8f) next = "Tilt: UP";
|
||||
else if (acc.y < -0.8f) next = "Tilt: DOWN";
|
||||
if (next != tiltLine) {
|
||||
tiltLine = next;
|
||||
drawDynamic();
|
||||
}
|
||||
}
|
||||
|
||||
void playBeep() {
|
||||
static int16_t tone[16000];
|
||||
for (int i = 0; i < 8000; ++i) {
|
||||
float s = sinf(2.0f * 3.1415926f * 440.0f * ((float)i / SAMPLE_RATE));
|
||||
int16_t v = (int16_t)(s * 6000.0f);
|
||||
tone[i * 2] = v;
|
||||
tone[i * 2 + 1] = v;
|
||||
}
|
||||
statusLine = "Playing test beep...";
|
||||
drawDynamic();
|
||||
i2s.write((uint8_t *)tone, 8000 * 2 * sizeof(int16_t));
|
||||
statusLine = "Beep done";
|
||||
drawDynamic();
|
||||
}
|
||||
|
||||
void recordAudio() {
|
||||
if (!audioBuf || !micReady) {
|
||||
statusLine = "No audio buffer";
|
||||
drawDynamic();
|
||||
return;
|
||||
}
|
||||
statusLine = "Recording 2 sec...";
|
||||
drawDynamic();
|
||||
size_t total = 0;
|
||||
uint16_t peakAll = 0;
|
||||
while (total < AUDIO_BYTES) {
|
||||
size_t chunk = min((size_t)4096, AUDIO_BYTES - total);
|
||||
size_t got = i2s.readBytes((char *)(audioBuf + total), chunk);
|
||||
if (!got) break;
|
||||
total += got;
|
||||
|
||||
int16_t *s = (int16_t *)(audioBuf + total - got);
|
||||
size_t n = got / sizeof(int16_t);
|
||||
uint16_t peak = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
uint16_t a = (uint16_t)abs((int)s[i]);
|
||||
if (a > peak) peak = a;
|
||||
}
|
||||
if (peak > peakAll) peakAll = peak;
|
||||
|
||||
int bar = map((int)peak, 0, 20000, 0, 296);
|
||||
if (bar < 0) bar = 0;
|
||||
if (bar > 296) bar = 296;
|
||||
gfx->fillRect(30, 280, 296, 16, RGB565_BLACK);
|
||||
uint16_t c = (peak > 12000) ? RGB565_RED : ((peak > 6000) ? RGB565_YELLOW : RGB565_GREEN);
|
||||
gfx->fillRect(30, 280, bar, 16, c);
|
||||
}
|
||||
audioLen = total;
|
||||
audioLine = (total > 0) ? "Audio: recorded" : "Audio: empty";
|
||||
if (total > 0) {
|
||||
statusLine = String("Record done, peak=") + String((int)peakAll);
|
||||
} else {
|
||||
statusLine = "Record failed";
|
||||
}
|
||||
drawDynamic();
|
||||
}
|
||||
|
||||
void playAudio() {
|
||||
if (!audioLen) {
|
||||
statusLine = "No recording. Try BEEP.";
|
||||
drawDynamic();
|
||||
return;
|
||||
}
|
||||
statusLine = "Playing recording...";
|
||||
drawDynamic();
|
||||
size_t wrote = i2s.write(audioBuf, audioLen);
|
||||
statusLine = (wrote > 0) ? "Play done" : "Play failed";
|
||||
drawDynamic();
|
||||
}
|
||||
|
||||
void handleTouch() {
|
||||
if (!touchIrq) return;
|
||||
touchIrq = false;
|
||||
int16_t x[1];
|
||||
int16_t y[1];
|
||||
uint8_t touched = touch.getPoint(x, y, 1);
|
||||
if (!touched) return;
|
||||
|
||||
int px = x[0];
|
||||
int py = y[0];
|
||||
if (py < 76 || py > 140) return;
|
||||
if (px >= 24 && px <= 156) recordAudio();
|
||||
else if (px >= 174 && px <= 306) playAudio();
|
||||
else if (px >= 324 && px <= 456) playBeep();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Wire.begin(IIC_SDA, IIC_SCL);
|
||||
|
||||
pinMode(PA, OUTPUT);
|
||||
digitalWrite(PA, HIGH);
|
||||
|
||||
if (!gfx->begin()) Serial.println("gfx begin failed");
|
||||
bus->writeC8D8(0x36, 0xA0);
|
||||
gfx->setBrightness(200);
|
||||
|
||||
pinMode(TP_RST, OUTPUT);
|
||||
digitalWrite(TP_RST, LOW);
|
||||
delay(30);
|
||||
digitalWrite(TP_RST, HIGH);
|
||||
delay(50);
|
||||
touch.setPins(TP_RST, TP_INT);
|
||||
touch.begin(Wire, 0x5A, IIC_SDA, IIC_SCL);
|
||||
touch.setMaxCoordinates(480, 480);
|
||||
touch.setSwapXY(true);
|
||||
touch.setMirrorXY(true, false);
|
||||
attachInterrupt(TP_INT, onTouchInt, FALLING);
|
||||
|
||||
if (qmi.begin(Wire, QMI8658_L_SLAVE_ADDRESS, IIC_SDA, IIC_SCL)) {
|
||||
qmi.configAccelerometer(SensorQMI8658::ACC_RANGE_4G, SensorQMI8658::ACC_ODR_1000Hz, SensorQMI8658::LPF_MODE_0);
|
||||
qmi.enableAccelerometer();
|
||||
} else {
|
||||
tiltLine = "Tilt: IMU not found";
|
||||
}
|
||||
|
||||
i2s.setPins(9, 45, 8, 10, 42);
|
||||
if (!i2s.begin(I2S_MODE_STD, SAMPLE_RATE, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO, I2S_STD_SLOT_BOTH)) {
|
||||
statusLine = "I2S init failed";
|
||||
} else if (codecInit() != ESP_OK) {
|
||||
statusLine = "ES8311 init failed";
|
||||
} else if (micInit() != ESP_OK) {
|
||||
statusLine = "ES7210 init failed";
|
||||
} else {
|
||||
micReady = true;
|
||||
}
|
||||
|
||||
audioBuf = (uint8_t *)ps_malloc(AUDIO_BYTES);
|
||||
if (!audioBuf) statusLine = "No PSRAM buffer";
|
||||
|
||||
drawStaticUi();
|
||||
drawDynamic();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
handleTouch();
|
||||
if (millis() - lastTiltMs > 120) {
|
||||
lastTiltMs = millis();
|
||||
updateTilt();
|
||||
}
|
||||
delay(5);
|
||||
}
|
||||
@ -1,2 +1,2 @@
|
||||
client.version=1.2.88
|
||||
server.version=1.2.82
|
||||
client.version=1.2.89
|
||||
server.version=1.2.83
|
||||
|
||||
Loading…
Reference in New Issue
Block a user