Add GPIO26 speaker beep and melody test.

Play audio on boot and every 12s via the onboard amp; show status on the LVGL UI and document pins.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-17 17:30:39 +08:00
parent 23956897fa
commit 82e1ace24f
8 changed files with 132 additions and 18 deletions

49
src/audio_test.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include "audio_test.h"
#include "pins.h"
static const char *g_status = "Idle";
void audio_init() {
pinMode(SPEAKER_PIN, OUTPUT);
digitalWrite(SPEAKER_PIN, LOW);
noTone(SPEAKER_PIN);
g_status = "Ready GPIO26";
Serial.printf("Speaker init on GPIO%d (SC8002B amp)\n", SPEAKER_PIN);
}
static void beep(uint16_t freq_hz, uint16_t ms) {
tone(SPEAKER_PIN, freq_hz);
delay(ms);
noTone(SPEAKER_PIN);
digitalWrite(SPEAKER_PIN, LOW);
}
void audio_play_beep_sequence() {
g_status = "Beep x3";
Serial.println("Audio: beep sequence");
beep(880, 120);
delay(80);
beep(1175, 120);
delay(80);
beep(1397, 180);
delay(50);
g_status = "Beep done";
}
void audio_play_melody() {
// 简化版:C D E G A
static const uint16_t notes[] = {523, 587, 659, 784, 880, 784, 659};
static const uint16_t durs[] = {160, 160, 160, 200, 240, 160, 280};
g_status = "Melody";
Serial.println("Audio: melody test");
for (size_t i = 0; i < sizeof(notes) / sizeof(notes[0]); ++i) {
beep(notes[i], durs[i]);
delay(40);
}
g_status = "Melody done";
}
const char *audio_last_status() {
return g_status;
}

View File

@@ -2,6 +2,7 @@
#include <TFT_eSPI.h>
#include <lvgl.h>
#include "audio_test.h"
#include "pins.h"
#include "sd_info.h"
#include "ui.h"
@@ -11,7 +12,9 @@ static constexpr uint32_t DRAW_BUF_SIZE =
static uint32_t draw_buf[DRAW_BUF_SIZE / 4];
static uint32_t last_refresh_ms = 0;
static uint32_t last_audio_ms = 0;
static constexpr uint32_t REFRESH_MS = 3000;
static constexpr uint32_t AUDIO_INTERVAL_MS = 12000;
static void refresh_sd_ui() {
SdCardInfo info = sd_read_info();
@@ -31,26 +34,29 @@ static void refresh_sd_ui() {
}
ui_update(info);
ui_set_audio_status(audio_last_status());
}
void setup() {
Serial.begin(115200);
delay(200);
Serial.println();
Serial.printf("LVGL %d.%d.%d | CYD TF info\n", lv_version_major(),
Serial.printf("LVGL %d.%d.%d | CYD TF + Speaker\n", lv_version_major(),
lv_version_minor(), lv_version_patch());
pinMode(TFT_BL_PIN, OUTPUT);
digitalWrite(TFT_BL_PIN, HIGH);
audio_init();
lv_init();
lv_display_t *disp =
lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));
// 横屏:320x240
lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270);
ui_create();
ui_set_audio_status(audio_last_status());
if (!sd_init()) {
Serial.println("SD mount failed at boot");
@@ -58,6 +64,13 @@ void setup() {
refresh_sd_ui();
last_refresh_ms = millis();
// 开机立刻测喇叭
audio_play_beep_sequence();
delay(200);
audio_play_melody();
ui_set_audio_status(audio_last_status());
last_audio_ms = millis();
}
void loop() {
@@ -70,5 +83,11 @@ void loop() {
refresh_sd_ui();
}
if (now - last_audio_ms >= AUDIO_INTERVAL_MS) {
last_audio_ms = now;
audio_play_beep_sequence();
ui_set_audio_status(audio_last_status());
}
delay(5);
}

View File

@@ -12,6 +12,7 @@ static lv_obj_t *lbl_free;
static lv_obj_t *lbl_files;
static lv_obj_t *bar_usage;
static lv_obj_t *lbl_hint;
static lv_obj_t *lbl_audio;
static lv_obj_t *make_row(lv_obj_t *parent, const char *title, lv_obj_t **value_out) {
lv_obj_t *row = lv_obj_create(parent);
@@ -82,6 +83,8 @@ void ui_create() {
make_row(col, "Used", &lbl_used);
make_row(col, "Free", &lbl_free);
make_row(col, "Root files", &lbl_files);
make_row(col, "Speaker", &lbl_audio);
lv_label_set_text(lbl_audio, "GPIO26");
bar_usage = lv_bar_create(col);
lv_obj_set_width(bar_usage, LV_PCT(100));
@@ -144,7 +147,14 @@ void ui_update(const SdCardInfo &info) {
}
lv_bar_set_value(bar_usage, pct, LV_ANIM_ON);
char hint[40];
snprintf(hint, sizeof(hint), "Used %d%% | auto refresh", pct);
char hint[48];
snprintf(hint, sizeof(hint), "Used %d%% | SD+Audio", pct);
lv_label_set_text(lbl_hint, hint);
}
void ui_set_audio_status(const char *text) {
if (lbl_audio == nullptr || text == nullptr) {
return;
}
lv_label_set_text(lbl_audio, text);
}