SD 卡控制器
ZYNQ 中的 SD 卡控制(SD/SDIO Controller)器符合 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
。将use_lfn
设置为true
,使能长文件名以及文件名的小写字母,点击OK
按钮完成设置:
- 在工程中添加以下代码:
#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(); kprintf("Scan Files in %s: \r\n", SD_FS); scan_files(SD_FS); while(1) { } return 0; }
实验现象
- 终端输出:
- 查看 SD 卡:
参考来源
- 正点原子领航者
- 黑金 ZYNQ7035
更多内容
- CSDN博客:@Hello阿尔法
- 哔哩哔哩:@Hello阿尔法
- 知乎:@Hello阿尔法