Compare commits
7 Commits
3218e6039f
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| 051c0c13c9 | |||
| 21da24695a | |||
| e9933da1a4 | |||
| 89ded8b119 | |||
| f3d5e4018b | |||
| de959b9a8b | |||
| 5b4691dc53 |
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -5,7 +5,7 @@
|
||||
"idf.openOcdConfigs": [
|
||||
"board/esp32s3-builtin.cfg"
|
||||
],
|
||||
"idf.port": "/dev/tty.BLTH",
|
||||
"idf.port": "/dev/tty.usbmodem14301",
|
||||
"idf.toolsPath": "/Users/tarassivas/.espressif",
|
||||
"idf.customExtraVars": {
|
||||
"IDF_TARGET": "esp32s3"
|
||||
@@ -15,5 +15,6 @@
|
||||
"--background-index",
|
||||
"--query-driver=/Users/tarassivas/.espressif/tools/xtensa-esp-elf/esp-14.2.0_20241119/xtensa-esp-elf/bin/xtensa-esp32-elf-gcc",
|
||||
"--compile-commands-dir=${workspaceFolder}/build"
|
||||
]
|
||||
],
|
||||
"idf.flashType": "UART"
|
||||
}
|
||||
|
||||
11
.vscode/sftp.json
vendored
Normal file
11
.vscode/sftp.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "My Server",
|
||||
"host": "localhost",
|
||||
"protocol": "sftp",
|
||||
"port": 22,
|
||||
"username": "username",
|
||||
"remotePath": "/",
|
||||
"uploadOnSave": false,
|
||||
"useTempFile": false,
|
||||
"openSsh": false
|
||||
}
|
||||
80
README.md
80
README.md
@@ -4,19 +4,33 @@ watch-watch — вбудована система на ESP32-S3 для нагл
|
||||
|
||||
## Основні можливості
|
||||
- **Керування каналами живлення**: 5 незалежних ліній `EN` (GPIO 2, 4, 5, 18, 19), які можна увімкнути, вимкнути або перемкнути з коду чи CLI.
|
||||
- **Послідовний автотест**: у `app_main` реалізовано базову логіку — канали вмикаються по черзі з інтервалом 4 с, що дозволяє перевірити всі DC/DC.
|
||||
- **Послідовний автотест**: у `app_main` реалізовано базову логіку — канали вмикаються по черзі з інтервалом 3 с, що дозволяє перевірити всі DC/DC без стрибків споживання.
|
||||
- **Світлодіодний індикатор стану**: п’ять WS2812 (GPIO 8) світяться зеленим під час стартової затримки, після чого кожен канал сигналізує лише про дві події — відсутність VPN (два червоних блимання) та падіння APP (три жовтих блимання).
|
||||
- **Моніторинг навантаження**: датчики INA226 вимірюють напругу, струм та потужність кожного каналу, інформація потрапляє в лог і CLI.
|
||||
- **UART взаємодія з Raspberry Pi**: один UART через мультиплексор (A0/A1/A2) ділиться між п’ятьма Pi, дозволяючи надсилати службові повідомлення або обмінюватися даними.
|
||||
- **Нативний USB-CLI**: ESP32-S3 підключається до Raspberry Pi 5 по USB і стає CDC ACM пристроєм; командний інтерфейс дозволяє керувати каналами та дивитись стан у реальному часі.
|
||||
- **Модульна архітектура**: окремі компоненти `dcdc_controller` і `usb_cdc_cli` спрощують розширення (телеметрія, автоматизація, протоколи зв’язку).
|
||||
|
||||
## Структура проєкту
|
||||
```
|
||||
├── CMakeLists.txt
|
||||
├── Kconfig.projbuild # меню і параметри WATCH_WS2812_*
|
||||
├── README.md
|
||||
├── sdkconfig # збережені налаштування menuconfig
|
||||
├── dependencies.lock
|
||||
├── main
|
||||
│ ├── main.c // базова логіка, ініціалізація модулів
|
||||
│ ├── dcdc_controller.c/.h // API керування DC/DC каналами
|
||||
│ ├── usb_cdc_cli.c/.h // TinyUSB CLI для Raspberry Pi 5
|
||||
│ └── idf_component.yml // залежність на espressif/esp_tinyusb
|
||||
├── sdkconfig // конфігурація (опції TinyUSB увімкнені)
|
||||
└── README.md // цей файл
|
||||
│ ├── CMakeLists.txt
|
||||
│ ├── idf_component.yml # залежності: esp_tinyusb, led_strip
|
||||
│ ├── main.c # головний цикл, тестова логіка DC/DC
|
||||
│ ├── dcdc_controller.c/.h # керування GPIO EN
|
||||
│ ├── usb_cdc_cli.c/.h # CLI по USB CDC
|
||||
│ ├── ws2812_status.c/.h # індикація стану на WS2812
|
||||
│ ├── ina226_monitor.c/.h # вимірювання напруги/струму/потужності
|
||||
│ └── uart_mux.c/.h # UART взаємодія з 5 Raspberry Pi
|
||||
├── managed_components
|
||||
│ ├── espressif__esp_tinyusb # бібліотека TinyUSB від Espressif
|
||||
│ └── espressif__led_strip # драйвер керування WS2812 (RMT/SPI)
|
||||
└── .vscode / .devcontainer / .clangd # допоміжні файли середовища розробки
|
||||
```
|
||||
|
||||
## GPIO-призначення каналів
|
||||
@@ -31,6 +45,35 @@ watch-watch — вбудована система на ESP32-S3 для нагл
|
||||
|
||||
> Піни можна змінити в `main/dcdc_controller.c`, масив `s_dcdc_gpio_map`.
|
||||
|
||||
## Світлодіоди стану
|
||||
- IC WS2812 підключений до GPIO 8 (один ланцюг із 5 діодів).
|
||||
- Після старту всі п’ять діодів світяться сталим зеленим протягом затримки `heartbeat_start_delay_sec`, щоб показати фазу ініціалізації.
|
||||
- Після завершення затримки вся стрічка гасне, а кожен канал індикує лише два типи подій:
|
||||
- VPN=0 — два червоних блимання по 200 мс із паузою між циклами;
|
||||
- APP=0 — три жовтих блимання по 200 мс.
|
||||
Якщо активні обидва попередження, вони програються послідовно (спочатку VPN, потім APP) із паузою 2 с між послідовностями.
|
||||
- При критичній помилці (наприклад, DCDC не ініціалізувався) всі індикатори залишаються червоними.
|
||||
- GPIO, кількість діодів та тактову частоту RMT можна змінити через `idf.py menuconfig` (розділ *Налаштування watch-watch*).
|
||||
|
||||
## Моніторинг живлення (INA226)
|
||||
- Один INA226 підключений до загальної шини живлення (I2C порт 0, GPIO 6/7; адреса налаштовується параметром `WATCH_INA226_ADDR`).
|
||||
- Модуль `ina226_monitor` вимірює сумарну напругу, струм і розраховує потужність — ці значення фіксуються в логах і використовуються для телеметрії.
|
||||
- Команда CLI `sense` показує поточні показники для всієї системи (канал не вказується, бо датчик один).
|
||||
- Конфігурація I2C, адреси, шунта та кроку струму знаходиться в `menuconfig → INA226 моніторинг`.
|
||||
|
||||
## UART взаємодія та heartbeat
|
||||
- Загальний UART (типово UART1, GPIO17/16) підключений до аналогового мультиплексора, лінії адреси `A0/A1/A2` (GPIO 9/10/11) вибирають одну з Raspberry Pi.
|
||||
- Модуль `uart_mux` серіалізує доступ до UART, надає API для `uart_mux_write/read` і періодично опитує UART на наявність heartbeat.
|
||||
- Після кожного вимірювання INA226 ESP32-S3 відправляє поточну телеметрію (`PWR <V>V <I>A`) до активної Raspberry Pi.
|
||||
- Якщо heartbeat від Pi не надходить протягом `CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC` (за замовчуванням 60 с), відповідний канал живлення вимикається й знову вмикається для примусового перезапуску.
|
||||
- Команди CLI `uart send` / `uart read` дозволяють вручну надсилати/читати повідомлення, а в `app_main` можна реалізувати власні протоколи синхронізації.
|
||||
|
||||
## UART взаємодія з Raspberry Pi
|
||||
- Шина UART (типово UART1, TX=GPIO17, RX=GPIO16) підключена до аналогового мультиплексора з адресними лініями A0/A1/A2 (GPIO 9/10/11), що дозволяє вибирати одну з 5 Raspberry Pi.
|
||||
- Модуль `uart_mux` гарантує серійний доступ: перед операцією він виставляє двійковий код каналу на A0-A2 та блокує UART м’ютексом.
|
||||
- У `app_main` після вимірювань кожному Pi відправляється телеметрія (`CHx <V>V <I>A`), а через CLI можна виконати `uart send <n> <msg>` або `uart read <n> [len]`.
|
||||
- Усі параметри (порт, швидкість, GPIO) доступні в `menuconfig → UART мультиплексор`.
|
||||
|
||||
## USB CDC CLI
|
||||
Після підключення ESP32-S3 до Raspberry Pi 5 з’являється USB-пристрій (CDC ACM). У CLI доступні команди:
|
||||
|
||||
@@ -41,9 +84,32 @@ watch-watch — вбудована система на ESP32-S3 для нагл
|
||||
| `enable <n>` | увімкнути канал `n` (0..4) |
|
||||
| `disable <n>` | вимкнути канал `n` |
|
||||
| `toggle <n>` | перемкнути канал `n` |
|
||||
| `sense` | виміряти загальну напругу/струм/потужність |
|
||||
| `uart send <n> <msg>` | надіслати повідомлення у Raspberry Pi `n` |
|
||||
| `uart read <n> [len]` | прочитати відповідь від Raspberry Pi `n` |
|
||||
| `config show` | переглянути таймінги heartbeat/DCDC |
|
||||
| `config set …` | змінити та зберегти таймінги / моніторинг |
|
||||
| `reset` | м’яке перезавантаження ESP32-S3 |
|
||||
| `bootloader` | перезавантажити ESP32-S3 у ROM bootloader для `esptool.py` |
|
||||
|
||||
CLI з’являється після того, як Raspberry Pi встановить DTR (наприклад, через `screen`, `picocom` або власний скрипт).
|
||||
|
||||
### Керування таймінгами heartbeat
|
||||
Команда `config` дозволяє зберігати параметри роботи watchdog та циклу heartbeat у NVS, і вони одразу набувають чинності без перезбирання прошивки:
|
||||
|
||||
1. `hb_period` — інтервал (сек) між опитуваннями/heartbeat у головному циклі.
|
||||
2. `dcdc_off` — тривалість вимкнення каналу DCDC при автоматичному перезапуску (сек).
|
||||
3. `hb_start` — затримка перед стартом опитування після завантаження (сек); застосовується як у головному циклі, так і у watchdog-завданні.
|
||||
4. `hb_monitor` — значення `1` вмикає моніторинг heartbeat (за замовчуванням), `0` вимикає лише контроль/рестарти каналів, але тестові повідомлення heartbeat продовжують відправлятися.
|
||||
5. `hb_miss` — кількість послідовних запитів без відповіді перед автоматичним перезапуском каналу (за замовчуванням 3). Значення зберігає watchdog та використовується в CLI для відображення статистики пропусків/рестартів.
|
||||
|
||||
### Прошивка без натискання BOOT
|
||||
1. Під’єднайтесь до CLI та виконайте команду `bootloader`. ESP32-S3 перезавантажиться у ROM bootloader, а на хості з’явиться USB-пристрій `USB JTAG/serial`.
|
||||
2. На Raspberry Pi запустіть `esptool.py` або `idf.py flash` і вкажіть новий порт (`/dev/ttyACM*` або `/dev/cu.usbmodem*`). Наприклад:
|
||||
`esptool.py --chip esp32s3 --port /dev/ttyACM0 --before usb_reset --after no_reset write_flash 0x0 build/watch-watch.bin`
|
||||
3. Після завершення прошивки виконайте `esptool.py --after hard_reset reset` або просто перезавантажте живлення — пристрій вийде з bootloader і повернеться до нормальної роботи.
|
||||
Таким чином процедура оновлення доступна без натискання кнопки BOOT і може виконуватись безпосередньо з підключеної Raspberry Pi.
|
||||
|
||||
## Збірка та конфігурація
|
||||
1. Встановіть ESP-IDF v5.5.1 (шлях `IDF_PATH` має вказувати на `/Users/tarassivas/esp/v5.5.1/esp-idf`).
|
||||
2. Один раз виконайте `idf.py reconfigure`, щоб підвантажити залежності з `idf_component.yml`.
|
||||
|
||||
@@ -13,6 +13,16 @@ dependencies:
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 1.7.6~2
|
||||
espressif/led_strip:
|
||||
component_hash: 28c6509a727ef74925b372ed404772aeedf11cce10b78c3f69b3c66799095e2d
|
||||
dependencies:
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=4.4'
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 2.5.5
|
||||
espressif/tinyusb:
|
||||
component_hash: 5ea9d3b6d6b0734a0a0b3491967aa0e1bece2974132294dbda5dd2839b247bfa
|
||||
dependencies:
|
||||
@@ -34,6 +44,7 @@ dependencies:
|
||||
version: 5.5.1
|
||||
direct_dependencies:
|
||||
- espressif/esp_tinyusb
|
||||
manifest_hash: 5a05c0273068b434734dee09d2c2ea1da5ef6181b054b4a9d51531ebb931282d
|
||||
- espressif/led_strip
|
||||
manifest_hash: 584163379689100c49748a32c2503844d55d9168901c12c7eec84929b4541e28
|
||||
target: esp32s3
|
||||
version: 2.0.0
|
||||
|
||||
24
kicad/.gitignore
vendored
Normal file
24
kicad/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Temporary and local KiCad artifacts
|
||||
*-backups/
|
||||
*.kicad_prl
|
||||
*.kicad_pro-bak
|
||||
*.kicad_sch-bak
|
||||
*.kicad_pcb-bak
|
||||
*.sch-bak
|
||||
*.pcb-bak
|
||||
*.bak
|
||||
|
||||
# Cache and automatically generated tables
|
||||
sym-lib-table-bak
|
||||
fp-lib-table-bak
|
||||
*-cache.lib
|
||||
*-cache.dcm
|
||||
|
||||
# Generated fabrication outputs (regenerate as needed)
|
||||
*.net
|
||||
*.xml
|
||||
*.csv
|
||||
*.tsv
|
||||
*.pos
|
||||
*.ipr
|
||||
*.rpt
|
||||
@@ -0,0 +1 @@
|
||||
{"ARCHIVE_NAME": "", "EXTRA_LAYERS": "", "ALL_ACTIVE_LAYERS": false, "EXTEND_EDGE_CUT": false, "ALTERNATIVE_EDGE_CUT": false, "AUTO TRANSLATE": true, "AUTO FILL": true, "EXCLUDE DNP": false, "OPEN BROWSER": true, "NO_BACKUP_OPT": false}
|
||||
105078
kicad/watch-watchkicad_pro/fp-info-cache
Normal file
105078
kicad/watch-watchkicad_pro/fp-info-cache
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
506
kicad/watch-watchkicad_pro/production/netlist.ipc
Normal file
506
kicad/watch-watchkicad_pro/production/netlist.ipc
Normal file
@@ -0,0 +1,506 @@
|
||||
P CODE 00
|
||||
P UNITS CUST 0
|
||||
P arrayDim N
|
||||
317GND VIA MD0118PA00X+052000Y-017250X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+051500Y-017250X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+066063Y-030875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+066063Y-030875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+068875Y-025750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+070125Y-026750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+071813Y-029063X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+069188Y-028750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+072875Y-030125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+071625Y-030125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+048938Y-028500X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+048938Y-026000X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+048938Y-018500X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+048875Y-021000X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+048938Y-023500X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+053563Y-020313X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+054188Y-019813X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+055000Y-020625X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+055000Y-020125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+057438Y-020063X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+058125Y-019813X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+063063Y-016313X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+063063Y-017563X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+063063Y-018688X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+063063Y-019625X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+063063Y-020563X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+064313Y-021000X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+064875Y-021688X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+066188Y-025188X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+065750Y-025188X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+065938Y-027625X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+066625Y-027625X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+066625Y-026813X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+065875Y-026813X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+063938Y-026875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+060688Y-026813X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+062875Y-028063X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+061000Y-028000X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+059938Y-029000X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+057813Y-028750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+056625Y-028750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+054188Y-028750X0236Y0000R000S1526506179
|
||||
317+3.3V VIA MD0118PA00X+052625Y-017249X0236Y0000R000S1526506179
|
||||
317+3.3V VIA MD0118PA00X+053313Y-018188X0236Y0000R000S1526506179
|
||||
317ET-(J14-PIN_2) VIA MD0118PA00X+055750Y-019813X0236Y0000R000S1526506179
|
||||
317ET-(J14-PIN_2) VIA MD0118PA00X+064188Y-024625X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+068500Y-016125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+073813Y-016063X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+073313Y-017688X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+068563Y-017563X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+072438Y-019438X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+071813Y-019438X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+070375Y-019313X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+070125Y-019750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+072813Y-024188X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+071000Y-026750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+065688Y-030000X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+065625Y-030875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+065000Y-030875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+063875Y-030875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+063250Y-030813X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+057750Y-029750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+060813Y-029688X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+058188Y-029688X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+060250Y-029688X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+058813Y-029313X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+059813Y-029688X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+058813Y-029688X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+052750Y-024750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+052875Y-027500X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+054125Y-027813X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+055125Y-027813X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+058500Y-020875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+058813Y-017881X0236Y0000R000S1526506179
|
||||
317+5V VIA MD0118PA00X+060063Y-019000X0236Y0000R000S1526506179
|
||||
317+5V VIA MD0118PA00X+058188Y-019000X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+055500Y-025625X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+057000Y-025375X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+057750Y-025250X0236Y0000R000S1526506179
|
||||
317+3.3V VIA MD0118PA00X+059509Y-026962X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+057750Y-025750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+056563Y-020250X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+057250Y-030500X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+057250Y-029750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+047250Y-030000X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+047250Y-030625X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+054000Y-029875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+054000Y-030500X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+062000Y-017500X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+060000Y-017125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+058375Y-017125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+056250Y-017125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+054875Y-018500X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+054188Y-019375X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+055750Y-018938X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+056250Y-018500X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+056125Y-020250X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+055438Y-020563X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+060750Y-021250X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+061125Y-025750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+062000Y-026875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+067875Y-026750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+066625Y-025188X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+066250Y-026813X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+066250Y-027625X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+072875Y-025875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+070125Y-025625X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+069000Y-025250X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+068250Y-025875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+071000Y-024250X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+070000Y-024250X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+067250Y-021875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+071000Y-020750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+065875Y-016250X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+065125Y-016250X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+065875Y-018500X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+065250Y-018500X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+062063Y-018688X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+062000Y-016313X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+060000Y-016125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+058250Y-016125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+047000Y-016875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+047000Y-016125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+052000Y-018000X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+052000Y-020500X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+052000Y-023000X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+052000Y-025500X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+052000Y-028000X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+062500Y-030875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+062500Y-030375X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+062500Y-030000X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+061250Y-030750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+061250Y-030375X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+061250Y-030000X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+064750Y-029125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+064750Y-028750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+064750Y-028375X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+063875Y-029125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+063875Y-028750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+063875Y-028375X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+063375Y-029125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+063375Y-028750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+063375Y-028375X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+065250Y-029125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+065250Y-028750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+065250Y-028375X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+067375Y-029375X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+070375Y-030625X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+069500Y-030625X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+076000Y-030625X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+075375Y-030625X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+075125Y-027750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+076125Y-026375X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+075500Y-026375X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+072875Y-025000X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+072875Y-023500X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+075375Y-022500X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+074875Y-020125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+070000Y-022125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+069000Y-022125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+068000Y-020875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+069000Y-028125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+068500Y-028125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+068000Y-028125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+066938Y-024563X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+064875Y-022625X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+066750Y-022625X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+062063Y-019625X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+060125Y-020125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+058063Y-020750X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+061875Y-025875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+055375Y-026250X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+057625Y-027000X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+053375Y-025875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+053750Y-023125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+054000Y-020875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+055000Y-021250X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+055000Y-022125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+055000Y-022875X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+055000Y-023438X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+055000Y-024125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+055000Y-025125X0236Y0000R000S1526506179
|
||||
317GND VIA MD0118PA00X+062750Y-022625X0236Y0000R000S1526506179
|
||||
317NET-(U3-GPIO0) VIA MD0118PA00X+069375Y-019250X0236Y0000R000S1526506179
|
||||
317NET-(U3-GPIO0) VIA MD0118PA00X+060750Y-027250X0236Y0000R000S1526506179
|
||||
317+3.3V VIA MD0118PA00X+062375Y-025750X0236Y0000R000S1526506179
|
||||
317+3.3V VIA MD0118PA00X+064750Y-023250X0236Y0000R000S1526506179
|
||||
317+3.3V VIA MD0118PA00X+064750Y-024875X0236Y0000R000S1526506179
|
||||
317+5V VIA MD0118PA00X+060625Y-028500X0236Y0000R000S1526506179
|
||||
317RX VIA MD0118PA00X+068500Y-026375X0236Y0000R000S1526506179
|
||||
317TX VIA MD0118PA00X+068750Y-020625X0236Y0000R000S1526506179
|
||||
317A0 VIA MD0118PA00X+069000Y-022875X0236Y0000R000S1526506179
|
||||
317A1 VIA MD0118PA00X+068500Y-022875X0236Y0000R000S1526506179
|
||||
317A2 VIA MD0118PA00X+068000Y-022875X0236Y0000R000S1526506179
|
||||
317POWER_RAIL_IN VIA MD0118PA00X+067375Y-020250X0236Y0000R000S1526506179
|
||||
317POWER_RAIL_IN VIA MD0118PA00X+065250Y-022000X0236Y0000R000S1526506179
|
||||
317+3.3V VIA MD0118PA00X+062125Y-022000X0236Y0000R000S1526506179
|
||||
317EN0 VIA MD0118PA00X+059000Y-027375X0236Y0000R000S1526506179
|
||||
317EN1 VIA MD0118PA00X+058494Y-027625X0236Y0000R000S1526506179
|
||||
317EN2 VIA MD0118PA00X+059500Y-027375X0236Y0000R000S1526506179
|
||||
317EN3 VIA MD0118PA00X+059921Y-027375X0236Y0000R000S1526506179
|
||||
317EN4 VIA MD0118PA00X+060250Y-027750X0236Y0000R000S1526506179
|
||||
317+3.3V VIA MD0118PA00X+057000Y-022250X0236Y0000R000S1526506179
|
||||
317+3.3V VIA MD0118PA00X+057539Y-024173X0236Y0000R000S1526506179
|
||||
317+3.3V VIA MD0118PA00X+069625Y-022500X0236Y0000R000S1526506179
|
||||
317+3.3V VIA MD0118PA00X+072000Y-020250X0236Y0000R000S1526506179
|
||||
317+3.3V VIA MD0118PA00X+072750Y-029250X0236Y0000R000S1526506179
|
||||
317+3.3V VIA MD0118PA00X+072750Y-027500X0236Y0000R000S1526506179
|
||||
317+3.3V VIA MD0118PA00X+072000Y-026000X0236Y0000R000S1526506179
|
||||
317+3.3V VIA MD0118PA00X+072000Y-024750X0236Y0000R000S1526506179
|
||||
317EN0 VIA MD0118PA00X+058986Y-025688X0236Y0000R000S1526506179
|
||||
317RX VIA MD0118PA00X+056929Y-023051X0236Y0000R000S1526506179
|
||||
317TX VIA MD0118PA00X+057323Y-023051X0236Y0000R000S1526506179
|
||||
317EN3 VIA MD0118PA00X+059921Y-025669X0236Y0000R000S1526506179
|
||||
317EN2 VIA MD0118PA00X+059606Y-025669X0236Y0000R000S1526506179
|
||||
317EN1 VIA MD0118PA00X+059155Y-025296X0236Y0000R000S1526506179
|
||||
317A2 VIA MD0118PA00X+062205Y-023858X0236Y0000R000S1526506179
|
||||
317A1 VIA MD0118PA00X+061811Y-023858X0236Y0000R000S1526506179
|
||||
317A0 VIA MD0118PA00X+061417Y-023858X0236Y0000R000S1526506179
|
||||
317EN4 VIA MD0118PA00X+060236Y-025669X0236Y0000R000S1526506179
|
||||
317TX3 VIA MD0118PA00X+069750Y-028474X0236Y0000R000S1526506179
|
||||
317TX0 VIA MD0118PA00X+070500Y-028474X0236Y0000R000S1526506179
|
||||
317TX1 VIA MD0118PA00X+071250Y-028724X0236Y0000R000S1526506179
|
||||
317TX2 VIA MD0118PA00X+071313Y-030438X0236Y0000R000S1526506179
|
||||
317RX4 VIA MD0118PA00X+072250Y-021474X0236Y0000R000S1526506179
|
||||
317RX4 VIA MD0118PA00X+074119Y-022474X0236Y0000R000S1526506179
|
||||
327EN0.0 U9 -4 A01X+048905Y-019000X0719Y0256R180S2
|
||||
327EN0.1 U9 -3 A01X+048905Y-018000X0719Y0256R180S2
|
||||
327GND U9 -2 A01X+052595Y-018000X0719Y0256R180S2
|
||||
327NET-(R1-PAD1) U9 -1 A01X+052595Y-019000X0719Y0256R180S2
|
||||
317GND J14 -1 D0394PA00X+049813Y-016563X0669Y0669R270S0
|
||||
317ET-(J14-PIN_2) J14 -2 D0394PA00X+050813Y-016563X0669Y0000R270S0
|
||||
317+3.3V J14 -3 D0394PA00X+051813Y-016563X0669Y0000R270S0
|
||||
327+3.3V C1 -1 A01X+053063Y-017249X0394Y0571R270S2
|
||||
327GND C1 -2 A01X+053063Y-016501X0394Y0571R270S2
|
||||
327GND J1 -S1 A01X+057122Y-016805X0858Y0787R180S2
|
||||
327GND J1 -S1 A01X+057122Y-018352X0858Y0787R180S2
|
||||
327GND J1 -S1 A01X+061146Y-016805X0858Y0787R180S2
|
||||
327GND J1 -S1 A01X+061146Y-018352X0858Y0787R180S2
|
||||
327GND J1 -B12 A01X+060394Y-018579X0236Y0453R180S2
|
||||
327+5V J1 -B9 A01X+060079Y-018579X0236Y0453R180S2
|
||||
327J1-SBU2-PADB8) J1 -B8 A01X+059823Y-018579X0118Y0453R180S2
|
||||
327-(J1-D--PADA7) J1 -B7 A01X+059429Y-018579X0118Y0453R180S2
|
||||
327-(J1-D+-PADA6) J1 -B6 A01X+058839Y-018579X0118Y0453R180S2
|
||||
327NET-(J1-CC2) J1 -B5 A01X+058445Y-018579X0118Y0453R180S2
|
||||
327+5V J1 -B4 A01X+058189Y-018579X0236Y0453R180S2
|
||||
327GND J1 -B1 A01X+057874Y-018579X0236Y0453R180S2
|
||||
327GND J1 -A12 A01X+057874Y-018579X0236Y0453R180S2
|
||||
327+5V J1 -A9 A01X+058189Y-018579X0236Y0453R180S2
|
||||
327J1-SBU1-PADA8) J1 -A8 A01X+058642Y-018579X0118Y0453R180S2
|
||||
327-(J1-D--PADA7) J1 -A7 A01X+059035Y-018579X0118Y0453R180S2
|
||||
327-(J1-D+-PADA6) J1 -A6 A01X+059232Y-018579X0118Y0453R180S2
|
||||
327NET-(J1-CC1) J1 -A5 A01X+059626Y-018579X0118Y0453R180S2
|
||||
327+5V J1 -A4 A01X+060079Y-018579X0236Y0453R180S2
|
||||
327GND J1 -A1 A01X+060394Y-018579X0236Y0453R180S2
|
||||
367N/C J1 D0256UA00X+057996Y-018156X0256Y0000R180S0
|
||||
367N/C J1 D0256UA00X+060272Y-018156X0256Y0000R180S0
|
||||
327TX TP4 -1 A01X+069500Y-020250X0787Y0000R000S2
|
||||
317GND U3 -57 D0079PA00X+059823Y-022047X0197Y0000R270S3
|
||||
317GND U3 -57 D0079PA00X+059134Y-022047X0197Y0000R270S3
|
||||
317GND U3 -57 D0079PA00X+058445Y-022047X0197Y0000R270S3
|
||||
317GND U3 -57 D0079PA00X+059823Y-022736X0197Y0000R270S3
|
||||
327GND U3 -57 A02X+059134Y-022736X1575Y1575R270S3
|
||||
327GND U3 -57 A01X+059134Y-022736X1575Y1575R270S2
|
||||
317GND U3 -57 D0079PA00X+059134Y-022736X0197Y0000R270S3
|
||||
317GND U3 -57 D0079PA00X+058445Y-022736X0197Y0000R270S3
|
||||
317GND U3 -57 D0079PA00X+059823Y-023425X0197Y0000R270S3
|
||||
317GND U3 -57 D0079PA00X+059134Y-023425X0197Y0000R270S3
|
||||
317GND U3 -57 D0079PA00X+058445Y-023425X0197Y0000R270S3
|
||||
327+3.3V U3 -56 A01X+057776Y-023760X0079Y0315R270S2
|
||||
327+3.3V U3 -55 A01X+057776Y-023602X0079Y0315R270S2
|
||||
327ET-(U3-XTAL_P) U3 -54 A01X+057776Y-023445X0079Y0315R270S2
|
||||
327ET-(U3-XTAL_N) U3 -53 A01X+057776Y-023287X0079Y0315R270S2
|
||||
327-GPIO46-PAD52) U3 -52 A01X+057776Y-023130X0079Y0315R270S2
|
||||
327-GPIO45-PAD51) U3 -51 A01X+057776Y-022972X0079Y0315R270S2
|
||||
327TX U3 -50 A01X+057776Y-022815X0079Y0315R270S2
|
||||
327RX U3 -49 A01X+057776Y-022657X0079Y0315R270S2
|
||||
327U3-MTMS-PAD48) U3 -48 A01X+057776Y-022500X0079Y0315R270S2
|
||||
327U3-MTDI-PAD47) U3 -47 A01X+057776Y-022343X0079Y0315R270S2
|
||||
327+3.3V U3 -46 A01X+057776Y-022185X0079Y0315R270S2
|
||||
327U3-MTDO-PAD45) U3 -45 A01X+057776Y-022028X0079Y0315R270S2
|
||||
327U3-MTCK-PAD44) U3 -44 A01X+057776Y-021870X0079Y0315R270S2
|
||||
327-GPIO38-PAD43) U3 -43 A01X+057776Y-021713X0079Y0315R270S2
|
||||
327-GPIO37-PAD42) U3 -42 A01X+058110Y-021378X0315Y0079R270S2
|
||||
327-GPIO36-PAD41) U3 -41 A01X+058268Y-021378X0315Y0079R270S2
|
||||
327-GPIO35-PAD40) U3 -40 A01X+058425Y-021378X0315Y0079R270S2
|
||||
327-GPIO34-PAD39) U3 -39 A01X+058583Y-021378X0315Y0079R270S2
|
||||
327-GPIO33-PAD38) U3 -38 A01X+058740Y-021378X0315Y0079R270S2
|
||||
327PICLK_P-PAD37) U3 -37 A01X+058898Y-021378X0315Y0079R270S2
|
||||
327PICLK_N-PAD36) U3 -36 A01X+059055Y-021378X0315Y0079R270S2
|
||||
327U3-SPID-PAD35) U3 -35 A01X+059213Y-021378X0315Y0079R270S2
|
||||
327U3-SPIQ-PAD34) U3 -34 A01X+059370Y-021378X0315Y0079R270S2
|
||||
327-SPICLK-PAD33) U3 -33 A01X+059528Y-021378X0315Y0079R270S2
|
||||
327-SPICS0-PAD32) U3 -32 A01X+059685Y-021378X0315Y0079R270S2
|
||||
3273-SPIWP-PAD31) U3 -31 A01X+059843Y-021378X0315Y0079R270S2
|
||||
3273-SPIHD-PAD30) U3 -30 A01X+060000Y-021378X0315Y0079R270S2
|
||||
327VDD_SPI-PAD29) U3 -29 A01X+060157Y-021378X0315Y0079R270S2
|
||||
327-SPICS1-PAD28) U3 -28 A01X+060492Y-021713X0079Y0315R270S2
|
||||
327-GPIO21-PAD27) U3 -27 A01X+060492Y-021870X0079Y0315R270S2
|
||||
327DP U3 -26 A01X+060492Y-022028X0079Y0315R270S2
|
||||
327DM U3 -25 A01X+060492Y-022185X0079Y0315R270S2
|
||||
327ET-(U3-GPIO18) U3 -24 A01X+060492Y-022343X0079Y0315R270S2
|
||||
327ET-(U3-GPIO17) U3 -23 A01X+060492Y-022500X0079Y0315R270S2
|
||||
327L_32K_N-PAD22) U3 -22 A01X+060492Y-022657X0079Y0315R270S2
|
||||
327L_32K_P-PAD21) U3 -21 A01X+060492Y-022815X0079Y0315R270S2
|
||||
327+3.3V U3 -20 A01X+060492Y-022972X0079Y0315R270S2
|
||||
327ET-(U3-GPIO14) U3 -19 A01X+060492Y-023130X0079Y0315R270S2
|
||||
327A2 U3 -18 A01X+060492Y-023287X0079Y0315R270S2
|
||||
327A1 U3 -17 A01X+060492Y-023445X0079Y0315R270S2
|
||||
327A0 U3 -16 A01X+060492Y-023602X0079Y0315R270S2
|
||||
327SCL U3 -15 A01X+060492Y-023760X0079Y0315R270S2
|
||||
327SDA U3 -14 A01X+060157Y-024094X0315Y0079R270S2
|
||||
327RGB U3 -13 A01X+060000Y-024094X0315Y0079R270S2
|
||||
327NET-(U3-GPIO7) U3 -12 A01X+059843Y-024094X0315Y0079R270S2
|
||||
327NET-(U3-GPIO6) U3 -11 A01X+059685Y-024094X0315Y0079R270S2
|
||||
327EN4 U3 -10 A01X+059528Y-024094X0315Y0079R270S2
|
||||
327EN3 U3 -9 A01X+059370Y-024094X0315Y0079R270S2
|
||||
327EN2 U3 -8 A01X+059213Y-024094X0315Y0079R270S2
|
||||
327EN1 U3 -7 A01X+059055Y-024094X0315Y0079R270S2
|
||||
327EN0 U3 -6 A01X+058898Y-024094X0315Y0079R270S2
|
||||
327NET-(U3-GPIO0) U3 -5 A01X+058740Y-024094X0315Y0079R270S2
|
||||
327T-(U3-CHIP_PU) U3 -4 A01X+058583Y-024094X0315Y0079R270S2
|
||||
327+3.3V U3 -3 A01X+058425Y-024094X0315Y0079R270S2
|
||||
327+3.3V U3 -2 A01X+058268Y-024094X0315Y0079R270S2
|
||||
327ET-(U3-LNA_IN) U3 -1 A01X+058110Y-024094X0315Y0079R270S2
|
||||
327ET-(U3-LNA_IN) TP1 -1 A01X+056375Y-026500X0787Y0000R000S2
|
||||
327GND C15 -2 A01X+057087Y-021122X0394Y0571R270S2
|
||||
327+3.3V C15 -1 A01X+057087Y-021870X0394Y0571R270S2
|
||||
327ET-(U3-XTAL_N) Y1 -1 A01X+056250Y-022874X0453Y0394R180S2
|
||||
327GND Y1 -2 A01X+055561Y-022874X0453Y0394R180S2
|
||||
327GND Y1 -3 A01X+055561Y-023425X0453Y0394R180S2
|
||||
327ET-(U3-XTAL_P) Y1 -4 A01X+056250Y-023425X0453Y0394R180S2
|
||||
327GND C10 -2 A01X+055531Y-024173X0394Y0571R180S2
|
||||
327ET-(U3-XTAL_P) C10 -1 A01X+056280Y-024173X0394Y0571R180S2
|
||||
327GND C11 -2 A01X+055531Y-022126X0394Y0571R180S2
|
||||
327ET-(U3-XTAL_N) C11 -1 A01X+056280Y-022126X0394Y0571R180S2
|
||||
327DP R13 -2 A01X+058740Y-020202X0404Y0551R090S2
|
||||
327-(J1-D+-PADA6) R13 -1 A01X+058740Y-019483X0404Y0551R090S2
|
||||
327GND C13 -2 A01X+062374Y-022625X0394Y0571R000S2
|
||||
327+3.3V C13 -1 A01X+061626Y-022625X0394Y0571R000S2
|
||||
327GND C12 -2 A01X+057087Y-024921X0394Y0571R090S2
|
||||
327+3.3V C12 -1 A01X+057087Y-024173X0394Y0571R090S2
|
||||
327POWER_RAIL_IN U2 -10 A01X+065366Y-022801X0571Y0118R270S2
|
||||
327POWER_RAIL_OUT U2 -9 A01X+065563Y-022801X0571Y0118R270S2
|
||||
327POWER_RAIL_IN U2 -8 A01X+065760Y-022801X0571Y0118R270S2
|
||||
327GND U2 -7 A01X+065957Y-022801X0571Y0118R270S2
|
||||
327+3.3V U2 -6 A01X+066154Y-022801X0571Y0118R270S2
|
||||
327SCL U2 -5 A01X+066154Y-024533X0571Y0118R270S2
|
||||
327SDA U2 -4 A01X+065957Y-024533X0571Y0118R270S2
|
||||
327~{ALERT}-PAD3) U2 -3 A01X+065760Y-024533X0571Y0118R270S2
|
||||
327D-(U2-A0-PAD2) U2 -2 A01X+065563Y-024533X0571Y0118R270S2
|
||||
327D-(U2-A1-PAD1) U2 -1 A01X+065366Y-024533X0571Y0118R270S2
|
||||
327GND C16 -2 A01X+058110Y-025492X0394Y0571R090S2
|
||||
327+3.3V C16 -1 A01X+058110Y-024744X0394Y0571R090S2
|
||||
327ET-(U3-GPIO17) TP9 -1 A01X+063625Y-021875X0787Y0000R000S2
|
||||
327POWER_RAIL_OUT R9 -2 A01X+064715Y-020000X0482Y1043R180S2
|
||||
327POWER_RAIL_IN R9 -1 A01X+066535Y-020000X0482Y1043R180S2
|
||||
327RX TP5 -1 A01X+069500Y-026250X0787Y0000R000S2
|
||||
327ET-(J14-PIN_2) R6 -2 A01X+061855Y-025197X0404Y0551R000S2
|
||||
327RGB R6 -1 A01X+061137Y-025197X0404Y0551R000S2
|
||||
327ET-(U3-GPIO14) TP8 -1 A01X+063625Y-023500X0787Y0000R000S2
|
||||
327GND C9 -2 A01X+066929Y-024075X0394Y0571R090S2
|
||||
327+3.3V C9 -1 A01X+066929Y-023327X0394Y0571R090S2
|
||||
327DM R12 -2 A01X+059528Y-020202X0404Y0551R090S2
|
||||
327-(J1-D--PADA7) R12 -1 A01X+059528Y-019483X0404Y0551R090S2
|
||||
327ET-(U3-GPIO18) TP10 -1 A01X+062125Y-020625X0787Y0000R000S2
|
||||
317RX3 J3 -3 D0394PA00X+073750Y-025474X0669Y0000R000S0
|
||||
317TX3 J3 -2 D0394PA00X+073750Y-024474X0669Y0000R000S0
|
||||
317GND J3 -1 D0394PA00X+073750Y-023474X0669Y0669R000S0
|
||||
327NET-(U3-GPIO6) TP6 -1 A01X+060079Y-026457X0787Y0000R000S2
|
||||
327RX4 U10 -1 A01X+071500Y-021500X0768Y0236R090S2
|
||||
327-(U10-A6-PAD2) U10 -2 A01X+071000Y-021500X0768Y0236R090S2
|
||||
327TX U10 -3 A01X+070500Y-021500X0768Y0236R090S2
|
||||
327-(U10-A7-PAD4) U10 -4 A01X+070000Y-021500X0768Y0236R090S2
|
||||
327-(U10-A5-PAD5) U10 -5 A01X+069500Y-021500X0768Y0236R090S2
|
||||
327GND U10 -6 A01X+069000Y-021500X0768Y0236R090S2
|
||||
327GND U10 -7 A01X+068500Y-021500X0768Y0236R090S2
|
||||
327GND U10 -8 A01X+068000Y-021500X0768Y0236R090S2
|
||||
327A2 U10 -9 A01X+068000Y-023449X0768Y0236R090S2
|
||||
327A1 U10 -10 A01X+068500Y-023449X0768Y0236R090S2
|
||||
327A0 U10 -11 A01X+069000Y-023449X0768Y0236R090S2
|
||||
327RX3 U10 -12 A01X+069500Y-023449X0768Y0236R090S2
|
||||
327RX0 U10 -13 A01X+070000Y-023449X0768Y0236R090S2
|
||||
327RX1 U10 -14 A01X+070500Y-023449X0768Y0236R090S2
|
||||
327RX2 U10 -15 A01X+071000Y-023449X0768Y0236R090S2
|
||||
327+3.3V U10 -16 A01X+071500Y-023449X0768Y0236R090S2
|
||||
327+5V D6 -1 A01X+060625Y-029000X0354Y0472R180S2
|
||||
327NET-(D6-A) D6 -2 A01X+059326Y-029000X0354Y0472R180S2
|
||||
327NET-(J1-CC2) R11 -1 A01X+057493Y-019442X0404Y0551R180S2
|
||||
327GND R11 -2 A01X+056775Y-019442X0404Y0551R180S2
|
||||
327+5V C19 -1 A01X+062374Y-028500X0394Y0571R180S2
|
||||
327GND C19 -2 A01X+061626Y-028500X0394Y0571R180S2
|
||||
327T-(U3-CHIP_PU) R15 -1 A01X+058189Y-026176X0404Y0551R090S2
|
||||
327+3.3V R15 -2 A01X+058189Y-026895X0404Y0551R090S2
|
||||
327TX R7 -1 A01X+070641Y-020250X0404Y0551R000S2
|
||||
327+3.3V R7 -2 A01X+071359Y-020250X0404Y0551R000S2
|
||||
317GND H4 -1 D1063PA00X+075250Y-017125X2126Y0000R000S0
|
||||
327N/C FID3 A01X+066750Y-030125X0591Y0000R000S2
|
||||
327+3.3V C17 -1 A01X+064376Y-026500X0394Y0571R000S2
|
||||
327GND C17 -2 A01X+065124Y-026500X0394Y0571R000S2
|
||||
317GND J5 -1 D0394PA00X+075750Y-023474X0669Y0669R000S0
|
||||
317TX1 J5 -2 D0394PA00X+075750Y-024474X0669Y0000R000S0
|
||||
317RX1 J5 -3 D0394PA00X+075750Y-025474X0669Y0000R000S0
|
||||
327TX4 U11 -1 A01X+071500Y-027500X0768Y0236R090S2
|
||||
327-(U11-A6-PAD2) U11 -2 A01X+071000Y-027500X0768Y0236R090S2
|
||||
327RX U11 -3 A01X+070500Y-027500X0768Y0236R090S2
|
||||
327-(U11-A7-PAD4) U11 -4 A01X+070000Y-027500X0768Y0236R090S2
|
||||
327-(U11-A5-PAD5) U11 -5 A01X+069500Y-027500X0768Y0236R090S2
|
||||
327GND U11 -6 A01X+069000Y-027500X0768Y0236R090S2
|
||||
327GND U11 -7 A01X+068500Y-027500X0768Y0236R090S2
|
||||
327GND U11 -8 A01X+068000Y-027500X0768Y0236R090S2
|
||||
327A2 U11 -9 A01X+068000Y-029449X0768Y0236R090S2
|
||||
327A1 U11 -10 A01X+068500Y-029449X0768Y0236R090S2
|
||||
327A0 U11 -11 A01X+069000Y-029449X0768Y0236R090S2
|
||||
327TX3 U11 -12 A01X+069500Y-029449X0768Y0236R090S2
|
||||
327TX0 U11 -13 A01X+070000Y-029449X0768Y0236R090S2
|
||||
327TX1 U11 -14 A01X+070500Y-029449X0768Y0236R090S2
|
||||
327TX2 U11 -15 A01X+071000Y-029449X0768Y0236R090S2
|
||||
327+3.3V U11 -16 A01X+071500Y-029449X0768Y0236R090S2
|
||||
327+3.3V TP3 -1 A01X+063375Y-025250X0787Y0000R000S2
|
||||
317EN0.0 J7 -1 D0394PA00X+047250Y-018992X0669Y0669R180S0
|
||||
317EN0.1 J7 -2 D0394PA00X+047250Y-017992X0669Y0000R180S0
|
||||
327+3.3V C4 -1 A01X+056249Y-025125X0394Y0571R180S2
|
||||
327GND C4 -2 A01X+055501Y-025125X0394Y0571R180S2
|
||||
327GND SW1 -1 A01X+069300Y-016739X0630Y0551R180S2
|
||||
327GND SW1 -1 A01X+072450Y-016739X0630Y0551R180S2
|
||||
327NET-(U3-GPIO0) SW1 -2 A01X+069300Y-018511X0630Y0551R180S2
|
||||
327NET-(U3-GPIO0) SW1 -2 A01X+072450Y-018511X0630Y0551R180S2
|
||||
327NET-(R3-PAD1) U6 -1 A01X+052595Y-024000X0719Y0256R180S2
|
||||
327GND U6 -2 A01X+052595Y-023000X0719Y0256R180S2
|
||||
327EN2.1 U6 -3 A01X+048905Y-023000X0719Y0256R180S2
|
||||
327EN2.0 U6 -4 A01X+048905Y-024000X0719Y0256R180S2
|
||||
327NET-(R5-PAD1) U8 -1 A01X+052595Y-029000X0719Y0256R180S2
|
||||
327GND U8 -2 A01X+052595Y-028000X0719Y0256R180S2
|
||||
327EN4.1 U8 -3 A01X+048905Y-028000X0719Y0256R180S2
|
||||
327EN4.0 U8 -4 A01X+048905Y-029000X0719Y0256R180S2
|
||||
327+3.3V C7 -1 A01X+072250Y-029376X0394Y0571R090S2
|
||||
327GND C7 -2 A01X+072250Y-030124X0394Y0571R090S2
|
||||
327NET-(R4-PAD1) U7 -1 A01X+052595Y-026500X0719Y0256R180S2
|
||||
327GND U7 -2 A01X+052595Y-025500X0719Y0256R180S2
|
||||
327EN3.1 U7 -3 A01X+048905Y-025500X0719Y0256R180S2
|
||||
327EN3.0 U7 -4 A01X+048905Y-026500X0719Y0256R180S2
|
||||
327NET-(U3-GPIO0) R14 -1 A01X+059055Y-026176X0404Y0551R090S2
|
||||
327+3.3V R14 -2 A01X+059055Y-026895X0404Y0551R090S2
|
||||
327NET-(J1-CC1) R10 -1 A01X+060775Y-019442X0404Y0551R000S2
|
||||
327GND R10 -2 A01X+061493Y-019442X0404Y0551R000S2
|
||||
317EN1.0 J8 -1 D0394PA00X+047250Y-021500X0669Y0669R180S0
|
||||
317EN1.1 J8 -2 D0394PA00X+047250Y-020500X0669Y0000R180S0
|
||||
327NET-(U3-GPIO7) TP7 -1 A01X+061339Y-026457X0787Y0000R000S2
|
||||
327+3.3V C8 -1 A01X+071374Y-024724X0394Y0571R180S2
|
||||
327GND C8 -2 A01X+070626Y-024724X0394Y0571R180S2
|
||||
327RX R8 -1 A01X+070641Y-026220X0404Y0551R000S2
|
||||
327+3.3V R8 -2 A01X+071359Y-026220X0404Y0551R000S2
|
||||
327NET-(R1-PAD1) R1 -1 A01X+053625Y-019016X0404Y0551R090S2
|
||||
327EN0 R1 -2 A01X+053625Y-019734X0404Y0551R090S2
|
||||
327+5V C20 -1 A01X+062375Y-027625X0394Y0571R180S2
|
||||
327GND C20 -2 A01X+061627Y-027625X0394Y0571R180S2
|
||||
317GND J2 -1 D0394PA00X+073750Y-027724X0669Y0669R000S0
|
||||
317TX4 J2 -2 D0394PA00X+073750Y-028724X0669Y0000R000S0
|
||||
317RX4 J2 -3 D0394PA00X+073750Y-029724X0669Y0000R000S0
|
||||
327+3.3V C14 -1 A01X+056124Y-021250X0394Y0571R180S2
|
||||
327GND C14 -2 A01X+055376Y-021250X0394Y0571R180S2
|
||||
317GND J6 -1 D0394PA00X+075750Y-019474X0669Y0669R000S0
|
||||
317TX0 J6 -2 D0394PA00X+075750Y-020474X0669Y0000R000S0
|
||||
317RX0 J6 -3 D0394PA00X+075750Y-021474X0669Y0000R000S0
|
||||
327N/C FID2 A01X+047750Y-016500X0591Y0000R000S2
|
||||
327GND U1 -1 A01X+065281Y-027510X0787Y0591R090S2
|
||||
327+3.3V U1 -2 A01X+064375Y-027510X0787Y0591R090S2
|
||||
327+3.3V U1 -2 A01X+064375Y-029990X0787Y1496R090S2
|
||||
327+5V U1 -3 A01X+063469Y-027510X0787Y0591R090S2
|
||||
317EN3.0 J10 -1 D0394PA00X+047250Y-026500X0669Y0669R180S0
|
||||
317EN3.1 J10 -2 D0394PA00X+047250Y-025500X0669Y0000R180S0
|
||||
327NET-(R4-PAD1) R4 -1 A01X+053625Y-026516X0404Y0551R090S2
|
||||
327EN3 R4 -2 A01X+053625Y-027234X0404Y0551R090S2
|
||||
327NET-(R5-PAD1) R5 -1 A01X+053625Y-028984X0404Y0551R270S2
|
||||
327EN4 R5 -2 A01X+053625Y-028266X0404Y0551R270S2
|
||||
327NET-(R3-PAD1) R3 -1 A01X+053625Y-024016X0404Y0551R090S2
|
||||
327EN2 R3 -2 A01X+053625Y-024734X0404Y0551R090S2
|
||||
317GND H3 -1 D1063PA00X+054750Y-017125X2126Y0000R000S0
|
||||
317EN4.0 J11 -1 D0394PA00X+047250Y-029000X0669Y0669R180S0
|
||||
317EN4.1 J11 -2 D0394PA00X+047250Y-028000X0669Y0000R180S0
|
||||
327+5V TP2 -1 A01X+062625Y-026500X0787Y0000R000S2
|
||||
327+3.3V C18 -1 A01X+064376Y-025625X0394Y0571R000S2
|
||||
327GND C18 -2 A01X+065124Y-025625X0394Y0571R000S2
|
||||
317GND H1 -1 D1063PA00X+055625Y-029750X2126Y0000R000S0
|
||||
317NET-(D6-A) J13 -1 D0394PA00X+059326Y-030486X0669Y0669R270S0
|
||||
317GND J13 -2 D0394PA00X+060326Y-030486X0669Y0000R270S0
|
||||
317POWER_RAIL_IN J12 -1 D0630PA00X+066937Y-017414X0787Y1024R180S0
|
||||
317GND J12 -2 D0630PA00X+065559Y-017414X0787Y1024R180S0
|
||||
317POWER_RAIL_OUT J12 -3 D0630PA00X+064181Y-017414X0787Y1024R180S0
|
||||
317GND J4 -1 D0394PA00X+075750Y-027724X0669Y0669R000S0
|
||||
317TX2 J4 -2 D0394PA00X+075750Y-028724X0669Y0000R000S0
|
||||
317RX2 J4 -3 D0394PA00X+075750Y-029724X0669Y0000R000S0
|
||||
327NET-(R2-PAD1) R2 -1 A01X+053625Y-021500X0404Y0551R090S2
|
||||
327EN1 R2 -2 A01X+053625Y-022219X0404Y0551R090S2
|
||||
327N/C FID1 A01X+073500Y-020000X0591Y0000R000S2
|
||||
327NET-(R2-PAD1) U5 -1 A01X+052595Y-021500X0719Y0256R180S2
|
||||
327GND U5 -2 A01X+052595Y-020500X0719Y0256R180S2
|
||||
327EN1.1 U5 -3 A01X+048905Y-020500X0719Y0256R180S2
|
||||
327EN1.0 U5 -4 A01X+048905Y-021500X0719Y0256R180S2
|
||||
317EN2.0 J9 -1 D0394PA00X+047250Y-024000X0669Y0669R180S0
|
||||
317EN2.1 J9 -2 D0394PA00X+047250Y-023000X0669Y0000R180S0
|
||||
999
|
||||
BIN
kicad/watch-watchkicad_pro/production/watch-watchkicad_pro.zip
Normal file
BIN
kicad/watch-watchkicad_pro/production/watch-watchkicad_pro.zip
Normal file
Binary file not shown.
27041
kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_pcb
Normal file
27041
kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_pcb
Normal file
File diff suppressed because it is too large
Load Diff
669
kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_pro
Normal file
669
kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_pro
Normal file
@@ -0,0 +1,669 @@
|
||||
{
|
||||
"board": {
|
||||
"3dviewports": [],
|
||||
"design_settings": {
|
||||
"defaults": {
|
||||
"apply_defaults_to_fp_fields": false,
|
||||
"apply_defaults_to_fp_shapes": false,
|
||||
"apply_defaults_to_fp_text": false,
|
||||
"board_outline_line_width": 0.05,
|
||||
"copper_line_width": 0.2,
|
||||
"copper_text_italic": false,
|
||||
"copper_text_size_h": 1.5,
|
||||
"copper_text_size_v": 1.5,
|
||||
"copper_text_thickness": 0.3,
|
||||
"copper_text_upright": false,
|
||||
"courtyard_line_width": 0.05,
|
||||
"dimension_precision": 4,
|
||||
"dimension_units": 3,
|
||||
"dimensions": {
|
||||
"arrow_length": 1270000,
|
||||
"extension_offset": 500000,
|
||||
"keep_text_aligned": true,
|
||||
"suppress_zeroes": true,
|
||||
"text_position": 0,
|
||||
"units_format": 0
|
||||
},
|
||||
"fab_line_width": 0.1,
|
||||
"fab_text_italic": false,
|
||||
"fab_text_size_h": 1.0,
|
||||
"fab_text_size_v": 1.0,
|
||||
"fab_text_thickness": 0.15,
|
||||
"fab_text_upright": false,
|
||||
"other_line_width": 0.1,
|
||||
"other_text_italic": false,
|
||||
"other_text_size_h": 1.0,
|
||||
"other_text_size_v": 1.0,
|
||||
"other_text_thickness": 0.15,
|
||||
"other_text_upright": false,
|
||||
"pads": {
|
||||
"drill": 1.0,
|
||||
"height": 1.7,
|
||||
"width": 1.7
|
||||
},
|
||||
"silk_line_width": 0.1,
|
||||
"silk_text_italic": false,
|
||||
"silk_text_size_h": 1.0,
|
||||
"silk_text_size_v": 1.0,
|
||||
"silk_text_thickness": 0.1,
|
||||
"silk_text_upright": false,
|
||||
"zones": {
|
||||
"min_clearance": 0.4
|
||||
}
|
||||
},
|
||||
"diff_pair_dimensions": [],
|
||||
"drc_exclusions": [],
|
||||
"meta": {
|
||||
"version": 2
|
||||
},
|
||||
"rule_severities": {
|
||||
"annular_width": "error",
|
||||
"clearance": "error",
|
||||
"connection_width": "warning",
|
||||
"copper_edge_clearance": "error",
|
||||
"copper_sliver": "warning",
|
||||
"courtyards_overlap": "error",
|
||||
"creepage": "error",
|
||||
"diff_pair_gap_out_of_range": "error",
|
||||
"diff_pair_uncoupled_length_too_long": "error",
|
||||
"drill_out_of_range": "error",
|
||||
"duplicate_footprints": "warning",
|
||||
"extra_footprint": "warning",
|
||||
"footprint": "error",
|
||||
"footprint_filters_mismatch": "ignore",
|
||||
"footprint_symbol_mismatch": "warning",
|
||||
"footprint_type_mismatch": "ignore",
|
||||
"hole_clearance": "error",
|
||||
"hole_to_hole": "warning",
|
||||
"holes_co_located": "warning",
|
||||
"invalid_outline": "error",
|
||||
"isolated_copper": "warning",
|
||||
"item_on_disabled_layer": "error",
|
||||
"items_not_allowed": "error",
|
||||
"length_out_of_range": "error",
|
||||
"lib_footprint_issues": "warning",
|
||||
"lib_footprint_mismatch": "warning",
|
||||
"malformed_courtyard": "error",
|
||||
"microvia_drill_out_of_range": "error",
|
||||
"mirrored_text_on_front_layer": "warning",
|
||||
"missing_courtyard": "ignore",
|
||||
"missing_footprint": "warning",
|
||||
"net_conflict": "warning",
|
||||
"nonmirrored_text_on_back_layer": "warning",
|
||||
"npth_inside_courtyard": "ignore",
|
||||
"padstack": "warning",
|
||||
"pth_inside_courtyard": "ignore",
|
||||
"shorting_items": "error",
|
||||
"silk_edge_clearance": "warning",
|
||||
"silk_over_copper": "warning",
|
||||
"silk_overlap": "warning",
|
||||
"skew_out_of_range": "error",
|
||||
"solder_mask_bridge": "error",
|
||||
"starved_thermal": "error",
|
||||
"text_height": "warning",
|
||||
"text_on_edge_cuts": "error",
|
||||
"text_thickness": "warning",
|
||||
"through_hole_pad_without_hole": "error",
|
||||
"too_many_vias": "error",
|
||||
"track_angle": "error",
|
||||
"track_dangling": "warning",
|
||||
"track_segment_length": "error",
|
||||
"track_width": "error",
|
||||
"tracks_crossing": "error",
|
||||
"unconnected_items": "error",
|
||||
"unresolved_variable": "error",
|
||||
"via_dangling": "warning",
|
||||
"zones_intersect": "error"
|
||||
},
|
||||
"rules": {
|
||||
"max_error": 0.005,
|
||||
"min_clearance": 0.0,
|
||||
"min_connection": 0.0,
|
||||
"min_copper_edge_clearance": 0.5,
|
||||
"min_groove_width": 0.0,
|
||||
"min_hole_clearance": 0.25,
|
||||
"min_hole_to_hole": 0.25,
|
||||
"min_microvia_diameter": 0.2,
|
||||
"min_microvia_drill": 0.1,
|
||||
"min_resolved_spokes": 2,
|
||||
"min_silk_clearance": 0.0,
|
||||
"min_text_height": 0.8,
|
||||
"min_text_thickness": 0.08,
|
||||
"min_through_hole_diameter": 0.3,
|
||||
"min_track_width": 0.0,
|
||||
"min_via_annular_width": 0.1,
|
||||
"min_via_diameter": 0.5,
|
||||
"solder_mask_to_copper_clearance": 0.005,
|
||||
"use_height_for_length_calcs": true
|
||||
},
|
||||
"teardrop_options": [
|
||||
{
|
||||
"td_onpthpad": true,
|
||||
"td_onroundshapesonly": false,
|
||||
"td_onsmdpad": true,
|
||||
"td_ontrackend": false,
|
||||
"td_onvia": true
|
||||
}
|
||||
],
|
||||
"teardrop_parameters": [
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_round_shape",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
},
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_rect_shape",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
},
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_track_end",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
}
|
||||
],
|
||||
"track_widths": [],
|
||||
"tuning_pattern_settings": {
|
||||
"diff_pair_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 1.0
|
||||
},
|
||||
"diff_pair_skew_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 0.6
|
||||
},
|
||||
"single_track_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 0.6
|
||||
}
|
||||
},
|
||||
"via_dimensions": [],
|
||||
"zones_allow_external_fillets": false
|
||||
},
|
||||
"ipc2581": {
|
||||
"dist": "",
|
||||
"distpn": "",
|
||||
"internal_id": "",
|
||||
"mfg": "",
|
||||
"mpn": ""
|
||||
},
|
||||
"layer_pairs": [],
|
||||
"layer_presets": [],
|
||||
"viewports": []
|
||||
},
|
||||
"boards": [],
|
||||
"cvpcb": {
|
||||
"equivalence_files": []
|
||||
},
|
||||
"erc": {
|
||||
"erc_exclusions": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"pin_map": [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
]
|
||||
],
|
||||
"rule_severities": {
|
||||
"bus_definition_conflict": "error",
|
||||
"bus_entry_needed": "error",
|
||||
"bus_to_bus_conflict": "error",
|
||||
"bus_to_net_conflict": "error",
|
||||
"different_unit_footprint": "error",
|
||||
"different_unit_net": "error",
|
||||
"duplicate_reference": "error",
|
||||
"duplicate_sheet_names": "error",
|
||||
"endpoint_off_grid": "warning",
|
||||
"extra_units": "error",
|
||||
"footprint_filter": "ignore",
|
||||
"footprint_link_issues": "warning",
|
||||
"four_way_junction": "ignore",
|
||||
"global_label_dangling": "warning",
|
||||
"hier_label_mismatch": "error",
|
||||
"label_dangling": "error",
|
||||
"label_multiple_wires": "warning",
|
||||
"lib_symbol_issues": "warning",
|
||||
"lib_symbol_mismatch": "warning",
|
||||
"missing_bidi_pin": "warning",
|
||||
"missing_input_pin": "warning",
|
||||
"missing_power_pin": "error",
|
||||
"missing_unit": "warning",
|
||||
"multiple_net_names": "warning",
|
||||
"net_not_bus_member": "warning",
|
||||
"no_connect_connected": "warning",
|
||||
"no_connect_dangling": "warning",
|
||||
"pin_not_connected": "error",
|
||||
"pin_not_driven": "error",
|
||||
"pin_to_pin": "warning",
|
||||
"power_pin_not_driven": "error",
|
||||
"same_local_global_label": "warning",
|
||||
"similar_label_and_power": "warning",
|
||||
"similar_labels": "warning",
|
||||
"similar_power": "warning",
|
||||
"simulation_model_issue": "ignore",
|
||||
"single_global_label": "ignore",
|
||||
"unannotated": "error",
|
||||
"unconnected_wire_endpoint": "warning",
|
||||
"undefined_netclass": "error",
|
||||
"unit_value_mismatch": "error",
|
||||
"unresolved_variable": "error",
|
||||
"wire_dangling": "error"
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"pinned_footprint_libs": [],
|
||||
"pinned_symbol_libs": []
|
||||
},
|
||||
"meta": {
|
||||
"filename": "watch-watchkicad_pro.kicad_pro",
|
||||
"version": 3
|
||||
},
|
||||
"net_settings": {
|
||||
"classes": [
|
||||
{
|
||||
"bus_width": 12,
|
||||
"clearance": 0.2,
|
||||
"diff_pair_gap": 0.25,
|
||||
"diff_pair_via_gap": 0.25,
|
||||
"diff_pair_width": 0.2,
|
||||
"line_style": 0,
|
||||
"microvia_diameter": 0.3,
|
||||
"microvia_drill": 0.1,
|
||||
"name": "Default",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"priority": 2147483647,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.2,
|
||||
"via_diameter": 0.6,
|
||||
"via_drill": 0.3,
|
||||
"wire_width": 6
|
||||
},
|
||||
{
|
||||
"clearance": 0.2,
|
||||
"diff_pair_gap": 0.25,
|
||||
"diff_pair_width": 0.2,
|
||||
"microvia_diameter": 0.3,
|
||||
"microvia_drill": 0.1,
|
||||
"name": "DigitalSig",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"priority": 0,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.3,
|
||||
"via_diameter": 0.6,
|
||||
"via_drill": 0.3
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"version": 4
|
||||
},
|
||||
"net_colors": null,
|
||||
"netclass_assignments": null,
|
||||
"netclass_patterns": []
|
||||
},
|
||||
"pcbnew": {
|
||||
"last_paths": {
|
||||
"gencad": "",
|
||||
"idf": "",
|
||||
"netlist": "",
|
||||
"plot": "",
|
||||
"pos_files": "",
|
||||
"specctra_dsn": "",
|
||||
"step": "",
|
||||
"svg": "",
|
||||
"vrml": ""
|
||||
},
|
||||
"page_layout_descr_file": ""
|
||||
},
|
||||
"schematic": {
|
||||
"annotate_start_num": 0,
|
||||
"bom_export_filename": "${PROJECTNAME}.csv",
|
||||
"bom_fmt_presets": [],
|
||||
"bom_fmt_settings": {
|
||||
"field_delimiter": ",",
|
||||
"keep_line_breaks": false,
|
||||
"keep_tabs": false,
|
||||
"name": "CSV",
|
||||
"ref_delimiter": ",",
|
||||
"ref_range_delimiter": "",
|
||||
"string_delimiter": "\""
|
||||
},
|
||||
"bom_presets": [],
|
||||
"bom_settings": {
|
||||
"exclude_dnp": false,
|
||||
"fields_ordered": [
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Reference",
|
||||
"name": "Reference",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Qty",
|
||||
"name": "${QUANTITY}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Value",
|
||||
"name": "Value",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "DNP",
|
||||
"name": "${DNP}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Exclude from BOM",
|
||||
"name": "${EXCLUDE_FROM_BOM}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Exclude from Board",
|
||||
"name": "${EXCLUDE_FROM_BOARD}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Footprint",
|
||||
"name": "Footprint",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Datasheet",
|
||||
"name": "Datasheet",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "постачальник",
|
||||
"name": "постачальник",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Постачальник",
|
||||
"name": "Постачальник",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Опис",
|
||||
"name": "Description",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "#",
|
||||
"name": "${ITEM_NUMBER}",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Sim.Pins",
|
||||
"name": "Sim.Pins",
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Sim.Device",
|
||||
"name": "Sim.Device",
|
||||
"show": false
|
||||
}
|
||||
],
|
||||
"filter_string": "",
|
||||
"group_symbols": true,
|
||||
"include_excluded_from_bom": true,
|
||||
"name": "",
|
||||
"sort_asc": true,
|
||||
"sort_field": "Reference"
|
||||
},
|
||||
"connection_grid_size": 50.0,
|
||||
"drawing": {
|
||||
"dashed_lines_dash_length_ratio": 12.0,
|
||||
"dashed_lines_gap_length_ratio": 3.0,
|
||||
"default_line_thickness": 6.0,
|
||||
"default_text_size": 50.0,
|
||||
"field_names": [],
|
||||
"intersheets_ref_own_page": false,
|
||||
"intersheets_ref_prefix": "",
|
||||
"intersheets_ref_short": false,
|
||||
"intersheets_ref_show": false,
|
||||
"intersheets_ref_suffix": "",
|
||||
"junction_size_choice": 3,
|
||||
"label_size_ratio": 0.375,
|
||||
"operating_point_overlay_i_precision": 3,
|
||||
"operating_point_overlay_i_range": "~A",
|
||||
"operating_point_overlay_v_precision": 3,
|
||||
"operating_point_overlay_v_range": "~V",
|
||||
"overbar_offset_ratio": 1.23,
|
||||
"pin_symbol_size": 25.0,
|
||||
"text_offset_ratio": 0.15
|
||||
},
|
||||
"legacy_lib_dir": "",
|
||||
"legacy_lib_list": [],
|
||||
"meta": {
|
||||
"version": 1
|
||||
},
|
||||
"net_format_name": "",
|
||||
"page_layout_descr_file": "",
|
||||
"plot_directory": "",
|
||||
"space_save_all_events": true,
|
||||
"spice_current_sheet_as_root": false,
|
||||
"spice_external_command": "spice \"%I\"",
|
||||
"spice_model_current_sheet_as_root": true,
|
||||
"spice_save_all_currents": false,
|
||||
"spice_save_all_dissipations": false,
|
||||
"spice_save_all_voltages": false,
|
||||
"subpart_first_id": 65,
|
||||
"subpart_id_separator": 0
|
||||
},
|
||||
"sheets": [
|
||||
[
|
||||
"e717a214-aafd-4c38-81fe-477ca8e64d07",
|
||||
"Root"
|
||||
]
|
||||
],
|
||||
"text_variables": {}
|
||||
}
|
||||
18687
kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_sch
Normal file
18687
kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_sch
Normal file
File diff suppressed because it is too large
Load Diff
BIN
kicad/watch-watchkicad_pro/watch-watchkicad_pro.pdf
Normal file
BIN
kicad/watch-watchkicad_pro/watch-watchkicad_pro.pdf
Normal file
Binary file not shown.
@@ -1,4 +1,8 @@
|
||||
idf_component_register(SRCS "main.c"
|
||||
idf_component_register(SRCS "ws2812_status.c" "watch_config.c" "usb_cdc_cli.c" "main.c"
|
||||
"dcdc_controller.c"
|
||||
"usb_cdc_cli.c"
|
||||
INCLUDE_DIRS ".")
|
||||
"usb_cdc_log.c"
|
||||
|
||||
"ina226_monitor.c"
|
||||
"uart_mux.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES nvs_flash driver esp_timer)
|
||||
|
||||
156
main/Kconfig.projbuild
Normal file
156
main/Kconfig.projbuild
Normal file
@@ -0,0 +1,156 @@
|
||||
menu "Налаштування watch-watch"
|
||||
|
||||
menu "DC/DC контролер"
|
||||
|
||||
config WATCH_DCDC_EN_GPIO_0
|
||||
int "GPIO EN каналу 0"
|
||||
range 0 48
|
||||
default 2
|
||||
|
||||
config WATCH_DCDC_EN_GPIO_1
|
||||
int "GPIO EN каналу 1"
|
||||
range 0 48
|
||||
default 4
|
||||
|
||||
config WATCH_DCDC_EN_GPIO_2
|
||||
int "GPIO EN каналу 2"
|
||||
range 0 48
|
||||
default 5
|
||||
|
||||
config WATCH_DCDC_EN_GPIO_3
|
||||
int "GPIO EN каналу 3"
|
||||
range 0 48
|
||||
default 18
|
||||
|
||||
config WATCH_DCDC_EN_GPIO_4
|
||||
int "GPIO EN каналу 4"
|
||||
range 0 48
|
||||
default 19
|
||||
|
||||
endmenu
|
||||
|
||||
config WATCH_WS2812_LED_COUNT
|
||||
int "Кількість статусних світлодіодів WS2812"
|
||||
range 1 30
|
||||
default 5
|
||||
help
|
||||
Визначає кількість послідовно з’єднаних WS2812 індикаторів,
|
||||
що показують стан каналів DC/DC.
|
||||
|
||||
config WATCH_WS2812_GPIO
|
||||
int "GPIO для WS2812"
|
||||
range 0 48
|
||||
default 8
|
||||
help
|
||||
Встановіть номер GPIO, до якого підключено стрічку WS2812
|
||||
(за замовчуванням GPIO8).
|
||||
|
||||
config WATCH_WS2812_RMT_RESOLUTION
|
||||
int "RMT роздільна здатність (Гц)"
|
||||
default 10000000
|
||||
help
|
||||
Тактова частота RMT драйвера для управління WS2812.
|
||||
10 МГц = 0.1 мкс на імпульс.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "INA226 моніторинг"
|
||||
|
||||
config WATCH_INA226_ENABLED
|
||||
bool "Увімкнути моніторинг INA226"
|
||||
default y
|
||||
help
|
||||
Якщо увімкнено, ESP32-S3 опитує датчики INA226 для вимірювання
|
||||
напруги, струму та потужності кожного каналу.
|
||||
|
||||
config WATCH_INA226_I2C_PORT
|
||||
int "I2C порт"
|
||||
range 0 1
|
||||
default 0
|
||||
|
||||
config WATCH_INA226_I2C_SDA
|
||||
int "GPIO SDA"
|
||||
range 0 48
|
||||
default 6
|
||||
|
||||
config WATCH_INA226_I2C_SCL
|
||||
int "GPIO SCL"
|
||||
range 0 48
|
||||
default 7
|
||||
|
||||
config WATCH_INA226_I2C_FREQ_HZ
|
||||
int "Швидкість I2C (Гц)"
|
||||
range 10000 1000000
|
||||
default 400000
|
||||
|
||||
config WATCH_INA226_SHUNT_MILLIOHM
|
||||
int "Опір шунта (мОм)"
|
||||
range 1 500
|
||||
default 10
|
||||
|
||||
config WATCH_INA226_CURRENT_LSB_uA
|
||||
int "Крок струму (мкА/LSB)"
|
||||
range 10 10000
|
||||
default 100
|
||||
|
||||
config WATCH_INA226_SAMPLE_INTERVAL_MS
|
||||
int "Інтервал опитування (мс)"
|
||||
range 50 5000
|
||||
default 500
|
||||
|
||||
config WATCH_INA226_ADDR
|
||||
hex "Адреса INA226 (загальна шина)"
|
||||
default 0x40
|
||||
|
||||
endmenu
|
||||
|
||||
menu "UART мультиплексор"
|
||||
|
||||
config WATCH_UART_MUX_ENABLED
|
||||
bool "Увімкнути взаємодію з Raspberry Pi через UART"
|
||||
default y
|
||||
|
||||
config WATCH_UART_MUX_CHANNELS
|
||||
int "Кількість каналів (Raspberry Pi)"
|
||||
range 1 8
|
||||
default 5
|
||||
|
||||
config WATCH_UART_PORT
|
||||
int "Номер UART"
|
||||
range 0 2
|
||||
default 1
|
||||
|
||||
config WATCH_UART_BAUD
|
||||
int "Швидкість UART, біт/с"
|
||||
default 115200
|
||||
|
||||
config WATCH_UART_TX_GPIO
|
||||
int "GPIO TX"
|
||||
default 17
|
||||
|
||||
config WATCH_UART_RX_GPIO
|
||||
int "GPIO RX"
|
||||
default 16
|
||||
|
||||
config WATCH_UART_MUX_SEL_A0
|
||||
int "GPIO A0"
|
||||
default 9
|
||||
|
||||
config WATCH_UART_MUX_SEL_A1
|
||||
int "GPIO A1"
|
||||
default 10
|
||||
|
||||
config WATCH_UART_MUX_SEL_A2
|
||||
int "GPIO A2"
|
||||
default 11
|
||||
|
||||
config WATCH_UART_MUX_DEFAULT_READ_LEN
|
||||
int "Типова довжина читання (байт)"
|
||||
default 128
|
||||
|
||||
config WATCH_UART_HEARTBEAT_TIMEOUT_SEC
|
||||
int "Тайм-аут heartbeat (сек.)"
|
||||
range 5 600
|
||||
default 60
|
||||
|
||||
endmenu
|
||||
@@ -2,15 +2,32 @@
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static const char *TAG = "dcdc";
|
||||
|
||||
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_0
|
||||
#define CONFIG_WATCH_DCDC_EN_GPIO_0 2
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_1
|
||||
#define CONFIG_WATCH_DCDC_EN_GPIO_1 4
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_2
|
||||
#define CONFIG_WATCH_DCDC_EN_GPIO_2 5
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_3
|
||||
#define CONFIG_WATCH_DCDC_EN_GPIO_3 18
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_4
|
||||
#define CONFIG_WATCH_DCDC_EN_GPIO_4 19
|
||||
#endif
|
||||
|
||||
static const gpio_num_t s_dcdc_gpio_map[DCDC_CHANNEL_COUNT] = {
|
||||
GPIO_NUM_2,
|
||||
GPIO_NUM_4,
|
||||
GPIO_NUM_5,
|
||||
GPIO_NUM_18,
|
||||
GPIO_NUM_19,
|
||||
(gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_0,
|
||||
(gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_1,
|
||||
(gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_2,
|
||||
(gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_3,
|
||||
(gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_4,
|
||||
};
|
||||
|
||||
static bool s_initialized;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_tinyusb: "^1"
|
||||
espressif/led_strip: "^2"
|
||||
|
||||
185
main/ina226_monitor.c
Normal file
185
main/ina226_monitor.c
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Developed by TComLab
|
||||
* Version: v0.1
|
||||
* Date: 2025-12-15
|
||||
*/
|
||||
|
||||
#include "ina226_monitor.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "driver/i2c.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifndef CONFIG_WATCH_INA226_ENABLED
|
||||
#define CONFIG_WATCH_INA226_ENABLED 0
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_INA226_I2C_PORT
|
||||
#define CONFIG_WATCH_INA226_I2C_PORT 0
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_INA226_I2C_SDA
|
||||
#define CONFIG_WATCH_INA226_I2C_SDA 6
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_INA226_I2C_SCL
|
||||
#define CONFIG_WATCH_INA226_I2C_SCL 7
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_INA226_I2C_FREQ_HZ
|
||||
#define CONFIG_WATCH_INA226_I2C_FREQ_HZ 400000
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_INA226_CURRENT_LSB_uA
|
||||
#define CONFIG_WATCH_INA226_CURRENT_LSB_uA 50
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_INA226_SHUNT_MILLIOHM
|
||||
#define CONFIG_WATCH_INA226_SHUNT_MILLIOHM 100
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_INA226_ADDR
|
||||
#define CONFIG_WATCH_INA226_ADDR 0x40
|
||||
#endif
|
||||
|
||||
#define INA226_REG_CONFIG 0x00
|
||||
#define INA226_REG_BUS 0x02
|
||||
#define INA226_REG_CURRENT 0x04
|
||||
#define INA226_REG_CALIBRATION 0x05
|
||||
|
||||
#define INA226_CONFIG_AVG_16 (0x04 << 9)
|
||||
#define INA226_CONFIG_VBUS_1100US (0x04 << 6)
|
||||
#define INA226_CONFIG_VSH_1100US (0x04 << 3)
|
||||
#define INA226_CONFIG_MODE_SHUNT_BUS_CONT 0x07
|
||||
|
||||
#if CONFIG_WATCH_INA226_ENABLED
|
||||
static const char *TAG = "ina226";
|
||||
static bool s_initialized;
|
||||
static float s_current_lsb_a;
|
||||
static uint8_t s_address = CONFIG_WATCH_INA226_ADDR;
|
||||
static ina226_reading_t s_last_reading;
|
||||
#endif
|
||||
|
||||
// Ініціалізує контролер INA226: налаштовує I2C, записує конфігурацію та
|
||||
// калібрувальні значення, а також зберігає початковий стан вимірювань.
|
||||
esp_err_t ina226_monitor_init(void)
|
||||
{
|
||||
#if !CONFIG_WATCH_INA226_ENABLED
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
if (s_initialized) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
i2c_config_t config = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = CONFIG_WATCH_INA226_I2C_SDA,
|
||||
.scl_io_num = CONFIG_WATCH_INA226_I2C_SCL,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = CONFIG_WATCH_INA226_I2C_FREQ_HZ,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(i2c_param_config(CONFIG_WATCH_INA226_I2C_PORT, &config), TAG, "i2c config failed");
|
||||
ESP_RETURN_ON_ERROR(i2c_driver_install(CONFIG_WATCH_INA226_I2C_PORT, config.mode, 0, 0, 0),
|
||||
TAG, "i2c driver install failed");
|
||||
|
||||
double current_lsb_a = ((double)CONFIG_WATCH_INA226_CURRENT_LSB_uA) / 1000000.0;
|
||||
double shunt_ohms = ((double)CONFIG_WATCH_INA226_SHUNT_MILLIOHM) / 1000.0;
|
||||
double calibration = 0.00512 / (current_lsb_a * shunt_ohms);
|
||||
if (calibration > 0xFFFF) calibration = 0xFFFF;
|
||||
uint16_t calibration_value = (uint16_t)calibration;
|
||||
s_current_lsb_a = (float)current_lsb_a;
|
||||
|
||||
const uint16_t config_value = INA226_CONFIG_AVG_16 |
|
||||
INA226_CONFIG_VBUS_1100US |
|
||||
INA226_CONFIG_VSH_1100US |
|
||||
INA226_CONFIG_MODE_SHUNT_BUS_CONT;
|
||||
|
||||
uint8_t payload_cfg[3] = { INA226_REG_CONFIG, (uint8_t)(config_value >> 8), (uint8_t)(config_value & 0xFF) };
|
||||
ESP_RETURN_ON_ERROR(i2c_master_write_to_device(CONFIG_WATCH_INA226_I2C_PORT,
|
||||
s_address, payload_cfg, sizeof(payload_cfg),
|
||||
pdMS_TO_TICKS(100)),
|
||||
TAG, "config write failed");
|
||||
|
||||
uint8_t payload_cal[3] = { INA226_REG_CALIBRATION, (uint8_t)(calibration_value >> 8),
|
||||
(uint8_t)(calibration_value & 0xFF) };
|
||||
ESP_RETURN_ON_ERROR(i2c_master_write_to_device(CONFIG_WATCH_INA226_I2C_PORT,
|
||||
s_address, payload_cal, sizeof(payload_cal),
|
||||
pdMS_TO_TICKS(100)),
|
||||
TAG, "calibration write failed");
|
||||
|
||||
s_last_reading = (ina226_reading_t){0};
|
||||
s_initialized = true;
|
||||
ESP_LOGI(TAG, "INA226 ініціалізовано (адреса 0x%02X)", s_address);
|
||||
return ESP_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Дозволяє швидко перевірити, чи був модуль INA226 успішно ініціалізований.
|
||||
bool ina226_monitor_ready(void)
|
||||
{
|
||||
#if CONFIG_WATCH_INA226_ENABLED
|
||||
return s_initialized;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// INA226 вимірює єдиний канал живлення, тому повертаємо 1.
|
||||
size_t ina226_monitor_channel_count(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if CONFIG_WATCH_INA226_ENABLED
|
||||
// Зчитує 16-бітний регістр INA226, використовуючи транзакцію write-then-read.
|
||||
static esp_err_t ina226_read_register(uint8_t reg, uint16_t *out_value)
|
||||
{
|
||||
uint8_t value[2];
|
||||
esp_err_t err = i2c_master_write_read_device(CONFIG_WATCH_INA226_I2C_PORT,
|
||||
s_address, ®, 1, value, sizeof(value),
|
||||
pdMS_TO_TICKS(100));
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
*out_value = ((uint16_t)value[0] << 8) | value[1];
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Виконує одне вимірювання напруги/струму через INA226 та кешує результат.
|
||||
esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading)
|
||||
{
|
||||
#if !CONFIG_WATCH_INA226_ENABLED
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
if (!s_initialized) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
uint16_t bus_raw = 0;
|
||||
uint16_t current_raw = 0;
|
||||
|
||||
ESP_RETURN_ON_ERROR(ina226_read_register(INA226_REG_BUS, &bus_raw), TAG, "bus read failed");
|
||||
ESP_RETURN_ON_ERROR(ina226_read_register(INA226_REG_CURRENT, ¤t_raw), TAG, "current read failed");
|
||||
|
||||
float voltage_v = (float)bus_raw * 1.25f / 1000.0f;
|
||||
float current_a = (int16_t)current_raw * s_current_lsb_a;
|
||||
float power_w = voltage_v * current_a;
|
||||
|
||||
s_last_reading = (ina226_reading_t){
|
||||
.voltage_v = voltage_v,
|
||||
.current_a = current_a,
|
||||
.power_w = power_w,
|
||||
};
|
||||
if (out_reading) {
|
||||
*out_reading = s_last_reading;
|
||||
}
|
||||
return ESP_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Повертає останній виміряний набір даних без нового звернення до шини I2C.
|
||||
const ina226_reading_t *ina226_monitor_get_last(void)
|
||||
{
|
||||
#if CONFIG_WATCH_INA226_ENABLED
|
||||
return &s_last_reading;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
29
main/ina226_monitor.h
Normal file
29
main/ina226_monitor.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Developed by TComLab
|
||||
* Version: v0.1
|
||||
* Date: 2025-12-15
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
typedef struct {
|
||||
float voltage_v;
|
||||
float current_a;
|
||||
float power_w;
|
||||
} ina226_reading_t;
|
||||
|
||||
// Ініціалізує INA226: конфігурує I2C та калібрує вимірювач.
|
||||
esp_err_t ina226_monitor_init(void);
|
||||
// true, якщо драйвер вже ініціалізований.
|
||||
bool ina226_monitor_ready(void);
|
||||
// INA226 підтримує один канал, але інтерфейс залишається узагальненим.
|
||||
size_t ina226_monitor_channel_count(void);
|
||||
// Зчитує нові дані; out_reading може бути NULL, якщо дані не потрібні.
|
||||
esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading);
|
||||
// Повертає кеш останнього вимірювання або NULL, якщо модуль неактивний.
|
||||
const ina226_reading_t *ina226_monitor_get_last(void);
|
||||
176
main/main.c
176
main/main.c
@@ -1,42 +1,190 @@
|
||||
/*
|
||||
* Developed by TComLab
|
||||
* Version: v0.1
|
||||
* Date: 2025-12-15
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "dcdc_controller.h"
|
||||
#include "ina226_monitor.h"
|
||||
#include "uart_mux.h"
|
||||
#include "usb_cdc_cli.h"
|
||||
#include "usb_cdc_log.h"
|
||||
#include "watch_config.h"
|
||||
#include "ws2812_status.h"
|
||||
|
||||
static const char *TAG = "watch-watch";
|
||||
static const char HB_MESSAGE[] = "{\"cmd\":\"status\"}\r\n";
|
||||
|
||||
static int noop_vprintf(const char *fmt, va_list args)
|
||||
{
|
||||
(void)fmt;
|
||||
(void)args;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_set_vprintf(noop_vprintf);
|
||||
|
||||
if (watch_config_init() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Не вдалося ініціалізувати конфігурацію");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Конфігурацію завантажено");
|
||||
}
|
||||
|
||||
if (usb_cdc_cli_init() == ESP_OK) {
|
||||
ESP_LOGI(TAG, "USB CDC CLI активовано");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Не вдалося запустити USB CLI");
|
||||
}
|
||||
|
||||
bool ws_ready = false;
|
||||
if (ws2812_status_init() == ESP_OK) {
|
||||
ws_ready = true;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "WS2812 статусний індикатор недоступний");
|
||||
}
|
||||
|
||||
const watch_config_t *cfg = watch_config_get();
|
||||
const uint32_t start_delay_ms = cfg->heartbeat_start_delay_sec * 1000U;
|
||||
TickType_t start_delay = pdMS_TO_TICKS(start_delay_ms);
|
||||
if (start_delay > 0) {
|
||||
ESP_LOGI(TAG, "Очікування %u с перед стартом опитування", cfg->heartbeat_start_delay_sec);
|
||||
if (ws_ready) {
|
||||
ws2812_status_set_startup_hold(start_delay_ms);
|
||||
}
|
||||
vTaskDelay(start_delay);
|
||||
if (ws_ready) {
|
||||
ws2812_status_set_startup_hold(0);
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Запуск watch-watch systems");
|
||||
if (dcdc_init() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Помилка ініціалізації DCDC контролера");
|
||||
if (ws_ready) {
|
||||
ws2812_status_set_error(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (usb_cdc_cli_init() != ESP_OK) {
|
||||
ESP_LOGW(TAG, "USB CDC CLI недоступний");
|
||||
if (ws_ready) {
|
||||
ws2812_status_refresh_from_dcdc();
|
||||
}
|
||||
|
||||
if (ina226_monitor_init() == ESP_OK) {
|
||||
ESP_LOGI(TAG, "INA226 моніторинг активовано");
|
||||
ina226_monitor_sample(NULL);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "USB CDC CLI запущено");
|
||||
ESP_LOGW(TAG, "Моніторинг навантаження недоступний");
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Початок послідовного ввімкнення каналів з інтервалом 4 с");
|
||||
const TickType_t on_time = pdMS_TO_TICKS(4000);
|
||||
if (uart_mux_init() == ESP_OK) {
|
||||
ESP_LOGI(TAG, "UART мультиплексор активовано");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "UART мультиплексор недоступний");
|
||||
}
|
||||
|
||||
const TickType_t mux_timeout = pdMS_TO_TICKS(600);
|
||||
TickType_t channel_next_hb[DCDC_CHANNEL_COUNT] = {0};
|
||||
bool channel_powered[DCDC_CHANNEL_COUNT] = {false};
|
||||
const TickType_t power_on_stagger = pdMS_TO_TICKS(3000);
|
||||
ESP_LOGI(TAG, "Початок циклічного опитування всіх каналів");
|
||||
|
||||
size_t prev_channel = DCDC_CHANNEL_COUNT - 1;
|
||||
while (true) {
|
||||
for (size_t ch = 0; ch < dcdc_channel_count(); ++ch) {
|
||||
if (prev_channel != ch) {
|
||||
dcdc_disable(prev_channel);
|
||||
cfg = watch_config_get();
|
||||
TickType_t hb_period = pdMS_TO_TICKS(cfg->heartbeat_period_sec * 1000U);
|
||||
if (hb_period == 0) {
|
||||
hb_period = 1;
|
||||
}
|
||||
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
size_t channels = dcdc_channel_count();
|
||||
for (size_t ch = 0; ch < channels; ++ch) {
|
||||
bool powered = dcdc_get_state(ch);
|
||||
if (!powered) {
|
||||
channel_powered[ch] = false;
|
||||
ESP_LOGI(TAG, "-> Ввімкнення каналу %d", (int)ch);
|
||||
dcdc_enable(ch);
|
||||
vTaskDelay(on_time);
|
||||
|
||||
prev_channel = ch;
|
||||
if (dcdc_enable(ch) == ESP_OK) {
|
||||
powered = true;
|
||||
channel_powered[ch] = true;
|
||||
channel_next_hb[ch] = now + hb_period;
|
||||
if (ws_ready) {
|
||||
ws2812_status_set_channel_state(ch, true);
|
||||
}
|
||||
vTaskDelay(power_on_stagger);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Не вдалося ввімкнути канал %d", (int)ch);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!channel_powered[ch]) {
|
||||
channel_powered[ch] = true;
|
||||
channel_next_hb[ch] = now + hb_period;
|
||||
continue;
|
||||
}
|
||||
|
||||
now = xTaskGetTickCount();
|
||||
if (now < channel_next_hb[ch]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ina226_reading_t reading = {0};
|
||||
if (ina226_monitor_ready() && ina226_monitor_sample(&reading) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Живлення: %.2f В, %.2f А, %.2f Вт",
|
||||
reading.voltage_v, reading.current_a, reading.power_w);
|
||||
}
|
||||
|
||||
if (uart_mux_ready()) {
|
||||
esp_err_t tx_err = uart_mux_write(ch,
|
||||
(const uint8_t *)HB_MESSAGE,
|
||||
sizeof(HB_MESSAGE) - 1,
|
||||
mux_timeout);
|
||||
if (tx_err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Не вдалося надіслати heartbeat на канал %d: %s",
|
||||
(int)ch, esp_err_to_name(tx_err));
|
||||
} else {
|
||||
uint8_t rx_buffer[CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN];
|
||||
size_t received = 0;
|
||||
esp_err_t rx_err = uart_mux_read(ch,
|
||||
rx_buffer,
|
||||
sizeof(rx_buffer),
|
||||
&received,
|
||||
mux_timeout);
|
||||
if (rx_err == ESP_OK) {
|
||||
if (received > 0) {
|
||||
ESP_LOGI(TAG, "RX CH%u (%u байт після heartbeat)", (unsigned)ch, (unsigned)received);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(TAG, rx_buffer, received, ESP_LOG_INFO);
|
||||
uart_mux_process_rx(ch, rx_buffer, received);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Канал %d не відповів даними на heartbeat", (int)ch);
|
||||
uart_mux_report_miss(ch);
|
||||
}
|
||||
} else if (rx_err == ESP_ERR_TIMEOUT) {
|
||||
ESP_LOGW(TAG, "Час очікування відповіді з каналу %d перевищено", (int)ch);
|
||||
uart_mux_report_miss(ch);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Помилка читання відповіді CH%u: %s",
|
||||
(unsigned)ch, esp_err_to_name(rx_err));
|
||||
uart_mux_report_miss(ch);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "UART мультиплексор недоступний, очікування...");
|
||||
}
|
||||
|
||||
channel_next_hb[ch] = now + hb_period;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
}
|
||||
|
||||
520
main/uart_mux.c
Normal file
520
main/uart_mux.c
Normal file
@@ -0,0 +1,520 @@
|
||||
/*
|
||||
* Developed by TComLab
|
||||
* Version: v0.1
|
||||
* Date: 2025-12-15
|
||||
*/
|
||||
|
||||
#include "uart_mux.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "dcdc_controller.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/uart.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "ws2812_status.h"
|
||||
#include "watch_config.h"
|
||||
|
||||
#ifndef CONFIG_WATCH_UART_MUX_CHANNELS
|
||||
#define CONFIG_WATCH_UART_MUX_CHANNELS 5
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC
|
||||
#define CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC 60
|
||||
#endif
|
||||
|
||||
#if CONFIG_WATCH_UART_MUX_ENABLED
|
||||
|
||||
#define UART_MUX_MAX_CHANNELS CONFIG_WATCH_UART_MUX_CHANNELS
|
||||
|
||||
static const char *TAG = "uart_mux";
|
||||
|
||||
static const gpio_num_t s_select_pins[3] = {
|
||||
CONFIG_WATCH_UART_MUX_SEL_A0,
|
||||
CONFIG_WATCH_UART_MUX_SEL_A1,
|
||||
CONFIG_WATCH_UART_MUX_SEL_A2,
|
||||
};
|
||||
|
||||
static SemaphoreHandle_t s_mutex;
|
||||
static size_t s_active_channel = SIZE_MAX;
|
||||
static bool s_initialized;
|
||||
static int64_t s_last_heartbeat_us[UART_MUX_MAX_CHANNELS];
|
||||
static TaskHandle_t s_watchdog_task;
|
||||
static uint8_t s_consecutive_miss[UART_MUX_MAX_CHANNELS];
|
||||
static bool s_watchdog_armed[UART_MUX_MAX_CHANNELS];
|
||||
static uint32_t s_total_miss_count[UART_MUX_MAX_CHANNELS];
|
||||
static uint32_t s_restart_count[UART_MUX_MAX_CHANNELS];
|
||||
|
||||
static uint8_t uart_mux_get_miss_limit_from_config(void);
|
||||
static void uart_mux_restart_channel(size_t channel);
|
||||
static void uart_mux_record_miss(size_t channel, uint8_t miss_limit);
|
||||
|
||||
static TickType_t uart_mux_restart_delay_ticks(void)
|
||||
{
|
||||
const watch_config_t *cfg = watch_config_get();
|
||||
uint32_t sec = cfg ? cfg->dcdc_restart_off_sec : 2U;
|
||||
if (sec == 0) {
|
||||
sec = 1;
|
||||
}
|
||||
return pdMS_TO_TICKS(sec * 1000U);
|
||||
}
|
||||
|
||||
static uint8_t uart_mux_get_miss_limit_from_config(void)
|
||||
{
|
||||
const watch_config_t *cfg = watch_config_get();
|
||||
uint32_t limit = cfg ? cfg->heartbeat_miss_limit : 3U;
|
||||
if (limit == 0) {
|
||||
limit = 3U;
|
||||
}
|
||||
if (limit > UINT8_MAX) {
|
||||
limit = UINT8_MAX;
|
||||
}
|
||||
return (uint8_t)limit;
|
||||
}
|
||||
|
||||
static void uart_mux_restart_channel(size_t channel)
|
||||
{
|
||||
if (channel >= UART_MUX_MAX_CHANNELS) {
|
||||
return;
|
||||
}
|
||||
ESP_LOGW(TAG, "CH%u: перезапуск живлення після відсутності відповіді", (unsigned)channel);
|
||||
dcdc_disable(channel);
|
||||
vTaskDelay(uart_mux_restart_delay_ticks());
|
||||
dcdc_enable(channel);
|
||||
s_consecutive_miss[channel] = 0;
|
||||
s_watchdog_armed[channel] = false;
|
||||
if (s_restart_count[channel] < UINT32_MAX) {
|
||||
++s_restart_count[channel];
|
||||
}
|
||||
s_last_heartbeat_us[channel] = esp_timer_get_time();
|
||||
}
|
||||
|
||||
static void uart_mux_record_miss(size_t channel, uint8_t miss_limit)
|
||||
{
|
||||
if (channel >= UART_MUX_MAX_CHANNELS) {
|
||||
return;
|
||||
}
|
||||
if (!dcdc_get_state(channel)) {
|
||||
return;
|
||||
}
|
||||
if (!s_watchdog_armed[channel]) {
|
||||
s_watchdog_armed[channel] = true;
|
||||
s_consecutive_miss[channel] = 0;
|
||||
}
|
||||
if (s_total_miss_count[channel] < UINT32_MAX) {
|
||||
++s_total_miss_count[channel];
|
||||
}
|
||||
if (s_consecutive_miss[channel] < UINT8_MAX) {
|
||||
++s_consecutive_miss[channel];
|
||||
}
|
||||
if (miss_limit == 0) {
|
||||
miss_limit = 1;
|
||||
}
|
||||
if (s_consecutive_miss[channel] >= miss_limit) {
|
||||
uart_mux_restart_channel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
static bool uart_mux_extract_numeric_field(const uint8_t *data,
|
||||
size_t length,
|
||||
const char *key,
|
||||
int *out_value)
|
||||
{
|
||||
if (!data || !key || !out_value) {
|
||||
return false;
|
||||
}
|
||||
const size_t key_len = strlen(key);
|
||||
if (key_len == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
if (data[i] != '"') {
|
||||
continue;
|
||||
}
|
||||
size_t j = i + 1;
|
||||
if (j + key_len >= length) {
|
||||
break;
|
||||
}
|
||||
if (memcmp(&data[j], key, key_len) != 0) {
|
||||
continue;
|
||||
}
|
||||
j += key_len;
|
||||
if (j >= length || data[j] != '"') {
|
||||
continue;
|
||||
}
|
||||
++j;
|
||||
while (j < length && isspace((unsigned char)data[j])) {
|
||||
++j;
|
||||
}
|
||||
if (j >= length || data[j] != ':') {
|
||||
continue;
|
||||
}
|
||||
++j;
|
||||
while (j < length && isspace((unsigned char)data[j])) {
|
||||
++j;
|
||||
}
|
||||
bool negative = false;
|
||||
if (j < length && (data[j] == '-' || data[j] == '+')) {
|
||||
negative = (data[j] == '-');
|
||||
++j;
|
||||
}
|
||||
bool has_digit = false;
|
||||
int value = 0;
|
||||
while (j < length && isdigit((unsigned char)data[j])) {
|
||||
has_digit = true;
|
||||
value = value * 10 + (data[j] - '0');
|
||||
++j;
|
||||
}
|
||||
if (has_digit) {
|
||||
*out_value = negative ? -value : value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void uart_mux_decode_status_payload(const uint8_t *data,
|
||||
size_t length,
|
||||
bool *hb_ack,
|
||||
bool *vpn_ok,
|
||||
bool *app_ok)
|
||||
{
|
||||
int value = 0;
|
||||
if (hb_ack) {
|
||||
bool found = uart_mux_extract_numeric_field(data, length, "hb", &value);
|
||||
*hb_ack = found && value == 2;
|
||||
}
|
||||
if (vpn_ok) {
|
||||
bool found = uart_mux_extract_numeric_field(data, length, "VPN", &value);
|
||||
*vpn_ok = found && value != 0;
|
||||
}
|
||||
if (app_ok) {
|
||||
bool found = uart_mux_extract_numeric_field(data, length, "APP", &value);
|
||||
*app_ok = found && value != 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Перевіряє, чи містить буфер відповідь {"hb":2} від Raspberry Pi.
|
||||
static bool uart_mux_contains_hb_ack(const uint8_t *data, size_t length)
|
||||
{
|
||||
bool ack = false;
|
||||
uart_mux_decode_status_payload(data, length, &ack, NULL, NULL);
|
||||
return ack;
|
||||
}
|
||||
|
||||
// Перемикає апаратний мультиплексор на вказаний канал під захистом мьютекса,
|
||||
// оновлюючи таймстемп останнього heartbeat для контролю watchdog.
|
||||
static esp_err_t uart_mux_select_locked(size_t channel)
|
||||
{
|
||||
if (channel >= UART_MUX_MAX_CHANNELS) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (channel == s_active_channel) {
|
||||
return ESP_OK;
|
||||
}
|
||||
for (int bit = 0; bit < 3; ++bit) {
|
||||
int level = (channel >> bit) & 0x1;
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(s_select_pins[bit], level), TAG,
|
||||
"GPIO set failed");
|
||||
}
|
||||
s_active_channel = channel;
|
||||
s_last_heartbeat_us[channel] = esp_timer_get_time();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Періодично опитує всі канали, щоб зчитати heartbeat та перезапускає DCDC,
|
||||
// якщо канал «мовчить» довше за CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC.
|
||||
static void uart_mux_watchdog_task(void *arg)
|
||||
{
|
||||
const TickType_t poll_interval = pdMS_TO_TICKS(10000);
|
||||
const TickType_t read_timeout = pdMS_TO_TICKS(300);
|
||||
const int64_t timeout_us = (int64_t)CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC * 1000000LL;
|
||||
uint8_t buffer[CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN];
|
||||
const watch_config_t *cfg = watch_config_get();
|
||||
TickType_t start_delay = pdMS_TO_TICKS(cfg->heartbeat_start_delay_sec * 1000U);
|
||||
if (start_delay > 0) {
|
||||
vTaskDelay(start_delay);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
vTaskDelay(poll_interval);
|
||||
const watch_config_t *cfg = watch_config_get();
|
||||
if (!cfg->heartbeat_monitor_enabled) {
|
||||
continue;
|
||||
}
|
||||
uint8_t miss_limit = uart_mux_get_miss_limit_from_config();
|
||||
int64_t now = esp_timer_get_time();
|
||||
for (size_t ch = 0; ch < UART_MUX_MAX_CHANNELS; ++ch) {
|
||||
if (!dcdc_get_state(ch)) {
|
||||
s_watchdog_armed[ch] = false;
|
||||
s_consecutive_miss[ch] = 0;
|
||||
continue;
|
||||
}
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(20)) == pdTRUE) {
|
||||
if (uart_mux_select_locked(ch) == ESP_OK) {
|
||||
int read = uart_read_bytes(CONFIG_WATCH_UART_PORT,
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
read_timeout);
|
||||
if (read > 0) {
|
||||
bool ack = false;
|
||||
bool vpn_ok = false;
|
||||
bool app_ok = false;
|
||||
uart_mux_decode_status_payload(buffer, read, &ack, &vpn_ok, &app_ok);
|
||||
ESP_LOGI(TAG, "UART0 RX CH%u (%d байт)%s",
|
||||
(unsigned)ch, read, ack ? " [HB ACK]" : "");
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(TAG, buffer, read, ESP_LOG_INFO);
|
||||
if (ack) {
|
||||
s_last_heartbeat_us[ch] = now;
|
||||
s_consecutive_miss[ch] = 0;
|
||||
s_watchdog_armed[ch] = true;
|
||||
ws2812_status_set_service_state(ch, vpn_ok, app_ok);
|
||||
} else {
|
||||
uart_mux_record_miss(ch, miss_limit);
|
||||
}
|
||||
} else {
|
||||
uart_mux_record_miss(ch, miss_limit);
|
||||
ESP_LOGD(TAG, "UART0 RX CH%u: немає відповіді у watchdog (%u)",
|
||||
(unsigned)ch, (unsigned)s_consecutive_miss[ch]);
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(s_mutex);
|
||||
}
|
||||
|
||||
if (dcdc_get_state(ch) && s_watchdog_armed[ch] && s_consecutive_miss[ch] >= miss_limit) {
|
||||
uart_mux_restart_channel(ch);
|
||||
}
|
||||
|
||||
if (dcdc_get_state(ch) && s_last_heartbeat_us[ch] > 0 &&
|
||||
(now - s_last_heartbeat_us[ch]) > timeout_us) {
|
||||
uart_mux_restart_channel(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CONFIG_WATCH_UART_MUX_ENABLED
|
||||
|
||||
// Налаштовує GPIO-вибірники, драйвер UART та створює watchdog-задачу для
|
||||
// мультиплексора; повторний виклик просто повертає ESP_OK.
|
||||
esp_err_t uart_mux_init(void)
|
||||
{
|
||||
#if !CONFIG_WATCH_UART_MUX_ENABLED
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
if (s_initialized) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
gpio_config_t io_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pin_bit_mask = 0,
|
||||
};
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
io_conf.pin_bit_mask = 1ULL << s_select_pins[i];
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&io_conf), TAG, "GPIO config failed");
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(s_select_pins[i], 0), TAG, "GPIO init level failed");
|
||||
}
|
||||
s_active_channel = 0;
|
||||
|
||||
uart_config_t uart_cfg = {
|
||||
.baud_rate = CONFIG_WATCH_UART_BAUD,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.source_clk = UART_SCLK_APB,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(uart_param_config(CONFIG_WATCH_UART_PORT, &uart_cfg), TAG, "UART config failed");
|
||||
ESP_RETURN_ON_ERROR(uart_set_pin(CONFIG_WATCH_UART_PORT,
|
||||
CONFIG_WATCH_UART_TX_GPIO,
|
||||
CONFIG_WATCH_UART_RX_GPIO,
|
||||
UART_PIN_NO_CHANGE,
|
||||
UART_PIN_NO_CHANGE),
|
||||
TAG, "UART pin assign failed");
|
||||
ESP_RETURN_ON_ERROR(uart_driver_install(CONFIG_WATCH_UART_PORT,
|
||||
CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN * 2,
|
||||
CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN * 2,
|
||||
0, NULL, 0),
|
||||
TAG, "UART driver install failed");
|
||||
|
||||
s_mutex = xSemaphoreCreateMutex();
|
||||
ESP_RETURN_ON_FALSE(s_mutex, ESP_ERR_NO_MEM, TAG, "mutex alloc failed");
|
||||
|
||||
int64_t now = esp_timer_get_time();
|
||||
for (size_t ch = 0; ch < UART_MUX_MAX_CHANNELS; ++ch) {
|
||||
s_last_heartbeat_us[ch] = now;
|
||||
s_consecutive_miss[ch] = 0;
|
||||
s_watchdog_armed[ch] = false;
|
||||
s_total_miss_count[ch] = 0;
|
||||
s_restart_count[ch] = 0;
|
||||
}
|
||||
|
||||
if (xTaskCreate(uart_mux_watchdog_task, "uart_mux_wd", 4096, NULL, 5, &s_watchdog_task) != pdPASS) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
s_initialized = true;
|
||||
ESP_LOGI(TAG, "UART мультиплексор активовано, каналів: %d", UART_MUX_MAX_CHANNELS);
|
||||
return ESP_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Повертає ознаку ініціалізації модулю UART мультиплексора.
|
||||
bool uart_mux_ready(void)
|
||||
{
|
||||
#if CONFIG_WATCH_UART_MUX_ENABLED
|
||||
return s_initialized;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
// Кількість доступних каналів, визначених у конфігурації.
|
||||
size_t uart_mux_channel_count(void)
|
||||
{
|
||||
return CONFIG_WATCH_UART_MUX_CHANNELS;
|
||||
}
|
||||
|
||||
// Перемикається на канал, передає буфер даних через загальний UART та
|
||||
// захищає доступ до шини мьютексом, щоб уникнути гонок між задачами.
|
||||
esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, TickType_t timeout)
|
||||
{
|
||||
#if !CONFIG_WATCH_UART_MUX_ENABLED
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
if (!s_initialized) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (channel >= UART_MUX_MAX_CHANNELS) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!data || length == 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
if (xSemaphoreTake(s_mutex, timeout) != pdTRUE) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
esp_err_t err = uart_mux_select_locked(channel);
|
||||
if (err == ESP_OK) {
|
||||
int written = uart_write_bytes(CONFIG_WATCH_UART_PORT, (const char *)data, length);
|
||||
if (written < 0 || (size_t)written != length) {
|
||||
err = ESP_FAIL;
|
||||
} else {
|
||||
if (uart_wait_tx_done(CONFIG_WATCH_UART_PORT, timeout) != ESP_OK) {
|
||||
err = ESP_ERR_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(s_mutex);
|
||||
return err;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Читає дані з вказаного каналу, оновлюючи heartbeat під час успішного
|
||||
// зчитування, і повертає кількість байтів через out_length.
|
||||
esp_err_t uart_mux_read(size_t channel, uint8_t *buffer, size_t buffer_size, size_t *out_length, TickType_t timeout)
|
||||
{
|
||||
#if !CONFIG_WATCH_UART_MUX_ENABLED
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
if (!s_initialized) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (channel >= UART_MUX_MAX_CHANNELS || !buffer || buffer_size == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (xSemaphoreTake(s_mutex, timeout) != pdTRUE) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
esp_err_t err = uart_mux_select_locked(channel);
|
||||
if (err == ESP_OK) {
|
||||
int read = uart_read_bytes(CONFIG_WATCH_UART_PORT, buffer, buffer_size, timeout);
|
||||
if (read < 0) {
|
||||
err = ESP_FAIL;
|
||||
} else {
|
||||
if (out_length) {
|
||||
*out_length = (size_t)read;
|
||||
}
|
||||
if (read > 0) {
|
||||
s_last_heartbeat_us[channel] = esp_timer_get_time();
|
||||
}
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(s_mutex);
|
||||
return err;
|
||||
#endif
|
||||
}
|
||||
|
||||
void uart_mux_process_rx(size_t channel, const uint8_t *data, size_t length)
|
||||
{
|
||||
#if CONFIG_WATCH_UART_MUX_ENABLED
|
||||
if (!s_initialized || channel >= UART_MUX_MAX_CHANNELS || !data || length == 0) {
|
||||
return;
|
||||
}
|
||||
bool ack = false;
|
||||
bool vpn_ok = false;
|
||||
bool app_ok = false;
|
||||
uart_mux_decode_status_payload(data, length, &ack, &vpn_ok, &app_ok);
|
||||
int64_t now = esp_timer_get_time();
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
|
||||
s_last_heartbeat_us[channel] = now;
|
||||
if (ack) {
|
||||
s_consecutive_miss[channel] = 0;
|
||||
s_watchdog_armed[channel] = true;
|
||||
}
|
||||
xSemaphoreGive(s_mutex);
|
||||
}
|
||||
if (ack) {
|
||||
ws2812_status_set_service_state(channel, vpn_ok, app_ok);
|
||||
} else {
|
||||
uart_mux_record_miss(channel, uart_mux_get_miss_limit_from_config());
|
||||
}
|
||||
#else
|
||||
(void)channel;
|
||||
(void)data;
|
||||
(void)length;
|
||||
#endif
|
||||
}
|
||||
|
||||
void uart_mux_report_miss(size_t channel)
|
||||
{
|
||||
#if CONFIG_WATCH_UART_MUX_ENABLED
|
||||
if (!s_initialized) {
|
||||
return;
|
||||
}
|
||||
uart_mux_record_miss(channel, uart_mux_get_miss_limit_from_config());
|
||||
#else
|
||||
(void)channel;
|
||||
#endif
|
||||
}
|
||||
|
||||
void uart_mux_get_channel_stats(size_t channel, uart_mux_channel_stats_t *out_stats)
|
||||
{
|
||||
if (!out_stats) {
|
||||
return;
|
||||
}
|
||||
#if CONFIG_WATCH_UART_MUX_ENABLED
|
||||
if (!s_initialized || channel >= UART_MUX_MAX_CHANNELS) {
|
||||
out_stats->missed_heartbeats = 0;
|
||||
out_stats->restart_count = 0;
|
||||
return;
|
||||
}
|
||||
out_stats->missed_heartbeats = s_total_miss_count[channel];
|
||||
out_stats->restart_count = s_restart_count[channel];
|
||||
#else
|
||||
(void)channel;
|
||||
out_stats->missed_heartbeats = 0;
|
||||
out_stats->restart_count = 0;
|
||||
#endif
|
||||
}
|
||||
35
main/uart_mux.h
Normal file
35
main/uart_mux.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Developed by TComLab
|
||||
* Version: v0.1
|
||||
* Date: 2025-12-15
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t missed_heartbeats;
|
||||
uint32_t restart_count;
|
||||
} uart_mux_channel_stats_t;
|
||||
|
||||
// Ініціалізує апаратний мультиплексор UART: GPIO, UART драйвер та watchdog.
|
||||
esp_err_t uart_mux_init(void);
|
||||
// Повертає true, якщо драйвер готовий приймати виклики.
|
||||
bool uart_mux_ready(void);
|
||||
// Кількість доступних каналів мультиплексора.
|
||||
size_t uart_mux_channel_count(void);
|
||||
// Відправляє буфер даних на вказаний канал з тайм-аутом очікування мьютекса.
|
||||
esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, TickType_t timeout);
|
||||
// Зчитує дані з каналу, повертаючи кількість байтів через out_length.
|
||||
esp_err_t uart_mux_read(size_t channel, uint8_t *buffer, size_t buffer_size, size_t *out_length, TickType_t timeout);
|
||||
// Повідомляє драйверу про дані, зчитані поза watchdog-ом (для оновлення станів).
|
||||
void uart_mux_process_rx(size_t channel, const uint8_t *data, size_t length);
|
||||
// Повідомляє про пропущений heartbeat (використовується основним циклом).
|
||||
void uart_mux_report_miss(size_t channel);
|
||||
// Повертає статистику по каналу (кількість пропущених відповідей та рестартів).
|
||||
void uart_mux_get_channel_stats(size_t channel, uart_mux_channel_stats_t *out_stats);
|
||||
@@ -11,12 +11,20 @@
|
||||
#include "dcdc_controller.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "tinyusb.h"
|
||||
#include "tusb_cdc_acm.h"
|
||||
#include "ws2812_status.h"
|
||||
#include "watch_config.h"
|
||||
#include "esp_system.h"
|
||||
#include "ina226_monitor.h"
|
||||
#include "uart_mux.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/soc.h"
|
||||
|
||||
#define CLI_LINE_MAX_LEN 128
|
||||
#define CLI_QUEUE_LEN 8
|
||||
@@ -24,6 +32,9 @@
|
||||
#ifndef CONFIG_TINYUSB_CDC_RX_BUFSIZE
|
||||
#define CONFIG_TINYUSB_CDC_RX_BUFSIZE 64
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN
|
||||
#define CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN 128
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
size_t length;
|
||||
@@ -39,6 +50,10 @@ static bool s_host_ready;
|
||||
static void usb_cli_task(void *arg);
|
||||
static void usb_cli_process_line(char *line);
|
||||
static void usb_cli_prompt(void);
|
||||
static void usb_cli_enter_bootloader(void);
|
||||
static void usb_cli_handle_config(char *args);
|
||||
static void usb_cli_print_config(void);
|
||||
static void usb_cli_handle_reset(void);
|
||||
|
||||
static void usb_cli_write_raw(const char *data, size_t len)
|
||||
{
|
||||
@@ -106,10 +121,16 @@ static void usb_cli_print_status(void)
|
||||
usb_cli_printf("\r\nСтан каналів:\r\n");
|
||||
for (size_t i = 0; i < dcdc_channel_count(); ++i) {
|
||||
dcdc_channel_t ch = (dcdc_channel_t)i;
|
||||
usb_cli_printf(" - CH%u: %s (GPIO %d)\r\n",
|
||||
uart_mux_channel_stats_t stats = {0};
|
||||
if (uart_mux_ready()) {
|
||||
uart_mux_get_channel_stats(i, &stats);
|
||||
}
|
||||
usb_cli_printf(" - CH%u: %s (GPIO %d) | miss=%u restart=%u\r\n",
|
||||
(unsigned)i,
|
||||
dcdc_get_state(ch) ? "ON" : "OFF",
|
||||
(int)dcdc_get_gpio(ch));
|
||||
(int)dcdc_get_gpio(ch),
|
||||
(unsigned)stats.missed_heartbeats,
|
||||
(unsigned)stats.restart_count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +139,18 @@ static void usb_cli_prompt(void)
|
||||
usb_cli_write_raw(PROMPT, strlen(PROMPT));
|
||||
}
|
||||
|
||||
static void usb_cli_enter_bootloader(void)
|
||||
{
|
||||
usb_cli_printf("\r\nПерехід у режим прошивки esptool...\r\n"
|
||||
"Після перезавантаження з’явиться ROM-порт USB CDC/Serial.\r\n"
|
||||
"Запустіть esptool.py або idf.py flash та прошийте пристрій. "
|
||||
"Для виходу з bootloader виконайте 'esptool.py --after hard_reset reset' або перезавантажте живлення.\r\n");
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
tinyusb_driver_uninstall();
|
||||
REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT);
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
static void usb_cli_handle_switch(const char *cmd, char *args)
|
||||
{
|
||||
dcdc_channel_t channel;
|
||||
@@ -140,8 +173,87 @@ static void usb_cli_handle_switch(const char *cmd, char *args)
|
||||
if (err != ESP_OK) {
|
||||
usb_cli_printf("\r\nПомилка керування каналом: %s\r\n", esp_err_to_name(err));
|
||||
} else {
|
||||
usb_cli_printf("\r\nКанал %d -> %s\r\n", channel,
|
||||
dcdc_get_state(channel) ? "ON" : "OFF");
|
||||
bool state = dcdc_get_state(channel);
|
||||
usb_cli_printf("\r\nКанал %d -> %s\r\n", channel, state ? "ON" : "OFF");
|
||||
ws2812_status_set_channel_state(channel, state);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_cli_print_measurement(size_t channel, const ina226_reading_t *reading)
|
||||
{
|
||||
usb_cli_printf("\r\nCH%u: %.2f В, %.3f А, %.3f Вт",
|
||||
(unsigned)channel, reading->voltage_v, reading->current_a, reading->power_w);
|
||||
}
|
||||
|
||||
static void usb_cli_handle_sense(char *args)
|
||||
{
|
||||
if (!ina226_monitor_ready()) {
|
||||
usb_cli_printf("\r\nМоніторинг INA226 недоступний\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ina226_reading_t reading = {0};
|
||||
if (ina226_monitor_sample(&reading) == ESP_OK) {
|
||||
usb_cli_print_measurement(0, &reading);
|
||||
} else {
|
||||
usb_cli_printf("\r\nНе вдалося зчитати дані з INA226\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_cli_handle_uart(char *args)
|
||||
{
|
||||
if (!uart_mux_ready()) {
|
||||
usb_cli_printf("\r\nUART мультиплексор недоступний\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char *ctx = NULL;
|
||||
char *action = strtok_r(args, " ", &ctx);
|
||||
if (!action) {
|
||||
usb_cli_printf("\r\nUART usage: uart send <channel> <text> | uart read <channel> [len]\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char *channel_str = strtok_r(NULL, " ", &ctx);
|
||||
dcdc_channel_t channel;
|
||||
if (!usb_cli_parse_channel(channel_str, &channel)) {
|
||||
usb_cli_printf("\r\nНевірний номер каналу\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(action, "send") == 0) {
|
||||
char *payload = ctx;
|
||||
if (!payload || *payload == '\0') {
|
||||
usb_cli_printf("\r\nНемає даних для відправлення\r\n");
|
||||
return;
|
||||
}
|
||||
size_t len = strlen(payload);
|
||||
if (uart_mux_write(channel, (const uint8_t *)payload, len, pdMS_TO_TICKS(200)) == ESP_OK) {
|
||||
usb_cli_printf("\r\nВідправлено %u байт\r\n", (unsigned)len);
|
||||
} else {
|
||||
usb_cli_printf("\r\nПомилка відправлення UART\r\n");
|
||||
}
|
||||
} else if (strcasecmp(action, "read") == 0) {
|
||||
char *len_str = ctx ? strtok_r(NULL, " ", &ctx) : NULL;
|
||||
size_t req_len = len_str ? strtoul(len_str, NULL, 10) : CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN;
|
||||
if (req_len == 0) {
|
||||
req_len = CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN;
|
||||
}
|
||||
if (req_len > 256) {
|
||||
req_len = 256;
|
||||
}
|
||||
uint8_t buffer[256];
|
||||
size_t received = 0;
|
||||
if (uart_mux_read(channel, buffer, req_len, &received, pdMS_TO_TICKS(200)) == ESP_OK && received > 0) {
|
||||
usb_cli_printf("\r\nCH%u UART (%u байт): ", (unsigned)channel, (unsigned)received);
|
||||
for (size_t i = 0; i < received; ++i) {
|
||||
usb_cli_write_raw((char *)&buffer[i], 1);
|
||||
}
|
||||
} else {
|
||||
usb_cli_printf("\r\nДані відсутні\r\n");
|
||||
}
|
||||
} else {
|
||||
usb_cli_printf("\r\nНевірна дія '%s'\r\n", action);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,22 +273,135 @@ static void usb_cli_process_line(char *line)
|
||||
if (strcasecmp(cmd, "help") == 0) {
|
||||
usb_cli_printf(
|
||||
"\r\nДоступні команди:\r\n"
|
||||
" help - показати довідку\r\n"
|
||||
" status - стан всіх каналів\r\n"
|
||||
" enable <n> - увімкнути канал n\r\n"
|
||||
" help - показати цю довідку\r\n"
|
||||
" status - показати стан усіх каналів DCDC\r\n"
|
||||
" enable <n> - увімкнути канал n (0..4)\r\n"
|
||||
" disable <n> - вимкнути канал n\r\n"
|
||||
" toggle <n> - перемкнути канал n\r\n");
|
||||
" toggle <n> - перемкнути канал n\r\n"
|
||||
" sense - виміряти напругу/струм/потужність INA226\r\n"
|
||||
" uart send <n> <msg> - надіслати текстове повідомлення в Raspberry Pi n\r\n"
|
||||
" uart read <n> [len] - прочитати до [len] байт відповіді від Raspberry Pi n\r\n"
|
||||
" config show - показати збережені таймінги heartbeat/DCDC\r\n"
|
||||
" config set hb_period <сек> - змінити інтервал відправки heartbeat\r\n"
|
||||
" config set dcdc_off <сек> - задати тривалість вимкнення DCDC при рестарті\r\n"
|
||||
" config set hb_start <сек> - налаштувати затримку перед стартом опитування після boot\r\n"
|
||||
" config set hb_monitor <0|1> - увімкнути/вимкнути контроль відповіді heartbeat\r\n"
|
||||
" config set hb_miss <шт> - кількість пропусків відповіді до рестарту каналу\r\n"
|
||||
" reset - м'яко перезавантажити ESP32-S3\r\n"
|
||||
" bootloader - перезавантажити ESP32-S3 у ROM bootloader для esptool\r\n");
|
||||
} else if (strcasecmp(cmd, "status") == 0) {
|
||||
usb_cli_print_status();
|
||||
} else if (strcasecmp(cmd, "enable") == 0 ||
|
||||
strcasecmp(cmd, "disable") == 0 ||
|
||||
strcasecmp(cmd, "toggle") == 0) {
|
||||
usb_cli_handle_switch(cmd, save_ptr);
|
||||
} else if (strcasecmp(cmd, "sense") == 0) {
|
||||
usb_cli_handle_sense(save_ptr);
|
||||
} else if (strcasecmp(cmd, "uart") == 0) {
|
||||
usb_cli_handle_uart(save_ptr);
|
||||
} else if (strcasecmp(cmd, "config") == 0) {
|
||||
usb_cli_handle_config(save_ptr);
|
||||
} else if (strcasecmp(cmd, "reset") == 0) {
|
||||
usb_cli_handle_reset();
|
||||
} else if (strcasecmp(cmd, "bootloader") == 0) {
|
||||
usb_cli_enter_bootloader();
|
||||
} else {
|
||||
usb_cli_printf("\r\nНевідома команда '%s'\r\n", cmd);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_cli_print_config(void)
|
||||
{
|
||||
const watch_config_t *cfg = watch_config_get();
|
||||
usb_cli_printf(
|
||||
"\r\nПоточні налаштування:\r\n"
|
||||
" hb_period: %u с\r\n"
|
||||
" dcdc_off: %u с\r\n"
|
||||
" hb_start_delay: %u с\r\n"
|
||||
" hb_monitor: %s\r\n"
|
||||
" hb_miss_limit: %u зап.\r\n",
|
||||
(unsigned)cfg->heartbeat_period_sec,
|
||||
(unsigned)cfg->dcdc_restart_off_sec,
|
||||
(unsigned)cfg->heartbeat_start_delay_sec,
|
||||
cfg->heartbeat_monitor_enabled ? "on" : "off",
|
||||
(unsigned)cfg->heartbeat_miss_limit);
|
||||
}
|
||||
|
||||
static void usb_cli_handle_config(char *args)
|
||||
{
|
||||
if (!args || *args == '\0') {
|
||||
usb_cli_print_config();
|
||||
return;
|
||||
}
|
||||
|
||||
char *ctx = NULL;
|
||||
char *action = strtok_r(args, " ", &ctx);
|
||||
if (!action || strcasecmp(action, "show") == 0) {
|
||||
usb_cli_print_config();
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(action, "set") != 0) {
|
||||
usb_cli_printf("\r\nВикористання: config show | config set <hb_period|dcdc_off|hb_start|hb_monitor|hb_miss> <знач>\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char *param = strtok_r(NULL, " ", &ctx);
|
||||
char *value_str = strtok_r(NULL, " ", &ctx);
|
||||
if (!param || !value_str) {
|
||||
usb_cli_printf("\r\nВкажіть параметр і значення в секундах\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t value = (uint32_t)strtoul(value_str, NULL, 10);
|
||||
|
||||
watch_config_t new_cfg = *watch_config_get();
|
||||
if (strcasecmp(param, "hb_period") == 0) {
|
||||
if (value == 0) {
|
||||
usb_cli_printf("\r\nЗначення має бути більше нуля\r\n");
|
||||
return;
|
||||
}
|
||||
new_cfg.heartbeat_period_sec = value;
|
||||
} else if (strcasecmp(param, "dcdc_off") == 0) {
|
||||
if (value == 0) {
|
||||
usb_cli_printf("\r\nЗначення має бути більше нуля\r\n");
|
||||
return;
|
||||
}
|
||||
new_cfg.dcdc_restart_off_sec = value;
|
||||
} else if (strcasecmp(param, "hb_start") == 0 ||
|
||||
strcasecmp(param, "hb_start_delay") == 0) {
|
||||
if (value == 0) {
|
||||
usb_cli_printf("\r\nЗначення має бути більше нуля\r\n");
|
||||
return;
|
||||
}
|
||||
new_cfg.heartbeat_start_delay_sec = value;
|
||||
} else if (strcasecmp(param, "hb_monitor") == 0) {
|
||||
if (value != 0 && value != 1) {
|
||||
usb_cli_printf("\r\nhb_monitor приймає 0 або 1\r\n");
|
||||
return;
|
||||
}
|
||||
new_cfg.heartbeat_monitor_enabled = (value != 0);
|
||||
} else if (strcasecmp(param, "hb_miss") == 0 ||
|
||||
strcasecmp(param, "hb_miss_limit") == 0) {
|
||||
if (value == 0) {
|
||||
usb_cli_printf("\r\nhb_miss має бути більше нуля\r\n");
|
||||
return;
|
||||
}
|
||||
new_cfg.heartbeat_miss_limit = value;
|
||||
} else {
|
||||
usb_cli_printf("\r\nНевідомий параметр '%s'\r\n", param);
|
||||
return;
|
||||
}
|
||||
|
||||
esp_err_t err = watch_config_save(&new_cfg);
|
||||
if (err == ESP_OK) {
|
||||
usb_cli_printf("\r\nПараметр оновлено\r\n");
|
||||
usb_cli_print_config();
|
||||
} else {
|
||||
usb_cli_printf("\r\nПомилка збереження конфігурації: %s\r\n", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_cli_task(void *arg)
|
||||
{
|
||||
usb_cli_msg_t msg;
|
||||
@@ -193,14 +418,17 @@ static void usb_cli_task(void *arg)
|
||||
usb_cli_process_line(line);
|
||||
line_len = 0;
|
||||
}
|
||||
usb_cli_write_raw("\r\n", 2);
|
||||
usb_cli_prompt();
|
||||
} else if (ch == 0x7F || ch == '\b') {
|
||||
if (line_len > 0) {
|
||||
line_len--;
|
||||
usb_cli_write_raw("\b \b", 3);
|
||||
}
|
||||
} else if (isprint((unsigned char)ch)) {
|
||||
if (line_len < CLI_LINE_MAX_LEN - 1) {
|
||||
line[line_len++] = ch;
|
||||
usb_cli_write_raw(&ch, 1);
|
||||
} else {
|
||||
usb_cli_printf("\r\nРядок занадто довгий\r\n");
|
||||
line_len = 0;
|
||||
@@ -294,3 +522,9 @@ esp_err_t usb_cdc_cli_init(void)
|
||||
ESP_LOGI(TAG, "USB CDC CLI ініціалізовано");
|
||||
return ESP_OK;
|
||||
}
|
||||
static void usb_cli_handle_reset(void)
|
||||
{
|
||||
usb_cli_printf("\r\nПерезавантаження пристрою...\r\n");
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
66
main/usb_cdc_log.c
Normal file
66
main/usb_cdc_log.c
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "usb_cdc_log.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
#include "tinyusb.h"
|
||||
#include "tusb_cdc_acm.h"
|
||||
|
||||
static const char *TAG = "usb_log";
|
||||
|
||||
static bool s_initialized;
|
||||
static bool s_host_ready;
|
||||
|
||||
static void usb_cdc_line_state_changed_callback(int itf, cdcacm_event_t *event)
|
||||
{
|
||||
(void)itf;
|
||||
s_host_ready = event->line_state_changed_data.dtr;
|
||||
if (s_host_ready) {
|
||||
const char banner[] = "\r\nwatch-watch log over USB CDC\r\n";
|
||||
tinyusb_cdcacm_write_queue(TINYUSB_CDC_ACM_0, (const uint8_t *)banner, sizeof(banner) - 1);
|
||||
tinyusb_cdcacm_write_flush(TINYUSB_CDC_ACM_0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool usb_cdc_log_ready(void)
|
||||
{
|
||||
return s_host_ready;
|
||||
}
|
||||
|
||||
esp_err_t usb_cdc_log_init(void)
|
||||
{
|
||||
if (s_initialized) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.device_descriptor = NULL,
|
||||
.string_descriptor = NULL,
|
||||
.external_phy = false,
|
||||
#if (TUD_OPT_HIGH_SPEED)
|
||||
.fs_configuration_descriptor = NULL,
|
||||
.hs_configuration_descriptor = NULL,
|
||||
.qualifier_descriptor = NULL,
|
||||
#else
|
||||
.configuration_descriptor = NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
ESP_RETURN_ON_ERROR(tinyusb_driver_install(&tusb_cfg), TAG, "TinyUSB init failed");
|
||||
|
||||
const tinyusb_config_cdcacm_t acm_cfg = {
|
||||
.usb_dev = TINYUSB_USBDEV_0,
|
||||
.cdc_port = TINYUSB_CDC_ACM_0,
|
||||
.rx_unread_buf_sz = 64,
|
||||
.callback_rx = NULL,
|
||||
.callback_rx_wanted_char = NULL,
|
||||
.callback_line_state_changed = usb_cdc_line_state_changed_callback,
|
||||
.callback_line_coding_changed = NULL,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(tusb_cdc_acm_init(&acm_cfg), TAG, "CDC init failed");
|
||||
|
||||
s_initialized = true;
|
||||
ESP_LOGI(TAG, "USB CDC лог ініціалізовано (перенаправлення ESP_LOG вимкнено)");
|
||||
return ESP_OK;
|
||||
}
|
||||
15
main/usb_cdc_log.h
Normal file
15
main/usb_cdc_log.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
/**
|
||||
* @brief Ініціалізує TinyUSB CDC та перенаправляє ESP_LOG у USB.
|
||||
*/
|
||||
esp_err_t usb_cdc_log_init(void);
|
||||
|
||||
/**
|
||||
* @brief Чи встановив хост DTR і готовий приймати лог.
|
||||
*/
|
||||
bool usb_cdc_log_ready(void);
|
||||
141
main/watch_config.c
Normal file
141
main/watch_config.c
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "watch_config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
#define WATCH_CONFIG_NAMESPACE "watchcfg"
|
||||
#define KEY_HB_PERIOD "hb_period"
|
||||
#define KEY_DCDC_OFF "dcdc_off"
|
||||
#define KEY_HB_START "hb_start"
|
||||
#define KEY_HB_MON "hb_mon"
|
||||
#define KEY_HB_MISS "hb_miss"
|
||||
|
||||
#define DEFAULT_HB_PERIOD_SEC 4U
|
||||
#define DEFAULT_DCDC_OFF_SEC 2U
|
||||
#define DEFAULT_HB_START_SEC 2U
|
||||
#define DEFAULT_HB_MISS_LIMIT 3U
|
||||
|
||||
static watch_config_t s_config = {
|
||||
.heartbeat_period_sec = DEFAULT_HB_PERIOD_SEC,
|
||||
.dcdc_restart_off_sec = DEFAULT_DCDC_OFF_SEC,
|
||||
.heartbeat_start_delay_sec = DEFAULT_HB_START_SEC,
|
||||
.heartbeat_monitor_enabled = true,
|
||||
.heartbeat_miss_limit = DEFAULT_HB_MISS_LIMIT,
|
||||
};
|
||||
static const char *TAG = "watch_cfg";
|
||||
static bool s_initialized;
|
||||
|
||||
static void watch_config_apply_defaults(void)
|
||||
{
|
||||
s_config.heartbeat_period_sec = DEFAULT_HB_PERIOD_SEC;
|
||||
s_config.dcdc_restart_off_sec = DEFAULT_DCDC_OFF_SEC;
|
||||
s_config.heartbeat_start_delay_sec = DEFAULT_HB_START_SEC;
|
||||
s_config.heartbeat_monitor_enabled = true;
|
||||
s_config.heartbeat_miss_limit = DEFAULT_HB_MISS_LIMIT;
|
||||
}
|
||||
|
||||
static void watch_config_clamp(watch_config_t *cfg)
|
||||
{
|
||||
if (!cfg->heartbeat_period_sec) {
|
||||
cfg->heartbeat_period_sec = DEFAULT_HB_PERIOD_SEC;
|
||||
}
|
||||
if (!cfg->dcdc_restart_off_sec) {
|
||||
cfg->dcdc_restart_off_sec = DEFAULT_DCDC_OFF_SEC;
|
||||
}
|
||||
if (!cfg->heartbeat_start_delay_sec) {
|
||||
cfg->heartbeat_start_delay_sec = DEFAULT_HB_START_SEC;
|
||||
}
|
||||
cfg->heartbeat_monitor_enabled = cfg->heartbeat_monitor_enabled ? true : false;
|
||||
if (!cfg->heartbeat_miss_limit) {
|
||||
cfg->heartbeat_miss_limit = DEFAULT_HB_MISS_LIMIT;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t watch_config_init(void)
|
||||
{
|
||||
if (s_initialized) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t err = nvs_flash_init();
|
||||
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_RETURN_ON_ERROR(nvs_flash_erase(), TAG, "NVS erase failed");
|
||||
err = nvs_flash_init();
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(err, TAG, "NVS init failed");
|
||||
|
||||
watch_config_apply_defaults();
|
||||
|
||||
nvs_handle_t handle = 0;
|
||||
err = nvs_open(WATCH_CONFIG_NAMESPACE, NVS_READWRITE, &handle);
|
||||
ESP_RETURN_ON_ERROR(err, TAG, "NVS open failed");
|
||||
|
||||
uint32_t value = 0;
|
||||
if (nvs_get_u32(handle, KEY_HB_PERIOD, &value) == ESP_OK && value > 0) {
|
||||
s_config.heartbeat_period_sec = value;
|
||||
}
|
||||
if (nvs_get_u32(handle, KEY_DCDC_OFF, &value) == ESP_OK && value > 0) {
|
||||
s_config.dcdc_restart_off_sec = value;
|
||||
}
|
||||
if (nvs_get_u32(handle, KEY_HB_START, &value) == ESP_OK && value > 0) {
|
||||
s_config.heartbeat_start_delay_sec = value;
|
||||
}
|
||||
uint8_t hb_mon = 1;
|
||||
if (nvs_get_u8(handle, KEY_HB_MON, &hb_mon) == ESP_OK) {
|
||||
s_config.heartbeat_monitor_enabled = (hb_mon != 0);
|
||||
}
|
||||
if (nvs_get_u32(handle, KEY_HB_MISS, &value) == ESP_OK && value > 0) {
|
||||
s_config.heartbeat_miss_limit = value;
|
||||
}
|
||||
|
||||
nvs_close(handle);
|
||||
s_initialized = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const watch_config_t *watch_config_get(void)
|
||||
{
|
||||
return &s_config;
|
||||
}
|
||||
|
||||
esp_err_t watch_config_save(const watch_config_t *cfg)
|
||||
{
|
||||
if (!cfg) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
watch_config_t tmp = *cfg;
|
||||
watch_config_clamp(&tmp);
|
||||
|
||||
nvs_handle_t handle = 0;
|
||||
ESP_RETURN_ON_ERROR(nvs_open(WATCH_CONFIG_NAMESPACE, NVS_READWRITE, &handle),
|
||||
TAG, "NVS open failed");
|
||||
|
||||
esp_err_t err = nvs_set_u32(handle, KEY_HB_PERIOD, tmp.heartbeat_period_sec);
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_set_u32(handle, KEY_DCDC_OFF, tmp.dcdc_restart_off_sec);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_set_u32(handle, KEY_HB_START, tmp.heartbeat_start_delay_sec);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_set_u8(handle, KEY_HB_MON, tmp.heartbeat_monitor_enabled ? 1 : 0);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_set_u32(handle, KEY_HB_MISS, tmp.heartbeat_miss_limit);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_commit(handle);
|
||||
}
|
||||
nvs_close(handle);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
s_config = tmp;
|
||||
return ESP_OK;
|
||||
}
|
||||
18
main/watch_config.h
Normal file
18
main/watch_config.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t heartbeat_period_sec;
|
||||
uint32_t dcdc_restart_off_sec;
|
||||
uint32_t heartbeat_start_delay_sec;
|
||||
bool heartbeat_monitor_enabled;
|
||||
uint32_t heartbeat_miss_limit;
|
||||
} watch_config_t;
|
||||
|
||||
esp_err_t watch_config_init(void);
|
||||
const watch_config_t *watch_config_get(void);
|
||||
esp_err_t watch_config_save(const watch_config_t *cfg);
|
||||
367
main/ws2812_status.c
Normal file
367
main/ws2812_status.c
Normal file
@@ -0,0 +1,367 @@
|
||||
#include "ws2812_status.h"
|
||||
|
||||
#include "dcdc_controller.h"
|
||||
#include "led_strip.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/timers.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifndef CONFIG_WATCH_WS2812_LED_COUNT
|
||||
#define CONFIG_WATCH_WS2812_LED_COUNT 5
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_WS2812_GPIO
|
||||
#define CONFIG_WATCH_WS2812_GPIO 8
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_WS2812_RMT_RESOLUTION
|
||||
#define CONFIG_WATCH_WS2812_RMT_RESOLUTION (10 * 1000 * 1000)
|
||||
#endif
|
||||
|
||||
#define WS2812_STATUS_GPIO ((gpio_num_t)CONFIG_WATCH_WS2812_GPIO)
|
||||
#define WS2812_STATUS_RESOLUTION_HZ CONFIG_WATCH_WS2812_RMT_RESOLUTION
|
||||
#define WS2812_ANIM_REFRESH pdMS_TO_TICKS(100)
|
||||
#define WS2812_ALERT_BLINK_PERIOD pdMS_TO_TICKS(200)
|
||||
#define WS2812_ALERT_GAP_PERIOD pdMS_TO_TICKS(2000)
|
||||
#define WS2812_VPN_ALERT_BLINKS 2
|
||||
#define WS2812_APP_ALERT_BLINKS 3
|
||||
#define WS2812_BRIGHTNESS_PERCENT 5
|
||||
|
||||
#define WS2812_VPN_SECTION_TICKS (WS2812_ALERT_BLINK_PERIOD * 2U * WS2812_VPN_ALERT_BLINKS)
|
||||
#define WS2812_APP_SECTION_TICKS (WS2812_ALERT_BLINK_PERIOD * 2U * WS2812_APP_ALERT_BLINKS)
|
||||
|
||||
static const char *TAG = "ws2812";
|
||||
|
||||
static led_strip_handle_t s_strip;
|
||||
static bool s_channel_enabled[WS2812_STATUS_LED_COUNT];
|
||||
static bool s_vpn_state[WS2812_STATUS_LED_COUNT];
|
||||
static bool s_app_state[WS2812_STATUS_LED_COUNT];
|
||||
static SemaphoreHandle_t s_ws_lock;
|
||||
static TimerHandle_t s_animation_timer;
|
||||
static bool s_error_state;
|
||||
static bool s_startup_hold;
|
||||
static TickType_t s_startup_expire_tick;
|
||||
static TickType_t s_alert_cycle_epoch;
|
||||
static size_t s_active_vpn_alerts;
|
||||
static size_t s_active_app_alerts;
|
||||
|
||||
static esp_err_t ws2812_status_apply(void);
|
||||
static void ws2812_animation_timer_cb(TimerHandle_t timer);
|
||||
static void ws2812_status_compute_color(size_t index,
|
||||
TickType_t now_ticks,
|
||||
uint8_t *r,
|
||||
uint8_t *g,
|
||||
uint8_t *b);
|
||||
static bool ws2812_recalculate_alert_counts(void);
|
||||
static uint8_t ws2812_apply_brightness(uint8_t component);
|
||||
|
||||
static void ws2812_animation_timer_cb(TimerHandle_t timer)
|
||||
{
|
||||
(void)timer;
|
||||
ws2812_status_apply();
|
||||
}
|
||||
|
||||
static bool ws2812_startup_hold_active(TickType_t now_ticks)
|
||||
{
|
||||
if (!s_startup_hold) {
|
||||
return false;
|
||||
}
|
||||
if (now_ticks >= s_startup_expire_tick) {
|
||||
s_startup_hold = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ws2812_status_compute_color(size_t index,
|
||||
TickType_t now_ticks,
|
||||
uint8_t *r,
|
||||
uint8_t *g,
|
||||
uint8_t *b)
|
||||
{
|
||||
if (!r || !g || !b || index >= WS2812_STATUS_LED_COUNT) {
|
||||
return;
|
||||
}
|
||||
|
||||
*r = 0;
|
||||
*g = 0;
|
||||
*b = 0;
|
||||
|
||||
if (s_error_state) {
|
||||
*r = 40;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ws2812_startup_hold_active(now_ticks)) {
|
||||
*g = 40;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s_channel_enabled[index]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool vpn_alert = !s_vpn_state[index];
|
||||
const bool app_alert = !s_app_state[index];
|
||||
if (!vpn_alert && !app_alert) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool vpn_global = (s_active_vpn_alerts > 0);
|
||||
const bool app_global = (s_active_app_alerts > 0);
|
||||
|
||||
TickType_t cycle_ticks = 0;
|
||||
if (vpn_global) {
|
||||
cycle_ticks += WS2812_VPN_SECTION_TICKS + WS2812_ALERT_GAP_PERIOD;
|
||||
}
|
||||
if (app_global) {
|
||||
cycle_ticks += WS2812_APP_SECTION_TICKS + WS2812_ALERT_GAP_PERIOD;
|
||||
}
|
||||
if (cycle_ticks == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
TickType_t blink_period = WS2812_ALERT_BLINK_PERIOD ? WS2812_ALERT_BLINK_PERIOD : 1;
|
||||
TickType_t cycle_pos = (now_ticks - s_alert_cycle_epoch) % cycle_ticks;
|
||||
|
||||
if (vpn_global) {
|
||||
if (cycle_pos < WS2812_VPN_SECTION_TICKS) {
|
||||
if (vpn_alert) {
|
||||
const bool on = ((cycle_pos / blink_period) % 2U) == 0U;
|
||||
if (on) {
|
||||
*r = 90;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
cycle_pos -= WS2812_VPN_SECTION_TICKS;
|
||||
if (cycle_pos < WS2812_ALERT_GAP_PERIOD) {
|
||||
return;
|
||||
}
|
||||
cycle_pos -= WS2812_ALERT_GAP_PERIOD;
|
||||
}
|
||||
|
||||
if (app_global) {
|
||||
if (cycle_pos < WS2812_APP_SECTION_TICKS) {
|
||||
if (app_alert) {
|
||||
const bool on = ((cycle_pos / blink_period) % 2U) == 0U;
|
||||
if (on) {
|
||||
*r = 80;
|
||||
*g = 80;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
cycle_pos -= WS2812_APP_SECTION_TICKS;
|
||||
if (cycle_pos < WS2812_ALERT_GAP_PERIOD) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t ws2812_status_apply(void)
|
||||
{
|
||||
if (!s_strip) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (!s_ws_lock) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (xSemaphoreTake(s_ws_lock, portMAX_DELAY) != pdTRUE) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
const TickType_t now_ticks = xTaskGetTickCount();
|
||||
esp_err_t err = ESP_OK;
|
||||
for (size_t i = 0; i < WS2812_STATUS_LED_COUNT && err == ESP_OK; ++i) {
|
||||
uint8_t r = 0, g = 0, b = 0;
|
||||
ws2812_status_compute_color(i, now_ticks, &r, &g, &b);
|
||||
err = led_strip_set_pixel(s_strip,
|
||||
i,
|
||||
ws2812_apply_brightness(g),
|
||||
ws2812_apply_brightness(r),
|
||||
ws2812_apply_brightness(b));
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
err = led_strip_refresh(s_strip);
|
||||
}
|
||||
|
||||
xSemaphoreGive(s_ws_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t ws2812_status_init(void)
|
||||
{
|
||||
if (s_strip) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
led_strip_config_t strip_config = {
|
||||
.strip_gpio_num = WS2812_STATUS_GPIO,
|
||||
.max_leds = WS2812_STATUS_LED_COUNT,
|
||||
.led_model = LED_MODEL_WS2812,
|
||||
.flags.invert_out = false,
|
||||
};
|
||||
led_strip_rmt_config_t rmt_cfg = {
|
||||
.clk_src = RMT_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = WS2812_STATUS_RESOLUTION_HZ,
|
||||
.flags.with_dma = false,
|
||||
};
|
||||
|
||||
ESP_RETURN_ON_ERROR(
|
||||
led_strip_new_rmt_device(&strip_config, &rmt_cfg, &s_strip),
|
||||
TAG,
|
||||
"Не вдалося створити RMT пристрій для WS2812");
|
||||
ESP_RETURN_ON_ERROR(led_strip_clear(s_strip), TAG, "clear fail");
|
||||
|
||||
s_ws_lock = xSemaphoreCreateMutex();
|
||||
ESP_RETURN_ON_FALSE(s_ws_lock, ESP_ERR_NO_MEM, TAG, "mutex alloc failed");
|
||||
|
||||
const TickType_t now = xTaskGetTickCount();
|
||||
for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) {
|
||||
s_channel_enabled[i] = false;
|
||||
s_vpn_state[i] = true;
|
||||
s_app_state[i] = true;
|
||||
}
|
||||
s_error_state = false;
|
||||
s_startup_hold = false;
|
||||
s_startup_expire_tick = 0;
|
||||
s_alert_cycle_epoch = now;
|
||||
(void)ws2812_recalculate_alert_counts();
|
||||
|
||||
if (!s_animation_timer) {
|
||||
s_animation_timer = xTimerCreate("ws2812_anim",
|
||||
WS2812_ANIM_REFRESH,
|
||||
pdTRUE,
|
||||
NULL,
|
||||
ws2812_animation_timer_cb);
|
||||
ESP_RETURN_ON_FALSE(s_animation_timer, ESP_ERR_NO_MEM, TAG, "anim timer alloc failed");
|
||||
ESP_RETURN_ON_FALSE(xTimerStart(s_animation_timer, 0) == pdPASS,
|
||||
ESP_FAIL,
|
||||
TAG,
|
||||
"anim timer start failed");
|
||||
}
|
||||
|
||||
(void)ws2812_recalculate_alert_counts();
|
||||
return ws2812_status_apply();
|
||||
}
|
||||
|
||||
esp_err_t ws2812_status_set_channel_state(size_t channel, bool enabled)
|
||||
{
|
||||
if (channel >= WS2812_STATUS_LED_COUNT) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!s_strip) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
s_channel_enabled[channel] = enabled;
|
||||
if (!enabled) {
|
||||
s_vpn_state[channel] = true;
|
||||
s_app_state[channel] = true;
|
||||
}
|
||||
return ws2812_status_apply();
|
||||
}
|
||||
|
||||
esp_err_t ws2812_status_set_error(bool has_error)
|
||||
{
|
||||
if (!s_strip) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
s_error_state = has_error;
|
||||
return ws2812_status_apply();
|
||||
}
|
||||
|
||||
esp_err_t ws2812_status_set_service_state(size_t channel, bool vpn_ok, bool app_ok)
|
||||
{
|
||||
if (channel >= WS2812_STATUS_LED_COUNT) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!s_strip) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
bool changed = false;
|
||||
if (s_vpn_state[channel] != vpn_ok) {
|
||||
s_vpn_state[channel] = vpn_ok;
|
||||
changed = true;
|
||||
}
|
||||
if (s_app_state[channel] != app_ok) {
|
||||
s_app_state[channel] = app_ok;
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
(void)ws2812_recalculate_alert_counts();
|
||||
s_alert_cycle_epoch = xTaskGetTickCount();
|
||||
}
|
||||
return ws2812_status_apply();
|
||||
}
|
||||
|
||||
esp_err_t ws2812_status_refresh_from_dcdc(void)
|
||||
{
|
||||
if (!s_strip) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
const size_t available_channels = dcdc_channel_count();
|
||||
const size_t count = available_channels < WS2812_STATUS_LED_COUNT
|
||||
? available_channels
|
||||
: WS2812_STATUS_LED_COUNT;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
s_channel_enabled[i] = dcdc_get_state(i);
|
||||
}
|
||||
for (size_t i = count; i < WS2812_STATUS_LED_COUNT; ++i) {
|
||||
s_channel_enabled[i] = false;
|
||||
s_vpn_state[i] = true;
|
||||
s_app_state[i] = true;
|
||||
}
|
||||
(void)ws2812_recalculate_alert_counts();
|
||||
s_alert_cycle_epoch = xTaskGetTickCount();
|
||||
return ws2812_status_apply();
|
||||
}
|
||||
|
||||
esp_err_t ws2812_status_set_startup_hold(uint32_t duration_ms)
|
||||
{
|
||||
if (!s_strip) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (duration_ms == 0) {
|
||||
s_startup_hold = false;
|
||||
s_startup_expire_tick = 0;
|
||||
return ws2812_status_apply();
|
||||
}
|
||||
|
||||
TickType_t duration_ticks = pdMS_TO_TICKS(duration_ms);
|
||||
if (duration_ticks == 0) {
|
||||
duration_ticks = 1;
|
||||
}
|
||||
s_startup_hold = true;
|
||||
s_startup_expire_tick = xTaskGetTickCount() + duration_ticks;
|
||||
return ws2812_status_apply();
|
||||
}
|
||||
static bool ws2812_recalculate_alert_counts(void)
|
||||
{
|
||||
size_t vpn = 0;
|
||||
size_t app = 0;
|
||||
for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) {
|
||||
if (!s_channel_enabled[i]) {
|
||||
continue;
|
||||
}
|
||||
if (!s_vpn_state[i]) {
|
||||
++vpn;
|
||||
}
|
||||
if (!s_app_state[i]) {
|
||||
++app;
|
||||
}
|
||||
}
|
||||
const bool changed = (vpn != s_active_vpn_alerts) || (app != s_active_app_alerts);
|
||||
s_active_vpn_alerts = vpn;
|
||||
s_active_app_alerts = app;
|
||||
return changed;
|
||||
}
|
||||
|
||||
static uint8_t ws2812_apply_brightness(uint8_t component)
|
||||
{
|
||||
// Scale brightness down (component * 20%) with rounding to nearest.
|
||||
const uint16_t scaled = (component * WS2812_BRIGHTNESS_PERCENT + 50U) / 100U;
|
||||
return (uint8_t)scaled;
|
||||
}
|
||||
21
main/ws2812_status.h
Normal file
21
main/ws2812_status.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifndef CONFIG_WATCH_WS2812_LED_COUNT
|
||||
#define CONFIG_WATCH_WS2812_LED_COUNT 5
|
||||
#endif
|
||||
|
||||
#define WS2812_STATUS_LED_COUNT CONFIG_WATCH_WS2812_LED_COUNT
|
||||
|
||||
esp_err_t ws2812_status_init(void);
|
||||
esp_err_t ws2812_status_set_channel_state(size_t channel, bool enabled);
|
||||
esp_err_t ws2812_status_set_error(bool has_error);
|
||||
esp_err_t ws2812_status_set_service_state(size_t channel, bool vpn_ok, bool app_ok);
|
||||
esp_err_t ws2812_status_set_startup_hold(uint32_t duration_ms);
|
||||
esp_err_t ws2812_status_refresh_from_dcdc(void);
|
||||
1
managed_components/espressif__led_strip/.component_hash
Normal file
1
managed_components/espressif__led_strip/.component_hash
Normal file
@@ -0,0 +1 @@
|
||||
28c6509a727ef74925b372ed404772aeedf11cce10b78c3f69b3c66799095e2d
|
||||
54
managed_components/espressif__led_strip/CHANGELOG.md
Normal file
54
managed_components/espressif__led_strip/CHANGELOG.md
Normal file
@@ -0,0 +1,54 @@
|
||||
## 2.5.5
|
||||
|
||||
- Simplified the led_strip component dependency, the time of full build with ESP-IDF v5.3 can now be shorter.
|
||||
|
||||
## 2.5.4
|
||||
|
||||
- Inserted extra delay when initialize the SPI LED device, to ensure all LEDs are in the reset state correctly
|
||||
|
||||
## 2.5.3
|
||||
|
||||
- Extend reset time (280us) to support WS2812B-V5
|
||||
|
||||
## 2.5.2
|
||||
|
||||
- Added API reference doc (api.md)
|
||||
|
||||
## 2.5.0
|
||||
|
||||
- Enabled support for IDF4.4 and above
|
||||
- with RMT backend only
|
||||
- Added API `led_strip_set_pixel_hsv`
|
||||
|
||||
## 2.4.0
|
||||
|
||||
- Support configurable SPI mode to control leds
|
||||
- recommend enabling DMA when using SPI mode
|
||||
|
||||
## 2.3.0
|
||||
|
||||
- Support configurable RMT channel size by setting `mem_block_symbols`
|
||||
|
||||
## 2.2.0
|
||||
|
||||
- Support for 4 components RGBW leds (SK6812):
|
||||
- in led_strip_config_t new fields
|
||||
led_pixel_format, controlling byte format (LED_PIXEL_FORMAT_GRB, LED_PIXEL_FORMAT_GRBW)
|
||||
led_model, used to configure bit timing (LED_MODEL_WS2812, LED_MODEL_SK6812)
|
||||
- new API led_strip_set_pixel_rgbw
|
||||
- new interface type set_pixel_rgbw
|
||||
|
||||
## 2.1.0
|
||||
|
||||
- Support DMA feature, which offloads the CPU by a lot when it comes to drive a bunch of LEDs
|
||||
- Support various RMT clock sources
|
||||
- Acquire and release the power management lock before and after each refresh
|
||||
- New driver flag: `invert_out` which can invert the led control signal by hardware
|
||||
|
||||
## 2.0.0
|
||||
|
||||
- Reimplemented the driver using the new RMT driver (`driver/rmt_tx.h`)
|
||||
|
||||
## 1.0.0
|
||||
|
||||
- Initial driver version, based on the legacy RMT driver (`driver/rmt.h`)
|
||||
1
managed_components/espressif__led_strip/CHECKSUMS.json
Normal file
1
managed_components/espressif__led_strip/CHECKSUMS.json
Normal file
@@ -0,0 +1 @@
|
||||
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-05-21T16:14:59.641729+00:00", "files": [{"path": "CMakeLists.txt", "size": 1131, "hash": "527e1c93841dcf98e4e30767c0b55ec117e1af52638eb72a7aec23d25a31074b"}, {"path": "api.md", "size": 12932, "hash": "fa49f00d63cab790d2e3ffb1cc2436ec337fab336f55a8c2730aa1d923b655e4"}, {"path": "LICENSE", "size": 11358, "hash": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"}, {"path": "CHANGELOG.md", "size": 1469, "hash": "8f323462bb2f1c0159b2e2c5fef08628d47a7ff55ef9829221889c2486bbfad8"}, {"path": "idf_component.yml", "size": 335, "hash": "12ff580cba4f58c8c41cff96d0c81bc001d40d99ad15ba16afabce64261d4b8a"}, {"path": "README.md", "size": 4920, "hash": "f94c5b61f704ec6247450b3252e35b6130b5350edff560dd172f3a7345fac637"}, {"path": "Doxyfile", "size": 684, "hash": "f05e35d4108be9b46398fbd7c0cef9880c0e802abbe36a36a49c7a0ac4126826"}, {"path": "interface/led_strip_interface.h", "size": 2934, "hash": "5b7d0c326d0d0d9748830d4aec46d765400e1446055d4a1197c83111e937d74c"}, {"path": "include/led_strip_spi.h", "size": 1596, "hash": "d79f3a803834ff28930a32380a48afe0977de167d31d673589e98c2a3cf66e5f"}, {"path": "include/led_strip_types.h", "size": 1352, "hash": "13fb81ed62bcaa6d8686c5b095c45adede526bcba3980ad0f9341c38b4bedc96"}, {"path": "include/led_strip_rmt.h", "size": 1876, "hash": "d3d6f0880f7443c21b48c1715e524282688ce9743085b4d7358322f6714b82d2"}, {"path": "include/led_strip.h", "size": 3586, "hash": "f01e0f282fe3042b03381495c616eae211d0b75d04c64e29b3dc92721fbadc4f"}, {"path": "src/led_strip_spi_dev.c", "size": 9346, "hash": "bff2b2e1dc3240d21b1679fba42f0f4395bf9449b21ee3c2408bbc1ae1b9c211"}, {"path": "src/led_strip_rmt_encoder.h", "size": 977, "hash": "690381c35ace2703a5c7156f6547a8524f4cbfe5bef40be619e2097960120a40"}, {"path": "src/led_strip_rmt_dev_idf4.c", "size": 7143, "hash": "81b4a239b554f1ed3326b5380490316a9602bc606a00691a5f325adcbdf2145c"}, {"path": "src/led_strip_api.c", "size": 2575, "hash": "2a8be1284ed6b2ad000907bca3829f71a499aa0d06d47f078d6306392dcc4f4c"}, {"path": "src/led_strip_rmt_encoder.c", "size": 6097, "hash": "7e349a50bd3a161932200dd3da24a6e7316438e0e31615aed6d20deeb3917558"}, {"path": "src/led_strip_rmt_dev.c", "size": 6885, "hash": "3a41df64f2582f3abedd4bddf3111a6bba4a1f886d58dd8391ac2b903ea82e9f"}, {"path": "examples/led_strip_rmt_ws2812/CMakeLists.txt", "size": 381, "hash": "26a31ebd66ad328361d8b69d7f99b9250685932d077dc300dc96280ff6623bc4"}, {"path": "examples/led_strip_rmt_ws2812/README.md", "size": 1200, "hash": "a5f39b31c5f7cbf548ee31b61ab22e430a6c823404c0ddb113703512bcb3ad3c"}, {"path": "examples/led_strip_spi_ws2812/CMakeLists.txt", "size": 381, "hash": "3096c4f9363c9ccf5733c19542163dcb4497764435fb996156552b81c55c6e14"}, {"path": "examples/led_strip_spi_ws2812/README.md", "size": 1201, "hash": "2c02a29197cd1f2d4af4c4c9cd44677e303b0e168a1773eef9fc3fdb39377d27"}, {"path": "examples/led_strip_spi_ws2812/main/CMakeLists.txt", "size": 99, "hash": "34e7f83d26bca924c629ea2012e6f200b415d486907863fe936d94872ff739eb"}, {"path": "examples/led_strip_spi_ws2812/main/idf_component.yml", "size": 142, "hash": "fe89054a1b7988a7068a2952d01bfc6ae40dc59a33bc7dcc8a98cbb231d472fb"}, {"path": "examples/led_strip_spi_ws2812/main/led_strip_spi_ws2812_main.c", "size": 2462, "hash": "8e052cbfd7e27d5e3382f5f115db23ea884db25f4a73fb69d34ed4867dfbf80a"}, {"path": "examples/led_strip_rmt_ws2812/main/CMakeLists.txt", "size": 99, "hash": "8960b68811805d3aa40e1a7f44ddf7400c0d0731829b6d2b3b1584d8dcd3b392"}, {"path": "examples/led_strip_rmt_ws2812/main/idf_component.yml", "size": 125, "hash": "05a57e3d6808bac9e1a2f41cbcfa26d90b5a3427d518b619a1ff4946e62d791d"}, {"path": "examples/led_strip_rmt_ws2812/main/led_strip_rmt_ws2812_main.c", "size": 2705, "hash": "497f3b88387865c41f7a30c7ab5e0086066a344bf16f250d973bf0af907fa78f"}]}
|
||||
31
managed_components/espressif__led_strip/CMakeLists.txt
Normal file
31
managed_components/espressif__led_strip/CMakeLists.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
include($ENV{IDF_PATH}/tools/cmake/version.cmake)
|
||||
|
||||
set(srcs "src/led_strip_api.c")
|
||||
set(public_requires)
|
||||
|
||||
# Starting from esp-idf v5.x, the RMT driver is rewritten
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
|
||||
if(CONFIG_SOC_RMT_SUPPORTED)
|
||||
list(APPEND srcs "src/led_strip_rmt_dev.c" "src/led_strip_rmt_encoder.c")
|
||||
endif()
|
||||
else()
|
||||
list(APPEND srcs "src/led_strip_rmt_dev_idf4.c")
|
||||
endif()
|
||||
|
||||
# the SPI backend driver relies on some feature that was available in IDF 5.1
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.1")
|
||||
if(CONFIG_SOC_GPSPI_SUPPORTED)
|
||||
list(APPEND srcs "src/led_strip_spi_dev.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Starting from esp-idf v5.3, the RMT and SPI drivers are moved to separate components
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.3")
|
||||
list(APPEND public_requires "esp_driver_rmt" "esp_driver_spi")
|
||||
else()
|
||||
list(APPEND public_requires "driver")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS "include" "interface"
|
||||
REQUIRES ${public_requires})
|
||||
28
managed_components/espressif__led_strip/Doxyfile
Normal file
28
managed_components/espressif__led_strip/Doxyfile
Normal file
@@ -0,0 +1,28 @@
|
||||
# Set this to the header file you want
|
||||
INPUT = \
|
||||
include/ \
|
||||
interface/
|
||||
|
||||
# Output goes into doxygen directory, which is added to gitignore
|
||||
OUTPUT_DIRECTORY = doxygen
|
||||
|
||||
# Warning-related settings, it's recommended to keep them enabled
|
||||
WARN_IF_UNDOC_ENUM_VAL = YES
|
||||
WARN_AS_ERROR = YES
|
||||
|
||||
# Other common settings
|
||||
FULL_PATH_NAMES = YES
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = YES
|
||||
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||
EXPAND_ONLY_PREDEF = YES
|
||||
EXTRACT_ALL = YES
|
||||
PREDEFINED = $(ENV_DOXYGEN_DEFINES)
|
||||
HAVE_DOT = NO
|
||||
GENERATE_XML = YES
|
||||
XML_OUTPUT = xml
|
||||
GENERATE_HTML = NO
|
||||
HAVE_DOT = NO
|
||||
GENERATE_LATEX = NO
|
||||
QUIET = YES
|
||||
MARKDOWN_SUPPORT = YES
|
||||
202
managed_components/espressif__led_strip/LICENSE
Normal file
202
managed_components/espressif__led_strip/LICENSE
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
97
managed_components/espressif__led_strip/README.md
Normal file
97
managed_components/espressif__led_strip/README.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# LED Strip Driver
|
||||
|
||||
[](https://components.espressif.com/components/espressif/led_strip)
|
||||
|
||||
This driver is designed for addressable LEDs like [WS2812](http://www.world-semi.com/Certifications/WS2812B.html), where each LED is controlled by a single data line.
|
||||
|
||||
## Backend Controllers
|
||||
|
||||
### The [RMT](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/rmt.html) Peripheral
|
||||
|
||||
This is the most economical way to drive the LEDs because it only consumes one RMT channel, leaving other channels free to use. However, the memory usage increases dramatically with the number of LEDs. If the RMT hardware can't be assist by DMA, the driver will going into interrupt very frequently, thus result in a high CPU usage. What's worse, if the RMT interrupt is delayed or not serviced in time (e.g. if Wi-Fi interrupt happens on the same CPU core), the RMT transaction will be corrupted and the LEDs will display incorrect colors. If you want to use RMT to drive a large number of LEDs, you'd better to enable the DMA feature if possible [^1].
|
||||
|
||||
#### Allocate LED Strip Object with RMT Backend
|
||||
|
||||
```c
|
||||
#define BLINK_GPIO 0
|
||||
|
||||
led_strip_handle_t led_strip;
|
||||
|
||||
/* LED strip initialization with the GPIO and pixels number*/
|
||||
led_strip_config_t strip_config = {
|
||||
.strip_gpio_num = BLINK_GPIO, // The GPIO that connected to the LED strip's data line
|
||||
.max_leds = 1, // The number of LEDs in the strip,
|
||||
.led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip
|
||||
.led_model = LED_MODEL_WS2812, // LED strip model
|
||||
.flags.invert_out = false, // whether to invert the output signal (useful when your hardware has a level inverter)
|
||||
};
|
||||
|
||||
led_strip_rmt_config_t rmt_config = {
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
.rmt_channel = 0,
|
||||
#else
|
||||
.clk_src = RMT_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||
.resolution_hz = 10 * 1000 * 1000, // 10MHz
|
||||
.flags.with_dma = false, // whether to enable the DMA feature
|
||||
#endif
|
||||
};
|
||||
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
|
||||
```
|
||||
|
||||
You can create multiple LED strip objects with different GPIOs and pixel numbers. The backend driver will automatically allocate the RMT channel for you if there is more available.
|
||||
|
||||
### The [SPI](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html) Peripheral
|
||||
|
||||
SPI peripheral can also be used to generate the timing required by the LED strip. However this backend is not as economical as the RMT one, because it will take up the whole **bus**, unlike the RMT just takes one **channel**. You **CANT** connect other devices to the same SPI bus if it's been used by the led_strip, because the led_strip doesn't have the concept of "Chip Select".
|
||||
|
||||
Please note, the SPI backend has a dependency of **ESP-IDF >= 5.1**
|
||||
|
||||
#### Allocate LED Strip Object with SPI Backend
|
||||
|
||||
```c
|
||||
#define BLINK_GPIO 0
|
||||
|
||||
led_strip_handle_t led_strip;
|
||||
|
||||
/* LED strip initialization with the GPIO and pixels number*/
|
||||
led_strip_config_t strip_config = {
|
||||
.strip_gpio_num = BLINK_GPIO, // The GPIO that connected to the LED strip's data line
|
||||
.max_leds = 1, // The number of LEDs in the strip,
|
||||
.led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip
|
||||
.led_model = LED_MODEL_WS2812, // LED strip model
|
||||
.flags.invert_out = false, // whether to invert the output signal (useful when your hardware has a level inverter)
|
||||
};
|
||||
|
||||
led_strip_spi_config_t spi_config = {
|
||||
.clk_src = SPI_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||
.flags.with_dma = true, // Using DMA can improve performance and help drive more LEDs
|
||||
.spi_bus = SPI2_HOST, // SPI bus ID
|
||||
};
|
||||
ESP_ERROR_CHECK(led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
|
||||
```
|
||||
|
||||
The number of LED strip objects can be created depends on how many free SPI buses are free to use in your project.
|
||||
|
||||
## FAQ
|
||||
|
||||
* Which led_strip backend should I choose?
|
||||
* It depends on your application requirement and target chip's ability.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A{Is RMT supported?}
|
||||
A --> |No| B[SPI backend]
|
||||
B --> C{Does the led strip has \n a larger number of LEDs?}
|
||||
C --> |No| D[Don't have to enable the DMA of the backend]
|
||||
C --> |Yes| E[Enable the DMA of the backend]
|
||||
A --> |Yes| F{Does the led strip has \n a larger number of LEDs?}
|
||||
F --> |Yes| G{Does RMT support DMA?}
|
||||
G --> |Yes| E
|
||||
G --> |No| B
|
||||
F --> |No| H[RMT backend] --> D
|
||||
```
|
||||
|
||||
* How to set the brightness of the LED strip?
|
||||
* You can tune the brightness by scaling the value of each R-G-B element with a **same** factor. But pay attention to the overflow of the value.
|
||||
|
||||
[^1]: The RMT DMA feature is not available on all ESP chips. Please check the data sheet before using it.
|
||||
454
managed_components/espressif__led_strip/api.md
Normal file
454
managed_components/espressif__led_strip/api.md
Normal file
@@ -0,0 +1,454 @@
|
||||
# API Reference
|
||||
|
||||
## Header files
|
||||
|
||||
- [include/led_strip.h](#file-includeled_striph)
|
||||
- [include/led_strip_rmt.h](#file-includeled_strip_rmth)
|
||||
- [include/led_strip_spi.h](#file-includeled_strip_spih)
|
||||
- [include/led_strip_types.h](#file-includeled_strip_typesh)
|
||||
- [interface/led_strip_interface.h](#file-interfaceled_strip_interfaceh)
|
||||
|
||||
## File include/led_strip.h
|
||||
|
||||
## Functions
|
||||
|
||||
| Type | Name |
|
||||
| ---: | :--- |
|
||||
| esp\_err\_t | [**led\_strip\_clear**](#function-led_strip_clear) ([**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) strip) <br>_Clear LED strip (turn off all LEDs)_ |
|
||||
| esp\_err\_t | [**led\_strip\_del**](#function-led_strip_del) ([**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) strip) <br>_Free LED strip resources._ |
|
||||
| esp\_err\_t | [**led\_strip\_refresh**](#function-led_strip_refresh) ([**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) strip) <br>_Refresh memory colors to LEDs._ |
|
||||
| esp\_err\_t | [**led\_strip\_set\_pixel**](#function-led_strip_set_pixel) ([**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) strip, uint32\_t index, uint32\_t red, uint32\_t green, uint32\_t blue) <br>_Set RGB for a specific pixel._ |
|
||||
| esp\_err\_t | [**led\_strip\_set\_pixel\_hsv**](#function-led_strip_set_pixel_hsv) ([**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) strip, uint32\_t index, uint16\_t hue, uint8\_t saturation, uint8\_t value) <br>_Set HSV for a specific pixel._ |
|
||||
| esp\_err\_t | [**led\_strip\_set\_pixel\_rgbw**](#function-led_strip_set_pixel_rgbw) ([**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) strip, uint32\_t index, uint32\_t red, uint32\_t green, uint32\_t blue, uint32\_t white) <br>_Set RGBW for a specific pixel._ |
|
||||
|
||||
## Functions Documentation
|
||||
|
||||
### function `led_strip_clear`
|
||||
|
||||
_Clear LED strip (turn off all LEDs)_
|
||||
|
||||
```c
|
||||
esp_err_t led_strip_clear (
|
||||
led_strip_handle_t strip
|
||||
)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `strip` LED strip
|
||||
|
||||
**Returns:**
|
||||
|
||||
- ESP\_OK: Clear LEDs successfully
|
||||
- ESP\_FAIL: Clear LEDs failed because some other error occurred
|
||||
|
||||
### function `led_strip_del`
|
||||
|
||||
_Free LED strip resources._
|
||||
|
||||
```c
|
||||
esp_err_t led_strip_del (
|
||||
led_strip_handle_t strip
|
||||
)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `strip` LED strip
|
||||
|
||||
**Returns:**
|
||||
|
||||
- ESP\_OK: Free resources successfully
|
||||
- ESP\_FAIL: Free resources failed because error occurred
|
||||
|
||||
### function `led_strip_refresh`
|
||||
|
||||
_Refresh memory colors to LEDs._
|
||||
|
||||
```c
|
||||
esp_err_t led_strip_refresh (
|
||||
led_strip_handle_t strip
|
||||
)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `strip` LED strip
|
||||
|
||||
**Returns:**
|
||||
|
||||
- ESP\_OK: Refresh successfully
|
||||
- ESP\_FAIL: Refresh failed because some other error occurred
|
||||
|
||||
**Note:**
|
||||
|
||||
: After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
|
||||
|
||||
### function `led_strip_set_pixel`
|
||||
|
||||
_Set RGB for a specific pixel._
|
||||
|
||||
```c
|
||||
esp_err_t led_strip_set_pixel (
|
||||
led_strip_handle_t strip,
|
||||
uint32_t index,
|
||||
uint32_t red,
|
||||
uint32_t green,
|
||||
uint32_t blue
|
||||
)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `strip` LED strip
|
||||
- `index` index of pixel to set
|
||||
- `red` red part of color
|
||||
- `green` green part of color
|
||||
- `blue` blue part of color
|
||||
|
||||
**Returns:**
|
||||
|
||||
- ESP\_OK: Set RGB for a specific pixel successfully
|
||||
- ESP\_ERR\_INVALID\_ARG: Set RGB for a specific pixel failed because of invalid parameters
|
||||
- ESP\_FAIL: Set RGB for a specific pixel failed because other error occurred
|
||||
|
||||
### function `led_strip_set_pixel_hsv`
|
||||
|
||||
_Set HSV for a specific pixel._
|
||||
|
||||
```c
|
||||
esp_err_t led_strip_set_pixel_hsv (
|
||||
led_strip_handle_t strip,
|
||||
uint32_t index,
|
||||
uint16_t hue,
|
||||
uint8_t saturation,
|
||||
uint8_t value
|
||||
)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `strip` LED strip
|
||||
- `index` index of pixel to set
|
||||
- `hue` hue part of color (0 - 360)
|
||||
- `saturation` saturation part of color (0 - 255, rescaled from 0 - 1. e.g. saturation = 0.5, rescaled to 127)
|
||||
- `value` value part of color (0 - 255, rescaled from 0 - 1. e.g. value = 0.5, rescaled to 127)
|
||||
|
||||
**Returns:**
|
||||
|
||||
- ESP\_OK: Set HSV color for a specific pixel successfully
|
||||
- ESP\_ERR\_INVALID\_ARG: Set HSV color for a specific pixel failed because of an invalid argument
|
||||
- ESP\_FAIL: Set HSV color for a specific pixel failed because other error occurred
|
||||
|
||||
### function `led_strip_set_pixel_rgbw`
|
||||
|
||||
_Set RGBW for a specific pixel._
|
||||
|
||||
```c
|
||||
esp_err_t led_strip_set_pixel_rgbw (
|
||||
led_strip_handle_t strip,
|
||||
uint32_t index,
|
||||
uint32_t red,
|
||||
uint32_t green,
|
||||
uint32_t blue,
|
||||
uint32_t white
|
||||
)
|
||||
```
|
||||
|
||||
**Note:**
|
||||
|
||||
Only call this function if your led strip does have the white component (e.g. SK6812-RGBW)
|
||||
|
||||
**Note:**
|
||||
|
||||
Also see `led_strip_set_pixel` if you only want to specify the RGB part of the color and bypass the white component
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `strip` LED strip
|
||||
- `index` index of pixel to set
|
||||
- `red` red part of color
|
||||
- `green` green part of color
|
||||
- `blue` blue part of color
|
||||
- `white` separate white component
|
||||
|
||||
**Returns:**
|
||||
|
||||
- ESP\_OK: Set RGBW color for a specific pixel successfully
|
||||
- ESP\_ERR\_INVALID\_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
|
||||
- ESP\_FAIL: Set RGBW color for a specific pixel failed because other error occurred
|
||||
|
||||
## File include/led_strip_rmt.h
|
||||
|
||||
## Structures and Types
|
||||
|
||||
| Type | Name |
|
||||
| ---: | :--- |
|
||||
| struct | [**led\_strip\_rmt\_config\_t**](#struct-led_strip_rmt_config_t) <br>_LED Strip RMT specific configuration._ |
|
||||
|
||||
## Functions
|
||||
|
||||
| Type | Name |
|
||||
| ---: | :--- |
|
||||
| esp\_err\_t | [**led\_strip\_new\_rmt\_device**](#function-led_strip_new_rmt_device) (const [**led\_strip\_config\_t**](#struct-led_strip_config_t) \*led\_config, const [**led\_strip\_rmt\_config\_t**](#struct-led_strip_rmt_config_t) \*rmt\_config, [**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) \*ret\_strip) <br>_Create LED strip based on RMT TX channel._ |
|
||||
|
||||
## Structures and Types Documentation
|
||||
|
||||
### struct `led_strip_rmt_config_t`
|
||||
|
||||
_LED Strip RMT specific configuration._
|
||||
|
||||
Variables:
|
||||
|
||||
- rmt\_clock\_source\_t clk_src <br>RMT clock source
|
||||
|
||||
- struct led\_strip\_rmt\_config\_t::@0 flags <br>Extra driver flags
|
||||
|
||||
- size\_t mem_block_symbols <br>How many RMT symbols can one RMT channel hold at one time. Set to 0 will fallback to use the default size.
|
||||
|
||||
- uint32\_t resolution_hz <br>RMT tick resolution, if set to zero, a default resolution (10MHz) will be applied
|
||||
|
||||
- uint32\_t with_dma <br>Use DMA to transmit data
|
||||
|
||||
## Functions Documentation
|
||||
|
||||
### function `led_strip_new_rmt_device`
|
||||
|
||||
_Create LED strip based on RMT TX channel._
|
||||
|
||||
```c
|
||||
esp_err_t led_strip_new_rmt_device (
|
||||
const led_strip_config_t *led_config,
|
||||
const led_strip_rmt_config_t *rmt_config,
|
||||
led_strip_handle_t *ret_strip
|
||||
)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `led_config` LED strip configuration
|
||||
- `rmt_config` RMT specific configuration
|
||||
- `ret_strip` Returned LED strip handle
|
||||
|
||||
**Returns:**
|
||||
|
||||
- ESP\_OK: create LED strip handle successfully
|
||||
- ESP\_ERR\_INVALID\_ARG: create LED strip handle failed because of invalid argument
|
||||
- ESP\_ERR\_NO\_MEM: create LED strip handle failed because of out of memory
|
||||
- ESP\_FAIL: create LED strip handle failed because some other error
|
||||
|
||||
## File include/led_strip_spi.h
|
||||
|
||||
## Structures and Types
|
||||
|
||||
| Type | Name |
|
||||
| ---: | :--- |
|
||||
| struct | [**led\_strip\_spi\_config\_t**](#struct-led_strip_spi_config_t) <br>_LED Strip SPI specific configuration._ |
|
||||
|
||||
## Functions
|
||||
|
||||
| Type | Name |
|
||||
| ---: | :--- |
|
||||
| esp\_err\_t | [**led\_strip\_new\_spi\_device**](#function-led_strip_new_spi_device) (const [**led\_strip\_config\_t**](#struct-led_strip_config_t) \*led\_config, const [**led\_strip\_spi\_config\_t**](#struct-led_strip_spi_config_t) \*spi\_config, [**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) \*ret\_strip) <br>_Create LED strip based on SPI MOSI channel._ |
|
||||
|
||||
## Structures and Types Documentation
|
||||
|
||||
### struct `led_strip_spi_config_t`
|
||||
|
||||
_LED Strip SPI specific configuration._
|
||||
|
||||
Variables:
|
||||
|
||||
- spi\_clock\_source\_t clk_src <br>SPI clock source
|
||||
|
||||
- struct led\_strip\_spi\_config\_t::@1 flags <br>Extra driver flags
|
||||
|
||||
- spi\_host\_device\_t spi_bus <br>SPI bus ID. Which buses are available depends on the specific chip
|
||||
|
||||
- uint32\_t with_dma <br>Use DMA to transmit data
|
||||
|
||||
## Functions Documentation
|
||||
|
||||
### function `led_strip_new_spi_device`
|
||||
|
||||
_Create LED strip based on SPI MOSI channel._
|
||||
|
||||
```c
|
||||
esp_err_t led_strip_new_spi_device (
|
||||
const led_strip_config_t *led_config,
|
||||
const led_strip_spi_config_t *spi_config,
|
||||
led_strip_handle_t *ret_strip
|
||||
)
|
||||
```
|
||||
|
||||
**Note:**
|
||||
|
||||
Although only the MOSI line is used for generating the signal, the whole SPI bus can't be used for other purposes.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `led_config` LED strip configuration
|
||||
- `spi_config` SPI specific configuration
|
||||
- `ret_strip` Returned LED strip handle
|
||||
|
||||
**Returns:**
|
||||
|
||||
- ESP\_OK: create LED strip handle successfully
|
||||
- ESP\_ERR\_INVALID\_ARG: create LED strip handle failed because of invalid argument
|
||||
- ESP\_ERR\_NOT\_SUPPORTED: create LED strip handle failed because of unsupported configuration
|
||||
- ESP\_ERR\_NO\_MEM: create LED strip handle failed because of out of memory
|
||||
- ESP\_FAIL: create LED strip handle failed because some other error
|
||||
|
||||
## File include/led_strip_types.h
|
||||
|
||||
## Structures and Types
|
||||
|
||||
| Type | Name |
|
||||
| ---: | :--- |
|
||||
| enum | [**led\_model\_t**](#enum-led_model_t) <br>_LED strip model._ |
|
||||
| enum | [**led\_pixel\_format\_t**](#enum-led_pixel_format_t) <br>_LED strip pixel format._ |
|
||||
| struct | [**led\_strip\_config\_t**](#struct-led_strip_config_t) <br>_LED Strip Configuration._ |
|
||||
| typedef struct [**led\_strip\_t**](#struct-led_strip_t) \* | [**led\_strip\_handle\_t**](#typedef-led_strip_handle_t) <br>_LED strip handle._ |
|
||||
|
||||
## Structures and Types Documentation
|
||||
|
||||
### enum `led_model_t`
|
||||
|
||||
_LED strip model._
|
||||
|
||||
```c
|
||||
enum led_model_t {
|
||||
LED_MODEL_WS2812,
|
||||
LED_MODEL_SK6812,
|
||||
LED_MODEL_INVALID
|
||||
};
|
||||
```
|
||||
|
||||
**Note:**
|
||||
|
||||
Different led model may have different timing parameters, so we need to distinguish them.
|
||||
|
||||
### enum `led_pixel_format_t`
|
||||
|
||||
_LED strip pixel format._
|
||||
|
||||
```c
|
||||
enum led_pixel_format_t {
|
||||
LED_PIXEL_FORMAT_GRB,
|
||||
LED_PIXEL_FORMAT_GRBW,
|
||||
LED_PIXEL_FORMAT_INVALID
|
||||
};
|
||||
```
|
||||
|
||||
### struct `led_strip_config_t`
|
||||
|
||||
_LED Strip Configuration._
|
||||
|
||||
Variables:
|
||||
|
||||
- struct led\_strip\_config\_t::@2 flags <br>Extra driver flags
|
||||
|
||||
- uint32\_t invert_out <br>Invert output signal
|
||||
|
||||
- [**led\_model\_t**](#enum-led_model_t) led_model <br>LED model
|
||||
|
||||
- [**led\_pixel\_format\_t**](#enum-led_pixel_format_t) led_pixel_format <br>LED pixel format
|
||||
|
||||
- uint32\_t max_leds <br>Maximum LEDs in a single strip
|
||||
|
||||
- int strip_gpio_num <br>GPIO number that used by LED strip
|
||||
|
||||
### typedef `led_strip_handle_t`
|
||||
|
||||
_LED strip handle._
|
||||
|
||||
```c
|
||||
typedef struct led_strip_t* led_strip_handle_t;
|
||||
```
|
||||
|
||||
## File interface/led_strip_interface.h
|
||||
|
||||
## Structures and Types
|
||||
|
||||
| Type | Name |
|
||||
| ---: | :--- |
|
||||
| struct | [**led\_strip\_t**](#struct-led_strip_t) <br>_LED strip interface definition._ |
|
||||
| typedef struct [**led\_strip\_t**](#struct-led_strip_t) | [**led\_strip\_t**](#typedef-led_strip_t) <br> |
|
||||
|
||||
## Structures and Types Documentation
|
||||
|
||||
### struct `led_strip_t`
|
||||
|
||||
_LED strip interface definition._
|
||||
|
||||
Variables:
|
||||
|
||||
- esp\_err\_t(\* clear <br>_Clear LED strip (turn off all LEDs)_<br>**Parameters:**
|
||||
|
||||
- `strip` LED strip
|
||||
- `timeout_ms` timeout value for clearing task
|
||||
|
||||
**Returns:**
|
||||
|
||||
- ESP\_OK: Clear LEDs successfully
|
||||
- ESP\_FAIL: Clear LEDs failed because some other error occurred
|
||||
|
||||
- esp\_err\_t(\* del <br>_Free LED strip resources._<br>**Parameters:**
|
||||
|
||||
- `strip` LED strip
|
||||
|
||||
**Returns:**
|
||||
|
||||
- ESP\_OK: Free resources successfully
|
||||
- ESP\_FAIL: Free resources failed because error occurred
|
||||
|
||||
- esp\_err\_t(\* refresh <br>_Refresh memory colors to LEDs._<br>**Parameters:**
|
||||
|
||||
- `strip` LED strip
|
||||
- `timeout_ms` timeout value for refreshing task
|
||||
|
||||
**Returns:**
|
||||
|
||||
- ESP\_OK: Refresh successfully
|
||||
- ESP\_FAIL: Refresh failed because some other error occurred
|
||||
|
||||
**Note:**
|
||||
|
||||
: After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
|
||||
|
||||
- esp\_err\_t(\* set_pixel <br>_Set RGB for a specific pixel._<br>**Parameters:**
|
||||
|
||||
- `strip` LED strip
|
||||
- `index` index of pixel to set
|
||||
- `red` red part of color
|
||||
- `green` green part of color
|
||||
- `blue` blue part of color
|
||||
|
||||
**Returns:**
|
||||
|
||||
- ESP\_OK: Set RGB for a specific pixel successfully
|
||||
- ESP\_ERR\_INVALID\_ARG: Set RGB for a specific pixel failed because of invalid parameters
|
||||
- ESP\_FAIL: Set RGB for a specific pixel failed because other error occurred
|
||||
|
||||
- esp\_err\_t(\* set_pixel_rgbw <br>_Set RGBW for a specific pixel. Similar to_ `set_pixel`_but also set the white component._<br>**Parameters:**
|
||||
|
||||
- `strip` LED strip
|
||||
- `index` index of pixel to set
|
||||
- `red` red part of color
|
||||
- `green` green part of color
|
||||
- `blue` blue part of color
|
||||
- `white` separate white component
|
||||
|
||||
**Returns:**
|
||||
|
||||
- ESP\_OK: Set RGBW color for a specific pixel successfully
|
||||
- ESP\_ERR\_INVALID\_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
|
||||
- ESP\_FAIL: Set RGBW color for a specific pixel failed because other error occurred
|
||||
|
||||
### typedef `led_strip_t`
|
||||
|
||||
```c
|
||||
typedef struct led_strip_t led_strip_t;
|
||||
```
|
||||
|
||||
Type of LED strip
|
||||
@@ -0,0 +1,8 @@
|
||||
# For more information about build system see
|
||||
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(led_strip_rmt_ws2812)
|
||||
@@ -0,0 +1,31 @@
|
||||
# LED Strip Example (RMT backend + WS2812)
|
||||
|
||||
This example demonstrates how to blink the WS2812 LED using the [led_strip](https://components.espressif.com/component/espressif/led_strip) component.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with Espressif SoC
|
||||
* A USB cable for Power supply and programming
|
||||
* WS2812 LED strip
|
||||
|
||||
### Configure the Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`. Then assign the proper GPIO in the [source file](main/led_strip_rmt_ws2812_main.c). If your led strip has multiple LEDs, don't forget update the number.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```text
|
||||
I (299) gpio: GPIO[8]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (309) example: Created LED strip object with RMT backend
|
||||
I (309) example: Start blinking LED strip
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "led_strip_rmt_ws2812_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,5 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/led_strip:
|
||||
version: '^2'
|
||||
override_path: '../../../'
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "led_strip.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
// GPIO assignment
|
||||
#define LED_STRIP_BLINK_GPIO 2
|
||||
// Numbers of the LED in the strip
|
||||
#define LED_STRIP_LED_NUMBERS 24
|
||||
// 10MHz resolution, 1 tick = 0.1us (led strip needs a high resolution)
|
||||
#define LED_STRIP_RMT_RES_HZ (10 * 1000 * 1000)
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
led_strip_handle_t configure_led(void)
|
||||
{
|
||||
// LED strip general initialization, according to your led board design
|
||||
led_strip_config_t strip_config = {
|
||||
.strip_gpio_num = LED_STRIP_BLINK_GPIO, // The GPIO that connected to the LED strip's data line
|
||||
.max_leds = LED_STRIP_LED_NUMBERS, // The number of LEDs in the strip,
|
||||
.led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip
|
||||
.led_model = LED_MODEL_WS2812, // LED strip model
|
||||
.flags.invert_out = false, // whether to invert the output signal
|
||||
};
|
||||
|
||||
// LED strip backend configuration: RMT
|
||||
led_strip_rmt_config_t rmt_config = {
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
.rmt_channel = 0,
|
||||
#else
|
||||
.clk_src = RMT_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||
.resolution_hz = LED_STRIP_RMT_RES_HZ, // RMT counter clock frequency
|
||||
.flags.with_dma = false, // DMA feature is available on ESP target like ESP32-S3
|
||||
#endif
|
||||
};
|
||||
|
||||
// LED Strip object handle
|
||||
led_strip_handle_t led_strip;
|
||||
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
|
||||
ESP_LOGI(TAG, "Created LED strip object with RMT backend");
|
||||
return led_strip;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
led_strip_handle_t led_strip = configure_led();
|
||||
bool led_on_off = false;
|
||||
|
||||
ESP_LOGI(TAG, "Start blinking LED strip");
|
||||
while (1) {
|
||||
if (led_on_off) {
|
||||
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
|
||||
for (int i = 0; i < LED_STRIP_LED_NUMBERS; i++) {
|
||||
ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i, 5, 5, 5));
|
||||
}
|
||||
/* Refresh the strip to send data */
|
||||
ESP_ERROR_CHECK(led_strip_refresh(led_strip));
|
||||
ESP_LOGI(TAG, "LED ON!");
|
||||
} else {
|
||||
/* Set all LED off to clear all pixels */
|
||||
ESP_ERROR_CHECK(led_strip_clear(led_strip));
|
||||
ESP_LOGI(TAG, "LED OFF!");
|
||||
}
|
||||
|
||||
led_on_off = !led_on_off;
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# For more information about build system see
|
||||
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(led_strip_spi_ws2812)
|
||||
@@ -0,0 +1,31 @@
|
||||
# LED Strip Example (SPI backend + WS2812)
|
||||
|
||||
This example demonstrates how to blink the WS2812 LED using the [led_strip](https://components.espressif.com/component/espressif/led_strip) component.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with Espressif SoC
|
||||
* A USB cable for Power supply and programming
|
||||
* WS2812 LED strip
|
||||
|
||||
### Configure the Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`. Then assign the proper GPIO in the [source file](main/led_strip_spi_ws2812_main.c). If your led strip has multiple LEDs, don't forget update the number.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```text
|
||||
I (299) gpio: GPIO[14]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (309) example: Created LED strip object with SPI backend
|
||||
I (309) example: Start blinking LED strip
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "led_strip_spi_ws2812_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,6 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/led_strip:
|
||||
version: '^2.4'
|
||||
override_path: '../../../'
|
||||
idf: ">=5.1"
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "led_strip.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
// GPIO assignment
|
||||
#define LED_STRIP_BLINK_GPIO 2
|
||||
// Numbers of the LED in the strip
|
||||
#define LED_STRIP_LED_NUMBERS 24
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
led_strip_handle_t configure_led(void)
|
||||
{
|
||||
// LED strip general initialization, according to your led board design
|
||||
led_strip_config_t strip_config = {
|
||||
.strip_gpio_num = LED_STRIP_BLINK_GPIO, // The GPIO that connected to the LED strip's data line
|
||||
.max_leds = LED_STRIP_LED_NUMBERS, // The number of LEDs in the strip,
|
||||
.led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip
|
||||
.led_model = LED_MODEL_WS2812, // LED strip model
|
||||
.flags.invert_out = false, // whether to invert the output signal
|
||||
};
|
||||
|
||||
// LED strip backend configuration: SPI
|
||||
led_strip_spi_config_t spi_config = {
|
||||
.clk_src = SPI_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||
.flags.with_dma = true, // Using DMA can improve performance and help drive more LEDs
|
||||
.spi_bus = SPI2_HOST, // SPI bus ID
|
||||
};
|
||||
|
||||
// LED Strip object handle
|
||||
led_strip_handle_t led_strip;
|
||||
ESP_ERROR_CHECK(led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
|
||||
ESP_LOGI(TAG, "Created LED strip object with SPI backend");
|
||||
return led_strip;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
led_strip_handle_t led_strip = configure_led();
|
||||
bool led_on_off = false;
|
||||
|
||||
ESP_LOGI(TAG, "Start blinking LED strip");
|
||||
while (1) {
|
||||
if (led_on_off) {
|
||||
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
|
||||
for (int i = 0; i < LED_STRIP_LED_NUMBERS; i++) {
|
||||
ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i, 5, 5, 5));
|
||||
}
|
||||
/* Refresh the strip to send data */
|
||||
ESP_ERROR_CHECK(led_strip_refresh(led_strip));
|
||||
ESP_LOGI(TAG, "LED ON!");
|
||||
} else {
|
||||
/* Set all LED off to clear all pixels */
|
||||
ESP_ERROR_CHECK(led_strip_clear(led_strip));
|
||||
ESP_LOGI(TAG, "LED OFF!");
|
||||
}
|
||||
|
||||
led_on_off = !led_on_off;
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
dependencies:
|
||||
idf: '>=4.4'
|
||||
description: Driver for Addressable LED Strip (WS2812, etc)
|
||||
repository: git://github.com/espressif/idf-extra-components.git
|
||||
repository_info:
|
||||
commit_sha: 60c14263f3b69ac6e98ecae79beecbe5c18d5596
|
||||
path: led_strip
|
||||
url: https://github.com/espressif/idf-extra-components/tree/master/led_strip
|
||||
version: 2.5.5
|
||||
111
managed_components/espressif__led_strip/include/led_strip.h
Normal file
111
managed_components/espressif__led_strip/include/led_strip.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "led_strip_rmt.h"
|
||||
#include "esp_idf_version.h"
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
#include "led_strip_spi.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Set RGB for a specific pixel
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param red: red part of color
|
||||
* @param green: green part of color
|
||||
* @param blue: blue part of color
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set RGB for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
|
||||
* - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
|
||||
|
||||
/**
|
||||
* @brief Set RGBW for a specific pixel
|
||||
*
|
||||
* @note Only call this function if your led strip does have the white component (e.g. SK6812-RGBW)
|
||||
* @note Also see `led_strip_set_pixel` if you only want to specify the RGB part of the color and bypass the white component
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param red: red part of color
|
||||
* @param green: green part of color
|
||||
* @param blue: blue part of color
|
||||
* @param white: separate white component
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set RGBW color for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
|
||||
* - ESP_FAIL: Set RGBW color for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t led_strip_set_pixel_rgbw(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white);
|
||||
|
||||
/**
|
||||
* @brief Set HSV for a specific pixel
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param hue: hue part of color (0 - 360)
|
||||
* @param saturation: saturation part of color (0 - 255, rescaled from 0 - 1. e.g. saturation = 0.5, rescaled to 127)
|
||||
* @param value: value part of color (0 - 255, rescaled from 0 - 1. e.g. value = 0.5, rescaled to 127)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set HSV color for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set HSV color for a specific pixel failed because of an invalid argument
|
||||
* - ESP_FAIL: Set HSV color for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint8_t saturation, uint8_t value);
|
||||
|
||||
/**
|
||||
* @brief Refresh memory colors to LEDs
|
||||
*
|
||||
* @param strip: LED strip
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Refresh successfully
|
||||
* - ESP_FAIL: Refresh failed because some other error occurred
|
||||
*
|
||||
* @note:
|
||||
* After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
|
||||
*/
|
||||
esp_err_t led_strip_refresh(led_strip_handle_t strip);
|
||||
|
||||
/**
|
||||
* @brief Clear LED strip (turn off all LEDs)
|
||||
*
|
||||
* @param strip: LED strip
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Clear LEDs successfully
|
||||
* - ESP_FAIL: Clear LEDs failed because some other error occurred
|
||||
*/
|
||||
esp_err_t led_strip_clear(led_strip_handle_t strip);
|
||||
|
||||
/**
|
||||
* @brief Free LED strip resources
|
||||
*
|
||||
* @param strip: LED strip
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Free resources successfully
|
||||
* - ESP_FAIL: Free resources failed because error occurred
|
||||
*/
|
||||
esp_err_t led_strip_del(led_strip_handle_t strip);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "led_strip_types.h"
|
||||
#include "esp_idf_version.h"
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#include "driver/rmt_types.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LED Strip RMT specific configuration
|
||||
*/
|
||||
typedef struct {
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
uint8_t rmt_channel; /*!< Specify the channel number, the legacy RMT driver doesn't support channel allocator */
|
||||
#else // new driver supports specify the clock source and clock resolution
|
||||
rmt_clock_source_t clk_src; /*!< RMT clock source */
|
||||
uint32_t resolution_hz; /*!< RMT tick resolution, if set to zero, a default resolution (10MHz) will be applied */
|
||||
#endif
|
||||
size_t mem_block_symbols; /*!< How many RMT symbols can one RMT channel hold at one time. Set to 0 will fallback to use the default size. */
|
||||
struct {
|
||||
uint32_t with_dma: 1; /*!< Use DMA to transmit data */
|
||||
} flags; /*!< Extra driver flags */
|
||||
} led_strip_rmt_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create LED strip based on RMT TX channel
|
||||
*
|
||||
* @param led_config LED strip configuration
|
||||
* @param rmt_config RMT specific configuration
|
||||
* @param ret_strip Returned LED strip handle
|
||||
* @return
|
||||
* - ESP_OK: create LED strip handle successfully
|
||||
* - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory
|
||||
* - ESP_FAIL: create LED strip handle failed because some other error
|
||||
*/
|
||||
esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "led_strip_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LED Strip SPI specific configuration
|
||||
*/
|
||||
typedef struct {
|
||||
spi_clock_source_t clk_src; /*!< SPI clock source */
|
||||
spi_host_device_t spi_bus; /*!< SPI bus ID. Which buses are available depends on the specific chip */
|
||||
struct {
|
||||
uint32_t with_dma: 1; /*!< Use DMA to transmit data */
|
||||
} flags; /*!< Extra driver flags */
|
||||
} led_strip_spi_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create LED strip based on SPI MOSI channel
|
||||
* @note Although only the MOSI line is used for generating the signal, the whole SPI bus can't be used for other purposes.
|
||||
*
|
||||
* @param led_config LED strip configuration
|
||||
* @param spi_config SPI specific configuration
|
||||
* @param ret_strip Returned LED strip handle
|
||||
* @return
|
||||
* - ESP_OK: create LED strip handle successfully
|
||||
* - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument
|
||||
* - ESP_ERR_NOT_SUPPORTED: create LED strip handle failed because of unsupported configuration
|
||||
* - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory
|
||||
* - ESP_FAIL: create LED strip handle failed because some other error
|
||||
*/
|
||||
esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const led_strip_spi_config_t *spi_config, led_strip_handle_t *ret_strip);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LED strip pixel format
|
||||
*/
|
||||
typedef enum {
|
||||
LED_PIXEL_FORMAT_GRB, /*!< Pixel format: GRB */
|
||||
LED_PIXEL_FORMAT_GRBW, /*!< Pixel format: GRBW */
|
||||
LED_PIXEL_FORMAT_INVALID /*!< Invalid pixel format */
|
||||
} led_pixel_format_t;
|
||||
|
||||
/**
|
||||
* @brief LED strip model
|
||||
* @note Different led model may have different timing parameters, so we need to distinguish them.
|
||||
*/
|
||||
typedef enum {
|
||||
LED_MODEL_WS2812, /*!< LED strip model: WS2812 */
|
||||
LED_MODEL_SK6812, /*!< LED strip model: SK6812 */
|
||||
LED_MODEL_INVALID /*!< Invalid LED strip model */
|
||||
} led_model_t;
|
||||
|
||||
/**
|
||||
* @brief LED strip handle
|
||||
*/
|
||||
typedef struct led_strip_t *led_strip_handle_t;
|
||||
|
||||
/**
|
||||
* @brief LED Strip Configuration
|
||||
*/
|
||||
typedef struct {
|
||||
int strip_gpio_num; /*!< GPIO number that used by LED strip */
|
||||
uint32_t max_leds; /*!< Maximum LEDs in a single strip */
|
||||
led_pixel_format_t led_pixel_format; /*!< LED pixel format */
|
||||
led_model_t led_model; /*!< LED model */
|
||||
|
||||
struct {
|
||||
uint32_t invert_out: 1; /*!< Invert output signal */
|
||||
} flags; /*!< Extra driver flags */
|
||||
} led_strip_config_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct led_strip_t led_strip_t; /*!< Type of LED strip */
|
||||
|
||||
/**
|
||||
* @brief LED strip interface definition
|
||||
*/
|
||||
struct led_strip_t {
|
||||
/**
|
||||
* @brief Set RGB for a specific pixel
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param red: red part of color
|
||||
* @param green: green part of color
|
||||
* @param blue: blue part of color
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set RGB for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
|
||||
* - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t (*set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
|
||||
|
||||
/**
|
||||
* @brief Set RGBW for a specific pixel. Similar to `set_pixel` but also set the white component
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param red: red part of color
|
||||
* @param green: green part of color
|
||||
* @param blue: blue part of color
|
||||
* @param white: separate white component
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set RGBW color for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
|
||||
* - ESP_FAIL: Set RGBW color for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t (*set_pixel_rgbw)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white);
|
||||
|
||||
/**
|
||||
* @brief Refresh memory colors to LEDs
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param timeout_ms: timeout value for refreshing task
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Refresh successfully
|
||||
* - ESP_FAIL: Refresh failed because some other error occurred
|
||||
*
|
||||
* @note:
|
||||
* After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
|
||||
*/
|
||||
esp_err_t (*refresh)(led_strip_t *strip);
|
||||
|
||||
/**
|
||||
* @brief Clear LED strip (turn off all LEDs)
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param timeout_ms: timeout value for clearing task
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Clear LEDs successfully
|
||||
* - ESP_FAIL: Clear LEDs failed because some other error occurred
|
||||
*/
|
||||
esp_err_t (*clear)(led_strip_t *strip);
|
||||
|
||||
/**
|
||||
* @brief Free LED strip resources
|
||||
*
|
||||
* @param strip: LED strip
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Free resources successfully
|
||||
* - ESP_FAIL: Free resources failed because error occurred
|
||||
*/
|
||||
esp_err_t (*del)(led_strip_t *strip);
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
94
managed_components/espressif__led_strip/src/led_strip_api.c
Normal file
94
managed_components/espressif__led_strip/src/led_strip_api.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "led_strip.h"
|
||||
#include "led_strip_interface.h"
|
||||
|
||||
static const char *TAG = "led_strip";
|
||||
|
||||
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->set_pixel(strip, index, red, green, blue);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint8_t saturation, uint8_t value)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
uint32_t red = 0;
|
||||
uint32_t green = 0;
|
||||
uint32_t blue = 0;
|
||||
|
||||
uint32_t rgb_max = value;
|
||||
uint32_t rgb_min = rgb_max * (255 - saturation) / 255.0f;
|
||||
|
||||
uint32_t i = hue / 60;
|
||||
uint32_t diff = hue % 60;
|
||||
|
||||
// RGB adjustment amount by hue
|
||||
uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
red = rgb_max;
|
||||
green = rgb_min + rgb_adj;
|
||||
blue = rgb_min;
|
||||
break;
|
||||
case 1:
|
||||
red = rgb_max - rgb_adj;
|
||||
green = rgb_max;
|
||||
blue = rgb_min;
|
||||
break;
|
||||
case 2:
|
||||
red = rgb_min;
|
||||
green = rgb_max;
|
||||
blue = rgb_min + rgb_adj;
|
||||
break;
|
||||
case 3:
|
||||
red = rgb_min;
|
||||
green = rgb_max - rgb_adj;
|
||||
blue = rgb_max;
|
||||
break;
|
||||
case 4:
|
||||
red = rgb_min + rgb_adj;
|
||||
green = rgb_min;
|
||||
blue = rgb_max;
|
||||
break;
|
||||
default:
|
||||
red = rgb_max;
|
||||
green = rgb_min;
|
||||
blue = rgb_max - rgb_adj;
|
||||
break;
|
||||
}
|
||||
|
||||
return strip->set_pixel(strip, index, red, green, blue);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_set_pixel_rgbw(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->set_pixel_rgbw(strip, index, red, green, blue, white);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_refresh(led_strip_handle_t strip)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->refresh(strip);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_clear(led_strip_handle_t strip)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->clear(strip);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_del(led_strip_handle_t strip)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->del(strip);
|
||||
}
|
||||
164
managed_components/espressif__led_strip/src/led_strip_rmt_dev.c
Normal file
164
managed_components/espressif__led_strip/src/led_strip_rmt_dev.c
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/rmt_tx.h"
|
||||
#include "led_strip.h"
|
||||
#include "led_strip_interface.h"
|
||||
#include "led_strip_rmt_encoder.h"
|
||||
|
||||
#define LED_STRIP_RMT_DEFAULT_RESOLUTION 10000000 // 10MHz resolution
|
||||
#define LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE 4
|
||||
// the memory size of each RMT channel, in words (4 bytes)
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 64
|
||||
#else
|
||||
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 48
|
||||
#endif
|
||||
|
||||
static const char *TAG = "led_strip_rmt";
|
||||
|
||||
typedef struct {
|
||||
led_strip_t base;
|
||||
rmt_channel_handle_t rmt_chan;
|
||||
rmt_encoder_handle_t strip_encoder;
|
||||
uint32_t strip_len;
|
||||
uint8_t bytes_per_pixel;
|
||||
uint8_t pixel_buf[];
|
||||
} led_strip_rmt_obj;
|
||||
|
||||
static esp_err_t led_strip_rmt_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||
uint32_t start = index * rmt_strip->bytes_per_pixel;
|
||||
// In thr order of GRB, as LED strip like WS2812 sends out pixels in this order
|
||||
rmt_strip->pixel_buf[start + 0] = green & 0xFF;
|
||||
rmt_strip->pixel_buf[start + 1] = red & 0xFF;
|
||||
rmt_strip->pixel_buf[start + 2] = blue & 0xFF;
|
||||
if (rmt_strip->bytes_per_pixel > 3) {
|
||||
rmt_strip->pixel_buf[start + 3] = 0;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||
ESP_RETURN_ON_FALSE(rmt_strip->bytes_per_pixel == 4, ESP_ERR_INVALID_ARG, TAG, "wrong LED pixel format, expected 4 bytes per pixel");
|
||||
uint8_t *buf_start = rmt_strip->pixel_buf + index * 4;
|
||||
// SK6812 component order is GRBW
|
||||
*buf_start = green & 0xFF;
|
||||
*++buf_start = red & 0xFF;
|
||||
*++buf_start = blue & 0xFF;
|
||||
*++buf_start = white & 0xFF;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_refresh(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
rmt_transmit_config_t tx_conf = {
|
||||
.loop_count = 0,
|
||||
};
|
||||
|
||||
ESP_RETURN_ON_ERROR(rmt_enable(rmt_strip->rmt_chan), TAG, "enable RMT channel failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_transmit(rmt_strip->rmt_chan, rmt_strip->strip_encoder, rmt_strip->pixel_buf,
|
||||
rmt_strip->strip_len * rmt_strip->bytes_per_pixel, &tx_conf), TAG, "transmit pixels by RMT failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(rmt_strip->rmt_chan, -1), TAG, "flush RMT channel failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_disable(rmt_strip->rmt_chan), TAG, "disable RMT channel failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_clear(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
// Write zero to turn off all leds
|
||||
memset(rmt_strip->pixel_buf, 0, rmt_strip->strip_len * rmt_strip->bytes_per_pixel);
|
||||
return led_strip_rmt_refresh(strip);
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_del(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_ERROR(rmt_del_channel(rmt_strip->rmt_chan), TAG, "delete RMT channel failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_del_encoder(rmt_strip->strip_encoder), TAG, "delete strip encoder failed");
|
||||
free(rmt_strip);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = NULL;
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE(led_config && rmt_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led_pixel_format");
|
||||
uint8_t bytes_per_pixel = 3;
|
||||
if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) {
|
||||
bytes_per_pixel = 4;
|
||||
} else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) {
|
||||
bytes_per_pixel = 3;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
rmt_strip = calloc(1, sizeof(led_strip_rmt_obj) + led_config->max_leds * bytes_per_pixel);
|
||||
ESP_GOTO_ON_FALSE(rmt_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for rmt strip");
|
||||
uint32_t resolution = rmt_config->resolution_hz ? rmt_config->resolution_hz : LED_STRIP_RMT_DEFAULT_RESOLUTION;
|
||||
|
||||
// for backward compatibility, if the user does not set the clk_src, use the default value
|
||||
rmt_clock_source_t clk_src = RMT_CLK_SRC_DEFAULT;
|
||||
if (rmt_config->clk_src) {
|
||||
clk_src = rmt_config->clk_src;
|
||||
}
|
||||
size_t mem_block_symbols = LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS;
|
||||
// override the default value if the user sets it
|
||||
if (rmt_config->mem_block_symbols) {
|
||||
mem_block_symbols = rmt_config->mem_block_symbols;
|
||||
}
|
||||
rmt_tx_channel_config_t rmt_chan_config = {
|
||||
.clk_src = clk_src,
|
||||
.gpio_num = led_config->strip_gpio_num,
|
||||
.mem_block_symbols = mem_block_symbols,
|
||||
.resolution_hz = resolution,
|
||||
.trans_queue_depth = LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE,
|
||||
.flags.with_dma = rmt_config->flags.with_dma,
|
||||
.flags.invert_out = led_config->flags.invert_out,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_tx_channel(&rmt_chan_config, &rmt_strip->rmt_chan), err, TAG, "create RMT TX channel failed");
|
||||
|
||||
led_strip_encoder_config_t strip_encoder_conf = {
|
||||
.resolution = resolution,
|
||||
.led_model = led_config->led_model
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_led_strip_encoder(&strip_encoder_conf, &rmt_strip->strip_encoder), err, TAG, "create LED strip encoder failed");
|
||||
|
||||
|
||||
rmt_strip->bytes_per_pixel = bytes_per_pixel;
|
||||
rmt_strip->strip_len = led_config->max_leds;
|
||||
rmt_strip->base.set_pixel = led_strip_rmt_set_pixel;
|
||||
rmt_strip->base.set_pixel_rgbw = led_strip_rmt_set_pixel_rgbw;
|
||||
rmt_strip->base.refresh = led_strip_rmt_refresh;
|
||||
rmt_strip->base.clear = led_strip_rmt_clear;
|
||||
rmt_strip->base.del = led_strip_rmt_del;
|
||||
|
||||
*ret_strip = &rmt_strip->base;
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (rmt_strip) {
|
||||
if (rmt_strip->rmt_chan) {
|
||||
rmt_del_channel(rmt_strip->rmt_chan);
|
||||
}
|
||||
if (rmt_strip->strip_encoder) {
|
||||
rmt_del_encoder(rmt_strip->strip_encoder);
|
||||
}
|
||||
free(rmt_strip);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/rmt.h"
|
||||
#include "led_strip.h"
|
||||
#include "led_strip_interface.h"
|
||||
|
||||
static const char *TAG = "led_strip_rmt";
|
||||
|
||||
#define WS2812_T0H_NS (300)
|
||||
#define WS2812_T0L_NS (900)
|
||||
#define WS2812_T1H_NS (900)
|
||||
#define WS2812_T1L_NS (300)
|
||||
|
||||
#define SK6812_T0H_NS (300)
|
||||
#define SK6812_T0L_NS (900)
|
||||
#define SK6812_T1H_NS (600)
|
||||
#define SK6812_T1L_NS (600)
|
||||
|
||||
#define LED_STRIP_RESET_MS (10)
|
||||
|
||||
// the memory size of each RMT channel, in words (4 bytes)
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 64
|
||||
#else
|
||||
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 48
|
||||
#endif
|
||||
|
||||
static uint32_t led_t0h_ticks = 0;
|
||||
static uint32_t led_t1h_ticks = 0;
|
||||
static uint32_t led_t0l_ticks = 0;
|
||||
static uint32_t led_t1l_ticks = 0;
|
||||
|
||||
typedef struct {
|
||||
led_strip_t base;
|
||||
rmt_channel_t rmt_channel;
|
||||
uint32_t strip_len;
|
||||
uint8_t bytes_per_pixel;
|
||||
uint8_t buffer[0];
|
||||
} led_strip_rmt_obj;
|
||||
|
||||
static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
|
||||
size_t wanted_num, size_t *translated_size, size_t *item_num)
|
||||
{
|
||||
if (src == NULL || dest == NULL) {
|
||||
*translated_size = 0;
|
||||
*item_num = 0;
|
||||
return;
|
||||
}
|
||||
const rmt_item32_t bit0 = {{{ led_t0h_ticks, 1, led_t0l_ticks, 0 }}}; //Logical 0
|
||||
const rmt_item32_t bit1 = {{{ led_t1h_ticks, 1, led_t1l_ticks, 0 }}}; //Logical 1
|
||||
size_t size = 0;
|
||||
size_t num = 0;
|
||||
uint8_t *psrc = (uint8_t *)src;
|
||||
rmt_item32_t *pdest = dest;
|
||||
while (size < src_size && num < wanted_num) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
// MSB first
|
||||
if (*psrc & (1 << (7 - i))) {
|
||||
pdest->val = bit1.val;
|
||||
} else {
|
||||
pdest->val = bit0.val;
|
||||
}
|
||||
num++;
|
||||
pdest++;
|
||||
}
|
||||
size++;
|
||||
psrc++;
|
||||
}
|
||||
*translated_size = size;
|
||||
*item_num = num;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of the maximum number of leds");
|
||||
uint32_t start = index * rmt_strip->bytes_per_pixel;
|
||||
// In thr order of GRB
|
||||
rmt_strip->buffer[start + 0] = green & 0xFF;
|
||||
rmt_strip->buffer[start + 1] = red & 0xFF;
|
||||
rmt_strip->buffer[start + 2] = blue & 0xFF;
|
||||
if (rmt_strip->bytes_per_pixel > 3) {
|
||||
rmt_strip->buffer[start + 3] = 0;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_refresh(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_ERROR(rmt_write_sample(rmt_strip->rmt_channel, rmt_strip->buffer, rmt_strip->strip_len * rmt_strip->bytes_per_pixel, true), TAG,
|
||||
"transmit RMT samples failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(LED_STRIP_RESET_MS));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_clear(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
// Write zero to turn off all LEDs
|
||||
memset(rmt_strip->buffer, 0, rmt_strip->strip_len * rmt_strip->bytes_per_pixel);
|
||||
return led_strip_rmt_refresh(strip);
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_del(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_ERROR(rmt_driver_uninstall(rmt_strip->rmt_channel), TAG, "uninstall RMT driver failed");
|
||||
free(rmt_strip);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *dev_config, led_strip_handle_t *ret_strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = NULL;
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(led_config && dev_config && ret_strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, TAG, "invalid led_pixel_format");
|
||||
ESP_RETURN_ON_FALSE(dev_config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, TAG, "DMA is not supported");
|
||||
|
||||
uint8_t bytes_per_pixel = 3;
|
||||
if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) {
|
||||
bytes_per_pixel = 4;
|
||||
} else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) {
|
||||
bytes_per_pixel = 3;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// allocate memory for led_strip object
|
||||
rmt_strip = calloc(1, sizeof(led_strip_rmt_obj) + led_config->max_leds * bytes_per_pixel);
|
||||
ESP_RETURN_ON_FALSE(rmt_strip, ESP_ERR_NO_MEM, TAG, "request memory for les_strip failed");
|
||||
|
||||
// install RMT channel driver
|
||||
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(led_config->strip_gpio_num, dev_config->rmt_channel);
|
||||
// set the minimal clock division because the LED strip needs a high clock resolution
|
||||
config.clk_div = 2;
|
||||
|
||||
uint8_t mem_block_num = 2;
|
||||
// override the default value if the user specify the mem block size
|
||||
if (dev_config->mem_block_symbols) {
|
||||
mem_block_num = (dev_config->mem_block_symbols + LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS / 2) / LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS;
|
||||
}
|
||||
config.mem_block_num = mem_block_num;
|
||||
|
||||
ESP_GOTO_ON_ERROR(rmt_config(&config), err, TAG, "RMT config failed");
|
||||
ESP_GOTO_ON_ERROR(rmt_driver_install(config.channel, 0, 0), err, TAG, "RMT install failed");
|
||||
|
||||
uint32_t counter_clk_hz = 0;
|
||||
rmt_get_counter_clock((rmt_channel_t)dev_config->rmt_channel, &counter_clk_hz);
|
||||
// ns -> ticks
|
||||
float ratio = (float)counter_clk_hz / 1e9;
|
||||
if (led_config->led_model == LED_MODEL_WS2812) {
|
||||
led_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
|
||||
led_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
|
||||
led_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
|
||||
led_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);
|
||||
} else if (led_config->led_model == LED_MODEL_SK6812) {
|
||||
led_t0h_ticks = (uint32_t)(ratio * SK6812_T0H_NS);
|
||||
led_t0l_ticks = (uint32_t)(ratio * SK6812_T0L_NS);
|
||||
led_t1h_ticks = (uint32_t)(ratio * SK6812_T1H_NS);
|
||||
led_t1l_ticks = (uint32_t)(ratio * SK6812_T1L_NS);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// adapter to translates the LES strip date frame into RMT symbols
|
||||
rmt_translator_init((rmt_channel_t)dev_config->rmt_channel, ws2812_rmt_adapter);
|
||||
|
||||
rmt_strip->bytes_per_pixel = bytes_per_pixel;
|
||||
rmt_strip->rmt_channel = (rmt_channel_t)dev_config->rmt_channel;
|
||||
rmt_strip->strip_len = led_config->max_leds;
|
||||
rmt_strip->base.set_pixel = led_strip_rmt_set_pixel;
|
||||
rmt_strip->base.refresh = led_strip_rmt_refresh;
|
||||
rmt_strip->base.clear = led_strip_rmt_clear;
|
||||
rmt_strip->base.del = led_strip_rmt_del;
|
||||
|
||||
*ret_strip = &rmt_strip->base;
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (rmt_strip) {
|
||||
free(rmt_strip);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "led_strip_rmt_encoder.h"
|
||||
|
||||
static const char *TAG = "led_rmt_encoder";
|
||||
|
||||
typedef struct {
|
||||
rmt_encoder_t base;
|
||||
rmt_encoder_t *bytes_encoder;
|
||||
rmt_encoder_t *copy_encoder;
|
||||
int state;
|
||||
rmt_symbol_word_t reset_code;
|
||||
} rmt_led_strip_encoder_t;
|
||||
|
||||
static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
|
||||
{
|
||||
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||
rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder;
|
||||
rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder;
|
||||
rmt_encode_state_t session_state = 0;
|
||||
rmt_encode_state_t state = 0;
|
||||
size_t encoded_symbols = 0;
|
||||
switch (led_encoder->state) {
|
||||
case 0: // send RGB data
|
||||
encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state);
|
||||
if (session_state & RMT_ENCODING_COMPLETE) {
|
||||
led_encoder->state = 1; // switch to next state when current encoding session finished
|
||||
}
|
||||
if (session_state & RMT_ENCODING_MEM_FULL) {
|
||||
state |= RMT_ENCODING_MEM_FULL;
|
||||
goto out; // yield if there's no free space for encoding artifacts
|
||||
}
|
||||
// fall-through
|
||||
case 1: // send reset code
|
||||
encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code,
|
||||
sizeof(led_encoder->reset_code), &session_state);
|
||||
if (session_state & RMT_ENCODING_COMPLETE) {
|
||||
led_encoder->state = 0; // back to the initial encoding session
|
||||
state |= RMT_ENCODING_COMPLETE;
|
||||
}
|
||||
if (session_state & RMT_ENCODING_MEM_FULL) {
|
||||
state |= RMT_ENCODING_MEM_FULL;
|
||||
goto out; // yield if there's no free space for encoding artifacts
|
||||
}
|
||||
}
|
||||
out:
|
||||
*ret_state = state;
|
||||
return encoded_symbols;
|
||||
}
|
||||
|
||||
static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder)
|
||||
{
|
||||
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||
rmt_del_encoder(led_encoder->bytes_encoder);
|
||||
rmt_del_encoder(led_encoder->copy_encoder);
|
||||
free(led_encoder);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder)
|
||||
{
|
||||
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||
rmt_encoder_reset(led_encoder->bytes_encoder);
|
||||
rmt_encoder_reset(led_encoder->copy_encoder);
|
||||
led_encoder->state = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
rmt_led_strip_encoder_t *led_encoder = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(config->led_model < LED_MODEL_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led model");
|
||||
led_encoder = calloc(1, sizeof(rmt_led_strip_encoder_t));
|
||||
ESP_GOTO_ON_FALSE(led_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for led strip encoder");
|
||||
led_encoder->base.encode = rmt_encode_led_strip;
|
||||
led_encoder->base.del = rmt_del_led_strip_encoder;
|
||||
led_encoder->base.reset = rmt_led_strip_encoder_reset;
|
||||
rmt_bytes_encoder_config_t bytes_encoder_config;
|
||||
if (config->led_model == LED_MODEL_SK6812) {
|
||||
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
|
||||
.bit0 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us
|
||||
},
|
||||
.bit1 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.6 * config->resolution / 1000000, // T1H=0.6us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.6 * config->resolution / 1000000, // T1L=0.6us
|
||||
},
|
||||
.flags.msb_first = 1 // SK6812 transfer bit order: G7...G0R7...R0B7...B0(W7...W0)
|
||||
};
|
||||
} else if (config->led_model == LED_MODEL_WS2812) {
|
||||
// different led strip might have its own timing requirements, following parameter is for WS2812
|
||||
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
|
||||
.bit0 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us
|
||||
},
|
||||
.bit1 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.9 * config->resolution / 1000000, // T1H=0.9us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.3 * config->resolution / 1000000, // T1L=0.3us
|
||||
},
|
||||
.flags.msb_first = 1 // WS2812 transfer bit order: G7...G0R7...R0B7...B0
|
||||
};
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), err, TAG, "create bytes encoder failed");
|
||||
rmt_copy_encoder_config_t copy_encoder_config = {};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(©_encoder_config, &led_encoder->copy_encoder), err, TAG, "create copy encoder failed");
|
||||
|
||||
uint32_t reset_ticks = config->resolution / 1000000 * 280 / 2; // reset code duration defaults to 280us to accomodate WS2812B-V5
|
||||
led_encoder->reset_code = (rmt_symbol_word_t) {
|
||||
.level0 = 0,
|
||||
.duration0 = reset_ticks,
|
||||
.level1 = 0,
|
||||
.duration1 = reset_ticks,
|
||||
};
|
||||
*ret_encoder = &led_encoder->base;
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (led_encoder) {
|
||||
if (led_encoder->bytes_encoder) {
|
||||
rmt_del_encoder(led_encoder->bytes_encoder);
|
||||
}
|
||||
if (led_encoder->copy_encoder) {
|
||||
rmt_del_encoder(led_encoder->copy_encoder);
|
||||
}
|
||||
free(led_encoder);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "driver/rmt_encoder.h"
|
||||
#include "led_strip_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Type of led strip encoder configuration
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t resolution; /*!< Encoder resolution, in Hz */
|
||||
led_model_t led_model; /*!< LED model */
|
||||
} led_strip_encoder_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create RMT encoder for encoding LED strip pixels into RMT symbols
|
||||
*
|
||||
* @param[in] config Encoder configuration
|
||||
* @param[out] ret_encoder Returned encoder handle
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG for any invalid arguments
|
||||
* - ESP_ERR_NO_MEM out of memory when creating led strip encoder
|
||||
* - ESP_OK if creating encoder successfully
|
||||
*/
|
||||
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
211
managed_components/espressif__led_strip/src/led_strip_spi_dev.c
Normal file
211
managed_components/espressif__led_strip/src/led_strip_spi_dev.c
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "soc/spi_periph.h"
|
||||
#include "led_strip.h"
|
||||
#include "led_strip_interface.h"
|
||||
#include "hal/spi_hal.h"
|
||||
|
||||
#define LED_STRIP_SPI_DEFAULT_RESOLUTION (2.5 * 1000 * 1000) // 2.5MHz resolution
|
||||
#define LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE 4
|
||||
|
||||
#define SPI_BYTES_PER_COLOR_BYTE 3
|
||||
#define SPI_BITS_PER_COLOR_BYTE (SPI_BYTES_PER_COLOR_BYTE * 8)
|
||||
|
||||
static const char *TAG = "led_strip_spi";
|
||||
|
||||
typedef struct {
|
||||
led_strip_t base;
|
||||
spi_host_device_t spi_host;
|
||||
spi_device_handle_t spi_device;
|
||||
uint32_t strip_len;
|
||||
uint8_t bytes_per_pixel;
|
||||
uint8_t pixel_buf[];
|
||||
} led_strip_spi_obj;
|
||||
|
||||
// please make sure to zero-initialize the buf before calling this function
|
||||
static void __led_strip_spi_bit(uint8_t data, uint8_t *buf)
|
||||
{
|
||||
// Each color of 1 bit is represented by 3 bits of SPI, low_level:100 ,high_level:110
|
||||
// So a color byte occupies 3 bytes of SPI.
|
||||
*(buf + 2) |= data & BIT(0) ? BIT(2) | BIT(1) : BIT(2);
|
||||
*(buf + 2) |= data & BIT(1) ? BIT(5) | BIT(4) : BIT(5);
|
||||
*(buf + 2) |= data & BIT(2) ? BIT(7) : 0x00;
|
||||
*(buf + 1) |= BIT(0);
|
||||
*(buf + 1) |= data & BIT(3) ? BIT(3) | BIT(2) : BIT(3);
|
||||
*(buf + 1) |= data & BIT(4) ? BIT(6) | BIT(5) : BIT(6);
|
||||
*(buf + 0) |= data & BIT(5) ? BIT(1) | BIT(0) : BIT(1);
|
||||
*(buf + 0) |= data & BIT(6) ? BIT(4) | BIT(3) : BIT(4);
|
||||
*(buf + 0) |= data & BIT(7) ? BIT(7) | BIT(6) : BIT(7);
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||
// LED_PIXEL_FORMAT_GRB takes 72bits(9bytes)
|
||||
uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
|
||||
memset(spi_strip->pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||
__led_strip_spi_bit(green, &spi_strip->pixel_buf[start]);
|
||||
__led_strip_spi_bit(red, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE]);
|
||||
__led_strip_spi_bit(blue, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 2]);
|
||||
if (spi_strip->bytes_per_pixel > 3) {
|
||||
__led_strip_spi_bit(0, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 3]);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||
ESP_RETURN_ON_FALSE(spi_strip->bytes_per_pixel == 4, ESP_ERR_INVALID_ARG, TAG, "wrong LED pixel format, expected 4 bytes per pixel");
|
||||
// LED_PIXEL_FORMAT_GRBW takes 96bits(12bytes)
|
||||
uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
|
||||
// SK6812 component order is GRBW
|
||||
memset(spi_strip->pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||
__led_strip_spi_bit(green, &spi_strip->pixel_buf[start]);
|
||||
__led_strip_spi_bit(red, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE]);
|
||||
__led_strip_spi_bit(blue, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 2]);
|
||||
__led_strip_spi_bit(white, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 3]);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_refresh(led_strip_t *strip)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
spi_transaction_t tx_conf;
|
||||
memset(&tx_conf, 0, sizeof(tx_conf));
|
||||
|
||||
tx_conf.length = spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BITS_PER_COLOR_BYTE;
|
||||
tx_conf.tx_buffer = spi_strip->pixel_buf;
|
||||
tx_conf.rx_buffer = NULL;
|
||||
ESP_RETURN_ON_ERROR(spi_device_transmit(spi_strip->spi_device, &tx_conf), TAG, "transmit pixels by SPI failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_clear(led_strip_t *strip)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
//Write zero to turn off all leds
|
||||
memset(spi_strip->pixel_buf, 0, spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||
uint8_t *buf = spi_strip->pixel_buf;
|
||||
for (int index = 0; index < spi_strip->strip_len * spi_strip->bytes_per_pixel; index++) {
|
||||
__led_strip_spi_bit(0, buf);
|
||||
buf += SPI_BYTES_PER_COLOR_BYTE;
|
||||
}
|
||||
|
||||
return led_strip_spi_refresh(strip);
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_del(led_strip_t *strip)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
|
||||
ESP_RETURN_ON_ERROR(spi_bus_remove_device(spi_strip->spi_device), TAG, "delete spi device failed");
|
||||
ESP_RETURN_ON_ERROR(spi_bus_free(spi_strip->spi_host), TAG, "free spi bus failed");
|
||||
|
||||
free(spi_strip);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const led_strip_spi_config_t *spi_config, led_strip_handle_t *ret_strip)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = NULL;
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE(led_config && spi_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led_pixel_format");
|
||||
uint8_t bytes_per_pixel = 3;
|
||||
if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) {
|
||||
bytes_per_pixel = 4;
|
||||
} else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) {
|
||||
bytes_per_pixel = 3;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
uint32_t mem_caps = MALLOC_CAP_DEFAULT;
|
||||
if (spi_config->flags.with_dma) {
|
||||
// DMA buffer must be placed in internal SRAM
|
||||
mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA;
|
||||
}
|
||||
spi_strip = heap_caps_calloc(1, sizeof(led_strip_spi_obj) + led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE, mem_caps);
|
||||
|
||||
ESP_GOTO_ON_FALSE(spi_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for spi strip");
|
||||
|
||||
spi_strip->spi_host = spi_config->spi_bus;
|
||||
// for backward compatibility, if the user does not set the clk_src, use the default value
|
||||
spi_clock_source_t clk_src = SPI_CLK_SRC_DEFAULT;
|
||||
if (spi_config->clk_src) {
|
||||
clk_src = spi_config->clk_src;
|
||||
}
|
||||
|
||||
spi_bus_config_t spi_bus_cfg = {
|
||||
.mosi_io_num = led_config->strip_gpio_num,
|
||||
//Only use MOSI to generate the signal, set -1 when other pins are not used.
|
||||
.miso_io_num = -1,
|
||||
.sclk_io_num = -1,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.max_transfer_sz = led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(spi_bus_initialize(spi_strip->spi_host, &spi_bus_cfg, spi_config->flags.with_dma ? SPI_DMA_CH_AUTO : SPI_DMA_DISABLED), err, TAG, "create SPI bus failed");
|
||||
|
||||
if (led_config->flags.invert_out == true) {
|
||||
esp_rom_gpio_connect_out_signal(led_config->strip_gpio_num, spi_periph_signal[spi_strip->spi_host].spid_out, true, false);
|
||||
}
|
||||
|
||||
spi_device_interface_config_t spi_dev_cfg = {
|
||||
.clock_source = clk_src,
|
||||
.command_bits = 0,
|
||||
.address_bits = 0,
|
||||
.dummy_bits = 0,
|
||||
.clock_speed_hz = LED_STRIP_SPI_DEFAULT_RESOLUTION,
|
||||
.mode = 0,
|
||||
//set -1 when CS is not used
|
||||
.spics_io_num = -1,
|
||||
.queue_size = LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE,
|
||||
};
|
||||
|
||||
ESP_GOTO_ON_ERROR(spi_bus_add_device(spi_strip->spi_host, &spi_dev_cfg, &spi_strip->spi_device), err, TAG, "Failed to add spi device");
|
||||
//ensure the reset time is enough
|
||||
esp_rom_delay_us(10);
|
||||
int clock_resolution_khz = 0;
|
||||
spi_device_get_actual_freq(spi_strip->spi_device, &clock_resolution_khz);
|
||||
// TODO: ideally we should decide the SPI_BYTES_PER_COLOR_BYTE by the real clock resolution
|
||||
// But now, let's fixed the resolution, the downside is, we don't support a clock source whose frequency is not multiple of LED_STRIP_SPI_DEFAULT_RESOLUTION
|
||||
// clock_resolution between 2.2MHz to 2.8MHz is supported
|
||||
ESP_GOTO_ON_FALSE((clock_resolution_khz < LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000 + 300) && (clock_resolution_khz > LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000 - 300), ESP_ERR_NOT_SUPPORTED, err,
|
||||
TAG, "unsupported clock resolution:%dKHz", clock_resolution_khz);
|
||||
|
||||
spi_strip->bytes_per_pixel = bytes_per_pixel;
|
||||
spi_strip->strip_len = led_config->max_leds;
|
||||
spi_strip->base.set_pixel = led_strip_spi_set_pixel;
|
||||
spi_strip->base.set_pixel_rgbw = led_strip_spi_set_pixel_rgbw;
|
||||
spi_strip->base.refresh = led_strip_spi_refresh;
|
||||
spi_strip->base.clear = led_strip_spi_clear;
|
||||
spi_strip->base.del = led_strip_spi_del;
|
||||
|
||||
*ret_strip = &spi_strip->base;
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (spi_strip) {
|
||||
if (spi_strip->spi_device) {
|
||||
spi_bus_remove_device(spi_strip->spi_device);
|
||||
}
|
||||
if (spi_strip->spi_host) {
|
||||
spi_bus_free(spi_strip->spi_host);
|
||||
}
|
||||
free(spi_strip);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user