SHA256
Перенёс основной ESP32-скетч в main-device
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
# Main Device
|
||||
|
||||
Основной скетч homeserver и старые тестовые скетчи для быстрой проверки платы.
|
||||
`burn.sh` теперь:
|
||||
- сам пытается найти USB-порт ESP32;
|
||||
- сначала делает быструю инкрементальную сборку;
|
||||
- если быстрая сборка не удалась, автоматически повторяет полную `clean`-сборку.
|
||||
|
||||
Для режимов `widgets`, `audio` и `hello` рядом должен лежать локальный checkout `official-demo/` из официального репозитория Waveshare. В основной git он не добавляется, потому что это большой внешний набор примеров, библиотек, прошивок и артефактов.
|
||||
|
||||
Режимы:
|
||||
- `widgets` — экран + touch + IMU (пример `05_LVGL_Widgets`)
|
||||
- `audio` — динамик/аудио-кодек (пример `07_ES8311`)
|
||||
- `hello` — базовый тест экрана (пример `01_HelloWorld`)
|
||||
- `simple` — простой кастомный тест: экран + touch + запись/проигрывание + наклон (IMU)
|
||||
- `argon2` — генерация masterSecret через Argon2id с SD-картой как памятью (тест скорости)
|
||||
- `homeserver-ui` — совместимый алиас, указывает на `shine_homeserver_main/`
|
||||
- `shine-homeserver-main` — основной скетч проекта `SHiNE` для ESP32, текущая рабочая версия UI
|
||||
- `shine-homeserver-ui-main` — старое имя основного скетча, оставлено как совместимый алиас
|
||||
- `legacy-homeserver-ui` — старый UI-прототип `shine_homeserver_ui/`, оставлен как тестовый и не является основным
|
||||
- `text-test` — диагностический экран рендера текста: default font, U8g2 ASCII, U8g2 кириллица, кнопки с подписями
|
||||
- `gfx-text-test` — тот же тест рендера текста, но уже внутри новой папки `test_sketches/`
|
||||
- `gfx-layout-test` — тест геометрии и нижних рядов кнопок
|
||||
- `lvgl-basic-test` — минимальный экран на `LVGL` с текстом и кнопками
|
||||
- `lvgl-interaction-test` — экран на `LVGL` с большим числом кнопок и сообщением о нажатой кнопке
|
||||
- `lvgl-touch-debug-test` — точечная диагностика touch: сырые координаты, маркер точки и большая тест-кнопка `LVGL`
|
||||
- `lvgl-official-based-test` — наш минимальный экран, но на максимально близкой к официальному `LVGL_Widgets` инициализации
|
||||
- `lvgl-subserver-touch-test` — старый гибридный тест: `LVGL`-интерфейс, но display/touch init и raw touch-read взяты из старого `shine_homeserver_ui`; подтверждено на устройстве, touch работает, зелёных линий по краям нет
|
||||
- `lvgl-russian-font-test` — тест кастомного `LVGL`-шрифта с кириллицей: русские кнопки, длинные подписи и статусы
|
||||
- `lvgl-nav-minimal-test` — старое имя основного скетча, теперь ведёт на `shine_homeserver_main/` для совместимости
|
||||
|
||||
Запуск:
|
||||
|
||||
- `./burn.sh widgets`
|
||||
- `./burn.sh audio`
|
||||
- `./burn.sh hello`
|
||||
- `./burn.sh simple`
|
||||
- `./burn.sh homeserver-ui`
|
||||
- `./burn.sh shine-homeserver-main`
|
||||
- `./burn.sh shine-homeserver-ui-main`
|
||||
- `./burn.sh legacy-homeserver-ui`
|
||||
- `./burn.sh text-test`
|
||||
- `./burn.sh gfx-text-test`
|
||||
- `./burn.sh gfx-layout-test`
|
||||
- `./burn.sh lvgl-basic-test`
|
||||
- `./burn.sh lvgl-interaction-test`
|
||||
- `./burn.sh lvgl-touch-debug-test`
|
||||
- `./burn.sh lvgl-official-based-test`
|
||||
- `./burn.sh lvgl-subserver-touch-test`
|
||||
- `./burn.sh lvgl-russian-font-test`
|
||||
- `./burn.sh lvgl-nav-minimal-test`
|
||||
- `./flash_shine_homeserver_main.sh` - автоматически находит USB-порт и заливает `shine_homeserver_main`
|
||||
+1066
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// BLAKE2b state
|
||||
struct B2State {
|
||||
uint64_t h[8], t[2], f[2];
|
||||
uint8_t buf[128];
|
||||
size_t buflen, outlen;
|
||||
};
|
||||
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BOARD_DIR="$(cd "${ROOT_DIR}/.." && pwd)"
|
||||
DEMO_BASE="${BOARD_DIR}/official-demo/examples/Arduino-v3.3.5"
|
||||
MODE="${1:-widgets}"
|
||||
PORT="${PORT:-}"
|
||||
FQBN="${FQBN:-esp32:esp32:esp32s3:USBMode=hwcdc,CDCOnBoot=cdc,UploadSpeed=921600,CPUFreq=240,FlashMode=dio,FlashSize=16M,PartitionScheme=app3M_fat9M_16MB,PSRAM=opi}"
|
||||
BUILD_DIR="${BUILD_DIR:-${ROOT_DIR}/.arduino-build/build-${MODE}}"
|
||||
OUT_DIR="${OUT_DIR:-${ROOT_DIR}/.arduino-build/out-${MODE}}"
|
||||
|
||||
detect_port() {
|
||||
local detected
|
||||
detected="$(arduino-cli board list 2>/dev/null | awk '/\/dev\/tty(ACM|USB)/ {print $1; exit}')"
|
||||
if [[ -n "${detected}" ]]; then
|
||||
echo "${detected}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
for candidate in /dev/ttyACM* /dev/ttyUSB*; do
|
||||
if [[ -e "${candidate}" ]]; then
|
||||
echo "${candidate}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
case "${MODE}" in
|
||||
hello) SKETCH_DIR="${DEMO_BASE}/examples/01_HelloWorld" ;;
|
||||
widgets) SKETCH_DIR="${DEMO_BASE}/examples/05_LVGL_Widgets" ;;
|
||||
audio) SKETCH_DIR="${DEMO_BASE}/examples/07_ES8311" ;;
|
||||
simple) SKETCH_DIR="${ROOT_DIR}/simple_av_test" ;;
|
||||
argon2) SKETCH_DIR="${ROOT_DIR}/argon2_sd_test" ;;
|
||||
homeserver-ui) SKETCH_DIR="${ROOT_DIR}/shine_homeserver_main" ;;
|
||||
shine-homeserver-main) SKETCH_DIR="${ROOT_DIR}/shine_homeserver_main" ;;
|
||||
shine-homeserver-ui-main) SKETCH_DIR="${ROOT_DIR}/shine_homeserver_main" ;;
|
||||
legacy-homeserver-ui) SKETCH_DIR="${ROOT_DIR}/shine_homeserver_ui" ;;
|
||||
text-test) SKETCH_DIR="${ROOT_DIR}/text_render_test" ;;
|
||||
gfx-text-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/gfx_text_render_test" ;;
|
||||
gfx-layout-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/gfx_button_layout_test" ;;
|
||||
lvgl-basic-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_basic_test" ;;
|
||||
lvgl-interaction-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_interaction_test" ;;
|
||||
lvgl-touch-debug-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_touch_debug_test" ;;
|
||||
lvgl-official-based-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_official_based_test" ;;
|
||||
lvgl-subserver-touch-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_subserver_touch_test" ;;
|
||||
lvgl-russian-font-test) SKETCH_DIR="${ROOT_DIR}/test_sketches/lvgl_russian_font_test" ;;
|
||||
lvgl-nav-minimal-test) SKETCH_DIR="${ROOT_DIR}/shine_homeserver_main" ;;
|
||||
*)
|
||||
echo "Unknown mode: ${MODE}" >&2
|
||||
echo "Use one of: hello, widgets, audio, simple, argon2, homeserver-ui, shine-homeserver-main, shine-homeserver-ui-main, legacy-homeserver-ui, text-test, gfx-text-test, gfx-layout-test, lvgl-basic-test, lvgl-interaction-test, lvgl-touch-debug-test, lvgl-official-based-test, lvgl-subserver-touch-test, lvgl-russian-font-test" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -z "${PORT}" ]]; then
|
||||
if ! PORT="$(detect_port)"; then
|
||||
echo "Failed to auto-detect ESP32 port. Set PORT=/dev/ttyACM0 ./burn.sh ${MODE}" >&2
|
||||
exit 3
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "== Mode: ${MODE}"
|
||||
echo "== Sketch: ${SKETCH_DIR}"
|
||||
echo "== Port: ${PORT}"
|
||||
echo "== FQBN: ${FQBN}"
|
||||
|
||||
mkdir -p "${BUILD_DIR}" "${OUT_DIR}"
|
||||
|
||||
compile_args=(
|
||||
--fqbn "${FQBN}"
|
||||
--build-path "${BUILD_DIR}"
|
||||
--output-dir "${OUT_DIR}"
|
||||
--library "${DEMO_BASE}/libraries/GFX_Library_for_Arduino"
|
||||
--library "${DEMO_BASE}/libraries/SensorLib"
|
||||
--library "${DEMO_BASE}/libraries/XPowersLib"
|
||||
--library "${DEMO_BASE}/libraries/lvgl"
|
||||
--library "${DEMO_BASE}/libraries/Mylibrary"
|
||||
"${SKETCH_DIR}"
|
||||
)
|
||||
|
||||
echo "== Compile: fast incremental build"
|
||||
if ! arduino-cli compile "${compile_args[@]}"; then
|
||||
echo "== Compile: fast build failed, retrying clean build"
|
||||
arduino-cli compile --clean "${compile_args[@]}"
|
||||
fi
|
||||
|
||||
arduino-cli upload \
|
||||
-p "${PORT}" \
|
||||
--fqbn "${FQBN}" \
|
||||
--input-dir "${OUT_DIR}" \
|
||||
"${SKETCH_DIR}"
|
||||
|
||||
echo
|
||||
echo "== Done."
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
detect_port_from_arduino_cli() {
|
||||
local line
|
||||
while IFS= read -r line; do
|
||||
[[ -z "${line}" ]] && continue
|
||||
[[ "${line}" == Port* ]] && continue
|
||||
if [[ "${line}" == /dev/* ]]; then
|
||||
awk '{print $1}' <<<"${line}"
|
||||
return 0
|
||||
fi
|
||||
done < <(arduino-cli board list 2>/dev/null || true)
|
||||
return 1
|
||||
}
|
||||
|
||||
detect_port_from_dev() {
|
||||
local candidates=()
|
||||
local path
|
||||
for path in /dev/ttyACM* /dev/ttyUSB*; do
|
||||
[[ -e "${path}" ]] || continue
|
||||
candidates+=("${path}")
|
||||
done
|
||||
|
||||
if [[ "${#candidates[@]}" -eq 1 ]]; then
|
||||
printf '%s\n' "${candidates[0]}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
PORT="${PORT:-}"
|
||||
if [[ -z "${PORT}" ]]; then
|
||||
PORT="$(detect_port_from_arduino_cli || true)"
|
||||
fi
|
||||
if [[ -z "${PORT}" ]]; then
|
||||
PORT="$(detect_port_from_dev || true)"
|
||||
fi
|
||||
|
||||
if [[ -z "${PORT}" ]]; then
|
||||
echo "Не удалось автоматически найти USB-порт ESP32." >&2
|
||||
echo "Подключите плату и проверьте 'arduino-cli board list'." >&2
|
||||
echo "Либо укажите порт вручную: PORT=/dev/ttyACM0 ./flash_shine_homeserver_main.sh" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "== Найден порт: ${PORT}"
|
||||
PORT="${PORT}" "${ROOT_DIR}/burn.sh" shine-homeserver-main
|
||||
@@ -0,0 +1,14 @@
|
||||
# SHiNE Homeserver UI Main
|
||||
|
||||
Это основной рабочий скетч ESP32-проекта `SHiNE`.
|
||||
|
||||
Текущая каноническая точка запуска:
|
||||
|
||||
- `./burn.sh shine-homeserver-main`
|
||||
- `./burn.sh homeserver-ui`
|
||||
|
||||
Историческое имя этого скетча:
|
||||
|
||||
- `lvgl-nav-minimal-test`
|
||||
|
||||
Прежние тестовые варианты для этой платы остаются в `main-device/test_sketches/` и должны восприниматься как старые диагностические сборки, а не как основной UI.
|
||||
+3332
File diff suppressed because it is too large
Load Diff
+578
@@ -0,0 +1,578 @@
|
||||
#include "shine_secret_generation.h"
|
||||
|
||||
#include <SD_MMC.h>
|
||||
#include <Preferences.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <mbedtls/base64.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#define PIN_SD_CLK 2
|
||||
#define PIN_SD_CMD 1
|
||||
#define PIN_SD_D0 3
|
||||
#define SD_MEM_FILE "/argon2.bin"
|
||||
|
||||
#define A2_T 2u
|
||||
#define A2_M 65536u
|
||||
#define A2_P 1u
|
||||
#define A2_DKLEN 32u
|
||||
#define A2_SYNC 4u
|
||||
#define A2_SEG (A2_M / A2_SYNC)
|
||||
#define A2_VERSION 0x13u
|
||||
#define A2_TYPE 2u
|
||||
#define A2_BLKSZ 1024u
|
||||
#define TOTAL_FILLS (A2_T * A2_M - 2u)
|
||||
|
||||
struct B2State {
|
||||
uint64_t h[8], t[2], f[2];
|
||||
uint8_t buf[128];
|
||||
size_t buflen, outlen;
|
||||
};
|
||||
|
||||
static bool gSdReady = false;
|
||||
static bool gRunning = false;
|
||||
static bool gDone = false;
|
||||
static bool gError = false;
|
||||
static char gMessage[96] = {};
|
||||
static uint8_t gSecret[32] = {};
|
||||
static char gSecretB58[64] = {};
|
||||
static uint32_t gDoneBlocks = 0;
|
||||
static uint32_t gStartMs = 0;
|
||||
static uint32_t gCurPass = 0;
|
||||
static uint32_t gCurBlock = 2;
|
||||
static bool gInitDone = false;
|
||||
static File gSdFile;
|
||||
static uint8_t *gBufPrev = nullptr;
|
||||
static uint8_t *gBufRef = nullptr;
|
||||
static uint8_t *gBufOut = nullptr;
|
||||
static uint8_t *gBufZero = nullptr;
|
||||
static uint8_t *gBufAddr = nullptr;
|
||||
static uint32_t *gJ1Seg = nullptr;
|
||||
static uint8_t gH0[64] = {};
|
||||
static B2State gB2S;
|
||||
|
||||
#define ROTR64(x,n) (((x)>>(n))|((x)<<(64-(n))))
|
||||
static const uint64_t B2IV[8] = {
|
||||
0x6A09E667F3BCC908ULL,0xBB67AE8584CAA73BULL,
|
||||
0x3C6EF372FE94F82BULL,0xA54FF53A5F1D36F1ULL,
|
||||
0x510E527FADE682D1ULL,0x9B05688C2B3E6C1FULL,
|
||||
0x1F83D9ABFB41BD6BULL,0x5BE0CD19137E2179ULL
|
||||
};
|
||||
static const uint8_t B2SIGMA[12][16] = {
|
||||
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},
|
||||
{14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3},
|
||||
{11,8,12,0,5,2,15,13,10,14,3,6,7,1,9,4},
|
||||
{7,9,3,1,13,12,11,14,2,6,5,10,4,0,15,8},
|
||||
{9,0,5,7,2,4,10,15,14,1,11,12,6,8,3,13},
|
||||
{2,12,6,10,0,11,8,3,4,13,7,5,15,14,1,9},
|
||||
{12,5,1,15,14,13,4,10,0,7,6,3,9,2,8,11},
|
||||
{13,11,7,14,12,1,3,9,5,0,15,4,8,6,2,10},
|
||||
{6,15,14,9,11,3,0,8,12,2,13,7,1,4,10,5},
|
||||
{10,2,8,4,7,6,1,5,15,11,9,14,3,12,13,0},
|
||||
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},
|
||||
{14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3}
|
||||
};
|
||||
|
||||
static const char B58_ALPHA[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
|
||||
static void setMessage(const char *message) {
|
||||
snprintf(gMessage, sizeof(gMessage), "%s", message ? message : "");
|
||||
}
|
||||
|
||||
static void b2_compress(B2State *S, const uint8_t *blk) {
|
||||
uint64_t m[16], v[16];
|
||||
for (int i = 0; i < 16; i++) m[i] = ((const uint64_t *)blk)[i];
|
||||
for (int i = 0; i < 8; i++) v[i] = S->h[i];
|
||||
v[8]=B2IV[0];v[9]=B2IV[1];v[10]=B2IV[2];v[11]=B2IV[3];
|
||||
v[12]=B2IV[4]^S->t[0];v[13]=B2IV[5]^S->t[1];
|
||||
v[14]=B2IV[6]^S->f[0];v[15]=B2IV[7]^S->f[1];
|
||||
#define BG(r,i,a,b,c,d) \
|
||||
v[a]+=v[b]+m[B2SIGMA[r][2*(i)]]; v[d]=ROTR64(v[d]^v[a],32); \
|
||||
v[c]+=v[d]; v[b]=ROTR64(v[b]^v[c],24); \
|
||||
v[a]+=v[b]+m[B2SIGMA[r][2*(i)+1]]; v[d]=ROTR64(v[d]^v[a],16); \
|
||||
v[c]+=v[d]; v[b]=ROTR64(v[b]^v[c],63)
|
||||
for (int r = 0; r < 12; r++) {
|
||||
BG(r,0,0,4,8,12);BG(r,1,1,5,9,13);BG(r,2,2,6,10,14);BG(r,3,3,7,11,15);
|
||||
BG(r,4,0,5,10,15);BG(r,5,1,6,11,12);BG(r,6,2,7,8,13);BG(r,7,3,4,9,14);
|
||||
}
|
||||
#undef BG
|
||||
for (int i = 0; i < 8; i++) S->h[i] ^= v[i] ^ v[i + 8];
|
||||
}
|
||||
|
||||
static void b2_init(B2State *S, size_t outlen) {
|
||||
memset(S, 0, sizeof(*S));
|
||||
memcpy(S->h, B2IV, 64);
|
||||
S->h[0] ^= 0x01010000ULL | outlen;
|
||||
S->outlen = outlen;
|
||||
}
|
||||
|
||||
static void b2_update(B2State *S, const uint8_t *in, size_t inlen) {
|
||||
while (inlen > 0) {
|
||||
size_t fill = 128 - S->buflen;
|
||||
size_t use = inlen < fill ? inlen : fill;
|
||||
memcpy(S->buf + S->buflen, in, use);
|
||||
S->buflen += use;
|
||||
in += use;
|
||||
inlen -= use;
|
||||
if (S->buflen == 128 && inlen > 0) {
|
||||
S->t[0] += 128;
|
||||
if (S->t[0] < 128) S->t[1]++;
|
||||
b2_compress(S, S->buf);
|
||||
S->buflen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void b2_final(B2State *S, uint8_t *digest) {
|
||||
S->f[0] = ~0ULL;
|
||||
S->t[0] += S->buflen;
|
||||
if (S->t[0] < S->buflen) S->t[1]++;
|
||||
memset(S->buf + S->buflen, 0, 128 - S->buflen);
|
||||
b2_compress(S, S->buf);
|
||||
uint8_t tmp[64];
|
||||
for (int i = 0; i < 8; i++) {
|
||||
uint64_t w = S->h[i];
|
||||
for (int j = 0; j < 8; j++) tmp[i * 8 + j] = (uint8_t)(w >> (j * 8));
|
||||
}
|
||||
memcpy(digest, tmp, S->outlen);
|
||||
}
|
||||
|
||||
static void b2hash(const uint8_t *in, size_t inlen, uint8_t *digest, size_t outlen) {
|
||||
b2_init(&gB2S, outlen);
|
||||
b2_update(&gB2S, in, inlen);
|
||||
b2_final(&gB2S, digest);
|
||||
}
|
||||
|
||||
static void b2long(const uint8_t *in, size_t inlen, uint8_t *digest, uint32_t outlen) {
|
||||
uint8_t lenle[4] = {(uint8_t)outlen, (uint8_t)(outlen >> 8), (uint8_t)(outlen >> 16), (uint8_t)(outlen >> 24)};
|
||||
if (outlen <= 64) {
|
||||
b2_init(&gB2S, outlen);
|
||||
b2_update(&gB2S, lenle, 4);
|
||||
b2_update(&gB2S, in, inlen);
|
||||
b2_final(&gB2S, digest);
|
||||
return;
|
||||
}
|
||||
uint8_t tmp[64];
|
||||
b2_init(&gB2S, 64);
|
||||
b2_update(&gB2S, lenle, 4);
|
||||
b2_update(&gB2S, in, inlen);
|
||||
b2_final(&gB2S, tmp);
|
||||
memcpy(digest, tmp, 32);
|
||||
digest += 32;
|
||||
uint32_t rem = outlen - 32;
|
||||
while (rem > 64) {
|
||||
b2hash(tmp, 64, tmp, 64);
|
||||
memcpy(digest, tmp, 32);
|
||||
digest += 32;
|
||||
rem -= 32;
|
||||
}
|
||||
b2hash(tmp, 64, tmp, rem);
|
||||
memcpy(digest, tmp, rem);
|
||||
}
|
||||
|
||||
#define A2G(a,b,c,d) do{ \
|
||||
(a)+=(b)+2*((a)&0xFFFFFFFFULL)*((b)&0xFFFFFFFFULL); \
|
||||
(d)=ROTR64((d)^(a),32); \
|
||||
(c)+=(d)+2*((c)&0xFFFFFFFFULL)*((d)&0xFFFFFFFFULL); \
|
||||
(b)=ROTR64((b)^(c),24); \
|
||||
(a)+=(b)+2*((a)&0xFFFFFFFFULL)*((b)&0xFFFFFFFFULL); \
|
||||
(d)=ROTR64((d)^(a),16); \
|
||||
(c)+=(d)+2*((c)&0xFFFFFFFFULL)*((d)&0xFFFFFFFFULL); \
|
||||
(b)=ROTR64((b)^(c),63); \
|
||||
}while(0)
|
||||
#define A2P(v) do{ \
|
||||
A2G((v)[0],(v)[4],(v)[8],(v)[12]);A2G((v)[1],(v)[5],(v)[9],(v)[13]); \
|
||||
A2G((v)[2],(v)[6],(v)[10],(v)[14]);A2G((v)[3],(v)[7],(v)[11],(v)[15]); \
|
||||
A2G((v)[0],(v)[5],(v)[10],(v)[15]);A2G((v)[1],(v)[6],(v)[11],(v)[12]); \
|
||||
A2G((v)[2],(v)[7],(v)[8],(v)[13]);A2G((v)[3],(v)[4],(v)[9],(v)[14]); \
|
||||
}while(0)
|
||||
|
||||
static void fillBlock(uint32_t prevIdx, uint32_t refIdx, uint32_t outIdx, bool xorMode) {
|
||||
gSdFile.seek((uint64_t)prevIdx * A2_BLKSZ); gSdFile.read(gBufPrev, A2_BLKSZ);
|
||||
gSdFile.seek((uint64_t)refIdx * A2_BLKSZ); gSdFile.read(gBufRef, A2_BLKSZ);
|
||||
uint64_t *R = (uint64_t *)gBufOut, *P = (uint64_t *)gBufPrev, *F = (uint64_t *)gBufRef;
|
||||
for (int i = 0; i < 128; i++) R[i] = P[i] ^ F[i];
|
||||
uint64_t *Q = (uint64_t *)gBufRef; memcpy(Q, R, A2_BLKSZ);
|
||||
for (int j = 0; j < 8; j++) A2P(&Q[16 * j]);
|
||||
uint64_t row[16];
|
||||
for (int i = 0; i < 8; i++) {
|
||||
for (int k = 0; k < 8; k++) { row[2 * k] = Q[2 * i + 16 * k]; row[2 * k + 1] = Q[2 * i + 16 * k + 1]; }
|
||||
A2P(row);
|
||||
for (int k = 0; k < 8; k++) { Q[2 * i + 16 * k] = row[2 * k]; Q[2 * i + 16 * k + 1] = row[2 * k + 1]; }
|
||||
}
|
||||
for (int i = 0; i < 128; i++) R[i] = Q[i] ^ R[i];
|
||||
if (xorMode) {
|
||||
gSdFile.seek((uint64_t)outIdx * A2_BLKSZ); gSdFile.read(gBufPrev, A2_BLKSZ);
|
||||
uint64_t *O = (uint64_t *)gBufPrev; for (int i = 0; i < 128; i++) R[i] ^= O[i];
|
||||
}
|
||||
gSdFile.seek((uint64_t)outIdx * A2_BLKSZ); gSdFile.write(gBufOut, A2_BLKSZ);
|
||||
}
|
||||
|
||||
static void generateAddresses(uint32_t pass, uint32_t slice) {
|
||||
memset(gBufZero, 0, A2_BLKSZ);
|
||||
uint8_t inputBlk[A2_BLKSZ]; memset(inputBlk, 0, A2_BLKSZ);
|
||||
uint64_t *iv = (uint64_t *)inputBlk;
|
||||
iv[0]=pass;iv[1]=0;iv[2]=slice;iv[3]=A2_M;iv[4]=A2_T;iv[5]=A2_TYPE;
|
||||
uint32_t count = 0;
|
||||
for (uint32_t b = 0; b < A2_SEG; b += 128) {
|
||||
iv[6] = ++count;
|
||||
{
|
||||
uint64_t *R=(uint64_t*)gBufAddr,*Z=(uint64_t*)gBufZero,*I=iv;
|
||||
for(int i=0;i<128;i++)R[i]=Z[i]^I[i];
|
||||
uint64_t *Q=R,row[16];
|
||||
for(int j=0;j<8;j++)A2P(&Q[16*j]);
|
||||
for(int i=0;i<8;i++){
|
||||
for(int k=0;k<8;k++){row[2*k]=Q[2*i+16*k];row[2*k+1]=Q[2*i+16*k+1];}
|
||||
A2P(row);
|
||||
for(int k=0;k<8;k++){Q[2*i+16*k]=row[2*k];Q[2*i+16*k+1]=row[2*k+1];}
|
||||
}
|
||||
for(int i=0;i<128;i++)R[i]=Q[i]^(Z[i]^I[i]);
|
||||
}
|
||||
{
|
||||
uint64_t *R=(uint64_t*)gBufPrev,*Z=(uint64_t*)gBufZero,*A=(uint64_t*)gBufAddr;
|
||||
for(int i=0;i<128;i++)R[i]=Z[i]^A[i];
|
||||
uint64_t *Q=R,row[16];
|
||||
for(int j=0;j<8;j++)A2P(&Q[16*j]);
|
||||
for(int i=0;i<8;i++){
|
||||
for(int k=0;k<8;k++){row[2*k]=Q[2*i+16*k];row[2*k+1]=Q[2*i+16*k+1];}
|
||||
A2P(row);
|
||||
for(int k=0;k<8;k++){Q[2*i+16*k]=row[2*k];Q[2*i+16*k+1]=row[2*k+1];}
|
||||
}
|
||||
for(int i=0;i<128;i++)R[i]=Q[i]^(Z[i]^A[i]);
|
||||
memcpy(gBufAddr,gBufPrev,A2_BLKSZ);
|
||||
}
|
||||
uint64_t *addr=(uint64_t*)gBufAddr;
|
||||
uint32_t cnt=(b+128<=A2_SEG)?128:(A2_SEG-b);
|
||||
for(uint32_t j=0;j<cnt;j++)gJ1Seg[b+j]=(uint32_t)(addr[j]&0xFFFFFFFFULL);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t indexAlpha(uint32_t pass, uint32_t slice, uint32_t posInSlice, uint32_t J1) {
|
||||
uint32_t refAreaSize;
|
||||
if (pass == 0) {
|
||||
if (slice == 0) {
|
||||
uint32_t pos = posInSlice;
|
||||
refAreaSize = (pos < 2) ? 0 : pos - 1;
|
||||
} else {
|
||||
refAreaSize = slice * A2_SEG + posInSlice - 1;
|
||||
}
|
||||
} else {
|
||||
refAreaSize = A2_M - A2_SEG + posInSlice - 1;
|
||||
}
|
||||
if (refAreaSize == 0) refAreaSize = 1;
|
||||
uint64_t relPos = (uint64_t)J1 * J1 >> 32;
|
||||
relPos = refAreaSize - 1 - ((uint64_t)refAreaSize * relPos >> 32);
|
||||
uint32_t startPos = 0;
|
||||
if (pass != 0 && slice != A2_SYNC - 1) startPos = (slice + 1) * A2_SEG;
|
||||
return (startPos + relPos) % A2_M;
|
||||
}
|
||||
|
||||
static void sha256calc(const uint8_t *in, size_t len, uint8_t *out32) {
|
||||
mbedtls_sha256_context ctx;
|
||||
mbedtls_sha256_init(&ctx);
|
||||
mbedtls_sha256_starts(&ctx, 0);
|
||||
mbedtls_sha256_update(&ctx, in, len);
|
||||
mbedtls_sha256_finish(&ctx, out32);
|
||||
mbedtls_sha256_free(&ctx);
|
||||
}
|
||||
|
||||
static void trimInPlace(char *text) {
|
||||
if (!text) return;
|
||||
size_t len = strlen(text);
|
||||
size_t start = 0;
|
||||
while (start < len && (text[start] == ' ' || text[start] == '\t' || text[start] == '\n' || text[start] == '\r')) start++;
|
||||
size_t end = len;
|
||||
while (end > start && (text[end - 1] == ' ' || text[end - 1] == '\t' || text[end - 1] == '\n' || text[end - 1] == '\r')) end--;
|
||||
if (start > 0 && end > start) memmove(text, text + start, end - start);
|
||||
if (end <= start) { text[0] = '\0'; return; }
|
||||
text[end - start] = '\0';
|
||||
}
|
||||
|
||||
static void lowercaseAsciiInPlace(char *text) {
|
||||
if (!text) return;
|
||||
for (int i = 0; text[i]; i++) if (text[i] >= 'A' && text[i] <= 'Z') text[i] += 32;
|
||||
}
|
||||
|
||||
static void normalizeLoginInPlace(char *text) {
|
||||
trimInPlace(text);
|
||||
lowercaseAsciiInPlace(text);
|
||||
}
|
||||
|
||||
static void bytesToBase64Std(const uint8_t *data, size_t len, char *out, size_t outSz) {
|
||||
size_t b64len = 0;
|
||||
if (outSz == 0) return;
|
||||
if (mbedtls_base64_encode((uint8_t*)out, outSz, &b64len, data, len) != 0) {
|
||||
out[0] = '\0';
|
||||
return;
|
||||
}
|
||||
if (b64len >= outSz) b64len = outSz - 1;
|
||||
out[b64len] = '\0';
|
||||
}
|
||||
|
||||
static void deriveLegacyMasterSecret(const char *password, uint8_t *out32) {
|
||||
uint8_t baseHash[32];
|
||||
sha256calc((const uint8_t*)password, strlen(password), baseHash);
|
||||
char baseB64[64];
|
||||
bytesToBase64Std(baseHash, 32, baseB64, sizeof(baseB64));
|
||||
char material[96];
|
||||
snprintf(material, sizeof(material), "%smaster.secret", baseB64);
|
||||
sha256calc((const uint8_t*)material, strlen(material), out32);
|
||||
}
|
||||
|
||||
void shineSecretBase58Encode(const uint8_t *data, size_t len, char *out, size_t outSz) {
|
||||
int zeros = 0;
|
||||
while (zeros < (int)len && data[zeros] == 0) zeros++;
|
||||
uint8_t tmp[128] = {};
|
||||
int tmpLen = 0;
|
||||
for (int i = zeros; i < (int)len; i++) {
|
||||
int carry = data[i];
|
||||
for (int j = 0; j < tmpLen; j++) { carry += 256 * tmp[j]; tmp[j] = carry % 58; carry /= 58; }
|
||||
while (carry) { tmp[tmpLen++] = carry % 58; carry /= 58; }
|
||||
}
|
||||
int n = 0;
|
||||
for (int i = 0; i < zeros && n < (int)outSz - 1; i++) out[n++] = '1';
|
||||
for (int i = tmpLen - 1; i >= 0 && n < (int)outSz - 1; i--) out[n++] = B58_ALPHA[(int)tmp[i]];
|
||||
out[n] = '\0';
|
||||
}
|
||||
|
||||
bool shineSecretBase58Decode(const char *input, uint8_t *out, size_t *outLen, size_t maxOutLen, String &error) {
|
||||
error = "";
|
||||
if (!input || !*input) {
|
||||
error = "secret too short";
|
||||
return false;
|
||||
}
|
||||
size_t inLen = strlen(input);
|
||||
uint8_t tmp[256] = {};
|
||||
size_t tmpLen = 0;
|
||||
size_t zeros = 0;
|
||||
while (zeros < inLen && input[zeros] == '1') zeros++;
|
||||
|
||||
for (size_t i = zeros; i < inLen; ++i) {
|
||||
const char *pos = strchr(B58_ALPHA, input[i]);
|
||||
if (!pos) {
|
||||
error = "invalid base58";
|
||||
return false;
|
||||
}
|
||||
int carry = (int)(pos - B58_ALPHA);
|
||||
for (size_t j = 0; j < tmpLen; ++j) {
|
||||
carry += 58 * tmp[j];
|
||||
tmp[j] = carry & 0xFF;
|
||||
carry >>= 8;
|
||||
}
|
||||
while (carry > 0) {
|
||||
tmp[tmpLen++] = carry & 0xFF;
|
||||
carry >>= 8;
|
||||
if (tmpLen >= sizeof(tmp)) {
|
||||
error = "secret too long";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t decodedLen = zeros + tmpLen;
|
||||
if (decodedLen > maxOutLen) {
|
||||
error = "secret too long";
|
||||
return false;
|
||||
}
|
||||
memset(out, 0, maxOutLen);
|
||||
size_t offset = 0;
|
||||
for (size_t i = 0; i < zeros; ++i) out[offset++] = 0;
|
||||
for (size_t i = 0; i < tmpLen; ++i) out[offset + i] = tmp[tmpLen - 1 - i];
|
||||
*outLen = decodedLen;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void argon2Init(const char *login, const char *password) {
|
||||
char saltSrc[256];
|
||||
snprintf(saltSrc, sizeof(saltSrc), "shine-auth-v2|login=%s|suffix=master.secret", login);
|
||||
uint8_t saltHash[32]; sha256calc((uint8_t*)saltSrc, strlen(saltSrc), saltHash);
|
||||
uint8_t salt[16]; memcpy(salt, saltHash, 16);
|
||||
|
||||
char passBytes[128];
|
||||
snprintf(passBytes, sizeof(passBytes), "%s\n%s", login, password);
|
||||
uint32_t passLen = strlen(passBytes), saltLen = 16;
|
||||
|
||||
b2_init(&gB2S, 64);
|
||||
uint8_t le4[4];
|
||||
#define B2LE32(val) do{uint32_t _v=(val);le4[0]=_v;le4[1]=_v>>8;le4[2]=_v>>16;le4[3]=_v>>24;b2_update(&gB2S,le4,4);}while(0)
|
||||
B2LE32(A2_P);B2LE32(A2_DKLEN);B2LE32(A2_M);B2LE32(A2_T);
|
||||
B2LE32(A2_VERSION);B2LE32(A2_TYPE);
|
||||
B2LE32(passLen);b2_update(&gB2S,(const uint8_t*)passBytes,passLen);
|
||||
B2LE32(saltLen);b2_update(&gB2S,salt,saltLen);
|
||||
B2LE32(0);B2LE32(0);
|
||||
#undef B2LE32
|
||||
b2_final(&gB2S, gH0);
|
||||
|
||||
uint8_t input[72]; memcpy(input, gH0, 64);
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
input[64]=i;input[65]=0;input[66]=0;input[67]=0;
|
||||
input[68]=0;input[69]=0;input[70]=0;input[71]=0;
|
||||
b2long(input, 72, gBufOut, A2_BLKSZ);
|
||||
gSdFile.seek((uint64_t)i * A2_BLKSZ); gSdFile.write(gBufOut, A2_BLKSZ);
|
||||
}
|
||||
gCurPass = 0;
|
||||
gCurBlock = 2;
|
||||
gDoneBlocks = 0;
|
||||
gStartMs = millis();
|
||||
gInitDone = true;
|
||||
generateAddresses(0, 0);
|
||||
}
|
||||
|
||||
bool shineSecretInitSd(String &error) {
|
||||
error = "";
|
||||
if (gSdReady) return true;
|
||||
gpio_reset_pin(GPIO_NUM_2);
|
||||
pinMode(PIN_SD_CMD, INPUT_PULLUP);
|
||||
pinMode(PIN_SD_D0, INPUT_PULLUP);
|
||||
delay(20);
|
||||
SD_MMC.setPins(PIN_SD_CLK, PIN_SD_CMD, PIN_SD_D0);
|
||||
gSdReady = SD_MMC.begin("/sdcard", true);
|
||||
if (!gSdReady) {
|
||||
error = "SD card error";
|
||||
return false;
|
||||
}
|
||||
if (!gBufPrev) {
|
||||
gBufPrev=(uint8_t*)ps_malloc(A2_BLKSZ);
|
||||
gBufRef=(uint8_t*)ps_malloc(A2_BLKSZ);
|
||||
gBufOut=(uint8_t*)ps_malloc(A2_BLKSZ);
|
||||
gBufZero=(uint8_t*)ps_calloc(1, A2_BLKSZ);
|
||||
gBufAddr=(uint8_t*)ps_malloc(A2_BLKSZ);
|
||||
gJ1Seg=(uint32_t*)ps_malloc(A2_SEG * sizeof(uint32_t));
|
||||
}
|
||||
if (!gBufPrev || !gBufRef || !gBufOut || !gBufZero || !gBufAddr || !gJ1Seg) {
|
||||
error = "PSRAM alloc failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shineSecretStart(const char *normalizedLogin, const char *password, String &error) {
|
||||
error = "";
|
||||
if (!shineSecretInitSd(error)) return false;
|
||||
if (!normalizedLogin || !*normalizedLogin) {
|
||||
error = "login not set";
|
||||
return false;
|
||||
}
|
||||
|
||||
char loginBuf[64];
|
||||
snprintf(loginBuf, sizeof(loginBuf), "%s", normalizedLogin);
|
||||
normalizeLoginInPlace(loginBuf);
|
||||
if (!loginBuf[0]) {
|
||||
error = "login not set";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gSdFile) gSdFile.close();
|
||||
SD_MMC.remove(SD_MEM_FILE);
|
||||
gSdFile = SD_MMC.open(SD_MEM_FILE, "w+");
|
||||
if (!gSdFile) {
|
||||
error = "SD open failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(gSecret, 0, sizeof(gSecret));
|
||||
gSecretB58[0] = '\0';
|
||||
gDone = false;
|
||||
gError = false;
|
||||
gRunning = true;
|
||||
gInitDone = false;
|
||||
setMessage("Generating secret...");
|
||||
|
||||
if (!password || !password[0]) {
|
||||
deriveLegacyMasterSecret(password ? password : "", gSecret);
|
||||
shineSecretBase58Encode(gSecret, 32, gSecretB58, sizeof(gSecretB58));
|
||||
gDone = true;
|
||||
gRunning = false;
|
||||
setMessage("Secret generated");
|
||||
gSdFile.close();
|
||||
SD_MMC.remove(SD_MEM_FILE);
|
||||
return true;
|
||||
}
|
||||
|
||||
argon2Init(loginBuf, password);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shineSecretStep(uint32_t blocksPerTick) {
|
||||
if (!gRunning || !gInitDone) return gDone;
|
||||
bool done = false;
|
||||
for (uint32_t i = 0; i < blocksPerTick && !done; i++) {
|
||||
if (gCurPass >= A2_T) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
uint32_t blk = gCurBlock;
|
||||
uint32_t slice = blk / A2_SEG;
|
||||
uint32_t posInSlice = blk - slice * A2_SEG;
|
||||
bool xorMode = (gCurPass > 0);
|
||||
uint32_t J1;
|
||||
bool useArgon2i = (gCurPass == 0 && slice < 2);
|
||||
if (useArgon2i) {
|
||||
J1 = gJ1Seg[posInSlice];
|
||||
} else {
|
||||
uint32_t prevIdx = (blk == 0) ? A2_M - 1 : blk - 1;
|
||||
gSdFile.seek((uint64_t)prevIdx * A2_BLKSZ);
|
||||
gSdFile.read(gBufRef, 8);
|
||||
J1 = ((uint32_t*)gBufRef)[0];
|
||||
}
|
||||
uint32_t prevIdx = (blk == 0) ? A2_M - 1 : blk - 1;
|
||||
uint32_t refIdx = indexAlpha(gCurPass, slice, posInSlice, J1);
|
||||
fillBlock(prevIdx, refIdx, blk, xorMode);
|
||||
gDoneBlocks++;
|
||||
gCurBlock++;
|
||||
if (gCurBlock >= A2_M) {
|
||||
gCurBlock = 0;
|
||||
gCurPass++;
|
||||
} else {
|
||||
uint32_t newSlice = gCurBlock / A2_SEG;
|
||||
if (newSlice != slice && gCurPass == 0 && newSlice < 2) generateAddresses(gCurPass, newSlice);
|
||||
}
|
||||
done = (gCurPass >= A2_T);
|
||||
}
|
||||
|
||||
if (done) {
|
||||
gSdFile.seek((uint64_t)(A2_M - 1) * A2_BLKSZ);
|
||||
gSdFile.read(gBufOut, A2_BLKSZ);
|
||||
b2long(gBufOut, A2_BLKSZ, gSecret, A2_DKLEN);
|
||||
shineSecretBase58Encode(gSecret, 32, gSecretB58, sizeof(gSecretB58));
|
||||
gDone = true;
|
||||
gRunning = false;
|
||||
setMessage("Secret generated");
|
||||
gSdFile.close();
|
||||
SD_MMC.remove(SD_MEM_FILE);
|
||||
}
|
||||
return gDone;
|
||||
}
|
||||
|
||||
void shineSecretAbort() {
|
||||
gRunning = false;
|
||||
gDone = false;
|
||||
gError = false;
|
||||
gInitDone = false;
|
||||
gDoneBlocks = 0;
|
||||
if (gSdFile) gSdFile.close();
|
||||
if (gSdReady) SD_MMC.remove(SD_MEM_FILE);
|
||||
setMessage("Generation cancelled");
|
||||
}
|
||||
|
||||
ShineSecretGenerationStatus shineSecretStatus() {
|
||||
ShineSecretGenerationStatus status = {};
|
||||
status.running = gRunning;
|
||||
status.done = gDone;
|
||||
status.error = gError;
|
||||
status.doneBlocks = gDoneBlocks;
|
||||
status.totalBlocks = TOTAL_FILLS;
|
||||
status.elapsedSec = gStartMs == 0 ? 0 : (millis() - gStartMs) / 1000;
|
||||
snprintf(status.message, sizeof(status.message), "%s", gMessage);
|
||||
snprintf(status.secretBase58, sizeof(status.secretBase58), "%s", gSecretB58);
|
||||
return status;
|
||||
}
|
||||
|
||||
const uint8_t *shineSecretBytes() {
|
||||
return gSecret;
|
||||
}
|
||||
|
||||
const char *shineSecretBase58() {
|
||||
return gSecretB58;
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct ShineSecretGenerationStatus {
|
||||
bool running;
|
||||
bool done;
|
||||
bool error;
|
||||
uint32_t doneBlocks;
|
||||
uint32_t totalBlocks;
|
||||
uint32_t elapsedSec;
|
||||
char message[96];
|
||||
char secretBase58[64];
|
||||
};
|
||||
|
||||
bool shineSecretInitSd(String &error);
|
||||
bool shineSecretStart(const char *normalizedLogin, const char *password, String &error);
|
||||
bool shineSecretStep(uint32_t blocksPerTick = 4);
|
||||
void shineSecretAbort();
|
||||
ShineSecretGenerationStatus shineSecretStatus();
|
||||
const uint8_t *shineSecretBytes();
|
||||
const char *shineSecretBase58();
|
||||
void shineSecretBase58Encode(const uint8_t *data, size_t len, char *out, size_t outSz);
|
||||
bool shineSecretBase58Decode(const char *input, uint8_t *out, size_t *outLen, size_t maxOutLen, String &error);
|
||||
@@ -0,0 +1,6 @@
|
||||
# SHiNE Homeserver UI Legacy
|
||||
|
||||
Это старый тестовый вариант UI для ESP32-платы `Waveshare ESP32-S3-Touch-AMOLED-2.16`.
|
||||
|
||||
Не использовать как основной скетч проекта.
|
||||
Основной рабочий скетч сейчас лежит в `../shine_homeserver_main/`.
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../official-demo/examples/Arduino-v3.3.5/libraries/lvgl/src/extra/libs/qrcode/qrcodegen.c"
|
||||
+2622
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _AUDIO_HAL_H_
|
||||
#define _AUDIO_HAL_H_
|
||||
|
||||
#define AUDIO_HAL_VOL_DEFAULT 60
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Select media hal codec mode
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_HAL_CODEC_MODE_ENCODE = 1, /*!< select adc */
|
||||
AUDIO_HAL_CODEC_MODE_DECODE, /*!< select dac */
|
||||
AUDIO_HAL_CODEC_MODE_BOTH, /*!< select both adc and dac */
|
||||
AUDIO_HAL_CODEC_MODE_LINE_IN, /*!< set adc channel */
|
||||
} audio_hal_codec_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Select adc channel for input mic signal
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_HAL_ADC_INPUT_LINE1 = 0x00, /*!< mic input to adc channel 1 */
|
||||
AUDIO_HAL_ADC_INPUT_LINE2, /*!< mic input to adc channel 2 */
|
||||
AUDIO_HAL_ADC_INPUT_ALL, /*!< mic input to both channels of adc */
|
||||
AUDIO_HAL_ADC_INPUT_DIFFERENCE, /*!< mic input to adc difference channel */
|
||||
} audio_hal_adc_input_t;
|
||||
|
||||
/**
|
||||
* @brief Select channel for dac output
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_HAL_DAC_OUTPUT_LINE1 = 0x00, /*!< dac output signal to channel 1 */
|
||||
AUDIO_HAL_DAC_OUTPUT_LINE2, /*!< dac output signal to channel 2 */
|
||||
AUDIO_HAL_DAC_OUTPUT_ALL, /*!< dac output signal to both channels */
|
||||
} audio_hal_dac_output_t;
|
||||
|
||||
/**
|
||||
* @brief Select operating mode i.e. start or stop for audio codec chip
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_HAL_CTRL_STOP = 0x00, /*!< set stop mode */
|
||||
AUDIO_HAL_CTRL_START = 0x01, /*!< set start mode */
|
||||
} audio_hal_ctrl_t;
|
||||
|
||||
/**
|
||||
* @brief Select I2S interface operating mode i.e. master or slave for audio codec chip
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_HAL_MODE_SLAVE = 0x00, /*!< set slave mode */
|
||||
AUDIO_HAL_MODE_MASTER = 0x01, /*!< set master mode */
|
||||
} audio_hal_iface_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Select I2S interface samples per second
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_HAL_08K_SAMPLES, /*!< set to 8k samples per second */
|
||||
AUDIO_HAL_11K_SAMPLES, /*!< set to 11.025k samples per second */
|
||||
AUDIO_HAL_16K_SAMPLES, /*!< set to 16k samples in per second */
|
||||
AUDIO_HAL_22K_SAMPLES, /*!< set to 22.050k samples per second */
|
||||
AUDIO_HAL_24K_SAMPLES, /*!< set to 24k samples in per second */
|
||||
AUDIO_HAL_32K_SAMPLES, /*!< set to 32k samples in per second */
|
||||
AUDIO_HAL_44K_SAMPLES, /*!< set to 44.1k samples per second */
|
||||
AUDIO_HAL_48K_SAMPLES, /*!< set to 48k samples per second */
|
||||
} audio_hal_iface_samples_t;
|
||||
|
||||
/**
|
||||
* @brief Select I2S interface number of bits per sample
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_HAL_BIT_LENGTH_16BITS = 1, /*!< set 16 bits per sample */
|
||||
AUDIO_HAL_BIT_LENGTH_24BITS, /*!< set 24 bits per sample */
|
||||
AUDIO_HAL_BIT_LENGTH_32BITS, /*!< set 32 bits per sample */
|
||||
} audio_hal_iface_bits_t;
|
||||
|
||||
/**
|
||||
* @brief Select I2S interface format for audio codec chip
|
||||
*/
|
||||
typedef enum {
|
||||
AUDIO_HAL_I2S_NORMAL = 0, /*!< set normal I2S format */
|
||||
AUDIO_HAL_I2S_LEFT, /*!< set all left format */
|
||||
AUDIO_HAL_I2S_RIGHT, /*!< set all right format */
|
||||
AUDIO_HAL_I2S_DSP, /*!< set dsp/pcm format */
|
||||
} audio_hal_iface_format_t;
|
||||
|
||||
/**
|
||||
* @brief I2s interface configuration for audio codec chip
|
||||
*/
|
||||
typedef struct {
|
||||
audio_hal_iface_mode_t mode; /*!< audio codec chip mode */
|
||||
audio_hal_iface_format_t fmt; /*!< I2S interface format */
|
||||
audio_hal_iface_samples_t samples; /*!< I2S interface samples per second */
|
||||
audio_hal_iface_bits_t bits; /*!< i2s interface number of bits per sample */
|
||||
} audio_hal_codec_i2s_iface_t;
|
||||
|
||||
/**
|
||||
* @brief Configure media hal for initialization of audio codec chip
|
||||
*/
|
||||
typedef struct {
|
||||
audio_hal_adc_input_t adc_input; /*!< set adc channel */
|
||||
audio_hal_dac_output_t dac_output; /*!< set dac channel */
|
||||
audio_hal_codec_mode_t codec_mode; /*!< select codec mode: adc, dac or both */
|
||||
audio_hal_codec_i2s_iface_t i2s_iface; /*!< set I2S interface configuration */
|
||||
} audio_hal_codec_config_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //__AUDIO_HAL_H__
|
||||
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2021 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
#include <Wire.h>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "es7210.h"
|
||||
|
||||
|
||||
#define I2S_DSP_MODE_A 0
|
||||
#define MCLK_DIV_FRE 256
|
||||
|
||||
|
||||
#define ES7210_MCLK_SOURCE FROM_CLOCK_DOUBLE_PIN /* In master mode, 0 : MCLK from pad 1 : MCLK from clock doubler */
|
||||
#define FROM_PAD_PIN 0
|
||||
#define FROM_CLOCK_DOUBLE_PIN 1
|
||||
|
||||
|
||||
static TwoWire *es7210wire;
|
||||
static es7210_gain_value_t gain;
|
||||
|
||||
/*
|
||||
* Clock coefficient structer
|
||||
*/
|
||||
struct _coeff_div_es7210 {
|
||||
uint32_t mclk; /* mclk frequency */
|
||||
uint32_t lrck; /* lrck */
|
||||
uint8_t ss_ds;
|
||||
uint8_t adc_div; /* adcclk divider */
|
||||
uint8_t dll; /* dll_bypass */
|
||||
uint8_t doubler; /* doubler enable */
|
||||
uint8_t osr; /* adc osr */
|
||||
uint8_t mclk_src; /* select mclk source */
|
||||
uint32_t lrck_h; /* The high 4 bits of lrck */
|
||||
uint32_t lrck_l; /* The low 8 bits of lrck */
|
||||
};
|
||||
|
||||
static const char *TAG = "ES7210";
|
||||
|
||||
static es7210_input_mics_t mic_select = (es7210_input_mics_t)(ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2 | ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4);
|
||||
|
||||
/* Codec hifi mclk clock divider coefficients
|
||||
* MEMBER REG
|
||||
* mclk: 0x03
|
||||
* lrck: standard
|
||||
* ss_ds: --
|
||||
* adc_div: 0x02
|
||||
* dll: 0x06
|
||||
* doubler: 0x02
|
||||
* osr: 0x07
|
||||
* mclk_src: 0x03
|
||||
* lrckh: 0x04
|
||||
* lrckl: 0x05
|
||||
*/
|
||||
static const struct _coeff_div_es7210 coeff_div[] = {
|
||||
//mclk lrck ss_ds adc_div dll doubler osr mclk_src lrckh lrckl
|
||||
/* 8k */
|
||||
{12288000, 8000 , 0x00, 0x03, 0x01, 0x00, 0x20, 0x00, 0x06, 0x00},
|
||||
{16384000, 8000 , 0x00, 0x04, 0x01, 0x00, 0x20, 0x00, 0x08, 0x00},
|
||||
{19200000, 8000 , 0x00, 0x1e, 0x00, 0x01, 0x28, 0x00, 0x09, 0x60},
|
||||
{4096000, 8000 , 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
|
||||
|
||||
/* 11.025k */
|
||||
{11289600, 11025, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00},
|
||||
|
||||
/* 12k */
|
||||
{12288000, 12000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00},
|
||||
{19200000, 12000, 0x00, 0x14, 0x00, 0x01, 0x28, 0x00, 0x06, 0x40},
|
||||
|
||||
/* 16k */
|
||||
{4096000, 16000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
|
||||
{19200000, 16000, 0x00, 0x0a, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x80},
|
||||
{16384000, 16000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00},
|
||||
{12288000, 16000, 0x00, 0x03, 0x01, 0x01, 0x20, 0x00, 0x03, 0x00},
|
||||
|
||||
/* 22.05k */
|
||||
{11289600, 22050, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
|
||||
|
||||
/* 24k */
|
||||
{12288000, 24000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
|
||||
{19200000, 24000, 0x00, 0x0a, 0x00, 0x01, 0x28, 0x00, 0x03, 0x20},
|
||||
|
||||
/* 32k */
|
||||
{12288000, 32000, 0x00, 0x03, 0x00, 0x00, 0x20, 0x00, 0x01, 0x80},
|
||||
{16384000, 32000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
|
||||
{19200000, 32000, 0x00, 0x05, 0x00, 0x00, 0x1e, 0x00, 0x02, 0x58},
|
||||
|
||||
/* 44.1k */
|
||||
{11289600, 44100, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
|
||||
|
||||
/* 48k */
|
||||
{12288000, 48000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
|
||||
{19200000, 48000, 0x00, 0x05, 0x00, 0x01, 0x28, 0x00, 0x01, 0x90},
|
||||
|
||||
/* 64k */
|
||||
{16384000, 64000, 0x01, 0x01, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00},
|
||||
{19200000, 64000, 0x00, 0x05, 0x00, 0x01, 0x1e, 0x00, 0x01, 0x2c},
|
||||
|
||||
/* 88.2k */
|
||||
{11289600, 88200, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80},
|
||||
|
||||
/* 96k */
|
||||
{12288000, 96000, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80},
|
||||
{19200000, 96000, 0x01, 0x05, 0x00, 0x01, 0x28, 0x00, 0x00, 0xc8},
|
||||
};
|
||||
|
||||
static esp_err_t es7210_write_reg(uint8_t reg_addr, uint8_t data)
|
||||
{
|
||||
|
||||
es7210wire->beginTransmission(ES7210_ADDR);
|
||||
es7210wire->write(reg_addr);
|
||||
es7210wire->write(data);
|
||||
return es7210wire->endTransmission();
|
||||
|
||||
}
|
||||
|
||||
static esp_err_t es7210_update_reg_bit(uint8_t reg_addr, uint8_t update_bits, uint8_t data)
|
||||
{
|
||||
uint8_t regv;
|
||||
regv = es7210_read_reg(reg_addr);
|
||||
regv = (regv & (~update_bits)) | (update_bits & data);
|
||||
return es7210_write_reg(reg_addr, regv);
|
||||
}
|
||||
|
||||
static int get_coeff(uint32_t mclk, uint32_t lrck)
|
||||
{
|
||||
for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) {
|
||||
if (coeff_div[i].lrck == lrck && coeff_div[i].mclk == mclk)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int8_t get_es7210_mclk_src(void)
|
||||
{
|
||||
return ES7210_MCLK_SOURCE;
|
||||
}
|
||||
|
||||
int es7210_read_reg(uint8_t reg_addr)
|
||||
{
|
||||
uint8_t data;
|
||||
es7210wire->beginTransmission(ES7210_ADDR);
|
||||
es7210wire->write(reg_addr);
|
||||
es7210wire->endTransmission(false);
|
||||
es7210wire->requestFrom(ES7210_ADDR, (size_t)1);
|
||||
data = es7210wire->read();
|
||||
return (int)data;
|
||||
}
|
||||
|
||||
esp_err_t es7210_config_sample(audio_hal_iface_samples_t sample)
|
||||
{
|
||||
uint8_t regv;
|
||||
int coeff;
|
||||
int sample_fre = 0;
|
||||
int mclk_fre = 0;
|
||||
esp_err_t ret = ESP_OK;
|
||||
switch (sample) {
|
||||
case AUDIO_HAL_08K_SAMPLES:
|
||||
sample_fre = 8000;
|
||||
break;
|
||||
case AUDIO_HAL_11K_SAMPLES:
|
||||
sample_fre = 11025;
|
||||
break;
|
||||
case AUDIO_HAL_16K_SAMPLES:
|
||||
sample_fre = 16000;
|
||||
break;
|
||||
case AUDIO_HAL_22K_SAMPLES:
|
||||
sample_fre = 22050;
|
||||
break;
|
||||
case AUDIO_HAL_24K_SAMPLES:
|
||||
sample_fre = 24000;
|
||||
break;
|
||||
case AUDIO_HAL_32K_SAMPLES:
|
||||
sample_fre = 32000;
|
||||
break;
|
||||
case AUDIO_HAL_44K_SAMPLES:
|
||||
sample_fre = 44100;
|
||||
break;
|
||||
case AUDIO_HAL_48K_SAMPLES:
|
||||
sample_fre = 48000;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Unable to configure sample rate %dHz", sample_fre);
|
||||
break;
|
||||
}
|
||||
mclk_fre = sample_fre * MCLK_DIV_FRE;
|
||||
coeff = get_coeff(mclk_fre, sample_fre);
|
||||
if (coeff < 0) {
|
||||
ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_fre, mclk_fre);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* Set clock parammeters */
|
||||
if (coeff >= 0) {
|
||||
/* Set adc_div & doubler & dll */
|
||||
regv = es7210_read_reg(ES7210_MAINCLK_REG02) & 0x00;
|
||||
regv |= coeff_div[coeff].adc_div;
|
||||
regv |= coeff_div[coeff].doubler << 6;
|
||||
regv |= coeff_div[coeff].dll << 7;
|
||||
ret |= es7210_write_reg(ES7210_MAINCLK_REG02, regv);
|
||||
/* Set osr */
|
||||
regv = coeff_div[coeff].osr;
|
||||
ret |= es7210_write_reg(ES7210_OSR_REG07, regv);
|
||||
/* Set lrck */
|
||||
regv = coeff_div[coeff].lrck_h;
|
||||
ret |= es7210_write_reg(ES7210_LRCK_DIVH_REG04, regv);
|
||||
regv = coeff_div[coeff].lrck_l;
|
||||
ret |= es7210_write_reg(ES7210_LRCK_DIVL_REG05, regv);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_mic_select(es7210_input_mics_t mic)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
mic_select = mic;
|
||||
if (mic_select & (ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2 | ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4)) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43 + i, 0x10, 0x00);
|
||||
}
|
||||
ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B, 0xff);
|
||||
ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0xff);
|
||||
if (mic_select & ES7210_INPUT_MIC1) {
|
||||
ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC1");
|
||||
ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00);
|
||||
ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B, 0x00);
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43, 0x10, 0x10);
|
||||
}
|
||||
if (mic_select & ES7210_INPUT_MIC2) {
|
||||
ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC2");
|
||||
ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00);
|
||||
ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B, 0x00);
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC2_GAIN_REG44, 0x10, 0x10);
|
||||
}
|
||||
if (mic_select & ES7210_INPUT_MIC3) {
|
||||
ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC3");
|
||||
ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x15, 0x00);
|
||||
ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0x00);
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC3_GAIN_REG45, 0x10, 0x10);
|
||||
}
|
||||
if (mic_select & ES7210_INPUT_MIC4) {
|
||||
ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC4");
|
||||
ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x15, 0x00);
|
||||
ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0x00);
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC4_GAIN_REG46, 0x10, 0x10);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Microphone selection error");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_adc_init(TwoWire *tw, audio_hal_codec_config_t *codec_cfg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
es7210wire = tw;
|
||||
|
||||
ret |= es7210_write_reg(ES7210_RESET_REG00, 0xff);
|
||||
ret |= es7210_write_reg(ES7210_RESET_REG00, 0x41);
|
||||
ret |= es7210_write_reg(ES7210_CLOCK_OFF_REG01, 0x1f);
|
||||
ret |= es7210_write_reg(ES7210_TIME_CONTROL0_REG09, 0x30); /* Set chip state cycle */
|
||||
ret |= es7210_write_reg(ES7210_TIME_CONTROL1_REG0A, 0x30); /* Set power on state cycle */
|
||||
// ret |= es7210_write_reg(ES7210_ADC12_HPF2_REG23, 0x2a); /* Quick setup */
|
||||
// ret |= es7210_write_reg(ES7210_ADC12_HPF1_REG22, 0x0a);
|
||||
// ret |= es7210_write_reg(ES7210_ADC34_HPF2_REG20, 0x0a);
|
||||
// ret |= es7210_write_reg(ES7210_ADC34_HPF1_REG21, 0x2a);
|
||||
/* Set master/slave audio interface */
|
||||
audio_hal_codec_i2s_iface_t *i2s_cfg = & (codec_cfg->i2s_iface);
|
||||
switch (i2s_cfg->mode) {
|
||||
case AUDIO_HAL_MODE_MASTER: /* MASTER MODE */
|
||||
ESP_LOGI(TAG, "ES7210 in Master mode");
|
||||
// ret |= es7210_update_reg_bit(ES7210_MODE_CONFIG_REG08, 0x01, 0x01);
|
||||
ret |= es7210_write_reg(ES7210_MODE_CONFIG_REG08, 0x20);
|
||||
/* Select clock source for internal mclk */
|
||||
switch (get_es7210_mclk_src()) {
|
||||
case FROM_PAD_PIN:
|
||||
ret |= es7210_update_reg_bit(ES7210_MASTER_CLK_REG03, 0x80, 0x00);
|
||||
break;
|
||||
case FROM_CLOCK_DOUBLE_PIN:
|
||||
ret |= es7210_update_reg_bit(ES7210_MASTER_CLK_REG03, 0x80, 0x80);
|
||||
break;
|
||||
default:
|
||||
ret |= es7210_update_reg_bit(ES7210_MASTER_CLK_REG03, 0x80, 0x00);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AUDIO_HAL_MODE_SLAVE: /* SLAVE MODE */
|
||||
ESP_LOGI(TAG, "ES7210 in Slave mode");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ret |= es7210_write_reg(ES7210_ANALOG_REG40, 0xC3); /* Select power off analog, vdda = 3.3V, close vx20ff, VMID select 5KΩ start */
|
||||
ret |= es7210_write_reg(ES7210_MIC12_BIAS_REG41, 0x70); /* Select 2.87v */
|
||||
ret |= es7210_write_reg(ES7210_MIC34_BIAS_REG42, 0x70); /* Select 2.87v */
|
||||
ret |= es7210_write_reg(ES7210_OSR_REG07, 0x20);
|
||||
ret |= es7210_write_reg(ES7210_MAINCLK_REG02, 0xc1); /* Set the frequency division coefficient and use dll except clock doubler, and need to set 0xc1 to clear the state */
|
||||
ret |= es7210_config_sample(i2s_cfg->samples);
|
||||
ret |= es7210_mic_select(mic_select);
|
||||
ret |= es7210_adc_set_gain_all(GAIN_0DB);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t es7210_adc_deinit()
|
||||
{
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t es7210_config_fmt(audio_hal_iface_format_t fmt)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint8_t adc_iface = 0;
|
||||
adc_iface = es7210_read_reg(ES7210_SDP_INTERFACE1_REG11);
|
||||
adc_iface &= 0xfc;
|
||||
switch (fmt) {
|
||||
case AUDIO_HAL_I2S_NORMAL:
|
||||
ESP_LOGD(TAG, "ES7210 in I2S Format");
|
||||
adc_iface |= 0x00;
|
||||
break;
|
||||
case AUDIO_HAL_I2S_LEFT:
|
||||
case AUDIO_HAL_I2S_RIGHT:
|
||||
ESP_LOGD(TAG, "ES7210 in LJ Format");
|
||||
adc_iface |= 0x01;
|
||||
break;
|
||||
case AUDIO_HAL_I2S_DSP:
|
||||
if (I2S_DSP_MODE_A) {
|
||||
ESP_LOGD(TAG, "ES7210 in DSP-A Format");
|
||||
adc_iface |= 0x03;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "ES7210 in DSP-B Format");
|
||||
adc_iface |= 0x13;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
adc_iface &= 0xfc;
|
||||
break;
|
||||
}
|
||||
ret |= es7210_write_reg(ES7210_SDP_INTERFACE1_REG11, adc_iface);
|
||||
/* Force ADC1/2 output to SDOUT1 and ADC3/4 output to SDOUT2 */
|
||||
ret |= es7210_write_reg(ES7210_SDP_INTERFACE2_REG12, 0x00);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_set_bits(audio_hal_iface_bits_t bits)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint8_t adc_iface = 0;
|
||||
adc_iface = es7210_read_reg(ES7210_SDP_INTERFACE1_REG11);
|
||||
adc_iface &= 0x1f;
|
||||
switch (bits) {
|
||||
case AUDIO_HAL_BIT_LENGTH_16BITS:
|
||||
adc_iface |= 0x60;
|
||||
break;
|
||||
case AUDIO_HAL_BIT_LENGTH_24BITS:
|
||||
adc_iface |= 0x00;
|
||||
break;
|
||||
case AUDIO_HAL_BIT_LENGTH_32BITS:
|
||||
adc_iface |= 0x80;
|
||||
break;
|
||||
default:
|
||||
adc_iface |= 0x60;
|
||||
break;
|
||||
}
|
||||
ret |= es7210_write_reg(ES7210_SDP_INTERFACE1_REG11, adc_iface);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_adc_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret |= es7210_set_bits(iface->bits);
|
||||
ret |= es7210_config_fmt(iface->fmt);
|
||||
ret |= es7210_config_sample(iface->samples);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_start(uint8_t clock_reg_value)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret |= es7210_write_reg(ES7210_CLOCK_OFF_REG01, clock_reg_value);
|
||||
ret |= es7210_write_reg(ES7210_POWER_DOWN_REG06, 0x00);
|
||||
// ret |= es7210_write_reg(ES7210_ANALOG_REG40, 0x40);
|
||||
ret |= es7210_write_reg(ES7210_MIC1_POWER_REG47, 0x00);
|
||||
ret |= es7210_write_reg(ES7210_MIC2_POWER_REG48, 0x00);
|
||||
ret |= es7210_write_reg(ES7210_MIC3_POWER_REG49, 0x00);
|
||||
ret |= es7210_write_reg(ES7210_MIC4_POWER_REG4A, 0x00);
|
||||
ret |= es7210_mic_select(mic_select);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_stop(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret |= es7210_write_reg(ES7210_MIC1_POWER_REG47, 0xff);
|
||||
ret |= es7210_write_reg(ES7210_MIC2_POWER_REG48, 0xff);
|
||||
ret |= es7210_write_reg(ES7210_MIC3_POWER_REG49, 0xff);
|
||||
ret |= es7210_write_reg(ES7210_MIC4_POWER_REG4A, 0xff);
|
||||
ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B,0xff);
|
||||
ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0xff);
|
||||
// ret |= es7210_write_reg(ES7210_ANALOG_REG40, 0xc0);
|
||||
ret |= es7210_write_reg(ES7210_CLOCK_OFF_REG01, 0x7f);
|
||||
ret |= es7210_write_reg(ES7210_POWER_DOWN_REG06, 0x07);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_adc_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state)
|
||||
{
|
||||
static uint8_t regv;
|
||||
esp_err_t ret = ESP_OK;
|
||||
// ESP_LOGW(TAG, "ES7210 only supports ADC mode");
|
||||
ret = es7210_read_reg(ES7210_CLOCK_OFF_REG01);
|
||||
if ((ret != 0x7f) && (ret != 0xff)) {
|
||||
regv = es7210_read_reg(ES7210_CLOCK_OFF_REG01);
|
||||
}
|
||||
if (ctrl_state == AUDIO_HAL_CTRL_START) {
|
||||
ESP_LOGI(TAG, "The ES7210_CLOCK_OFF_REG01 value before stop is %x",regv);
|
||||
ret |= es7210_start(regv);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "The codec is about to stop");
|
||||
regv = es7210_read_reg(ES7210_CLOCK_OFF_REG01);
|
||||
ret |= es7210_stop();
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t es7210_adc_set_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t gain)
|
||||
{
|
||||
esp_err_t ret_val = ESP_OK;
|
||||
|
||||
if (gain < GAIN_0DB) {
|
||||
gain = GAIN_0DB;
|
||||
}
|
||||
|
||||
if (gain > GAIN_37_5DB) {
|
||||
gain = GAIN_37_5DB;
|
||||
}
|
||||
|
||||
if (mic_mask & ES7210_INPUT_MIC1) {
|
||||
ret_val |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43, 0x0f, gain);
|
||||
}
|
||||
if (mic_mask & ES7210_INPUT_MIC2) {
|
||||
ret_val |= es7210_update_reg_bit(ES7210_MIC2_GAIN_REG44, 0x0f, gain);
|
||||
}
|
||||
if (mic_mask & ES7210_INPUT_MIC3) {
|
||||
ret_val |= es7210_update_reg_bit(ES7210_MIC3_GAIN_REG45, 0x0f, gain);
|
||||
}
|
||||
if (mic_mask & ES7210_INPUT_MIC4) {
|
||||
ret_val |= es7210_update_reg_bit(ES7210_MIC4_GAIN_REG46, 0x0f, gain);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
esp_err_t es7210_adc_set_gain_all(es7210_gain_value_t gain)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint32_t max_gain_vaule = 14;
|
||||
if (gain < 0) {
|
||||
gain = (es7210_gain_value_t) 0;
|
||||
} else if (gain > max_gain_vaule) {
|
||||
gain = (es7210_gain_value_t) max_gain_vaule;
|
||||
}
|
||||
ESP_LOGD(TAG, "SET: gain:%d", gain);
|
||||
if (mic_select & ES7210_INPUT_MIC1) {
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43, 0x0f, gain);
|
||||
}
|
||||
if (mic_select & ES7210_INPUT_MIC2) {
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC2_GAIN_REG44, 0x0f, gain);
|
||||
}
|
||||
if (mic_select & ES7210_INPUT_MIC3) {
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC3_GAIN_REG45, 0x0f, gain);
|
||||
}
|
||||
if (mic_select & ES7210_INPUT_MIC4) {
|
||||
ret |= es7210_update_reg_bit(ES7210_MIC4_GAIN_REG46, 0x0f, gain);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_adc_get_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t *gain)
|
||||
{
|
||||
int regv = 0;
|
||||
uint8_t gain_value;
|
||||
if (mic_mask & ES7210_INPUT_MIC1) {
|
||||
regv = es7210_read_reg(ES7210_MIC1_GAIN_REG43);
|
||||
} else if (mic_mask & ES7210_INPUT_MIC2) {
|
||||
regv = es7210_read_reg(ES7210_MIC2_GAIN_REG44);
|
||||
} else if (mic_mask & ES7210_INPUT_MIC3) {
|
||||
regv = es7210_read_reg(ES7210_MIC3_GAIN_REG45);
|
||||
} else if (mic_mask & ES7210_INPUT_MIC4) {
|
||||
regv = es7210_read_reg(ES7210_MIC4_GAIN_REG46);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No MIC selected");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (regv == ESP_FAIL) {
|
||||
return regv;
|
||||
}
|
||||
gain_value = (regv & 0x0f); /* Retain the last four bits for gain */
|
||||
*gain = (es7210_gain_value_t) gain_value;
|
||||
ESP_LOGI(TAG, "GET: gain_value:%d", gain_value);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t es7210_adc_set_volume(int volume)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_LOGD(TAG, "ADC can adjust gain");
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t es7210_set_mute(bool enable)
|
||||
{
|
||||
ESP_LOGD(TAG, "ES7210 SetMute :%d", enable);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void es7210_read_all(void)
|
||||
{
|
||||
for (int i = 0; i <= 0x4E; i++) {
|
||||
uint8_t reg = es7210_read_reg(i);
|
||||
ets_printf("REG:%02x, %02x\n", reg, i);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2021 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _ES7210_H
|
||||
#define _ES7210_H
|
||||
|
||||
#include "audio_hal.h"
|
||||
#include <Wire.h>
|
||||
|
||||
typedef enum {
|
||||
ES7210_AD1_AD0_00 = 0x40,
|
||||
ES7210_AD1_AD0_01 = 0x41,
|
||||
ES7210_AD1_AD0_10 = 0x42,
|
||||
ES7210_AD1_AD0_11 = 0x43,
|
||||
} es7210_address_t;
|
||||
|
||||
/* ES7210 address*/
|
||||
#define ES7210_ADDR ES7210_AD1_AD0_00
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ES7210_RESET_REG00 0x00 /* Reset control */
|
||||
#define ES7210_CLOCK_OFF_REG01 0x01 /* Used to turn off the ADC clock */
|
||||
#define ES7210_MAINCLK_REG02 0x02 /* Set ADC clock frequency division */
|
||||
#define ES7210_MASTER_CLK_REG03 0x03 /* MCLK source $ SCLK division */
|
||||
#define ES7210_LRCK_DIVH_REG04 0x04 /* lrck_divh */
|
||||
#define ES7210_LRCK_DIVL_REG05 0x05 /* lrck_divl */
|
||||
#define ES7210_POWER_DOWN_REG06 0x06 /* power down */
|
||||
#define ES7210_OSR_REG07 0x07
|
||||
#define ES7210_MODE_CONFIG_REG08 0x08 /* Set master/slave & channels */
|
||||
#define ES7210_TIME_CONTROL0_REG09 0x09 /* Set Chip intial state period*/
|
||||
#define ES7210_TIME_CONTROL1_REG0A 0x0A /* Set Power up state period */
|
||||
#define ES7210_SDP_INTERFACE1_REG11 0x11 /* Set sample & fmt */
|
||||
#define ES7210_SDP_INTERFACE2_REG12 0x12 /* Pins state */
|
||||
#define ES7210_ADC_AUTOMUTE_REG13 0x13 /* Set mute */
|
||||
#define ES7210_ADC34_MUTERANGE_REG14 0x14 /* Set mute range */
|
||||
#define ES7210_ADC34_HPF2_REG20 0x20 /* HPF */
|
||||
#define ES7210_ADC34_HPF1_REG21 0x21
|
||||
#define ES7210_ADC12_HPF1_REG22 0x22
|
||||
#define ES7210_ADC12_HPF2_REG23 0x23
|
||||
#define ES7210_ANALOG_REG40 0x40 /* ANALOG Power */
|
||||
#define ES7210_MIC12_BIAS_REG41 0x41
|
||||
#define ES7210_MIC34_BIAS_REG42 0x42
|
||||
#define ES7210_MIC1_GAIN_REG43 0x43
|
||||
#define ES7210_MIC2_GAIN_REG44 0x44
|
||||
#define ES7210_MIC3_GAIN_REG45 0x45
|
||||
#define ES7210_MIC4_GAIN_REG46 0x46
|
||||
#define ES7210_MIC1_POWER_REG47 0x47
|
||||
#define ES7210_MIC2_POWER_REG48 0x48
|
||||
#define ES7210_MIC3_POWER_REG49 0x49
|
||||
#define ES7210_MIC4_POWER_REG4A 0x4A
|
||||
#define ES7210_MIC12_POWER_REG4B 0x4B /* MICBias & ADC & PGA Power */
|
||||
#define ES7210_MIC34_POWER_REG4C 0x4C
|
||||
|
||||
|
||||
|
||||
typedef enum {
|
||||
ES7210_INPUT_MIC1 = 0x01,
|
||||
ES7210_INPUT_MIC2 = 0x02,
|
||||
ES7210_INPUT_MIC3 = 0x04,
|
||||
ES7210_INPUT_MIC4 = 0x08
|
||||
} es7210_input_mics_t;
|
||||
|
||||
typedef enum gain_value{
|
||||
GAIN_0DB = 0,
|
||||
GAIN_3DB,
|
||||
GAIN_6DB,
|
||||
GAIN_9DB,
|
||||
GAIN_12DB,
|
||||
GAIN_15DB,
|
||||
GAIN_18DB,
|
||||
GAIN_21DB,
|
||||
GAIN_24DB,
|
||||
GAIN_27DB,
|
||||
GAIN_30DB,
|
||||
GAIN_33DB,
|
||||
GAIN_34_5DB,
|
||||
GAIN_36DB,
|
||||
GAIN_37_5DB,
|
||||
} es7210_gain_value_t;
|
||||
|
||||
/*
|
||||
* @brief Initialize ES7210 ADC chip
|
||||
*
|
||||
* @param[in] codec_cfg: configuration of ES7210
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*/
|
||||
esp_err_t es7210_adc_init(TwoWire *tw, audio_hal_codec_config_t *codec_cfg);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize ES7210 ADC chip
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*/
|
||||
esp_err_t es7210_adc_deinit();
|
||||
|
||||
/**
|
||||
* @brief Configure ES7210 ADC mode and I2S interface
|
||||
*
|
||||
* @param[in] mode: codec mode
|
||||
* @param[in] iface: I2S config
|
||||
*
|
||||
* @return
|
||||
* - ESP_FAIL Parameter error
|
||||
* - ESP_OK Success
|
||||
*/
|
||||
esp_err_t es7210_adc_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface);
|
||||
|
||||
/**
|
||||
* @brief Control ES7210 ADC chip
|
||||
*
|
||||
* @param[in] mode: codec mode
|
||||
* @param[in] ctrl_state: start or stop progress
|
||||
*
|
||||
* @return
|
||||
* - ESP_FAIL Parameter error
|
||||
* - ESP_OK Success
|
||||
*/
|
||||
esp_err_t es7210_adc_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state);
|
||||
|
||||
/**
|
||||
* @brief Set gain of given mask
|
||||
*
|
||||
* @param[in] mic_mask Mask of MIC channel
|
||||
*
|
||||
* @param[in] gain: gain
|
||||
*
|
||||
* gain : value
|
||||
* GAIN_0DB : 1
|
||||
* GAIN_3DB : 2
|
||||
* GAIN_6DB : 3
|
||||
* ·
|
||||
* ·
|
||||
* ·
|
||||
* GAIN_30DB : 10
|
||||
* GAIN_33DB : 11
|
||||
* GAIN_34_5DB : 12
|
||||
* GAIN_36DB : 13
|
||||
* GAIN_37_5DB : 14
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*/
|
||||
esp_err_t es7210_adc_set_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t gain);
|
||||
|
||||
/**
|
||||
* @brief Set gain (Note: the enabled microphone sets the same gain)
|
||||
*
|
||||
* @param[in] gain: gain
|
||||
*
|
||||
* gain : value
|
||||
* GAIN_0DB : 1
|
||||
* GAIN_3DB : 2
|
||||
* GAIN_6DB : 3
|
||||
* ·
|
||||
* ·
|
||||
* ·
|
||||
* GAIN_30DB : 10
|
||||
* GAIN_33DB : 11
|
||||
* GAIN_34_5DB : 12
|
||||
* GAIN_36DB : 13
|
||||
* GAIN_37_5DB : 14
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*/
|
||||
esp_err_t es7210_adc_set_gain_all(es7210_gain_value_t gain);
|
||||
|
||||
/**
|
||||
* @brief Get MIC gain
|
||||
*
|
||||
* @param mic_mask Selected MIC
|
||||
* @param gain Pointer to `es7210_gain_value_t`
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*/
|
||||
esp_err_t es7210_adc_get_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t *gain);
|
||||
|
||||
/**
|
||||
* @brief Set volume
|
||||
*
|
||||
* @param[in] volume: volume
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
*/
|
||||
esp_err_t es7210_adc_set_volume(int volume);
|
||||
|
||||
/**
|
||||
* @brief Set ES7210 ADC mute status
|
||||
*
|
||||
* @return
|
||||
* - ESP_FAIL
|
||||
* - ESP_OK
|
||||
*/
|
||||
esp_err_t es7210_set_mute(bool enable);
|
||||
|
||||
/**
|
||||
* @brief Select ES7210 mic
|
||||
*
|
||||
* @param[in] mic: mics
|
||||
*
|
||||
* @return
|
||||
* - ESP_FAIL
|
||||
* - ESP_OK
|
||||
*/
|
||||
esp_err_t es7210_mic_select(es7210_input_mics_t mic);
|
||||
|
||||
/**
|
||||
* @brief Read regs of ES7210
|
||||
*
|
||||
* @param[in] reg_addr: reg_addr
|
||||
*
|
||||
* @return
|
||||
* - ESP_FAIL
|
||||
* - ESP_OK
|
||||
*/
|
||||
int es7210_read_reg(uint8_t reg_addr);
|
||||
|
||||
/**
|
||||
* @brief Read all regs of ES7210
|
||||
*/
|
||||
void es7210_read_all(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ES7210_H_ */
|
||||
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "es8311.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "es8311_reg.h"
|
||||
#include "esp32-hal-i2c.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
unsigned int port;
|
||||
uint16_t dev_addr;
|
||||
} es8311_dev_t;
|
||||
|
||||
/*
|
||||
* Clock coefficient structure
|
||||
*/
|
||||
struct _coeff_div {
|
||||
uint32_t mclk; /* mclk frequency */
|
||||
uint32_t rate; /* sample rate */
|
||||
uint8_t pre_div; /* the pre divider with range from 1 to 8 */
|
||||
uint8_t pre_multi; /* the pre multiplier with 0: 1x, 1: 2x, 2: 4x, 3: 8x selection */
|
||||
uint8_t adc_div; /* adcclk divider */
|
||||
uint8_t dac_div; /* dacclk divider */
|
||||
uint8_t fs_mode; /* double speed or single speed, =0, ss, =1, ds */
|
||||
uint8_t lrck_h; /* adclrck divider and daclrck divider */
|
||||
uint8_t lrck_l;
|
||||
uint8_t bclk_div; /* sclk divider */
|
||||
uint8_t adc_osr; /* adc osr */
|
||||
uint8_t dac_osr; /* dac osr */
|
||||
};
|
||||
|
||||
/* codec hifi mclk clock divider coefficients */
|
||||
static const struct _coeff_div coeff_div[] = {
|
||||
/*!<mclk rate pre_div mult adc_div dac_div fs_mode lrch lrcl bckdiv osr */
|
||||
/* 8k */
|
||||
{12288000, 8000, 0x06, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 8000, 0x03, 0x01, 0x03, 0x03, 0x00, 0x05, 0xff, 0x18, 0x10, 0x10},
|
||||
{16384000, 8000, 0x08, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{8192000, 8000, 0x04, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 8000, 0x03, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{4096000, 8000, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 8000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2048000, 8000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 8000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1024000, 8000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 11.025k */
|
||||
{11289600, 11025, 0x04, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{5644800, 11025, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2822400, 11025, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1411200, 11025, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 12k */
|
||||
{12288000, 12000, 0x04, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 12000, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 12000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 12000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 16k */
|
||||
{12288000, 16000, 0x03, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 16000, 0x03, 0x01, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10},
|
||||
{16384000, 16000, 0x04, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{8192000, 16000, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 16000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{4096000, 16000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 16000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2048000, 16000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 16000, 0x03, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1024000, 16000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 22.05k */
|
||||
{11289600, 22050, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{5644800, 22050, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2822400, 22050, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1411200, 22050, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{705600, 22050, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 24k */
|
||||
{12288000, 24000, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 24000, 0x03, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 24000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 24000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 24000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 32k */
|
||||
{12288000, 32000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 32000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10},
|
||||
{16384000, 32000, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{8192000, 32000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 32000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{4096000, 32000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 32000, 0x03, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2048000, 32000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 32000, 0x03, 0x03, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
||||
{1024000, 32000, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 44.1k */
|
||||
{11289600, 44100, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{5644800, 44100, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2822400, 44100, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1411200, 44100, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 48k */
|
||||
{12288000, 48000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 48000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 48000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 48000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 48000, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 64k */
|
||||
{12288000, 64000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 64000, 0x03, 0x02, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
|
||||
{16384000, 64000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{8192000, 64000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 64000, 0x01, 0x02, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
|
||||
{4096000, 64000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 64000, 0x01, 0x03, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
|
||||
{2048000, 64000, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 64000, 0x01, 0x03, 0x01, 0x01, 0x01, 0x00, 0xbf, 0x03, 0x18, 0x18},
|
||||
{1024000, 64000, 0x01, 0x03, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
||||
|
||||
/* 88.2k */
|
||||
{11289600, 88200, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{5644800, 88200, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2822400, 88200, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1411200, 88200, 0x01, 0x03, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
||||
|
||||
/* 96k */
|
||||
{12288000, 96000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 96000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 96000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 96000, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 96000, 0x01, 0x03, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
||||
};
|
||||
|
||||
static const char *TAG = "ES8311";
|
||||
|
||||
static inline esp_err_t es8311_write_reg(es8311_handle_t dev, uint8_t reg_addr, uint8_t data)
|
||||
{
|
||||
es8311_dev_t *es = (es8311_dev_t *) dev;
|
||||
const uint8_t write_buf[2] = {reg_addr, data};
|
||||
return i2cWrite(es->port, es->dev_addr, write_buf, sizeof(write_buf), 1000);
|
||||
}
|
||||
|
||||
static inline esp_err_t es8311_read_reg(es8311_handle_t dev, uint8_t reg_addr, uint8_t *reg_value)
|
||||
{
|
||||
es8311_dev_t *es = (es8311_dev_t *) dev;
|
||||
size_t readCount = 0;
|
||||
return i2cWriteReadNonStop(es->port, es->dev_addr, ®_addr, 1, reg_value, 1, 1000, &readCount);
|
||||
}
|
||||
|
||||
/*
|
||||
* look for the coefficient in coeff_div[] table
|
||||
*/
|
||||
static int get_coeff(uint32_t mclk, uint32_t rate)
|
||||
{
|
||||
for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) {
|
||||
if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
esp_err_t es8311_sample_frequency_config(es8311_handle_t dev, int mclk_frequency, int sample_frequency)
|
||||
{
|
||||
uint8_t regv;
|
||||
|
||||
/* Get clock coefficients from coefficient table */
|
||||
int coeff = get_coeff(mclk_frequency, sample_frequency);
|
||||
|
||||
if (coeff < 0) {
|
||||
ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_frequency, mclk_frequency);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
const struct _coeff_div *const selected_coeff = &coeff_div[coeff];
|
||||
|
||||
/* register 0x02 */
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_CLK_MANAGER_REG02, ®v), TAG, "I2C read/write error");
|
||||
regv &= 0x07;
|
||||
regv |= (selected_coeff->pre_div - 1) << 5;
|
||||
regv |= selected_coeff->pre_multi << 3;
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG02, regv), TAG, "I2C read/write error");
|
||||
|
||||
/* register 0x03 */
|
||||
const uint8_t reg03 = (selected_coeff->fs_mode << 6) | selected_coeff->adc_osr;
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG03, reg03), TAG, "I2C read/write error");
|
||||
|
||||
/* register 0x04 */
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG04, selected_coeff->dac_osr), TAG, "I2C read/write error");
|
||||
|
||||
/* register 0x05 */
|
||||
const uint8_t reg05 = ((selected_coeff->adc_div - 1) << 4) | (selected_coeff->dac_div - 1);
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG05, reg05), TAG, "I2C read/write error");
|
||||
|
||||
/* register 0x06 */
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_CLK_MANAGER_REG06, ®v), TAG, "I2C read/write error");
|
||||
regv &= 0xE0;
|
||||
|
||||
if (selected_coeff->bclk_div < 19) {
|
||||
regv |= (selected_coeff->bclk_div - 1) << 0;
|
||||
} else {
|
||||
regv |= (selected_coeff->bclk_div) << 0;
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG06, regv), TAG, "I2C read/write error");
|
||||
|
||||
/* register 0x07 */
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_CLK_MANAGER_REG07, ®v), TAG, "I2C read/write error");
|
||||
regv &= 0xC0;
|
||||
regv |= selected_coeff->lrck_h << 0;
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG07, regv), TAG, "I2C read/write error");
|
||||
|
||||
/* register 0x08 */
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG08, selected_coeff->lrck_l), TAG, "I2C read/write error");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t es8311_clock_config(es8311_handle_t dev, const es8311_clock_config_t *const clk_cfg, es8311_resolution_t res)
|
||||
{
|
||||
uint8_t reg06;
|
||||
uint8_t reg01 = 0x3F; // Enable all clocks
|
||||
int mclk_hz;
|
||||
|
||||
/* Select clock source for internal MCLK and determine its frequency */
|
||||
if (clk_cfg->mclk_from_mclk_pin) {
|
||||
mclk_hz = clk_cfg->mclk_frequency;
|
||||
} else {
|
||||
mclk_hz = clk_cfg->sample_frequency * (int)res * 2;
|
||||
reg01 |= BIT(7); // Select BCLK (a.k.a. SCK) pin
|
||||
}
|
||||
|
||||
if (clk_cfg->mclk_inverted) {
|
||||
reg01 |= BIT(6); // Invert MCLK pin
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG01, reg01), TAG, "I2C read/write error");
|
||||
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_CLK_MANAGER_REG06, ®06), TAG, "I2C read/write error");
|
||||
if (clk_cfg->sclk_inverted) {
|
||||
reg06 |= BIT(5);
|
||||
} else {
|
||||
reg06 &= ~BIT(5);
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG06, reg06), TAG, "I2C read/write error");
|
||||
|
||||
/* Configure clock dividers */
|
||||
return es8311_sample_frequency_config(dev, mclk_hz, clk_cfg->sample_frequency);
|
||||
}
|
||||
|
||||
static esp_err_t es8311_resolution_config(const es8311_resolution_t res, uint8_t *reg)
|
||||
{
|
||||
switch (res) {
|
||||
case ES8311_RESOLUTION_16:
|
||||
*reg |= (3 << 2);
|
||||
break;
|
||||
case ES8311_RESOLUTION_18:
|
||||
*reg |= (2 << 2);
|
||||
break;
|
||||
case ES8311_RESOLUTION_20:
|
||||
*reg |= (1 << 2);
|
||||
break;
|
||||
case ES8311_RESOLUTION_24:
|
||||
*reg |= (0 << 2);
|
||||
break;
|
||||
case ES8311_RESOLUTION_32:
|
||||
*reg |= (4 << 2);
|
||||
break;
|
||||
default:
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t es8311_fmt_config(es8311_handle_t dev, const es8311_resolution_t res_in, const es8311_resolution_t res_out)
|
||||
{
|
||||
uint8_t reg09 = 0; // SDP In
|
||||
uint8_t reg0a = 0; // SDP Out
|
||||
|
||||
ESP_LOGI(TAG, "ES8311 in Slave mode and I2S format");
|
||||
uint8_t reg00;
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_RESET_REG00, ®00), TAG, "I2C read/write error");
|
||||
reg00 &= 0xBF;
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_RESET_REG00, reg00), TAG, "I2C read/write error"); // Slave serial port - default
|
||||
|
||||
/* Setup SDP In and Out resolution */
|
||||
es8311_resolution_config(res_in, ®09);
|
||||
es8311_resolution_config(res_out, ®0a);
|
||||
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SDPIN_REG09, reg09), TAG, "I2C read/write error");
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SDPOUT_REG0A, reg0a), TAG, "I2C read/write error");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t es8311_microphone_config(es8311_handle_t dev, bool digital_mic)
|
||||
{
|
||||
uint8_t reg14 = 0x1A; // enable analog MIC and max PGA gain
|
||||
|
||||
/* PDM digital microphone enable or disable */
|
||||
if (digital_mic) {
|
||||
reg14 |= BIT(6);
|
||||
}
|
||||
es8311_write_reg(dev, ES8311_ADC_REG17, 0xC8); // Set ADC gain @todo move this to ADC config section
|
||||
|
||||
return es8311_write_reg(dev, ES8311_SYSTEM_REG14, reg14);
|
||||
}
|
||||
|
||||
esp_err_t es8311_init(es8311_handle_t dev, const es8311_clock_config_t *const clk_cfg, const es8311_resolution_t res_in, const es8311_resolution_t res_out)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(
|
||||
(clk_cfg->sample_frequency >= 8000) && (clk_cfg->sample_frequency <= 96000),
|
||||
ESP_ERR_INVALID_ARG, TAG, "ES8311 init needs frequency in interval [8000; 96000] Hz"
|
||||
);
|
||||
if (!clk_cfg->mclk_from_mclk_pin) {
|
||||
ESP_RETURN_ON_FALSE(res_out == res_in, ESP_ERR_INVALID_ARG, TAG, "Resolution IN/OUT must be equal if MCLK is taken from SCK pin");
|
||||
}
|
||||
|
||||
|
||||
/* Reset ES8311 to its default */
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_RESET_REG00, 0x1F), TAG, "I2C read/write error");
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_RESET_REG00, 0x00), TAG, "I2C read/write error");
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_RESET_REG00, 0x80), TAG, "I2C read/write error"); // Power-on command
|
||||
|
||||
/* Setup clock: source, polarity and clock dividers */
|
||||
ESP_RETURN_ON_ERROR(es8311_clock_config(dev, clk_cfg, res_out), TAG, "");
|
||||
|
||||
/* Setup audio format (fmt): master/slave, resolution, I2S */
|
||||
ESP_RETURN_ON_ERROR(es8311_fmt_config(dev, res_in, res_out), TAG, "");
|
||||
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SYSTEM_REG0D, 0x01), TAG, "I2C read/write error"); // Power up analog circuitry - NOT default
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SYSTEM_REG0E, 0x02), TAG, "I2C read/write error"); // Enable analog PGA, enable ADC modulator - NOT default
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SYSTEM_REG12, 0x00), TAG, "I2C read/write error"); // power-up DAC - NOT default
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SYSTEM_REG13, 0x10), TAG, "I2C read/write error"); // Enable output to HP drive - NOT default
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_ADC_REG1C, 0x6A), TAG, "I2C read/write error"); // ADC Equalizer bypass, cancel DC offset in digital domain
|
||||
ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_DAC_REG37, 0x08), TAG, "I2C read/write error"); // Bypass DAC equalizer - NOT default
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void es8311_delete(es8311_handle_t dev)
|
||||
{
|
||||
free(dev);
|
||||
}
|
||||
|
||||
esp_err_t es8311_voice_volume_set(es8311_handle_t dev, int volume, int *volume_set)
|
||||
{
|
||||
if (volume < 0) {
|
||||
volume = 0;
|
||||
} else if (volume > 100) {
|
||||
volume = 100;
|
||||
}
|
||||
|
||||
int reg32;
|
||||
if (volume == 0) {
|
||||
reg32 = 0;
|
||||
} else {
|
||||
reg32 = ((volume) * 256 / 100) - 1;
|
||||
}
|
||||
|
||||
// provide user with real volume set
|
||||
if (volume_set != NULL) {
|
||||
*volume_set = volume;
|
||||
}
|
||||
return es8311_write_reg(dev, ES8311_DAC_REG32, reg32);
|
||||
}
|
||||
|
||||
esp_err_t es8311_voice_volume_get(es8311_handle_t dev, int *volume)
|
||||
{
|
||||
uint8_t reg32;
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_DAC_REG32, ®32), TAG, "I2C read/write error");
|
||||
|
||||
if (reg32 == 0) {
|
||||
*volume = 0;
|
||||
} else {
|
||||
*volume = ((reg32 * 100) / 256) + 1;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t es8311_voice_mute(es8311_handle_t dev, bool mute)
|
||||
{
|
||||
uint8_t reg31;
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_DAC_REG31, ®31), TAG, "I2C read/write error");
|
||||
|
||||
if (mute) {
|
||||
reg31 |= BIT(6) | BIT(5);
|
||||
} else {
|
||||
reg31 &= ~(BIT(6) | BIT(5));
|
||||
}
|
||||
|
||||
return es8311_write_reg(dev, ES8311_DAC_REG31, reg31);
|
||||
}
|
||||
|
||||
esp_err_t es8311_microphone_gain_set(es8311_handle_t dev, es8311_mic_gain_t gain_db)
|
||||
{
|
||||
return es8311_write_reg(dev, ES8311_ADC_REG16, gain_db); // ADC gain scale up
|
||||
}
|
||||
|
||||
esp_err_t es8311_voice_fade(es8311_handle_t dev, const es8311_fade_t fade)
|
||||
{
|
||||
uint8_t reg37;
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_DAC_REG37, ®37), TAG, "I2C read/write error");
|
||||
reg37 &= 0x0F;
|
||||
reg37 |= (fade << 4);
|
||||
return es8311_write_reg(dev, ES8311_DAC_REG37, reg37);
|
||||
}
|
||||
|
||||
esp_err_t es8311_microphone_fade(es8311_handle_t dev, const es8311_fade_t fade)
|
||||
{
|
||||
uint8_t reg15;
|
||||
ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_ADC_REG15, ®15), TAG, "I2C read/write error");
|
||||
reg15 &= 0x0F;
|
||||
reg15 |= (fade << 4);
|
||||
return es8311_write_reg(dev, ES8311_ADC_REG15, reg15);
|
||||
}
|
||||
|
||||
void es8311_register_dump(es8311_handle_t dev)
|
||||
{
|
||||
for (int reg = 0; reg < 0x4A; reg++) {
|
||||
uint8_t value;
|
||||
ESP_ERROR_CHECK(es8311_read_reg(dev, reg, &value));
|
||||
printf("REG:%02x: %02x", reg, value);
|
||||
}
|
||||
}
|
||||
|
||||
es8311_handle_t es8311_create(const unsigned int port, const uint16_t dev_addr)
|
||||
{
|
||||
es8311_dev_t *sensor = (es8311_dev_t *) calloc(1, sizeof(es8311_dev_t));
|
||||
sensor->port = port;
|
||||
sensor->dev_addr = dev_addr;
|
||||
return (es8311_handle_t) sensor;
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ES8311 driver
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_types.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
/* ES8311 address: CE pin low - 0x18, CE pin high - 0x19 */
|
||||
#define ES8311_ADDRRES_0 0x18u // Leaving this here for backward compatibility
|
||||
#define ES8311_ADDRESS_0 0x18u
|
||||
#define ES8311_ADDRESS_1 0x19u
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void *es8311_handle_t;
|
||||
|
||||
typedef enum {
|
||||
ES8311_MIC_GAIN_MIN = -1,
|
||||
ES8311_MIC_GAIN_0DB,
|
||||
ES8311_MIC_GAIN_6DB,
|
||||
ES8311_MIC_GAIN_12DB,
|
||||
ES8311_MIC_GAIN_18DB,
|
||||
ES8311_MIC_GAIN_24DB,
|
||||
ES8311_MIC_GAIN_30DB,
|
||||
ES8311_MIC_GAIN_36DB,
|
||||
ES8311_MIC_GAIN_42DB,
|
||||
ES8311_MIC_GAIN_MAX
|
||||
} es8311_mic_gain_t;
|
||||
|
||||
typedef enum {
|
||||
ES8311_FADE_OFF = 0,
|
||||
ES8311_FADE_4LRCK, // 4LRCK means ramp 0.25dB/4LRCK
|
||||
ES8311_FADE_8LRCK,
|
||||
ES8311_FADE_16LRCK,
|
||||
ES8311_FADE_32LRCK,
|
||||
ES8311_FADE_64LRCK,
|
||||
ES8311_FADE_128LRCK,
|
||||
ES8311_FADE_256LRCK,
|
||||
ES8311_FADE_512LRCK,
|
||||
ES8311_FADE_1024LRCK,
|
||||
ES8311_FADE_2048LRCK,
|
||||
ES8311_FADE_4096LRCK,
|
||||
ES8311_FADE_8192LRCK,
|
||||
ES8311_FADE_16384LRCK,
|
||||
ES8311_FADE_32768LRCK,
|
||||
ES8311_FADE_65536LRCK
|
||||
} es8311_fade_t;
|
||||
|
||||
typedef enum es8311_resolution_t {
|
||||
ES8311_RESOLUTION_16 = 16,
|
||||
ES8311_RESOLUTION_18 = 18,
|
||||
ES8311_RESOLUTION_20 = 20,
|
||||
ES8311_RESOLUTION_24 = 24,
|
||||
ES8311_RESOLUTION_32 = 32
|
||||
} es8311_resolution_t;
|
||||
|
||||
typedef struct es8311_clock_config_t {
|
||||
bool mclk_inverted;
|
||||
bool sclk_inverted;
|
||||
bool mclk_from_mclk_pin; // true: from MCLK pin (pin no. 2), false: from SCLK pin (pin no. 6)
|
||||
int mclk_frequency; // This parameter is ignored if MCLK is taken from SCLK pin
|
||||
int sample_frequency; // in Hz
|
||||
} es8311_clock_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize ES8311
|
||||
*
|
||||
* There are two ways of providing Master Clock (MCLK) signal to ES8311 in Slave Mode:
|
||||
* 1. From MCLK pin:
|
||||
* For flexible scenarios. A clock signal from I2S master is routed to MCLK pin.
|
||||
* Its frequency must be defined in clk_cfg->mclk_frequency parameter.
|
||||
* 2. From SCLK pin:
|
||||
* For simpler scenarios. ES8311 takes its clock from SCK pin. MCLK pin does not have to be connected.
|
||||
* In this case, res_in must equal res_out; clk_cfg->mclk_frequency parameter is ignored
|
||||
* and MCLK is calculated as MCLK = clk_cfg->sample_frequency * res_out * 2.
|
||||
* Not all sampling frequencies are supported in this mode.
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
* @param[in] clk_cfg Clock configuration
|
||||
* @param[in] res_in Input serial port resolution
|
||||
* @param[in] res_out Output serial port resolution
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - ESP_ERR_INVALID_ARG Sample frequency or resolution invalid
|
||||
* - Else fail
|
||||
*/
|
||||
esp_err_t es8311_init(es8311_handle_t dev, const es8311_clock_config_t *const clk_cfg, const es8311_resolution_t res_in,
|
||||
const es8311_resolution_t res_out);
|
||||
|
||||
/**
|
||||
* @brief Set output volume
|
||||
*
|
||||
* Volume paramter out of <0, 100> interval will be truncated.
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
* @param[in] volume Set volume (0 ~ 100)
|
||||
* @param[out] volume_set Volume that was set. Same as volume, unless volume is outside of <0, 100> interval.
|
||||
* This parameter can be set to NULL, if user does not need this information.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - Else fail
|
||||
*/
|
||||
esp_err_t es8311_voice_volume_set(es8311_handle_t dev, int volume, int *volume_set);
|
||||
|
||||
/**
|
||||
* @brief Get output volume
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
* @param[out] volume get volume (0 ~ 100)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - Else fail
|
||||
*/
|
||||
esp_err_t es8311_voice_volume_get(es8311_handle_t dev, int *volume);
|
||||
|
||||
/**
|
||||
* @brief Print out ES8311 register content
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
*/
|
||||
void es8311_register_dump(es8311_handle_t dev);
|
||||
|
||||
/**
|
||||
* @brief Mute ES8311 output
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
* @param[in] enable true: mute, false: don't mute
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - Else fail
|
||||
*/
|
||||
esp_err_t es8311_voice_mute(es8311_handle_t dev, bool enable);
|
||||
|
||||
/**
|
||||
* @brief Set Microphone gain
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
* @param[in] gain_db Microphone gain
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - Else fail
|
||||
*/
|
||||
esp_err_t es8311_microphone_gain_set(es8311_handle_t dev, es8311_mic_gain_t gain_db);
|
||||
|
||||
/**
|
||||
* @brief Configure microphone
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
* @param[in] digital_mic Set to true for digital microphone
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - Else fail
|
||||
*/
|
||||
esp_err_t es8311_microphone_config(es8311_handle_t dev, bool digital_mic);
|
||||
|
||||
/**
|
||||
* @brief Configure sampling frequency
|
||||
*
|
||||
* @note This function is called by es8311_init().
|
||||
* Call this function explicitly only if you want to change sample frequency during runtime.
|
||||
* @param dev ES8311 handle
|
||||
* @param[in] mclk_frequency MCLK frequency in [Hz] (MCLK or SCLK pin, depending on bit register01[7])
|
||||
* @param[in] sample_frequency Required sample frequency in [Hz], e.g. 44100, 22050...
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - ESP_ERR_INVALID_ARG cannot set clock dividers for given MCLK and sampling frequency
|
||||
* - Else I2C read/write error
|
||||
*/
|
||||
esp_err_t es8311_sample_frequency_config(es8311_handle_t dev, int mclk_frequency, int sample_frequency);
|
||||
|
||||
/**
|
||||
* @brief Configure fade in/out for ADC: voice
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
* @param[in] fade Fade ramp rate
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - Else I2C read/write error
|
||||
*/
|
||||
esp_err_t es8311_voice_fade(es8311_handle_t dev, const es8311_fade_t fade);
|
||||
|
||||
/**
|
||||
* @brief Configure fade in/out for DAC: microphone
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
* @param[in] fade Fade ramp rate
|
||||
* @return
|
||||
* - ESP_OK success
|
||||
* - Else I2C read/write error
|
||||
*/
|
||||
esp_err_t es8311_microphone_fade(es8311_handle_t dev, const es8311_fade_t fade);
|
||||
|
||||
/**
|
||||
* @brief Create ES8311 object and return its handle
|
||||
*
|
||||
* @param[in] port I2C port number
|
||||
* @param[in] dev_addr I2C device address of ES8311
|
||||
*
|
||||
* @return
|
||||
* - NULL Fail
|
||||
* - Others Success
|
||||
*/
|
||||
es8311_handle_t es8311_create(const unsigned int port, const uint16_t dev_addr);
|
||||
|
||||
/**
|
||||
* @brief Delete ES8311 object
|
||||
*
|
||||
* @param dev ES8311 handle
|
||||
*/
|
||||
void es8311_delete(es8311_handle_t dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* ES8311_REGISTER NAME_REG_REGISTER ADDRESS
|
||||
*/
|
||||
#define ES8311_RESET_REG00 0x00 /*reset digital,csm,clock manager etc.*/
|
||||
|
||||
/*
|
||||
* Clock Scheme Register definition
|
||||
*/
|
||||
#define ES8311_CLK_MANAGER_REG01 0x01 /* select clk src for mclk, enable clock for codec */
|
||||
#define ES8311_CLK_MANAGER_REG02 0x02 /* clk divider and clk multiplier */
|
||||
#define ES8311_CLK_MANAGER_REG03 0x03 /* adc fsmode and osr */
|
||||
#define ES8311_CLK_MANAGER_REG04 0x04 /* dac osr */
|
||||
#define ES8311_CLK_MANAGER_REG05 0x05 /* clk divier for adc and dac */
|
||||
#define ES8311_CLK_MANAGER_REG06 0x06 /* bclk inverter and divider */
|
||||
#define ES8311_CLK_MANAGER_REG07 0x07 /* tri-state, lrck divider */
|
||||
#define ES8311_CLK_MANAGER_REG08 0x08 /* lrck divider */
|
||||
/*
|
||||
* SDP
|
||||
*/
|
||||
#define ES8311_SDPIN_REG09 0x09 /* dac serial digital port */
|
||||
#define ES8311_SDPOUT_REG0A 0x0A /* adc serial digital port */
|
||||
/*
|
||||
* SYSTEM
|
||||
*/
|
||||
#define ES8311_SYSTEM_REG0B 0x0B /* system */
|
||||
#define ES8311_SYSTEM_REG0C 0x0C /* system */
|
||||
#define ES8311_SYSTEM_REG0D 0x0D /* system, power up/down */
|
||||
#define ES8311_SYSTEM_REG0E 0x0E /* system, power up/down */
|
||||
#define ES8311_SYSTEM_REG0F 0x0F /* system, low power */
|
||||
#define ES8311_SYSTEM_REG10 0x10 /* system */
|
||||
#define ES8311_SYSTEM_REG11 0x11 /* system */
|
||||
#define ES8311_SYSTEM_REG12 0x12 /* system, Enable DAC */
|
||||
#define ES8311_SYSTEM_REG13 0x13 /* system */
|
||||
#define ES8311_SYSTEM_REG14 0x14 /* system, select DMIC, select analog pga gain */
|
||||
/*
|
||||
* ADC
|
||||
*/
|
||||
#define ES8311_ADC_REG15 0x15 /* ADC, adc ramp rate, dmic sense */
|
||||
#define ES8311_ADC_REG16 0x16 /* ADC */
|
||||
#define ES8311_ADC_REG17 0x17 /* ADC, volume */
|
||||
#define ES8311_ADC_REG18 0x18 /* ADC, alc enable and winsize */
|
||||
#define ES8311_ADC_REG19 0x19 /* ADC, alc maxlevel */
|
||||
#define ES8311_ADC_REG1A 0x1A /* ADC, alc automute */
|
||||
#define ES8311_ADC_REG1B 0x1B /* ADC, alc automute, adc hpf s1 */
|
||||
#define ES8311_ADC_REG1C 0x1C /* ADC, equalizer, hpf s2 */
|
||||
/*
|
||||
* DAC
|
||||
*/
|
||||
#define ES8311_DAC_REG31 0x31 /* DAC, mute */
|
||||
#define ES8311_DAC_REG32 0x32 /* DAC, volume */
|
||||
#define ES8311_DAC_REG33 0x33 /* DAC, offset */
|
||||
#define ES8311_DAC_REG34 0x34 /* DAC, drc enable, drc winsize */
|
||||
#define ES8311_DAC_REG35 0x35 /* DAC, drc maxlevel, minilevel */
|
||||
#define ES8311_DAC_REG37 0x37 /* DAC, ramprate */
|
||||
/*
|
||||
*GPIO
|
||||
*/
|
||||
#define ES8311_GPIO_REG44 0x44 /* GPIO, dac2adc for test */
|
||||
#define ES8311_GP_REG45 0x45 /* GP CONTROL */
|
||||
/*
|
||||
* CHIP
|
||||
*/
|
||||
#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */
|
||||
#define ES8311_CHD2_REGFE 0xFE /* CHIP ID2 */
|
||||
#define ES8311_CHVER_REGFF 0xFF /* VERSION */
|
||||
#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */
|
||||
|
||||
#define ES8311_MAX_REGISTER 0xFF
|
||||
@@ -0,0 +1,272 @@
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <math.h>
|
||||
#include "ESP_I2S.h"
|
||||
#include "esp_check.h"
|
||||
#include "Arduino_GFX_Library.h"
|
||||
#include "pin_config.h"
|
||||
#include "TouchDrvCSTXXX.hpp"
|
||||
#include "SensorQMI8658.hpp"
|
||||
#include "es8311.h"
|
||||
#include "es7210.h"
|
||||
|
||||
static const int DISP_W = 480;
|
||||
static const int DISP_H = 480;
|
||||
static const int SAFE = 20;
|
||||
static const uint32_t SAMPLE_RATE = 16000;
|
||||
static const size_t REC_SECONDS = 2;
|
||||
static const size_t AUDIO_BYTES = SAMPLE_RATE * REC_SECONDS * 2 * 2;
|
||||
static const int I2C_NUM = 0;
|
||||
|
||||
I2SClass i2s;
|
||||
TouchDrvCST92xx touch;
|
||||
SensorQMI8658 qmi;
|
||||
IMUdata acc;
|
||||
|
||||
Arduino_DataBus *bus = new Arduino_ESP32QSPI(LCD_CS, LCD_SCLK, LCD_SDIO0, LCD_SDIO1, LCD_SDIO2, LCD_SDIO3);
|
||||
Arduino_CO5300 *gfx = new Arduino_CO5300(bus, LCD_RESET, 0, DISP_W, DISP_H, 0, 0, 0, 0);
|
||||
|
||||
uint8_t *audioBuf = nullptr;
|
||||
size_t audioLen = 0;
|
||||
bool touchIrq = false;
|
||||
String statusLine = "Ready";
|
||||
String tiltLine = "Tilt: FLAT";
|
||||
String audioLine = "Audio: empty";
|
||||
uint32_t lastTiltMs = 0;
|
||||
bool micReady = false;
|
||||
|
||||
void IRAM_ATTR onTouchInt() { touchIrq = true; }
|
||||
|
||||
esp_err_t codecInit() {
|
||||
es8311_handle_t esHandle = es8311_create(I2C_NUM, ES8311_ADDRRES_0);
|
||||
ESP_RETURN_ON_FALSE(esHandle, ESP_FAIL, "AV", "es8311 create failed");
|
||||
const es8311_clock_config_t clk = {
|
||||
.mclk_inverted = false,
|
||||
.sclk_inverted = false,
|
||||
.mclk_from_mclk_pin = true,
|
||||
.mclk_frequency = SAMPLE_RATE * 256,
|
||||
.sample_frequency = SAMPLE_RATE,
|
||||
};
|
||||
ESP_ERROR_CHECK(es8311_init(esHandle, &clk, ES8311_RESOLUTION_16, ES8311_RESOLUTION_16));
|
||||
ESP_RETURN_ON_ERROR(es8311_sample_frequency_config(esHandle, clk.mclk_frequency, clk.sample_frequency), "AV", "clock cfg failed");
|
||||
ESP_RETURN_ON_ERROR(es8311_microphone_config(esHandle, false), "AV", "mic cfg failed");
|
||||
ESP_RETURN_ON_ERROR(es8311_voice_volume_set(esHandle, 90, NULL), "AV", "volume failed");
|
||||
ESP_RETURN_ON_ERROR(es8311_microphone_gain_set(esHandle, (es8311_mic_gain_t)6), "AV", "mic gain failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t micInit() {
|
||||
audio_hal_codec_config_t cfg = {
|
||||
.adc_input = AUDIO_HAL_ADC_INPUT_ALL,
|
||||
.codec_mode = AUDIO_HAL_CODEC_MODE_ENCODE,
|
||||
.i2s_iface = {
|
||||
.mode = AUDIO_HAL_MODE_SLAVE,
|
||||
.fmt = AUDIO_HAL_I2S_NORMAL,
|
||||
.samples = AUDIO_HAL_16K_SAMPLES,
|
||||
.bits = AUDIO_HAL_BIT_LENGTH_16BITS,
|
||||
},
|
||||
};
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret |= es7210_adc_init(&Wire, &cfg);
|
||||
ret |= es7210_adc_config_i2s(cfg.codec_mode, &cfg.i2s_iface);
|
||||
ret |= es7210_adc_set_gain(
|
||||
(es7210_input_mics_t)(ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2),
|
||||
(es7210_gain_value_t)GAIN_24DB);
|
||||
ret |= es7210_adc_set_gain(
|
||||
(es7210_input_mics_t)(ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4),
|
||||
(es7210_gain_value_t)GAIN_24DB);
|
||||
ret |= es7210_adc_ctrl_state(cfg.codec_mode, AUDIO_HAL_CTRL_START);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void drawStaticUi() {
|
||||
gfx->fillScreen(RGB565_BLACK);
|
||||
gfx->setTextSize(2);
|
||||
gfx->setTextColor(RGB565_WHITE);
|
||||
gfx->setCursor(SAFE + 16, SAFE + 10);
|
||||
gfx->println("Simple AV + Tilt Test");
|
||||
|
||||
gfx->drawRect(24, 76, 132, 64, RGB565_GREEN);
|
||||
gfx->setCursor(44, 100);
|
||||
gfx->println("RECORD");
|
||||
|
||||
gfx->drawRect(174, 76, 132, 64, RGB565_YELLOW);
|
||||
gfx->setCursor(212, 100);
|
||||
gfx->println("PLAY");
|
||||
|
||||
gfx->drawRect(324, 76, 132, 64, RGB565_CYAN);
|
||||
gfx->setCursor(360, 100);
|
||||
gfx->println("BEEP");
|
||||
}
|
||||
|
||||
void drawDynamic() {
|
||||
gfx->fillRect(20, 166, 440, 140, RGB565_BLACK);
|
||||
gfx->setTextSize(2);
|
||||
gfx->setTextColor(RGB565_WHITE);
|
||||
gfx->setCursor(28, 178);
|
||||
gfx->println(statusLine);
|
||||
gfx->setTextColor(RGB565_CYAN);
|
||||
gfx->setCursor(28, 212);
|
||||
gfx->println(tiltLine);
|
||||
gfx->setTextColor(RGB565_ORANGE);
|
||||
gfx->setCursor(28, 246);
|
||||
gfx->println(audioLine);
|
||||
gfx->drawRect(28, 278, 300, 20, RGB565_DARKGREY);
|
||||
}
|
||||
|
||||
void updateTilt() {
|
||||
if (!qmi.getDataReady()) return;
|
||||
if (!qmi.getAccelerometer(acc.x, acc.y, acc.z)) return;
|
||||
String next = "Tilt: FLAT";
|
||||
if (acc.x > 0.8f) next = "Tilt: RIGHT";
|
||||
else if (acc.x < -0.8f) next = "Tilt: LEFT";
|
||||
else if (acc.y > 0.8f) next = "Tilt: UP";
|
||||
else if (acc.y < -0.8f) next = "Tilt: DOWN";
|
||||
if (next != tiltLine) {
|
||||
tiltLine = next;
|
||||
drawDynamic();
|
||||
}
|
||||
}
|
||||
|
||||
void playBeep() {
|
||||
static int16_t tone[16000];
|
||||
for (int i = 0; i < 8000; ++i) {
|
||||
float s = sinf(2.0f * 3.1415926f * 440.0f * ((float)i / SAMPLE_RATE));
|
||||
int16_t v = (int16_t)(s * 6000.0f);
|
||||
tone[i * 2] = v;
|
||||
tone[i * 2 + 1] = v;
|
||||
}
|
||||
statusLine = "Playing test beep...";
|
||||
drawDynamic();
|
||||
i2s.write((uint8_t *)tone, 8000 * 2 * sizeof(int16_t));
|
||||
statusLine = "Beep done";
|
||||
drawDynamic();
|
||||
}
|
||||
|
||||
void recordAudio() {
|
||||
if (!audioBuf || !micReady) {
|
||||
statusLine = "No audio buffer";
|
||||
drawDynamic();
|
||||
return;
|
||||
}
|
||||
statusLine = "Recording 2 sec...";
|
||||
drawDynamic();
|
||||
size_t total = 0;
|
||||
uint16_t peakAll = 0;
|
||||
while (total < AUDIO_BYTES) {
|
||||
size_t chunk = min((size_t)4096, AUDIO_BYTES - total);
|
||||
size_t got = i2s.readBytes((char *)(audioBuf + total), chunk);
|
||||
if (!got) break;
|
||||
total += got;
|
||||
|
||||
int16_t *s = (int16_t *)(audioBuf + total - got);
|
||||
size_t n = got / sizeof(int16_t);
|
||||
uint16_t peak = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
uint16_t a = (uint16_t)abs((int)s[i]);
|
||||
if (a > peak) peak = a;
|
||||
}
|
||||
if (peak > peakAll) peakAll = peak;
|
||||
|
||||
int bar = map((int)peak, 0, 20000, 0, 296);
|
||||
if (bar < 0) bar = 0;
|
||||
if (bar > 296) bar = 296;
|
||||
gfx->fillRect(30, 280, 296, 16, RGB565_BLACK);
|
||||
uint16_t c = (peak > 12000) ? RGB565_RED : ((peak > 6000) ? RGB565_YELLOW : RGB565_GREEN);
|
||||
gfx->fillRect(30, 280, bar, 16, c);
|
||||
}
|
||||
audioLen = total;
|
||||
audioLine = (total > 0) ? "Audio: recorded" : "Audio: empty";
|
||||
if (total > 0) {
|
||||
statusLine = String("Record done, peak=") + String((int)peakAll);
|
||||
} else {
|
||||
statusLine = "Record failed";
|
||||
}
|
||||
drawDynamic();
|
||||
}
|
||||
|
||||
void playAudio() {
|
||||
if (!audioLen) {
|
||||
statusLine = "No recording. Try BEEP.";
|
||||
drawDynamic();
|
||||
return;
|
||||
}
|
||||
statusLine = "Playing recording...";
|
||||
drawDynamic();
|
||||
size_t wrote = i2s.write(audioBuf, audioLen);
|
||||
statusLine = (wrote > 0) ? "Play done" : "Play failed";
|
||||
drawDynamic();
|
||||
}
|
||||
|
||||
void handleTouch() {
|
||||
if (!touchIrq) return;
|
||||
touchIrq = false;
|
||||
int16_t x[1];
|
||||
int16_t y[1];
|
||||
uint8_t touched = touch.getPoint(x, y, 1);
|
||||
if (!touched) return;
|
||||
|
||||
int px = x[0];
|
||||
int py = y[0];
|
||||
if (py < 76 || py > 140) return;
|
||||
if (px >= 24 && px <= 156) recordAudio();
|
||||
else if (px >= 174 && px <= 306) playAudio();
|
||||
else if (px >= 324 && px <= 456) playBeep();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Wire.begin(IIC_SDA, IIC_SCL);
|
||||
|
||||
pinMode(PA, OUTPUT);
|
||||
digitalWrite(PA, HIGH);
|
||||
|
||||
if (!gfx->begin()) Serial.println("gfx begin failed");
|
||||
bus->writeC8D8(0x36, 0xA0);
|
||||
gfx->setBrightness(200);
|
||||
|
||||
pinMode(TP_RST, OUTPUT);
|
||||
digitalWrite(TP_RST, LOW);
|
||||
delay(30);
|
||||
digitalWrite(TP_RST, HIGH);
|
||||
delay(50);
|
||||
touch.setPins(TP_RST, TP_INT);
|
||||
touch.begin(Wire, 0x5A, IIC_SDA, IIC_SCL);
|
||||
touch.setMaxCoordinates(480, 480);
|
||||
touch.setSwapXY(true);
|
||||
touch.setMirrorXY(true, false);
|
||||
attachInterrupt(TP_INT, onTouchInt, FALLING);
|
||||
|
||||
if (qmi.begin(Wire, QMI8658_L_SLAVE_ADDRESS, IIC_SDA, IIC_SCL)) {
|
||||
qmi.configAccelerometer(SensorQMI8658::ACC_RANGE_4G, SensorQMI8658::ACC_ODR_1000Hz, SensorQMI8658::LPF_MODE_0);
|
||||
qmi.enableAccelerometer();
|
||||
} else {
|
||||
tiltLine = "Tilt: IMU not found";
|
||||
}
|
||||
|
||||
i2s.setPins(9, 45, 8, 10, 42);
|
||||
if (!i2s.begin(I2S_MODE_STD, SAMPLE_RATE, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO, I2S_STD_SLOT_BOTH)) {
|
||||
statusLine = "I2S init failed";
|
||||
} else if (codecInit() != ESP_OK) {
|
||||
statusLine = "ES8311 init failed";
|
||||
} else if (micInit() != ESP_OK) {
|
||||
statusLine = "ES7210 init failed";
|
||||
} else {
|
||||
micReady = true;
|
||||
}
|
||||
|
||||
audioBuf = (uint8_t *)ps_malloc(AUDIO_BYTES);
|
||||
if (!audioBuf) statusLine = "No PSRAM buffer";
|
||||
|
||||
drawStaticUi();
|
||||
drawDynamic();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
handleTouch();
|
||||
if (millis() - lastTiltMs > 120) {
|
||||
lastTiltMs = millis();
|
||||
updateTilt();
|
||||
}
|
||||
delay(5);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
# Test Sketches
|
||||
|
||||
Набор старых отдельных диагностических скетчей для `Waveshare ESP32-S3-Touch-AMOLED-2.16`.
|
||||
|
||||
Скетчи в этой папке нужны для быстрой проверки конкретных гипотез и не являются основным UI проекта.
|
||||
Основной скетч сейчас лежит в `main-device/shine_homeserver_main/`.
|
||||
|
||||
## Список
|
||||
|
||||
- `gfx_text_render_test/` - проверка рендера текста через `Arduino_GFX` и сравнение с `U8g2`
|
||||
- `gfx_button_layout_test/` - проверка геометрии кнопок, особенно нижних рядов и широких кнопок
|
||||
- `lvgl_basic_test/` - минимальный тест `LVGL` с заголовком, текстом и кнопками
|
||||
- `lvgl_interaction_test/` - расширенный тест `LVGL` с 9 кнопками, touch-вводом и статусом нажатия
|
||||
- `lvgl_touch_debug_test/` - диагностика touch: сырые координаты, точка касания и одна большая кнопка `LVGL`
|
||||
- `lvgl_official_based_test/` - минимальный наш экран поверх максимально близкой к официальному `05_LVGL_Widgets` инициализации
|
||||
- `lvgl_subserver_touch_test/` - старый гибридный тест: `LVGL`-экран с инициализацией дисплея и чтением touch из старого `shine_homeserver_ui`; подтверждён на реальном устройстве
|
||||
- `lvgl_russian_font_test/` - тест кастомного кириллического `LVGL`-шрифта с русскими кнопками, длинными строками и рабочим touch
|
||||
- `lvgl_nav_minimal_test/` - старое тестовое имя, этот скетч перенесён в `shine_homeserver_main/` и теперь является основным
|
||||
|
||||
## Запуск
|
||||
|
||||
Использовать через `burn.sh`:
|
||||
|
||||
- `./burn.sh gfx-text-test`
|
||||
- `./burn.sh gfx-layout-test`
|
||||
- `./burn.sh lvgl-basic-test`
|
||||
- `./burn.sh lvgl-interaction-test`
|
||||
- `./burn.sh lvgl-touch-debug-test`
|
||||
- `./burn.sh lvgl-official-based-test`
|
||||
- `./burn.sh lvgl-subserver-touch-test`
|
||||
- `./burn.sh lvgl-russian-font-test`
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <Arduino_GFX_Library.h>
|
||||
|
||||
#define PIN_LCD_CS 12
|
||||
#define PIN_LCD_SCLK 38
|
||||
#define PIN_LCD_D0 4
|
||||
#define PIN_LCD_D1 5
|
||||
#define PIN_LCD_D2 6
|
||||
#define PIN_LCD_D3 7
|
||||
#define PIN_LCD_RST 2
|
||||
#define PIN_I2C_SDA 15
|
||||
#define PIN_I2C_SCL 14
|
||||
|
||||
#define DISP_W 480
|
||||
#define DISP_H 480
|
||||
|
||||
#define C_BG 0x0841u
|
||||
#define C_PANEL 0x1082u
|
||||
#define C_CARD 0x18C3u
|
||||
#define C_BORDER 0x39C7u
|
||||
#define C_TEXT 0xFFFFu
|
||||
#define C_OK 0x3666u
|
||||
#define C_WARN 0xECA0u
|
||||
#define C_BUTTON 0x2145u
|
||||
#define C_BUTTON2 0x3186u
|
||||
|
||||
Arduino_DataBus *gBus = new Arduino_ESP32QSPI(
|
||||
PIN_LCD_CS, PIN_LCD_SCLK, PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3);
|
||||
Arduino_CO5300 *gfx = new Arduino_CO5300(
|
||||
gBus, PIN_LCD_RST, 0, DISP_W, DISP_H, 0, 0, 0, 0);
|
||||
|
||||
static void drawPanel(int x, int y, int w, int h, uint16_t fill, uint16_t border, int radius = 10) {
|
||||
gfx->fillRoundRect(x, y, w, h, radius, fill);
|
||||
gfx->drawRoundRect(x, y, w, h, radius, border);
|
||||
}
|
||||
|
||||
static void drawText(int x, int y, const char *text, uint16_t color, uint8_t size = 2) {
|
||||
gfx->setFont();
|
||||
gfx->setTextSize(size);
|
||||
gfx->setTextColor(color);
|
||||
gfx->setCursor(x, y);
|
||||
gfx->print(text);
|
||||
}
|
||||
|
||||
static void drawButton(int x, int y, int w, int h, uint16_t fill, const char *label, uint8_t size = 2) {
|
||||
drawPanel(x, y, w, h, fill, C_BORDER, 12);
|
||||
gfx->setFont();
|
||||
gfx->setTextSize(size);
|
||||
gfx->setTextColor(C_TEXT);
|
||||
int textW = strlen(label) * 6 * size;
|
||||
int textX = x + (w - textW) / 2;
|
||||
if (textX < x + 8) {
|
||||
textX = x + 8;
|
||||
}
|
||||
int textY = y + (h + 8 * size) / 2 - 2;
|
||||
gfx->setCursor(textX, textY);
|
||||
gfx->print(label);
|
||||
}
|
||||
|
||||
static void drawScreen() {
|
||||
gfx->fillScreen(C_BG);
|
||||
drawPanel(12, 12, 456, 456, C_PANEL, C_BORDER, 16);
|
||||
drawText(24, 42, "BUTTON LAYOUT TEST", C_TEXT, 2);
|
||||
drawText(24, 70, "Check bottom and wide buttons", C_WARN, 1);
|
||||
|
||||
drawButton(20, 110, 136, 52, C_BUTTON, "STATUS");
|
||||
drawButton(172, 110, 136, 52, C_BUTTON, "CONNECT");
|
||||
drawButton(324, 110, 136, 52, C_BUTTON, "ACCOUNT");
|
||||
|
||||
drawButton(20, 180, 136, 52, C_BUTTON, "WALLET");
|
||||
drawButton(172, 180, 136, 52, C_BUTTON, "REQUESTS");
|
||||
drawButton(324, 180, 136, 52, C_BUTTON, "SETTINGS");
|
||||
|
||||
drawButton(20, 270, 212, 48, C_BUTTON2, "BACK");
|
||||
drawButton(248, 270, 212, 48, C_OK, "REFRESH");
|
||||
|
||||
drawButton(20, 338, 440, 40, C_BUTTON2, "FULL WIDTH BUTTON", 2);
|
||||
drawButton(20, 390, 440, 56, C_OK, "REGISTER DEVICE", 2);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);
|
||||
gfx->begin();
|
||||
gBus->writeC8D8(0x36, 0xA0);
|
||||
gfx->setBrightness(220);
|
||||
drawScreen();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(1000);
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <Arduino_GFX_Library.h>
|
||||
#include <U8g2lib.h>
|
||||
|
||||
#define PIN_LCD_CS 12
|
||||
#define PIN_LCD_SCLK 38
|
||||
#define PIN_LCD_D0 4
|
||||
#define PIN_LCD_D1 5
|
||||
#define PIN_LCD_D2 6
|
||||
#define PIN_LCD_D3 7
|
||||
#define PIN_LCD_RST 2
|
||||
#define PIN_I2C_SDA 15
|
||||
#define PIN_I2C_SCL 14
|
||||
|
||||
#define DISP_W 480
|
||||
#define DISP_H 480
|
||||
|
||||
#define C_BG 0x0841u
|
||||
#define C_PANEL 0x1082u
|
||||
#define C_CARD 0x18C3u
|
||||
#define C_BORDER 0x39C7u
|
||||
#define C_TEXT 0xFFFFu
|
||||
#define C_MUTE 0xBDF7u
|
||||
#define C_OK 0x3666u
|
||||
#define C_WARN 0xECA0u
|
||||
#define C_BUTTON 0x2145u
|
||||
#define C_BUTTON2 0x3186u
|
||||
|
||||
#define FONT_BODY u8g2_font_9x15_t_cyrillic
|
||||
#define FONT_SMALL u8g2_font_6x13_t_cyrillic
|
||||
|
||||
Arduino_DataBus *gBus = new Arduino_ESP32QSPI(
|
||||
PIN_LCD_CS, PIN_LCD_SCLK, PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3);
|
||||
Arduino_CO5300 *gfx = new Arduino_CO5300(
|
||||
gBus, PIN_LCD_RST, 0, DISP_W, DISP_H, 0, 0, 0, 0);
|
||||
|
||||
static void drawPanel(int x, int y, int w, int h, uint16_t fill, uint16_t border, int radius = 10) {
|
||||
gfx->fillRoundRect(x, y, w, h, radius, fill);
|
||||
gfx->drawRoundRect(x, y, w, h, radius, border);
|
||||
}
|
||||
|
||||
static void drawDefaultText(int x, int y, const char *text, uint16_t color, uint8_t size = 1) {
|
||||
gfx->setFont();
|
||||
gfx->setTextSize(size);
|
||||
gfx->setTextColor(color);
|
||||
gfx->setCursor(x, y);
|
||||
gfx->print(text);
|
||||
}
|
||||
|
||||
static void drawU8Text(int x, int y, const String &text, uint16_t color, const uint8_t *font) {
|
||||
gfx->setTextSize(1);
|
||||
gfx->setFont(font);
|
||||
gfx->setTextColor(color);
|
||||
gfx->setCursor(x, y);
|
||||
gfx->print(text);
|
||||
}
|
||||
|
||||
static void drawButton(int x, int y, int w, int h, uint16_t fill, const String &label, const uint8_t *font, bool useDefaultFont = false) {
|
||||
drawPanel(x, y, w, h, fill, C_BORDER, 12);
|
||||
if (useDefaultFont) {
|
||||
drawDefaultText(x + 14, y + 30, label.c_str(), C_TEXT, 2);
|
||||
return;
|
||||
}
|
||||
drawU8Text(x + 14, y + 32, label, C_TEXT, font);
|
||||
}
|
||||
|
||||
static void drawScreen() {
|
||||
gfx->fillScreen(C_BG);
|
||||
|
||||
drawPanel(12, 12, 456, 456, C_PANEL, C_BORDER, 16);
|
||||
drawDefaultText(28, 42, "TEXT TEST 123", C_TEXT, 2);
|
||||
drawU8Text(28, 72, "Default ASCII above", C_MUTE, (const uint8_t *)FONT_SMALL);
|
||||
|
||||
drawPanel(24, 90, 432, 72, C_CARD, C_BORDER, 12);
|
||||
drawDefaultText(38, 118, "A: Default font ABC123", C_TEXT, 2);
|
||||
drawDefaultText(38, 146, "Visible? then base path OK", C_WARN, 1);
|
||||
|
||||
drawPanel(24, 174, 432, 84, C_CARD, C_BORDER, 12);
|
||||
drawU8Text(38, 204, "B: U8g2 ASCII abc123", C_TEXT, (const uint8_t *)FONT_BODY);
|
||||
drawU8Text(38, 230, "C: Русский текст 123", C_OK, (const uint8_t *)FONT_BODY);
|
||||
|
||||
drawPanel(24, 270, 432, 84, C_CARD, C_BORDER, 12);
|
||||
drawU8Text(38, 298, "D: Мелкий шрифт кнопок", C_TEXT, (const uint8_t *)FONT_SMALL);
|
||||
drawU8Text(38, 320, "Если это видно, FONT_SMALL жив", C_MUTE, (const uint8_t *)FONT_SMALL);
|
||||
|
||||
drawButton(24, 368, 128, 72, C_BUTTON, "BTN 1", nullptr, true);
|
||||
drawButton(176, 368, 128, 72, C_OK, "abc123", (const uint8_t *)FONT_BODY);
|
||||
drawButton(328, 368, 128, 72, C_BUTTON2, "Русский", (const uint8_t *)FONT_BODY);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);
|
||||
gfx->begin();
|
||||
gBus->writeC8D8(0x36, 0xA0);
|
||||
gfx->setBrightness(220);
|
||||
gfx->setUTF8Print(true);
|
||||
drawScreen();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(1000);
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <lvgl.h>
|
||||
#include <Arduino_GFX_Library.h>
|
||||
#include "pin_config.h"
|
||||
|
||||
#define EXAMPLE_LVGL_TICK_PERIOD_MS 2
|
||||
|
||||
static lv_disp_draw_buf_t gDrawBuf;
|
||||
static lv_color_t *gBuf1 = nullptr;
|
||||
static lv_color_t *gBuf2 = nullptr;
|
||||
|
||||
Arduino_DataBus *gBus = new Arduino_ESP32QSPI(
|
||||
LCD_CS, LCD_SCLK, LCD_SDIO0, LCD_SDIO1, LCD_SDIO2, LCD_SDIO3);
|
||||
Arduino_CO5300 *gfx = new Arduino_CO5300(
|
||||
gBus, LCD_RESET, 0, LCD_WIDTH, LCD_HEIGHT, 0, 0, 0, 0);
|
||||
|
||||
static void lvglRounderCb(lv_disp_drv_t *dispDrv, lv_area_t *area) {
|
||||
LV_UNUSED(dispDrv);
|
||||
if (area->x1 % 2 != 0) area->x1--;
|
||||
if (area->y1 % 2 != 0) area->y1--;
|
||||
if (area->x2 % 2 == 0) area->x2++;
|
||||
if (area->y2 % 2 == 0) area->y2++;
|
||||
}
|
||||
|
||||
static void lvglFlushCb(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *colorP) {
|
||||
uint32_t w = area->x2 - area->x1 + 1;
|
||||
uint32_t h = area->y2 - area->y1 + 1;
|
||||
#if (LV_COLOR_16_SWAP != 0)
|
||||
gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&colorP->full, w, h);
|
||||
#else
|
||||
gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&colorP->full, w, h);
|
||||
#endif
|
||||
lv_disp_flush_ready(disp);
|
||||
}
|
||||
|
||||
static void lvglTick(void *arg) {
|
||||
LV_UNUSED(arg);
|
||||
lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
static void createUi() {
|
||||
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x0B1320), 0);
|
||||
lv_obj_set_style_bg_opa(lv_scr_act(), LV_OPA_COVER, 0);
|
||||
|
||||
lv_obj_t *title = lv_label_create(lv_scr_act());
|
||||
lv_label_set_text(title, "LVGL BASIC TEST");
|
||||
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_set_style_text_font(title, &lv_font_montserrat_22, 0);
|
||||
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 18);
|
||||
|
||||
lv_obj_t *subtitle = lv_label_create(lv_scr_act());
|
||||
lv_label_set_text(subtitle, "If this text is visible, LVGL path works.");
|
||||
lv_obj_set_width(subtitle, 420);
|
||||
lv_label_set_long_mode(subtitle, LV_LABEL_LONG_WRAP);
|
||||
lv_obj_set_style_text_color(subtitle, lv_color_hex(0xD5DCE5), 0);
|
||||
lv_obj_set_style_text_font(subtitle, &lv_font_montserrat_16, 0);
|
||||
lv_obj_align(subtitle, LV_ALIGN_TOP_MID, 0, 58);
|
||||
|
||||
const char *labels[3] = {"Button One", "Second Button", "Bottom Action"};
|
||||
const lv_color_t colors[3] = {
|
||||
lv_color_hex(0x2B4C7E),
|
||||
lv_color_hex(0x2E8B57),
|
||||
lv_color_hex(0xB45F06),
|
||||
};
|
||||
const lv_coord_t ys[3] = {150, 236, 340};
|
||||
const lv_coord_t hs[3] = {58, 58, 64};
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
lv_obj_t *btn = lv_btn_create(lv_scr_act());
|
||||
lv_obj_set_size(btn, 360, hs[i]);
|
||||
lv_obj_align(btn, LV_ALIGN_TOP_MID, 0, ys[i]);
|
||||
lv_obj_set_style_radius(btn, 14, 0);
|
||||
lv_obj_set_style_bg_color(btn, colors[i], 0);
|
||||
lv_obj_set_style_border_width(btn, 2, 0);
|
||||
lv_obj_set_style_border_color(btn, lv_color_hex(0x7F96B0), 0);
|
||||
|
||||
lv_obj_t *label = lv_label_create(btn);
|
||||
lv_label_set_text(label, labels[i]);
|
||||
lv_obj_set_style_text_font(label, &lv_font_montserrat_20, 0);
|
||||
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_center(label);
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Wire.begin(IIC_SDA, IIC_SCL);
|
||||
|
||||
gfx->begin();
|
||||
gBus->writeC8D8(0x36, 0xA0);
|
||||
gfx->setBrightness(220);
|
||||
|
||||
lv_init();
|
||||
|
||||
uint32_t screenWidth = gfx->width();
|
||||
uint32_t screenHeight = gfx->height();
|
||||
gBuf1 = (lv_color_t *)heap_caps_malloc(screenWidth * screenHeight / 4 * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
gBuf2 = (lv_color_t *)heap_caps_malloc(screenWidth * screenHeight / 4 * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
lv_disp_draw_buf_init(&gDrawBuf, gBuf1, gBuf2, screenWidth * screenHeight / 4);
|
||||
|
||||
static lv_disp_drv_t dispDrv;
|
||||
lv_disp_drv_init(&dispDrv);
|
||||
dispDrv.hor_res = screenWidth;
|
||||
dispDrv.ver_res = screenHeight;
|
||||
dispDrv.flush_cb = lvglFlushCb;
|
||||
dispDrv.rounder_cb = lvglRounderCb;
|
||||
dispDrv.draw_buf = &gDrawBuf;
|
||||
lv_disp_drv_register(&dispDrv);
|
||||
|
||||
const esp_timer_create_args_t lvglTickTimerArgs = {
|
||||
.callback = &lvglTick,
|
||||
.name = "lvgl_tick"};
|
||||
esp_timer_handle_t lvglTickTimer = nullptr;
|
||||
esp_timer_create(&lvglTickTimerArgs, &lvglTickTimer);
|
||||
esp_timer_start_periodic(lvglTickTimer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000);
|
||||
|
||||
createUi();
|
||||
Serial.println("LVGL basic test ready");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
lv_timer_handler();
|
||||
delay(5);
|
||||
}
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <lvgl.h>
|
||||
#include <Arduino_GFX_Library.h>
|
||||
#include "pin_config.h"
|
||||
#include "TouchDrvCSTXXX.hpp"
|
||||
|
||||
#define EXAMPLE_LVGL_TICK_PERIOD_MS 2
|
||||
|
||||
static lv_disp_draw_buf_t gDrawBuf;
|
||||
static lv_color_t *gBuf1 = nullptr;
|
||||
static lv_color_t *gBuf2 = nullptr;
|
||||
static lv_obj_t *gStatusLabel = nullptr;
|
||||
static uint32_t gPressCount = 0;
|
||||
|
||||
static TouchDrvCST92xx gTouch;
|
||||
static int16_t gTouchX[5];
|
||||
static int16_t gTouchY[5];
|
||||
|
||||
Arduino_DataBus *gBus = new Arduino_ESP32QSPI(
|
||||
LCD_CS, LCD_SCLK, LCD_SDIO0, LCD_SDIO1, LCD_SDIO2, LCD_SDIO3);
|
||||
Arduino_CO5300 *gfx = new Arduino_CO5300(
|
||||
gBus, LCD_RESET, 0, LCD_WIDTH, LCD_HEIGHT, 0, 0, 0, 0);
|
||||
|
||||
static void lvglFlushCb(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *colorP) {
|
||||
uint32_t w = area->x2 - area->x1 + 1;
|
||||
uint32_t h = area->y2 - area->y1 + 1;
|
||||
#if (LV_COLOR_16_SWAP != 0)
|
||||
gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&colorP->full, w, h);
|
||||
#else
|
||||
gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&colorP->full, w, h);
|
||||
#endif
|
||||
lv_disp_flush_ready(disp);
|
||||
}
|
||||
|
||||
static void lvglTick(void *arg) {
|
||||
LV_UNUSED(arg);
|
||||
lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
static void lvglTouchRead(lv_indev_drv_t *indevDrv, lv_indev_data_t *data) {
|
||||
LV_UNUSED(indevDrv);
|
||||
uint8_t touched = gTouch.getPoint(gTouchX, gTouchY, gTouch.getSupportTouchPoint());
|
||||
if (touched > 0) {
|
||||
data->state = LV_INDEV_STATE_PR;
|
||||
data->point.x = gTouchX[0];
|
||||
data->point.y = gTouchY[0];
|
||||
return;
|
||||
}
|
||||
|
||||
data->state = LV_INDEV_STATE_REL;
|
||||
}
|
||||
|
||||
static void buttonEventCb(lv_event_t *event) {
|
||||
if (lv_event_get_code(event) != LV_EVENT_CLICKED) {
|
||||
return;
|
||||
}
|
||||
|
||||
lv_obj_t *btn = lv_event_get_target(event);
|
||||
const char *name = (const char *)lv_event_get_user_data(event);
|
||||
gPressCount++;
|
||||
|
||||
lv_obj_set_style_outline_width(btn, 4, 0);
|
||||
lv_obj_set_style_outline_color(btn, lv_color_hex(0xFFF3B0), 0);
|
||||
lv_obj_set_style_outline_pad(btn, 2, 0);
|
||||
|
||||
String status = "Pressed: ";
|
||||
status += name;
|
||||
status += " (#";
|
||||
status += String(gPressCount);
|
||||
status += ")";
|
||||
lv_label_set_text(gStatusLabel, status.c_str());
|
||||
|
||||
Serial.println(status);
|
||||
}
|
||||
|
||||
static void createButton(lv_obj_t *parent, const char *labelText, lv_coord_t col, lv_coord_t row, uint32_t colorHex) {
|
||||
static const lv_coord_t kStartX = 22;
|
||||
static const lv_coord_t kStartY = 122;
|
||||
static const lv_coord_t kButtonW = 134;
|
||||
static const lv_coord_t kButtonH = 64;
|
||||
static const lv_coord_t kGapX = 18;
|
||||
static const lv_coord_t kGapY = 16;
|
||||
|
||||
lv_obj_t *btn = lv_btn_create(parent);
|
||||
lv_obj_set_size(btn, kButtonW, kButtonH);
|
||||
lv_obj_set_pos(btn, kStartX + col * (kButtonW + kGapX), kStartY + row * (kButtonH + kGapY));
|
||||
lv_obj_set_style_radius(btn, 16, 0);
|
||||
lv_obj_set_style_bg_color(btn, lv_color_hex(colorHex), 0);
|
||||
lv_obj_set_style_border_width(btn, 2, 0);
|
||||
lv_obj_set_style_border_color(btn, lv_color_hex(0x7F96B0), 0);
|
||||
lv_obj_set_style_shadow_width(btn, 0, 0);
|
||||
lv_obj_add_event_cb(btn, buttonEventCb, LV_EVENT_CLICKED, (void *)labelText);
|
||||
|
||||
lv_obj_t *label = lv_label_create(btn);
|
||||
lv_label_set_text(label, labelText);
|
||||
lv_obj_set_style_text_font(label, &lv_font_montserrat_18, 0);
|
||||
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_center(label);
|
||||
}
|
||||
|
||||
static void createUi() {
|
||||
lv_obj_t *screen = lv_scr_act();
|
||||
lv_obj_set_style_bg_color(screen, lv_color_hex(0x0B1320), 0);
|
||||
lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, 0);
|
||||
|
||||
lv_obj_t *title = lv_label_create(screen);
|
||||
lv_label_set_text(title, "LVGL INTERACTION TEST");
|
||||
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_set_style_text_font(title, &lv_font_montserrat_22, 0);
|
||||
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 14);
|
||||
|
||||
lv_obj_t *subtitle = lv_label_create(screen);
|
||||
lv_label_set_text(subtitle, "Tap buttons. Bottom label must change on every press.");
|
||||
lv_obj_set_width(subtitle, 420);
|
||||
lv_label_set_long_mode(subtitle, LV_LABEL_LONG_WRAP);
|
||||
lv_obj_set_style_text_color(subtitle, lv_color_hex(0xD5DCE5), 0);
|
||||
lv_obj_set_style_text_font(subtitle, &lv_font_montserrat_14, 0);
|
||||
lv_obj_align(subtitle, LV_ALIGN_TOP_MID, 0, 48);
|
||||
|
||||
createButton(screen, "Status", 0, 0, 0x355C7D);
|
||||
createButton(screen, "Connect", 1, 0, 0x2A9D8F);
|
||||
createButton(screen, "Wallet", 2, 0, 0x457B9D);
|
||||
createButton(screen, "Requests", 0, 1, 0xE76F51);
|
||||
createButton(screen, "Settings", 1, 1, 0x8D5A97);
|
||||
createButton(screen, "Register", 2, 1, 0x6A994E);
|
||||
createButton(screen, "Approve", 0, 2, 0xBC6C25);
|
||||
createButton(screen, "Reject", 1, 2, 0xB56576);
|
||||
createButton(screen, "Back", 2, 2, 0x6C757D);
|
||||
|
||||
lv_obj_t *statusPanel = lv_obj_create(screen);
|
||||
lv_obj_set_size(statusPanel, 436, 56);
|
||||
lv_obj_align(statusPanel, LV_ALIGN_BOTTOM_MID, 0, -16);
|
||||
lv_obj_set_style_radius(statusPanel, 14, 0);
|
||||
lv_obj_set_style_bg_color(statusPanel, lv_color_hex(0x162033), 0);
|
||||
lv_obj_set_style_border_width(statusPanel, 2, 0);
|
||||
lv_obj_set_style_border_color(statusPanel, lv_color_hex(0x415A77), 0);
|
||||
lv_obj_set_style_pad_all(statusPanel, 10, 0);
|
||||
|
||||
gStatusLabel = lv_label_create(statusPanel);
|
||||
lv_label_set_text(gStatusLabel, "Pressed: none");
|
||||
lv_obj_set_style_text_font(gStatusLabel, &lv_font_montserrat_18, 0);
|
||||
lv_obj_set_style_text_color(gStatusLabel, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_center(gStatusLabel);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Wire.begin(IIC_SDA, IIC_SCL);
|
||||
|
||||
pinMode(TP_RST, OUTPUT);
|
||||
pinMode(TP_INT, INPUT);
|
||||
digitalWrite(TP_RST, LOW);
|
||||
delay(30);
|
||||
digitalWrite(TP_RST, HIGH);
|
||||
delay(50);
|
||||
delay(1000);
|
||||
|
||||
gTouch.setPins(TP_RST, TP_INT);
|
||||
bool touchOk = gTouch.begin(Wire, 0x5A, IIC_SDA, IIC_SCL);
|
||||
if (!touchOk) {
|
||||
Serial.println("Touch init failed");
|
||||
} else {
|
||||
Serial.print("Touch model: ");
|
||||
Serial.println(gTouch.getModelName());
|
||||
gTouch.sleep();
|
||||
gTouch.reset();
|
||||
gTouch.setMaxCoordinates(480, 480);
|
||||
gTouch.setSwapXY(true);
|
||||
gTouch.setMirrorXY(true, false);
|
||||
}
|
||||
|
||||
gfx->begin();
|
||||
gBus->writeC8D8(0x36, 0xA0);
|
||||
gfx->setBrightness(220);
|
||||
gfx->fillScreen(0x0000);
|
||||
|
||||
lv_init();
|
||||
|
||||
uint32_t screenWidth = gfx->width();
|
||||
uint32_t screenHeight = gfx->height();
|
||||
gBuf1 = (lv_color_t *)heap_caps_malloc(screenWidth * screenHeight / 4 * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
gBuf2 = (lv_color_t *)heap_caps_malloc(screenWidth * screenHeight / 4 * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
lv_disp_draw_buf_init(&gDrawBuf, gBuf1, gBuf2, screenWidth * screenHeight / 4);
|
||||
|
||||
static lv_disp_drv_t dispDrv;
|
||||
lv_disp_drv_init(&dispDrv);
|
||||
dispDrv.hor_res = screenWidth;
|
||||
dispDrv.ver_res = screenHeight;
|
||||
dispDrv.flush_cb = lvglFlushCb;
|
||||
dispDrv.draw_buf = &gDrawBuf;
|
||||
lv_disp_drv_register(&dispDrv);
|
||||
|
||||
static lv_indev_drv_t indevDrv;
|
||||
lv_indev_drv_init(&indevDrv);
|
||||
indevDrv.type = LV_INDEV_TYPE_POINTER;
|
||||
indevDrv.read_cb = lvglTouchRead;
|
||||
lv_indev_drv_register(&indevDrv);
|
||||
|
||||
const esp_timer_create_args_t lvglTickTimerArgs = {
|
||||
.callback = &lvglTick,
|
||||
.name = "lvgl_tick"};
|
||||
esp_timer_handle_t lvglTickTimer = nullptr;
|
||||
esp_timer_create(&lvglTickTimerArgs, &lvglTickTimer);
|
||||
esp_timer_start_periodic(lvglTickTimer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000);
|
||||
|
||||
createUi();
|
||||
Serial.println("LVGL interaction test ready");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
lv_timer_handler();
|
||||
delay(5);
|
||||
}
|
||||
+252
@@ -0,0 +1,252 @@
|
||||
#include <lvgl.h>
|
||||
#include "Arduino_GFX_Library.h"
|
||||
#include "pin_config.h"
|
||||
#include "TouchDrvCSTXXX.hpp"
|
||||
#include "lv_conf.h"
|
||||
#include "HWCDC.h"
|
||||
#include <Wire.h>
|
||||
|
||||
HWCDC USBSerial;
|
||||
|
||||
#define EXAMPLE_LVGL_TICK_PERIOD_MS 2
|
||||
|
||||
static lv_disp_draw_buf_t draw_buf;
|
||||
static lv_color_t *buf1 = nullptr;
|
||||
static lv_color_t *buf2 = nullptr;
|
||||
static lv_obj_t *status_label = nullptr;
|
||||
static uint32_t click_count = 0;
|
||||
|
||||
Arduino_DataBus *bus = new Arduino_ESP32QSPI(
|
||||
LCD_CS, LCD_SCLK, LCD_SDIO0, LCD_SDIO1, LCD_SDIO2, LCD_SDIO3);
|
||||
|
||||
Arduino_CO5300 *gfx = new Arduino_CO5300(
|
||||
bus, LCD_RESET, 0, LCD_WIDTH, LCD_HEIGHT, 0, 0, 0, 0);
|
||||
|
||||
TouchDrvCST92xx touch;
|
||||
int16_t touch_x[5], touch_y[5];
|
||||
bool isPressed = false;
|
||||
uint32_t screenWidth = 0;
|
||||
uint32_t screenHeight = 0;
|
||||
|
||||
#if LV_USE_LOG != 0
|
||||
void my_print(const char *buf) {
|
||||
USBSerial.printf(buf);
|
||||
USBSerial.flush();
|
||||
}
|
||||
#endif
|
||||
|
||||
static void example_lvgl_rounder_cb(struct _lv_disp_drv_t *disp_drv, lv_area_t *area) {
|
||||
LV_UNUSED(disp_drv);
|
||||
if (area->x1 % 2 != 0) area->x1--;
|
||||
if (area->y1 % 2 != 0) area->y1--;
|
||||
if (area->x2 % 2 == 0) area->x2++;
|
||||
if (area->y2 % 2 == 0) area->y2++;
|
||||
}
|
||||
|
||||
static void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
|
||||
uint32_t w = (area->x2 - area->x1 + 1);
|
||||
uint32_t h = (area->y2 - area->y1 + 1);
|
||||
|
||||
#if (LV_COLOR_16_SWAP != 0)
|
||||
gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
|
||||
#else
|
||||
gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
|
||||
#endif
|
||||
|
||||
lv_disp_flush_ready(disp);
|
||||
}
|
||||
|
||||
static void example_increase_lvgl_tick(void *arg) {
|
||||
LV_UNUSED(arg);
|
||||
lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
static void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data) {
|
||||
LV_UNUSED(indev_driver);
|
||||
if (isPressed) {
|
||||
uint8_t touched = touch.getPoint(touch_x, touch_y, touch.getSupportTouchPoint());
|
||||
if (touched) {
|
||||
isPressed = false;
|
||||
data->state = LV_INDEV_STATE_PR;
|
||||
data->point.x = touch_x[0];
|
||||
data->point.y = touch_y[0];
|
||||
USBSerial.print("Touch x=");
|
||||
USBSerial.print(touch_x[0]);
|
||||
USBSerial.print(" y=");
|
||||
USBSerial.println(touch_y[0]);
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_REL;
|
||||
}
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_REL;
|
||||
}
|
||||
}
|
||||
|
||||
static void button_event_cb(lv_event_t *event) {
|
||||
if (lv_event_get_code(event) != LV_EVENT_CLICKED) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char *name = (const char *)lv_event_get_user_data(event);
|
||||
click_count++;
|
||||
String text = "Pressed: ";
|
||||
text += name;
|
||||
text += " (#";
|
||||
text += String(click_count);
|
||||
text += ")";
|
||||
lv_label_set_text(status_label, text.c_str());
|
||||
USBSerial.println(text);
|
||||
}
|
||||
|
||||
static lv_obj_t *make_button(lv_obj_t *parent, const char *label_text, lv_coord_t x, lv_coord_t y, lv_coord_t w, lv_coord_t h, lv_color_t bg) {
|
||||
lv_obj_t *btn = lv_btn_create(parent);
|
||||
lv_obj_set_size(btn, w, h);
|
||||
lv_obj_set_pos(btn, x, y);
|
||||
lv_obj_set_style_radius(btn, 16, 0);
|
||||
lv_obj_set_style_bg_color(btn, bg, 0);
|
||||
lv_obj_set_style_border_width(btn, 2, 0);
|
||||
lv_obj_set_style_border_color(btn, lv_color_hex(0x8AA4BF), 0);
|
||||
lv_obj_set_style_shadow_width(btn, 0, 0);
|
||||
lv_obj_add_event_cb(btn, button_event_cb, LV_EVENT_CLICKED, (void *)label_text);
|
||||
|
||||
lv_obj_t *label = lv_label_create(btn);
|
||||
lv_label_set_text(label, label_text);
|
||||
lv_obj_set_style_text_font(label, &lv_font_montserrat_18, 0);
|
||||
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_center(label);
|
||||
return btn;
|
||||
}
|
||||
|
||||
static void create_ui() {
|
||||
lv_obj_t *screen = lv_scr_act();
|
||||
lv_obj_set_style_bg_color(screen, lv_color_hex(0x0B1320), 0);
|
||||
lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, 0);
|
||||
|
||||
lv_obj_t *title = lv_label_create(screen);
|
||||
lv_label_set_text(title, "LVGL OFFICIAL BASED TEST");
|
||||
lv_obj_set_style_text_font(title, &lv_font_montserrat_20, 0);
|
||||
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 14);
|
||||
|
||||
lv_obj_t *hint = lv_label_create(screen);
|
||||
lv_label_set_text(hint, "Based on official LVGL_Widgets init. Tap buttons and watch the status.");
|
||||
lv_obj_set_width(hint, 430);
|
||||
lv_label_set_long_mode(hint, LV_LABEL_LONG_WRAP);
|
||||
lv_obj_set_style_text_font(hint, &lv_font_montserrat_14, 0);
|
||||
lv_obj_set_style_text_color(hint, lv_color_hex(0xD4DDE7), 0);
|
||||
lv_obj_align(hint, LV_ALIGN_TOP_MID, 0, 44);
|
||||
|
||||
make_button(screen, "Status", 22, 120, 136, 60, lv_color_hex(0x355C7D));
|
||||
make_button(screen, "Connect", 174, 120, 136, 60, lv_color_hex(0x2A9D8F));
|
||||
make_button(screen, "Wallet", 326, 120, 132, 60, lv_color_hex(0x457B9D));
|
||||
|
||||
make_button(screen, "Requests", 22, 196, 136, 60, lv_color_hex(0xE76F51));
|
||||
make_button(screen, "Settings",174, 196, 136, 60, lv_color_hex(0x8D5A97));
|
||||
make_button(screen, "Register",326, 196, 132, 60, lv_color_hex(0x6A994E));
|
||||
|
||||
make_button(screen, "Approve", 22, 272, 136, 60, lv_color_hex(0xBC6C25));
|
||||
make_button(screen, "Reject", 174, 272, 136, 60, lv_color_hex(0xB56576));
|
||||
make_button(screen, "Back", 326, 272, 132, 60, lv_color_hex(0x6C757D));
|
||||
|
||||
lv_obj_t *bottom_btn = make_button(screen, "Bottom Action", 22, 350, 436, 64, lv_color_hex(0x1D6F42));
|
||||
lv_obj_set_style_text_font(lv_obj_get_child(bottom_btn, 0), &lv_font_montserrat_20, 0);
|
||||
|
||||
lv_obj_t *status_panel = lv_obj_create(screen);
|
||||
lv_obj_set_size(status_panel, 436, 42);
|
||||
lv_obj_set_pos(status_panel, 22, 426);
|
||||
lv_obj_set_style_radius(status_panel, 12, 0);
|
||||
lv_obj_set_style_bg_color(status_panel, lv_color_hex(0x17263A), 0);
|
||||
lv_obj_set_style_border_width(status_panel, 2, 0);
|
||||
lv_obj_set_style_border_color(status_panel, lv_color_hex(0x496582), 0);
|
||||
lv_obj_set_style_pad_all(status_panel, 6, 0);
|
||||
|
||||
status_label = lv_label_create(status_panel);
|
||||
lv_label_set_text(status_label, "Pressed: none");
|
||||
lv_obj_set_style_text_font(status_label, &lv_font_montserrat_16, 0);
|
||||
lv_obj_set_style_text_color(status_label, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_center(status_label);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
USBSerial.begin(115200);
|
||||
|
||||
Wire.begin(IIC_SDA, IIC_SCL);
|
||||
|
||||
digitalWrite(TP_RST, LOW);
|
||||
delay(30);
|
||||
digitalWrite(TP_RST, HIGH);
|
||||
delay(50);
|
||||
delay(1000);
|
||||
|
||||
Wire.begin(IIC_SDA, IIC_SCL);
|
||||
|
||||
touch.setPins(TP_RST, TP_INT);
|
||||
bool result = touch.begin(Wire, 0x5A, IIC_SDA, IIC_SCL);
|
||||
if (result == false) {
|
||||
USBSerial.println("touch is not online...");
|
||||
while (1) delay(1000);
|
||||
}
|
||||
USBSerial.print("Touch model: ");
|
||||
USBSerial.println(touch.getModelName());
|
||||
touch.sleep();
|
||||
touch.reset();
|
||||
touch.setMaxCoordinates(480, 480);
|
||||
touch.setSwapXY(true);
|
||||
touch.setMirrorXY(true, false);
|
||||
attachInterrupt(
|
||||
TP_INT, []() {
|
||||
isPressed = true;
|
||||
},
|
||||
FALLING);
|
||||
|
||||
gfx->begin();
|
||||
gfx->setBrightness(200);
|
||||
bus->writeC8D8(0x36, 0xA0);
|
||||
|
||||
screenWidth = gfx->width();
|
||||
screenHeight = gfx->height();
|
||||
|
||||
lv_init();
|
||||
|
||||
buf1 = (lv_color_t *)heap_caps_malloc(screenWidth * screenHeight / 4 * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
buf2 = (lv_color_t *)heap_caps_malloc(screenWidth * screenHeight / 4 * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
|
||||
#if LV_USE_LOG != 0
|
||||
lv_log_register_print_cb(my_print);
|
||||
#endif
|
||||
|
||||
lv_disp_draw_buf_init(&draw_buf, buf1, buf2, screenWidth * screenHeight / 4);
|
||||
|
||||
static lv_disp_drv_t disp_drv;
|
||||
lv_disp_drv_init(&disp_drv);
|
||||
disp_drv.hor_res = screenWidth;
|
||||
disp_drv.ver_res = screenHeight;
|
||||
disp_drv.flush_cb = my_disp_flush;
|
||||
disp_drv.rounder_cb = example_lvgl_rounder_cb;
|
||||
disp_drv.draw_buf = &draw_buf;
|
||||
disp_drv.sw_rotate = 1;
|
||||
lv_disp_drv_register(&disp_drv);
|
||||
|
||||
static lv_indev_drv_t indev_drv;
|
||||
lv_indev_drv_init(&indev_drv);
|
||||
indev_drv.type = LV_INDEV_TYPE_POINTER;
|
||||
indev_drv.read_cb = my_touchpad_read;
|
||||
lv_indev_drv_register(&indev_drv);
|
||||
|
||||
const esp_timer_create_args_t lvgl_tick_timer_args = {
|
||||
.callback = &example_increase_lvgl_tick,
|
||||
.name = "lvgl_tick"
|
||||
};
|
||||
|
||||
esp_timer_handle_t lvgl_tick_timer = NULL;
|
||||
esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer);
|
||||
esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000);
|
||||
|
||||
create_ui();
|
||||
USBSerial.println("LVGL official based test ready");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
lv_timer_handler();
|
||||
delay(5);
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <lvgl.h>
|
||||
LV_FONT_DECLARE(lv_font_ru_18);
|
||||
LV_FONT_DECLARE(lv_font_ru_24);
|
||||
+1553
File diff suppressed because it is too large
Load Diff
+2048
File diff suppressed because it is too large
Load Diff
+264
@@ -0,0 +1,264 @@
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <lvgl.h>
|
||||
#include <Arduino_GFX_Library.h>
|
||||
#include <TouchDrvCSTXXX.hpp>
|
||||
#include "lv_font_ru.h"
|
||||
|
||||
#define PIN_LCD_CS 12
|
||||
#define PIN_LCD_SCLK 38
|
||||
#define PIN_LCD_D0 4
|
||||
#define PIN_LCD_D1 5
|
||||
#define PIN_LCD_D2 6
|
||||
#define PIN_LCD_D3 7
|
||||
#define PIN_LCD_RST 2
|
||||
#define PIN_I2C_SDA 15
|
||||
#define PIN_I2C_SCL 14
|
||||
#define PIN_TP_INT 11
|
||||
|
||||
#define DISP_W 480
|
||||
#define DISP_H 480
|
||||
#define LVGL_TICK_MS 2
|
||||
#define TEST_VERSION "v1-ru-font"
|
||||
|
||||
static lv_disp_draw_buf_t gDrawBuf;
|
||||
static lv_color_t *gBuf1 = nullptr;
|
||||
static lv_color_t *gBuf2 = nullptr;
|
||||
static lv_obj_t *gStatusLabel = nullptr;
|
||||
static lv_obj_t *gTouchLabel = nullptr;
|
||||
static uint32_t gClickCount = 0;
|
||||
static bool gTouchActive = false;
|
||||
static int16_t gLastX = -1;
|
||||
static int16_t gLastY = -1;
|
||||
|
||||
Arduino_DataBus *gBus = new Arduino_ESP32QSPI(
|
||||
PIN_LCD_CS, PIN_LCD_SCLK, PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3);
|
||||
Arduino_CO5300 *gfx = new Arduino_CO5300(
|
||||
gBus, PIN_LCD_RST, 0, DISP_W, DISP_H, 0, 0, 0, 0);
|
||||
TouchDrvCST92xx gTouch;
|
||||
|
||||
static void lvglFlushCb(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *colorP) {
|
||||
uint32_t w = area->x2 - area->x1 + 1;
|
||||
uint32_t h = area->y2 - area->y1 + 1;
|
||||
#if (LV_COLOR_16_SWAP != 0)
|
||||
gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&colorP->full, w, h);
|
||||
#else
|
||||
gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&colorP->full, w, h);
|
||||
#endif
|
||||
lv_disp_flush_ready(disp);
|
||||
}
|
||||
|
||||
static void lvglTick(void *arg) {
|
||||
LV_UNUSED(arg);
|
||||
lv_tick_inc(LVGL_TICK_MS);
|
||||
}
|
||||
|
||||
static void updateTouchLabel() {
|
||||
if (!gTouchLabel) {
|
||||
return;
|
||||
}
|
||||
|
||||
char text[96];
|
||||
if (gTouchActive) {
|
||||
snprintf(text, sizeof(text), "Касание: x=%d y=%d", gLastX, gLastY);
|
||||
} else {
|
||||
snprintf(text, sizeof(text), "Касание: нет x=%d y=%d", gLastX, gLastY);
|
||||
}
|
||||
lv_label_set_text(gTouchLabel, text);
|
||||
}
|
||||
|
||||
static void lvglTouchRead(lv_indev_drv_t *indevDrv, lv_indev_data_t *data) {
|
||||
LV_UNUSED(indevDrv);
|
||||
int16_t x = 0;
|
||||
int16_t y = 0;
|
||||
bool touching = gTouch.getPoint(&x, &y, 1) > 0;
|
||||
|
||||
if (touching) {
|
||||
gTouchActive = true;
|
||||
gLastX = x;
|
||||
gLastY = y;
|
||||
data->state = LV_INDEV_STATE_PR;
|
||||
data->point.x = x;
|
||||
data->point.y = y;
|
||||
} else {
|
||||
gTouchActive = false;
|
||||
data->state = LV_INDEV_STATE_REL;
|
||||
data->point.x = gLastX >= 0 ? gLastX : 0;
|
||||
data->point.y = gLastY >= 0 ? gLastY : 0;
|
||||
}
|
||||
|
||||
updateTouchLabel();
|
||||
}
|
||||
|
||||
static void buttonEventCb(lv_event_t *event) {
|
||||
if (lv_event_get_code(event) != LV_EVENT_CLICKED) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char *name = (const char *)lv_event_get_user_data(event);
|
||||
gClickCount++;
|
||||
|
||||
char text[128];
|
||||
snprintf(text, sizeof(text), "Нажато: %s (%lu)", name, (unsigned long)gClickCount);
|
||||
lv_label_set_text(gStatusLabel, text);
|
||||
Serial.println(text);
|
||||
}
|
||||
|
||||
static lv_obj_t *makeButton(lv_obj_t *parent,
|
||||
const char *labelText,
|
||||
lv_coord_t x,
|
||||
lv_coord_t y,
|
||||
lv_coord_t w,
|
||||
lv_coord_t h,
|
||||
uint32_t bgColor) {
|
||||
lv_obj_t *btn = lv_btn_create(parent);
|
||||
lv_obj_set_size(btn, w, h);
|
||||
lv_obj_set_pos(btn, x, y);
|
||||
lv_obj_set_style_radius(btn, 16, 0);
|
||||
lv_obj_set_style_bg_color(btn, lv_color_hex(bgColor), 0);
|
||||
lv_obj_set_style_bg_opa(btn, LV_OPA_COVER, 0);
|
||||
lv_obj_set_style_border_width(btn, 2, 0);
|
||||
lv_obj_set_style_border_color(btn, lv_color_hex(0x6F8BA4), 0);
|
||||
lv_obj_set_style_shadow_width(btn, 0, 0);
|
||||
lv_obj_set_style_pad_all(btn, 4, 0);
|
||||
lv_obj_add_event_cb(btn, buttonEventCb, LV_EVENT_CLICKED, (void *)labelText);
|
||||
|
||||
lv_obj_t *label = lv_label_create(btn);
|
||||
lv_label_set_text(label, labelText);
|
||||
lv_obj_set_width(label, w - 12);
|
||||
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
|
||||
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
|
||||
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_set_style_text_font(label, &lv_font_ru_18, 0);
|
||||
lv_obj_center(label);
|
||||
return btn;
|
||||
}
|
||||
|
||||
static void createUi() {
|
||||
lv_obj_t *screen = lv_scr_act();
|
||||
lv_obj_set_style_bg_color(screen, lv_color_hex(0x08131E), 0);
|
||||
lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, 0);
|
||||
lv_obj_set_style_pad_all(screen, 0, 0);
|
||||
lv_obj_set_scrollbar_mode(screen, LV_SCROLLBAR_MODE_OFF);
|
||||
|
||||
lv_obj_t *title = lv_label_create(screen);
|
||||
lv_label_set_text(title, "Русский тест LVGL");
|
||||
lv_obj_set_style_text_font(title, &lv_font_ru_24, 0);
|
||||
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 10);
|
||||
|
||||
lv_obj_t *version = lv_label_create(screen);
|
||||
lv_label_set_text(version, "Версия: " TEST_VERSION);
|
||||
lv_obj_set_style_text_font(version, &lv_font_ru_18, 0);
|
||||
lv_obj_set_style_text_color(version, lv_color_hex(0xD8E0EA), 0);
|
||||
lv_obj_align(version, LV_ALIGN_TOP_MID, 0, 42);
|
||||
|
||||
lv_obj_t *hint = lv_label_create(screen);
|
||||
lv_label_set_text(hint, "Проверка кириллицы, длинных слов, кнопок и нажатий.");
|
||||
lv_obj_set_width(hint, 436);
|
||||
lv_label_set_long_mode(hint, LV_LABEL_LONG_WRAP);
|
||||
lv_obj_set_style_text_font(hint, &lv_font_ru_18, 0);
|
||||
lv_obj_set_style_text_color(hint, lv_color_hex(0xD8E0EA), 0);
|
||||
lv_obj_align(hint, LV_ALIGN_TOP_MID, 0, 72);
|
||||
|
||||
makeButton(screen, "Статус", 22, 126, 136, 62, 0x355C7D);
|
||||
makeButton(screen, "Подключение", 172, 126, 136, 62, 0x2A9D8F);
|
||||
makeButton(screen, "Кошелёк", 322, 126, 136, 62, 0x457B9D);
|
||||
|
||||
makeButton(screen, "Запросы", 22, 204, 136, 62, 0xE76F51);
|
||||
makeButton(screen, "Настройки", 172, 204, 136, 62, 0x8D5A97);
|
||||
makeButton(screen, "Регистрация", 322, 204, 136, 62, 0x6A994E);
|
||||
|
||||
makeButton(screen, "Разрешить", 22, 282, 136, 62, 0xBC6C25);
|
||||
makeButton(screen, "Отклонить", 172, 282, 136, 62, 0xB56576);
|
||||
makeButton(screen, "Назад", 322, 282, 136, 62, 0x6C757D);
|
||||
|
||||
makeButton(screen, "Проверка переноса русского текста", 22, 360, 436, 64, 0x1D6F42);
|
||||
|
||||
lv_obj_t *statusPanel = lv_obj_create(screen);
|
||||
lv_obj_set_size(statusPanel, 436, 24);
|
||||
lv_obj_set_pos(statusPanel, 22, 428);
|
||||
lv_obj_set_style_radius(statusPanel, 10, 0);
|
||||
lv_obj_set_style_bg_color(statusPanel, lv_color_hex(0x162435), 0);
|
||||
lv_obj_set_style_bg_opa(statusPanel, LV_OPA_COVER, 0);
|
||||
lv_obj_set_style_border_width(statusPanel, 1, 0);
|
||||
lv_obj_set_style_border_color(statusPanel, lv_color_hex(0x4D6986), 0);
|
||||
lv_obj_set_style_pad_all(statusPanel, 2, 0);
|
||||
|
||||
gStatusLabel = lv_label_create(statusPanel);
|
||||
lv_label_set_text(gStatusLabel, "Нажато: ничего");
|
||||
lv_obj_set_style_text_font(gStatusLabel, &lv_font_ru_18, 0);
|
||||
lv_obj_set_style_text_color(gStatusLabel, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_center(gStatusLabel);
|
||||
|
||||
lv_obj_t *touchPanel = lv_obj_create(screen);
|
||||
lv_obj_set_size(touchPanel, 436, 24);
|
||||
lv_obj_set_pos(touchPanel, 22, 454);
|
||||
lv_obj_set_style_radius(touchPanel, 10, 0);
|
||||
lv_obj_set_style_bg_color(touchPanel, lv_color_hex(0x162435), 0);
|
||||
lv_obj_set_style_bg_opa(touchPanel, LV_OPA_COVER, 0);
|
||||
lv_obj_set_style_border_width(touchPanel, 1, 0);
|
||||
lv_obj_set_style_border_color(touchPanel, lv_color_hex(0x4D6986), 0);
|
||||
lv_obj_set_style_pad_all(touchPanel, 2, 0);
|
||||
|
||||
gTouchLabel = lv_label_create(touchPanel);
|
||||
lv_label_set_text(gTouchLabel, "Касание: нет x=-1 y=-1");
|
||||
lv_obj_set_style_text_font(gTouchLabel, &lv_font_ru_18, 0);
|
||||
lv_obj_set_style_text_color(gTouchLabel, lv_color_hex(0xD8E0EA), 0);
|
||||
lv_obj_center(gTouchLabel);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);
|
||||
|
||||
gfx->begin();
|
||||
gBus->writeC8D8(0x36, 0xA0);
|
||||
gfx->setBrightness(220);
|
||||
gfx->fillScreen(0x0000);
|
||||
|
||||
gTouch.setPins(PIN_TP_INT, -1);
|
||||
gTouch.begin(Wire, CST92XX_SLAVE_ADDRESS, PIN_I2C_SDA, PIN_I2C_SCL);
|
||||
gTouch.setMaxCoordinates(DISP_W, DISP_H);
|
||||
gTouch.setSwapXY(true);
|
||||
gTouch.setMirrorXY(true, false);
|
||||
|
||||
lv_init();
|
||||
|
||||
uint32_t screenWidth = gfx->width();
|
||||
uint32_t screenHeight = gfx->height();
|
||||
uint32_t bufPixels = screenWidth * 40;
|
||||
gBuf1 = (lv_color_t *)heap_caps_malloc(bufPixels * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
gBuf2 = (lv_color_t *)heap_caps_malloc(bufPixels * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
|
||||
lv_disp_draw_buf_init(&gDrawBuf, gBuf1, gBuf2, bufPixels);
|
||||
|
||||
static lv_disp_drv_t dispDrv;
|
||||
lv_disp_drv_init(&dispDrv);
|
||||
dispDrv.hor_res = screenWidth;
|
||||
dispDrv.ver_res = screenHeight;
|
||||
dispDrv.flush_cb = lvglFlushCb;
|
||||
dispDrv.draw_buf = &gDrawBuf;
|
||||
lv_disp_drv_register(&dispDrv);
|
||||
|
||||
static lv_indev_drv_t indevDrv;
|
||||
lv_indev_drv_init(&indevDrv);
|
||||
indevDrv.type = LV_INDEV_TYPE_POINTER;
|
||||
indevDrv.read_cb = lvglTouchRead;
|
||||
lv_indev_drv_register(&indevDrv);
|
||||
|
||||
const esp_timer_create_args_t tickArgs = {
|
||||
.callback = &lvglTick,
|
||||
.name = "lvgl_tick"};
|
||||
esp_timer_handle_t tickTimer = nullptr;
|
||||
esp_timer_create(&tickArgs, &tickTimer);
|
||||
esp_timer_start_periodic(tickTimer, LVGL_TICK_MS * 1000);
|
||||
|
||||
createUi();
|
||||
Serial.println("Русский LVGL font test ready: " TEST_VERSION);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
lv_timer_handler();
|
||||
delay(5);
|
||||
}
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <lvgl.h>
|
||||
#include <Arduino_GFX_Library.h>
|
||||
#include <TouchDrvCSTXXX.hpp>
|
||||
|
||||
// Подтверждено на устройстве: LVGL-рендер работает вместе с touch-путём из shine_homeserver_ui.
|
||||
|
||||
#define PIN_LCD_CS 12
|
||||
#define PIN_LCD_SCLK 38
|
||||
#define PIN_LCD_D0 4
|
||||
#define PIN_LCD_D1 5
|
||||
#define PIN_LCD_D2 6
|
||||
#define PIN_LCD_D3 7
|
||||
#define PIN_LCD_RST 2
|
||||
#define PIN_I2C_SDA 15
|
||||
#define PIN_I2C_SCL 14
|
||||
#define PIN_TP_INT 11
|
||||
|
||||
#define DISP_W 480
|
||||
#define DISP_H 480
|
||||
#define LVGL_TICK_MS 2
|
||||
#define TEST_VERSION "v1-subtouch"
|
||||
|
||||
static lv_disp_draw_buf_t gDrawBuf;
|
||||
static lv_color_t *gBuf1 = nullptr;
|
||||
static lv_color_t *gBuf2 = nullptr;
|
||||
static lv_obj_t *gStatusLabel = nullptr;
|
||||
static lv_obj_t *gTouchLabel = nullptr;
|
||||
static lv_obj_t *gVersionLabel = nullptr;
|
||||
static uint32_t gClickCount = 0;
|
||||
static bool gTouchActive = false;
|
||||
static int16_t gLastX = -1;
|
||||
static int16_t gLastY = -1;
|
||||
|
||||
Arduino_DataBus *gBus = new Arduino_ESP32QSPI(
|
||||
PIN_LCD_CS, PIN_LCD_SCLK, PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3);
|
||||
Arduino_CO5300 *gfx = new Arduino_CO5300(
|
||||
gBus, PIN_LCD_RST, 0, DISP_W, DISP_H, 0, 0, 0, 0);
|
||||
TouchDrvCST92xx gTouch;
|
||||
|
||||
static void lvglFlushCb(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *colorP) {
|
||||
uint32_t w = area->x2 - area->x1 + 1;
|
||||
uint32_t h = area->y2 - area->y1 + 1;
|
||||
#if (LV_COLOR_16_SWAP != 0)
|
||||
gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&colorP->full, w, h);
|
||||
#else
|
||||
gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&colorP->full, w, h);
|
||||
#endif
|
||||
lv_disp_flush_ready(disp);
|
||||
}
|
||||
|
||||
static void lvglTick(void *arg) {
|
||||
LV_UNUSED(arg);
|
||||
lv_tick_inc(LVGL_TICK_MS);
|
||||
}
|
||||
|
||||
static void updateTouchLabel() {
|
||||
if (!gTouchLabel) {
|
||||
return;
|
||||
}
|
||||
|
||||
char text[96];
|
||||
if (gTouchActive) {
|
||||
snprintf(text, sizeof(text), "Touch: pressed x=%d y=%d", gLastX, gLastY);
|
||||
} else {
|
||||
snprintf(text, sizeof(text), "Touch: released x=%d y=%d", gLastX, gLastY);
|
||||
}
|
||||
lv_label_set_text(gTouchLabel, text);
|
||||
}
|
||||
|
||||
static void lvglTouchRead(lv_indev_drv_t *indevDrv, lv_indev_data_t *data) {
|
||||
LV_UNUSED(indevDrv);
|
||||
int16_t x = 0;
|
||||
int16_t y = 0;
|
||||
bool touching = gTouch.getPoint(&x, &y, 1) > 0;
|
||||
|
||||
if (touching) {
|
||||
gTouchActive = true;
|
||||
gLastX = x;
|
||||
gLastY = y;
|
||||
data->state = LV_INDEV_STATE_PR;
|
||||
data->point.x = x;
|
||||
data->point.y = y;
|
||||
} else {
|
||||
gTouchActive = false;
|
||||
data->state = LV_INDEV_STATE_REL;
|
||||
data->point.x = gLastX >= 0 ? gLastX : 0;
|
||||
data->point.y = gLastY >= 0 ? gLastY : 0;
|
||||
}
|
||||
|
||||
updateTouchLabel();
|
||||
}
|
||||
|
||||
static void buttonEventCb(lv_event_t *event) {
|
||||
if (lv_event_get_code(event) != LV_EVENT_CLICKED) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char *name = (const char *)lv_event_get_user_data(event);
|
||||
gClickCount++;
|
||||
|
||||
char text[96];
|
||||
snprintf(text, sizeof(text), "Pressed: %s (#%lu)", name, (unsigned long)gClickCount);
|
||||
lv_label_set_text(gStatusLabel, text);
|
||||
Serial.println(text);
|
||||
}
|
||||
|
||||
static lv_obj_t *makeButton(lv_obj_t *parent,
|
||||
const char *labelText,
|
||||
lv_coord_t x,
|
||||
lv_coord_t y,
|
||||
lv_coord_t w,
|
||||
lv_coord_t h,
|
||||
uint32_t bgColor) {
|
||||
lv_obj_t *btn = lv_btn_create(parent);
|
||||
lv_obj_set_size(btn, w, h);
|
||||
lv_obj_set_pos(btn, x, y);
|
||||
lv_obj_set_style_radius(btn, 16, 0);
|
||||
lv_obj_set_style_bg_color(btn, lv_color_hex(bgColor), 0);
|
||||
lv_obj_set_style_bg_opa(btn, LV_OPA_COVER, 0);
|
||||
lv_obj_set_style_border_width(btn, 2, 0);
|
||||
lv_obj_set_style_border_color(btn, lv_color_hex(0x6F8BA4), 0);
|
||||
lv_obj_set_style_shadow_width(btn, 0, 0);
|
||||
lv_obj_add_event_cb(btn, buttonEventCb, LV_EVENT_CLICKED, (void *)labelText);
|
||||
|
||||
lv_obj_t *label = lv_label_create(btn);
|
||||
lv_label_set_text(label, labelText);
|
||||
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_set_style_text_font(label, &lv_font_montserrat_18, 0);
|
||||
lv_obj_center(label);
|
||||
return btn;
|
||||
}
|
||||
|
||||
static void createUi() {
|
||||
lv_obj_t *screen = lv_scr_act();
|
||||
lv_obj_set_style_bg_color(screen, lv_color_hex(0x07111C), 0);
|
||||
lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, 0);
|
||||
lv_obj_set_style_pad_all(screen, 0, 0);
|
||||
lv_obj_set_scrollbar_mode(screen, LV_SCROLLBAR_MODE_OFF);
|
||||
|
||||
gVersionLabel = lv_label_create(screen);
|
||||
lv_label_set_text(gVersionLabel, "LVGL + subserver touch " TEST_VERSION);
|
||||
lv_obj_set_style_text_font(gVersionLabel, &lv_font_montserrat_18, 0);
|
||||
lv_obj_set_style_text_color(gVersionLabel, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_align(gVersionLabel, LV_ALIGN_TOP_MID, 0, 12);
|
||||
|
||||
lv_obj_t *subtitle = lv_label_create(screen);
|
||||
lv_label_set_text(subtitle, "Touch path comes from shine_homeserver_ui. Tap buttons and watch status.");
|
||||
lv_obj_set_width(subtitle, 436);
|
||||
lv_label_set_long_mode(subtitle, LV_LABEL_LONG_WRAP);
|
||||
lv_obj_set_style_text_font(subtitle, &lv_font_montserrat_14, 0);
|
||||
lv_obj_set_style_text_color(subtitle, lv_color_hex(0xD6DEE7), 0);
|
||||
lv_obj_align(subtitle, LV_ALIGN_TOP_MID, 0, 42);
|
||||
|
||||
makeButton(screen, "Status", 22, 110, 136, 62, 0x355C7D);
|
||||
makeButton(screen, "Connect", 172, 110, 136, 62, 0x2A9D8F);
|
||||
makeButton(screen, "Wallet", 322, 110, 136, 62, 0x457B9D);
|
||||
|
||||
makeButton(screen, "Requests", 22, 188, 136, 62, 0xE76F51);
|
||||
makeButton(screen, "Settings", 172, 188, 136, 62, 0x8D5A97);
|
||||
makeButton(screen, "Register", 322, 188, 136, 62, 0x6A994E);
|
||||
|
||||
makeButton(screen, "Approve", 22, 266, 136, 62, 0xBC6C25);
|
||||
makeButton(screen, "Reject", 172, 266, 136, 62, 0xB56576);
|
||||
makeButton(screen, "Back", 322, 266, 136, 62, 0x6C757D);
|
||||
|
||||
makeButton(screen, "Bottom Action", 22, 346, 436, 68, 0x1D6F42);
|
||||
|
||||
lv_obj_t *statusPanel = lv_obj_create(screen);
|
||||
lv_obj_set_size(statusPanel, 436, 26);
|
||||
lv_obj_set_pos(statusPanel, 22, 420);
|
||||
lv_obj_set_style_radius(statusPanel, 10, 0);
|
||||
lv_obj_set_style_bg_color(statusPanel, lv_color_hex(0x162435), 0);
|
||||
lv_obj_set_style_bg_opa(statusPanel, LV_OPA_COVER, 0);
|
||||
lv_obj_set_style_border_width(statusPanel, 1, 0);
|
||||
lv_obj_set_style_border_color(statusPanel, lv_color_hex(0x4D6986), 0);
|
||||
lv_obj_set_style_pad_all(statusPanel, 2, 0);
|
||||
|
||||
gStatusLabel = lv_label_create(statusPanel);
|
||||
lv_label_set_text(gStatusLabel, "Pressed: none");
|
||||
lv_obj_set_style_text_font(gStatusLabel, &lv_font_montserrat_14, 0);
|
||||
lv_obj_set_style_text_color(gStatusLabel, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_center(gStatusLabel);
|
||||
|
||||
lv_obj_t *touchPanel = lv_obj_create(screen);
|
||||
lv_obj_set_size(touchPanel, 436, 26);
|
||||
lv_obj_set_pos(touchPanel, 22, 450);
|
||||
lv_obj_set_style_radius(touchPanel, 10, 0);
|
||||
lv_obj_set_style_bg_color(touchPanel, lv_color_hex(0x162435), 0);
|
||||
lv_obj_set_style_bg_opa(touchPanel, LV_OPA_COVER, 0);
|
||||
lv_obj_set_style_border_width(touchPanel, 1, 0);
|
||||
lv_obj_set_style_border_color(touchPanel, lv_color_hex(0x4D6986), 0);
|
||||
lv_obj_set_style_pad_all(touchPanel, 2, 0);
|
||||
|
||||
gTouchLabel = lv_label_create(touchPanel);
|
||||
lv_label_set_text(gTouchLabel, "Touch: released x=-1 y=-1");
|
||||
lv_obj_set_style_text_font(gTouchLabel, &lv_font_montserrat_14, 0);
|
||||
lv_obj_set_style_text_color(gTouchLabel, lv_color_hex(0xD6DEE7), 0);
|
||||
lv_obj_center(gTouchLabel);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);
|
||||
|
||||
gfx->begin();
|
||||
gBus->writeC8D8(0x36, 0xA0);
|
||||
gfx->setBrightness(220);
|
||||
gfx->fillScreen(0x0000);
|
||||
|
||||
gTouch.setPins(PIN_TP_INT, -1);
|
||||
gTouch.begin(Wire, CST92XX_SLAVE_ADDRESS, PIN_I2C_SDA, PIN_I2C_SCL);
|
||||
gTouch.setMaxCoordinates(DISP_W, DISP_H);
|
||||
gTouch.setSwapXY(true);
|
||||
gTouch.setMirrorXY(true, false);
|
||||
|
||||
lv_init();
|
||||
|
||||
uint32_t screenWidth = gfx->width();
|
||||
uint32_t screenHeight = gfx->height();
|
||||
uint32_t bufPixels = screenWidth * 40;
|
||||
gBuf1 = (lv_color_t *)heap_caps_malloc(bufPixels * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
gBuf2 = (lv_color_t *)heap_caps_malloc(bufPixels * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
|
||||
lv_disp_draw_buf_init(&gDrawBuf, gBuf1, gBuf2, bufPixels);
|
||||
|
||||
static lv_disp_drv_t dispDrv;
|
||||
lv_disp_drv_init(&dispDrv);
|
||||
dispDrv.hor_res = screenWidth;
|
||||
dispDrv.ver_res = screenHeight;
|
||||
dispDrv.flush_cb = lvglFlushCb;
|
||||
dispDrv.draw_buf = &gDrawBuf;
|
||||
lv_disp_drv_register(&dispDrv);
|
||||
|
||||
static lv_indev_drv_t indevDrv;
|
||||
lv_indev_drv_init(&indevDrv);
|
||||
indevDrv.type = LV_INDEV_TYPE_POINTER;
|
||||
indevDrv.read_cb = lvglTouchRead;
|
||||
lv_indev_drv_register(&indevDrv);
|
||||
|
||||
const esp_timer_create_args_t tickArgs = {
|
||||
.callback = &lvglTick,
|
||||
.name = "lvgl_tick"};
|
||||
esp_timer_handle_t tickTimer = nullptr;
|
||||
esp_timer_create(&tickArgs, &tickTimer);
|
||||
esp_timer_start_periodic(tickTimer, LVGL_TICK_MS * 1000);
|
||||
|
||||
createUi();
|
||||
Serial.println("LVGL + subserver touch test ready: " TEST_VERSION);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
lv_timer_handler();
|
||||
delay(5);
|
||||
}
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <lvgl.h>
|
||||
#include <Arduino_GFX_Library.h>
|
||||
#include "pin_config.h"
|
||||
#include "TouchDrvCSTXXX.hpp"
|
||||
|
||||
#define EXAMPLE_LVGL_TICK_PERIOD_MS 2
|
||||
|
||||
static lv_disp_draw_buf_t gDrawBuf;
|
||||
static lv_color_t *gBuf1 = nullptr;
|
||||
static lv_color_t *gBuf2 = nullptr;
|
||||
static lv_obj_t *gRawLabel = nullptr;
|
||||
static lv_obj_t *gEventLabel = nullptr;
|
||||
static lv_obj_t *gMarker = nullptr;
|
||||
|
||||
static TouchDrvCST92xx gTouch;
|
||||
static int16_t gTouchX[5];
|
||||
static int16_t gTouchY[5];
|
||||
static bool gTouchPressed = false;
|
||||
static uint32_t gRawTouchCount = 0;
|
||||
static uint32_t gLvglButtonCount = 0;
|
||||
|
||||
Arduino_DataBus *gBus = new Arduino_ESP32QSPI(
|
||||
LCD_CS, LCD_SCLK, LCD_SDIO0, LCD_SDIO1, LCD_SDIO2, LCD_SDIO3);
|
||||
Arduino_CO5300 *gfx = new Arduino_CO5300(
|
||||
gBus, LCD_RESET, 0, LCD_WIDTH, LCD_HEIGHT, 0, 0, 0, 0);
|
||||
|
||||
static void lvglFlushCb(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *colorP) {
|
||||
uint32_t w = area->x2 - area->x1 + 1;
|
||||
uint32_t h = area->y2 - area->y1 + 1;
|
||||
#if (LV_COLOR_16_SWAP != 0)
|
||||
gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&colorP->full, w, h);
|
||||
#else
|
||||
gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&colorP->full, w, h);
|
||||
#endif
|
||||
lv_disp_flush_ready(disp);
|
||||
}
|
||||
|
||||
static void lvglTick(void *arg) {
|
||||
LV_UNUSED(arg);
|
||||
lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
static void lvglTouchRead(lv_indev_drv_t *indevDrv, lv_indev_data_t *data) {
|
||||
LV_UNUSED(indevDrv);
|
||||
uint8_t touched = gTouch.getPoint(gTouchX, gTouchY, gTouch.getSupportTouchPoint());
|
||||
if (touched > 0) {
|
||||
data->state = LV_INDEV_STATE_PR;
|
||||
data->point.x = gTouchX[0];
|
||||
data->point.y = gTouchY[0];
|
||||
return;
|
||||
}
|
||||
data->state = LV_INDEV_STATE_REL;
|
||||
}
|
||||
|
||||
static void bigButtonEventCb(lv_event_t *event) {
|
||||
if (lv_event_get_code(event) != LV_EVENT_CLICKED) {
|
||||
return;
|
||||
}
|
||||
gLvglButtonCount++;
|
||||
String status = "LVGL button clicked: ";
|
||||
status += String(gLvglButtonCount);
|
||||
lv_label_set_text(gEventLabel, status.c_str());
|
||||
Serial.println(status);
|
||||
}
|
||||
|
||||
static void updateRawTouchState() {
|
||||
uint8_t touched = gTouch.getPoint(gTouchX, gTouchY, gTouch.getSupportTouchPoint());
|
||||
if (touched > 0) {
|
||||
gTouchPressed = true;
|
||||
gRawTouchCount++;
|
||||
|
||||
String line1 = "RAW pressed";
|
||||
String line2 = "x=" + String(gTouchX[0]) + " y=" + String(gTouchY[0]) + " n=" + String(gRawTouchCount);
|
||||
String text = line1 + "\n" + line2;
|
||||
lv_label_set_text(gRawLabel, text.c_str());
|
||||
|
||||
int markerX = gTouchX[0] - 8;
|
||||
int markerY = gTouchY[0] - 8;
|
||||
if (markerX < 0) markerX = 0;
|
||||
if (markerY < 0) markerY = 0;
|
||||
lv_obj_set_pos(gMarker, markerX, markerY);
|
||||
lv_obj_clear_flag(gMarker, LV_OBJ_FLAG_HIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gTouchPressed) {
|
||||
gTouchPressed = false;
|
||||
lv_label_set_text(gRawLabel, "RAW released\nTouch ended");
|
||||
lv_obj_add_flag(gMarker, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
static void createUi() {
|
||||
lv_obj_t *screen = lv_scr_act();
|
||||
lv_obj_set_style_bg_color(screen, lv_color_hex(0x000000), 0);
|
||||
lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, 0);
|
||||
|
||||
lv_obj_t *title = lv_label_create(screen);
|
||||
lv_label_set_text(title, "LVGL TOUCH DEBUG");
|
||||
lv_obj_set_style_text_font(title, &lv_font_montserrat_22, 0);
|
||||
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 12);
|
||||
|
||||
gRawLabel = lv_label_create(screen);
|
||||
lv_label_set_text(gRawLabel, "RAW released\nTouch not seen yet");
|
||||
lv_obj_set_style_text_font(gRawLabel, &lv_font_montserrat_18, 0);
|
||||
lv_obj_set_style_text_color(gRawLabel, lv_color_hex(0x9EE493), 0);
|
||||
lv_obj_align(gRawLabel, LV_ALIGN_TOP_LEFT, 16, 56);
|
||||
|
||||
gEventLabel = lv_label_create(screen);
|
||||
lv_label_set_text(gEventLabel, "LVGL button clicked: 0");
|
||||
lv_obj_set_style_text_font(gEventLabel, &lv_font_montserrat_18, 0);
|
||||
lv_obj_set_style_text_color(gEventLabel, lv_color_hex(0xFFD166), 0);
|
||||
lv_obj_align(gEventLabel, LV_ALIGN_TOP_LEFT, 16, 118);
|
||||
|
||||
lv_obj_t *btn = lv_btn_create(screen);
|
||||
lv_obj_set_size(btn, 360, 110);
|
||||
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 36);
|
||||
lv_obj_set_style_radius(btn, 18, 0);
|
||||
lv_obj_set_style_bg_color(btn, lv_color_hex(0x2A9D8F), 0);
|
||||
lv_obj_set_style_border_width(btn, 3, 0);
|
||||
lv_obj_set_style_border_color(btn, lv_color_hex(0xBDE0FE), 0);
|
||||
lv_obj_add_event_cb(btn, bigButtonEventCb, LV_EVENT_CLICKED, nullptr);
|
||||
|
||||
lv_obj_t *btnLabel = lv_label_create(btn);
|
||||
lv_label_set_text(btnLabel, "Tap Here");
|
||||
lv_obj_set_style_text_font(btnLabel, &lv_font_montserrat_24, 0);
|
||||
lv_obj_set_style_text_color(btnLabel, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_center(btnLabel);
|
||||
|
||||
lv_obj_t *hint = lv_label_create(screen);
|
||||
lv_label_set_text(hint, "Need both: RAW coords change and LVGL button click.");
|
||||
lv_obj_set_width(hint, 430);
|
||||
lv_label_set_long_mode(hint, LV_LABEL_LONG_WRAP);
|
||||
lv_obj_set_style_text_font(hint, &lv_font_montserrat_16, 0);
|
||||
lv_obj_set_style_text_color(hint, lv_color_hex(0xD9D9D9), 0);
|
||||
lv_obj_align(hint, LV_ALIGN_BOTTOM_MID, 0, -34);
|
||||
|
||||
gMarker = lv_obj_create(screen);
|
||||
lv_obj_set_size(gMarker, 16, 16);
|
||||
lv_obj_set_style_radius(gMarker, LV_RADIUS_CIRCLE, 0);
|
||||
lv_obj_set_style_bg_color(gMarker, lv_color_hex(0xFF4D6D), 0);
|
||||
lv_obj_set_style_border_width(gMarker, 2, 0);
|
||||
lv_obj_set_style_border_color(gMarker, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_add_flag(gMarker, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Wire.begin(IIC_SDA, IIC_SCL);
|
||||
|
||||
pinMode(TP_RST, OUTPUT);
|
||||
pinMode(TP_INT, INPUT);
|
||||
digitalWrite(TP_RST, LOW);
|
||||
delay(30);
|
||||
digitalWrite(TP_RST, HIGH);
|
||||
delay(50);
|
||||
delay(1000);
|
||||
|
||||
gTouch.setPins(TP_RST, TP_INT);
|
||||
bool touchOk = gTouch.begin(Wire, 0x5A, IIC_SDA, IIC_SCL);
|
||||
if (!touchOk) {
|
||||
Serial.println("Touch init failed");
|
||||
} else {
|
||||
Serial.print("Touch model: ");
|
||||
Serial.println(gTouch.getModelName());
|
||||
gTouch.sleep();
|
||||
gTouch.reset();
|
||||
gTouch.setMaxCoordinates(480, 480);
|
||||
gTouch.setSwapXY(true);
|
||||
gTouch.setMirrorXY(true, false);
|
||||
}
|
||||
|
||||
gfx->begin();
|
||||
gBus->writeC8D8(0x36, 0xA0);
|
||||
gfx->setBrightness(220);
|
||||
gfx->fillScreen(0x0000);
|
||||
|
||||
lv_init();
|
||||
|
||||
uint32_t screenWidth = gfx->width();
|
||||
uint32_t screenHeight = gfx->height();
|
||||
gBuf1 = (lv_color_t *)heap_caps_malloc(screenWidth * screenHeight / 4 * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
gBuf2 = (lv_color_t *)heap_caps_malloc(screenWidth * screenHeight / 4 * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
lv_disp_draw_buf_init(&gDrawBuf, gBuf1, gBuf2, screenWidth * screenHeight / 4);
|
||||
|
||||
static lv_disp_drv_t dispDrv;
|
||||
lv_disp_drv_init(&dispDrv);
|
||||
dispDrv.hor_res = screenWidth;
|
||||
dispDrv.ver_res = screenHeight;
|
||||
dispDrv.flush_cb = lvglFlushCb;
|
||||
dispDrv.draw_buf = &gDrawBuf;
|
||||
lv_disp_drv_register(&dispDrv);
|
||||
|
||||
static lv_indev_drv_t indevDrv;
|
||||
lv_indev_drv_init(&indevDrv);
|
||||
indevDrv.type = LV_INDEV_TYPE_POINTER;
|
||||
indevDrv.read_cb = lvglTouchRead;
|
||||
lv_indev_drv_register(&indevDrv);
|
||||
|
||||
const esp_timer_create_args_t lvglTickTimerArgs = {
|
||||
.callback = &lvglTick,
|
||||
.name = "lvgl_tick"};
|
||||
esp_timer_handle_t lvglTickTimer = nullptr;
|
||||
esp_timer_create(&lvglTickTimerArgs, &lvglTickTimer);
|
||||
esp_timer_start_periodic(lvglTickTimer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000);
|
||||
|
||||
createUi();
|
||||
Serial.println("LVGL touch debug test ready");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
updateRawTouchState();
|
||||
lv_timer_handler();
|
||||
delay(5);
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <Arduino_GFX_Library.h>
|
||||
#include <U8g2lib.h>
|
||||
|
||||
#define PIN_LCD_CS 12
|
||||
#define PIN_LCD_SCLK 38
|
||||
#define PIN_LCD_D0 4
|
||||
#define PIN_LCD_D1 5
|
||||
#define PIN_LCD_D2 6
|
||||
#define PIN_LCD_D3 7
|
||||
#define PIN_LCD_RST 2
|
||||
#define PIN_I2C_SDA 15
|
||||
#define PIN_I2C_SCL 14
|
||||
|
||||
#define DISP_W 480
|
||||
#define DISP_H 480
|
||||
|
||||
#define C_BG 0x0841u
|
||||
#define C_PANEL 0x1082u
|
||||
#define C_CARD 0x18C3u
|
||||
#define C_BORDER 0x39C7u
|
||||
#define C_TEXT 0xFFFFu
|
||||
#define C_MUTE 0xBDF7u
|
||||
#define C_OK 0x3666u
|
||||
#define C_WARN 0xECA0u
|
||||
#define C_BAD 0xD145u
|
||||
#define C_BUTTON 0x2145u
|
||||
#define C_BUTTON2 0x3186u
|
||||
|
||||
#define FONT_HEAD u8g2_font_10x20_t_cyrillic
|
||||
#define FONT_BODY u8g2_font_9x15_t_cyrillic
|
||||
#define FONT_SMALL u8g2_font_6x13_t_cyrillic
|
||||
|
||||
Arduino_DataBus *gBus = new Arduino_ESP32QSPI(
|
||||
PIN_LCD_CS, PIN_LCD_SCLK, PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3);
|
||||
Arduino_CO5300 *gfx = new Arduino_CO5300(
|
||||
gBus, PIN_LCD_RST, 0, DISP_W, DISP_H, 0, 0, 0, 0);
|
||||
|
||||
static void drawPanel(int x, int y, int w, int h, uint16_t fill, uint16_t border, int radius = 10) {
|
||||
gfx->fillRoundRect(x, y, w, h, radius, fill);
|
||||
gfx->drawRoundRect(x, y, w, h, radius, border);
|
||||
}
|
||||
|
||||
static void drawDefaultText(int x, int y, const char *text, uint16_t color, uint8_t size = 1) {
|
||||
gfx->setFont();
|
||||
gfx->setTextSize(size);
|
||||
gfx->setTextColor(color);
|
||||
gfx->setCursor(x, y);
|
||||
gfx->print(text);
|
||||
}
|
||||
|
||||
static void drawU8Text(int x, int y, const String &text, uint16_t color, const uint8_t *font) {
|
||||
gfx->setTextSize(1);
|
||||
gfx->setFont(font);
|
||||
gfx->setTextColor(color);
|
||||
gfx->setCursor(x, y);
|
||||
gfx->print(text);
|
||||
}
|
||||
|
||||
static void drawButton(int x, int y, int w, int h, uint16_t fill, const String &label, const uint8_t *font, bool useDefaultFont = false) {
|
||||
drawPanel(x, y, w, h, fill, C_BORDER, 12);
|
||||
if (useDefaultFont) {
|
||||
drawDefaultText(x + 14, y + 30, label.c_str(), C_TEXT, 2);
|
||||
return;
|
||||
}
|
||||
drawU8Text(x + 14, y + 32, label, C_TEXT, font);
|
||||
}
|
||||
|
||||
static void drawScreen() {
|
||||
gfx->fillScreen(C_BG);
|
||||
|
||||
drawPanel(12, 12, 456, 456, C_PANEL, C_BORDER, 16);
|
||||
drawDefaultText(28, 42, "TEXT TEST 123", C_TEXT, 2);
|
||||
drawU8Text(28, 72, "Default ASCII above", C_MUTE, (const uint8_t *)FONT_SMALL);
|
||||
|
||||
drawPanel(24, 90, 432, 72, C_CARD, C_BORDER, 12);
|
||||
drawDefaultText(38, 118, "A: Default font ABC123", C_TEXT, 2);
|
||||
drawDefaultText(38, 146, "Visible? then base path OK", C_WARN, 1);
|
||||
|
||||
drawPanel(24, 174, 432, 84, C_CARD, C_BORDER, 12);
|
||||
drawU8Text(38, 204, "B: U8g2 ASCII abc123", C_TEXT, (const uint8_t *)FONT_BODY);
|
||||
drawU8Text(38, 230, "C: Русский текст 123", C_OK, (const uint8_t *)FONT_BODY);
|
||||
|
||||
drawPanel(24, 270, 432, 84, C_CARD, C_BORDER, 12);
|
||||
drawU8Text(38, 298, "D: Мелкий шрифт кнопок", C_TEXT, (const uint8_t *)FONT_SMALL);
|
||||
drawU8Text(38, 320, "Если это видно, FONT_SMALL жив", C_MUTE, (const uint8_t *)FONT_SMALL);
|
||||
|
||||
drawButton(24, 368, 128, 72, C_BUTTON, "BTN 1", nullptr, true);
|
||||
drawButton(176, 368, 128, 72, C_OK, "abc123", (const uint8_t *)FONT_BODY);
|
||||
drawButton(328, 368, 128, 72, C_BUTTON2, "Русский", (const uint8_t *)FONT_BODY);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);
|
||||
|
||||
gfx->begin();
|
||||
gBus->writeC8D8(0x36, 0xA0);
|
||||
gfx->setBrightness(220);
|
||||
gfx->setUTF8Print(true);
|
||||
|
||||
drawScreen();
|
||||
|
||||
Serial.println();
|
||||
Serial.println("=== text_render_test ===");
|
||||
Serial.println("A: default Arduino_GFX font");
|
||||
Serial.println("B: U8g2 ASCII");
|
||||
Serial.println("C: U8g2 Cyrillic");
|
||||
Serial.println("Buttons: default / ASCII / Cyrillic");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(1000);
|
||||
}
|
||||
Reference in New Issue
Block a user