文章目录
一、SD基础知识
1.SD卡的类型、协议规范、容量等级及支持的文件系统
Micro SD卡,是一种极细小的快闪存储器卡,MicroSD 卡插入适配器(Adapter)可以转换成 SD 卡,其操作时序和 SD卡是一样的。
开发板原理图如下,可知本开发板使用的是SDIO模式的SD卡。
标准 SD 卡 2.0 版本中,工作时钟频率可以达到 50Mhz。
在 SDIO 模式下采用 4 位数据位宽,理论上可以达到 200Mbps(50Mx4bit)的传输速率;
在 SPI 模式下采用 1 位数据位宽,理论上可以达到 50Mbps 的传输速率。
因此 SD 卡在 SDIO 模式下的传输速率更快,同时其操作时序也更复杂。
二、SD卡控制器
ZYNQ 中的 SD 卡控制器符合 SD2.0 协议规范,接口兼容 eMMC、MMC3.31、SDIO2.0、SD2.0、SPI,
支持 SDHC、SDHS 器件。SD 卡控制器支持 SDMA(单操作 DMA)、ADMA1(4K 边界限制 DMA)和
ADMA2(在 32 位系统中允许任何位置和任意大小)。ARM 处理器通过 AHB 总线访问 SD 卡控制器,SD
控制器采用读和写通道各自双缓冲 FIFO 的机制提高吞吐带宽。其内部框图如下图所示:
值得一提的是,ZYNQ 内部集成了两个 SD 卡控制器,并且 Xilinx SDK 的 standalone 已经移植好了 FATFS(SDK 软件中叫做 xilffs)文件系统,因此在 SDK 中添加 xilffs 库后,就可以在程序中使用 FATFS 中的 API 函数来操作 SD 卡。
SD 控制器读写通道采用独立的 512 字节深度的双缓冲 FIFO 执行读和写操作。
在写操作时,处理器向其中一个 FIFO 写数据,将另一个 FIFO 的数据写到 SD 总线;
在读操作时,SD 总线上的数据向其中一个FIFO 写数据,处理器将数据从另一个 FIFO 读出数据。
SD 卡控制器通过双缓冲机制以保证最大带宽。
三、文件系统(FATFS)
FATFS 是一个完全开源免费的 FAT 文件系统模块,专门为小型的嵌入式系统而设计。它完全用标准 C语言编写,所以具有良好的硬件平台独立性,可以很方便的移植到各种嵌入式处理器中。Xilinx SDK 的standalone 已经移植好了 FATFS 文件系统,因此在 SDK 中添加 xilffs 后,就可以在程序中使用 FATFS 中的 API 函数来操作 SD 卡。
FATFS 的特点如下:
1、 结构清晰,代码量少,文件系统和 IO 底层分开,特别适合新手入门学习;
2、 支持最多 10个逻辑盘符和两级文件夹;
3、 支持 FAT12/FAT16 和 FAT32 文件系统;
4、 支持长文件名称。
四、 SD卡控制实例:
4.1 txt文本任务:
通过Xilinx SDK自带的FATFS库,完成对TF卡中TXT文本读写的功能,并将读写测试结果通过串口打印出来。
4.1.1 硬件部分:
UART、SD接口、DDR3存储器
4.1.2 软件部分:
1.板级支持包中加入FATFS文件系统
出现下列bsp则说明添加成功,里面包含各种需要的函数:
2.C设计
流程:
代码:
#include "xparameters.h"
#include "xil_printf.h"
#include "ff.h" //FATFS
#include "xdevcfg.h" //参数
#define FILE_NAME "ZDYZ.txt" //定义文件名
const char src_str[30] = "www.openedv.com"; //定义文本内容
static FATFS fatfs; //文件系统
//初始化文件系统
int platform_init_fs()
{
FRESULT status;
TCHAR *Path = "0:/";
BYTE work[FF_MAX_SS];
//注册一个工作区(挂载分区文件系统)
//在使用任何其它文件函数之前,必须使用f_mount函数为每个使用卷注册一个工作区
status = f_mount(&fatfs, Path, 1); //挂载SD卡
if (status != FR_OK) {
xil_printf("Volume is not FAT formated; formating FAT\r\n");
//格式化SD卡
status = f_mkfs(Path, FM_FAT32, 0, work, sizeof work);
if (status != FR_OK) {
xil_printf("Unable to format FATfs\r\n");
return -1;
}
//格式化之后,重新挂载SD卡
status = f_mount(&fatfs, Path, 1);
if (status != FR_OK) {
xil_printf("Unable to mount FATfs\r\n");
return -1;
}
}
return 0;
}
//挂载SD(TF)卡
int sd_mount()
{
FRESULT status;
//初始化文件系统(挂载SD卡,如果挂载不成功,则格式化SD卡)
status = platform_init_fs();
if(status){
xil_printf("ERROR: f_mount returned %d!\n",status);
return XST_FAILURE;
}
return XST_SUCCESS;
}
//SD卡写数据
int sd_write_data(char *file_name,u32 src_addr,u32 byte_len)
{
FIL fil; //文件对象
UINT bw; //f_write函数返回已写入的字节数
//打开一个文件,如果不存在,则创建一个文件
f_open(&fil,file_name,FA_CREATE_ALWAYS | FA_WRITE);
//移动打开的文件对象的文件读/写指针 0:指向文件开头
f_lseek(&fil, 0);
//向文件中写入数据
f_write(&fil,(void*) src_addr,byte_len,&bw);
//关闭文件
f_close(&fil);
return 0;
}
//SD卡读数据
int sd_read_data(char *file_name,u32 src_addr,u32 byte_len)
{
FIL fil; //文件对象
UINT br; //f_read函数返回已读出的字节数
//打开一个只读的文件
f_open(&fil,file_name,FA_READ);
//移动打开的文件对象的文件读/写指针 0:指向文件开头
f_lseek(&fil,0);
//从SD卡中读出数据
f_read(&fil,(void*)src_addr,byte_len,&br);
//关闭文件
f_close(&fil);
return 0;
}
//main函数
int main()
{
int status,len;
char dest_str[30] = "";
status = sd_mount(); //挂载SD卡
if(status != XST_SUCCESS){
xil_printf("Failed to open SD card!\n");
return 0;
}
else
xil_printf("Success to open SD card!\n");
len = strlen(src_str); //计算字符串长度
//SD卡写数据
sd_write_data(FILE_NAME,(u32)src_str,len);
//SD卡读数据
sd_read_data(FILE_NAME,(u32)dest_str,len);
//比较写入的字符串和读出的字符串是否相等
if (strcmp(src_str, dest_str) == 0)
xil_printf("src_str is equal to dest_str,SD card test success!\n");
else
xil_printf("src_str is not equal to dest_str,SD card test failed!\n");
return 0;
}
结果:运行程序后,SD卡中存在名为ZDYZ的txt文件,且文件中的字符串就是我们自定义的内容。
4.2 SD卡BMP拍照功能
将图像数据从DDR3内存当中写入到SD卡当中,并通过添加BMP图片的文件头和信息头,生成一张BMP图片,存储在SD卡当中。
4.2.1 BMP文件结构组成:
位图文件头+位图信息头+图像数据
- 16位图:常见有两种格式,即 555 格式和 565 格式。555 格式只使用了 15 位,最后一位保留设为 0。
- 24位图:使用 3 字节保存颜色值,每一个字节代表一种颜色,按红、绿、蓝排列。
- 32位图:使用 4 字节保存颜色值,每一个字节代表一种颜色,红、绿、蓝,还有 Alpha 通道,即透明色。
这里以 24 位位图为例作为设计对象
4.2.2 SD卡中生成BMP文件的核心步骤
从DDR3读取32位图像数据:
- 通过直接访问内存地址从DDR3中读取一帧32位的图像数据。
- 假设这些图像数据已经被存储在某个固定的内存地址(例如
0x2000000
)。
将32位图像数据处理成24位图像数据:
- 直接从源地址读取32位图像数据。
- 处理数据,去除每个像素中的Alpha通道,只保留红色、绿色和蓝色通道,生成24位图像数据。
- 将处理后的24位图像数据存储到DDR3的另一个缓存区(例如
0x3800000
)。
将24位图像数据加上BMP文件头写入到SD卡:
- 创建并填写BMP文件头和信息头。
- 打开SD卡文件系统,并创建一个新的BMP文件。
- 将BMP文件头、信息头和24位图像数据依次写入到SD卡中的文件。
32位图像数据处理成24位图像数据代码:
int get24BitPixelDirect(u8* imgSrc, u8* imgDst, int width, int height) {
int r, c, rgb;
for (r = 0; r < height; r++) {
for (c = 0; c < width; c++) {
for (rgb = 0; rgb < 4; rgb++) {
if (rgb < 3) {
*imgDst = *imgSrc; // 复制R, G, B通道
imgDst++;
}
imgSrc++; // 跳过A通道
}
}
}
return 0;
}
效果:实际存储的bmp照片为上下颠倒的,
原因是:DDR3中图像数据是从上到下存储的,而BMP文件格式要求图像数据从下到上存储
因此这里可以在将图像数据写入BMP文件之前,对图像数据进行上下翻转。(修改之前的get24BitPixelDirect代码)
int get24BitPixelDirect(u8* imgSrc, u8* imgDst, int width, int height) {
int r, c, rgb;
u8 *tempBuffer = (u8 *)malloc(width * height * 3); // 临时缓冲区用于存储翻转后的数据
if (tempBuffer == NULL) {
return -1; // 内存分配失败
}
u8 *dstPtr = tempBuffer + (height - 1) * width * 3; // 指向临时缓冲区的最后一行
for (r = 0; r < height; r++) {
for (c = 0; c < width; c++) {
for (rgb = 0; rgb < 4; rgb++) {
if (rgb < 3) {
*dstPtr = *imgSrc; // 复制R, G, B通道
dstPtr++;
}
imgSrc++; // 跳过A通道
}
}
dstPtr -= 2 * width * 3; // 每处理完一行,移动指针到临时缓冲区的上一行
}
// 将翻转后的数据复制到目标缓冲区
memcpy(imgDst, tempBuffer, width * height * 3);
free(tempBuffer); // 释放临时缓冲区
return 0;
}