65.9K
CodeProject 正在变化。 阅读更多。
Home

在带有 ILI9341 320×240 LCD 模块的 PIC 上使用 picojpeg 库

2023年5月7日

CPOL

6分钟阅读

viewsIcon

2584

如何在带有 ILI9341 320×240 LCD 模块的 PIC 上使用 picojpeg 库

我从 eBay 购买了一个 320×240 的 LCD 模块,它支持 320×240 分辨率,并带有一个 SD 卡插槽。

这款 LCD 使用 ILI9341 控制器,支持 SPI 模式。在 adafruit 提供的示例代码的基础上,几分钟内我就绘制了一个可以在 LCD 上显示文本和图形的程序,没有任何困难。

鉴于 LCD 的分辨率较高,我决定尝试一件我以前从未做过的事情,这也是许多爱好者在这款 16 位微控制器上认为的巨大挑战:从 SD 卡解码并显示 JPEG 图像。

寻找 JPEG 解码库

我首先想到的候选库是 Microchip Graphics Library,它专门为 16 位和 32 位 PIC 微控制器构建,因为我对他们强大的 Memory Disk Drive 库有很好的经验,该库能够处理各种文件系统。然而,下载后快速浏览文件发现事情并不那么简单——库的示例应用程序是为了适应各种 PIC 系列而设计的,并且它被设计成从特定的闪存芯片读取图形图像并显示在一些支持的 LCD 显示器上。由于我的 ILI9341 不受支持,我发现要清理代码以获得我想要的部分将是一个挑战,于是我决定寻找一个更简洁的 JPEG 解码库。

经过一番研究,我选择了 picojpeg,这是一个用 C 语言编写的开源 JPEG 解压缩器,它只有一个源代码文件,并针对小型 8/16 位嵌入式设备进行了特定优化。在 Visual Studio 中成功运行了示例应用程序(它将 JPEG 转换为 TGA 文件)后,我继续将该库移植到 C30。

将 picojpeg 移植到 C30

该库 仅包含 2 个文件,picojpeg.cpicojpeg.h,它们使用标准的 ANSI C,应该可以在 C30 下编译而没有问题。然而,示例应用程序 jpg2tga.c 包含了使用该库解码 JPEG 的示例代码,它是针对 Windows 和 Visual Studio 编写的,需要调整才能在 C30 下工作。具体来说,像 intlong 这样的数据类型声明需要修改,因为 Windows 上的 int 默认是 32 位,而在 C30 中它是 16 位。此外,由于 C30 中的右移始终是无符号的,因此需要声明并设置为 1 以下的预处理器定义,如 picojpeg.c 中注释所示,否则显示的颜色将会是错误的。

// Set to 1 if right shifts on signed ints are always unsigned (logical) shifts
// When 1, arithmetic right shifts will be emulated by using a logical shift
// with special case code to ensure the sign bit is replicated.
#define PJPG_RIGHT_SHIFT_IS_ALWAYS_UNSIGNED 1

通过改编 jpg2tga 示例应用程序中的代码,我编写了一个辅助文件 jpeg_helper.c,其中包含以下函数,用于从 SD 卡读取 JPEG 文件并将其绘制到 LCD 上。

JPEG_Info pjpeg_load_from_file
(const char *pFilename, int reduce, unsigned char showHorizontal)

showHorizontal 传递 1 来以横向模式在屏幕上显示图像。传递 0 来以纵向模式显示。

由于 JPEG 文件中的图像数据在内部存储为一系列相对较小的、独立编码的矩形块,通常为 8×8 或 16×16,称为 最小编码单元 (MCU),因此无需在显示之前将整个 JPEG 文件读入内存。因此,即使在 PIC 有限的内存下,也可以通过在图像渲染时读取数据并进行解码,在 LCD 上显示大型 JPEG 文件(受文件系统大小限制和 LCD 分辨率限制)。这也使得加载高分辨率 JPEG 文件的缩小版本成为可能,只需渲染每个 MCU 块的第一个像素,而不是整个块。要显示图像的缩小版本,请将 reduce 参数传递 1

为简单起见,jpeg_load_from_file 函数不处理灰度 JPEG 文件。

通过上述更改,我成功地使用 picojpeg 库在 LCD 上显示了一个 320×240 的 JPEG。在 PIC24HJ128GP202 上以 32 MHz 时钟速度运行,PIC 读取图像数据、解码图像并显示在 LCD 上总共需要 10 秒钟。这个过程在下面的视频中有所展示。

原始照片可以 这里 下载。

在我的测试中,仅绘制每个 MCU 的第一个像素,在相同的 PIC 配置下,一个 2816×2112 (2.41MB) 的 JPEG 文件在 320×240 的 LCD 上渲染完成,耗时 105 秒,没有出现问题。

过载 PIC

尽管 16 位微控制器以 32 MHz 的速度能够渲染大型 JPEG 文件对我来说已经很惊人了,但其速度(320×240 图像需要 10 秒,2.41MB 图像缩小显示需要 105 秒) 对于任何实际用途来说都太慢了。为了提高渲染速度,我决定让 PIC 以更快的时钟速度运行。仍然使用内部振荡器,这是通过增加频率倍增器来实现的。

// Using internal oscillator = 7.37MHz
// Running Frequency = Fosc = 7.37 * PLLDIV / 1 / 2 / 2
// Output at RA3 = Fosc / 2
CLKDIVbits.FRCDIV = 0;      // FRC divide by 1
CLKDIVbits.PLLPOST = 0;     // PLL divide by 2
CLKDIVbits.PLLPRE = 0;      // PLL divide by 2
PLLFBDbits.PLLDIV = 15;     // Freq. Multiplier (default is 50)

根据数据手册,PIC24HJ128GP202 可以以最高 80MHz @ 40 MIPS 的速度运行,方法是将倍增器设置为大约 43。在我实验期间,PIC 似乎仍然可以以 100MHz 运行并且能够进行简单的 UART 通信,尽管设备会稍微发热。高于 100MHz 且高达 120MHz 时,开始出现问题,例如,程序会意外终止,MPLAB 会报告“Target Halted.”。通过打开 **View** > **File Registers** 并检查地址 0740 的 RCON 寄存器,看起来发生了欠压复位(RCON 的第 1 位被设置,有时第 0 位也被设置)。在 PIC24HJ128GP202 上,无法关闭欠压复位功能——它是不可配置的。高于 120MHz 时,MPLAB 甚至无法使用 PICKit2 成功调试 PIC 上的程序。

PIC 时钟速度与 SD 卡 SPI 速度

在高时钟速度和内部振荡器下,选择正确的 UART 波特率 BRG 值也会有问题——事实上,在 100MHz 测试时,我只能让 UART 以 9600bps 运行!由于 UART 在我的情况下主要用于调试,这应该不是问题。另一个更大的问题是 SD 卡 SPI 时钟速度,因为许多较旧的 SD 卡仅支持高达 20MHz,而 MDD 库默认以 PIC 时钟速度的 1/4 运行 SD 卡 SPI 时钟。这在 SD-SPI.hSYNC_MODE_FAST 声明中可以看到。

// Description: This macro is used to initialize a 16-bit PIC SPI module
#ifndef SYNC_MODE_FAST
    // primary precaler: 1:1 secondary prescaler: 4:1
    #define   SYNC_MODE_FAST    0x3E
#endif

这意味着即使 PIC 时钟速度仅为 80MHz,SD 卡 SPI 速度也将达到 20MHz——达到了某些卡的最大支持速度。为了解决这个问题,需要将 SPI 预分频器更改为 8:1 以将速度降低到 10MHz。

#define   SYNC_MODE_FAST    0b111010

这会将 SPI 速度减半,使得读取 SD 卡数据和渲染图像的速度变慢,从而失去了超频的目的。

在我的测试中,即使只有 64MHz,如果在面包板上构建电路,密集地从 SD 卡读取 JPEG 数据也会随机且意外地失败。迁移到洞洞板可以解决这个问题,并允许提高时钟速度。我将其归因于面包板上的杂散电容,当 SD 卡 SPI 频率增加时,它会成为一个问题。实际上,32 MHz 是我能在面包板上可靠运行电路的最高速度。

移植后的 picojpeg 库的 MPLAB 源代码可以在 这里 下载。

© . All rights reserved.