Skip to main content

TRMNL 7.5" (OG) DIY 套件 Arduino 入门指南

介绍

TRMNL 7.5" (OG) DIY 套件是一个多功能开发平台,以强大的 XIAO ESP32-S3 Plus 作为其核心。这个 DIY 套件将 ESP32-S3 的处理能力与美观的 7.5 英寸电子纸显示屏相结合,为低功耗信息显示项目创造了完美的基础。本指南专注于使用 Arduino 框架对 TRMNL DIY 套件进行编程,为您提供利用其各种硬件功能的基础知识。

入门指南

在深入了解具体功能之前,让我们先为 TRMNL 7.5" (OG) DIY 套件设置开发环境。

环境准备

要使用 Arduino 对 TRMNL DIY 套件进行编程,您需要设置支持 ESP32 的 Arduino IDE。

tip

如果这是您第一次使用 Arduino,我们强烈建议您参考 Arduino 入门指南

步骤 1. 下载并安装 Arduino IDE 并启动 Arduino 应用程序。


步骤 2. 向 Arduino IDE 添加 ESP32 开发板支持。

在 Arduino IDE 中,转到 文件 > 首选项,并将以下 URL 添加到"附加开发板管理器网址"字段中:

https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json

步骤 3. 安装 ESP32 开发板包。

导航到 工具 > 开发板 > 开发板管理器,搜索 "esp32" 并安装由 Espressif Systems 提供的 ESP32 包。

步骤 4. 选择正确的开发板。

转到 工具 > 开发板 > ESP32 Arduino 并选择 "XIAO_ESP32S3_PLUS"。

步骤 5. 使用 USB-C 线缆将您的 TRMNL DIY Kit 连接到计算机。

步骤 6.工具 > 端口 选择正确的端口。

Arduino 示例

现在让我们通过 Arduino 代码示例来探索 TRMNL DIY Kit 的主要功能。

用户按钮

TRMNL DIY Kit 具有三个用户可编程按钮(D1、D2、D4)和一个复位按钮。让我们创建一个简单的示例来检测按钮按压。

按钮测试示例

此示例将读取三个用户按钮的状态并将其状态打印到串行监视器。

// TRMNL DIY Kit - Button Test Example

// Define button pins
const int BUTTON_D1 = D1; // First user button
const int BUTTON_D2 = D2; // Second user button
const int BUTTON_D4 = D4; // Third user button

void setup() {
// Initialize serial communication
Serial.begin(115200);
while (!Serial) {
; // Wait for serial port to connect
}

Serial.println("TRMNL DIY Kit - Button Test");

// Configure button pins as inputs with internal pull-up resistors
pinMode(BUTTON_D1, INPUT_PULLUP);
pinMode(BUTTON_D2, INPUT_PULLUP);
pinMode(BUTTON_D4, INPUT_PULLUP);
}

void loop() {
// Read button states (buttons are LOW when pressed because of pull-up resistors)
bool d1Pressed = !digitalRead(BUTTON_D1);
bool d2Pressed = !digitalRead(BUTTON_D2);
bool d4Pressed = !digitalRead(BUTTON_D4);

// Print button states if any button is pressed
if (d1Pressed || d2Pressed || d4Pressed) {
Serial.print("Button D1: ");
Serial.print(d1Pressed ? "PRESSED" : "released");
Serial.print(" | Button D2: ");
Serial.print(d2Pressed ? "PRESSED" : "released");
Serial.print(" | Button D4: ");
Serial.println(d4Pressed ? "PRESSED" : "released");

// Add a small delay to avoid repeated readings
delay(200);
}
}

代码工作原理:

  1. 我们为三个用户按钮(D1、D2、D4)定义引脚常量。

  2. setup() 函数中,我们初始化串行通信并将按钮引脚配置为带内部上拉电阻的输入。

  3. loop() 函数中,我们读取每个按钮的状态。由于我们使用上拉电阻,当按钮被按下时引脚读取为 LOW。

  4. 我们反转读取值(使用 ! 操作符),使得 true 表示"按下",false 表示"释放"。

  5. 如果任何按钮被按下,我们将所有按钮的状态打印到串行监视器。

  6. 小的延迟可防止按钮被按住时快速重复读取。

电池电压监测

TRMNL 7.5"(OG)DIY 套件包含一个用于监测连接到引脚 D0(GPIO1)的电池电压的电路。该设计的一个重要特性是它还包含一个控制引脚(GPIO6)来启用/禁用 ADC 电源,这有助于在不主动测量时节省电池电量。

电池电压监测示例

// TRMNL DIY Kit - Battery Voltage Monitoring Example

#define BATTERY_PIN 1 // GPIO1 (A0) - BAT_ADC
#define ADC_EN_PIN 6 // GPIO6 (A5) - ADC_EN

const float CALIBRATION_FACTOR = 0.968;

void setup() {
// Initialize serial communication
Serial.begin(115200);
while (!Serial) {
; // Wait for serial port to connect
}

Serial.println("TRMNL DIY Kit - Battery Voltage Monitoring Example");

// Configure ADC_EN
pinMode(ADC_EN_PIN, OUTPUT);
digitalWrite(ADC_EN_PIN, LOW); // Start with ADC disabled to save power

// Configure ADC
analogReadResolution(12);
analogSetPinAttenuation(BATTERY_PIN, ADC_11db);
}

void loop() {
// Read battery voltage
float voltage = readBatteryVoltage();

// Print the results
Serial.print("Battery Voltage: ");
Serial.print(voltage, 2); // Print with 2 decimal places
Serial.println("V");

// Determine battery level
String batteryStatus;
if (voltage >= 4.0) {
batteryStatus = "Full";
} else if (voltage >= 3.7) {
batteryStatus = "Good";
} else if (voltage >= 3.5) {
batteryStatus = "Medium";
} else if (voltage >= 3.2) {
batteryStatus = "Low";
} else {
batteryStatus = "Critical";
}

Serial.print("Battery Status: ");
Serial.println(batteryStatus);
Serial.println();

// Wait for a while before the next reading
delay(5000); // 5 seconds
}

float readBatteryVoltage() {
// Enable ADC
digitalWrite(ADC_EN_PIN, HIGH);
delay(10); // Short delay to stabilize

// Read 30 times and average for more stable readings
long sum = 0;
for(int i = 0; i < 30; i++) {
sum += analogRead(BATTERY_PIN);
delayMicroseconds(100);
}

// Disable ADC to save power
digitalWrite(ADC_EN_PIN, LOW);

// Calculate voltage
float adc_avg = sum / 30.0;
float voltage = (adc_avg / 4095.0) * 3.6 * 2.0 * CALIBRATION_FACTOR;

return voltage;
}

代码工作原理:

  1. 我们定义了两个重要的引脚:
  • BATTERY_PIN (GPIO1/A0):连接到电池电压分压器

  • ADC_EN_PIN (GPIO6/A5):控制 ADC 测量电路的电源

  1. 我们定义了一个 CALIBRATION_FACTOR(0.968)来微调电压读数的准确性。

  2. setup() 函数中:

  • 我们初始化串口通信

  • 将 ADC_EN 引脚配置为输出并设置为 LOW(禁用)以节省电源

  • 将 ADC 分辨率设置为 12 位(0-4095)

  • 为电池引脚配置适当的衰减

  1. loop() 函数中:
  • 我们调用 readBatteryVoltage() 来获取当前电池电压

  • 我们将电压打印到串口监视器

  • 我们根据电压阈值确定并显示电池状态

  • 我们等待 5 秒钟再进行下一次读数

  1. readBatteryVoltage() 函数:
  • 通过将 ADC_EN_PIN 设置为 HIGH 来启用 ADC 电路

  • 短暂等待电路稳定

  • 进行 30 次读数并取平均值以获得更稳定的结果

  • 禁用 ADC 电路以节省电源

  • 使用以下参数计算实际电池电压:

    • 平均 ADC 读数
    • ADC 分辨率(4095)
    • 参考电压(3.6V)
    • 电压分压器系数(2.0)
    • 校准系数(0.968)

节能设计:

此实现的一个关键特性是能够在不使用时禁用电池测量电路。TRMNL DIY 套件专为低功耗而设计,这种方法通过仅在需要实际测量时为电压分压器电路供电来帮助延长电池寿命。

校准系数(0.968)补偿了电压分压器中的元件公差,有助于确保准确的读数。如果您发现读数与万用表测量结果相比始终存在偏差,则可能需要为您的特定板子稍微调整此值。

进行多次读数并取平均值有助于减少噪声并提供更稳定的电压测量,这对于电池监控特别重要,因为小的电压变化对于确定剩余容量可能很重要。

ePaper 显示屏

TRMNL 7.5"(OG)DIY 套件配备了一块美观的 7.5 英寸黑白 ePaper 显示屏,在各种光照条件下都能提供清晰的可见性和超低功耗。在本节中,我们将探索如何使用 Arduino 设置和控制 ePaper 显示屏。

硬件设置

在开始编程之前,让我们确保 ePaper 显示屏正确连接到 TRMNL 板:

步骤 1. 将 ePaper 显示屏连接到驱动板上的 24 针连接器。连接器支持盲插,因此您不会插错。

步骤 2. 插入排线后,通过关闭锁定机构来固定它。

caution

排线很脆弱,容易损坏。尽量避免侧向弯曲。

步骤 3. 确保跳线连接板上的 24Pin 和 GND 引脚。

软件设置

为了控制 ePaper 显示屏,我们将使用 Seeed_GFX 库,它为各种 Seeed Studio 显示设备提供全面支持。

步骤 1. 从 GitHub 下载 Seeed_GFX 库:


步骤 2. 通过在 Arduino IDE 中添加 ZIP 文件来安装库。转到 Sketch > Include Library > Add .ZIP Library 并选择下载的 ZIP 文件。

note

如果您之前安装了 TFT_eSPI 库,您可能需要暂时从 Arduino 库文件夹中删除或重命名它以避免冲突,因为 Seeed_GFX 是 TFT_eSPI 的一个分支,具有额外功能。

步骤 3. 从 Seeed_GFX 库打开示例草图:File > Seeed_GFX > Examples > ePaper > Basic > Clock

步骤 4. 通过点击示例选项卡旁边的箭头并选择"New Tab",在与您的 Arduino 草图相同的文件夹中创建一个名为 driver.h 的新文件。

步骤 5. 将新文件命名为 driver.h

步骤 6. 转到 Seeed GFX 配置工具,并选择"TRMNL 7.5" (OG) DIY Kit",如图所示。

步骤 7. 复制生成的代码并将其粘贴到您创建的 driver.h 文件中。代码应该如下所示:

#define BOARD_SCREEN_COMBO 502 // 7.5 inch monochrome ePaper Screen (UC8179)
#define USE_XIAO_EPAPER_DISPLAY_BOARD_EE04

步骤 8. 现在您可以将示例上传到您的 TRMNL DIY 套件,并看到 ePaper 显示屏的实际效果!

基础 ePaper 显示示例

让我们探索一个简单的模拟时钟示例,它演示了如何在 ePaper 显示屏上绘制图形。这个示例创建了一个经典的时钟表盘,带有时针、分针和秒针。

#include <SPI.h>
#include <TFT_eSPI.h> // Hardware-specific library

#ifdef EPAPER_ENABLE // Only compile this code if the EPAPER_ENABLE is defined in User_Setup.h

EPaper epaper = EPaper(); // Invoke custom library

float sx = 0, sy = 1, mx = 1, my = 0, hx = -1, hy = 0; // Saved H, M, S x & y multipliers
float sdeg = 0, mdeg = 0, hdeg = 0;
uint16_t osx = 120, osy = 120, omx = 120, omy = 120, ohx = 120, ohy = 120; // Saved H, M, S x & y coords
uint16_t x0 = 0, x1 = 0, yy0 = 0, yy1 = 0;
uint32_t targetTime = 0; // for next 1 second timeout

static uint8_t conv2d(const char *p); // Forward declaration needed for IDE 1.6.x
uint8_t hh = conv2d(__TIME__), mm = conv2d(__TIME__ + 3), ss = conv2d(__TIME__ + 6); // Get H, M, S from compile time

bool initial = 1;

#endif

void setup(void)
{
#ifdef EPAPER_ENABLE
epaper.begin();
epaper.setRotation(0);

epaper.fillScreen(TFT_WHITE);

epaper.setTextColor(TFT_BLACK, TFT_WHITE); // Adding a background colour erases previous text automatically

// Draw clock face
epaper.fillCircle(120, 120, 118, TFT_BLACK);
epaper.fillCircle(120, 120, 110, TFT_WHITE);

// Draw 12 lines
for (int i = 0; i < 360; i += 30)
{
sx = cos((i - 90) * 0.0174532925);
sy = sin((i - 90) * 0.0174532925);
x0 = sx * 114 + 120;
yy0 = sy * 114 + 120;
x1 = sx * 100 + 120;
yy1 = sy * 100 + 120;

epaper.drawLine(x0, yy0, x1, yy1, TFT_BLACK);
}

// Draw 60 dots
for (int i = 0; i < 360; i += 6)
{
sx = cos((i - 90) * 0.0174532925);
sy = sin((i - 90) * 0.0174532925);
x0 = sx * 102 + 120;
yy0 = sy * 102 + 120;
// Draw minute markers
epaper.drawPixel(x0, yy0, TFT_BLACK);

// Draw main quadrant dots
if (i == 0 || i == 180)
epaper.fillCircle(x0, yy0, 2, TFT_BLACK);
if (i == 90 || i == 270)
epaper.fillCircle(x0, yy0, 2, TFT_BLACK);
}

epaper.fillCircle(120, 121, 3, TFT_BLACK);

// Draw text at position 120,260 using fonts 4
// Only font numbers 2,4,6,7 are valid. Font 6 only contains characters [space] 0 1 2 3 4 5 6 7 8 9 : . - a p m
// Font 7 is a 7 segment font and only contains characters [space] 0 1 2 3 4 5 6 7 8 9 : .
epaper.drawCentreString("Time flies", 120, 260, 4);

epaper.update();

targetTime = millis() + 1000;
#endif
}

void loop()
{
#ifdef EPAPER_ENABLE
if (targetTime < millis())
{
targetTime += 1000;
ss++; // Advance second
if (ss == 60)
{
ss = 0;
mm++; // Advance minute
if (mm > 59)
{
mm = 0;
hh++; // Advance hour
if (hh > 23)
{
hh = 0;
}
}
}

// Pre-compute hand degrees, x & y coords for a fast screen update
sdeg = ss * 6; // 0-59 -> 0-354
mdeg = mm * 6 + sdeg * 0.01666667; // 0-59 -> 0-360 - includes seconds
hdeg = hh * 30 + mdeg * 0.0833333; // 0-11 -> 0-360 - includes minutes and seconds
hx = cos((hdeg - 90) * 0.0174532925);
hy = sin((hdeg - 90) * 0.0174532925);
mx = cos((mdeg - 90) * 0.0174532925);
my = sin((mdeg - 90) * 0.0174532925);
sx = cos((sdeg - 90) * 0.0174532925);
sy = sin((sdeg - 90) * 0.0174532925);

if (ss == 0 || initial)
{
initial = 0;
// Erase hour and minute hand positions every minute
epaper.drawLine(ohx, ohy, 120, 121, TFT_WHITE);
ohx = hx * 62 + 121;
ohy = hy * 62 + 121;
epaper.drawLine(omx, omy, 120, 121, TFT_WHITE);
omx = mx * 84 + 120;
omy = my * 84 + 121;
}

// Redraw new hand positions, hour and minute hands not erased here to avoid flicker
epaper.drawLine(osx, osy, 120, 121, TFT_WHITE);
osx = sx * 90 + 121;
osy = sy * 90 + 121;
epaper.drawLine(osx, osy, 120, 121, TFT_BLACK);
epaper.drawLine(ohx, ohy, 120, 121, TFT_BLACK);
epaper.drawLine(omx, omy, 120, 121, TFT_BLACK);
epaper.drawLine(osx, osy, 120, 121, TFT_BLACK);

epaper.fillCircle(120, 121, 3, TFT_BLACK);
epaper.update();
}
#endif
}

#ifdef EPAPER_ENABLE
static uint8_t conv2d(const char *p)
{
uint8_t v = 0;
if ('0' <= *p && *p <= '9')
v = *p - '0';
return 10 * v + *++p - '0';
}
#endif

时钟示例的工作原理:

这个优雅的示例在ePaper显示屏上创建了一个经典的模拟时钟。让我们分解一下它是如何工作的:

  1. 初始化和配置:

    • 代码使用条件编译 #ifdef EPAPER_ENABLE 来确保它只在支持电子纸显示屏的硬件上运行。
    • 我们声明变量来跟踪时钟指针的位置和它们的先前位置。
    • 初始时间基于使用 __TIME__ 宏的草图编译时间设置。
  2. 设置函数:

    • 我们使用 epaper.begin() 初始化电子纸显示屏。
    • 使用 epaper.fillScreen(TFT_WHITE) 将显示屏填充为白色作为背景。
    • 我们绘制时钟表面为带有白色内部的黑色圆圈。
    • 小时标记绘制为围绕圆周的12条线。
    • 分钟标记绘制为60个点,在整点位置有特殊强调。
    • 在时钟表面的中心绘制一个小黑圆圈。
    • 在显示屏底部添加文本"Time flies"。
    • 最后,调用 epaper.update() 来更新物理显示屏。
  3. 循环函数:

    • 每秒(当 millis() 超过 targetTime 时),我们更新时钟指针。
    • 我们根据需要递增秒、分钟和小时。
    • 我们根据当前时间计算每个指针的角度。
    • 使用三角函数计算指针的位置:
      • 时针:每小时30度(加上分钟的调整)
      • 分针:每分钟6度(加上秒的调整)
      • 秒针:每秒6度
    • 为了减少不必要的更新并延长显示屏的寿命,我们只在每分钟(当秒数 = 0时)擦除和重绘时针和分针。
    • 秒针每秒更新一次,通过擦除其先前位置并在新位置绘制它。
    • 绘制所有指针后,我们刷新中心点并调用 epaper.update() 来更新物理显示屏。
  4. 辅助函数:

    • conv2d 函数将数字的字符串表示转换为整数值,用于解析编译时的小时、分钟和秒值。

关于此示例的重要说明:

  1. 显示更新: 与LCD或OLED显示屏不同,ePaper显示屏不是为频繁更新而设计的。此示例每秒更新显示屏,这对于演示目的来说是可以的,但在实际应用中,您可能希望更新频率更低以延长显示屏的寿命。

  2. 部分更新: 此示例通过只擦除和重绘显示屏的必要部分来演示部分更新,这比刷新整个屏幕更高效。

  3. 条件编译: #ifdef EPAPER_ENABLE 指令确保代码只在系统中正确配置电子纸显示屏时才编译和运行。

  4. 绘图函数: 示例展示了各种绘图函数:

    • fillCircle() 用于创建时钟表面
    • drawLine() 用于绘制时钟指针和小时标记
    • drawPixel()fillCircle() 用于分钟标记
    • drawCentreString() 用于居中文本

这个模拟时钟示例为在TRMNL 7.5"(OG)DIY套件的ePaper显示屏上创建您自己的图形应用程序提供了一个很好的起点。

绘图和文本函数

Seeed_GFX库提供了许多在显示屏上绘图的函数:

  • display.drawPixel(x, y, color):绘制单个像素
  • display.drawLine(x0, y0, x1, y1, color):绘制线条
  • display.drawRect(x, y, w, h, color):绘制矩形轮廓
  • display.fillRect(x, y, w, h, color):绘制填充矩形
  • display.drawCircle(x, y, r, color):绘制圆形轮廓
  • display.fillCircle(x, y, r, color):绘制填充圆形
  • display.drawTriangle(x0, y0, x1, y1, x2, y2, color):绘制三角形轮廓
  • display.fillTriangle(x0, y0, x1, y1, x2, y2, color):绘制填充三角形
  • display.setCursor(x, y):设置文本光标位置
  • display.setTextColor(color):设置文本颜色
  • display.setTextSize(size):设置文本大小(1-6)
  • display.print("text"):在光标位置打印文本
  • display.println("text"):打印带换行的文本

此单色显示屏的可用颜色为:

  • GxEPD_BLACK:黑色像素
  • GxEPD_WHITE:白色像素

记住在绘图操作后调用 display.update() 来更新物理显示屏。

技术支持与产品讨论

感谢您选择我们的产品!我们在这里为您提供不同的支持,以确保您使用我们产品的体验尽可能顺畅。我们提供多种沟通渠道,以满足不同的偏好和需求。

Loading Comments...