回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

【花雕学编程】Arduino动手做(249)---ESP32 1.28寸 TFT GC9A01屏幕之模拟手表时钟指针、数字时间显示、日期窗口和装饰性刻度 简单

头像 驴友花雕 2025.03.28 36 0

00 (1).jpg
00 (2).jpg
02.jpg
10 (1).jpg
10 (2).jpg
12.jpg
15.jpg

  【Arduino】189种传感器模块系列实验(资料代码+仿真编程+图形编程)
 实验二百四十九:1.28寸圆形彩色TFT显示屏 高清IPS 模块 240*240 SPI接口GC9A01驱动
 项目实验之三十九:模拟手表时钟指针、数字时间显示、日期窗口和装饰性刻度

实验开源代码

 

代码
/*
  【Arduino】189种传感器模块系列实验(资料代码+仿真编程+图形编程)
  实验二百四十九:1.28寸圆形彩色TFT显示屏 高清IPS 模块 240*240 SPI接口GC9A01驱动
  项目实验之三十九:模拟手表时钟指针、数字时间显示、日期窗口和装饰性刻度
*/

//       GC9A01---------- ESP32
//       RST ------------ NC(复位引脚,此处未连接)
//       CS ------------- D4(片选引脚,连接到ESP32的D4引脚)
//       DC ------------- D2(数据/命令选择引脚,连接到ESP32的D2引脚)
//       SDA ------------ D23 (green)(主数据输出引脚,连接到ESP32的D23引脚,绿色线)
//       SCL ------------ D18 (yellow)(时钟信号引脚,连接到ESP32的D18引脚,黄色线)
//       GND ------------ GND(接地引脚,连接到ESP32的接地端)
//       VCC -------------3V3(电源引脚,连接到ESP32的3.3V电源)

#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_GC9A01A.h"

// 定义屏幕引脚
#define TFT_CS 4        // 片选引脚
#define TFT_DC 2        // 数据/命令引脚
#define TFT_RST -1      // 重置引脚(未使用时设置为 -1)

// 初始化屏幕对象
Adafruit_GC9A01A tft = Adafruit_GC9A01A(TFT_CS, TFT_DC, TFT_RST);

// 定义屏幕中心
#define SCREEN_CENTER_X 120
#define SCREEN_CENTER_Y 120
#define CLOCK_RADIUS 100
#define HAND_COLOR GC9A01A_WHITE // 指针颜色

// 初始化时间变量
int hours = 01, minutes = 42, seconds = 00; // 初始化时间为 01:42:00

void drawClockFace() {
    // 绘制表盘外圈
    tft.drawCircle(SCREEN_CENTER_X, SCREEN_CENTER_Y, CLOCK_RADIUS, GC9A01A_WHITE);
    tft.drawCircle(SCREEN_CENTER_X, SCREEN_CENTER_Y, CLOCK_RADIUS - 5, GC9A01A_CYAN);

    // 绘制小时刻度
    for (int i = 0; i < 12; i++) {
        float angle = i * 30 * 3.14159 / 180;
        int x1 = SCREEN_CENTER_X + (CLOCK_RADIUS - 10) * cos(angle);
        int y1 = SCREEN_CENTER_Y + (CLOCK_RADIUS - 10) * sin(angle);
        int x2 = SCREEN_CENTER_X + (CLOCK_RADIUS - 20) * cos(angle);
        int y2 = SCREEN_CENTER_Y + (CLOCK_RADIUS - 20) * sin(angle);
        tft.drawLine(x1, y1, x2, y2, GC9A01A_WHITE);
    }

    // 绘制分钟刻度
    for (int i = 0; i < 60; i++) {
        if (i % 5 != 0) { // 避开小时刻度
            float angle = i * 6 * 3.14159 / 180;
            int x1 = SCREEN_CENTER_X + (CLOCK_RADIUS - 10) * cos(angle);
            int y1 = SCREEN_CENTER_Y + (CLOCK_RADIUS - 10) * sin(angle);
            tft.drawPixel(x1, y1, GC9A01A_YELLOW);
        }
    }
}

void drawHands() {
    // 计算时针位置
    float hourAngle = (hours % 12 + minutes / 60.0) * 30 * 3.14159 / 180;
    int hourX = SCREEN_CENTER_X + (CLOCK_RADIUS - 50) * cos(hourAngle);
    int hourY = SCREEN_CENTER_Y + (CLOCK_RADIUS - 50) * sin(hourAngle);
    tft.drawLine(SCREEN_CENTER_X, SCREEN_CENTER_Y, hourX, hourY, HAND_COLOR);

    // 计算分针位置
    float minuteAngle = (minutes + seconds / 60.0) * 6 * 3.14159 / 180;
    int minuteX = SCREEN_CENTER_X + (CLOCK_RADIUS - 30) * cos(minuteAngle);
    int minuteY = SCREEN_CENTER_Y + (CLOCK_RADIUS - 30) * sin(minuteAngle);
    tft.drawLine(SCREEN_CENTER_X, SCREEN_CENTER_Y, minuteX, minuteY, GC9A01A_GREEN);

    // 计算秒针位置
    float secondAngle = seconds * 6 * 3.14159 / 180;
    int secondX = SCREEN_CENTER_X + (CLOCK_RADIUS - 20) * cos(secondAngle);
    int secondY = SCREEN_CENTER_Y + (CLOCK_RADIUS - 20) * sin(secondAngle);
    tft.drawLine(SCREEN_CENTER_X, SCREEN_CENTER_Y, secondX, secondY, GC9A01A_RED);
}

void displayDigitalTime() {
    // 绘制数字时间
    tft.setTextSize(2);
    tft.setTextColor(GC9A01A_WHITE, GC9A01A_BLACK); // 白色文字,黑色背景
    tft.setCursor(73, 160);
    tft.printf("%02d:%02d:%02d", hours, minutes, seconds);

    // 显示日期窗口
    tft.setTextSize(2);
    tft.setCursor(62, 180);
    tft.printf("2025/03/28");
}

void updateClock() {
    seconds++;
    if (seconds >= 60) {
        seconds = 0;
        minutes++;
    }
    if (minutes >= 60) {
        minutes = 0;
        hours++;
    }
    if (hours >= 24) {
        hours = 0;
    }
}

void setup() {
    Serial.begin(115200);           // 初始化串口通信
    Serial.println("Complex Watch Face");

    tft.begin();                    // 初始化屏幕
    tft.setRotation(2);             // 设置屏幕方向
    tft.fillScreen(GC9A01A_BLACK);  // 清屏设置为黑色背景

    drawClockFace();                // 绘制表盘
}

void loop() {
    // 清除上一帧的指针和数字区域
    tft.fillCircle(SCREEN_CENTER_X, SCREEN_CENTER_Y, CLOCK_RADIUS - 5, GC9A01A_BLACK);

    // 更新动态内容
    drawClockFace();
    drawHands();
    displayDigitalTime();
    updateClock();

    delay(1000); // 每秒刷新一次
}

代码说明

1、复杂表盘设计:

多层刻度:包括小时刻度和分钟刻度。

双功能显示:模拟指针显示和数字时间显示同时存在。

2、动态时间更新:

时间每秒更新,分针和时针会根据当前时间调整位置。

3、多色指针:

不同颜色用于区分秒针(红色)、分针(绿色)和时针(白色)。

4、装饰性圆圈:

表盘边缘绘制多层圆圈,增强设计美感。

5、时间和日期显示:

在表盘底部显示当前时间和固定的日期信息。

6、效果描述

表盘中央模拟指针式时钟,刻度和指针分明,色彩丰富。

底部数字显示当前时间和固定日期信息,结合现代和经典设计。

动态更新的时间显示,让表盘实时变化。

代码解读

1、屏幕初始化

#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_GC9A01A.h"
引入 SPI 和 Adafruit_GFX 库,用于图形绘制功能。

引入 Adafruit_GC9A01A 库,专用于驱动 GC9A01A 圆形屏幕,提供绘制圆形、线条、文字等能力。

2、屏幕硬件配置

#define TFT_CS 4
#define TFT_DC 2
#define TFT_RST -1
Adafruit_GC9A01A tft = Adafruit_GC9A01A(TFT_CS, TFT_DC, TFT_RST);
TFT_CS、TFT_DC 和 TFT_RST 是屏幕的引脚定义。

TFT_RST 为 -1,表示未使用复位引脚。

使用 Adafruit_GC9A01A 初始化屏幕对象 tft。

3、 屏幕中心和表盘参数

#define SCREEN_CENTER_X 120
#define SCREEN_CENTER_Y 120
#define CLOCK_RADIUS 100
#define HAND_COLOR GC9A01A_WHITE
定义屏幕中心 (120, 120),适配 GC9A01A 的圆形屏幕(分辨率 240x240 像素)。

表盘半径设置为 100 像素,指针颜色设为白色。

4、时间变量初始化

int hours = 01, minutes = 42, seconds = 00;
时间初始化为 01:42:00。

5、绘制表盘的功能
5.1 绘制表盘外圈

tft.drawCircle(SCREEN_CENTER_X, SCREEN_CENTER_Y, CLOCK_RADIUS, GC9A01A_WHITE);
tft.drawCircle(SCREEN_CENTER_X, SCREEN_CENTER_Y, CLOCK_RADIUS - 5, GC9A01A_CYAN);
绘制两个同心圆,形成表盘外圈,分别使用白色和青色,增加美感。

5.2 绘制小时刻度

for (int i = 0; i < 12; i++) {
   float angle = i * 30 * 3.14159 / 180;
   int x1 = SCREEN_CENTER_X + (CLOCK_RADIUS - 10) * cos(angle);
   int y1 = SCREEN_CENTER_Y + (CLOCK_RADIUS - 10) * sin(angle);
   int x2 = SCREEN_CENTER_X + (CLOCK_RADIUS - 20) * cos(angle);
   int y2 = SCREEN_CENTER_Y + (CLOCK_RADIUS - 20) * sin(angle);
   tft.drawLine(x1, y1, x2, y2, GC9A01A_WHITE);
}
使用 cos 和 sin 函数计算小时刻度位置(角度每小时增加 30°)。

两点 (x1, y1) 和 (x2, y2) 通过 drawLine 绘制刻度线。

5.3 绘制分钟刻度

for (int i = 0; i < 60; i++) {
   if (i % 5 != 0) { // 避开小时刻度
       float angle = i * 6 * 3.14159 / 180;
       int x1 = SCREEN_CENTER_X + (CLOCK_RADIUS - 10) * cos(angle);
       int y1 = SCREEN_CENTER_Y + (CLOCK_RADIUS - 10) * sin(angle);
       tft.drawPixel(x1, y1, GC9A01A_YELLOW);
   }
}
使用 cos 和 sin 函数计算分钟刻度位置(角度每分钟增加 6°)。

每 5 分钟避开小时刻度,仅绘制点刻度。

6、绘制指针的功能
6.1 时针

float hourAngle = (hours % 12 + minutes / 60.0) * 30 * 3.14159 / 180;
int hourX = SCREEN_CENTER_X + (CLOCK_RADIUS - 50) * cos(hourAngle);
int hourY = SCREEN_CENTER_Y + (CLOCK_RADIUS - 50) * sin(hourAngle);
tft.drawLine(SCREEN_CENTER_X, SCREEN_CENTER_Y, hourX, hourY, HAND_COLOR);
根据当前时间计算时针角度:

每小时角度增量为 30°,并加入分钟的偏移。

时针长度为半径减 50 像素。

6.2 分针

float minuteAngle = (minutes + seconds / 60.0) * 6 * 3.14159 / 180;
int minuteX = SCREEN_CENTER_X + (CLOCK_RADIUS - 30) * cos(minuteAngle);
int minuteY = SCREEN_CENTER_Y + (CLOCK_RADIUS - 30) * sin(minuteAngle);
tft.drawLine(SCREEN_CENTER_X, SCREEN_CENTER_Y, minuteX, minuteY, GC9A01A_GREEN);
分针角度每分钟增加 6°,同时加入秒数偏移。

分针长度为半径减 30 像素。

6.3 秒针

float secondAngle = seconds * 6 * 3.14159 / 180;
int secondX = SCREEN_CENTER_X + (CLOCK_RADIUS - 20) * cos(secondAngle);
int secondY = SCREEN_CENTER_Y + (CLOCK_RADIUS - 20) * sin(secondAngle);
tft.drawLine(SCREEN_CENTER_X, SCREEN_CENTER_Y, secondX, secondY, GC9A01A_RED);
秒针角度每秒增加 6°。

秒针长度为半径减 20 像素。

7、显示数字时间

tft.setTextSize(2);
tft.setTextColor(GC9A01A_WHITE, GC9A01A_BLACK); // 白色文字,黑色背景
tft.setCursor(73, 160);
tft.printf("%02d:%02d:%02d", hours, minutes, seconds);
显示数字时间,格式为 01:42:00,居中显示在表盘下方。

8、更新时间

seconds++;
if (seconds >= 60) {
   seconds = 0;
   minutes++;
}
if (minutes >= 60) {
   minutes = 0;
   hours++;
}
if (hours >= 24) {
   hours = 0;
}
每秒更新时间变量,并递归处理分钟和小时的变化。

9、主循环

tft.fillCircle(SCREEN_CENTER_X, SCREEN_CENTER_Y, CLOCK_RADIUS - 5, GC9A01A_BLACK);
drawClockFace();
drawHands();
displayDigitalTime();
updateClock();
delay(1000);
清除上一帧内容,绘制表盘、指针和数字时间,并更新时间。

以每秒 (delay(1000)) 的速率刷新表盘内容。

实验场景图  动态图

 

55 (1).jpg
55 (2).jpg
00094.gif

评论

user-avatar
icon 他的勋章
    展开更多