【ZYNQ】SD 卡读写及文件扫描实验

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

更多内容

Zynq是Xilinx推出的一款嵌入式SoC(系统-on-a-chip),它集成了CPU核心和FPGA逻辑于一身。如果你想在Zynq上读取SD中的.bin文件,通常需要通过以下几个步骤: 1. **准备硬件**:确保Zynq开发板上有SD插槽,并且SD已插入并包含待读取的.bin文件。 2. **配置外设**:在Zynq中,你需要设置GPIO口作为SD控制器的I/O,以便控制数据传输。这可能涉及到使用PS(处理器部分)的硬件资源。 3. **驱动支持**:Zynq通常有现成的SD/MMC驱动,可以从Linux内核源码或第三方库获取。如果没有集成,可能需要编译并加载适当的驱动模块。 4. **打开文件系统**:在操作系统层面,你需要通过文件系统API(如Linux的`open()`, `read()`等函数)打开SD设备节点,并定位到存储.bin文件的位置。 5. **读取文件**:一旦文件描述符准备好,就可以开始读取.bin文件内容了。每次从SD读取数据时,可以指定缓冲区大小。 ```cpp #include <fcntl.h> #include <unistd.h> #include <sys/mman.h> int fd = open("/dev/mmcblk0p1", O_RDONLY); // 这里假设SD挂载点为/dev/mmcblk0p1 if (fd == -1) { perror("Failed to open device"); return -1; } off_t offset = /* 指定bin文件偏移量 */; size_t size = /* 指定要读取的数据长度 */; char* buffer = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, offset); if (buffer == MAP_FAILED) { perror("mmap failed"); close(fd); return -1; } // 现在可以处理buffer中的数据 // 使用完后记得 munmap(buffer, size); munmap(buffer, size); close(fd); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hello阿尔法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值