Arduino 开发
本章节包含以下部分,请按需阅读:
Arduino 入门教程
初次接触 Arduino ESP32 开发,想要快速上手?我们为您准备了一套通用的 入门教程。
- 第0节 认识 ESP32
- 第1节 安装和配置 Arduino IDE
- 第2节 Arduino 基础知识
- 第3节 数字输出/输入
- 第4节 模拟输入
- 第5节 脉冲宽度调制 (PWM)
- 第6节 串行通信 (UART)
- 第7节 I2C 通信
- 第8节 SPI 通信
- 第9节 Wi-Fi 基础用法
- 第10节 网页服务器
- 第11节 蓝牙 (Bluetooth)
- 第12节 LVGL 图形界面开发
- 第13节 综合项目
请注意:该教程使用 ESP32-S3-Zero 作为教学示例,所有硬件代码均基于其引脚布局。在动手实践前,建议您对照手中的开发板引脚图,确认引脚配置无误。
配置开发环境
安装 Arduino IDE
请参考 安装和配置 Arduino IDE 教程 安装 Arduino IDE,并添加 ESP32 开发板支持。
选择开发板和端口
连接 ESP32-C5-Touch-LCD-1.69 到电脑后,在"工具"菜单中选择对应的串口号。
安装示例库
Arduino 示例位于工程的 example/arduino/examples 目录。运行示例前,先解压 example/arduino/ESP32_C5_Touch_LCD_1in69.zip,再将解压后的库放入 Arduino 默认的 libraries 目录。
Arduino 库文件夹通常位于:
C:\Users\<用户名>\Documents\Arduino\libraries
也可以在 Arduino IDE 中通过"文件 > 首选项"查看"项目文件夹位置",并在该路径下找到 libraries 文件夹。
示例程序
| 示例程序 | 基础例程说明 |
|---|---|
| 01_RGB_Test | 屏幕刷色测试 |
| 02_Mic_Speaker_Test | 麦克风与扬声器测试 |
| 03_IMU_Test | IMU 测试 |
| 04_Bat_Test | 电池检测测试 |
| 05_RTC_Test | RTC 测试 |
| 06_LVGL_Demo_Test | LVGL 示例测试 |
01_RGB_Test
硬件连接
- 使用 USB 线将开发板接入电脑。
代码分析
RGB 刷色测试代码
static const uint16_t Colors[] = {
0xF800, // Red
0x07E0, // Green
0x001F, // Blue
0xFFFF, // White
0x0000, // Black
};
static const char *ColorNames[] = {
"R", "G", "B", "W", "BL",
};
void setup(void)
{
Serial.begin(115200);
delay(200);
if (!bsp_display_init()) {
Serial.println("bsp_display_init failed");
while (true) {
delay(1000);
}
}
bsp_display_backlight_on();
bsp_display_brightness_set(100);
}
void loop(void)
{
bsp_display_fill(Colors[color_index]);
Serial.printf("Show color: %s (%u)\n", ColorNames[color_index], color_index);
color_index++;
if (color_index >= (sizeof(Colors) / sizeof(Colors[0]))) {
color_index = 0;
}
delay(2000);
}
代码解释
Colors[]/ColorNames[]:RGB565 颜色值和名称数组(红/绿/蓝/白/黑),用于循环刷屏。bsp_display_init():初始化 SPI 总线和 ST7789 LCD 面板。bsp_display_backlight_on():点亮 LCD 背光。bsp_display_brightness_set(100):设置背光亮度为 100%。bsp_display_fill(Colors[color_index]):用指定 RGB565 颜色填充整屏。Serial.printf(...):通过串口输出当前颜色名称和索引,便于调试。
运行效果
- 屏幕依次显示红、绿、蓝、白、黑五种纯色,每种停留 2 秒后切换。
- 串口每 2 秒输出一次当前颜色名称和索引。
02_Mic_Speaker_Test
硬件连接
- 使用 USB 线将开发板接入电脑。
代码分析
麦克风回环播放代码
constexpr size_t audio_frame_bytes = 1024;
constexpr uint8_t speaker_volume = 70;
constexpr uint8_t mic_gain_db = 18;
uint8_t audio_buffer[audio_frame_bytes];
void setup()
{
Serial.begin(115200);
if (!bsp_audio_init()) {
Serial.println("bsp_audio_init failed");
return;
}
bsp_audio_set_speaker_volume(speaker_volume);
bsp_audio_set_mic_gain(mic_gain_db);
Serial.println("microphone loopback to speaker");
}
void loop()
{
size_t bytes_read = 0;
size_t bytes_written = 0;
if (!bsp_audio_read(audio_buffer, sizeof(audio_buffer), &bytes_read) || (bytes_read == 0)) {
Serial.println("bsp_audio_read failed");
delay(10);
return;
}
if (!bsp_audio_write(audio_buffer, bytes_read, &bytes_written) || (bytes_written != bytes_read)) {
Serial.println("bsp_audio_write failed");
delay(10);
}
}
代码解释
bsp_audio_init():初始化 I2S 总线和 ES8311 编解码器。bsp_audio_set_speaker_volume(70):设置扬声器音量为 70。bsp_audio_set_mic_gain(18):设置麦克风增益为 18dB。bsp_audio_read(audio_buffer, ...):从麦克风读取 1024 字节音频数据。bsp_audio_write(audio_buffer, ...):将读取的音频数据写入扬声器,实现实时回环。
运行效果
- 串口输出
microphone loopback to speaker。 - 对着麦克风说话,可从扬声器实时听到回放声音。
03_IMU_Test
硬件连接
- 使用 USB 线将开发板接入电脑。
代码分析
QMI8658 初始化与数据读取代码
#define QMI8658_I2C_ADDRESS 0x6B
static QMI8658 imu(Wire);
static AccelData accel_data = { 0 };
static GyroData gyro_data = { 0 };
void setup(void)
{
Serial.begin(115200);
bsp_display_brightness_init();
bsp_display_brightness_set(100);
if (!bsp_i2c_init()) {
Serial.println("I2C init failed");
while (true) {
delay(1000);
}
}
imu.init(imu_calibration, QMI8658_I2C_ADDRESS);
imu.setAccelRange(8);
imu.setGyroRange(512);
imu.setAccelODR(1000);
imu.setGyroODR(1000);
Serial.println("QMI8658 ready");
}
void loop(void)
{
imu.update();
imu.getAccel(&accel_data);
imu.getGyro(&gyro_data);
Serial.print("ACC[g] ");
Serial.print(accel_data.accelX, 3);
Serial.print(", ");
Serial.print(accel_data.accelY, 3);
Serial.print(", ");
Serial.print(accel_data.accelZ, 3);
Serial.print(" GYRO[dps] ");
Serial.print(gyro_data.gyroX, 3);
Serial.print(", ");
Serial.print(gyro_data.gyroY, 3);
Serial.print(", ");
Serial.print(gyro_data.gyroZ, 3);
Serial.print(" TEMP[C] ");
Serial.println(imu.getTemp(), 2);
delay(100);
}
代码解释
bsp_i2c_init():初始化 I2C 总线(SDA=GPIO8,SCL=GPIO9,400kHz)。imu.init(imu_calibration, QMI8658_I2C_ADDRESS):初始化 QMI8658,I2C 地址 0x6B。imu.setAccelRange(8)/imu.setGyroRange(512):设置加速度量程 ±8g、陀螺仪量程 ±512dps。imu.setAccelODR(1000)/imu.setGyroODR(1000):设置加速度和陀螺仪输出数据率为 1000Hz。imu.update():更新传感器数据。imu.getAccel(&accel_data)/imu.getGyro(&gyro_data):读取加速度和陀螺仪数据。imu.getTemp():读取温度数据。
运行效果
- 串口输出
QMI8658 ready后,每 100ms 输出一行加速度(g)、陀螺仪(dps)和温度(℃)数据。 - 轻微倾斜或转动开发板时,加速度和陀螺仪数据会随姿态变化。

04_Bat_Test
硬件连接
- 使用 USB 线将开发板接入电脑。
- 连接锂电池到开发板。
代码分析
电池信息读取与显示代码
static const uint16_t battery_capacity_mah = 1500;
static const bsp_display_lvgl_partial_cfg_t battery_display_cfg = {
.use_psram = false,
.double_buffer = false,
.buffer_height = 120,
};
static void bat_update_label(const bsp_bat_info_t *bat_info)
{
const char *battery_state = "Idle";
const char *battery_note = "";
battery_state = (bat_info->ma > 0) ? "Charging" : ((bat_info->ma < 0) ? "Discharging" : "Idle");
battery_note = (bat_info->ma == 0) ? "battery not connected or full" : "battery connected";
lv_label_set_text_fmt(battery_label,
"Battery Test\n"
"State: %s\n"
"Voltage: %u mV\n"
"Current: %d mA\n"
"SOC: %u %%\n"
"Temp: %d C\n"
"Capacity: %u mAh\n"
"%s",
battery_state,
bat_info->mv,
bat_info->ma,
bat_info->soc,
bat_info->tc,
battery_capacity_mah,
battery_note);
}
void setup(void)
{
bsp_display_start_partial(&battery_display_cfg);
bsp_display_brightness_set(100);
bsp_bat_init(battery_capacity_mah);
bsp_display_lock(0);
battery_label = lv_label_create(lv_scr_act());
lv_obj_align(battery_label, LV_ALIGN_TOP_LEFT, 10, 10);
lv_label_set_text(battery_label, "Battery Test\nBattery status updating");
bsp_display_unlock();
}
void loop(void)
{
bsp_bat_info_t bat_info = {};
if (!bsp_get_bat_info(&bat_info)) {
Serial.println("battery info update failed");
return;
}
if (bat_info_changed(&battery_last_info, &bat_info)) {
if (bsp_display_lock(0)) {
bat_update_label(&bat_info);
battery_last_info = bat_info;
bsp_display_unlock();
}
}
delay(1000);
}
代码解释
bsp_display_start_partial(&battery_display_cfg):以局部刷新模式启动 LVGL,减少显存占用。bsp_bat_init(battery_capacity_mah):初始化 BQ27220 电量计,设置电池容量为 1500mAh。bsp_get_bat_info(&bat_info):读取电池信息(电压/电流/SOC/温度等)。bsp_display_lock(0)/bsp_display_unlock():获取/释放 LVGL 互斥锁,确保线程安全。lv_label_set_text_fmt(battery_label, ...):格式化并更新电池信息标签。bat_info_changed(...):判断电池数据是否变化,避免无意义刷新。
运行效果
- 屏幕显示电池状态信息:状态(Charging/Discharging/Idle)、电压(mV)、电流(mA)、SOC(%)、温度(℃)、容量(mAh)。
- 串口同步输出电池数据。
- 接入电池后,电流不为 0 时状态为 Charging 或 Discharging;未接电池或充满时状态为 Idle。

05_RTC_Test
硬件连接
- 使用 USB 线将开发板接入电脑。
代码分析
RTC 初始化与时间读取代码
static PCF85063A rtc(Wire);
static void rtc_set_to_build_time(void)
{
struct tm now_tm = {};
const char *build_date = __DATE__;
const char *build_time = __TIME__;
now_tm.tm_year = ((build_date[7] - '0') * 1000 + (build_date[8] - '0') * 100 +
(build_date[9] - '0') * 10 + (build_date[10] - '0')) - 1900;
now_tm.tm_mon = month_from_build_date(build_date);
now_tm.tm_mday = (build_date[4] == ' ') ? (build_date[5] - '0')
: ((build_date[4] - '0') * 10 + (build_date[5] - '0'));
now_tm.tm_hour = (build_time[0] - '0') * 10 + (build_time[1] - '0');
now_tm.tm_min = (build_time[3] - '0') * 10 + (build_time[4] - '0');
now_tm.tm_sec = (build_time[6] - '0') * 10 + (build_time[7] - '0');
rtc.set(&now_tm);
}
void setup(void)
{
Serial.begin(115200);
bsp_display_brightness_init();
bsp_display_brightness_set(100);
bsp_i2c_init();
rtc.begin();
if (rtc.oscillator_stop()) {
Serial.println("RTC lost power, set to build time");
rtc_set_to_build_time();
} else {
Serial.println("RTC running");
}
}
void loop(void)
{
time_t current_time = rtc.time(NULL);
struct tm *now_tm = localtime(¤t_time);
Serial.printf("%04d-%02d-%02d %02d:%02d:%02d\r\n",
now_tm->tm_year + 1900,
now_tm->tm_mon + 1,
now_tm->tm_mday,
now_tm->tm_hour,
now_tm->tm_min,
now_tm->tm_sec);
delay(1000);
}
代码解释
bsp_i2c_init():初始化 I2C 总线。rtc.begin():初始化 PCF85063A RTC 芯片。rtc.oscillator_stop():检测 RTC 是否停振(掉电),返回 true 表示时间丢失。rtc_set_to_build_time():使用编译时间(__DATE__/__TIME__)设置 RTC 时间。rtc.time(NULL):读取 RTC 时间戳。localtime(¤t_time):将时间戳转换为本地时间结构体,便于格式化输出。
运行效果
- 串口输出
RTC running或RTC lost power, set to build time。 - 随后每秒输出一次
YYYY-MM-DD HH:MM:SS格式的时间。

06_LVGL_Demo_Test
硬件连接
- 使用 USB 线将开发板接入电脑。
代码分析
LVGL Benchmark 初始化代码
static const bsp_display_lvgl_full_frame_cfg_t lgvl_config = {
.use_psram = true,
.double_buffer = true,
.full_refresh = true,
};
void setup(void)
{
Serial.begin(115200);
bsp_display_start_full_frame(&lgvl_config);
bsp_display_brightness_set(100);
bsp_display_lock(0);
lv_demo_benchmark_set_max_speed(true);
lv_demo_benchmark();
bsp_display_unlock();
}
void loop(void)
{
delay(1000);
}
代码解释
bsp_display_start_full_frame(&lgvl_config):以全帧模式启动 LVGL,启用 PSRAM、双缓冲和全刷新,获得最佳显示效果。bsp_display_brightness_set(100):设置背光亮度为 100%。bsp_display_lock(0)/bsp_display_unlock():获取/释放 LVGL 互斥锁,确保 UI 操作线程安全。lv_demo_benchmark_set_max_speed(true):设置 benchmark 为最大速度模式。lv_demo_benchmark():启动 LVGL benchmark 跑分演示。
运行效果
- 屏幕依次显示 LVGL benchmark 各项测试画面(矩形、阴影、文字、图片、动画等渲染性能测试)。
- 测试完成后显示各项 FPS 评分汇总。