【花雕学编程】Arduino TFT 之绘制动态折线图

在这里插入图片描述

Arduino是一个开放源码的电子原型平台,它可以让你用简单的硬件和软件来创建各种互动的项目。Arduino的核心是一个微控制器板,它可以通过一系列的引脚来连接各种传感器、执行器、显示器等外部设备。Arduino的编程是基于C/C++语言的,你可以使用Arduino IDE(集成开发环境)来编写、编译和上传代码到Arduino板上。Arduino还有一个丰富的库和社区,你可以利用它们来扩展Arduino的功能和学习Arduino的知识。

Arduino的特点是:
1、开放源码:Arduino的硬件和软件都是开放源码的,你可以自由地修改、复制和分享它们。
2、易用:Arduino的硬件和软件都是为初学者和非专业人士设计的,你可以轻松地上手和使用它们。
3、便宜:Arduino的硬件和软件都是非常经济的,你可以用很低的成本来实现你的想法。
4、多样:Arduino有多种型号和版本,你可以根据你的需要和喜好来选择合适的Arduino板。
5、创新:Arduino可以让你用电子的方式来表达你的创意和想象,你可以用Arduino来制作各种有趣和有用的项目,如机器人、智能家居、艺术装置等。

在这里插入图片描述
Arduino TFT 是指与 Arduino 开发板兼容的薄膜晶体管(Thin - Film - Transistor)液晶显示屏,以下是其主要特点、应用场景及注意事项的详细介绍:

一、主要特点
1、高分辨率显示:TFT 显示屏通常具有较高的分辨率,能够提供清晰、细腻的图像和文字显示效果。这使得它可以展示复杂的图形界面、丰富的色彩以及详细的信息,满足各种应用对显示质量的要求。
2、快速响应速度:TFT 技术能够实现快速的像素切换,响应速度比传统的液晶显示屏更快。这意味着在显示动态图像或进行交互操作时,不会出现明显的拖影或延迟,提供流畅的视觉体验。
3、宽视角:具有较宽的可视角度,无论从哪个方向观看显示屏,都能获得清晰的图像和一致的色彩表现。这使得在不同角度下观察显示屏时,都能保证信息的准确传达,方便多人同时查看或在不同位置操作设备。
4、丰富的色彩表现:能够呈现出丰富、鲜艳的色彩,具备较高的色彩饱和度和对比度。可以准确地显示各种颜色,使图像和界面更加生动逼真,适用于需要展示彩色图像或进行视觉交互的应用。
5、易于控制:通过 Arduino 开发板可以方便地对 TFT 显示屏进行控制。借助相应的库函数和代码,能够轻松实现图像绘制、文字显示、触摸交互等功能,降低了开发难度,即使对于没有深厚显示技术背景的开发者也较为友好。

二、应用场景
1、人机交互界面:在各种电子设备中,如智能仪器仪表、智能家居控制系统、工业自动化控制面板等,作为人机交互的窗口,用于显示菜单、参数设置界面、实时数据监测以及操作提示等信息,方便用户与设备进行交互。
2、教育领域:可用于制作教育实验设备、电子学习工具等。例如,在教学机器人中作为显示屏,展示机器人的状态信息、任务指令以及互动内容;在电子积木等教育玩具中,显示图形化的编程界面或游戏画面,增加学习的趣味性和互动性。
3、便携式设备:如手持游戏机、电子书阅读器、便携式多媒体播放器等。其高分辨率、宽视角和丰富的色彩表现能够为用户提供良好的视觉享受,同时快速响应速度也适合处理游戏中的动态画面和多媒体内容的播放。
4、物联网应用:在物联网项目中,作为节点设备的显示屏,用于展示传感器采集的数据、设备状态信息以及远程控制界面等。例如,在环境监测系统中,显示温度、湿度、空气质量等数据;在智能农业中,展示农田的灌溉状态、作物生长信息等。

三、需要注意的事项
1、电源管理:TFT 显示屏通常需要稳定的电源供应,并且其功耗相对较高。在与 Arduino 连接时,要确保电源能够满足显示屏的需求,避免因电源不稳定导致显示屏出现闪烁、显示异常或损坏等问题。同时,要合理设计电源电路,考虑节能措施,特别是在电池供电的设备中,以延长设备的续航时间。
2、接口兼容性:不同型号的 Arduino 开发板和 TFT 显示屏可能具有不同的接口类型和通信协议。在选择和使用时,要确保两者之间的接口兼容,并且正确连接引脚。有些 TFT 显示屏可能需要额外的驱动电路或转接板才能与 Arduino 顺利连接,需要仔细阅读产品说明书并进行相应的配置。
3、显示内存限制:虽然 TFT 显示屏能够显示丰富的内容,但 Arduino 的内存资源有限。在绘制复杂图像或显示大量数据时,可能会遇到内存不足的问题。因此,需要合理规划显示内容,优化图像和数据的存储方式,避免因内存耗尽导致程序崩溃或显示异常。
4、驱动程序和库的使用:为了实现 TFT 显示屏的各种功能,需要使用相应的驱动程序和库。不同的显示屏可能需要不同的库,并且这些库可能会不断更新和改进。在使用过程中,要及时关注官方文档和社区资源,获取最新的驱动程序和库,并了解其使用方法和注意事项。同时,要注意库与 Arduino IDE 版本的兼容性,避免出现编译错误或运行时问题。
5、电磁干扰:在一些电磁环境较为复杂的场合,TFT 显示屏可能会受到电磁干扰,导致显示出现噪点、闪烁或颜色异常等问题。此时,需要采取相应的抗干扰措施,如合理布线、使用屏蔽线、添加滤波电容等,以确保显示屏的稳定工作。

在这里插入图片描述
一、主要特点
实时性
动态折线图的核心在于实时更新数据点,并根据这些数据点绘制线条,从而展示数据的变化趋势。这种特性使得它非常适合用于实时监控系统。
滚动显示
当数据点数量超过屏幕宽度时,通常采用滚动显示的方式,即新数据从右侧进入,旧数据从左侧移出。这种方式能够有效利用有限的屏幕空间。
可定制性强
动态折线图的颜色、坐标轴范围、刻度间隔等参数都可以根据需求进行调整,以满足不同的应用需求。
资源占用适中
绘制动态折线图需要一定的计算和存储资源,但由于其基于简单的像素操作,整体资源占用相对较低,适合运行在Arduino平台上。
交互性支持
结合触摸屏或其他输入设备,动态折线图可以实现交互功能,例如放大、缩小或查看特定数据点的详细信息。

二、应用场景
实时监控系统
温度监控:通过传感器采集环境温度,并以动态折线图的形式实时展示温度变化趋势。
电压/电流监控:监测电路中的电压或电流波动情况,帮助用户分析电源稳定性。
工业自动化
在工业生产线上,动态折线图可用于监控设备运行状态、压力变化或流量趋势,为决策提供直观依据。
健康监测设备
心率监测:将心率传感器的数据以折线图形式展示,便于观察心率变化。
血氧浓度监测:实时绘制血氧浓度曲线,辅助健康分析。
教育与实验
在教学或实验环境中,动态折线图可用于展示物理量(如加速度、光强、湿度等)随时间的变化规律,帮助学生理解科学原理。
智能家居
在智能家居系统中,动态折线图可用于展示室内空气质量、能耗分布或光照强度的变化趋势。
游戏开发与艺术创作
动态折线图可以作为游戏中的UI元素,或者用于生成动态的艺术图案。

三、需要注意的事项
数据采样频率与刷新率
数据采样频率决定了数据点的密度,而刷新率则影响折线图的流畅性。如果采样频率过高或刷新率过低,可能会导致数据丢失或显示卡顿。
需要根据硬件性能和实际需求合理设置采样频率和刷新率。
屏幕分辨率与数据点数量
屏幕分辨率限制了折线图的最大数据点数量。当数据点过多时,可能导致线条重叠或难以分辨。
可以通过降采样或滚动显示来解决这一问题。
内存管理
动态折线图需要存储历史数据点,因此会占用一定的内存资源。对于内存有限的Arduino板(如Uno),应注意优化数据存储方式。
如果数据量较大,可以考虑使用外部存储(如SD卡)或仅保留最近的部分数据点。
坐标轴与比例尺设计
合理设置坐标轴范围和比例尺,确保折线图既能显示数据的整体趋势,又能突出细节变化。
如果数据范围变化较大,可以考虑动态调整比例尺。
抗锯齿与视觉效果
由于TFT屏幕的像素离散性,斜线可能出现锯齿现象。可以通过算法优化或提高屏幕分辨率来改善视觉效果。
使用对比度较高的颜色(如深色背景配浅色线条)可以增强折线图的可读性。
库的选择与兼容性
不同型号的TFT屏幕可能需要使用不同的驱动库(如Adafruit GFX库、MCUFRIEND_kbv库等)。在使用前需确认所选库是否支持目标屏幕。
确保库版本与屏幕驱动芯片匹配,以避免功能异常。
数据精度与噪声处理
传感器采集的数据可能存在噪声,直接绘制可能导致折线图出现不必要的波动。建议对数据进行滤波处理(如滑动平均滤波)后再绘制。

在这里插入图片描述
1、模拟实时温度数据动态折线图

#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>

// 定义TFT引脚
#define TFT_CS     10
#define TFT_RST    9
#define TFT_DC     8
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

// 折线图参数
const int numPoints = 50;
int data[numPoints];
int currentIndex = 0;

void setup() {
  Serial.begin(9600);
  tft.initR(INITR_BLACKTAB);
  tft.setRotation(1);
  tft.fillScreen(ST7735_BLACK);
  // 初始化数据
  for (int i = 0; i < numPoints; i++) {
    data[i] = 20;
  }
}

void loop() {
  // 模拟新的温度数据
  int newData = random(15, 25);
  data[currentIndex] = newData;
  currentIndex = (currentIndex + 1) % numPoints;

  tft.fillScreen(ST7735_BLACK);

  // 绘制坐标轴
  tft.drawLine(10, 10, 10, tft.height() - 10, ST7735_WHITE);
  tft.drawLine(10, tft.height() - 10, tft.width() - 10, tft.height() - 10, ST7735_WHITE);

  // 绘制折线图
  for (int i = 0; i < numPoints - 1; i++) {
    int x1 = 10 + i * (tft.width() - 20) / (numPoints - 1);
    int y1 = tft.height() - 10 - map(data[(currentIndex + i) % numPoints], 15, 25, 0, tft.height() - 20);
    int x2 = 10 + (i + 1) * (tft.width() - 20) / (numPoints - 1);
    int y2 = tft.height() - 10 - map(data[(currentIndex + i + 1) % numPoints], 15, 25, 0, tft.height() - 20);
    tft.drawLine(x1, y1, x2, y2, ST7735_GREEN);
  }

  delay(1000);
}

要点解读:
数据存储:使用数组data存储温度数据,currentIndex记录当前存储数据的位置,采用循环数组的方式更新数据。
数据模拟:在loop函数中使用random函数模拟新的温度数据。
坐标轴绘制:绘制水平和垂直的坐标轴,为折线图提供参考。
折线图绘制:通过map函数将数据映射到屏幕坐标,使用drawLine函数连接相邻的数据点形成折线图。
延迟更新:每次更新数据和绘制折线图后延迟 1 秒,模拟实时数据更新。

2、结合模拟传感器数据的动态折线图

#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>

// 定义TFT引脚
#define TFT_CS     10
#define TFT_RST    9
#define TFT_DC     8
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

// 折线图参数
const int numPoints = 60;
int sensorData[numPoints];
int currentIndex = 0;

void setup() {
  Serial.begin(9600);
  tft.initR(INITR_BLACKTAB);
  tft.setRotation(1);
  tft.fillScreen(ST7735_BLACK);
  // 初始化数据
  for (int i = 0; i < numPoints; i++) {
    sensorData[i] = 0;
  }
}

void loop() {
  // 模拟传感器数据
  int newSensorData = analogRead(A0);
  sensorData[currentIndex] = newSensorData;
  currentIndex = (currentIndex + 1) % numPoints;

  tft.fillScreen(ST7735_BLACK);

  // 绘制坐标轴
  tft.drawLine(10, 10, 10, tft.height() - 10, ST7735_WHITE);
  tft.drawLine(10, tft.height() - 10, tft.width() - 10, tft.height() - 10, ST7735_WHITE);

  // 绘制折线图
  for (int i = 0; i < numPoints - 1; i++) {
    int x1 = 10 + i * (tft.width() - 20) / (numPoints - 1);
    int y1 = tft.height() - 10 - map(sensorData[(currentIndex + i) % numPoints], 0, 1023, 0, tft.height() - 20);
    int x2 = 10 + (i + 1) * (tft.width() - 20) / (numPoints - 1);
    int y2 = tft.height() - 10 - map(sensorData[(currentIndex + i + 1) % numPoints], 0, 1023, 0, tft.height() - 20);
    tft.drawLine(x1, y1, x2, y2, ST7735_YELLOW);
  }

  delay(500);
}

要点解读:
传感器数据读取:使用analogRead(A0)读取模拟传感器数据,将其作为折线图的数据来源。
数据更新:同样采用循环数组的方式更新 sensorData数组中的数据。
坐标轴和折线图绘制:与案例一类似,绘制坐标轴并根据传感器数据绘制折线图。
更新频率:每次更新数据和绘制折线图后延迟 500 毫秒,提高数据更新频率。

3、多组数据动态折线图(模拟电压和电流)

#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>

// 定义TFT引脚
#define TFT_CS     10
#define TFT_RST    9
#define TFT_DC     8
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

// 折线图参数
const int numPoints = 40;
int voltageData[numPoints];
int currentData[numPoints];
int currentIndex = 0;

void setup() {
  Serial.begin(9600);
  tft.initR(INITR_BLACKTAB);
  tft.setRotation(1);
  tft.fillScreen(ST7735_BLACK);
  // 初始化数据
  for (int i = 0; i < numPoints; i++) {
    voltageData[i] = 0;
    currentData[i] = 0;
  }
}

void loop() {
  // 模拟电压和电流数据
  int newVoltage = random(0, 500);
  int newCurrent = random(0, 200);
  voltageData[currentIndex] = newVoltage;
  currentData[currentIndex] = newCurrent;
  currentIndex = (currentIndex + 1) % numPoints;

  tft.fillScreen(ST7735_BLACK);

  // 绘制坐标轴
  tft.drawLine(10, 10, 10, tft.height() - 10, ST7735_WHITE);
  tft.drawLine(10, tft.height() - 10, tft.width() - 10, tft.height() - 10, ST7735_WHITE);

  // 绘制电压折线图
  for (int i = 0; i < numPoints - 1; i++) {
    int x1 = 10 + i * (tft.width() - 20) / (numPoints - 1);
    int y1 = tft.height() - 10 - map(voltageData[(currentIndex + i) % numPoints], 0, 500, 0, tft.height() - 20);
    int x2 = 10 + (i + 1) * (tft.width() - 20) / (numPoints - 1);
    int y2 = tft.height() - 10 - map(voltageData[(currentIndex + i + 1) % numPoints], 0, 500, 0, tft.height() - 20);
    tft.drawLine(x1, y1, x2, y2, ST7735_BLUE);
  }

  // 绘制电流折线图
  for (int i = 0; i < numPoints - 1; i++) {
    int x1 = 10 + i * (tft.width() - 20) / (numPoints - 1);
    int y1 = tft.height() - 10 - map(currentData[(currentIndex + i) % numPoints], 0, 200, 0, tft.height() - 20);
    int x2 = 10 + (i + 1) * (tft.width() - 20) / (numPoints - 1);
    int y2 = tft.height() - 10 - map(currentData[(currentIndex + i + 1) % numPoints], 0, 200, 0, tft.height() - 20);
    tft.drawLine(x1, y1, x2, y2, ST7735_RED);
  }

  delay(1500);
}

要点解读:
多组数据存储:使用两个数组voltageData和currentData分别存储电压和电流数据。
多组数据模拟:在loop函数中分别模拟新的电压和电流数据,并更新对应的数组。
多折线图绘制:分别绘制电压和电流的折线图,使用不同的颜色(蓝色和红色)进行区分。
更新间隔:每次更新数据和绘制折线图后延迟 1500 毫秒,控制数据更新速度。

在这里插入图片描述

4、实时绘制传感器数据折线图

#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();
 
#define GRAPH_WIDTH 240
#define GRAPH_HEIGHT 120
#define DATA_POINTS 100
 
int data[DATA_POINTS] = {0};
int dataIndex = 0;
 
void setup() {
    tft.init();
    tft.setRotation(1);
    tft.fillScreen(TFT_BLACK);
    drawGraphFrame();
}
 
void loop() {
    int newValue = analogRead(34); // 模拟传感器数据
    updateGraph(newValue);
    delay(100);
}
 
void drawGraphFrame() {
    tft.drawRect(10, 50, GRAPH_WIDTH, GRAPH_HEIGHT, TFT_WHITE);
    for (int i = 0; i <= 10; i++) {
        int y = 50 + i * (GRAPH_HEIGHT / 10);
        tft.drawFastHLine(10, y, GRAPH_WIDTH, TFT_DARKGREY);
        tft.setCursor(0, y - 5);
        tft.print(i * 10);
    }
}
 
void updateGraph(int value) {
    // 清除旧数据点
    tft.drawFastVLine(10 + dataIndex * (GRAPH_WIDTH / DATA_POINTS), 50, 120, TFT_BLACK);
    // 绘制新数据点
    int mappedValue = map(value, 0, 4095, GRAPH_HEIGHT, 0);
    tft.drawPixel(10 + dataIndex * (GRAPH_WIDTH / DATA_POINTS), 50 + mappedValue, TFT_GREEN);
    dataIndex = (dataIndex + 1) % DATA_POINTS;
}

要点解读
数据映射:使用map()函数将传感器数据(如ADC值)映射到屏幕高度范围。
动态更新:通过覆盖旧数据点位置实现动态更新,避免屏幕闪烁。
坐标轴绘制:使用drawRect()和drawFastHLine()绘制坐标轴和网格线。
数据点存储:通过数组存储数据点索引,实现循环覆盖。

5、波形监测折线图

#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();
 
#define SAMPLE_SIZE 128
int waveData[SAMPLE_SIZE];
int waveIndex = 0;
 
void setup() {
    tft.init();
    tft.setRotation(1);
    tft.fillScreen(TFT_BLACK);
    tft.setTextColor(TFT_WHITE, TFT_BLACK);
    tft.setTextSize(1);
    tft.setCursor(10, 10);
    tft.println("Waveform Monitor");
}
 
void loop() {
    int sample = analogRead(34); // 模拟音频信号
    waveData[waveIndex] = map(sample, 0, 4095, -64, 63); // 中心化
    drawWaveform();
    waveIndex = (waveIndex + 1) % SAMPLE_SIZE;
    delay(10);
}
 
void drawWaveform() {
    static int oldX[SAMPLE_SIZE], oldY[SAMPLE_SIZE];
    for (int i = 0; i < SAMPLE_SIZE; i++) {
        int x = 10 + i * 2;
        int y = 120 - waveData[(waveIndex + i) % SAMPLE_SIZE];
        if (i > 0) {
            tft.drawLine(oldX[i], oldY[i], x, y, TFT_CYAN);
        }
        oldX[i] = x;
        oldY[i] = y;
    }
    // 清除屏幕左侧(避免波形溢出)
    if (waveIndex % (SAMPLE_SIZE / 2) == 0) {
        tft.fillRect(10, 60, 256, 120, TFT_BLACK);
    }
}

要点解读
波形中心化:将ADC值映射到中心对称范围(-64到63),实现波形居中显示。
波形平移:通过环形缓冲区实现波形数据的连续平移,模拟示波器效果。
动态清屏:定期清除屏幕左侧部分区域,避免波形溢出。
坐标变换:将数据值映射到屏幕Y轴,实现波形缩放。

6、动态趋势分析折线图

#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();
 
#define TREND_POINTS 50
int trendData[TREND_POINTS] = {0};
int trendIndex = 0;
 
void setup() {
    tft.init();
    tft.setRotation(1);
    tft.fillScreen(TFT_BLACK);
    tft.setTextColor(TFT_WHITE, TFT_BLACK);
    tft.setTextSize(1);
    tft.setCursor(10, 10);
    tft.println("Trend Analysis");
}
 
void loop() {
    int value = random(0, 100); // 模拟趋势数据
    updateTrend(value);
    delay(500);
}
 
void updateTrend(int value) {
    // 清除旧趋势线
    for (int i = 1; i < TREND_POINTS; i++) {
        int x1 = 10 + (i - 1) * 5;
        int y1 = 120 - map(trendData[(trendIndex + i - 1) % TREND_POINTS], 0, 100, 0, 120);
        int x2 = 10 + i * 5;
        int y2 = 120 - map(trendData[(trendIndex + i) % TREND_POINTS], 0, 100, 0, 120);
        tft.drawLine(x1, y1, x2, y2, TFT_BLACK);
    }
    // 更新数据并绘制新趋势线
    trendData[trendIndex] = value;
    trendIndex = (trendIndex + 1) % TREND_POINTS;
    for (int i = 1; i < TREND_POINTS; i++) {
        int x1 = 10 + (i - 1) * 5;
        int y1 = 120 - map(trendData[(trendIndex + i - 1) % TREND_POINTS], 0, 100, 0, 120);
        int x2 = 10 + i * 5;
        int y2 = 120 - map(trendData[(trendIndex + i) % TREND_POINTS], 0, 100, 0, 120);
        tft.drawLine(x1, y1, x2, y2, TFT_YELLOW);
    }
}

要点解读
趋势数据模拟:使用random()函数生成模拟趋势数据,替代实际传感器输入。
动态趋势更新:通过两次绘制(先清除旧线,再绘制新线)实现趋势图的动态更新。
坐标映射:将数据值映射到屏幕Y轴,实现趋势数据的可视化。
时间轴压缩:通过固定X轴间隔(如每5像素一个点)实现时间轴的压缩显示。

注意,以上案例只是为了拓展思路,仅供参考。它们可能有错误、不适用或者无法编译。您的硬件平台、使用场景和Arduino版本可能影响使用方法的选择。实际编程时,您要根据自己的硬件配置、使用场景和具体需求进行调整,并多次实际测试。您还要正确连接硬件,了解所用传感器和设备的规范和特性。涉及硬件操作的代码,您要在使用前确认引脚和电平等参数的正确性和安全性。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

驴友花雕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值