【Arduino】189种传感器模块系列实验(资料代码+仿真编程+图形编程) 
  实验二百四十九:1.28寸圆形彩色TFT显示屏 高清IPS 模块 240*240 SPI接口GC9A01驱动 
  项目之一百四十三:ESP32+GC9A01终结者之眼动画 
 
实验开源代码 
 
			
			
			- /*
 -   【Arduino】189种传感器模块系列实验(资料代码+仿真编程+图形编程)
 -   实验二百四十九:1.28寸圆形彩色TFT显示屏 高清IPS 模块 240*240 SPI接口GC9A01驱动
 -   项目之一百四十三:ESP32+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电源)
 - 
 - // 显示的眼睛的大小取决于屏幕的尺寸和分辨率。
 - // 眼睛图像的宽度固定为 128 像素。在人类的生理结构中,
 - // 睑裂 (睁开的眼睛的宽度) 大约是 30 毫米。因此,
 - // 低分辨率、大像素尺寸的显示屏最适合以真实的比例
 - // 显示眼睛的图像。请注意,显示器制造商通常会标注对角线尺寸,
 - // 因此一个 128x128 的 1.7 英寸显示屏或 128x160 的 2 英寸显示屏
 - // 在尺寸上是比较合适的。
 - 
 - // 眼睛的配置设置,包括眼睛的样式、显示器的数量、
 - // 片选引脚以及眼睛在屏幕上的 X 轴偏移量,都可以在草图的
 - // "config.h" 选项卡中进行定义。
 - 
 - // 通过在 ESP32 和 STM32 处理器上使用 DMA (直接内存访问,仅适用于 SPI 显示屏),
 - // 可以显著提高程序的性能 (以每秒传输的帧数,即 fps 来衡量)。
 - // 应该使用显示器所支持的尽可能高的 SPI 时钟速率。
 - // 最小推荐速率为 27MHz,一些显示器甚至可以在 40-80MHz 的更高频率下稳定运行。
 - 
 - // 下表列出了在不同处理器上,使用单只默认眼睛 (defaultEye) 时的性能表现:
 - //                                 无 DMA    有 DMA
 - // ESP8266 (160MHz CPU) 40MHz SPI   36 fps
 - // ESP32 27MHz SPI                 53 fps     85 fps
 - // ESP32 40MHz SPI                 67 fps    102 fps
 - // ESP32 80MHz SPI                 82 fps    116 fps // 注意:很少有显示器能在 80MHz 下稳定工作
 - // STM32F401 55MHz SPI               44 fps     90 fps
 - // STM32F446 55MHz SPI               83 fps    155 fps
 - // STM32F767 55MHz SPI              136 fps    197 fps
 - 
 - // 当接口是 SPI 时,DMA 可以与 RP2040、STM32 和 ESP32 处理器一起使用。
 - // 要启用 DMA,请取消注释下一行:
 - //#define USE_DMA
 - 
 - // 加载 TFT_eSPI 库,用于控制 TFT LCD
 - #include <SPI.h>      // 包含 Arduino 的 SPI 库
 - #include <TFT_eSPI.h> // 包含 TFT_eSPI 库
 - TFT_eSPI tft;         // 创建一个 TFT_eSPI 类的实例,用于控制显示屏
 - 
 - // 在眼睛的渲染过程中,会使用一个像素缓冲区来存储即将绘制的数据。
 - // BUFFER_SIZE 定义了这个缓冲区的大小,128 到 1024 似乎是一个最佳的范围。
 - #define BUFFER_SIZE 240
 - 
 - #ifdef USE_DMA
 -   #define BUFFERS 2     // 如果启用了 DMA,则使用 2 个缓冲区进行乒乓操作,提高效率
 - #else
 -   #define BUFFERS 1     // 如果没有启用 DMA,则只需要 1 个缓冲区
 - #endif
 - 
 - uint16_t pbuffer[BUFFERS][BUFFER_SIZE]; // 声明一个二维数组作为像素渲染缓冲区,每个缓冲区可以存储 BUFFER_SIZE 个 16 位颜色值
 - bool      dmaBuf   = 0;                  // 一个布尔变量,用于选择当前正在使用的 DMA 缓冲区 (在双缓冲情况下)
 - 
 - // 这个结构体 (struct) 在 config.h 文件中被填充数据。
 - typedef struct {                 // 定义一个名为 eyeInfo_t 的结构体,用于存储每个眼睛的配置信息
 -   int8_t  select;               // 用于连接到每个眼睛的屏幕的片选 (Chip Select, CS) 引脚编号 (int8_t 是 8 位有符号整数)
 -   int8_t  wink;                 // 连接到每个眼睛的“眨眼”按钮的引脚编号 (int8_t)。如果某个眼睛没有眨眼按钮,则设置为 -1。
 -   uint8_t rotation;             // 该眼睛所连接的显示屏的旋转角度 (0-3)。uint8_t 是 8 位无符号整数。
 -   int16_t xposition;            // 眼睛图像在屏幕上的 X 轴位置偏移量 (int16_t 是 16 位有符号整数)。
 - } eyeInfo_t;
 - 
 - #include "config.h"             // ****** 所有的配置都在这个文件中完成 ******
 - 
 - extern void user_setup(void); // 声明在 user*.cpp 文件中定义的用户自定义初始化函数
 - extern void user_loop(void);  // 声明在 user*.cpp 文件中定义的用户自定义循环函数
 - 
 - #define SCREEN_X_START 0
 - #define SCREEN_X_END    SCREEN_WIDTH    // 定义屏幕上眼睛图像的 X 轴范围的起始和结束。SCREEN_WIDTH 应该在 config.h 中定义。
 - #define SCREEN_Y_START 0
 - #define SCREEN_Y_END    SCREEN_HEIGHT   // 定义屏幕上眼睛图像的 Y 轴范围的起始和结束。SCREEN_HEIGHT 应该在 config.h 中定义。
 - 
 - // 使用一个简单的状态机来控制眼睛的眨眼 (blink) 和单眼眨眼 (wink) 动作:
 - #define NOBLINK 0         // 定义一个状态:当前没有进行任何眨眼动作
 - #define ENBLINK 1         // 定义一个状态:眼睑当前正在闭合 (Engaging Blink)
 - #define DEBLINK 2         // 定义一个状态:眼睑当前正在张开 (Disengaging Blink)
 - typedef struct {                 // 定义一个名为 eyeBlink 的结构体,用于存储每个眼睛的眨眼状态信息
 -   uint8_t  state;         // 当前的眨眼状态 (NOBLINK, ENBLINK, DEBLINK)
 -   uint32_t duration;      // 当前眨眼状态的持续时间 (以微秒为单位)。uint32_t 是 32 位无符号整数。
 -   uint32_t startTime;     // 上次眨眼状态发生改变的时间 (以微秒为单位)。
 - } eyeBlink;
 - 
 - struct {                     // 定义一个匿名结构体,用于存储每个眼睛的特定信息。eye 数组的每个元素都是这个结构体。
 -   int16_t    tft_cs;         // 连接到每个显示屏的片选 (Chip Select) 引脚编号
 -   eyeBlink   blink;          // 一个 eyeBlink 结构体,存储该眼睛的当前眨眼状态
 -   int16_t    xposition;     // 该眼睛的图像在屏幕上的 X 轴位置
 - } eye[NUM_EYES];              // 声明一个名为 eye 的数组,其大小由 NUM_EYES (在 config.h 中定义) 决定。每个元素存储一只眼睛的信息。
 - 
 - uint32_t startTime;     // 用于记录程序启动或上次帧率计算的时间,以便计算每秒帧数 (FPS)。
 - 
 - // 初始化 -- 在启动时运行一次 --------------------------------------------------
 - void setup(void) {
 -   Serial.begin(115200); // 初始化串口通信,波特率为 115200
 -   //while (!Serial);    // 如果需要等待串口连接,则取消注释此行
 -   Serial.println("Starting"); // 通过串口打印 "Starting"
 - 
 - #if defined(DISPLAY_BACKLIGHT) && (DISPLAY_BACKLIGHT >= 0)
 -   // 如果定义了 DISPLAY_BACKLIGHT 并且其值大于等于 0 (表示使用了背光控制引脚)
 -   Serial.println("Backlight turned off"); // 通过串口打印 "Backlight turned off"
 -   pinMode(DISPLAY_BACKLIGHT, OUTPUT);  // 将背光控制引脚设置为输出模式
 -   digitalWrite(DISPLAY_BACKLIGHT, LOW); // 将背光引脚拉低,通常表示关闭背光
 - #endif
 - 
 -   // 调用用户在 user_setup() 函数中定义的任何附加初始化功能
 -   user_setup();
 - 
 -   // 初始化眼睛。这个函数可能会设置所有片选引脚为低电平,以便后续的 tft.init() 可以正常工作。
 -   initEyes(); // 这个函数应该在其他地方定义 (例如在 eye_functions.cpp 中)
 - 
 -   // 初始化 TFT 显示屏
 -   Serial.println("Initialising displays"); // 通过串口打印 "Initialising displays"
 -   tft.init();                               // 调用 TFT_eSPI 库的初始化函数
 - 
 - #ifdef USE_DMA
 -   tft.initDMA(); // 如果定义了 USE_DMA,则初始化 DMA 功能
 - #endif
 - 
 -   // 拉高片选引脚,以便可以单独配置每个显示屏
 -   digitalWrite(eye[0].tft_cs, HIGH); // 将第一个眼睛的片选引脚拉高
 -   if (NUM_EYES > 1) digitalWrite(eye[1].tft_cs, HIGH); // 如果有第二个眼睛,则将其片选引脚也拉高
 - 
 -   // 遍历每个眼睛
 -   for (uint8_t e = 0; e < NUM_EYES; e++) {
 -     digitalWrite(eye[e].tft_cs, LOW);     // 将当前眼睛的片选引脚拉低,选中该显示屏
 -     tft.setRotation(eyeInfo[e].rotation); // 设置当前显示屏的旋转角度,该角度从 eyeInfo 数组中获取
 -     tft.fillScreen(TFT_BLACK);          // 使用黑色填充当前显示屏
 -     digitalWrite(eye[e].tft_cs, HIGH);    // 将当前眼睛的片选引脚拉高,取消选中该显示屏
 -   }
 - 
 - #if defined(DISPLAY_BACKLIGHT) && (DISPLAY_BACKLIGHT >= 0)
 -   Serial.println("Backlight now on!"); // 通过串口打印 "Backlight now on!"
 -   analogWrite(DISPLAY_BACKLIGHT, BACKLIGHT_MAX); // 使用模拟写入设置背光亮度为最大值
 - #endif
 - 
 -   startTime = millis(); // 记录程序启动时的毫秒数,用于后续的帧率计算
 - }
 - 
 - // 主循环 -- 在 setup() 函数执行完毕后持续运行 ----------------------------
 - void loop() {
 -   updateEye(); // 调用 updateEye() 函数来更新眼睛的显示。这个函数应该在其他地方定义 (例如在 eye_functions.cpp 中)。
 - }
 
  复制代码
  
 
 |