SD 卡控制器(SD/SDIO Controller)
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 的机制提高吞吐带宽。
其内部框图如下图所示:
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、 支持长文件名称。
FATFS 的这些特点,加上开源、免费的原则,使得 FATFS 的应用非常广泛。FATFS 模块的层次结构分为顶层、中间层 FATFS 模块和底层接口。
最顶层是应用层,使用者无需理会 FATFS 的内部结构和复杂的 FAT 协议,只需要调用 FATFS 模块提供给用户的一系列应用接口函数,如 f_open,f_read,f_write 和 f_close 等,就可以像在 PC 上读/写文件那样简单。
中间层 FATFS 模块,实现了 FAT 文件读/写协议。FATFS 模块提供的是 ff.c 和 ff.h。除非有必要,使用者一般不用修改,使用时将头文件直接包含进去即可。
FATFS 模块提供的底层接口,它包括存储媒介读/写接口(disk I/O)和供给文件创建修改时间的实时时钟。
读写实验
- 实验平台:黑金 Zynq7035
- 开发环境:Vivado 2017.4
硬件设计
参考原理图可知,SD 卡接在了 PS_MIO40~45:
ZYNQ PS 做如下配置,配置 Bank 1 为 LVCMOS 1.8V,打开 UART0 和 SD0:
完善其他配置后生成比特流,导出硬件信息。
软件设计
创建 SDK 工程,在 BSP 设置中选中 xilffs
,xilffs
即为 FATFS
库,保存配置:
添加如下代码:
#include "xparameters.h"
#include "ff.h"
#include "xdevcfg.h"
#include "xil_printf.h"
#include "stdio.h"
#define kprintf xil_printf
#define SD_FS "0:/"
#define SD_FILE "0:SD_TEST.txt"
static FATFS sd_fatfs;
static FRESULT fatfs_init(FATFS *fatfs, TCHAR *path)
{
FRESULT res;
res = f_mount(fatfs, path, 1);
if(res != FR_OK)
{
res = f_mkfs(path, 0, 0);
if (res != FR_OK)
{
kprintf("ERROR: Unable to format FATfs.\r\n");
return res;
}
res = f_mount(fatfs, path, 1);
if(res != FR_OK)
{
kprintf("ERROR: f_mount returned %d.\r\n", res);
return res;
}
}
return res;
}
static FRESULT sd_read_data(char *FileName, uint32_t DestinationAddress, uint32_t ByteLength)
{
FIL fil;
FRESULT res;
UINT br;
res = f_open(&fil, FileName, FA_READ);
if(res)
{
kprintf("ERROR: %s f_open returned %d\r\n", FileName, res);
return res;
}
res = f_lseek(&fil, 0);
if(res)
{
kprintf("ERROR: %s f_lseek returned %d\r\n", FileName, res);
return res;
}
res = f_read(&fil, (void*)DestinationAddress, ByteLength, &br);
if(res)
{
kprintf("ERROR: %s f_read returned %d\r\n", FileName, res);
return res;
}
res = f_close(&fil);
if(res)
{
kprintf("ERROR: %s f_close returned %d\r\n", FileName, res);
return res;
}
return res;
}
static FRESULT sd_write_data(char *FileName, uint32_t SourceAddress, uint32_t ByteLength)
{
FIL fil;
FRESULT res;
UINT bw;
res = f_open(&fil, FileName, FA_CREATE_ALWAYS | FA_WRITE);
if(res)
{
kprintf("ERROR: %s f_open returned %d.\r\n", FileName, res);
return res;
}
res = f_lseek(&fil, 0);
if(res)
{
kprintf("ERROR: %s f_lseek returned %d.\r\n", FileName, res);
return res;
}
res = f_write(&fil, (void*) SourceAddress, ByteLength, &bw);
if(res)
{
kprintf("ERROR: %s f_write returned %d.\r\n", FileName, res);
return res;
}
res = f_close(&fil);
if(res)
{
kprintf("ERROR: %s f_close returned %d.\r\n", FileName, res);
return res;
}
return res;
}
static FRESULT sd_rw_test(void)
{
FRESULT res;
const char src_str[] = "ZYNQ test SD card write and read!";
char dest_str[33];
uint32_t len = strlen(src_str);
res = sd_write_data(SD_FILE, (uint32_t)src_str, len);
if(XST_SUCCESS != res)
{
kprintf("ERROR: fail to write SD Card.\r\n");
return res;
}
else
{
kprintf("Success to write SD Card.\r\n");
}
res = sd_read_data(SD_FILE, (uint32_t)dest_str, len);
if(XST_SUCCESS != res)
{
kprintf("ERROR: fail to read SD Card.\r\n");
return res;
}
else
{
kprintf("Success to read SD Card; data: %s \r\n", dest_str);
}
kprintf("SD Card Write and Read test end.\r\n");
return res;
}
static FRESULT scan_files(char *path)
{
FRESULT res;
DIR dir;
UINT i;
static FILINFO fno;
res = f_opendir(&dir, path);
char pathBuff[256];
if(res == FR_OK)
{
for( ; ; )
{
res = f_readdir(&dir, &fno);
if(res != FR_OK || fno.fname[0] == 0)
{
break;
}
if(fno.fattrib & AM_DIR)
{
i = strlen(path);
sprintf(&path[i], "/%s", fno.fname);
kprintf("%s \r\n", path);
res = scan_files(path);
if(res != FR_OK)
{
break;
}
path[i] = 0;
}
else
{
kprintf("%s/%s \r\n", path, fno.fname);
strcpy(pathBuff, fno.fname);
}
}
}
else
{
kprintf("Failed - %s", &res);
}
f_closedir(&dir);
return res;
}
int main(void)
{
kprintf("hello world. \r\n");
FRESULT res;
res = fatfs_init(&sd_fatfs, SD_FS);
if(XST_SUCCESS != res)
{
kprintf("ERROR: fail to open SD Card.\r\n");
}
else
{
kprintf("Success to open SD Card.\r\n");
}
sd_rw_test();
scan_files(SD_FS);
while(1)
{
}
return 0;
}
实验现象
- 终端输出: