用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。要想成功运行,有两种方法:短接法和外接电平转换模块法。
短接法的优点是操作简单,只需短接,不需要再外接器件,缺点是运行时,模块发热量较大,可能会影响模块的使用寿命。
外接电平转换模块法为常规操作,优点是运行时模块发热量小,运行稳定,缺点是操作稍微复杂(需要外接电平转换模块),增加成本(需要额外购买电平转换模块)。
综上所述,推荐使用外接电平转换模块法。
所以这里就告诉我们两条路:
- 电压转换:买 5V转换3.3V模块,某宝下单即可,路数别买少了;于是我就掏出了我的ESP系列开发板(3.3V电平)
- 短接法:我怂,不采取
- 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 LCD | ESP32 |
---|---|
VCC | 3V3 |
GND | GND |
CS | P5 |
RESET | P4 |
DC | P17 |
MOSI | P23 |
SCK | P18 |
LED | VIN(最好加一个电阻) |
MISO | P19(非必需,可以不接) |
点亮过程
首先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
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)