写在前面

前几天买了个tft屏幕,一路使用心路很曲折,故把踩坑过程写出来,方便日后查看

是一个st7789,240*240分辨率的屏幕

1 物理连接

网上全是各种各样不同的教程,给我整麻了,后面摸索出来了。

GPIO23----SDA

GPIO18----SCK

GPIO4----RES

GPIO2----DC

VCC----5V(3V3都可以)

GND----GND

似乎是线最好要等长,短一点更好,好像干扰还不小。

2 TFT_eSPI库的初始化

很像u8g2库的使用。

2.1 安装库,

如果你用的是arduino ide 可以直接在库管理里面搜这个库就有了,如果不是的话可以去这个链接下。

2.2 设置库

下一步就是找到项目文件夹下的libraries文件夹(也就是库文件存放的地方),打开TFT_eSPI文件夹,打开User_Setup_Select.h文件,然后注释掉user_setup,取消setup24的注释,大概就改成了这样子:

// This header file contains a list of user setup files and defines which one the
// compiler uses when the IDE performs a Verify/Compile or Upload.
//
// Users can create configurations for different boards and TFT displays.
// This makes selecting between hardware setups easy by "uncommenting" one line.

// The advantage of this hardware configuration method is that the examples provided
// with the library should work with immediately without any other changes being
// needed. It also improves the portability of users sketches to other hardware
// configurations and compatible libraries.
//
// Create a shortcut to this file on your desktop to permit quick access for editing.
// Re-compile and upload after making and saving any changes to this file.

// Customised User_Setup files are stored in the "User_Setups" folder.

// It is also possible for the user tft settings to be included with the sketch, see
// the "Sketch_with_tft_setup" generic example. This may be more convenient for
// multiple projects.

#ifndef USER_SETUP_LOADED //  Lets PlatformIO users define settings in
                          //  platformio.ini, see notes in "Tools" folder.

///////////////////////////////////////////////////////
//   User configuration selection lines are below    //
///////////////////////////////////////////////////////

// Only ONE line below should be uncommented to define your setup.  Add extra lines and files as needed.

//#include <User_Setup.h>           // Default setup is root library folder

//#include <User_Setups/Setup1_ILI9341.h>  // Setup file for ESP8266 configured for my ILI9341
//#include <User_Setups/Setup2_ST7735.h>   // Setup file for ESP8266 configured for my ST7735
//#include <User_Setups/Setup3_ILI9163.h>  // Setup file for ESP8266 configured for my ILI9163
//#include <User_Setups/Setup4_S6D02A1.h>  // Setup file for ESP8266 configured for my S6D02A1
//#include <User_Setups/Setup5_RPi_ILI9486.h>        // Setup file for ESP8266 configured for my stock RPi TFT
//#include <User_Setups/Setup6_RPi_Wr_ILI9486.h>     // Setup file for ESP8266 configured for my modified RPi TFT
//#include <User_Setups/Setup7_ST7735_128x128.h>     // Setup file for ESP8266 configured for my ST7735 128x128 display
//#include <User_Setups/Setup8_ILI9163_128x128.h>    // Setup file for ESP8266 configured for my ILI9163 128x128 display
//#include <User_Setups/Setup9_ST7735_Overlap.h>     // Setup file for ESP8266 configured for my ST7735
//#include <User_Setups/Setup10_RPi_touch_ILI9486.h> // Setup file for ESP8266 configured for ESP8266 and RPi TFT with touch

//#include <User_Setups/Setup11_RPi_touch_ILI9486.h> // Setup file configured for ESP32 and RPi TFT with touch
//#include <User_Setups/Setup12_M5Stack_Basic_Core.h>// Setup file for the ESP32 based M5Stack (Basic Core only)
//#include <User_Setups/Setup13_ILI9481_Parallel.h>  // Setup file for the ESP32 with parallel bus TFT
//#include <User_Setups/Setup14_ILI9341_Parallel.h>  // Setup file for the ESP32 with parallel bus TFT
//#include <User_Setups/Setup15_HX8357D.h>           // Setup file for ESP8266 configured for HX8357D
//#include <User_Setups/Setup16_ILI9488_Parallel.h>  // Setup file for the ESP32 with parallel bus TFT
//#include <User_Setups/Setup17_ePaper.h>            // Setup file for ESP8266 and any Waveshare ePaper display
//#include <User_Setups/Setup18_ST7789.h>            // Setup file for ESP8266 configured for ST7789

//#include <User_Setups/Setup19_RM68140_Parallel.h>	 // Setup file configured for RM68140 with parallel bus

//#include <User_Setups/Setup20_ILI9488.h>           // Setup file for ESP8266 and ILI9488 SPI bus TFT
//#include <User_Setups/Setup21_ILI9488.h>           // Setup file for ESP32 and ILI9488 SPI bus TFT

//#include <User_Setups/Setup22_TTGO_T4.h>           // Setup file for ESP32 and TTGO T4 version 1.2
//#include <User_Setups/Setup22_TTGO_T4_v1.3.h>      // Setup file for ESP32 and TTGO T4 version 1.3
//#include <User_Setups/Setup23_TTGO_TM.h>           // Setup file for ESP32 and TTGO TM ST7789 SPI bus TFT
#include <User_Setups/Setup24_ST7789.h>            // Setup file for DSTIKE/ESP32/ESP8266 configured for ST7789 240 x 240

下一步就是去user_setup文件夹下面找到setup24那个库,改成这个样子:

// ST7789 240 x 240 display with no chip select line
#define USER_SETUP_ID 24

#define ST7789_DRIVER     // Configure all registers

#define TFT_WIDTH  240
#define TFT_HEIGHT 240

//#define TFT_RGB_ORDER TFT_RGB  // Colour order Red-Green-Blue
//#define TFT_RGB_ORDER TFT_BGR  // Colour order Blue-Green-Red

//#define TFT_INVERSION_ON
//#define TFT_INVERSION_OFF

// DSTIKE stepup
//#define TFT_DC    23
//#define TFT_RST   32
//#define TFT_MOSI  26
//#define TFT_SCLK  27

// Generic ESP32 setup
#define TFT_MISO 19
#define TFT_MOSI 23
#define TFT_SCLK 18
#define TFT_CS    -1 // Not connected
#define TFT_DC    2
#define TFT_RST   4  // Connect reset to ensure display initialises

// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation
//#define TFT_CS   -1      // Define as not used
//#define TFT_DC   PIN_D1  // Data Command control pin
//#define TFT_RST  PIN_D4  // TFT reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST  -1    // TFT reset pin connect to NodeMCU RST, must also then add 10K pull down to TFT SCK


#define LOAD_GLCD   // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2  // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4  // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6  // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7  // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:.
#define LOAD_FONT8  // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
#define LOAD_GFXFF  // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts

#define SMOOTH_FONT


// #define SPI_FREQUENCY  27000000
#define SPI_FREQUENCY  40000000

#define SPI_READ_FREQUENCY  20000000

#define SPI_TOUCH_FREQUENCY  2500000

// #define SUPPORT_TRANSACTIONS

这样基本的前置任务就做完了。

3 tft函数的使用

3.1 基本操作

首先要导入相关的库文件

#include <SPI.h>
#include <TFT_eSPI.h>

然后定义tft对象(参数是你的分辨率)

TFT_eSPI tft = TFT_eSPI(240, 240);

然后初始化tft对象

tft.init();

这样tft的那一堆方法就可以用啦,坐标系和opencv一样,左上角为原点(高中物理实验题:小球平抛你懂吧)

// 设置文本显示坐标,默认以文本左上角为参考点,可以改变参考点
tft.setCursor(10, 10);
// 设置文本颜色
tft.setTextColor(TFT_GREEN);
// 设置文本大小,文本大小范围为 1~7 的整数
tft.setTextSize(4);
// 设置字体编号 font,编号范围是 1、2、4、6、7、8,不同的编号代表不同的字体
tft.setTextFont(1);

下一步是定义颜色,这个屏幕采用了R5G6B5的色彩空间,所以我们可以用一些它定义好的颜色

#define TFT_BLACK       0x0000      /*   0,   0,   0 */
#define TFT_NAVY        0x000F      /*   0,   0, 128 */
#define TFT_DARKGREEN   0x03E0      /*   0, 128,   0 */
#define TFT_DARKCYAN    0x03EF      /*   0, 128, 128 */
#define TFT_MAROON      0x7800      /* 128,   0,   0 */
#define TFT_PURPLE      0x780F      /* 128,   0, 128 */
#define TFT_OLIVE       0x7BE0      /* 128, 128,   0 */
#define TFT_LIGHTGREY   0xD69A      /* 211, 211, 211 */
#define TFT_DARKGREY    0x7BEF      /* 128, 128, 128 */
#define TFT_BLUE        0x001F      /*   0,   0, 255 */
#define TFT_GREEN       0x07E0      /*   0, 255,   0 */
#define TFT_CYAN        0x07FF      /*   0, 255, 255 */
#define TFT_RED         0xF800      /* 255,   0,   0 */
#define TFT_MAGENTA     0xF81F      /* 255,   0, 255 */
#define TFT_YELLOW      0xFFE0      /* 255, 255,   0 */
#define TFT_WHITE       0xFFFF      /* 255, 255, 255 */
#define TFT_ORANGE      0xFDA0      /* 255, 180,   0 */
#define TFT_GREENYELLOW 0xB7E0      /* 180, 255,   0 */
#define TFT_PINK        0xFE19      /* 255, 192, 203 */ //Lighter pink, was 0xFC9F      
#define TFT_BROWN       0x9A60      /* 150,  75,   0 */
#define TFT_GOLD        0xFEA0      /* 255, 215,   0 */
#define TFT_SILVER      0xC618      /* 192, 192, 192 */
#define TFT_SKYBLUE     0x867D      /* 135, 206, 235 */
#define TFT_VIOLET      0x915C      /* 180,  46, 226 */

全部的api在这里:

Sprite类是个啥一会说

//以下大多API对Sprite类也通用
//初始化
tft.init(); 
//填充全屏幕
tft.fillScreen(uint32_t color); 
//屏幕旋转,参数为:0, 1, 2, 3,分别代表 0°、90°、180°、270°
tft.setRotation(uint8_t r);     
//设置光标位置,font是可选参数
tft.setCursor(int16_t x, int16_t y, uint8_t font);   
//设置字体颜色
tft.setTextColor(uint16_t color); 
//设置文本颜色和背景色
tft.setTextColor(uint16_t fgcolor, uint16_t bgcolor);
//设置字体大小,文本大小范围为 1~7 的整数,特别注意: 字库7是仿7段数码屏的样式
tft.setTextSize(uint8_t size);
//加载字体
display.loadFont(Final_Frontier_28);    
     //括号中为示例,大小为28像素的平滑字体。
     //使用平滑字体时最好用setTextColor同时设置好字体和背景颜色,否则可能会不平滑。
//卸载当前字体
display.unloadFont();
//打印
tft.print("Hello World!");
tft.println("Hello World!");
//绘制字符串
 //居左
 tft.drawString(String, int32_t x, int32_t y, uint8_t font);//font是可选参数
 //居中
 tft.drawCentreString(String, int32_t x, int32_t y, uint8_t font);//font是可选参数
 //居右
 tft.drawRightString(String, int32_t x, int32_t y, uint8_t font);//font是可选参数
//画点
tft.drawPixel(int32_t x, int32_t y, uint32_t color);
//画线
tft.drawLine(int32_t xs, int32_t ys, int32_t xe, int32_t ye, uint32_t color);
//画横线
tft.drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color);
//画竖线
tft.drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color);
//画圆
//空心圆
tft.drawCircle(int32_t x, int32_t y, int32_t r, uint32_t color);
//实心圆
tft.fillCircle(int32_t x, int32_t y, int32_t r, uint32_t color);
//空心椭圆
tft.drawEllipse(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);
//实心椭圆
tft.fillEllipse(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);
//画矩形
//空心矩形
tft.drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);
//实心矩形
tft.fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);
//空心圆角矩形
tft.drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, uint32_t color);
//实心圆角矩形
tft.drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, uint32_t color);
//画三角形
//空心三角形
tft.drawTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, uint32_t color);
//实心三角形
tft.drawTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, uint32_t color);
//显示图片
tft.pushImage(int32_t x, int32_t y, int32_t w, int32_t h, data);

3.2 刷新,局部刷新,刷不了新

假设我们有一个需要动态显示的变量,比如时间,那么最常想到的刷新就是给屏幕使用tft.fillScreen(uint32_t color);方法再显示刷新后的内容就可以了,这样在需要刷新频率很低的时候(比如每分钟一次)不会有什么很大的问题,但是如果是一个需要实时刷新的数据,这样就会让屏幕一直爆闪,很恼火,所以就会有局部刷新的需求

首先要了解tft库采用了一个覆盖显示的方法,也就是说过去的图像只能被覆盖,而不是擦除,基于这一点,我们给需要刷新的地方用背景的颜色覆盖上就好啦。

tft.fillRect(0, 108, 140, 28, TFT_BLACK);
tft.setCursor(0,108);
tft.print("hell world");

当然有别的方法也可以实现这个操作,就是用 sprite库

这个可以想象为一个显示屏上面的显示屏,你可以设置它的大小,位置,以及不和下面显示屏同步来进行刷新,这样闪屏就解决了。

我们来看一点点这个玩意自己的api

/*实例化*/
TFT_eSprite clk = TFT_eSprite(&tft);
//创建一个 宽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 

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

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);

//设置滚动区域,从左上角定义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 处像素的数值(滚动时使用)

其他的方法都和tft库一模一样(我还是觉得直接覆盖比较好用)

3.3 俺的图图呢?

如果你想要在显示一张精妙的图片,需要对一张图片进行取模的操作

首先准备一张图片:

这里我的屏幕是正方形的,为了适配就用画图工具给裁剪了

然后打开image2lcd这个软件(有破解版,请自便)

这样搞完之后保存那个,把类型换成short(之后你就知道为啥了

打开程序存在的目录,把这个玩意丢进去

导入这个图片:

#include "tutu.c"

因为这个格式不是屏幕想要的,所以要重新写格式转换的函数

#define PI_BUF_SIZE 128
void showImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data){
  int32_t dx = 0;
  int32_t dy = 0;
  int32_t dw = w;
  int32_t dh = h*2;
 
  if (x < 0) { dw += x; dx = -x; x = 0; }
  if (y < 0) { dh += y; dy = -y; y = 0; }
 
  if (dw < 1 || dh < 1) return;
 
  CS_L;
 
  data += dx + dy * w;
 
  uint16_t  buffer[PI_BUF_SIZE];
  uint16_t* pix_buffer = buffer;
  uint16_t  high,low;
 
  tft.setWindow(x, y, x + dw - 1, y + dh - 1);
 
  // Work out the number whole buffers to send
  uint16_t nb = (dw * dh) / (2 * PI_BUF_SIZE);
 
  // Fill and send "nb" buffers to TFT
  for (int32_t i = 0; i < nb; i++) {
    for (int32_t j = 0; j < PI_BUF_SIZE; j++) {
      high = pgm_read_word(&data[(i * 2 * PI_BUF_SIZE) + 2 * j + 1]);
      low = pgm_read_word(&data[(i * 2 * PI_BUF_SIZE) + 2 * j ]);
      pix_buffer[j] = (high<<8)+low;
    }
    tft.pushPixels(pix_buffer, PI_BUF_SIZE);
  }
 
  // Work out number of pixels not yet sent
  uint16_t np = (dw * dh) % (2 * PI_BUF_SIZE);
 
  // Send any partial buffer left over
  if (np) {
    for (int32_t i = 0; i < np; i++)
    {
      high = pgm_read_word(&data[(nb * 2 * PI_BUF_SIZE) + 2 * i + 1]);
      low = pgm_read_word(&data[(nb * 2 * PI_BUF_SIZE) + 2 * i ]);
      pix_buffer[i] = (high<<8)+low;
    }
    tft.pushPixels(pix_buffer, np);
  }
 
  CS_H;
}

showImage(0, 0, 240, 240,gImage_tutu);

参数分别是:图片左上角坐标,图片大小,库里面图片的名字

然后编译上传图片:

完美!

汉字的取模和使用和图片差不多(

这里就不说了(太懒了想去打派派