本帖最后由 sipower 于 2023-8-13 20:25 编辑
上一帖介绍搭建MicroPython开发环境,并在此基础上设计一个天气时钟。这一贴介绍增加屏幕和SD卡组件,并通过Arduino编写基础程序模块,为后面实验做准备。本次实验要实现的功能:
焊接屏幕组件;调试显示和触摸;调试SD卡;SD卡上的大分辨率图片缩小显示。
这次测评只提供了FireBeetle 2ESP32-S3这个实验板,没有配套屏幕,DFRobot官网倒是可以买到他们GDI专用接口的屏幕,如下图所示,但是太贵了,买这个有点肉疼,所以我只好换个思路,从淘宝找个便宜的替代品。
图1、DFRobot官网GDI接口的屏幕
淘宝这个屏幕带的是电阻触摸屏,尺寸和分辨率和DFRobot官网一样,同样也预留SD卡接口,完全能满足我这次设计需求。屏幕模组图片如下。
图2、淘宝屏幕模组
这个屏幕资料还是挺全面的,用起来很方便,资料链接如下。
http://www.lcdwiki.com/zh/3.5inch_SPI_Module_ILI9488_SKU:MSP3520
目前网上搜索涉及到ESP32-S3接摄像头的操作,大部分是基于ESP-IDF或者Arduino平台的,ESP-IDF的难度系数有点高,暂时玩不转,因此后面所有实验均是基于Arduino平台的。
第一个任务,焊接屏幕组件。由于买来的屏幕模组只有DIP插针接口,要想和FireBeetle 2开发板连起来,需要焊接10根线。其中SPI和电源是显示、触摸、SD卡共用的,背光、片选都是独立线。如下图所示。
图3、屏幕模组接口
焊接这个线还是挺有挑战的,但是想到能省点有限的预算,咬咬牙还是得上。为了让屏幕和实验板能重复使用,我是先安装了插母,然后在插母上焊接导线,这样以后想接别的外设,可以很方便的拆下。经过两个晚上的努力,成果如下,而且一次成功。
图4、模组焊接完成
第二个任务,调试显示和触摸。在Arduino平台点屏,那肯定首选《TFT_eSPI》这个库了。使用这个库,首先就是要安装这个库文件,其他帖子介绍的比较多,此处不再赘述。然后最主要的一项工作就是配置屏幕参数头文件。我参照其他用ILI9488这个芯片的屏幕头文件改造了一下,并命名为《Setup208_ESP32_T_ILI9488_S3.h》,然后在《User_Setup_Select.h》这个头文件里面调用。在ArduinoIDE中打开《Animated_Eyes_1》这个例程,什么不用改直接运行,就可以成功显示了,如下图。
图5、显示调试成功
调试触摸屏的时候出现一点小插曲,下载测试程序后,触摸屏不好用,我反复检查焊接都没有发现问题,最后检查程序,才发现原来在改引用的头文件时,由于原来那个屏幕没有触摸,其中SPI输入引脚设置为:
#defineTFT_MISO -1
是没有使用的,我这个漏改了,我改正后触摸屏就好用。测试效果如下图。
图6、触摸调试成功
第三个任务,调试SD卡。使用SD卡,最重要的是片选要设置好,在SD.begin(GDI_SDCS)语句中设置,我看到好多新手发帖子说SD卡不好使,大部分就是这个没设置。我使用SD卡的目的是存储和显示JPEG图片,因此选用的是《ESP32_SDcard_jpeg》这个例程。其中为了实现JPEG解码,还需要安装《JPEGDecoder》库文件。将例程中的图片拷贝到SD卡上,然后运行程序,显示结果如下。
图7、SD调试成功。
第四个任务,SD卡上的大分辨率图片缩小显示。我预期的功能是用摄像头拍照并显示到屏幕上,摄像头拍摄照片分辨率是1600*1200,屏幕分辨率是是480*320,直接显示的话,只能看到一个角。我PS了一张1600*1200的图片放到卡上显示,原图和屏幕直接显示效果如下图。
图8、原图和屏幕显示效果
由上图可见,基本上就看不出来要显示的是什么内容了。要想全幅显示出来,就要缩小图片,但当前的解码程序不支持缩小分辨率的功能。经过群友提醒,有些JPEG的库是支持缩放的,我就搜索Scale这个关键词,最终还真被我找到了,就是《TFT_eSPI》这个库的作者Bodmer,在GitHub上回复一个话题说他新做了一个库《TJpg_Decoder》 ,是支持缩小分辨率的,采用的是抽点方式,虽然不如色块平均法效果好,但是在32位MCU上运行效率还是非常不错的。
有了大神的基础工作,我想实现的功能就简单了。然后就是迅速下载《TJpg_Decoder》 库,上例程开始调试。关于GitHub下载文件,这有个小窍门,当GitHub网站打不开时,可以在域名前面加个K,就是KGitHub,然后就可以打开国内镜像网站了,速度还是很不错的。
经过我的一通改造,终于实现了放在SD卡上的图片使用《TJpg_Decoder》 库缩小后显示到屏幕上,下面是代码和视频。
整体代码如下。
- // Example for library:
- // https://github.com/Bodmer/TJpg_Decoder
- // This example if for an ESP8266 or ESP32, it renders a Jpeg file
- // that is stored in a SD card file. The test image is in the sketch
- // "data" folder (press Ctrl+K to see it). You must save the image
- // to the SD card using you PC.
- // Include the jpeg decoder library
- #include
- // Include SD
- #define FS_NO_GLOBALS
- #include
- #ifdef ESP32
- #include "SPIFFS.h" // ESP32 only
- #endif
- #define SD_CS 4
- // Include the TFT library https://github.com/Bodmer/TFT_eSPI
- #include "SPI.h"
- #include // Hardware-specific library
- TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
- // This next function will be called during decoding of the jpeg file to
- // render each block to the TFT. If you use a different TFT library
- // you will need to adapt this function to suit.
- bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap)
- {
- // Stop further decoding as image is running off bottom of screen
- if ( y >= tft.height() ) return 0;
- // This function will clip the image block rendering automatically at the TFT boundaries
- tft.pushImage(x, y, w, h, bitmap);
- // This might work instead if you adapt the sketch to use the Adafruit_GFX library
- // tft.drawRGBBitmap(x, y, bitmap, w, h);
- // Return 1 to decode next block
- return 1;
- }
- void setup()
- {
- Serial.begin(115200);
- Serial.println("\n\n Testing TJpg_Decoder library");
- // Set all chip selects high to avoid bus contention during initialisation of each peripheral
- pinMode(GDI_CS, OUTPUT);
- pinMode(GDI_TCS, OUTPUT);
- pinMode(GDI_SDCS, OUTPUT);
- digitalWrite(GDI_TCS, HIGH); // Touch controller chip select (if used)
- digitalWrite(GDI_CS, HIGH); // TFT screen chip select
- digitalWrite(GDI_SDCS, HIGH); // SD card chips select, must use GPIO 5 (ESP32 SS)
- // Initialise SD before TFT
- if (!SD.begin(GDI_SDCS)) {
- Serial.println(F("SD.begin failed!"));
- while (1) delay(0);
- }
- Serial.println("\r\nInitialisation done.");
- // Initialise the TFT
- tft.begin();
- tft.setTextColor(0xFFFF, 0x0000);
- tft.fillScreen(TFT_BLACK);
- tft.setSwapBytes(true); // We need to swap the colour bytes (endianess)
- // The jpeg image can be scaled by a factor of 1, 2, 4, or 8
- TJpgDec.setJpgScale(1);
- // The decoder must be given the exact name of the rendering function above
- TJpgDec.setCallback(tft_output);
- }
- void loop()
- {
- tft.fillScreen(TFT_RED);
- // Time recorded for test purposes
- uint32_t t = millis();
- // Get the width and height in pixels of the jpeg if you wish
- uint16_t w = 0, h = 0;
- TJpgDec.getSdJpgSize(&w, &h, "/panda.jpg");
- Serial.print("Width = "); Serial.print(w); Serial.print(", height = "); Serial.println(h);
- // Draw the image, top left at 0,0
- TJpgDec.drawSdJpg(0, 0, "/panda.jpg");
- // How much time did rendering take
- t = millis() - t;
- Serial.print(t); Serial.println(" ms");
- // Wait before drawing again
- delay(2000);
- //////////////////////////////////////////////////////////////////////////////////////////////////////////
- tft.setRotation(1); // portrait
- //////////////////////////////////////////////////////////////////////////////////////////////////////////
- tft.fillScreen(TFT_RED);
- t = millis();
- TJpgDec.setJpgScale(1);
- // Get the width and height in pixels of the jpeg if you wish
- TJpgDec.getSdJpgSize(&w, &h, "/3.jpg");
- Serial.print("Width = "); Serial.print(w); Serial.print(", height = "); Serial.println(h);
- // Draw the image, top left at 0,0
- TJpgDec.drawSdJpg(0, 0, "/3.jpg");
- // How much time did rendering take
- t = millis() - t;
- Serial.print(t); Serial.println(" ms");
- // Wait before drawing again
- delay(2000);
- //////////////////////////////////////////////////////////////////////////////////////////////////////////
- tft.fillScreen(TFT_RED);
- t = millis();
- TJpgDec.setJpgScale(2);
- // Get the width and height in pixels of the jpeg if you wish
- TJpgDec.getSdJpgSize(&w, &h, "/3.jpg");
- Serial.print("Width = "); Serial.print(w); Serial.print(", height = "); Serial.println(h);
- // Draw the image, top left at 0,0
- TJpgDec.drawSdJpg(0, 0, "/3.jpg");
- // How much time did rendering take
- t = millis() - t;
- Serial.print(t); Serial.println(" ms");
- // Wait before drawing again
- delay(2000);
- //////////////////////////////////////////////////////////////////////////////////////////////////////////
- tft.fillScreen(TFT_RED);
- t = millis();
- TJpgDec.setJpgScale(4);
- // Get the width and height in pixels of the jpeg if you wish
- TJpgDec.getSdJpgSize(&w, &h, "/3.jpg");
- Serial.print("Width = "); Serial.print(w); Serial.print(", height = "); Serial.println(h);
- // Draw the image, top left at 0,0
- TJpgDec.drawSdJpg(0, 0, "/3.jpg");
- // How much time did rendering take
- t = millis() - t;
- Serial.print(t); Serial.println(" ms");
- // Wait before drawing again
- delay(2000);
- Serial.println("finished!");
- while (1);
- }
复制代码
展示视频如下。
缩小图片
|