用ESP学习单片机之Arduino_Esp-32s点亮2.8‘‘TFT LCD屏幕_ILI9341_SPI_240x320_V1.2

简介

  • 板子: Esp 32s开发板
  • 屏幕: 2.8‘’ TFT LCD (ILI9341、SPI
  • IDE: Arduino
  • 库:Ucglib
    玩Arduino也有一阵子了,B站小电视和天气始终带火了0.96寸的OLED,上手简单,确实好用,但有个短处就是太小了,而且不支持触摸。。。。那这次这个稍微大一点又支持触摸的吧(事实证明就是完虐自己)

首先就是某宝搜索arduino TFT屏幕(Arduino创客多用这种TFT屏幕),找了下五六十就有焊好排针的,还支持触摸啊,然后我选了个尺寸最大的,等了几天到手发现不是给Arduino玩耍的。。而是树莓派专属的屏幕(没有树莓派的哭泣ing,就是3.5寸蓝色电路板,26个排母的那个。。而且卖家的资料里压根都是七八年前的资料,也没有关于这款屏幕的资料。。。网络上也没有适配arduino的。。。);于是又买了一个稍微小一点2.8寸TFT屏幕,等了三四天开心的取快递,然后接线,上arduino Uno。。。咋整也没反应,就背光LED傻傻的亮着(白屏)。。。。整了一周多,查各种资料,博客、论坛、社区,心态都没了。。。。终于某天我掏出了esp8266和esp32的开发板进行尝试-。-最终在esp32上成功运行显示目标程序

为什么失败

  • Arduino Uno
    这个板子接上屏幕后尝试次数最多,我怀疑是我接线问题、程序不对。。。最后人傻了,在LCDwiki的概述中说

当我们直接将没有板载电平转换模块的SPI显示模块接到Arduino上运行时,会发现根本不能运行。
这是因为SPI模块的引脚只能输入3.3V高电平,而Arduino输出的高电平为5V。要想成功运行,有两种方法:短接法和外接电平转换模块法。
短接法的优点是操作简单,只需短接,不需要再外接器件,缺点是运行时,模块发热量较大,可能会影响模块的使用寿命。
外接电平转换模块法为常规操作,优点是运行时模块发热量小,运行稳定,缺点是操作稍微复杂(需要外接电平转换模块),增加成本(需要额外购买电平转换模块)。
综上所述,推荐使用外接电平转换模块法。

所以这里就告诉我们两条路:

  1. 电压转换:买 5V转换3.3V模块,某宝下单即可,路数别买少了;于是我就掏出了我的ESP系列开发板(3.3V电平)
  2. 短接法:我怂,不采取
  • Esp 8266
    这个板子失败的时候我人傻了。。。。先是板子死了两块。。。(于是我又下单买了不少开发板。。。早知道直接买电平转换模块了,死因不明。。。怀疑是垃圾开发板采用垃圾闪存寿命短吧),第三款新板子到手后尽量减少烧写次数,终于,屏幕有了一丢丢动作,是真的真的只有一丢丢动作(全屏扫描只扫描了几十条就从头开始扫描了。。压根没扫完)。。。。这里根据esp32的结果我猜是垃圾内存不足。。。
    扫屏

失败是成功之母

  • 选对屏幕:驱动芯片、通信类型、适配产品和平台、成功案例
    驱动芯片关系到成败,不是常用的有可供直接使用的库不买,自己能写库的大佬忽略。。。通信类型大部分都是SPI,需要留意自己的板子有没有预留接口,我买的是可以插2.54排针的屏幕,要是焊接功夫NB,可以直接焊接那种细细软软的接口倒是可以省一笔费用。。(焊坏就哭吧)
    适配性的话主要是屏幕能不能支持自己手上的板子,不能的买回来就吃灰。。。而成功案例至少你可以让它成功点亮,而不是只点亮背板LED
  • 资料筛选
    找了Arduino中文社区、野火、CSDN等平台,垃圾信息一大堆,你吵吵我,我抄抄你。。。。Arduino中文社区那个目前还在开贴,我。。。想找个成功案例都要哭啊,最后找到一个LCD WiKi可NB了,感谢LCD wiki的站长

2.8’’ TFT SPI ILI9341

在这里插入图片描述

接口定义

序号 引脚标号 说明
1 VCC 5V/3.3V电源输入
2 GND 接地
3 CS 液晶屏片选信号,低电平使能
4 RESET 液晶屏复位信号,低电平复位
5 DC/RS 液晶屏寄存器/数据选择信号,低电平:寄存器,高电平:数据
6 SDI(MOSI) SPI总线写数据信号
7 SCK SPI总线时钟信号
8 LED 背光控制,高电平点亮,如无需控制则接3.3V常亮
9 SDO(MISO) SPI总线读数据信号,如无需读取功能则可不接
(以下为触摸屏信号线接线,如无需触摸或者模块本身不带触摸功能,可不连接)
10 T_CLK 触摸SPI总线时钟信号
11 T_CS 触摸屏片选信号,低电平使能
12 T_DIN 触摸SPI总线输入
13 T_DO 触摸SPI总线输出
14 T_IRQ 触摸屏中断信号,检测到触摸时为低电平

产品参数

显示颜色 RGB 65K彩色
SKU 带触摸:MSP2807、无触摸:MSP2806
尺寸 2.8(inch)
类型 TFT
驱动芯片 ILI9341
分辨率 320*240 (Pixel)
模块接口 4-wire SPI interface
有效显示区域(AA区) 43.2x57.6(mm)
模块PCB底板尺寸 50.0x86.0(mm)
工作温度 -20℃~60℃
存储温度 -30℃~70℃
VCC电源电压 3.3V~5V
逻辑IO口电压 3.3V(TTL)

产品介绍

2.8寸彩屏,支持16BIT RGB 65K色显示,显示色彩丰富
320X240分辨率,可选触摸功能
采用SPI串行总线,只需几个IO即可点亮显示
带SD卡槽方便扩展实验

IO映射

TFT LCDESP32
VCC3V3
GNDGND
CSP5
RESETP4
DCP17
MOSIP23
SCKP18
LEDVIN(最好加一个电阻)
MISOP19(非必需,可以不接)

点亮过程

首先Arduino配置Esp32的开发环境请参考我的一篇博客,然后下载Ucglib库

HelloWorld

引脚连接顺序已在Ucglib_ILI9341_18x240x320_SWSPI ucg(/*sclk=*/ 18, /*data=*/ 23, /*cd=*/ 17, /*cs=*/ 5, /*reset=*/4); //ESP32中写明,按照该顺序连接后下载下面的程序,rst后屏幕首先后扫屏,一遍,然后在顶部显示 Hello World!

代码

#include <SPI.h>
#include "Ucglib.h"

Ucglib_ILI9341_18x240x320_SWSPI ucg(/*sclk=*/ 18, /*data=*/ 23, /*cd=*/ 17, /*cs=*/ 5, /*reset=*/4); //ESP32

void setup(void)
{
  delay(1000);
  ucg.begin(UCG_FONT_MODE_TRANSPARENT);
  ucg.clearScreen();
}

void loop(void)
{
  ucg.setFont(ucg_font_ncenR12_tr);
  ucg.setColor(255, 255, 255);
  ucg.setColor(1, 255, 0,0);
  
  ucg.setPrintPos(0,25);
  ucg.print("Hello World!");

  delay(500);  
}

效果图片

心力憔悴。。。。整了好几天才点亮,放弃了十几次吧。尝试了几十次终于亮了。。。。怀疑自己不存的,有点饿
做完有点心累,晚上回来继续写
在这里插入图片描述

Box3D

下载下面的程序,rst后屏幕首先后扫屏一遍,然后在中间显示一个彩色立方体,然后不断旋转(帧率肉眼可见。。。。)

代码

#include <SPI.h>
#include "Ucglib.h"

Ucglib_ILI9341_18x240x320_SWSPI ucg(/*sclk=*/ 18, /*data=*/ 23, /*cd=*/ 17, /*cs=*/ 5, /*reset=*/4);      //nodemcu-esp32s,没反应可下载后重新插拔电源,succeed

// define a 3d point structure 
struct pt3d 
{
  ucg_int_t x, y, z;
};

struct surface
{
  uint8_t p[4];
  int16_t z;
};

struct pt2d 
{
  ucg_int_t x, y;
  unsigned is_visible;
};


// define the point at which the observer looks, 3d box will be centered there
#define MX (ucg.getWidth()/2)
#define MY (ucg.getHeight()/2)

// define a value that corresponds to "1"
#define U 64

// eye to screen distance (fixed)
#define ZS U

// cube edge length is 2*U
struct pt3d cube[8] =
{
  { -U, -U, U}, 
  { U, -U, U}, 
  { U, -U, -U}, 
  { -U, -U, -U}, 
  { -U, U, U}, 
  { U, U, U}, 
  { U, U, -U}, 
  { -U, U, -U}, 
};

// define the surfaces
struct surface cube_surface[6] = 
{
  { {0, 1, 2, 3}, 0 },  // bottom
  { {4, 5, 6, 7}, 0 },  // top
  { {0, 1, 5, 4}, 0 },  // back
  
  { {3, 7, 6, 2}, 0 },  // front
  { {1, 2, 6, 5}, 0 },  // right
  { {0, 3, 7, 4}, 0 },  // left
};

// define some structures for the copy of the box, calculation will be done there
struct pt3d cube2[8];
struct pt2d cube_pt[8];

// will contain a rectangle border of the box projection into 2d plane
ucg_int_t x_min, x_max;
ucg_int_t y_min, y_max;

int16_t sin_tbl[65] = {
0,1606,3196,4756,6270,7723,9102,10394,11585,12665,13623,14449,15137,15679,16069,16305,16384,16305,16069,15679,
15137,14449,13623,12665,11585,10394,9102,7723,6270,4756,3196,1606,0,-1605,-3195,-4755,-6269,-7722,-9101,-10393,
-11584,-12664,-13622,-14448,-15136,-15678,-16068,-16304,-16383,-16304,-16068,-15678,-15136,-14448,-13622,-12664,-11584,-10393,-9101,-7722,
-6269,-4755,-3195,-1605,0};

int16_t cos_tbl[65] = {
16384,16305,16069,15679,15137,14449,13623,12665,11585,10394,9102,7723,6270,4756,3196,1606,0,-1605,-3195,-4755,
-6269,-7722,-9101,-10393,-11584,-12664,-13622,-14448,-15136,-15678,-16068,-16304,-16383,-16304,-16068,-15678,-15136,-14448,-13622,-12664,
-11584,-10393,-9101,-7722,-6269,-4755,-3195,-1605,0,1606,3196,4756,6270,7723,9102,10394,11585,12665,13623,14449,
15137,15679,16069,16305,16384};


void copy_cube(void)
{
  uint8_t i;
  for( i = 0; i < 8; i++ )
  {
    cube2[i] = cube[i];
  }
}

void rotate_cube_y(uint16_t w)
{
  uint8_t i;
  int16_t x, z;
  /*
    x' = x * cos(w) + z * sin(w)
    z' = - x * sin(w) + z * cos(w)  
  */
  for( i = 0; i < 8; i++ )
  {
    x = ((int32_t)cube2[i].x * (int32_t)cos_tbl[w] + (int32_t)cube2[i].z * (int32_t)sin_tbl[w])>>14;
    z = (- (int32_t)cube2[i].x * (int32_t)sin_tbl[w] + (int32_t)cube2[i].z * (int32_t)cos_tbl[w])>>14;
    //printf("%d: %d %d --> %d %d\n", i, cube2[i].x, cube2[i].z, x, z);
    cube2[i].x = x;
    cube2[i].z = z;
  }  
}

void rotate_cube_x(uint16_t w)
{
  uint8_t i;
  int16_t y, z;
  for( i = 0; i < 8; i++ )
  {
    y = ((int32_t)cube2[i].y * (int32_t)cos_tbl[w] + (int32_t)cube2[i].z * (int32_t)sin_tbl[w])>>14;
    z = (- (int32_t)cube2[i].y * (int32_t)sin_tbl[w] + (int32_t)cube2[i].z * (int32_t)cos_tbl[w])>>14;
    cube2[i].y = y;
    cube2[i].z = z;
  }  
}

void trans_cube(uint16_t z)
{
  uint8_t i;
  for( i = 0; i < 8; i++ )
  {
    cube2[i].z += z;
  }  
}

void reset_min_max(void)
{
  x_min = 0x07fff;
  y_min = 0x07fff;
  x_max = -0x07fff;
  y_max = -0x07fff;
}

// calculate xs and ys from a 3d value
void convert_3d_to_2d(struct pt3d *p3, struct pt2d *p2)
{
  int32_t t;
  p2->is_visible = 1;
  if ( p3->z >= ZS )
  {
    t = ZS;
    t *= p3->x;
    t <<=1;
    t /= p3->z;
    if ( t >= -MX && t <= MX-1 )
    {
      t += MX;
      p2->x = t;
      
      if ( x_min > t )
  x_min = t;
      if ( x_max < t )
  x_max = t;  
      
      t = ZS;
      t *= p3->y;
      t <<=1;
      t /= p3->z;
      if ( t >= -MY && t <= MY-1 )
      {
  t += MY;
  p2->y = t;
  
  if ( y_min > t )
    y_min = t;
  if ( y_max < t )
    y_max = t;  
      }
      else
      {
  p2->is_visible = 0;
      }
    }
    else
    {
      p2->is_visible = 0;
    }
  }
  else
  {
    p2->is_visible = 0;
  }
}

void convert_cube(void)
{
  uint8_t i;
  reset_min_max();
  for( i = 0; i < 8; i++ )
  {
    convert_3d_to_2d(cube2+i, cube_pt+i);
    //printf("%d: %d %d\n", i, cube_pt[i].x, cube_pt[i].y);
  }  
}

void calculate_z(void)
{
  uint8_t i, j;
  uint16_t z;
  for( i = 0; i < 6; i++ )
  {
    z = 0;
    for( j = 0; j < 4; j++ )
    {
      z+=cube2[cube_surface[i].p[j]].z;      
    }
    z/=4;
    cube_surface[i].z = z;
    //printf("%d: z=%d\n", i, z);
  }  
}

void draw_cube(void)
{
  uint8_t i, ii;
  uint8_t skip_cnt = 3;   /* it is known, that the first 3 surfaces are invisible */
  int16_t z, z_upper;
  
  
  z_upper = 32767;
  for(;;)
  {
    ii = 6;
    z = -32767;
    for( i = 0; i < 6; i++ )
    {
      if ( cube_surface[i].z <= z_upper )
      {
  if ( z < cube_surface[i].z )
  {
    z = cube_surface[i].z;
    ii = i;
  }
      }
    }
    
    if ( ii >= 6 )
      break;
    //printf("%d z=%d upper=%d\n", ii, z, z_upper);
    z_upper = cube_surface[ii].z;
    cube_surface[ii].z++;
    
    if ( skip_cnt > 0 )
    {
      skip_cnt--;
    }
    else
    {
      ucg.setColor(0, ((ii+1)&1)*255,(((ii+1)>>1)&1)*255,(((ii+1)>>2)&1)*255);
      ucg.drawTetragon( 
  cube_pt[cube_surface[ii].p[0]].x, cube_pt[cube_surface[ii].p[0]].y,
  cube_pt[cube_surface[ii].p[1]].x, cube_pt[cube_surface[ii].p[1]].y,
  cube_pt[cube_surface[ii].p[2]].x, cube_pt[cube_surface[ii].p[2]].y,
  cube_pt[cube_surface[ii].p[3]].x, cube_pt[cube_surface[ii].p[3]].y);
    }
  }
}



void calc_and_draw(uint16_t w, uint16_t v)
{
  copy_cube();
  rotate_cube_y(w);
  rotate_cube_x(v);
  trans_cube(U*8);
  convert_cube();
  calculate_z();
  draw_cube();
}


void setup(void)
{
  delay(1000);
  ucg.begin(UCG_FONT_MODE_TRANSPARENT);
  ucg.setRotate90();
  ucg.clearScreen();
  ucg.setFont(ucg_font_ncenR10_tr);
  ucg.setPrintPos(0,25);
  ucg.setColor(255, 255, 255);
  ucg.print("Ucglib Box3D");
  Serial.begin(9600);
    Serial.println("sr");
}

uint16_t w = 0;
uint16_t v = 0;

void loop(void)
{  
  Serial.println("loop");
  calc_and_draw(w, v>>3);

  v+=3;
  v &= 511;

  w++;
  w &= 63;  
  delay(30);  
  
  ucg.setColor(0,0,0);
  ucg.drawBox(x_min, y_min, x_max-x_min+1, y_max-y_min+1);
}

效果图片

在这里插入图片描述

参考

  • https://learn.adafruit.com/adafruit-2-8-and-3-2-color-tft-touchscreen-breakout-v2/pinouts
  • http://www.lcdwiki.com/zh/Run_Arduino_Demo_in_spi_model
  • http://www.lcdwiki.com/zh/2.8inch_SPI_Module_ILI9341_SKU:MSP2807
  • https://github.com/olikraus/ucglib/wiki/reference
  • https://www.arduino.cn/thread-41158-1-1.html
  • https://www.lab-z.com/arducg/
  • https://www.geek-workshop.com/thread-10634-1-1.html
  • https://blog.csdn.net/weixin_43353164/article/details/105060630
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值