Skip to content

5_TFTeSPI

前言

貌似在国内,使用Arduino的框架方式去开发某个项目并不流行,看起来在使用单片机开发的时候,都是自己从底层开始。很巧的是,最近使用ESP32开发的某个项目的基础原型,用的是Arduino框架,这个项目的原作者是一位外国人,与他聊天表示十分友好hhhhh。这里可以重新说明一下关于这个项目的开发框架。ESP32的开发方式有两种:

  • 一种是基于乐鑫官方提供的ESP-IDF的方式,使用ESP32-SDK进行开发。
  • 另一种是基于Arduino框架的方式进行开发。 然而事实是这样的,不管是使用IDF还是Arduino,都是基于原始的SDK进行的,而Arduino只是对SDK再进行了一次接口封装而已。

关于TFT_eSPI库

对于Arduino来说,现成库是很多很多的,就像人家搭好的梯子你直接拿来用。而TFT_eSPI就是一个很现成的例子。项目中由于需要驱动一个LCD+Touch,LCD使用的是ST7796S,touch芯片使用的是XPT2046。这看起来就是很经典、淘宝上很常见的LCD模块。遇见这样的,当然首先很开心,因为有很多代码可以用,因此通过查阅资料,果断使用TFT_eSPI这个库,github地址如下: https://github.com/Bodmer/TFT_eSPI

将它放入工程的位置之后,就可以用了。但事情并没有想象中顺利: 1)由于自己使用的屏幕是480320,而TFT_eSPI提供的ST7796驱动用的是320240,因此是不能直接驱动使用的。 2)由于屏幕、触控的电路是自己设计的,与别的配置不一样,也是不能直接使用的,所以需要修改配置文件。 从文件夹组成来看,并不算复杂,而我们只需要关注这几个就可以了,就算是TFT_eSPI,再牛逼的库,底子都应该是用SPI或其它所驱动,这个是核心,因此引脚的定义是首要的。 目录结构 Image

TFT_eSPI的配置使用

  1. 修改配置文件 User_Setup.h
  2. 选择LCD驱动芯片 Image
  3. 修改你的lcd脚位以适配硬件电路 Image
  4. 添加或删除字体文件 Image
  5. 修改spi驱动频率 Image

TFT_eSPI常用函数

//创建对象
TFT_eSPI tft = TFT_eSPI()
TFT_eSPI tft = TFT_eSPI(320,240)        // 在创建对象的时候设置屏幕尺寸


//初始化
void init(uint8_t tc = TAB_COLOUR)
void begin(uint8_t tc = TAB_COLOUR)
//注意:begin与init是相同的,可以在源码中看到,在begin直接调用了init函数。

//清屏
void fillScreen(uint32_t color) // 用某一颜色填充屏幕

//屏幕方向
void setRotation(uint8_t r);      // 设置显示图像旋转方向,r可选参数为0、1、2、3
uint8_t getRotation(void)         // 读取当前旋转角度
//注意:0, 1, 2, 3 分别代表 0°、90°、180°、270°,4为镜像。

//颜色转换
uint16_t color565(uint8_t red, uint8_t green, uint8_t blue)    // 将8位红色、绿色和蓝色转换为16位
uint16_t color8to16(uint8_t color332)                          // 将8位颜色转换为16位
uint8_t  color16to8(uint16_t color565)                         // 将16位颜色转换为8位
uint32_t color16to24(uint16_t color565)                        // 将16位颜色转换为24位
uint32_t color24to16(uint32_t color888)                        // 将24位颜色转换为16位

//颜色反转
void invertDisplay(bool i)      //反转所有显示颜色i = true反转,i = false正常


//文字设置
/* 游标 */
void setCursor(int16_t x, int16_t y)                     // 设置tft.print()的光标
void setCursor(int16_t x, int16_t y, uint8_t font)       // 设置tft.print()的光标和字号
int16_t getCursorX(void)                                 // 读取当前光标x位置(随tft.print()移动)
int16_t getCursorY(void)                                 // 读取当前光标y位置
/* 设置字体颜色 */
void setTextColor(uint16_t color)                        // 仅设置字符的颜色
void setTextColor(uint16_t fgcolor, uint16_t bgcolor, bool bgfill = false)   // 设置字符前景色和背景色,可选的背景填充以平滑字体
/* 设置字号 */
void setTextSize(uint8_t size)                           // 设置字符大小乘数(这会增加像素大小)
void setTextWrap(bool wrapX, bool wrapY = false)         // 打开/关闭TFT宽度和/或高度中文本的换行
/* 文本基准位置 */
void setTextDatum(uint8_t datum)                         // 设置文本基准位置(默认为左上角)
uint8_t getTextDatum(void)                               // 获取文本基准位置
/* 设置背景填充,可以用作清除指定区域的显示 */
void setTextPadding(uint16_t x_width)                    // 以像素为单位设置文本填充(背景空白/重写)宽度
uint16_t getTextPadding(void)                            // 获取文本填充
//注意:从上面函数可知,想要打印显示的文本,只需使用 tft.print() 函数即可。

Sprites 精灵

Sprite在概念上是一个不可见的图形屏幕,它保存在处理器的RAM中。图形可以被绘制到精灵中,就像它们可以直接绘制到屏幕上一样。精灵完成后,可以在屏幕上的任何位置绘制。如果有足够的内存,则精灵可以与屏幕大小相同,并用作帧缓冲区。精灵默认使用16位颜色,位深度可以设置为8位(256色),或1位(任意2种颜色),以减少所需的RAM。在ESP8266上,可以创建的最大16位彩色Sprite约为160x128像素,这消耗了40Kbytes的RAM。在ESP32上,工作区RAM比数据表所暗示的更为有限,因此16位彩色Sprite限制为200x200像素(约80Kbytes),8位Sprite限制为320x240像素(~76kbytes)。每像素1位的Sprite只需要9600字节就可以得到一个完整的320 x 240屏幕缓冲区,这是支持使用2种颜色位图字体的理想选择。

可以创建一个或多个精灵,精灵可以是任意像素宽度和高度,仅受可用RAM的限制。16位彩色深度精灵所需的RAM是(2 x宽度x高度)字节,对于8位颜色深度的精灵,所需RAM是(宽度x高度)字节。可以根据需要在草图中动态创建和删除精灵,这意味着在屏幕上绘制精灵后,可以释放RAM,然后可以运行基于WiFi的RAM密集型代码,并且正常的图形操作仍然有效。

在sprite中绘制图形非常快,对于那些熟悉Adafruit“graphicstest”示例的人来说,整个测试在160x128 sprite中在18ms内完成。sprite的使用示例可以在“Examples/sprite”文件夹中找到。

精灵可以用一种指定为“透明”的颜色绘制到TFT上,参见Transparent_Sprite_Demo示例。

如果一个ESP32板安装了SPIRAM(即PSRAM),那么sprite将使用PSRAM内存,并且可以创建大的全屏缓冲精灵。全屏精灵渲染时间较长(320 x 240 16位精灵约45毫秒),所以请记住这一点。

以下是Sprite 独有的函数:

//声明
TFT_eSprite yourSpriteName = TFT_eSprite(&tft);

/*
1.创建、删除Sprite
  创建sprite所需的内存是:
   1位色深:1bit / pixel
   4位色深:4bit / pixel
   8位色深:1Byte / pixel
   16 位色深: 2 Byte / pixel
*/
//创建一个 宽x高像素 的sprite,返回一个指向RAM的指针
//如果需要,Sketch 可以将返回值转换为 (uint16_t*) 以获得 16 位深度
void*  createSprite(int16_t width, int16_t height, uint8_t frames = 1);
void*  getPointer(void);//如果未创建,则返回一个指向精灵或 nullptr 的指针,用户必须转换为指针类型
bool   created(void);   //如果精灵已经创建,则返回真

void   deleteSprite(void);//删除精灵以释放 RAM 

//2.设置色深
//设置或获取颜色深度为 4、8 或 16 位。可用于更改现有精灵的深度,但会将其清除为黑色,如果重新创建精灵,则返回一个新指针。 
void*  setColorDepth(int8_t b);
int8_t getColorDepth(void);

//3.调色板
//设置4位深度精灵的调色板。仅使用前 16 种颜色。
void     createPalette(uint16_t *palette = nullptr, uint8_t colors = 16);       // RAM中的调色板
void     createPalette(const uint16_t *palette = nullptr, uint8_t colors = 16); // FLASH中的调色板
void     setPaletteColor(uint8_t index, uint16_t color); //将单个调色板索引设置为给定的颜色 
uint16_t getPaletteColor(uint8_t index);//获取给定调色板索引处的颜色 

//4.绘制

//4.1.字符
void     drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t font);//在 Adafruit GLCD 或 freefont 中绘制单个字符
//在屏幕上绘制一个 unicode 字形。任何 UTF-8 解码都必须在调用 drawChar() 之前完成 
int16_t  drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font);
int16_t  drawChar(uint16_t uniCode, int32_t x, int32_t y);

//4.2.滚动、旋转

//设置滚动区域,从左上角定义x,y,宽度和高度。颜色(可选,默认为黑色)用于填充滚动后的间隙
void     setScrollRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t color = TFT_BLACK);

//滚动 定义区域 dx,dy 像素。负值向上、左滚动;正值向右、下滚动。向上、向下滚动是可选的(默认是没有向上/向下滚动)。
//Sprite 坐标系不移动,移动的是像素
void     scroll(int16_t dx, int16_t dy = 0),

void     setRotation(uint8_t rotation);//设置Sprite 的旋转坐标(仅适用于 1位色深的Sprite )  显示器内部硬件中的 CGRAM 旋转
uint8_t  getRotation(void);

//将 Sprite 的旋转副本推送到具有可选透明颜色的 TFT 
bool     pushRotated(int16_t angle, uint32_t transp = 0x00FFFFFF);   // Using fixed point maths

//将 Sprite 的旋转副本推送到另一个具有可选透明颜色的不同 Sprite
bool     pushRotated(TFT_eSprite *spr, int16_t angle, uint32_t transp = 0x00FFFFFF);   

//获取此 Sprite 旋转副本的 TFT 边界框 
bool     getRotatedBounds(int16_t angle, int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y);

//获取此 Sprite 的旋转副本的目标 Sprite 边界框
bool     getRotatedBounds(TFT_eSprite *spr, int16_t angle, int16_t *min_x, int16_t *min_y,
                                                           int16_t *max_x, int16_t *max_y);

//获取旋转的 Sprite wrt 枢轴的 TFT 边界框
void     getRotatedBounds(int16_t angle, int16_t w, int16_t h, int16_t xp, int16_t yp,
                          int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y);

uint16_t readPixel(int32_t x0, int32_t y0);//读取 x,y 处像素的颜色并以 565 格式返回值
uint16_t readPixelValue(int32_t x, int32_t y);//返回 x,y 处像素的数值(滚动时使用)

//4.3.其他
//  TFT_eSPI类的绘制几何图形、线条等的函数Sprite都可以使用,重复的不再记录。


void    fillSprite(uint32_t color); //用颜色填充sprite       

//将图像(位图)写入sprite
void     pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint16_t *data, uint8_t sbpp = 0);
void     pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, const uint16_t *data);

//将精灵推到 x, y 处的 TFT
void     pushSprite(int32_t x, int32_t y);
void     pushSprite(int32_t x, int32_t y, uint16_t transparent);
bool     pushSprite(int32_t tx, int32_t ty, int32_t sx, int32_t sy, int32_t sw, int32_t sh)

//将sprite推送到 x,y 处的另一个sprite。此函数在目标sprite (dspr) 类中调用 了pushImage()
bool     pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y);
bool     pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y, uint16_t transparent);

//与抗锯齿字体相关的函数 
void     drawGlyph(uint16_t code);  //向sprite光标位置写入一个字符
void     printToSprite(String string);//向sprite光标位置写入字符串
void     printToSprite(char *cbuffer, uint16_t len);
int16_t  printToSprite(int16_t x, int16_t y, uint16_t index);

TFT_eSPI更多函数

我也查找了一下,网上好像没有 TFT_eSPI 的 API,开源的代码中好像也没提供,但是不要怕,我们可以从源码中的注释查看每个函数的说明。

仔细观察便可发现,使用 TFT_eSPI 是都是直接使用 TFT_eSPI 的对象进行调用的,所以我们只要看查看 TFT_eSPI.h 和 TFT_eSPI.cpp 文件中的注释即可,如下图所示

#include <TFT_eSPI.h>

#define TFT_GREY 0x5AEB  // New colour

/*Change to your screen resolution*/
static const uint16_t screenWidth = 128;
static const uint16_t screenHeight = 160;
//TFT_eSPI tft = TFT_eSPI();  // Invoke library
TFT_eSPI tft = TFT_eSPI(screenWidth, screenHeight); /* TFT instance */

void setup(void) {
  tft.init();
  tft.setRotation(0);
  tft.invertDisplay(0);
}

void loop() {

  // Fill screen with grey so we can see the effect of printing with and without
  // a background colour defined
  tft.fillScreen(TFT_BLACK);
  tft.setRotation(3);//设置屏幕旋转方向

  // Set "cursor" at top left corner of display (0,0) and select font 2
  // (cursor will move to next line automatically during printing with 'tft.println'
  //  or stay on the line is there is room for the text with tft.print)
  tft.setCursor(0, 0, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.setTextSize(1);
  // We can now plot text on screen using the "print" class
  tft.println("Hello World!");

  // Set the font colour to be yellow with no background, set to font 7
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextFont(7);
  tft.println("12:34");

  // Set the font colour to be green with black background, set to font 4
  tft.setTextColor(TFT_GREEN, TFT_BLACK);
  tft.setTextFont(4);
  tft.println("Groop");
  tft.println("I implore thee,");

  // Change to font 2
  tft.setTextFont(2);
  tft.println("my foonting turlingdromes.");
  tft.println("And hooptiously drangle me");
  tft.println("with crinkly bindlewurdles,");
  // This next line is deliberately made too long for the display width to test
  // automatic text wrapping onto the next line
  tft.println("Or I will rend thee in the gobberwarts with my blurglecruncheon, see if I don't!");

  // Test some print formatting functions
  float fnumber = 123.45;
  // Set the font colour to be blue with no background, set to font 4
  tft.setTextColor(TFT_BLUE);
  tft.setTextFont(4);
  tft.print("Float = ");
  tft.println(fnumber);  // Print floating point number
  tft.print("Binary = ");
  tft.println((int)fnumber, BIN);  // Print as integer value in binary
  tft.print("Hexadecimal = ");
  tft.println((int)fnumber, HEX);  // Print as integer number in Hexadecimal
  delay(10000);
}