作者:gtbestom
要实现电子相册功能,需要先实现多个外设驱动和软件模块,如下:
1、SD 卡驱动(SPI)
2、LCD 驱动( ILI9341)
3、FAT 文件系统驱动(Fatfs)
4、BMP 文件解码
实现效果如下:


【SD 卡驱动】
SD 卡或者 TF 卡是生活里较为常见的存储卡,形状一般如下:

SD 卡可以通过 SDIO 接口读写,也可以通过 SPI 接口读写,读写 SD 卡的协议规范目前也已经更新到了 SD 9.0 版本,高版本支持更大容量和更快的速度,接口上也更新了对 PCIE 4.0 的支持
由于单片机资源有限,目前单片机普遍能支持到的 SD 卡协议为 2.0 SDHC 标准,支持最大 32GB 容量
RA4L1 虽然没有支持 SDIO 接口,但也可以通过 SPI 对 SD 卡进行读写
初始化阶段,先设置 SPI 时钟速率为 400Kbps,初始化完成可以调高到最高 25Mbps,时钟和极性如下模式3所示:


初始化配置如下:


设置 SPI 速率时,可以重新构造一个初始化配置结构体,再调用 R_SPI_Open(&g_spi0_ctrl, &g_spi0_20m_cfg); 打开 SPI 总线。

【点亮LCD屏】
前面几篇介绍了刷各种屏幕,OLED12864 SSD1306 单色屏,OLED SSD1327 4阶灰度屏,WS2812 全彩点阵屏,彩屏自然也离不开 LCD,接下去点亮 LCD ILI9341
ILI9341 控制器可以驱动最大 320x240 的 16 位深 LCD 屏,这里使用并口屏,8080 时序驱动,由于现成的屏适用于 Arduino UNO,在 RA4L1 开发板上并口并不是按顺序的,需要做一些修改,刷屏速度也会有所降低
引脚定义如下代码:
#define LCD_ILI9341_RD_H GPIO_PIN_H(GPIO5,10)
#define LCD_ILI9341_RD_L GPIO_PIN_L(GPIO5,10)
#define LCD_ILI9341_WR_H GPIO_PIN_H(GPIO5,11)
#define LCD_ILI9341_WR_L GPIO_PIN_L(GPIO5,11)
#define LCD_ILI9341_RS_H GPIO_PIN_H(GPIO5,12)
#define LCD_ILI9341_RS_L GPIO_PIN_L(GPIO5,12)
#define LCD_ILI9341_CS_H GPIO_PIN_H(GPIO5,13)
#define LCD_ILI9341_CS_L GPIO_PIN_L(GPIO5,13)
#define LCD_ILI9341_RST_H GPIO_PIN_H(GPIO0,3)
#define LCD_ILI9341_RST_L GPIO_PIN_L(GPIO0,3)
#define LCD_ILI9341_DATA(dat) if((dat)&0x80)GPIO_PIN_H(GPIO1, 7);else GPIO_PIN_L(GPIO1, 7);\
if((dat)&0x40)GPIO_PIN_H(GPIO4, 6);else GPIO_PIN_L(GPIO4, 6);\
if((dat)&0x20)GPIO_PIN_H(GPIO4, 5);else GPIO_PIN_L(GPIO4, 5);\
if((dat)&0x10)GPIO_PIN_H(GPIO4, 4);else GPIO_PIN_L(GPIO4, 4);\
if((dat)&0x08)GPIO_PIN_H(GPIO4,14);else GPIO_PIN_L(GPIO4,14);\
if((dat)&0x04)GPIO_PIN_H(GPIO4,15);else GPIO_PIN_L(GPIO4,15);\
if((dat)&0x02)GPIO_PIN_H(GPIO6, 0);else GPIO_PIN_L(GPIO6, 0);\
if((dat)&0x01)GPIO_PIN_H(GPIO6, 2);else GPIO_PIN_L(GPIO6, 2)
【Fatsf 文件系统】
FATFS 是嵌入式开发中常用的 FAT 文件系统,最新版本为:R0.15a,可以再官网 https://elm-chan.org/fsw/ff/00index_e.html 下载,本次使用的就是这个最新版本,只需要改动 diskio 接口文件里的函数,使其能够通过 SPI 初始化,读写 SD 卡,获取 SD 卡状态即可。
【bmp 文件解码】
移植完 Fatfs 文件系统后,就可以对 SD 卡里的文件进行读写了,通常每个文件都有其特定的格式,需要用对应的解析方式,例如图片文件 bmp、png、jpg 都需要不同的解码算法,才能拿到真实的图像数据。
bmp 是相对简单的一种非压缩格式,包含信息头,位图信息头,调色板(非必须),之后就是图像数据了,解析信息头可以获取图像的 长、宽、位深等关键信息,以便将其正确显示在屏幕上
信息头如下:
//BMP头文件结构体
typedef struct
{
u16 bfType; // 文件标志.只对'BM' 0x424D ,用来识别BMP位图类型
u32 bfSize; // 文件大小,包括这14个字节
u16 bfReserved1; // 保留
u16 bfReserved2; // 保留
u32 bfOffBits; // 从文件开始到位图数据(bitmap data)开始之间的的偏移量,前三个部分的长度之和
}__attribute__((packed)) BITMAPFILEHEADER; // 固定14个字节
//BMP信息头
typedef struct
{
u32 biSize; // 说明BITMAPINFOHEADER结构所需要的字数
s32 biWidth; // 说明图象的宽度,以象素为单位
s32 biHeight; // 说明图象的高度,以象素为单位
u16 biPlanes; // 为目标设备说明位面数,其值将总是被设为1
u16 biBitCount; // 说明比特数/象素,其值为1、4、8、16、24、或32
u32 biCompression; // 说明图象数据压缩的类型。其值可以是下述值之一:
// BI_RGB:没有压缩;
// BI_RLE8:每个象素8比特的RLE压缩编码,压缩格式由2字节组成(重复象素计数和颜色索引);
// BI_RLE4:每个象素4比特的RLE压缩编码,压缩格式由2字节组成
// BI_BITFIELDS:每个象素的比特由指定的掩码决定。
u32 biSizeImage; // 说明图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0
s32 biXPelsPerMeter; // 说明水平分辨率,用象素/米表示
s32 biYPelsPerMeter; // 说明垂直分辨率,用象素/米表示
u32 biClrUsed; // 说明位图实际使用的彩色表中的颜色索引数
u32 biClrImportant; // 说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。
}__attribute__((packed)) BITMAPINFOHEADER ; // 固定40个字节,可能会有16字节掩码信息
这里使用 bmp 图像是 24 位,即 RGB888 格式,显示屏是 RGB565 格式,中间需要逐个像素进行 RGB888 到 RGB565 的转换
适配好这些过程后,就可以顺利将 SD 中的 bmp 文件显示到屏幕上,代码如下:

效果如视频所示,速度可以通过软硬件进一步优化,但在这里不再深入修改。