基于clion ide和paltformio插件,在arduino框架下,实现投屏
常见的都是使用spi串口屏,但是为了提高刷新速度,这里希望通过使用并口屏实现屏幕的驱动。
完整程序代码如下:https://github.com/OULIHONG1999/esp32s3_tcp_screencpy.git
代码仓库连接
库文件
1. TFT_eSPI
2. TJpg_Decoder
其中主要是修改了User_Setup.h中的文件配置,并在main文件的setup()函数中,取消原来使用DMA方式的初始化,以及在绘制屏幕画面时,取消了DMA发送数据,绘制屏幕的方式
部分代码展示
- main.cpp文件
#include <Arduino.h>
#include <TFT_eSPI.h>
#include <SPI.h>
#include <WiFi.h>
#include <TJpg_Decoder.h>
#include <pgmspace.h>
#include <esp_lcd_panel_ops.h>
// 从32改为128,增加缓存,增加速度
uint16_t PROGMEM dmaBuffer1[128 * 128]; // Toggle buffer for 32*32 MCU block, 1024bytes
uint16_t PROGMEM dmaBuffer2[128 * 128]; // Toggle buffer for 32*32 MCU block, 1024bytes
uint16_t *dmaBufferPtr = dmaBuffer1;
bool dmaBufferSel = 0;
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite clk = TFT_eSprite(&tft);
char *ssid = "CMCC-ABCD"; //填写你的wifi名字
char *password = "66668888"; //填写你的wifi密码
int httpPort = 8081; //设置监听端口
WiFiServer server; //初始化一个服务端对象
uint8_t buff[7000] PROGMEM = {0};//每一帧的临时缓存
uint8_t img_buff[50000] PROGMEM = {0};//用于存储tcp传过来的图片
uint16_t size_count = 0;//计算一帧的字节大小
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap) {
if (y >= tft.height()) return 0;
// Double buffering is used, the bitmap is copied to the buffer by pushImageDMA() the
// bitmap can then be updated by the jpeg decoder while DMA is in progress
if (dmaBufferSel) dmaBufferPtr = dmaBuffer2;
else dmaBufferPtr = dmaBuffer1;
dmaBufferSel = !dmaBufferSel; // Toggle buffer selection
// pushImageDMA() will clip the image block at screen boundaries before initiating DMA
// tft.pushImageDMA(x, y, w, h, bitmap, dmaBufferPtr); // Initiate DMA - blocking only if last DMA is not complete // 不使用,注释掉
tft.pushImage(x, y, w, h, bitmap); // 使用其他的绘制函数
return 1;
}
byte loadNum = 6;
void loading(byte delayTime) {//启动动画
clk.setColorDepth(8);
clk.createSprite(200, 50);
clk.fillSprite(0x0000);
clk.drawRoundRect(0, 0, 200, 16, 8, 0xFFFF);
clk.fillRoundRect(3, 3, loadNum, 10, 5, 0xFFFF);
clk.setTextDatum(CC_DATUM);
clk.setTextColor(TFT_GREEN, 0x0000);
clk.drawString("Connecting to WiFi", 100, 40, 2);
clk.pushSprite(20, 67);//20 110
clk.deleteSprite();
loadNum += 1;
if (loadNum >= 195) {
loadNum = 195;
}
delay(delayTime);
}
int power_on_key = 21;
// 开机使能按键,根据需求使用
void init_power() {
pinMode(power_on_key, OUTPUT);
digitalWrite(power_on_key, HIGH);
}
void setup() {
init_power();
// put your setup code here, to run once:
Serial.begin(115200);
tft.begin();
// tft.initDMA(); // 不使用,注释掉
tft.setRotation(3);//横屏
tft.fillScreen(TFT_BLACK);//黑色
tft.setTextColor(TFT_BLACK, TFT_WHITE);
WiFi.begin(ssid, password); //连接wifi
delay(1000); //等待1秒
while (WiFi.status() != WL_CONNECTED) {
for (byte n = 0; n < 10; n++) { //每500毫秒检测一次状态
loading(50);
}
}
while (loadNum < 195) { //让动画走完
loading(3);
}
if (WiFi.status() == WL_CONNECTED) //判断如果wifi连接成功
{
//client.setNoDelay(false);//关闭Nagle算法
Serial.println("wifi is connected!");
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
Serial.println("Port: " + String(httpPort));
//tft.setSwapBytes(true);
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);//黑色
tft.setTextColor(TFT_BLACK, TFT_WHITE);
tft.drawString("Wifi Have Connected To " + String(WiFi.SSID()), 20, 20, 2);
tft.drawString("IP: " + ip.toString(), 20, 40, 2);
tft.drawString("Port: " + String(httpPort), 20, 60, 2);
Serial.println("Waiting a client to connect.........");
server.begin(httpPort); //服务器启动监听端口号
server.setNoDelay(true);
}
TJpgDec.setJpgScale(1);
TJpgDec.setSwapBytes(true);
TJpgDec.setCallback(tft_output);//解码成功回调函数
}
uint16_t read_count = 0;//读取buff的长度
uint8_t pack_size[2];//用来装包大小字节
uint16_t frame_size;//当前帧大小
float start_time, end_time;//帧处理开始和结束时间
float receive_time, deal_time;//帧接收和解码时间
void loop() {
// put your main code here, to run repeatedly:
//沾包问题 recv阻塞,长时间收不到数据就会断开
//断开连接原因,读取buff太快,上位机发送太快造成buff溢出,清空缓冲区会断开(FLUSH)
WiFiClient client = server.available(); //尝试建立客户对象
if (client) {
Serial.println("[New Client!]");
client.write("ok");//向上位机发送下一帧发送指令
while (client.connected())//如果客户端处于连接状态client.connected()
{
client.write("no");//向上位机发送当前帧未写入完指令
while (client.available()) {
while (client.available()) {//检测缓冲区是否有数据
if (read_count == 0) {
start_time = millis();
client.read(pack_size, 2);//读取帧大小
frame_size = pack_size[0] + (pack_size[1] << 8);
}
read_count = client.read(buff, 7000);//向缓冲区读取数据
memcpy(&img_buff[size_count], buff, read_count);//将读取的buff字节地址复制给img_buff数组
size_count = size_count + read_count;//计数当前帧字节位置
// Serial.println(size_count);
if (img_buff[frame_size - 3] == 0xaa && img_buff[frame_size - 2] == 0xbb &&
img_buff[frame_size - 1] == 0xcc)//判断末尾数据是否当前帧校验位
{
receive_time = millis() - start_time;
deal_time = millis();
img_buff[frame_size - 3] = 0;
img_buff[frame_size - 2] = 0;
img_buff[frame_size - 1] = 0;//清除标志位
tft.startWrite();//必须先使用startWrite,以便TFT芯片选择保持低的DMA和SPI通道设置保持配置
TJpgDec.drawJpg(0, 0, img_buff,
sizeof(img_buff));//在左上角的0,0处绘制图像——在这个草图中,DMA请求在回调tft_output()中处理
tft.endWrite();//必须使用endWrite来释放TFT芯片选择和释放SPI通道吗
// memset(&img_buff,0,sizeof(img_buff));//清空buff
size_count = 0;//下一帧
read_count = 0;
client.write("ok");//向上位机发送下一帧发送指令
end_time = millis(); //计算mcu刷新一张图片的时间,从而算出1s能刷新多少张图,即得出最大刷新率
Serial.printf("帧大小:%d ", frame_size);
Serial.print("MCU处理速度:");
Serial.print(1000 / (end_time - start_time), 2);
Serial.print("Fps");
Serial.printf("帧接收耗时:%.2fms,帧解码显示耗时:%.2fms\n", receive_time,
(millis() - deal_time));
break;
}
}
}
}
client.stop();
Serial.println("连接中断,请复位重新创建服务端");
}
}
- User_Setup.h 部分配置文件代码
// Tell the library to use 8-bit parallel mode (otherwise SPI is assumed)
// st7789使用到的并口引脚,需要打开的内容
#define TFT_PARALLEL_8_BIT
// The ESP32 and TFT the pins used for testing are:
#undef TFT_CS // 防止重复定义
#define TFT_CS 6 // Chip select control pin (library pulls permanently low
#undef TFT_DC
#define TFT_DC 7 // Data Command control pin - must use a pin in the range 0-31
#undef TFT_RST
#define TFT_RST 5 // Reset pin, toggles on startup
#define TFT_WR 8 // Write strobe control pin - must use a pin in the range 0-31
#define TFT_RD 9 // Read strobe control pin
#define TFT_D0 39 // Must use pins in the range 0-31 for the data bus
#define TFT_D1 40 // so a single register write sets/clears all bits.
#define TFT_D2 41 // Pins can be randomly assigned, this does not affect
#define TFT_D3 42 // TFT screen update performance.
#define TFT_D4 45
#define TFT_D5 46
#define TFT_D6 47
#define TFT_D7 48
#define PIN_POWER_ON 21
#undef TFT_BL
#define TFT_BL 38 // LED back-light control pin
#define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW)
- 在配置代码中,记得取消掉原来的其他配置,防止编译时冲突